diff --git a/src/api/auth.c b/src/api/auth.c
index 49bada03..1ceba0f2 100644
--- a/src/api/auth.c
+++ b/src/api/auth.c
@@ -8,14 +8,14 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
-#include "FTL.h"
+#include "../FTL.h"
#include "../webserver/http-common.h"
#include "../webserver/json_macros.h"
#include "routes.h"
-#include "log.h"
-#include "config.h"
-// read_setupVarsconf()
-#include "setupVars.h"
+#include "../log.h"
+#include "../config.h"
+// read_se../tupVarsconf()
+#include "../setupVars.h"
static struct {
bool used;
@@ -77,15 +77,12 @@ int check_client_auth(struct mg_connection *conn)
auth_data[num].valid_until = now + httpsettings.session_timeout;
// Update user cookie
- char *buffer = NULL;
- if(asprintf(&buffer,
- "Set-Cookie: user_id=%u; Path=/; Max-Age=%u\r\n",
- num, httpsettings.session_timeout) < 0)
+ if(snprintf(pi_hole_extra_headers, sizeof(pi_hole_extra_headers),
+ "Set-Cookie: user_id=%u; Path=/; Max-Age=%u\r\n",
+ num, httpsettings.session_timeout) < 0)
{
return send_json_error(conn, 500, "internal_error", "Internal server error", NULL);
}
- my_set_cookie_header(conn, buffer);
- free(buffer);
if(config.debug & DEBUG_API)
{
@@ -201,15 +198,12 @@ int api_auth(struct mg_connection *conn)
if(config.debug & DEBUG_API)
logg("API Authentification: OK, localhost does not need auth.");
// We still have to send a cookie for the web interface to be happy
- char *buffer = NULL;
- if(asprintf(&buffer,
+ if(snprintf(pi_hole_extra_headers, sizeof(pi_hole_extra_headers),
"Set-Cookie: user_id=%u; Path=/; Max-Age=%u\r\n",
API_MAX_CLIENTS, API_SESSION_EXPIRE) < 0)
{
return send_json_error(conn, 500, "internal_error", "Internal server error", NULL);
}
- my_set_cookie_header(conn, buffer);
- free(buffer);
}
if(user_id > -1 && method == HTTP_GET)
{
@@ -219,15 +213,12 @@ int api_auth(struct mg_connection *conn)
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_REF_STR(json, "status", "success");
// Ten minutes validity
- char *buffer = NULL;
- if(asprintf(&buffer,
+ if(snprintf(pi_hole_extra_headers, sizeof(pi_hole_extra_headers),
"Set-Cookie: user_id=%u; Path=/; Max-Age=%u\r\n",
user_id, API_SESSION_EXPIRE) < 0)
{
return send_json_error(conn, 500, "internal_error", "Internal server error", NULL);
}
- my_set_cookie_header(conn, buffer);
- free(buffer);
return send_json_success(conn);
}
@@ -242,14 +233,12 @@ int api_auth(struct mg_connection *conn)
free(auth_data[user_id].remote_addr);
auth_data[user_id].remote_addr = NULL;
- const char *buffer = "Set-Cookie: user_id=deleted; Path=/; Max-Age=-1\r\n";
- my_set_cookie_header(conn, buffer);
+ strncpy(pi_hole_extra_headers, "Set-Cookie: user_id=deleted; Path=/; Max-Age=-1\r\n", sizeof(pi_hole_extra_headers));
return send_json_success(conn);
}
else
{
- const char *buffer = "Set-Cookie: user_id=deleted; Path=/; Max-Age=-1\r\n";
- my_set_cookie_header(conn, buffer);
+ strncpy(pi_hole_extra_headers, "Set-Cookie: user_id=deleted; Path=/; Max-Age=-1\r\n", sizeof(pi_hole_extra_headers));
return send_json_unauthorized(conn);
}
}
diff --git a/src/api/settings.c b/src/api/settings.c
index f8bda43a..cac6525b 100644
--- a/src/api/settings.c
+++ b/src/api/settings.c
@@ -8,7 +8,7 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
-#include "FTL.h"
+#include "../FTL.h"
#include "../webserver/http-common.h"
#include "../webserver/json_macros.h"
#include "routes.h"
diff --git a/src/api/version.c b/src/api/version.c
index 871239a3..f72b24fa 100644
--- a/src/api/version.c
+++ b/src/api/version.c
@@ -8,13 +8,13 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
-#include "FTL.h"
+#include "../FTL.h"
#include "../webserver/http-common.h"
#include "../webserver/json_macros.h"
#include "routes.h"
// get_FTL_version()
-#include "log.h"
-#include "version.h"
+#include "../log.h"
+#include "../version.h"
int api_version(struct mg_connection *conn)
{
diff --git a/src/civetweb/civetweb.c b/src/civetweb/civetweb.c
index d34a7a24..ad0eb551 100644
--- a/src/civetweb/civetweb.c
+++ b/src/civetweb/civetweb.c
@@ -50,7 +50,7 @@
#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
#endif
#if !defined(_WIN32_WINNT) /* defined for tdm-gcc so we can use getnameinfo */
-#define _WIN32_WINNT 0x0501
+#define _WIN32_WINNT 0x0502
#endif
#else
#if !defined(_GNU_SOURCE)
@@ -59,7 +59,7 @@
#if defined(__linux__) && !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */
#endif
-#if defined(__LSB_VERSION__)
+#if defined(__LSB_VERSION__) || defined(__sun)
#define NEED_TIMEGM
#define NO_THREAD_NAME
#endif
@@ -205,7 +205,7 @@ mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
#define ZEPHYR_STACK_SIZE USE_STACK_SIZE
#else
-#define ZEPHYR_STACK_SIZE 8096
+#define ZEPHYR_STACK_SIZE (1024 * 16)
#endif
K_THREAD_STACK_DEFINE(civetweb_main_stack, ZEPHYR_STACK_SIZE);
@@ -234,8 +234,8 @@ static void DEBUG_TRACE_FUNC(const char *func,
DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__)
#define NEED_DEBUG_TRACE_FUNC
-#ifndef DEBUG_TRACE_STREAM
-# define DEBUG_TRACE_STREAM stdout
+#if !defined(DEBUG_TRACE_STREAM)
+#define DEBUG_TRACE_STREAM stdout
#endif
#else
@@ -433,6 +433,20 @@ _civet_safe_clock_gettime(int clk_id, struct timespec *t)
#endif
+#if !defined(_WIN32)
+/* Unix might return different error codes indicating to try again.
+ * For Linux EAGAIN==EWOULDBLOCK, maybe EAGAIN!=EWOULDBLOCK is history from
+ * decades ago, but better check both and let the compile optimize it. */
+#define ERROR_TRY_AGAIN(err) \
+ (((err) == EAGAIN) || ((err) == EWOULDBLOCK) || ((err) == EINTR))
+#endif
+
+#if defined(USE_ZLIB)
+#include "zconf.h"
+#include "zlib.h"
+#endif
+
+
/********************************************************************/
/* CivetWeb configuration defines */
/********************************************************************/
@@ -570,30 +584,38 @@ typedef long off_t;
#define ERRNO ((int)(GetLastError()))
#define NO_SOCKLEN_T
+
#if defined(_WIN64) || defined(__MINGW64__)
#if !defined(SSL_LIB)
+
#if defined(OPENSSL_API_1_1)
#define SSL_LIB "libssl-1_1-x64.dll"
-#else /* OPENSSL_API_1_1 */
-#define SSL_LIB "ssleay64.dll"
-#endif /* OPENSSL_API_1_1 */
-#endif /* SSL_LIB */
-#if !defined(CRYPTO_LIB)
-#if defined(OPENSSL_API_1_1)
#define CRYPTO_LIB "libcrypto-1_1-x64.dll"
-#else /* OPENSSL_API_1_1 */
-#define CRYPTO_LIB "libeay64.dll"
#endif /* OPENSSL_API_1_1 */
-#endif /* CRYPTO_LIB */
+
+#if defined(OPENSSL_API_1_0)
+#define SSL_LIB "ssleay64.dll"
+#define CRYPTO_LIB "libeay64.dll"
+#endif /* OPENSSL_API_1_0 */
+
+#endif
#else /* defined(_WIN64) || defined(__MINGW64__) */
#if !defined(SSL_LIB)
+
+#if defined(OPENSSL_API_1_1)
+#define SSL_LIB "libssl-1_1.dll"
+#define CRYPTO_LIB "libcrypto-1_1.dll"
+#endif /* OPENSSL_API_1_1 */
+
+#if defined(OPENSSL_API_1_0)
#define SSL_LIB "ssleay32.dll"
-#endif /* SSL_LIB */
-#if !defined(CRYPTO_LIB)
#define CRYPTO_LIB "libeay32.dll"
-#endif /* CRYPTO_LIB */
+#endif /* OPENSSL_API_1_0 */
+
+#endif /* SSL_LIB */
#endif /* defined(_WIN64) || defined(__MINGW64__) */
+
#define O_NONBLOCK (0)
#if !defined(W_OK)
#define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */
@@ -789,8 +811,7 @@ static void path_to_unicode(const struct mg_connection *conn,
struct mg_file;
-static const char *
-mg_fgets(char *buf, size_t size, struct mg_file *filep, char **p);
+static const char *mg_fgets(char *buf, size_t size, struct mg_file *filep);
/* POSIX dirent interface */
@@ -1105,16 +1126,16 @@ gmtime_s(const time_t *ptime, struct tm *ptm)
}
-static int mg_atomic_inc(volatile int *addr);
-static struct tm tm_array[MAX_WORKER_THREADS];
-static int tm_index = 0;
+static ptrdiff_t mg_atomic_inc(volatile ptrdiff_t *addr);
+static struct tm tm_array[MAX_WORKER_THREADS]; /* Must be 2^n */
+static volatile ptrdiff_t tm_index = 0;
FUNCTION_MAY_BE_UNUSED
static struct tm *
localtime(const time_t *ptime)
{
- int i = mg_atomic_inc(&tm_index) % (sizeof(tm_array) / sizeof(tm_array[0]));
+ ptrdiff_t i = mg_atomic_inc(&tm_index) % ARRAY_SIZE(tm_array);
return localtime_s(ptime, tm_array + i);
}
@@ -1123,7 +1144,7 @@ FUNCTION_MAY_BE_UNUSED
static struct tm *
gmtime(const time_t *ptime)
{
- int i = mg_atomic_inc(&tm_index) % ARRAY_SIZE(tm_array);
+ ptrdiff_t i = mg_atomic_inc(&tm_index) % ARRAY_SIZE(tm_array);
return gmtime_s(ptime, tm_array + i);
}
@@ -1236,16 +1257,27 @@ mg_global_unlock(void)
}
+#if defined(_WIN64)
+mg_static_assert(SIZE_MAX == 0xFFFFFFFFFFFFFFFFu, "Mismatch for atomic types");
+#elif defined(_WIN32)
+mg_static_assert(SIZE_MAX == 0xFFFFFFFFu, "Mismatch for atomic types");
+#endif
+
+
+/* Atomic functions working on ptrdiff_t ("signed size_t").
+ * Operations: Increment, Decrement, Add, Maximum.
+ * Up to size_t, they do not an atomic "load" operation.
+ */
FUNCTION_MAY_BE_UNUSED
-static int
-mg_atomic_inc(volatile int *addr)
+static ptrdiff_t
+mg_atomic_inc(volatile ptrdiff_t *addr)
{
- int ret;
-#if defined(_WIN32) && !defined(NO_ATOMICS)
- /* 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);
+ ptrdiff_t ret;
+
+#if defined(_WIN64) && !defined(NO_ATOMICS)
+ ret = InterlockedIncrement64(addr);
+#elif defined(_WIN32) && !defined(NO_ATOMICS)
+ ret = InterlockedIncrement(addr);
#elif defined(__GNUC__) \
&& ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
&& !defined(NO_ATOMICS)
@@ -1260,15 +1292,15 @@ mg_atomic_inc(volatile int *addr)
FUNCTION_MAY_BE_UNUSED
-static int
-mg_atomic_dec(volatile int *addr)
+static ptrdiff_t
+mg_atomic_dec(volatile ptrdiff_t *addr)
{
- int ret;
-#if defined(_WIN32) && !defined(NO_ATOMICS)
- /* 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);
+ ptrdiff_t ret;
+
+#if defined(_WIN64) && !defined(NO_ATOMICS)
+ ret = InterlockedDecrement64(addr);
+#elif defined(_WIN32) && !defined(NO_ATOMICS)
+ ret = InterlockedDecrement(addr);
#elif defined(__GNUC__) \
&& ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
&& !defined(NO_ATOMICS)
@@ -1283,12 +1315,95 @@ mg_atomic_dec(volatile int *addr)
#if defined(USE_SERVER_STATS)
-static int64_t
-mg_atomic_add(volatile int64_t *addr, int64_t value)
+static ptrdiff_t
+mg_atomic_add(volatile ptrdiff_t *addr, ptrdiff_t value)
{
- int64_t ret;
+ ptrdiff_t ret;
+
#if defined(_WIN64) && !defined(NO_ATOMICS)
ret = InterlockedAdd64(addr, value);
+#elif defined(_WIN32) && !defined(NO_ATOMICS)
+ ret = InterlockedExchangeAdd(addr, value) + value;
+#elif defined(__GNUC__) \
+ && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
+ && !defined(NO_ATOMICS)
+ ret = __sync_add_and_fetch(addr, value);
+#else
+ mg_global_lock();
+ *addr += value;
+ ret = (*addr);
+ mg_global_unlock();
+#endif
+ return ret;
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static ptrdiff_t
+mg_atomic_compare_and_swap(volatile ptrdiff_t *addr,
+ ptrdiff_t oldval,
+ ptrdiff_t newval)
+{
+ ptrdiff_t ret;
+
+#if defined(_WIN64) && !defined(NO_ATOMICS)
+ ret = InterlockedCompareExchange64(addr, newval, oldval);
+#elif defined(_WIN32) && !defined(NO_ATOMICS)
+ ret = InterlockedCompareExchange(addr, newval, oldval);
+#elif defined(__GNUC__) \
+ && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
+ && !defined(NO_ATOMICS)
+ ret = __sync_val_compare_and_swap(addr, oldval, newval);
+#else
+ mg_global_lock();
+ ret = *addr;
+ if ((ret != newval) && (ret == oldval)) {
+ *addr = newval;
+ }
+ mg_global_unlock();
+#endif
+ return ret;
+}
+
+
+static void
+mg_atomic_max(volatile ptrdiff_t *addr, ptrdiff_t value)
+{
+ register ptrdiff_t tmp = *addr;
+
+#if defined(_WIN64) && !defined(NO_ATOMICS)
+ while (tmp < value) {
+ tmp = InterlockedCompareExchange64(addr, value, tmp);
+ }
+#elif defined(_WIN32) && !defined(NO_ATOMICS)
+ while (tmp < value) {
+ tmp = InterlockedCompareExchange(addr, value, tmp);
+ }
+#elif defined(__GNUC__) \
+ && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
+ && !defined(NO_ATOMICS)
+ while (tmp < value) {
+ tmp = __sync_val_compare_and_swap(addr, tmp, value);
+ }
+#else
+ mg_global_lock();
+ if (*addr < value) {
+ *addr = value;
+ }
+ mg_global_unlock();
+#endif
+}
+
+
+static int64_t
+mg_atomic_add64(volatile int64_t *addr, int64_t value)
+{
+ int64_t ret;
+
+#if defined(_WIN64) && !defined(NO_ATOMICS)
+ ret = InterlockedAdd64(addr, value);
+#elif defined(_WIN32) && !defined(NO_ATOMICS)
+ ret = InterlockedExchangeAdd64(addr, value) + value;
#elif defined(__GNUC__) \
&& ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
&& !defined(NO_ATOMICS)
@@ -1317,9 +1432,9 @@ mg_atomic_add(volatile int64_t *addr, int64_t value)
#if defined(USE_SERVER_STATS)
struct mg_memory_stat {
- volatile int64_t totalMemUsed;
- volatile int64_t maxMemUsed;
- volatile int blockCount;
+ volatile ptrdiff_t totalMemUsed;
+ volatile ptrdiff_t maxMemUsed;
+ volatile ptrdiff_t blockCount;
};
@@ -1344,12 +1459,8 @@ mg_malloc_ex(size_t size,
#endif
if (data) {
- int64_t mmem = mg_atomic_add(&mstat->totalMemUsed, (int64_t)size);
- if (mmem > mstat->maxMemUsed) {
- /* could use atomic compare exchange, but this
- * seems overkill for statistics data */
- mstat->maxMemUsed = mmem;
- }
+ ptrdiff_t mmem = mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)size);
+ mg_atomic_max(&mstat->maxMemUsed, mmem);
mg_atomic_inc(&mstat->blockCount);
((uintptr_t *)data)[0] = size;
@@ -1366,11 +1477,7 @@ mg_malloc_ex(size_t size,
(unsigned long)mstat->blockCount,
file,
line);
-#if defined(_WIN32)
- OutputDebugStringA(mallocStr);
-#else
DEBUG_TRACE("%s", mallocStr);
-#endif
#endif
return memory;
@@ -1396,9 +1503,6 @@ mg_calloc_ex(size_t count,
static void
mg_free_ex(void *memory, const char *file, unsigned line)
{
- void *data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t));
-
-
#if defined(MEMORY_DEBUGGING)
char mallocStr[256];
#else
@@ -1407,11 +1511,13 @@ mg_free_ex(void *memory, const char *file, unsigned line)
#endif
if (memory) {
+ void *data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t));
uintptr_t size = ((uintptr_t *)data)[0];
struct mg_memory_stat *mstat =
(struct mg_memory_stat *)(((uintptr_t *)data)[1]);
- mg_atomic_add(&mstat->totalMemUsed, -(int64_t)size);
+ mg_atomic_add(&mstat->totalMemUsed, -(ptrdiff_t)size);
mg_atomic_dec(&mstat->blockCount);
+
#if defined(MEMORY_DEBUGGING)
sprintf(mallocStr,
"MEM: %p %5lu free %7lu %4lu --- %s:%u\n",
@@ -1421,11 +1527,7 @@ mg_free_ex(void *memory, const char *file, unsigned line)
(unsigned long)mstat->blockCount,
file,
line);
-#if defined(_WIN32)
- OutputDebugStringA(mallocStr);
-#else
DEBUG_TRACE("%s", mallocStr);
-#endif
#endif
free(data);
}
@@ -1460,7 +1562,7 @@ mg_realloc_ex(void *memory,
_realloc = realloc(data, newsize + 2 * sizeof(uintptr_t));
if (_realloc) {
data = _realloc;
- mg_atomic_add(&mstat->totalMemUsed, -(int64_t)oldsize);
+ mg_atomic_add(&mstat->totalMemUsed, -(ptrdiff_t)oldsize);
#if defined(MEMORY_DEBUGGING)
sprintf(mallocStr,
"MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n",
@@ -1470,13 +1572,10 @@ mg_realloc_ex(void *memory,
(unsigned long)mstat->blockCount,
file,
line);
-#if defined(_WIN32)
- OutputDebugStringA(mallocStr);
-#else
DEBUG_TRACE("%s", mallocStr);
#endif
-#endif
- mg_atomic_add(&mstat->totalMemUsed, (int64_t)newsize);
+ mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)newsize);
+
#if defined(MEMORY_DEBUGGING)
sprintf(mallocStr,
"MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n",
@@ -1486,21 +1585,13 @@ mg_realloc_ex(void *memory,
(unsigned long)mstat->blockCount,
file,
line);
-#if defined(_WIN32)
- OutputDebugStringA(mallocStr);
-#else
DEBUG_TRACE("%s", mallocStr);
-#endif
#endif
*(uintptr_t *)data = newsize;
data = (void *)(((char *)data) + 2 * sizeof(uintptr_t));
} else {
#if defined(MEMORY_DEBUGGING)
-#if defined(_WIN32)
- OutputDebugStringA("MEM: realloc failed\n");
-#else
DEBUG_TRACE("%s", "MEM: realloc failed\n");
-#endif
#endif
return _realloc;
}
@@ -1517,6 +1608,7 @@ mg_realloc_ex(void *memory,
return data;
}
+
#define mg_malloc(a) mg_malloc_ex(a, NULL, __FILE__, __LINE__)
#define mg_calloc(a, b) mg_calloc_ex(a, b, NULL, __FILE__, __LINE__)
#define mg_realloc(a, b) mg_realloc_ex(a, b, NULL, __FILE__, __LINE__)
@@ -1526,8 +1618,10 @@ mg_realloc_ex(void *memory,
#define mg_calloc_ctx(a, b, c) mg_calloc_ex(a, b, c, __FILE__, __LINE__)
#define mg_realloc_ctx(a, b, c) mg_realloc_ex(a, b, c, __FILE__, __LINE__)
+
#else /* USE_SERVER_STATS */
+
static __inline void *
mg_malloc(size_t a)
{
@@ -1611,10 +1705,20 @@ static int mg_init_library_called = 0;
#if !defined(NO_SSL)
static int mg_ssl_initialized = 0;
+
+
+/* TODO: Selection of SSL library and version */
+#if !defined(OPENSSL_API_1_0) && !defined(OPENSSL_API_1_1)
+#error "Please define OPENSSL_API_1_0 or OPENSSL_API_1_1"
#endif
+#if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_1_1)
+#error "Multiple OPENSSL_API versions defined"
+#endif
+#endif /* NO_SSL */
+
static pthread_key_t sTlsKey; /* Thread local storage index */
-static int thread_idx_max = 0;
+static volatile ptrdiff_t thread_idx_max = 0;
#if defined(MG_LEGACY_INTERFACE)
#define MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE
@@ -1628,6 +1732,7 @@ struct mg_workerTLS {
HANDLE pthread_cond_helper_mutex;
struct mg_workerTLS *next_waiting_thread;
#endif
+ const char *alpn_proto;
#if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE)
char txtbuf[4];
#endif
@@ -1731,36 +1836,27 @@ static void
DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...)
{
va_list args;
- uint64_t nsnow;
- static uint64_t nslast;
struct timespec tsnow;
/* Get some operating system independent thread id */
unsigned long thread_id = mg_current_thread_id();
clock_gettime(CLOCK_REALTIME, &tsnow);
- nsnow = ((uint64_t)tsnow.tv_sec) * ((uint64_t)1000000000)
- + ((uint64_t)tsnow.tv_nsec);
-
- if (!nslast) {
- nslast = nsnow;
- }
flockfile(DEBUG_TRACE_STREAM);
- fprintf( DEBUG_TRACE_STREAM,"*** %lu.%09lu %12" INT64_FMT " %lu %s:%u: ",
- (unsigned long)tsnow.tv_sec,
- (unsigned long)tsnow.tv_nsec,
- nsnow - nslast,
- thread_id,
- func,
- line);
+ fprintf(DEBUG_TRACE_STREAM,
+ "*** %lu.%09lu %lu %s:%u: ",
+ (unsigned long)tsnow.tv_sec,
+ (unsigned long)tsnow.tv_nsec,
+ thread_id,
+ func,
+ line);
va_start(args, fmt);
vfprintf(DEBUG_TRACE_STREAM, fmt, args);
va_end(args);
putc('\n', DEBUG_TRACE_STREAM);
fflush(DEBUG_TRACE_STREAM);
funlockfile(DEBUG_TRACE_STREAM);
- nslast = nsnow;
}
#endif /* NEED_DEBUG_TRACE_FUNC */
@@ -1818,16 +1914,21 @@ typedef struct SSL_CTX SSL_CTX;
#define ENGINE_cleanup() ((void)0)
#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+
/* If OpenSSL headers are included, automatically select the API version */
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
#if !defined(OPENSSL_API_1_1)
#define OPENSSL_API_1_1
#endif
#define OPENSSL_REMOVE_THREAD_STATE()
#else
+#if !defined(OPENSSL_API_1_0)
+#define OPENSSL_API_1_0
+#endif
#define OPENSSL_REMOVE_THREAD_STATE() ERR_remove_thread_state(NULL)
#endif
+
#else
/* SSL loaded dynamically from DLL.
@@ -1893,9 +1994,21 @@ typedef struct x509 X509;
#define SSL_TLSEXT_ERR_ALERT_FATAL (2)
#define SSL_TLSEXT_ERR_NOACK (3)
+#define SSL_SESS_CACHE_BOTH (3)
+
+enum ssl_func_category {
+ TLS_Mandatory, /* required for HTTPS */
+ TLS_ALPN, /* required for Application Layer Protocol Negotiation */
+ TLS_END_OF_LIST
+};
+
+/* Check if all TLS functions/features are available */
+static int tls_feature_missing[TLS_END_OF_LIST] = {0};
+
struct ssl_func {
- const char *name; /* SSL function name */
- void (*ptr)(void); /* Function pointer */
+ const char *name; /* SSL function name */
+ enum ssl_func_category required; /* Mandatory or optional */
+ void (*ptr)(void); /* Function pointer */
};
@@ -1961,6 +2074,25 @@ struct ssl_func {
(*(const char *(*)(const SSL *, int type))ssl_sw[36].ptr)
#define SSL_set_SSL_CTX (*(SSL_CTX * (*)(SSL *, SSL_CTX *)) ssl_sw[37].ptr)
#define SSL_ctrl (*(long (*)(SSL *, int, long, void *))ssl_sw[38].ptr)
+#define SSL_CTX_set_alpn_protos \
+ (*(int (*)(SSL_CTX *, const unsigned char *, unsigned))ssl_sw[39].ptr)
+typedef int (*tSSL_alpn_select_cb)(SSL *ssl,
+ const unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen,
+ void *arg);
+#define SSL_CTX_set_alpn_select_cb \
+ (*(void (*)(SSL_CTX *, tSSL_alpn_select_cb, void *))ssl_sw[40].ptr)
+typedef int (*tSSL_next_protos_advertised_cb)(SSL *ssl,
+ const unsigned char **out,
+ unsigned int *outlen,
+ void *arg);
+#define SSL_CTX_set_next_protos_advertised_cb \
+ (*(void (*)(SSL_CTX *, tSSL_next_protos_advertised_cb, void *))ssl_sw[41] \
+ .ptr)
+
+#define SSL_CTX_set_timeout (*(long (*)(SSL_CTX *, long))ssl_sw[42].ptr)
#define SSL_CTX_clear_options(ctx, op) \
SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL)
@@ -1974,8 +2106,6 @@ struct ssl_func {
SSL_CTX_callback_ctrl(ctx, \
SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \
(void (*)(void))cb)
-#define SSL_CTX_set_tlsext_servername_arg(ctx, arg) \
- SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, (void *)arg)
#define SSL_set_tlsext_host_name(ctx, arg) \
SSL_ctrl(ctx, SSL_CTRL_SET_TLSEXT_HOSTNAME, 0, (void *)arg)
@@ -1985,6 +2115,11 @@ struct ssl_func {
#define SSL_set_app_data(s, arg) (SSL_set_ex_data(s, 0, (char *)arg))
#define SSL_get_app_data(s) (SSL_get_ex_data(s, 0))
+#define SSL_CTX_sess_set_cache_size(ctx, size) SSL_CTX_ctrl(ctx, 42, size, NULL)
+#define SSL_CTX_set_session_cache_mode(ctx, mode) \
+ SSL_CTX_ctrl(ctx, 44, mode, NULL)
+
+
#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[0].ptr)
#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[1].ptr)
#define CONF_modules_unload (*(void (*)(int))crypto_sw[2].ptr)
@@ -2017,67 +2152,73 @@ struct ssl_func {
* 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},
- {"TLS_server_method", NULL},
- {"OPENSSL_init_ssl", 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_CTX_use_certificate_chain_file", NULL},
- {"TLS_client_method", NULL},
- {"SSL_pending", NULL},
- {"SSL_CTX_set_verify", NULL},
- {"SSL_shutdown", NULL},
- {"SSL_CTX_load_verify_locations", NULL},
- {"SSL_CTX_set_default_verify_paths", NULL},
- {"SSL_CTX_set_verify_depth", NULL},
- {"SSL_get_peer_certificate", NULL},
- {"SSL_get_version", NULL},
- {"SSL_get_current_cipher", NULL},
- {"SSL_CIPHER_get_name", NULL},
- {"SSL_CTX_check_private_key", NULL},
- {"SSL_CTX_set_session_id_context", NULL},
- {"SSL_CTX_ctrl", NULL},
- {"SSL_CTX_set_cipher_list", NULL},
- {"SSL_CTX_set_options", NULL},
- {"SSL_CTX_set_info_callback", NULL},
- {"SSL_get_ex_data", NULL},
- {"SSL_set_ex_data", NULL},
- {"SSL_CTX_callback_ctrl", NULL},
- {"SSL_get_servername", NULL},
- {"SSL_set_SSL_CTX", NULL},
- {"SSL_ctrl", NULL},
- {NULL, NULL}};
+static struct ssl_func ssl_sw[] = {
+ {"SSL_free", TLS_Mandatory, NULL},
+ {"SSL_accept", TLS_Mandatory, NULL},
+ {"SSL_connect", TLS_Mandatory, NULL},
+ {"SSL_read", TLS_Mandatory, NULL},
+ {"SSL_write", TLS_Mandatory, NULL},
+ {"SSL_get_error", TLS_Mandatory, NULL},
+ {"SSL_set_fd", TLS_Mandatory, NULL},
+ {"SSL_new", TLS_Mandatory, NULL},
+ {"SSL_CTX_new", TLS_Mandatory, NULL},
+ {"TLS_server_method", TLS_Mandatory, NULL},
+ {"OPENSSL_init_ssl", TLS_Mandatory, NULL},
+ {"SSL_CTX_use_PrivateKey_file", TLS_Mandatory, NULL},
+ {"SSL_CTX_use_certificate_file", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_default_passwd_cb", TLS_Mandatory, NULL},
+ {"SSL_CTX_free", TLS_Mandatory, NULL},
+ {"SSL_CTX_use_certificate_chain_file", TLS_Mandatory, NULL},
+ {"TLS_client_method", TLS_Mandatory, NULL},
+ {"SSL_pending", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_verify", TLS_Mandatory, NULL},
+ {"SSL_shutdown", TLS_Mandatory, NULL},
+ {"SSL_CTX_load_verify_locations", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_default_verify_paths", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_verify_depth", TLS_Mandatory, NULL},
+ {"SSL_get_peer_certificate", TLS_Mandatory, NULL},
+ {"SSL_get_version", TLS_Mandatory, NULL},
+ {"SSL_get_current_cipher", TLS_Mandatory, NULL},
+ {"SSL_CIPHER_get_name", TLS_Mandatory, NULL},
+ {"SSL_CTX_check_private_key", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_session_id_context", TLS_Mandatory, NULL},
+ {"SSL_CTX_ctrl", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_cipher_list", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_options", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_info_callback", TLS_Mandatory, NULL},
+ {"SSL_get_ex_data", TLS_Mandatory, NULL},
+ {"SSL_set_ex_data", TLS_Mandatory, NULL},
+ {"SSL_CTX_callback_ctrl", TLS_Mandatory, NULL},
+ {"SSL_get_servername", TLS_Mandatory, NULL},
+ {"SSL_set_SSL_CTX", TLS_Mandatory, NULL},
+ {"SSL_ctrl", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_alpn_protos", TLS_ALPN, NULL},
+ {"SSL_CTX_set_alpn_select_cb", TLS_ALPN, NULL},
+ {"SSL_CTX_set_next_protos_advertised_cb", TLS_ALPN, NULL},
+ {"SSL_CTX_set_timeout", TLS_Mandatory, NULL},
+ {NULL, TLS_END_OF_LIST, NULL}};
/* Similar array as ssl_sw. These functions could be located in different
* lib. */
-static struct ssl_func crypto_sw[] = {{"ERR_get_error", NULL},
- {"ERR_error_string", NULL},
- {"CONF_modules_unload", NULL},
- {"X509_free", NULL},
- {"X509_get_subject_name", NULL},
- {"X509_get_issuer_name", NULL},
- {"X509_NAME_oneline", NULL},
- {"X509_get_serialNumber", NULL},
- {"EVP_get_digestbyname", NULL},
- {"EVP_Digest", NULL},
- {"i2d_X509", NULL},
- {"BN_bn2hex", NULL},
- {"ASN1_INTEGER_to_BN", NULL},
- {"BN_free", NULL},
- {"CRYPTO_free", NULL},
- {"ERR_clear_error", NULL},
- {NULL, NULL}};
+static struct ssl_func crypto_sw[] = {
+ {"ERR_get_error", TLS_Mandatory, NULL},
+ {"ERR_error_string", TLS_Mandatory, NULL},
+ {"CONF_modules_unload", TLS_Mandatory, NULL},
+ {"X509_free", TLS_Mandatory, NULL},
+ {"X509_get_subject_name", TLS_Mandatory, NULL},
+ {"X509_get_issuer_name", TLS_Mandatory, NULL},
+ {"X509_NAME_oneline", TLS_Mandatory, NULL},
+ {"X509_get_serialNumber", TLS_Mandatory, NULL},
+ {"EVP_get_digestbyname", TLS_Mandatory, NULL},
+ {"EVP_Digest", TLS_Mandatory, NULL},
+ {"i2d_X509", TLS_Mandatory, NULL},
+ {"BN_bn2hex", TLS_Mandatory, NULL},
+ {"ASN1_INTEGER_to_BN", TLS_Mandatory, NULL},
+ {"BN_free", TLS_Mandatory, NULL},
+ {"CRYPTO_free", TLS_Mandatory, NULL},
+ {"ERR_clear_error", TLS_Mandatory, NULL},
+ {NULL, TLS_END_OF_LIST, NULL}};
#else
#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr)
@@ -2135,6 +2276,26 @@ static struct ssl_func crypto_sw[] = {{"ERR_get_error", NULL},
(*(const char *(*)(const SSL *, int type))ssl_sw[36].ptr)
#define SSL_set_SSL_CTX (*(SSL_CTX * (*)(SSL *, SSL_CTX *)) ssl_sw[37].ptr)
#define SSL_ctrl (*(long (*)(SSL *, int, long, void *))ssl_sw[38].ptr)
+#define SSL_CTX_set_alpn_protos \
+ (*(int (*)(SSL_CTX *, const unsigned char *, unsigned))ssl_sw[39].ptr)
+typedef int (*tSSL_alpn_select_cb)(SSL *ssl,
+ const unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen,
+ void *arg);
+#define SSL_CTX_set_alpn_select_cb \
+ (*(void (*)(SSL_CTX *, tSSL_alpn_select_cb, void *))ssl_sw[40].ptr)
+typedef int (*tSSL_next_protos_advertised_cb)(SSL *ssl,
+ const unsigned char **out,
+ unsigned int *outlen,
+ void *arg);
+#define SSL_CTX_set_next_protos_advertised_cb \
+ (*(void (*)(SSL_CTX *, tSSL_next_protos_advertised_cb, void *))ssl_sw[41] \
+ .ptr)
+
+#define SSL_CTX_set_timeout (*(long (*)(SSL_CTX *, long))ssl_sw[42].ptr)
+
#define SSL_CTX_set_options(ctx, op) \
SSL_CTX_ctrl((ctx), SSL_CTRL_OPTIONS, (op), NULL)
@@ -2150,8 +2311,6 @@ static struct ssl_func crypto_sw[] = {{"ERR_get_error", NULL},
SSL_CTX_callback_ctrl(ctx, \
SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \
(void (*)(void))cb)
-#define SSL_CTX_set_tlsext_servername_arg(ctx, arg) \
- SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, (void *)arg)
#define SSL_set_tlsext_host_name(ctx, arg) \
SSL_ctrl(ctx, SSL_CTRL_SET_TLSEXT_HOSTNAME, 0, (void *)arg)
@@ -2161,6 +2320,11 @@ static struct ssl_func crypto_sw[] = {{"ERR_get_error", NULL},
#define SSL_set_app_data(s, arg) (SSL_set_ex_data(s, 0, (char *)arg))
#define SSL_get_app_data(s) (SSL_get_ex_data(s, 0))
+#define SSL_CTX_sess_set_cache_size(ctx, size) SSL_CTX_ctrl(ctx, 42, size, NULL)
+#define SSL_CTX_set_session_cache_mode(ctx, mode) \
+ SSL_CTX_ctrl(ctx, 44, mode, NULL)
+
+
#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)
@@ -2208,76 +2372,82 @@ static struct ssl_func crypto_sw[] = {{"ERR_get_error", 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},
- {"SSL_CTX_load_verify_locations", NULL},
- {"SSL_CTX_set_default_verify_paths", NULL},
- {"SSL_CTX_set_verify_depth", NULL},
- {"SSL_get_peer_certificate", NULL},
- {"SSL_get_version", NULL},
- {"SSL_get_current_cipher", NULL},
- {"SSL_CIPHER_get_name", NULL},
- {"SSL_CTX_check_private_key", NULL},
- {"SSL_CTX_set_session_id_context", NULL},
- {"SSL_CTX_ctrl", NULL},
- {"SSL_CTX_set_cipher_list", NULL},
- {"SSL_CTX_set_info_callback", NULL},
- {"SSL_get_ex_data", NULL},
- {"SSL_set_ex_data", NULL},
- {"SSL_CTX_callback_ctrl", NULL},
- {"SSL_get_servername", NULL},
- {"SSL_set_SSL_CTX", NULL},
- {"SSL_ctrl", NULL},
- {NULL, NULL}};
+static struct ssl_func ssl_sw[] = {
+ {"SSL_free", TLS_Mandatory, NULL},
+ {"SSL_accept", TLS_Mandatory, NULL},
+ {"SSL_connect", TLS_Mandatory, NULL},
+ {"SSL_read", TLS_Mandatory, NULL},
+ {"SSL_write", TLS_Mandatory, NULL},
+ {"SSL_get_error", TLS_Mandatory, NULL},
+ {"SSL_set_fd", TLS_Mandatory, NULL},
+ {"SSL_new", TLS_Mandatory, NULL},
+ {"SSL_CTX_new", TLS_Mandatory, NULL},
+ {"SSLv23_server_method", TLS_Mandatory, NULL},
+ {"SSL_library_init", TLS_Mandatory, NULL},
+ {"SSL_CTX_use_PrivateKey_file", TLS_Mandatory, NULL},
+ {"SSL_CTX_use_certificate_file", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_default_passwd_cb", TLS_Mandatory, NULL},
+ {"SSL_CTX_free", TLS_Mandatory, NULL},
+ {"SSL_load_error_strings", TLS_Mandatory, NULL},
+ {"SSL_CTX_use_certificate_chain_file", TLS_Mandatory, NULL},
+ {"SSLv23_client_method", TLS_Mandatory, NULL},
+ {"SSL_pending", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_verify", TLS_Mandatory, NULL},
+ {"SSL_shutdown", TLS_Mandatory, NULL},
+ {"SSL_CTX_load_verify_locations", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_default_verify_paths", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_verify_depth", TLS_Mandatory, NULL},
+ {"SSL_get_peer_certificate", TLS_Mandatory, NULL},
+ {"SSL_get_version", TLS_Mandatory, NULL},
+ {"SSL_get_current_cipher", TLS_Mandatory, NULL},
+ {"SSL_CIPHER_get_name", TLS_Mandatory, NULL},
+ {"SSL_CTX_check_private_key", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_session_id_context", TLS_Mandatory, NULL},
+ {"SSL_CTX_ctrl", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_cipher_list", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_info_callback", TLS_Mandatory, NULL},
+ {"SSL_get_ex_data", TLS_Mandatory, NULL},
+ {"SSL_set_ex_data", TLS_Mandatory, NULL},
+ {"SSL_CTX_callback_ctrl", TLS_Mandatory, NULL},
+ {"SSL_get_servername", TLS_Mandatory, NULL},
+ {"SSL_set_SSL_CTX", TLS_Mandatory, NULL},
+ {"SSL_ctrl", TLS_Mandatory, NULL},
+ {"SSL_CTX_set_alpn_protos", TLS_ALPN, NULL},
+ {"SSL_CTX_set_alpn_select_cb", TLS_ALPN, NULL},
+ {"SSL_CTX_set_next_protos_advertised_cb", TLS_ALPN, NULL},
+ {"SSL_CTX_set_timeout", TLS_Mandatory, NULL},
+ {NULL, TLS_END_OF_LIST, NULL}};
/* Similar array as ssl_sw. These functions could be located in different
* lib. */
-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},
- {"ERR_remove_state", NULL},
- {"ERR_free_strings", NULL},
- {"ENGINE_cleanup", NULL},
- {"CONF_modules_unload", NULL},
- {"CRYPTO_cleanup_all_ex_data", NULL},
- {"EVP_cleanup", NULL},
- {"X509_free", NULL},
- {"X509_get_subject_name", NULL},
- {"X509_get_issuer_name", NULL},
- {"X509_NAME_oneline", NULL},
- {"X509_get_serialNumber", NULL},
- {"i2c_ASN1_INTEGER", NULL},
- {"EVP_get_digestbyname", NULL},
- {"EVP_Digest", NULL},
- {"i2d_X509", NULL},
- {"BN_bn2hex", NULL},
- {"ASN1_INTEGER_to_BN", NULL},
- {"BN_free", NULL},
- {"CRYPTO_free", NULL},
- {"ERR_clear_error", NULL},
- {NULL, NULL}};
+static struct ssl_func crypto_sw[] = {
+ {"CRYPTO_num_locks", TLS_Mandatory, NULL},
+ {"CRYPTO_set_locking_callback", TLS_Mandatory, NULL},
+ {"CRYPTO_set_id_callback", TLS_Mandatory, NULL},
+ {"ERR_get_error", TLS_Mandatory, NULL},
+ {"ERR_error_string", TLS_Mandatory, NULL},
+ {"ERR_remove_state", TLS_Mandatory, NULL},
+ {"ERR_free_strings", TLS_Mandatory, NULL},
+ {"ENGINE_cleanup", TLS_Mandatory, NULL},
+ {"CONF_modules_unload", TLS_Mandatory, NULL},
+ {"CRYPTO_cleanup_all_ex_data", TLS_Mandatory, NULL},
+ {"EVP_cleanup", TLS_Mandatory, NULL},
+ {"X509_free", TLS_Mandatory, NULL},
+ {"X509_get_subject_name", TLS_Mandatory, NULL},
+ {"X509_get_issuer_name", TLS_Mandatory, NULL},
+ {"X509_NAME_oneline", TLS_Mandatory, NULL},
+ {"X509_get_serialNumber", TLS_Mandatory, NULL},
+ {"i2c_ASN1_INTEGER", TLS_Mandatory, NULL},
+ {"EVP_get_digestbyname", TLS_Mandatory, NULL},
+ {"EVP_Digest", TLS_Mandatory, NULL},
+ {"i2d_X509", TLS_Mandatory, NULL},
+ {"BN_bn2hex", TLS_Mandatory, NULL},
+ {"ASN1_INTEGER_to_BN", TLS_Mandatory, NULL},
+ {"BN_free", TLS_Mandatory, NULL},
+ {"CRYPTO_free", TLS_Mandatory, NULL},
+ {"ERR_clear_error", TLS_Mandatory, NULL},
+ {NULL, TLS_END_OF_LIST, NULL}};
#endif /* OPENSSL_API_1_1 */
#endif /* NO_SSL_DL */
#endif /* NO_SSL */
@@ -2299,8 +2469,7 @@ static const char month_names[][4] = {"Jan",
#endif /* !NO_CACHING */
/* Unified socket address. For IPv6 support, add IPv6 address structure in
- * the
- * union u. */
+ * the union u. */
union usa {
struct sockaddr sa;
struct sockaddr_in sin;
@@ -2309,6 +2478,13 @@ union usa {
#endif
};
+#if defined(USE_IPV6)
+#define USA_IN_PORT_UNSAFE(s) \
+ (((s)->sa.sa_family == AF_INET6) ? (s)->sin6.sin6_port : (s)->sin.sin_port)
+#else
+#define USA_IN_PORT_UNSAFE(s) ((s)->sin.sin_port)
+#endif
+
/* Describes a string (chunk of memory). */
struct vec {
const char *ptr;
@@ -2325,23 +2501,10 @@ struct mg_file_stat {
int location; /* 0 = nowhere, 1 = on disk, 2 = in memory */
};
-struct mg_file_in_memory {
- char *p;
- uint32_t pos;
- char mode;
-};
struct mg_file_access {
/* File properties filled by mg_fopen: */
FILE *fp;
-#if defined(MG_USE_OPEN_FILE)
- /* TODO (low): Remove obsolete "file in memory" implementation.
- * In an "early 2017" discussion at Google groups
- * https://groups.google.com/forum/#!topic/civetweb/h9HT4CmeYqI
- * we decided to get rid of this feature (after some fade-out
- * phase). */
- const char *membuf;
-#endif
};
struct mg_file {
@@ -2349,17 +2512,6 @@ struct mg_file {
struct mg_file_access access;
};
-#if defined(MG_USE_OPEN_FILE)
-
-#define STRUCT_FILE_INITIALIZER \
- { \
- {(uint64_t)0, (time_t)0, 0, 0, 0}, \
- { \
- (FILE *)NULL, (const char *)NULL \
- } \
- }
-
-#else
#define STRUCT_FILE_INITIALIZER \
{ \
@@ -2369,8 +2521,6 @@ struct mg_file {
} \
}
-#endif
-
/* Describes listening socket, or socket which was accept()-ed by the master
* thread and queued for future handling by the worker thread. */
@@ -2422,16 +2572,27 @@ enum {
LUA_BACKGROUND_SCRIPT,
LUA_BACKGROUND_SCRIPT_PARAMS,
#endif
-#if defined(USE_TIMERS)
- CGI_TIMEOUT,
+#if defined(USE_HTTP2)
+ ENABLE_HTTP2,
#endif
/* Once for each domain */
DOCUMENT_ROOT,
+
CGI_EXTENSIONS,
+ CGI2_EXTENSIONS,
CGI_ENVIRONMENT,
- PUT_DELETE_PASSWORDS_FILE,
+ CGI2_ENVIRONMENT,
CGI_INTERPRETER,
+ CGI2_INTERPRETER,
+ CGI_INTERPRETER_ARGS,
+ CGI2_INTERPRETER_ARGS,
+#if defined(USE_TIMERS)
+ CGI_TIMEOUT,
+ CGI2_TIMEOUT,
+#endif
+
+ PUT_DELETE_PASSWORDS_FILE,
PROTECT_URI,
AUTHENTICATION_DOMAIN,
ENABLE_AUTH_DOMAIN_CHECK,
@@ -2446,6 +2607,7 @@ enum {
URL_REWRITE_PATTERN,
HIDE_FILES,
SSL_DO_VERIFY_PEER,
+ SSL_CACHE_TIMEOUT,
SSL_CA_PATH,
SSL_CA_FILE,
SSL_VERIFY_DEPTH,
@@ -2529,16 +2691,27 @@ static const struct mg_option config_options[] = {
{"lua_background_script", MG_CONFIG_TYPE_FILE, NULL},
{"lua_background_script_params", MG_CONFIG_TYPE_STRING_LIST, NULL},
#endif
-#if defined(USE_TIMERS)
- {"cgi_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
+#if defined(USE_HTTP2)
+ {"enable_http2", MG_CONFIG_TYPE_BOOLEAN, "no"},
#endif
/* Once for each domain */
{"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
+
{"cgi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"},
+ {"cgi2_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
{"cgi_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
- {"put_delete_auth_file", MG_CONFIG_TYPE_FILE, NULL},
+ {"cgi2_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
{"cgi_interpreter", MG_CONFIG_TYPE_FILE, NULL},
+ {"cgi2_interpreter", MG_CONFIG_TYPE_FILE, NULL},
+ {"cgi_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
+ {"cgi2_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
+#if defined(USE_TIMERS)
+ {"cgi_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
+ {"cgi2_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
+#endif
+
+ {"put_delete_auth_file", MG_CONFIG_TYPE_FILE, NULL},
{"protect_uri", MG_CONFIG_TYPE_STRING_LIST, NULL},
{"authentication_domain", MG_CONFIG_TYPE_STRING, "mydomain.com"},
{"enable_auth_domain_check", MG_CONFIG_TYPE_BOOLEAN, "yes"},
@@ -2562,13 +2735,23 @@ static const struct mg_option config_options[] = {
{"hide_files_patterns", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
{"ssl_verify_peer", MG_CONFIG_TYPE_YES_NO_OPTIONAL, "no"},
+ {"ssl_cache_timeout", MG_CONFIG_TYPE_NUMBER, "-1"},
{"ssl_ca_path", MG_CONFIG_TYPE_DIRECTORY, NULL},
{"ssl_ca_file", MG_CONFIG_TYPE_FILE, NULL},
{"ssl_verify_depth", MG_CONFIG_TYPE_NUMBER, "9"},
{"ssl_default_verify_paths", MG_CONFIG_TYPE_BOOLEAN, "yes"},
{"ssl_cipher_list", MG_CONFIG_TYPE_STRING, NULL},
+
+#if defined(USE_HTTP2)
+ /* HTTP2 requires ALPN, and anyway TLS1.2 should be considered
+ * as a minimum in 2020 */
+ {"ssl_protocol_version", MG_CONFIG_TYPE_NUMBER, "4"},
+#else
+ /* Keep the default (compatibility) */
{"ssl_protocol_version", MG_CONFIG_TYPE_NUMBER, "0"},
+#endif /* defined(USE_HTTP2) */
+
{"ssl_short_trust", MG_CONFIG_TYPE_BOOLEAN, "no"},
#if defined(USE_LUA)
@@ -2632,9 +2815,7 @@ struct mg_handler_info {
/* Handler for http/https or authorization requests. */
mg_request_handler handler;
unsigned int refcount;
- pthread_mutex_t refcount_mutex; /* Protects refcount */
- pthread_cond_t
- refcount_cond; /* Signaled when handler refcount is decremented */
+ int removing;
/* Handler for ws/wss (websocket) requests. */
mg_websocket_connect_handler connect_handler;
@@ -2668,6 +2849,7 @@ struct mg_domain_context {
SSL_CTX *ssl_ctx; /* SSL context */
char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */
struct mg_handler_info *handlers; /* linked list of uri handlers */
+ int64_t ssl_cert_last_mtime;
/* Server nonce */
uint64_t auth_nonce_mask; /* Mask for all nonce values */
@@ -2683,6 +2865,46 @@ struct mg_domain_context {
};
+/* Stop flag can be "volatile" or require a lock.
+ * MSDN uses volatile for "Interlocked" operations, but also explicitly
+ * states a read operation for int is always atomic. */
+#if defined(STOP_FLAG_NEEDS_LOCK)
+
+typedef ptrdiff_t volatile stop_flag_t;
+
+static int
+STOP_FLAG_IS_ZERO(stop_flag_t *f)
+{
+ stop_flag_t sf = mg_atomic_add(f, 0);
+ return (sf == 0);
+}
+
+static int
+STOP_FLAG_IS_TWO(stop_flag_t *f)
+{
+ stop_flag_t sf = mg_atomic_add(f, 0);
+ return (sf == 2);
+}
+
+static void
+STOP_FLAG_ASSIGN(stop_flag_t *f, stop_flag_t v)
+{
+ stop_flag_t sf;
+ do {
+ sf = mg_atomic_compare_and_swap(f, *f, v);
+ } while (sf != v);
+}
+
+#else /* STOP_FLAG_NEEDS_LOCK */
+
+typedef int volatile stop_flag_t;
+#define STOP_FLAG_IS_ZERO(f) ((*(f)) == 0)
+#define STOP_FLAG_IS_TWO(f) ((*(f)) == 2)
+#define STOP_FLAG_ASSIGN(f, v) ((*(f)) = (v))
+
+#endif /* STOP_FLAG_NEEDS_LOCK */
+
+
struct mg_context {
/* Part 1 - Physical context:
@@ -2702,17 +2924,17 @@ struct mg_context {
* allocated for each worker */
#if defined(USE_SERVER_STATS)
- int active_connections;
- int max_active_connections;
- int64_t total_connections;
- int64_t total_requests;
- int64_t total_data_read;
- int64_t total_data_written;
+ volatile ptrdiff_t active_connections;
+ volatile ptrdiff_t max_active_connections;
+ volatile ptrdiff_t total_connections;
+ volatile ptrdiff_t total_requests;
+ volatile int64_t total_data_read;
+ volatile int64_t total_data_written;
#endif
/* Thread related */
- volatile int stop_flag; /* Should we stop event loop */
- pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */
+ stop_flag_t stop_flag; /* Should we stop event loop */
+ pthread_mutex_t thread_mutex; /* Protects client_socks or queue */
pthread_t masterthreadid; /* The master thread ID */
unsigned int
@@ -2760,7 +2982,9 @@ struct mg_context {
#endif
/* Server nonce */
- pthread_mutex_t nonce_mutex; /* Protects nonce_count */
+ pthread_mutex_t nonce_mutex; /* Protects ssl_ctx, handlers,
+ * ssl_cert_last_mtime, nonce_count, and
+ * next (linked list) */
/* Server callbacks */
struct mg_callbacks callbacks; /* User-defined callback function */
@@ -2791,13 +3015,39 @@ get_memory_stat(struct mg_context *ctx)
#endif
enum {
- CONNECTION_TYPE_INVALID,
- CONNECTION_TYPE_REQUEST,
- CONNECTION_TYPE_RESPONSE
+ CONNECTION_TYPE_INVALID = 0,
+ CONNECTION_TYPE_REQUEST = 1,
+ CONNECTION_TYPE_RESPONSE = 2
};
+enum {
+ PROTOCOL_TYPE_HTTP1 = 0,
+ PROTOCOL_TYPE_WEBSOCKET = 1,
+ PROTOCOL_TYPE_HTTP2 = 2
+};
+
+
+#if defined(USE_HTTP2)
+#if !defined(HTTP2_DYN_TABLE_SIZE)
+#define HTTP2_DYN_TABLE_SIZE (256)
+#endif
+
+struct mg_http2_connection {
+ uint32_t stream_id;
+ uint32_t dyn_table_size;
+ struct mg_header dyn_table[HTTP2_DYN_TABLE_SIZE];
+};
+#endif
+
+
struct mg_connection {
int connection_type; /* see CONNECTION_TYPE_* above */
+ int protocol_type; /* see PROTOCOL_TYPE_*: 0=http/1.x, 1=ws, 2=http/2 */
+ int request_state; /* 0: nothing sent, 1: header partially sent, 2: header
+ fully sent */
+#if defined(USE_HTTP2)
+ struct mg_http2_connection http2;
+#endif
struct mg_request_info request_info;
struct mg_response_info response_info;
@@ -2811,7 +3061,6 @@ struct mg_connection {
* mg_get_connection_info_impl */
#endif
- const char *host; /* Host (HTTP/1.1 header or SNI) */
SSL *ssl; /* SSL descriptor */
struct socket client; /* Connected client */
time_t conn_birth_time; /* Time (wall clock) when connection was
@@ -2846,6 +3095,18 @@ struct mg_connection {
* pages */
#if defined(USE_WEBSOCKET)
int in_websocket_handling; /* 1 if in read_websocket */
+#endif
+#if defined(USE_ZLIB) && defined(USE_WEBSOCKET) \
+ && defined(MG_EXPERIMENTAL_INTERFACES)
+ /* Parameters for websocket data compression according to rfc7692 */
+ int websocket_deflate_server_max_windows_bits;
+ int websocket_deflate_client_max_windows_bits;
+ int websocket_deflate_server_no_context_takeover;
+ int websocket_deflate_client_no_context_takeover;
+ int websocket_deflate_initialized;
+ int websocket_deflate_flush;
+ z_stream websocket_deflate_state;
+ z_stream websocket_inflate_state;
#endif
int handled_requests; /* Number of requests handled by this connection
*/
@@ -2866,8 +3127,6 @@ struct mg_connection {
void *tls_user_ptr; /* User defined pointer in thread local storage,
* for quick access */
-
- char *cookie_header; // <---- Pi-hole modification
};
@@ -2879,13 +3138,6 @@ struct de {
};
-#if defined(USE_WEBSOCKET)
-static int is_websocket_protocol(const struct mg_connection *conn);
-#else
-#define is_websocket_protocol(conn) (0)
-#endif
-
-
#define mg_cry_internal(conn, fmt, ...) \
mg_cry_internal_wrap(conn, NULL, __func__, __LINE__, fmt, __VA_ARGS__)
@@ -3135,25 +3387,6 @@ mg_set_thread_name(const char *threadName)
#endif
-#if defined(MG_LEGACY_INTERFACE)
-const char **
-mg_get_valid_option_names(void)
-{
- /* This function is deprecated. Use mg_get_valid_options instead. */
- 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;
- }
-
- return data;
-}
-#endif
-
-
const struct mg_option *
mg_get_valid_options(void)
{
@@ -3161,7 +3394,7 @@ mg_get_valid_options(void)
}
-/* Do not open file (used in is_file_in_memory) */
+/* Do not open file (unused) */
#define MG_FOPEN_MODE_NONE (0)
/* Open file for read only access */
@@ -3174,77 +3407,6 @@ mg_get_valid_options(void)
#define MG_FOPEN_MODE_APPEND (4)
-/* If a file is in memory, set all "stat" members and the membuf pointer of
- * output filep and return 1, otherwise return 0 and don't modify anything.
- */
-static int
-open_file_in_memory(const struct mg_connection *conn,
- const char *path,
- struct mg_file *filep,
- int mode)
-{
-#if defined(MG_USE_OPEN_FILE)
-
- size_t size = 0;
- const char *buf = NULL;
- if (!conn) {
- return 0;
- }
-
- if ((mode != MG_FOPEN_MODE_NONE) && (mode != MG_FOPEN_MODE_READ)) {
- return 0;
- }
-
- if (conn->phys_ctx->callbacks.open_file) {
- buf = conn->phys_ctx->callbacks.open_file(conn, path, &size);
- if (buf != NULL) {
- if (filep == NULL) {
- /* This is a file in memory, but we cannot store the
- * properties
- * now.
- * Called from "is_file_in_memory" function. */
- return 1;
- }
-
- /* NOTE: override filep->size only on success. Otherwise, it
- * might
- * break constructs like if (!mg_stat() || !mg_fopen()) ... */
- filep->access.membuf = buf;
- filep->access.fp = NULL;
-
- /* Size was set by the callback */
- filep->stat.size = size;
-
- /* Assume the data may change during runtime by setting
- * last_modified = now */
- filep->stat.last_modified = time(NULL);
-
- filep->stat.is_directory = 0;
- filep->stat.is_gzipped = 0;
- }
- }
-
- return (buf != NULL);
-
-#else
- (void)conn;
- (void)path;
- (void)filep;
- (void)mode;
-
- return 0;
-
-#endif
-}
-
-
-static int
-is_file_in_memory(const struct mg_connection *conn, const char *path)
-{
- return open_file_in_memory(conn, path, NULL, MG_FOPEN_MODE_NONE);
-}
-
-
static int
is_file_opened(const struct mg_file_access *fileacc)
{
@@ -3252,11 +3414,7 @@ is_file_opened(const struct mg_file_access *fileacc)
return 0;
}
-#if defined(MG_USE_OPEN_FILE)
- return (fileacc->membuf != NULL) || (fileacc->fp != NULL);
-#else
return (fileacc->fp != NULL);
-#endif
}
@@ -3266,11 +3424,51 @@ static int mg_stat(const struct mg_connection *conn,
struct mg_file_stat *filep);
+/* Reject files with special characters */
+static int
+mg_path_suspicious(const struct mg_connection *conn, const char *path)
+{
+ const uint8_t *c = (const uint8_t *)path;
+ (void)conn; /* not used */
+
+ if ((c == NULL) || (c[0] == 0)) {
+ /* Null pointer or empty path --> suspicious */
+ return 1;
+ }
+
+ while (*c) {
+ if (*c <= 32) {
+ /* Control character or space */
+ return 0;
+ }
+ if ((*c == '>') || (*c == '<') || (*c == '|')) {
+ /* stdin/stdout redirection character */
+ return 0;
+ }
+#if defined(_WIN32)
+ if (*c == '\\') {
+ /* Windows backslash */
+ return 0;
+ }
+#else
+ if (*c == '&') {
+ /* Linux ampersand */
+ return 0;
+ }
+#endif
+ c++;
+ }
+
+ /* Nothing suspicious found */
+ return 0;
+}
+
+
/* mg_fopen will open a file either in memory or on the disk.
* The input parameter path is a string in UTF-8 encoding.
* The input parameter mode is MG_FOPEN_MODE_*
- * On success, either fp or membuf will be set in the output
- * struct file. All status members will also be set.
+ * On success, fp will be set in the output struct mg_file.
+ * All status members will also be set.
* The function returns 1 on success, 0 on error. */
static int
mg_fopen(const struct mg_connection *conn,
@@ -3284,75 +3482,61 @@ mg_fopen(const struct mg_connection *conn,
return 0;
}
filep->access.fp = NULL;
-#if defined(MG_USE_OPEN_FILE)
- filep->access.membuf = NULL;
-#endif
- if (!is_file_in_memory(conn, path)) {
-
- /* filep is initialized in mg_stat: all fields with memset to,
- * some fields like size and modification date with values */
- found = mg_stat(conn, path, &(filep->stat));
-
- if ((mode == MG_FOPEN_MODE_READ) && (!found)) {
- /* file does not exist and will not be created */
- return 0;
- }
-
-#if defined(_WIN32)
- {
- wchar_t wbuf[W_PATH_MAX];
- path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
- switch (mode) {
- case MG_FOPEN_MODE_READ:
- filep->access.fp = _wfopen(wbuf, L"rb");
- break;
- case MG_FOPEN_MODE_WRITE:
- filep->access.fp = _wfopen(wbuf, L"wb");
- break;
- case MG_FOPEN_MODE_APPEND:
- filep->access.fp = _wfopen(wbuf, L"ab");
- break;
- }
- }
-#else
- /* Linux et al already use unicode. No need to convert. */
- switch (mode) {
- case MG_FOPEN_MODE_READ:
- filep->access.fp = fopen(path, "r");
- break;
- case MG_FOPEN_MODE_WRITE:
- filep->access.fp = fopen(path, "w");
- break;
- case MG_FOPEN_MODE_APPEND:
- filep->access.fp = fopen(path, "a");
- break;
- }
-
-#endif
- if (!found) {
- /* File did not exist before fopen was called.
- * Maybe it has been created now. Get stat info
- * like creation time now. */
- found = mg_stat(conn, path, &(filep->stat));
- (void)found;
- }
-
- /* file is on disk */
- return (filep->access.fp != NULL);
-
- } else {
-#if defined(MG_USE_OPEN_FILE)
- /* is_file_in_memory returned true */
- if (open_file_in_memory(conn, path, filep, mode)) {
- /* file is in memory */
- return (filep->access.membuf != NULL);
- }
-#endif
+ if (mg_path_suspicious(conn, path)) {
+ return 0;
}
- /* Open failed */
- return 0;
+ /* filep is initialized in mg_stat: all fields with memset to,
+ * some fields like size and modification date with values */
+ found = mg_stat(conn, path, &(filep->stat));
+
+ if ((mode == MG_FOPEN_MODE_READ) && (!found)) {
+ /* file does not exist and will not be created */
+ return 0;
+ }
+
+#if defined(_WIN32)
+ {
+ wchar_t wbuf[W_PATH_MAX];
+ path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
+ switch (mode) {
+ case MG_FOPEN_MODE_READ:
+ filep->access.fp = _wfopen(wbuf, L"rb");
+ break;
+ case MG_FOPEN_MODE_WRITE:
+ filep->access.fp = _wfopen(wbuf, L"wb");
+ break;
+ case MG_FOPEN_MODE_APPEND:
+ filep->access.fp = _wfopen(wbuf, L"ab");
+ break;
+ }
+ }
+#else
+ /* Linux et al already use unicode. No need to convert. */
+ switch (mode) {
+ case MG_FOPEN_MODE_READ:
+ filep->access.fp = fopen(path, "r");
+ break;
+ case MG_FOPEN_MODE_WRITE:
+ filep->access.fp = fopen(path, "w");
+ break;
+ case MG_FOPEN_MODE_APPEND:
+ filep->access.fp = fopen(path, "a");
+ break;
+ }
+
+#endif
+ if (!found) {
+ /* File did not exist before fopen was called.
+ * Maybe it has been created now. Get stat info
+ * like creation time now. */
+ found = mg_stat(conn, path, &(filep->stat));
+ (void)found;
+ }
+
+ /* return OK if file is opened */
+ return (filep->access.fp != NULL);
}
@@ -3364,10 +3548,6 @@ mg_fclose(struct mg_file_access *fileacc)
if (fileacc != NULL) {
if (fileacc->fp != NULL) {
ret = fclose(fileacc->fp);
-#if defined(MG_USE_OPEN_FILE)
- } else if (fileacc->membuf != NULL) {
- ret = 0;
-#endif
}
/* reset all members of fileacc */
memset(fileacc, 0, sizeof(*fileacc));
@@ -3630,13 +3810,7 @@ mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl)
}
for (i = 0; i < size && i < ctx->num_listening_sockets; i++) {
ssl[i] = ctx->listening_sockets[i].is_ssl;
- ports[i] =
-#if defined(USE_IPV6)
- (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6)
- ? ntohs(ctx->listening_sockets[i].lsa.sin6.sin6_port)
- :
-#endif
- ntohs(ctx->listening_sockets[i].lsa.sin.sin_port);
+ ports[i] = ntohs(USA_IN_PORT_UNSAFE(&(ctx->listening_sockets[i].lsa)));
}
return i;
}
@@ -3664,12 +3838,7 @@ mg_get_server_ports(const struct mg_context *ctx,
for (i = 0; (i < size) && (i < (int)ctx->num_listening_sockets); i++) {
ports[cnt].port =
-#if defined(USE_IPV6)
- (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6)
- ? ntohs(ctx->listening_sockets[i].lsa.sin6.sin6_port)
- :
-#endif
- ntohs(ctx->listening_sockets[i].lsa.sin.sin_port);
+ ntohs(USA_IN_PORT_UNSAFE(&(ctx->listening_sockets[i].lsa)));
ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
@@ -3968,9 +4137,9 @@ get_proto_name(const struct mg_connection *conn)
const struct mg_request_info *ri = &conn->request_info;
- const char *proto =
- (is_websocket_protocol(conn) ? (ri->is_ssl ? "wss" : "ws")
- : (ri->is_ssl ? "https" : "http"));
+ const char *proto = ((conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)
+ ? (ri->is_ssl ? "wss" : "ws")
+ : (ri->is_ssl ? "https" : "http"));
return proto;
@@ -3980,8 +4149,13 @@ get_proto_name(const struct mg_connection *conn)
}
-int
-mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen)
+static int
+mg_construct_local_link(const struct mg_connection *conn,
+ char *buf,
+ size_t buflen,
+ const char *define_proto,
+ int define_port,
+ const char *define_uri)
{
if ((buflen < 1) || (buf == 0) || (conn == 0)) {
return -1;
@@ -3990,53 +4164,49 @@ mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen)
int truncated = 0;
const struct mg_request_info *ri = &conn->request_info;
- const char *proto = get_proto_name(conn);
+ const char *proto =
+ (define_proto != NULL) ? define_proto : get_proto_name(conn);
+ const char *uri =
+ (define_uri != NULL)
+ ? define_uri
+ : ((ri->request_uri != NULL) ? ri->request_uri : ri->local_uri);
+ int port = (define_port > 0)
+ ? define_port
+ : htons(USA_IN_PORT_UNSAFE(&conn->client.lsa));
+ int default_port = 80;
- if (ri->local_uri == NULL) {
+ if (uri == NULL) {
return -1;
}
- if ((ri->request_uri != NULL)
- && (0 != strcmp(ri->local_uri, ri->request_uri))) {
- /* The request uri is different from the local uri.
- * This is usually if an absolute URI, including server
- * name has been provided. */
- mg_snprintf(conn,
- &truncated,
- buf,
- buflen,
- "%s://%s",
- proto,
- ri->request_uri);
- if (truncated) {
- return -1;
+ if (define_proto) {
+ /* If we got a protocol name, use the default port accordingly. */
+ if ((0 == strcmp(define_proto, "https"))
+ || (0 == strcmp(define_proto, "wss"))) {
+ default_port = 443;
}
- return 0;
-
- } else {
-
- /* The common case is a relative URI, so we have to
- * construct an absolute URI from server name and port */
+ } else if (ri->is_ssl) {
+ /* If we did not get a protocol name, use TLS as default if it is
+ * already used. */
+ default_port = 443;
+ }
+ {
#if defined(USE_IPV6)
int is_ipv6 = (conn->client.lsa.sa.sa_family == AF_INET6);
- int port = is_ipv6 ? htons(conn->client.lsa.sin6.sin6_port)
- : htons(conn->client.lsa.sin.sin_port);
-#else
- int port = htons(conn->client.lsa.sin.sin_port);
#endif
- int def_port = ri->is_ssl ? 443 : 80;
int auth_domain_check_enabled =
conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK]
&& (!mg_strcasecmp(
conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK], "yes"));
+
const char *server_domain =
conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
char portstr[16];
char server_ip[48];
- if (port != def_port) {
+ if (port != default_port) {
sprintf(portstr, ":%u", (unsigned)port);
} else {
portstr[0] = 0;
@@ -4055,11 +4225,20 @@ mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen)
&truncated,
buf,
buflen,
+#if defined(USE_IPV6)
+ "%s://%s%s%s%s%s",
+ proto,
+ (is_ipv6 && (server_domain == server_ip)) ? "[" : "",
+ server_domain,
+ (is_ipv6 && (server_domain == server_ip)) ? "]" : "",
+#else
"%s://%s%s%s",
proto,
server_domain,
+#endif
portstr,
ri->local_uri);
+
if (truncated) {
return -1;
}
@@ -4068,6 +4247,14 @@ mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen)
}
}
+
+int
+mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen)
+{
+ return mg_construct_local_link(conn, buf, buflen, NULL, -1, NULL);
+}
+
+
/* 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
@@ -4318,15 +4505,17 @@ match_prefix(const char *pattern, size_t pattern_len, const char *str)
i++;
if (pattern[i] == '*') {
i++;
- len = strlen(str + j);
+ len = (ptrdiff_t)strlen(str + j);
} else {
- len = strcspn(str + j, "/");
+ len = (ptrdiff_t)strcspn(str + j, "/");
}
if (i == (ptrdiff_t)pattern_len) {
return j + len;
}
do {
- res = match_prefix(pattern + i, pattern_len - i, str + j + len);
+ res = match_prefix(pattern + i,
+ (pattern_len - (size_t)i),
+ str + j + len);
} while (res == -1 && len-- > 0);
return (res == -1) ? -1 : j + res + len;
} else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
@@ -4337,6 +4526,16 @@ match_prefix(const char *pattern, size_t pattern_len, const char *str)
}
+static ptrdiff_t
+match_prefix_strlen(const char *pattern, const char *str)
+{
+ if (pattern == NULL) {
+ return -1;
+ }
+ return match_prefix(pattern, strlen(pattern), str);
+}
+
+
/* 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. */
@@ -4397,28 +4596,43 @@ suggest_connection_header(const struct mg_connection *conn)
}
-static int
+#include "response.inl"
+
+
+static void
send_no_cache_header(struct mg_connection *conn)
{
/* Send all current and obsolete cache opt-out directives. */
- return mg_printf(conn,
- "Cache-Control: no-cache, no-store, "
- "must-revalidate, private, max-age=0\r\n"
- "Pragma: no-cache\r\n"
- "Expires: 0\r\n");
+ mg_response_header_add(conn,
+ "Cache-Control",
+ "no-cache, no-store, "
+ "must-revalidate, private, max-age=0",
+ -1);
+ mg_response_header_add(conn, "Expires", "0", -1);
+
+ if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
+ /* Obsolete, but still send it for HTTP/1.0 */
+ mg_response_header_add(conn, "Pragma", "no-cache", -1);
+ }
}
-static int
+static void
send_static_cache_header(struct mg_connection *conn)
{
#if !defined(NO_CACHING)
int max_age;
+ char val[64];
+
const char *cache_control =
conn->dom_ctx->config[STATIC_FILE_CACHE_CONTROL];
+
+ /* If there is a full cache-control option configured,0 use it */
if (cache_control != NULL) {
- return mg_printf(conn, "Cache-Control: %s\r\n", cache_control);
+ mg_response_header_add(conn, "Cache-Control", cache_control, -1);
+ return;
}
+
/* Read the server config to check how long a file may be cached.
* The configuration is in seconds. */
max_age = atoi(conn->dom_ctx->config[STATIC_FILE_MAX_AGE]);
@@ -4427,7 +4641,8 @@ send_static_cache_header(struct mg_connection *conn)
* and may be used differently in the future. */
/* If a file should not be cached, do not only send
* max-age=0, but also pragmas and Expires headers. */
- return send_no_cache_header(conn);
+ send_no_cache_header(conn);
+ return;
}
/* Use "Cache-Control: max-age" instead of "Expires" header.
@@ -4439,45 +4654,49 @@ send_static_cache_header(struct mg_connection *conn)
* year to 31622400 seconds. For the moment, we just send whatever has
* been configured, still the behavior for >1 year should be considered
* as undefined. */
- return mg_printf(conn, "Cache-Control: max-age=%u\r\n", (unsigned)max_age);
+ mg_snprintf(
+ conn, NULL, val, sizeof(val), "max-age=%lu", (unsigned long)max_age);
+ mg_response_header_add(conn, "Cache-Control", val, -1);
+
#else /* NO_CACHING */
- return send_no_cache_header(conn);
+
+ send_no_cache_header(conn);
#endif /* !NO_CACHING */
}
-static int
+static void
send_additional_header(struct mg_connection *conn)
{
- int i = 0;
const char *header = conn->dom_ctx->config[ADDITIONAL_HEADER];
#if !defined(NO_SSL)
if (conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]) {
- int max_age = atoi(conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]);
+ long max_age = atol(conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]);
if (max_age >= 0) {
- i += mg_printf(conn,
- "Strict-Transport-Security: max-age=%u\r\n",
- (unsigned)max_age);
+ char val[64];
+ mg_snprintf(conn,
+ NULL,
+ val,
+ sizeof(val),
+ "max-age=%lu",
+ (unsigned long)max_age);
+ mg_response_header_add(conn, "Strict-Transport-Security", val, -1);
}
}
#endif
- /**************** Pi-hole modification ****************/
- if(conn->cookie_header != NULL &&
- conn->cookie_header[0])
- {
- i += mg_printf(conn, "%s", conn->cookie_header);
- mg_free(conn->cookie_header);
- conn->cookie_header = NULL;
- }
- /******************************************************/
-
if (header && header[0]) {
- i += mg_printf(conn, "%s\r\n", header);
+ mg_response_header_add_lines(conn, header);
}
- return i;
+ /*************** Pi-hole modification ****************/
+ if (pi_hole_extra_headers[0] != '\0') {
+ mg_response_header_add_lines(conn, pi_hole_extra_headers);
+ // Invalidate extra headers after having sent them to avoid repetitions
+ pi_hole_extra_headers[0] = '\0';
+ }
+ /*****************************************************/
}
@@ -4696,8 +4915,7 @@ mg_send_http_error_impl(struct mg_connection *conn,
char errmsg_buf[MG_BUF_LEN];
va_list ap;
int has_body;
- char date[64];
- time_t curtime = time(NULL);
+
#if !defined(NO_FILESYSTEMS)
char path_buf[PATH_MAX];
int len, i, page_handler_found, scope, truncated;
@@ -4707,8 +4925,6 @@ mg_send_http_error_impl(struct mg_connection *conn,
#endif /* NO_FILESYSTEMS */
int handled_by_callback = 0;
- const char *status_text = mg_get_response_code_text(conn, status);
-
if ((conn == NULL) || (fmt == NULL)) {
return -2;
}
@@ -4795,8 +5011,17 @@ mg_send_http_error_impl(struct mg_connection *conn,
* from the config, not from a client. */
(void)truncated;
+ /* The following code is redundant, but it should avoid
+ * false positives in static source code analyzers and
+ * vulnerability scanners.
+ */
+ path_buf[sizeof(path_buf) - 32] = 0;
len = (int)strlen(path_buf);
+ if (len > (int)sizeof(path_buf) - 32) {
+ len = (int)sizeof(path_buf) - 32;
+ }
+ /* Start with the file extenstion from the configuration. */
tstr = strchr(error_page_file_ext, '.');
while (tstr) {
@@ -4822,6 +5047,8 @@ mg_send_http_error_impl(struct mg_connection *conn,
DEBUG_TRACE("Check error page %s - not found",
path_buf);
+ /* Continue with the next file extenstion from the
+ * configuration (if there is a next one). */
tstr = strchr(tstr + i, '.');
}
}
@@ -4837,25 +5064,22 @@ mg_send_http_error_impl(struct mg_connection *conn,
}
/* No custom error page. Send default error page. */
- gmt_time_string(date, sizeof(date), &curtime);
-
conn->must_close = 1;
- mg_printf(conn, "HTTP/1.1 %d %s\r\n", status, status_text);
+ mg_response_header_start(conn, status);
send_no_cache_header(conn);
send_additional_header(conn);
if (has_body) {
- mg_printf(conn,
- "%s",
- "Content-Type: text/plain; charset=utf-8\r\n");
+ mg_response_header_add(conn,
+ "Content-Type",
+ "text/plain; charset=utf-8",
+ -1);
}
- mg_printf(conn,
- "Date: %s\r\n"
- "Connection: close\r\n\r\n",
- date);
+ mg_response_header_send(conn);
/* HTTP responses 1xx, 204 and 304 MUST NOT send a body */
if (has_body) {
/* For other errors, send a generic error message. */
+ const char *status_text = mg_get_response_code_text(conn, status);
mg_printf(conn, "Error %d: %s\n", status, status_text);
mg_write(conn, errmsg_buf, strlen(errmsg_buf));
@@ -4878,8 +5102,10 @@ void my_send_http_error_headers(struct mg_connection *conn,
const char *status_text = mg_get_response_code_text(conn, status);
mg_printf(conn, "HTTP/1.1 %d %s\r\n", status, status_text);
+ mg_response_header_start(conn, status);
send_no_cache_header(conn);
send_additional_header(conn);
+ mg_response_header_send(conn);
conn->must_close = 1;
char date[64];
@@ -4895,12 +5121,6 @@ void my_send_http_error_headers(struct mg_connection *conn,
(uint64_t)content_length);
}
-void my_set_cookie_header(struct mg_connection *conn,
- const char *cookie_header)
-{
- conn->cookie_header = mg_strdup(cookie_header);
-}
-
/********************************************************************************************/
int
@@ -4923,41 +5143,37 @@ mg_send_http_ok(struct mg_connection *conn,
const char *additional_headers,
long long content_length)
{
- char date[64];
- time_t curtime = time(NULL);
-
if ((mime_type == NULL) || (*mime_type == 0)) {
/* No content type defined: default to text/html */
mime_type = "text/html";
}
- gmt_time_string(date, sizeof(date), &curtime);
-
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: %s\r\n"
- "Date: %s\r\n"
- "Connection: %s\r\n",
- mime_type,
- date,
- suggest_connection_header(conn));
-
- /********************** Pi-hole modification **********************/
- if(additional_headers != NULL && strlen(additional_headers) > 0)
- {
- mg_write(conn, additional_headers, strlen(additional_headers));
- }
- /******************************************************************/
-
+ mg_response_header_start(conn, 200);
send_no_cache_header(conn);
send_additional_header(conn);
+ mg_response_header_add(conn, "Content-Type", mime_type, -1);
if (content_length < 0) {
- mg_printf(conn, "Transfer-Encoding: chunked\r\n\r\n");
+ /* Size not known. Use chunked encoding (HTTP/1.x) */
+ if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
+ /* Only HTTP/1.x defines "chunked" encoding, HTTP/2 does not*/
+ mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1);
+ }
} else {
- mg_printf(conn,
- "Content-Length: %" UINT64_FMT "\r\n\r\n",
- (uint64_t)content_length);
+ char len[32];
+ int trunc = 0;
+ mg_snprintf(conn,
+ &trunc,
+ len,
+ sizeof(len),
+ "%" UINT64_FMT,
+ (uint64_t)content_length);
+ if (!trunc) {
+ /* Since 32 bytes is enough to hold any 64 bit decimal number,
+ * !trunc is always true */
+ mg_response_header_add(conn, "Content-Length", len, -1);
+ }
}
+ mg_response_header_send(conn);
return 0;
}
@@ -4982,7 +5198,9 @@ mg_send_http_redirect(struct mg_connection *conn,
const char *redirect_text;
int ret;
size_t content_len = 0;
+#if defined(MG_SEND_REDIRECT_BODY)
char reply[MG_BUF_LEN];
+#endif
/* In case redirect_code=0, use 307. */
if (redirect_code == 0) {
@@ -5038,8 +5256,6 @@ mg_send_http_redirect(struct mg_connection *conn,
target_url,
target_url);
content_len = strlen(reply);
-#else
- reply[0] = 0;
#endif
/* Do not send any additional header. For all other options,
@@ -5055,6 +5271,7 @@ mg_send_http_redirect(struct mg_connection *conn,
(unsigned int)content_len,
suggest_connection_header(conn));
+#if defined(MG_SEND_REDIRECT_BODY)
/* Send response body */
if (ret > 0) {
/* ... unless it is a HEAD request */
@@ -5062,6 +5279,7 @@ mg_send_http_redirect(struct mg_connection *conn,
ret = mg_write(conn, reply, content_len);
}
}
+#endif
return (ret > 0) ? ret : -1;
}
@@ -5293,7 +5511,7 @@ change_slashes_to_backslashes(char *path)
/* remove double backslash (check i > 0 to preserve UNC paths,
* like \\server\file.txt) */
- if ((path[i] == '\\') && (i > 0)) {
+ if ((i > 0) && (path[i] == '\\')) {
while ((path[i + 1] == '\\') || (path[i + 1] == '/')) {
(void)memmove(path + i + 1, path + i + 2, strlen(path + i + 1));
}
@@ -5391,6 +5609,7 @@ path_to_unicode(const struct mg_connection *conn,
#if !defined(NO_FILESYSTEMS)
+/* Get file information, return 1 if file exists, 0 if not */
static int
mg_stat(const struct mg_connection *conn,
const char *path,
@@ -5406,33 +5625,8 @@ mg_stat(const struct mg_connection *conn,
}
memset(filep, 0, sizeof(*filep));
- if (conn && is_file_in_memory(conn, path)) {
- /* filep->is_directory = 0; filep->gzipped = 0; .. already done by
- * memset */
-
- /* Quick fix (for 1.9.x): */
- /* mg_stat must fill all fields, also for files in memory */
- struct mg_file tmp_file = STRUCT_FILE_INITIALIZER;
- open_file_in_memory(conn, path, &tmp_file, MG_FOPEN_MODE_NONE);
- filep->size = tmp_file.stat.size;
- filep->location = 2;
- /* TODO: for 1.10: restructure how files in memory are handled */
-
- /* The "file in memory" feature is a candidate for deletion.
- * Please join the discussion at
- * https://groups.google.com/forum/#!topic/civetweb/h9HT4CmeYqI
- */
-
- filep->last_modified = time(NULL); /* TODO */
- /* last_modified = now ... assumes the file may change during
- * runtime,
- * so every mg_fopen call may return different data */
- /* last_modified = conn->phys_ctx.start_time;
- * May be used it the data does not change during runtime. This
- * allows
- * browser caching. Since we do not know, we have to assume the file
- * in memory may change. */
- return 1;
+ if (mg_path_suspicious(conn, path)) {
+ return 0;
}
path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
@@ -5832,8 +6026,9 @@ spawn_process(struct mg_connection *conn,
const char *dir)
{
HANDLE me;
- char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX],
- cmdline[PATH_MAX], buf[PATH_MAX];
+ char *interp;
+ char *interp_arg = 0;
+ char full_dir[PATH_MAX], cmdline[PATH_MAX], buf[PATH_MAX];
int truncated;
struct mg_file file = STRUCT_FILE_INITIALIZER;
STARTUPINFOA si;
@@ -5883,12 +6078,19 @@ spawn_process(struct mg_connection *conn,
HANDLE_FLAG_INHERIT,
0);
- /* If CGI file is a script, try to read the interpreter line */
+ /* First check, if there is a CGI interpreter configured for all CGI
+ * scripts. */
interp = conn->dom_ctx->config[CGI_INTERPRETER];
- if (interp == NULL) {
+ if (interp != NULL) {
+ /* If there is a configured interpreter, check for additional arguments
+ */
+ interp_arg = conn->dom_ctx->config[CGI_INTERPRETER_ARGS];
+ } else {
+ /* Otherwise, the interpreter must be stated in the first line of the
+ * CGI script file, after a #! (shebang) mark. */
buf[0] = buf[1] = '\0';
- /* Read the first line of the script into the buffer */
+ /* Get the full script path */
mg_snprintf(
conn, &truncated, cmdline, sizeof(cmdline), "%s/%s", dir, prog);
@@ -5897,13 +6099,11 @@ spawn_process(struct mg_connection *conn,
goto spawn_cleanup;
}
+ /* Open the script file, to read the first line */
if (mg_fopen(conn, cmdline, MG_FOPEN_MODE_READ, &file)) {
-#if defined(MG_USE_OPEN_FILE)
- p = (char *)file.access.membuf;
-#else
- p = (char *)NULL;
-#endif
- mg_fgets(buf, sizeof(buf), &file, &p);
+
+ /* Read the first line of the script into the buffer */
+ mg_fgets(buf, sizeof(buf), &file);
(void)mg_fclose(&file.access); /* ignore error on read only file */
buf[sizeof(buf) - 1] = '\0';
}
@@ -5916,22 +6116,32 @@ spawn_process(struct mg_connection *conn,
interp = buf + 2;
}
- if (interp[0] != '\0') {
- GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL);
- interp = full_interp;
- }
GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL);
if (interp[0] != '\0') {
- mg_snprintf(conn,
- &truncated,
- cmdline,
- sizeof(cmdline),
- "\"%s\" \"%s\\%s\"",
- interp,
- full_dir,
- prog);
+ /* This is an interpreted script file. We must call the interpreter. */
+ if ((interp_arg != 0) && (interp_arg[0] != 0)) {
+ mg_snprintf(conn,
+ &truncated,
+ cmdline,
+ sizeof(cmdline),
+ "\"%s\" %s \"%s\\%s\"",
+ interp,
+ interp_arg,
+ full_dir,
+ prog);
+ } else {
+ mg_snprintf(conn,
+ &truncated,
+ cmdline,
+ sizeof(cmdline),
+ "\"%s\" \"%s\\%s\"",
+ interp,
+ full_dir,
+ prog);
+ }
} else {
+ /* This is (probably) a compiled program. We call it directly. */
mg_snprintf(conn,
&truncated,
cmdline,
@@ -5984,6 +6194,7 @@ set_blocking_mode(SOCKET sock)
return ioctlsocket(sock, (long)FIONBIO, &non_blocking);
}
+
static int
set_non_blocking_mode(SOCKET sock)
{
@@ -5991,8 +6202,10 @@ set_non_blocking_mode(SOCKET sock)
return ioctlsocket(sock, (long)FIONBIO, &non_blocking);
}
+
#else
+
#if !defined(NO_FILESYSTEMS)
static int
mg_stat(const struct mg_connection *conn,
@@ -6005,18 +6218,8 @@ mg_stat(const struct mg_connection *conn,
}
memset(filep, 0, sizeof(*filep));
- if (conn && is_file_in_memory(conn, path)) {
-
- /* Quick fix (for 1.9.x): */
- /* mg_stat must fill all fields, also for files in memory */
- struct mg_file tmp_file = STRUCT_FILE_INITIALIZER;
- open_file_in_memory(conn, path, &tmp_file, MG_FOPEN_MODE_NONE);
- filep->size = tmp_file.stat.size;
- filep->last_modified = time(NULL);
- filep->location = 2;
- /* TODO: remove legacy "files in memory" feature */
-
- return 1;
+ if (mg_path_suspicious(conn, path)) {
+ return 0;
}
if (0 == stat(path, &st)) {
@@ -6195,6 +6398,7 @@ spawn_process(struct mg_connection *conn,
interp = conn->dom_ctx->config[CGI_INTERPRETER];
if (interp == NULL) {
+ /* no interpreter configured, call the programm directly */
(void)execle(prog, prog, NULL, envp);
mg_cry_internal(conn,
"%s: execle(%s): %s",
@@ -6202,7 +6406,15 @@ spawn_process(struct mg_connection *conn,
prog,
strerror(ERRNO));
} else {
- (void)execle(interp, interp, prog, NULL, envp);
+ /* call the configured interpreter */
+ const char *interp_args =
+ conn->dom_ctx->config[CGI_INTERPRETER_ARGS];
+
+ if ((interp_args != NULL) && (interp_args[0] != 0)) {
+ (void)execle(interp, interp, interp_args, prog, NULL, envp);
+ } else {
+ (void)execle(interp, interp, prog, NULL, envp);
+ }
mg_cry_internal(conn,
"%s: execle(%s %s): %s",
__func__,
@@ -6285,7 +6497,7 @@ static int
mg_poll(struct mg_pollfd *pfd,
unsigned int n,
int milliseconds,
- volatile int *stop_server)
+ stop_flag_t *stop_flag)
{
/* Call poll, but only for a maximum time of a few seconds.
* This will allow to stop the server after some seconds, instead
@@ -6295,7 +6507,7 @@ mg_poll(struct mg_pollfd *pfd,
do {
int result;
- if (*stop_server) {
+ if (!STOP_FLAG_IS_ZERO(&*stop_flag)) {
/* Shut down signal */
return -2;
}
@@ -6316,7 +6528,7 @@ mg_poll(struct mg_pollfd *pfd,
milliseconds -= ms_now;
}
- } while (milliseconds != 0);
+ } while (milliseconds > 0);
/* timeout: return 0 */
return 0;
@@ -6371,6 +6583,7 @@ push_inner(struct mg_context *ctx,
#if !defined(NO_SSL)
if (ssl != NULL) {
+ ERR_clear_error();
n = SSL_write(ssl, buf, len);
if (n <= 0) {
err = SSL_get_error(ssl, n);
@@ -6381,8 +6594,10 @@ push_inner(struct mg_context *ctx,
n = 0;
} else {
DEBUG_TRACE("SSL_write() failed, error %d", err);
+ ERR_clear_error();
return -2;
}
+ ERR_clear_error();
} else {
err = 0;
}
@@ -6405,7 +6620,7 @@ push_inner(struct mg_context *ctx,
n = 0;
}
#else
- if (err == EWOULDBLOCK) {
+ if (ERROR_TRY_AGAIN(err)) {
err = 0;
n = 0;
}
@@ -6416,7 +6631,7 @@ push_inner(struct mg_context *ctx,
}
}
- if (ctx->stop_flag) {
+ if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
return -2;
}
@@ -6452,7 +6667,7 @@ push_inner(struct mg_context *ctx,
pfd[0].fd = sock;
pfd[0].events = POLLOUT;
pollres = mg_poll(pfd, 1, (int)(ms_wait), &(ctx->stop_flag));
- if (ctx->stop_flag) {
+ if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
return -2;
}
if (pollres > 0) {
@@ -6494,8 +6709,11 @@ push_all(struct mg_context *ctx,
if (ctx->dd.config[REQUEST_TIMEOUT]) {
timeout = atoi(ctx->dd.config[REQUEST_TIMEOUT]) / 1000.0;
}
+ if (timeout <= 0.0) {
+ timeout = atof(config_options[REQUEST_TIMEOUT].default_value) / 1000.0;
+ }
- while ((len > 0) && (ctx->stop_flag == 0)) {
+ while ((len > 0) && STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
n = push_inner(ctx, fp, sock, ssl, buf + nwritten, len, timeout);
if (n < 0) {
if (nwritten == 0) {
@@ -6534,9 +6752,6 @@ pull_inner(FILE *fp,
#else
typedef size_t len_t;
#endif
-#if !defined(NO_SSL)
- int ssl_pending;
-#endif
/* We need an additional wait loop around this, because in some cases
* with TLSwe may get data from the socket but not from SSL_read.
@@ -6561,49 +6776,34 @@ pull_inner(FILE *fp,
}
#if !defined(NO_SSL)
- } else if ((conn->ssl != NULL)
- && ((ssl_pending = SSL_pending(conn->ssl)) > 0)) {
- /* We already know there is no more data buffered in conn->buf
- * but there is more available in the SSL layer. So don't poll
- * conn->client.sock yet. */
- if (ssl_pending > len) {
- ssl_pending = len;
- }
- nread = SSL_read(conn->ssl, buf, ssl_pending);
- if (nread <= 0) {
- err = SSL_get_error(conn->ssl, nread);
- if ((err == SSL_ERROR_SYSCALL) && (nread == -1)) {
- err = ERRNO;
- } else if ((err == SSL_ERROR_WANT_READ)
- || (err == SSL_ERROR_WANT_WRITE)) {
- nread = 0;
- } else {
- /* All errors should return -2 */
- DEBUG_TRACE("SSL_read() failed, error %d", err);
- return -2;
- }
-
- ERR_clear_error();
- } else {
- err = 0;
- }
-
} else if (conn->ssl != NULL) {
-
+ int ssl_pending;
struct mg_pollfd pfd[1];
int pollres;
- pfd[0].fd = conn->client.sock;
- pfd[0].events = POLLIN;
- pollres = mg_poll(pfd,
- 1,
- (int)(timeout * 1000.0),
- &(conn->phys_ctx->stop_flag));
- if (conn->phys_ctx->stop_flag) {
- return -2;
+ if ((ssl_pending = SSL_pending(conn->ssl)) > 0) {
+ /* We already know there is no more data buffered in conn->buf
+ * but there is more available in the SSL layer. So don't poll
+ * conn->client.sock yet. */
+ if (ssl_pending > len) {
+ ssl_pending = len;
+ }
+ pollres = 1;
+ } else {
+ pfd[0].fd = conn->client.sock;
+ pfd[0].events = POLLIN;
+ pollres = mg_poll(pfd,
+ 1,
+ (int)(timeout * 1000.0),
+ &(conn->phys_ctx->stop_flag));
+ if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
+ return -2;
+ }
}
if (pollres > 0) {
- nread = SSL_read(conn->ssl, buf, len);
+ ERR_clear_error();
+ nread =
+ SSL_read(conn->ssl, buf, (ssl_pending > 0) ? ssl_pending : len);
if (nread <= 0) {
err = SSL_get_error(conn->ssl, nread);
if ((err == SSL_ERROR_SYSCALL) && (nread == -1)) {
@@ -6612,13 +6812,15 @@ pull_inner(FILE *fp,
|| (err == SSL_ERROR_WANT_WRITE)) {
nread = 0;
} else {
+ /* All errors should return -2 */
DEBUG_TRACE("SSL_read() failed, error %d", err);
+ ERR_clear_error();
return -2;
}
+ ERR_clear_error();
} else {
err = 0;
}
- ERR_clear_error();
} else if (pollres < 0) {
/* Error */
return -2;
@@ -6638,7 +6840,7 @@ pull_inner(FILE *fp,
1,
(int)(timeout * 1000.0),
&(conn->phys_ctx->stop_flag));
- if (conn->phys_ctx->stop_flag) {
+ if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
return -2;
}
if (pollres > 0) {
@@ -6657,7 +6859,7 @@ pull_inner(FILE *fp,
}
}
- if (conn->phys_ctx->stop_flag) {
+ if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
return -2;
}
@@ -6690,7 +6892,7 @@ pull_inner(FILE *fp,
* blocking in close_socket_gracefully, so we can not distinguish
* here. We have to wait for the timeout in both cases for now.
*/
- if ((err == EAGAIN) || (err == EWOULDBLOCK) || (err == EINTR)) {
+ if (ERROR_TRY_AGAIN(err)) {
/* TODO (low): check if this is still required */
/* EAGAIN/EWOULDBLOCK:
* standard case if called from close_socket_gracefully
@@ -6724,12 +6926,13 @@ pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
}
- if (timeout >= 0.0) {
- start_time = mg_get_current_time_ns();
- timeout_ns = (uint64_t)(timeout * 1.0E9);
+ if (timeout <= 0.0) {
+ timeout = atof(config_options[REQUEST_TIMEOUT].default_value) / 1000.0;
}
+ start_time = mg_get_current_time_ns();
+ timeout_ns = (uint64_t)(timeout * 1.0E9);
- while ((len > 0) && (conn->phys_ctx->stop_flag == 0)) {
+ while ((len > 0) && STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
n = pull_inner(fp, conn, buf + nread, len, timeout);
if (n == -2) {
if (nread == 0) {
@@ -6829,6 +7032,29 @@ mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
}
+/* Forward declarations */
+static void handle_request(struct mg_connection *);
+
+
+#if defined(USE_HTTP2)
+#if defined(NO_SSL)
+#error "HTTP2 requires ALPN, APLN requires SSL/TLS"
+#endif
+#define USE_ALPN
+#include "mod_http2.inl"
+/* Not supported with HTTP/2 */
+#define HTTP1_only \
+ { \
+ if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) { \
+ http2_must_use_http1(conn); \
+ return; \
+ } \
+ }
+#else
+#define HTTP1_only
+#endif
+
+
int
mg_read(struct mg_connection *conn, void *buf, size_t len)
{
@@ -6927,7 +7153,7 @@ mg_read(struct mg_connection *conn, void *buf, size_t len)
}
/* append a new chunk */
- conn->content_len += chunkSize;
+ conn->content_len += (int64_t)chunkSize;
}
}
@@ -6950,6 +7176,14 @@ mg_write(struct mg_connection *conn, const void *buf, size_t len)
return -1;
}
+ /* Mark connection as "data sent" */
+ conn->request_state = 10;
+#if defined(USE_HTTP2)
+ if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
+ http2_data_frame_head(conn, len, 0);
+ }
+#endif
+
if (conn->throttle > 0) {
if ((now = time(NULL)) != conn->last_throttle_time) {
conn->last_throttle_time = now;
@@ -6959,26 +7193,32 @@ mg_write(struct mg_connection *conn, const void *buf, size_t len)
if (allowed > (int)len) {
allowed = (int)len;
}
- if ((total = push_all(conn->phys_ctx,
- NULL,
- conn->client.sock,
- conn->ssl,
- (const char *)buf,
- allowed))
- == allowed) {
+
+ total = push_all(conn->phys_ctx,
+ NULL,
+ conn->client.sock,
+ conn->ssl,
+ (const char *)buf,
+ allowed);
+
+ if (total == allowed) {
+
buf = (const char *)buf + total;
conn->last_throttle_bytes += total;
- while ((total < (int)len) && (conn->phys_ctx->stop_flag == 0)) {
+ while ((total < (int)len)
+ && STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
allowed = (conn->throttle > ((int)len - total))
? (int)len - total
: conn->throttle;
- if ((n = push_all(conn->phys_ctx,
- NULL,
- conn->client.sock,
- conn->ssl,
- (const char *)buf,
- allowed))
- != allowed) {
+
+ n = push_all(conn->phys_ctx,
+ NULL,
+ conn->client.sock,
+ conn->ssl,
+ (const char *)buf,
+ allowed);
+
+ if (n != allowed) {
break;
}
sleep(1);
@@ -7209,6 +7449,15 @@ mg_url_decode(const char *src,
}
+/* form url decoding of an entire string */
+static void
+url_decode_in_place(char *buf)
+{
+ int len = (int)strlen(buf);
+ (void)mg_url_decode(buf, len, buf, len + 1, 1);
+}
+
+
int
mg_get_var(const char *data,
size_t data_len,
@@ -7277,6 +7526,105 @@ mg_get_var2(const char *data,
}
+/* split a string "key1=val1&key2=val2" into key/value pairs */
+int
+mg_split_form_urlencoded(char *data,
+ struct mg_header *form_fields,
+ unsigned num_form_fields)
+{
+ char *b;
+ int i;
+ int num = 0;
+
+ if (data == NULL) {
+ /* parameter error */
+ return -1;
+ }
+
+ if ((form_fields == NULL) && (num_form_fields == 0)) {
+ /* determine the number of expected fields */
+ if (data[0] == 0) {
+ return 0;
+ }
+ /* count number of & to return the number of key-value-pairs */
+ num = 1;
+ while (*data) {
+ if (*data == '&') {
+ num++;
+ }
+ data++;
+ }
+ return num;
+ }
+
+ if ((form_fields == NULL) || ((int)num_form_fields <= 0)) {
+ /* parameter error */
+ return -1;
+ }
+
+ for (i = 0; i < (int)num_form_fields; i++) {
+ /* extract key-value pairs from input data */
+ while ((*data == ' ') || (*data == '\t')) {
+ /* skip initial spaces */
+ data++;
+ }
+ if (*data == 0) {
+ /* end of string reached */
+ break;
+ }
+ form_fields[num].name = data;
+
+ /* find & or = */
+ b = data;
+ while ((*b != 0) && (*b != '&') && (*b != '=')) {
+ b++;
+ }
+
+ if (*b == 0) {
+ /* last key without value */
+ form_fields[num].value = NULL;
+ } else if (*b == '&') {
+ /* mid key without value */
+ form_fields[num].value = NULL;
+ } else {
+ /* terminate string */
+ *b = 0;
+ /* value starts after '=' */
+ data = b + 1;
+ form_fields[num].value = data;
+ }
+
+ /* new field is stored */
+ num++;
+
+ /* find a next key */
+ b = strchr(data, '&');
+ if (b == 0) {
+ /* no more data */
+ break;
+ } else {
+ /* terminate value of last field at '&' */
+ *b = 0;
+ /* next key-value-pairs starts after '&' */
+ data = b + 1;
+ }
+ }
+
+ /* Decode all values */
+ for (i = 0; i < num; i++) {
+ if (form_fields[i].name) {
+ url_decode_in_place((char *)form_fields[i].name);
+ }
+ if (form_fields[i].value) {
+ url_decode_in_place((char *)form_fields[i].value);
+ }
+ }
+
+ /* return number of fields found */
+ return num;
+}
+
+
/* HCP24: some changes to compare hole var_name */
int
mg_get_cookie(const char *cookie_header,
@@ -7446,25 +7794,25 @@ extention_matches_script(
)
{
#if !defined(NO_CGI)
- if (match_prefix(conn->dom_ctx->config[CGI_EXTENSIONS],
- strlen(conn->dom_ctx->config[CGI_EXTENSIONS]),
- filename)
+ if (match_prefix_strlen(conn->dom_ctx->config[CGI_EXTENSIONS], filename)
+ > 0) {
+ return 1;
+ }
+ if (match_prefix_strlen(conn->dom_ctx->config[CGI2_EXTENSIONS], filename)
> 0) {
return 1;
}
#endif
#if defined(USE_LUA)
- if (match_prefix(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS],
- strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS]),
- filename)
+ if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS],
+ filename)
> 0) {
return 1;
}
#endif
#if defined(USE_DUKTAPE)
- if (match_prefix(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
- strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
- filename)
+ if (match_prefix_strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
+ filename)
> 0) {
return 1;
}
@@ -7478,6 +7826,27 @@ extention_matches_script(
}
+static int
+extention_matches_template_text(
+ struct mg_connection *conn, /* in: request (must be valid) */
+ const char *filename /* in: filename (must be valid) */
+)
+{
+#if defined(USE_LUA)
+ if (match_prefix_strlen(conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
+ filename)
+ > 0) {
+ return 1;
+ }
+#endif
+ if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], filename)
+ > 0) {
+ return 1;
+ }
+ return 0;
+}
+
+
/* For given directory path, substitute it to valid index file.
* Return 1 if index file has been found, 0 if not found.
* If the file is found, it's stats is returned in stp. */
@@ -7537,7 +7906,8 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
int *is_found, /* out: file found (directly) */
int *is_script_resource, /* out: handled by a script? */
int *is_websocket_request, /* out: websocket connetion? */
- int *is_put_or_delete_request /* out: put/delete a file? */
+ int *is_put_or_delete_request, /* out: put/delete a file? */
+ int *is_template_text /* out: SSI file or LSP file? */
)
{
char const *accept_encoding;
@@ -7564,6 +7934,7 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
*filename = 0;
*is_found = 0;
*is_script_resource = 0;
+ *is_template_text = 0;
/* Step 2: Check if the request attempts to modify the file system */
*is_put_or_delete_request = is_put_or_delete_method(conn);
@@ -7571,7 +7942,7 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
/* Step 3: Check if it is a websocket request, and modify the document
* root if required */
#if defined(USE_WEBSOCKET)
- *is_websocket_request = is_websocket_protocol(conn);
+ *is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET);
#if !defined(NO_FILES)
if (*is_websocket_request && conn->dom_ctx->config[WEBSOCKET_ROOT]) {
root = conn->dom_ctx->config[WEBSOCKET_ROOT];
@@ -7604,6 +7975,7 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
* request uri. */
/* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift
* part of the path one byte on the right. */
+ truncated = 0;
mg_snprintf(
conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri);
@@ -7657,7 +8029,17 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
*is_script_resource = (!*is_put_or_delete_request);
}
- /* 8.3: If the request target is a directory, there could be
+ /* 8.3: Check for SSI and LSP files */
+ if (extention_matches_template_text(conn, filename)) {
+ /* Same as above, but for *.lsp and *.shtml files. */
+ /* A "template text" is a file delivered directly to the client,
+ * but with some text tags replaced by dynamic content.
+ * E.g. a Server Side Include (SSI) or Lua Page/Lua Server Page
+ * (LP, LSP) file. */
+ *is_template_text = (!*is_put_or_delete_request);
+ }
+
+ /* 8.4: If the request target is a directory, there could be
* a substitute file (index.html, index.cgi, ...). */
if (filestat->is_directory && is_uri_end_slash) {
/* Use a local copy here, since substitute_index_file will
@@ -7675,6 +8057,9 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
if (extention_matches_script(conn, filename)) {
/* Substitute file is a script file */
*is_script_resource = 1;
+ } else if (extention_matches_template_text(conn, filename)) {
+ /* Substitute file is a LSP or SSI file */
+ *is_template_text = 1;
} else {
/* Substitute file is a regular file */
*is_script_resource = 0;
@@ -7759,31 +8144,30 @@ interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
/* some intermediate directory has an index file */
if (extention_matches_script(conn, tmp_str)) {
- char *tmp_str2;
+ size_t script_name_len = strlen(tmp_str);
+
+ /* subres_name read before this memory locatio will be
+ overwritten */
+ char *subres_name = filename + sep_pos;
+ size_t subres_name_len = strlen(subres_name);
DEBUG_TRACE("Substitute script %s serving path %s",
tmp_str,
filename);
/* this index file is a script */
- tmp_str2 = mg_strdup_ctx(filename + sep_pos + 1,
- conn->phys_ctx);
- mg_snprintf(conn,
- &truncated,
- filename,
- filename_buf_len,
- "%s//%s",
- tmp_str,
- tmp_str2);
- mg_free(tmp_str2);
-
- if (truncated) {
+ if ((script_name_len + subres_name_len + 2)
+ >= filename_buf_len) {
mg_free(tmp_str);
goto interpret_cleanup;
}
- sep_pos = strlen(tmp_str);
- filename[sep_pos] = 0;
- conn->path_info = filename + sep_pos + 1;
+
+ conn->path_info =
+ filename + script_name_len + 1; /* new target */
+ memmove(conn->path_info, subres_name, subres_name_len);
+ conn->path_info[subres_name_len] = 0;
+ memcpy(filename, tmp_str, script_name_len + 1);
+
*is_script_resource = 1;
*is_found = 1;
break;
@@ -7848,9 +8232,7 @@ get_http_header_len(const char *buf, int buflen)
if (i < buflen - 1) {
if ((buf[i] == '\n') && (buf[i + 1] == '\n')) {
/* Two newline, no carriage return - not standard compliant,
- * but
- * it
- * should be accepted */
+ * but it should be accepted */
return i + 2;
}
}
@@ -7954,13 +8336,15 @@ static void
remove_dot_segments(char *inout)
{
/* Windows backend protection
- * (https://tools.ietf.org/html/rfc3986#section-7.3): Replace backslash in
- * URI by slash */
- char *in_copy = mg_strdup(inout);
- char *out_begin = inout;
+ * (https://tools.ietf.org/html/rfc3986#section-7.3): Replace backslash
+ * in URI by slash */
char *out_end = inout;
- char *in = in_copy;
- int replaced;
+ char *in = inout;
+
+ if (!in) {
+ /* Param error. */
+ return;
+ }
while (*in) {
if (*in == '\\') {
@@ -7975,11 +8359,13 @@ remove_dot_segments(char *inout)
* The input buffer is initialized.
* The output buffer is initialized to the empty string.
*/
- in = in_copy;
+ in = inout;
/* Step 2:
* While the input buffer is not empty, loop as follows:
*/
+ /* Less than out_end of the inout buffer is used as output, so keep
+ * condition: out_end <= in */
while (*in) {
/* Step 2a:
* If the input buffer begins with a prefix of "../" or "./",
@@ -8011,21 +8397,19 @@ remove_dot_segments(char *inout)
*/
else if (!strncmp(in, "/../", 4)) {
in += 3;
- if (out_begin != out_end) {
+ if (inout != out_end) {
/* remove last segment */
do {
out_end--;
- *out_end = 0;
- } while ((out_begin != out_end) && (*out_end != '/'));
+ } while ((inout != out_end) && (*out_end != '/'));
}
} else if (!strcmp(in, "/..")) {
in[1] = 0;
- if (out_begin != out_end) {
+ if (inout != out_end) {
/* remove last segment */
do {
out_end--;
- *out_end = 0;
- } while ((out_begin != out_end) && (*out_end != '/'));
+ } while ((inout != out_end) && (*out_end != '/'));
}
}
/* otherwise */
@@ -8060,47 +8444,44 @@ remove_dot_segments(char *inout)
*out_end = 0;
/* For Windows, the files/folders "x" and "x." (with a dot but without
- * extension) are identical. Replace all "./" by "/" and remove a "." at the
- * end.
- * Also replace all "//" by "/".
- * Repeat until there is no "./" or "//" anymore.
+ * extension) are identical. Replace all "./" by "/" and remove a "." at
+ * the end. Also replace all "//" by "/". Repeat until there is no "./"
+ * or "//" anymore.
*/
- do {
- replaced = 0;
-
- /* replace ./ by / */
- out_end = out_begin;
- while (*out_end) {
- if ((*out_end == '.')
- && ((out_end[1] == '/') || (out_end[1] == 0))) {
- char *r = out_end;
- do {
- r[0] = r[1];
- r++;
- replaced = 1;
- } while (r[0] != 0);
- }
- out_end++;
- }
-
- /* replace ./ by / */
- out_end = out_begin;
- while (*out_end) {
- if ((out_end[0] == '/') && (out_end[1] == '/')) {
- char *c = out_end;
- while (*c) {
- c[0] = c[1];
- c++;
+ out_end = in = inout;
+ while (*in) {
+ if (*in == '.') {
+ /* remove . at the end or preceding of / */
+ char *in_ahead = in;
+ do {
+ in_ahead++;
+ } while (*in_ahead == '.');
+ if (*in_ahead == '/') {
+ in = in_ahead;
+ if ((out_end != inout) && (out_end[-1] == '/')) {
+ /* remove generated // */
+ out_end--;
}
- replaced = 1;
+ } else if (*in_ahead == 0) {
+ in = in_ahead;
+ } else {
+ do {
+ *out_end++ = '.';
+ in++;
+ } while (in != in_ahead);
}
- out_end++;
+ } else if (*in == '/') {
+ /* replace // by / */
+ *out_end++ = '/';
+ do {
+ in++;
+ } while (*in == '/');
+ } else {
+ *out_end++ = *in;
+ in++;
}
-
- } while (replaced);
-
- /* Free temporary copies */
- mg_free(in_copy);
+ }
+ *out_end = 0;
}
@@ -8361,7 +8742,8 @@ open_auth_file(struct mg_connection *conn,
/* Use global passwords file */
if (!mg_fopen(conn, gpass, MG_FOPEN_MODE_READ, filep)) {
#if defined(DEBUG)
- /* Use mg_cry_internal here, since gpass has been configured. */
+ /* Use mg_cry_internal here, since gpass has been
+ * configured. */
mg_cry_internal(conn, "fopen(%s): %s", gpass, strerror(ERRNO));
#endif
}
@@ -8382,9 +8764,8 @@ open_auth_file(struct mg_connection *conn,
if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
#if defined(DEBUG)
- /* Don't use mg_cry_internal here, but only a trace, since this
- * is
- * a typical case. It will occur for every directory
+ /* Don't use mg_cry_internal here, but only a trace, since
+ * this is a typical case. It will occur for every directory
* without a password file. */
DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO));
#endif
@@ -8407,9 +8788,8 @@ open_auth_file(struct mg_connection *conn,
if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
#if defined(DEBUG)
- /* Don't use mg_cry_internal here, but only a trace, since this
- * is
- * a typical case. It will occur for every directory
+ /* Don't use mg_cry_internal here, but only a trace, since
+ * this is a typical case. It will occur for every directory
* without a password file. */
DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO));
#endif
@@ -8467,8 +8847,8 @@ parse_auth_header(struct mg_connection *conn,
s++;
}
} else {
- value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF uses
- * spaces */
+ value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF
+ * uses spaces */
}
if (*name == '\0') {
break;
@@ -8541,39 +8921,13 @@ parse_auth_header(struct mg_connection *conn,
static const char *
-mg_fgets(char *buf, size_t size, struct mg_file *filep, char **p)
+mg_fgets(char *buf, size_t size, struct mg_file *filep)
{
-#if defined(MG_USE_OPEN_FILE)
- const char *eof;
- size_t len;
- const char *memend;
-#else
- (void)p; /* parameter is unused */
-#endif
-
if (!filep) {
return NULL;
}
-#if defined(MG_USE_OPEN_FILE)
- if ((filep->access.membuf != NULL) && (*p != NULL)) {
- memend = (const char *)&filep->access.membuf[filep->stat.size];
- /* Search for \n from p till the end of stream */
- eof = (char *)memchr(*p, '\n', (size_t)(memend - *p));
- if (eof != NULL) {
- eof += 1; /* Include \n */
- } else {
- eof = memend; /* Copy remaining data */
- }
- len =
- ((size_t)(eof - *p) > (size - 1)) ? (size - 1) : (size_t)(eof - *p);
- memcpy(buf, *p, len);
- buf[len] = '\0';
- *p += len;
- return len ? eof : NULL;
- } else /* filep->access.fp block below */
-#endif
- if (filep->access.fp != NULL) {
+ if (filep->access.fp != NULL) {
return fgets(buf, (int)size, filep->access.fp);
} else {
return NULL;
@@ -8608,7 +8962,6 @@ read_auth_file(struct mg_file *filep,
struct read_auth_file_struct *workdata,
int depth)
{
- char *p = NULL /* init if MG_USE_OPEN_FILE is not set */;
int is_authorized = 0;
struct mg_file fp;
size_t l;
@@ -8617,11 +8970,8 @@ read_auth_file(struct mg_file *filep,
return 0;
}
-/* Loop over passwords file */
-#if defined(MG_USE_OPEN_FILE)
- p = (char *)filep->access.membuf;
-#endif
- while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep, &p) != NULL) {
+ /* Loop over passwords file */
+ while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep) != NULL) {
l = strlen(workdata->buf);
while (l > 0) {
if (isspace((unsigned char)workdata->buf[l - 1])
@@ -8829,38 +9179,45 @@ check_authorization(struct mg_connection *conn, const char *path)
static void
send_authorization_request(struct mg_connection *conn, const char *realm)
{
- char date[64];
- time_t curtime = time(NULL);
uint64_t nonce = (uint64_t)(conn->phys_ctx->start_time);
+ int trunc = 0;
+ char buf[128];
if (!realm) {
realm = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
}
- (void)pthread_mutex_lock(&conn->phys_ctx->nonce_mutex);
+ mg_lock_context(conn->phys_ctx);
nonce += conn->dom_ctx->nonce_count;
++conn->dom_ctx->nonce_count;
- (void)pthread_mutex_unlock(&conn->phys_ctx->nonce_mutex);
+ mg_unlock_context(conn->phys_ctx);
nonce ^= conn->dom_ctx->auth_nonce_mask;
- conn->status_code = 401;
conn->must_close = 1;
- gmt_time_string(date, sizeof(date), &curtime);
-
- mg_printf(conn, "HTTP/1.1 401 Unauthorized\r\n");
+ /* Create 401 response */
+ mg_response_header_start(conn, 401);
send_no_cache_header(conn);
send_additional_header(conn);
- mg_printf(conn,
- "Date: %s\r\n"
- "Connection: %s\r\n"
- "Content-Length: 0\r\n"
- "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
- "nonce=\"%" UINT64_FMT "\"\r\n\r\n",
- date,
- suggest_connection_header(conn),
- realm,
- nonce);
+ mg_response_header_add(conn, "Content-Length", "0", -1);
+
+ /* Content for "WWW-Authenticate" header */
+ mg_snprintf(conn,
+ &trunc,
+ buf,
+ sizeof(buf),
+ "Digest qop=\"auth\", realm=\"%s\", "
+ "nonce=\"%" UINT64_FMT "\"",
+ realm,
+ nonce);
+
+ if (!trunc) {
+ /* !trunc should always be true */
+ mg_response_header_add(conn, "WWW-Authenticate", buf, -1);
+ }
+
+ /* Send all headers */
+ mg_response_header_send(conn);
}
@@ -9021,7 +9378,7 @@ is_valid_port(unsigned long port)
static int
-mg_inet_pton(int af, const char *src, void *dst, size_t dstlen)
+mg_inet_pton(int af, const char *src, void *dst, size_t dstlen, int resolve_src)
{
struct addrinfo hints, *res, *ressave;
int func_ret = 0;
@@ -9029,6 +9386,9 @@ mg_inet_pton(int af, const char *src, void *dst, size_t dstlen)
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = af;
+ if (!resolve_src) {
+ hints.ai_flags = AI_NUMERICHOST;
+ }
gai_ret = getaddrinfo(src, NULL, &hints, &res);
if (gai_ret != 0) {
@@ -9045,7 +9405,8 @@ mg_inet_pton(int af, const char *src, void *dst, size_t dstlen)
ressave = res;
while (res) {
- if (dstlen >= (size_t)res->ai_addrlen) {
+ if ((dstlen >= (size_t)res->ai_addrlen)
+ && (res->ai_addr->sa_family == af)) {
memcpy(dst, res->ai_addr, res->ai_addrlen);
func_ret = 1;
}
@@ -9129,13 +9490,11 @@ connect_socket(struct mg_context *ctx /* may be NULL */,
(void)use_ssl;
#endif /* !defined(NO_SSL) */
- if (mg_inet_pton(AF_INET, host, &sa->sin, sizeof(sa->sin))) {
- sa->sin.sin_family = AF_INET;
+ if (mg_inet_pton(AF_INET, host, &sa->sin, sizeof(sa->sin), 1)) {
sa->sin.sin_port = htons((uint16_t)port);
ip_ver = 4;
#if defined(USE_IPV6)
- } else if (mg_inet_pton(AF_INET6, host, &sa->sin6, sizeof(sa->sin6))) {
- sa->sin6.sin6_family = AF_INET6;
+ } else if (mg_inet_pton(AF_INET6, host, &sa->sin6, sizeof(sa->sin6), 1)) {
sa->sin6.sin6_port = htons((uint16_t)port);
ip_ver = 6;
} else if (host[0] == '[') {
@@ -9145,8 +9504,7 @@ connect_socket(struct mg_context *ctx /* may be NULL */,
char *h = (l > 1) ? mg_strdup_ctx(host + 1, ctx) : NULL;
if (h) {
h[l - 1] = 0;
- if (mg_inet_pton(AF_INET6, h, &sa->sin6, sizeof(sa->sin6))) {
- sa->sin6.sin6_family = AF_INET6;
+ if (mg_inet_pton(AF_INET6, h, &sa->sin6, sizeof(sa->sin6), 0)) {
sa->sin6.sin6_port = htons((uint16_t)port);
ip_ver = 6;
}
@@ -9236,7 +9594,8 @@ connect_socket(struct mg_context *ctx /* may be NULL */,
struct mg_pollfd pfd[1];
int pollres;
int ms_wait = 10000; /* 10 second timeout */
- int nonstop = 0;
+ stop_flag_t nonstop;
+ STOP_FLAG_ASSIGN(&nonstop, 0);
/* For a non-blocking socket, the connect sequence is:
* 1) call connect (will not block)
@@ -9471,9 +9830,8 @@ must_hide_file(struct mg_connection *conn, const char *path)
if (conn && conn->dom_ctx) {
const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
const char *pattern = conn->dom_ctx->config[HIDE_FILES];
- return (match_prefix(pw_pattern, strlen(pw_pattern), path) > 0)
- || ((pattern != NULL)
- && (match_prefix(pattern, strlen(pattern), path) > 0));
+ return (match_prefix_strlen(pw_pattern, path) > 0)
+ || (match_prefix_strlen(pattern, path) > 0);
}
return 0;
}
@@ -9527,7 +9885,10 @@ scan_directory(struct mg_connection *conn,
strerror(ERRNO));
}
de.file_name = dp->d_name;
- cb(&de, data);
+ if (cb(&de, data)) {
+ /* stopped */
+ break;
+ }
}
(void)mg_closedir(dirp);
}
@@ -9607,44 +9968,37 @@ remove_directory(struct mg_connection *conn, const char *dir)
struct dir_scan_data {
struct de *entries;
- unsigned int num_entries;
- unsigned int arr_size;
+ size_t num_entries;
+ size_t arr_size;
};
-/* Behaves like realloc(), but frees original pointer on failure */
-static void *
-realloc2(void *ptr, size_t size)
-{
- void *new_ptr = mg_realloc(ptr, size);
- if ((new_ptr == NULL) && (size > 0)) {
- mg_free(ptr);
- }
- return new_ptr;
-}
-
-
#if !defined(NO_FILESYSTEMS)
static int
dir_scan_callback(struct de *de, void *data)
{
struct dir_scan_data *dsd = (struct dir_scan_data *)data;
+ struct de *entries = dsd->entries;
- if ((dsd->entries == NULL) || (dsd->num_entries >= dsd->arr_size)) {
+ if ((entries == NULL) || (dsd->num_entries >= dsd->arr_size)) {
+ entries =
+ (struct de *)mg_realloc(entries,
+ dsd->arr_size * 2 * sizeof(entries[0]));
+ if (entries == NULL) {
+ /* stop scan */
+ return 1;
+ }
+ dsd->entries = entries;
dsd->arr_size *= 2;
- dsd->entries =
- (struct de *)realloc2(dsd->entries,
- dsd->arr_size * sizeof(dsd->entries[0]));
}
- if (dsd->entries == NULL) {
- /* TODO(lsm, low): propagate an error to the caller */
- dsd->num_entries = 0;
- } else {
- dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
- dsd->entries[dsd->num_entries].file = de->file;
- dsd->entries[dsd->num_entries].conn = de->conn;
- dsd->num_entries++;
+ entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
+ if (entries[dsd->num_entries].file_name == NULL) {
+ /* stop scan */
+ return 1;
}
+ entries[dsd->num_entries].file = de->file;
+ entries[dsd->num_entries].conn = de->conn;
+ dsd->num_entries++;
return 0;
}
@@ -9653,13 +10007,17 @@ dir_scan_callback(struct de *de, void *data)
static void
handle_directory_request(struct mg_connection *conn, const char *dir)
{
- unsigned int i;
+ size_t i;
int sort_direction;
struct dir_scan_data data = {NULL, 0, 128};
char date[64], *esc, *p;
const char *title;
time_t curtime = time(NULL);
+ if (!conn) {
+ return;
+ }
+
if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
mg_send_http_error(conn,
500,
@@ -9671,10 +10029,6 @@ handle_directory_request(struct mg_connection *conn, const char *dir)
gmt_time_string(date, sizeof(date), &curtime);
- if (!conn) {
- return;
- }
-
esc = NULL;
title = conn->request_info.local_uri;
if (title[strcspn(title, "&<>")]) {
@@ -9703,14 +10057,20 @@ handle_directory_request(struct mg_connection *conn, const char *dir)
: 'd';
conn->must_close = 1;
- mg_printf(conn, "HTTP/1.1 200 OK\r\n");
+
+ /* Create 200 OK response */
+ mg_response_header_start(conn, 200);
send_static_cache_header(conn);
send_additional_header(conn);
- mg_printf(conn,
- "Date: %s\r\n"
- "Connection: close\r\n"
- "Content-Type: text/html; charset=utf-8\r\n\r\n",
- date);
+ mg_response_header_add(conn,
+ "Content-Type",
+ "text/html; charset=utf-8",
+ -1);
+
+ /* Send all headers */
+ mg_response_header_send(conn);
+
+ /* Body */
mg_printf(conn,
"
Index of %s"
""
@@ -9738,7 +10098,7 @@ handle_directory_request(struct mg_connection *conn, const char *dir)
/* Sort and print directory entries */
if (data.entries != NULL) {
qsort(data.entries,
- (size_t)data.num_entries,
+ data.num_entries,
sizeof(data.entries[0]),
compare_dir_entries);
for (i = 0; i < data.num_entries; i++) {
@@ -9774,16 +10134,7 @@ send_file_data(struct mg_connection *conn,
: (int64_t)(filep->stat.size);
offset = (offset < 0) ? 0 : ((offset > size) ? size : offset);
-#if defined(MG_USE_OPEN_FILE)
- if ((len > 0) && (filep->access.membuf != NULL) && (size > 0)) {
- /* file stored in memory */
- if (len > size - offset) {
- len = size - offset;
- }
- mg_write(conn, filep->access.membuf + offset, (size_t)len);
- } else /* else block below */
-#endif
- if (len > 0 && filep->access.fp != NULL) {
+ if (len > 0 && filep->access.fp != NULL) {
/* file stored on disk */
#if defined(__linux__)
/* sendfile is only available for Linux */
@@ -9921,25 +10272,23 @@ handle_static_file_request(struct mg_connection *conn,
const char *mime_type,
const char *additional_headers)
{
- char date[64], lm[64], etag[64];
+ char lm[64], etag[64];
char range[128]; /* large enough, so there will be no overflow */
- const char *msg = "OK";
const char *range_hdr;
- time_t curtime = time(NULL);
int64_t cl, r1, r2;
struct vec mime_vec;
int n, truncated;
char gz_path[PATH_MAX];
- const char *encoding = "";
+ const char *encoding = 0;
const char *origin_hdr;
const char *cors_orig_cfg;
- const char *cors1, *cors2, *cors3;
+ const char *cors1, *cors2;
int is_head_request;
#if defined(USE_ZLIB)
- /* Compression is allowed, unless there is a reason not to use compression.
- * If the file is already compressed, too small or a "range" request was
- * made, on the fly compression is not possible. */
+ /* Compression is allowed, unless there is a reason not to use
+ * compression. If the file is already compressed, too small or a
+ * "range" request was made, on the fly compression is not possible. */
int allow_on_the_fly_compression = 1;
#endif
@@ -9991,7 +10340,7 @@ handle_static_file_request(struct mg_connection *conn,
}
path = gz_path;
- encoding = "Content-Encoding: gzip\r\n";
+ encoding = "gzip";
#if defined(USE_ZLIB)
/* File is already compressed. No "on the fly" compression. */
@@ -10009,7 +10358,7 @@ handle_static_file_request(struct mg_connection *conn,
filep->stat = file_stat;
cl = (int64_t)filep->stat.size;
path = gz_path;
- encoding = "Content-Encoding: gzip\r\n";
+ encoding = "gzip";
#if defined(USE_ZLIB)
/* File is already compressed. No "on the fly" compression. */
@@ -10053,12 +10402,11 @@ handle_static_file_request(struct mg_connection *conn,
NULL, /* range buffer is big enough */
range,
sizeof(range),
- "Content-Range: bytes "
+ "bytes "
"%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n",
r1,
r1 + cl - 1,
filep->stat.size);
- msg = "Partial Content";
#if defined(USE_ZLIB)
/* Do not compress ranges. */
@@ -10084,77 +10432,73 @@ handle_static_file_request(struct mg_connection *conn,
* http://www.html5rocks.com/static/images/cors_server_flowchart.png
* -
* preflight is not supported for files. */
- cors1 = "Access-Control-Allow-Origin: ";
+ cors1 = "Access-Control-Allow-Origin";
cors2 = cors_orig_cfg;
- cors3 = "\r\n";
} else {
- cors1 = cors2 = cors3 = "";
+ cors1 = cors2 = "";
}
- /* Prepare Etag, Date, Last-Modified headers. Must be in UTC,
- * according to
- * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 */
- gmt_time_string(date, sizeof(date), &curtime);
+ /* Prepare Etag, and Last-Modified headers. */
gmt_time_string(lm, sizeof(lm), &filep->stat.last_modified);
construct_etag(etag, sizeof(etag), &filep->stat);
- /* Send header */
- (void)mg_printf(conn,
- "HTTP/1.1 %d %s\r\n"
- "%s%s%s" /* CORS */
- "Date: %s\r\n"
- "Last-Modified: %s\r\n"
- "Etag: %s\r\n"
- "Content-Type: %.*s\r\n"
- "Connection: %s\r\n",
- conn->status_code,
- msg,
- cors1,
- cors2,
- cors3,
- date,
- lm,
- etag,
- (int)mime_vec.len,
- mime_vec.ptr,
- suggest_connection_header(conn));
+ /* Create 2xx (200, 206) response */
+ mg_response_header_start(conn, conn->status_code);
send_static_cache_header(conn);
send_additional_header(conn);
+ mg_response_header_add(conn,
+ "Content-Type",
+ mime_vec.ptr,
+ (int)mime_vec.len);
+ if (cors1[0] != 0) {
+ mg_response_header_add(conn, cors1, cors2, -1);
+ }
+ mg_response_header_add(conn, "Last-Modified", lm, -1);
+ mg_response_header_add(conn, "Etag", etag, -1);
#if defined(USE_ZLIB)
/* On the fly compression allowed */
if (allow_on_the_fly_compression) {
/* For on the fly compression, we don't know the content size in
* advance, so we have to use chunked encoding */
- (void)mg_printf(conn,
- "Content-Encoding: gzip\r\n"
- "Transfer-Encoding: chunked\r\n");
+ encoding = "gzip";
+ if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
+ /* HTTP/2 is always using "chunks" (frames) */
+ mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1);
+ }
+
} else
#endif
{
/* Without on-the-fly compression, we know the content-length
* and we can use ranges (with on-the-fly compression we cannot).
* So we send these response headers only in this case. */
- (void)mg_printf(conn,
- "Content-Length: %" INT64_FMT "\r\n"
- "Accept-Ranges: bytes\r\n"
- "%s" /* range */
- "%s" /* encoding */,
- cl,
- range,
- encoding);
+ char len[32];
+ int trunc = 0;
+ mg_snprintf(conn, &trunc, len, sizeof(len), "%" INT64_FMT, cl);
+
+ if (!trunc) {
+ mg_response_header_add(conn, "Content-Length", len, -1);
+ }
+
+ mg_response_header_add(conn, "Accept-Ranges", "bytes", -1);
}
- /* The previous code must not add any header starting with X- to make
- * sure no one of the additional_headers is included twice */
- if (additional_headers != NULL) {
- (void)mg_printf(conn,
- "%.*s\r\n\r\n",
- (int)strlen(additional_headers),
- additional_headers);
- } else {
- (void)mg_printf(conn, "\r\n");
+ if (encoding) {
+ mg_response_header_add(conn, "Content-Encoding", encoding, -1);
}
+ if (range[0] != 0) {
+ mg_response_header_add(conn, "Content-Range", range, -1);
+ }
+
+ /* The code above does not add any header starting with X- to make
+ * sure no one of the additional_headers is included twice */
+ if ((additional_headers != NULL) && (*additional_headers != 0)) {
+ mg_response_header_add_lines(conn, additional_headers);
+ }
+
+ /* Send all headers */
+ mg_response_header_send(conn);
if (!is_head_request) {
#if defined(USE_ZLIB)
@@ -10203,37 +10547,29 @@ is_not_modified(const struct mg_connection *conn,
&& (filestat->last_modified <= parse_date_string(ims)));
}
+
static void
handle_not_modified_static_file_request(struct mg_connection *conn,
struct mg_file *filep)
{
- char date[64], lm[64], etag[64];
- time_t curtime = time(NULL);
+ char lm[64], etag[64];
if ((conn == NULL) || (filep == NULL)) {
return;
}
- conn->status_code = 304;
- gmt_time_string(date, sizeof(date), &curtime);
+
gmt_time_string(lm, sizeof(lm), &filep->stat.last_modified);
construct_etag(etag, sizeof(etag), &filep->stat);
- (void)mg_printf(conn,
- "HTTP/1.1 %d %s\r\n"
- "Date: %s\r\n",
- conn->status_code,
- mg_get_response_code_text(conn, conn->status_code),
- date);
+ /* Create 304 "not modified" response */
+ mg_response_header_start(conn, 304);
send_static_cache_header(conn);
send_additional_header(conn);
- (void)mg_printf(conn,
- "Last-Modified: %s\r\n"
- "Etag: %s\r\n"
- "Connection: %s\r\n"
- "\r\n",
- lm,
- etag,
- suggest_connection_header(conn));
+ mg_response_header_add(conn, "Last-Modified", lm, -1);
+ mg_response_header_add(conn, "Etag", etag, -1);
+
+ /* Send all headers */
+ mg_response_header_send(conn);
}
#endif
@@ -10537,7 +10873,7 @@ struct mg_http_method_info {
/* https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods */
-static struct mg_http_method_info http_methods[] = {
+static const struct mg_http_method_info http_methods[] = {
/* HTTP (RFC 2616) */
{"GET", 0, 1, 1, 1, 1},
{"POST", 1, 1, 0, 0, 0},
@@ -10707,7 +11043,6 @@ parse_http_request(char *buf, int len, struct mg_request_info *ri)
}
ri->http_version += 5;
-
/* Parse all HTTP headers */
ri->num_headers = parse_http_headers(&buf, ri->http_headers);
if (ri->num_headers < 0) {
@@ -10847,7 +11182,8 @@ read_message(FILE *fp,
/* value of request_timeout is in seconds, config in milliseconds */
request_timeout = atof(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
} else {
- request_timeout = -1.0;
+ request_timeout =
+ atof(config_options[REQUEST_TIMEOUT].default_value) / 1000.0;
}
if (conn->handled_requests > 0) {
if (conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT]) {
@@ -10860,7 +11196,7 @@ read_message(FILE *fp,
while (request_len == 0) {
/* Full request not yet received */
- if (conn->phys_ctx->stop_flag != 0) {
+ if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
/* Server is to be stopped. */
return -1;
}
@@ -10883,8 +11219,6 @@ read_message(FILE *fp,
if (n > 0) {
*nread += n;
request_len = get_http_header_len(buf, *nread);
- } else {
- request_len = 0;
}
if ((request_len == 0) && (request_timeout >= 0)) {
@@ -10974,7 +11308,7 @@ forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
#if !defined(NO_CGI)
/* This structure helps to create an environment for the spawned CGI
* program.
- * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
+ * Environment is an array of "VARIABLE=VALUE\0" ASCII strings,
* last element must be NULL.
* However, on Windows there is a requirement that all these
* VARIABLE=VALUE\0
@@ -11117,14 +11451,7 @@ prepare_cgi_environment(struct mg_connection *conn,
addenv(env, "%s", "SERVER_PROTOCOL=HTTP/1.1");
addenv(env, "%s", "REDIRECT_STATUS=200"); /* For PHP */
-#if defined(USE_IPV6)
- if (conn->client.lsa.sa.sa_family == AF_INET6) {
- addenv(env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin6.sin6_port));
- } else
-#endif
- {
- addenv(env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port));
- }
+ addenv(env, "SERVER_PORT=%d", ntohs(USA_IN_PORT_UNSAFE(&conn->client.lsa)));
sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
addenv(env, "REMOTE_ADDR=%s", src_addr);
@@ -11271,18 +11598,18 @@ prepare_cgi_environment(struct mg_connection *conn,
/* Data for CGI process control: PID and number of references */
struct process_control_data {
pid_t pid;
- int references;
+ ptrdiff_t references;
};
static int
-abort_process(void *data)
+abort_cgi_process(void *data)
{
- /* Waitpid checks for child status and won't work for a pid that does not
- * identify a child of the current process. Thus, if the pid is reused,
- * we will not affect a different process. */
+ /* Waitpid checks for child status and won't work for a pid that does
+ * not identify a child of the current process. Thus, if the pid is
+ * reused, we will not affect a different process. */
struct process_control_data *proc = (struct process_control_data *)data;
int status = 0;
- int refs;
+ ptrdiff_t refs;
pid_t ret_pid;
ret_pid = waitpid(proc->pid, &status, WNOHANG);
@@ -11326,11 +11653,15 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
struct process_control_data *proc = NULL;
#if defined(USE_TIMERS)
- double cgi_timeout = -1.0;
+ double cgi_timeout;
if (conn->dom_ctx->config[CGI_TIMEOUT]) {
/* Get timeout in seconds */
cgi_timeout = atof(conn->dom_ctx->config[CGI_TIMEOUT]) * 0.001;
+ } else {
+ cgi_timeout =
+ atof(config_options[REQUEST_TIMEOUT].default_value) * 0.001;
}
+
#endif
buf = NULL;
@@ -11393,11 +11724,7 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
"Error: CGI program \"%s\": Can not spawn CGI process: %s",
prog,
status);
- mg_send_http_error(conn,
- 500,
- "Error: Cannot spawn CGI process [%s]: %s",
- prog,
- status);
+ mg_send_http_error(conn, 500, "Error: Cannot spawn CGI process");
mg_free(proc);
proc = NULL;
goto done;
@@ -11416,8 +11743,9 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
cgi_timeout /* in seconds */,
0.0,
1,
- abort_process,
- (void *)proc);
+ abort_cgi_process,
+ (void *)proc,
+ NULL);
}
#endif
@@ -11590,7 +11918,7 @@ done:
mg_free(blk.buf);
if (pid != (pid_t)-1) {
- abort_process((void *)proc);
+ abort_cgi_process((void *)proc);
}
if (fdin[0] != -1) {
@@ -11632,8 +11960,6 @@ mkcol(struct mg_connection *conn, const char *path)
{
int rc, body_len;
struct de de;
- char date[64];
- time_t curtime = time(NULL);
if (conn == NULL) {
return;
@@ -11670,19 +11996,16 @@ mkcol(struct mg_connection *conn, const char *path)
rc = mg_mkdir(conn, path, 0755);
if (rc == 0) {
- conn->status_code = 201;
- gmt_time_string(date, sizeof(date), &curtime);
- mg_printf(conn,
- "HTTP/1.1 %d Created\r\n"
- "Date: %s\r\n",
- conn->status_code,
- date);
+
+ /* Create 201 "Created" response */
+ mg_response_header_start(conn, 201);
send_static_cache_header(conn);
send_additional_header(conn);
- mg_printf(conn,
- "Content-Length: 0\r\n"
- "Connection: %s\r\n\r\n",
- suggest_connection_header(conn));
+ mg_response_header_add(conn, "Content-Length", "0", -1);
+
+ /* Send all headers - there is no body */
+ mg_response_header_send(conn);
+
} else {
if (errno == EEXIST) {
mg_send_http_error(
@@ -11708,8 +12031,6 @@ put_file(struct mg_connection *conn, const char *path)
const char *range;
int64_t r1, r2;
int rc;
- char date[64];
- time_t curtime = time(NULL);
if (conn == NULL) {
return;
@@ -11728,22 +12049,9 @@ put_file(struct mg_connection *conn, const char *path)
/* File exists and is not a directory. */
/* Can it be replaced? */
-#if defined(MG_USE_OPEN_FILE)
- if (file.access.membuf != NULL) {
- /* This is an "in-memory" file, that can not be replaced */
- mg_send_http_error(conn,
- 405,
- "Error: Put not possible\nReplacing %s "
- "is not supported",
- path);
- return;
- }
-#endif
-
/* Check if the server may write this file */
if (access(path, W_OK) == 0) {
/* Access granted */
- conn->status_code = 200;
rc = 1;
} else {
mg_send_http_error(
@@ -11762,19 +12070,15 @@ put_file(struct mg_connection *conn, const char *path)
if (rc == 0) {
/* put_dir returns 0 if path is a directory */
- gmt_time_string(date, sizeof(date), &curtime);
- mg_printf(conn,
- "HTTP/1.1 %d %s\r\n",
- conn->status_code,
- mg_get_response_code_text(NULL, conn->status_code));
+
+ /* Create response */
+ mg_response_header_start(conn, conn->status_code);
send_no_cache_header(conn);
send_additional_header(conn);
- mg_printf(conn,
- "Date: %s\r\n"
- "Content-Length: 0\r\n"
- "Connection: %s\r\n\r\n",
- date,
- suggest_connection_header(conn));
+ mg_response_header_add(conn, "Content-Length", "0", -1);
+
+ /* Send all headers - there is no body */
+ mg_response_header_send(conn);
/* Request to create a directory has been fulfilled successfully.
* No need to put a file. */
@@ -11836,19 +12140,14 @@ put_file(struct mg_connection *conn, const char *path)
conn->status_code = 507;
}
- gmt_time_string(date, sizeof(date), &curtime);
- mg_printf(conn,
- "HTTP/1.1 %d %s\r\n",
- conn->status_code,
- mg_get_response_code_text(NULL, conn->status_code));
+ /* Create response (status_code has been set before) */
+ mg_response_header_start(conn, conn->status_code);
send_no_cache_header(conn);
send_additional_header(conn);
- mg_printf(conn,
- "Date: %s\r\n"
- "Content-Length: 0\r\n"
- "Connection: %s\r\n\r\n",
- date,
- suggest_connection_header(conn));
+ mg_response_header_add(conn, "Content-Length", "0", -1);
+
+ /* Send all headers - there is no body */
+ mg_response_header_send(conn);
}
@@ -11866,18 +12165,6 @@ delete_file(struct mg_connection *conn, const char *path)
return;
}
-#if 0 /* Ignore if a file in memory is inside a folder */
- if (de.access.membuf != NULL) {
- /* the file is cached in memory */
- mg_send_http_error(
- conn,
- 405,
- "Error: Delete not possible\nDeleting %s is not supported",
- path);
- return;
- }
-#endif
-
if (de.file.is_directory) {
if (remove_directory(conn, path)) {
/* Delete is successful: Return 204 without content. */
@@ -11904,7 +12191,12 @@ delete_file(struct mg_connection *conn, const char *path)
/* Try to delete it. */
if (mg_remove(conn, path) == 0) {
/* Delete was successful: Return 204 without content. */
- mg_send_http_error(conn, 204, "%s", "");
+ mg_response_header_start(conn, 204);
+ send_no_cache_header(conn);
+ send_additional_header(conn);
+ mg_response_header_add(conn, "Content-Length", "0", -1);
+ mg_response_header_send(conn);
+
} else {
/* Delete not successful (file locked). */
mg_send_http_error(conn,
@@ -11995,9 +12287,7 @@ do_ssi_include(struct mg_connection *conn,
strerror(ERRNO));
} else {
fclose_on_exec(&file.access, conn);
- if (match_prefix(conn->dom_ctx->config[SSI_EXTENSIONS],
- strlen(conn->dom_ctx->config[SSI_EXTENSIONS]),
- path)
+ if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path)
> 0) {
send_ssi_file(conn, path, &file, include_level + 1);
} else {
@@ -12034,20 +12324,13 @@ do_ssi_exec(struct mg_connection *conn, char *tag)
static int
-mg_fgetc(struct mg_file *filep, int offset)
+mg_fgetc(struct mg_file *filep)
{
- (void)offset; /* unused in case MG_USE_OPEN_FILE is set */
-
if (filep == NULL) {
return EOF;
}
-#if defined(MG_USE_OPEN_FILE)
- if ((filep->access.membuf != NULL) && (offset >= 0)
- && (((unsigned int)(offset)) < filep->stat.size)) {
- return ((const unsigned char *)filep->access.membuf)[offset];
- } else /* else block below */
-#endif
- if (filep->access.fp != NULL) {
+
+ if (filep->access.fp != NULL) {
return fgetc(filep->access.fp);
} else {
return EOF;
@@ -12062,17 +12345,17 @@ send_ssi_file(struct mg_connection *conn,
int include_level)
{
char buf[MG_BUF_LEN];
- int ch, offset, len, in_tag, in_ssi_tag;
+ int ch, len, in_tag, in_ssi_tag;
if (include_level > 10) {
mg_cry_internal(conn, "SSI #include level is too deep (%s)", path);
return;
}
- in_tag = in_ssi_tag = len = offset = 0;
+ in_tag = in_ssi_tag = len = 0;
/* Read file, byte by byte, and look for SSI include tags */
- while ((ch = mg_fgetc(filep, offset++)) != EOF) {
+ while ((ch = mg_fgetc(filep)) != EOF) {
if (in_tag) {
/* We are in a tag, either SSI tag or html tag */
@@ -12169,7 +12452,7 @@ handle_ssi_file_request(struct mg_connection *conn,
char date[64];
time_t curtime = time(NULL);
const char *cors_orig_cfg;
- const char *cors1, *cors2, *cors3;
+ const char *cors1, *cors2;
if ((conn == NULL) || (path == NULL) || (filep == NULL)) {
return;
@@ -12178,11 +12461,10 @@ handle_ssi_file_request(struct mg_connection *conn,
cors_orig_cfg = conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
if (cors_orig_cfg && *cors_orig_cfg && mg_get_header(conn, "Origin")) {
/* Cross-origin resource sharing (CORS). */
- cors1 = "Access-Control-Allow-Origin: ";
+ cors1 = "Access-Control-Allow-Origin";
cors2 = cors_orig_cfg;
- cors3 = "\r\n";
} else {
- cors1 = cors2 = cors3 = "";
+ cors1 = cors2 = "";
}
if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) {
@@ -12194,22 +12476,23 @@ handle_ssi_file_request(struct mg_connection *conn,
path,
strerror(ERRNO));
} else {
+ /* Set "must_close" for HTTP/1.x, since we do not know the
+ * content length */
conn->must_close = 1;
gmt_time_string(date, sizeof(date), &curtime);
fclose_on_exec(&filep->access, conn);
- mg_printf(conn, "HTTP/1.1 200 OK\r\n");
+
+ /* 200 OK response */
+ mg_response_header_start(conn, 200);
send_no_cache_header(conn);
send_additional_header(conn);
- mg_printf(conn,
- "%s%s%s"
- "Date: %s\r\n"
- "Content-Type: text/html\r\n"
- "Connection: %s\r\n\r\n",
- cors1,
- cors2,
- cors3,
- date,
- suggest_connection_header(conn));
+ mg_response_header_add(conn, "Content-Type", "text/html", -1);
+ if (cors1[0]) {
+ mg_response_header_add(conn, cors1, cors2, -1);
+ }
+ mg_response_header_send(conn);
+
+ /* Header sent, now send body */
send_ssi_file(conn, path, filep, 0);
(void)mg_fclose(&filep->access); /* Ignore errors for readonly files */
}
@@ -12221,31 +12504,30 @@ handle_ssi_file_request(struct mg_connection *conn,
static void
send_options(struct mg_connection *conn)
{
- char date[64];
- time_t curtime = time(NULL);
-
if (!conn) {
return;
}
- conn->status_code = 200;
- conn->must_close = 1;
- gmt_time_string(date, sizeof(date), &curtime);
-
/* We do not set a "Cache-Control" header here, but leave the default.
* Since browsers do not send an OPTIONS request, we can not test the
* effect anyway. */
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\n"
- "Date: %s\r\n"
- "Connection: %s\r\n"
- "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, "
- "PROPFIND, MKCOL\r\n"
- "DAV: 1\r\n",
- date,
- suggest_connection_header(conn));
+
+ mg_response_header_start(conn, 200);
+ mg_response_header_add(conn, "Content-Type", "text/html", -1);
+ if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
+ /* Use the same as before */
+ mg_response_header_add(
+ conn,
+ "Allow",
+ "GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, PROPFIND, MKCOL",
+ -1);
+ mg_response_header_add(conn, "DAV", "1", -1);
+ } else {
+ /* TODO: Check this later for HTTP/2 */
+ mg_response_header_add(conn, "Allow", "GET, POST", -1);
+ }
send_additional_header(conn);
- mg_printf(conn, "\r\n");
+ mg_response_header_send(conn);
}
@@ -12314,7 +12596,8 @@ print_dav_dir_entry(struct de *de, void *data)
if (!de || !conn
|| !print_props(
conn, conn->request_info.local_uri, de->file_name, &de->file)) {
- return -1;
+ /* stop scan */
+ return 1;
}
return 0;
}
@@ -12336,18 +12619,15 @@ handle_propfind(struct mg_connection *conn,
}
conn->must_close = 1;
- conn->status_code = 207;
- mg_printf(conn,
- "HTTP/1.1 207 Multi-Status\r\n"
- "Date: %s\r\n",
- date);
+
+ /* return 207 "Multi-Status" */
+ mg_response_header_start(conn, 207);
send_static_cache_header(conn);
send_additional_header(conn);
- mg_printf(conn,
- "Connection: %s\r\n"
- "Content-Type: text/xml; charset=utf-8\r\n\r\n",
- suggest_connection_header(conn));
+ mg_response_header_add(conn, "Content-Type", "text/xml; charset=utf-8", -1);
+ mg_response_header_send(conn);
+ /* Content */
mg_printf(conn,
""
"\n");
@@ -12387,7 +12667,7 @@ mg_unlock_connection(struct mg_connection *conn)
void
mg_lock_context(struct mg_context *ctx)
{
- if (ctx) {
+ if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
(void)pthread_mutex_lock(&ctx->nonce_mutex);
}
}
@@ -12395,7 +12675,7 @@ mg_lock_context(struct mg_context *ctx)
void
mg_unlock_context(struct mg_context *ctx)
{
- if (ctx) {
+ if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
(void)pthread_mutex_unlock(&ctx->nonce_mutex);
}
}
@@ -12443,6 +12723,12 @@ send_websocket_handshake(struct mg_connection *conn, const char *websock_key)
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n",
b64_sha);
+
+#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
+ // Send negotiated compression extension parameters
+ websocket_deflate_response(conn);
+#endif
+
if (conn->request_info.acceptedWebSocketSubprotocol) {
mg_printf(conn,
"Sec-WebSocket-Protocol: %s\r\n\r\n",
@@ -12514,6 +12800,9 @@ read_websocket(struct mg_connection *conn,
if ((timeout <= 0.0) && (conn->dom_ctx->config[REQUEST_TIMEOUT])) {
timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
}
+ if (timeout <= 0.0) {
+ timeout = atof(config_options[REQUEST_TIMEOUT].default_value) / 1000.0;
+ }
/* Enter data processing loop */
DEBUG_TRACE("Websocket connection %s:%u start data processing loop",
@@ -12524,7 +12813,8 @@ read_websocket(struct mg_connection *conn,
/* Loop continuously, reading messages from the socket, invoking the
* callback, and waiting repeatedly until an error occurs. */
- while (!conn->phys_ctx->stop_flag && !conn->must_close) {
+ while (STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)
+ && (!conn->must_close)) {
header_len = 0;
DEBUG_ASSERT(conn->data_len >= conn->request_len);
if ((body_len = (size_t)(conn->data_len - conn->request_len)) >= 2) {
@@ -12677,13 +12967,93 @@ read_websocket(struct mg_connection *conn,
} else {
/* Exit the loop if callback signals to exit (server side),
* or "connection close" opcode received (client side). */
- if ((ws_data_handler != NULL)
- && !ws_data_handler(conn,
- mop,
- (char *)data,
- (size_t)data_len,
- callback_data)) {
- exit_by_callback = 1;
+ if (ws_data_handler != NULL) {
+#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
+ if (mop & 0x40) {
+ /* Inflate the data received if bit RSV1 is set. */
+ if (!conn->websocket_deflate_initialized) {
+ if (websocket_deflate_initialize(conn, 1) != Z_OK)
+ exit_by_callback = 1;
+ }
+ if (!exit_by_callback) {
+ size_t inflate_buf_size_old = 0;
+ size_t inflate_buf_size =
+ data_len
+ * 4; // Initial guess of the inflated message
+ // size. We double the memory when needed.
+ Bytef *inflated;
+ Bytef *new_mem;
+ conn->websocket_inflate_state.avail_in =
+ (uInt)(data_len + 4);
+ conn->websocket_inflate_state.next_in = data;
+ // Add trailing 0x00 0x00 0xff 0xff bytes
+ data[data_len] = '\x00';
+ data[data_len + 1] = '\x00';
+ data[data_len + 2] = '\xff';
+ data[data_len + 3] = '\xff';
+ do {
+ if (inflate_buf_size_old == 0) {
+ new_mem = mg_calloc(inflate_buf_size,
+ sizeof(Bytef));
+ } else {
+ inflate_buf_size *= 2;
+ new_mem =
+ mg_realloc(inflated, inflate_buf_size);
+ }
+ if (new_mem == NULL) {
+ mg_cry_internal(
+ conn,
+ "Out of memory: Cannot allocate "
+ "inflate buffer of %i bytes",
+ inflate_buf_size);
+ exit_by_callback = 1;
+ break;
+ }
+ inflated = new_mem;
+ conn->websocket_inflate_state.avail_out =
+ (uInt)(inflate_buf_size
+ - inflate_buf_size_old);
+ conn->websocket_inflate_state.next_out =
+ inflated + inflate_buf_size_old;
+ int ret =
+ inflate(&conn->websocket_inflate_state,
+ Z_SYNC_FLUSH);
+ if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR
+ || ret == Z_MEM_ERROR || ret < 0) {
+ mg_cry_internal(
+ conn,
+ "ZLIB inflate error: %i %s",
+ ret,
+ (conn->websocket_inflate_state.msg
+ ? conn->websocket_inflate_state.msg
+ : ""));
+ exit_by_callback = 1;
+ break;
+ }
+ inflate_buf_size_old = inflate_buf_size;
+
+ } while (conn->websocket_inflate_state.avail_out
+ == 0);
+ inflate_buf_size -=
+ conn->websocket_inflate_state.avail_out;
+ if (!ws_data_handler(conn,
+ mop,
+ (char *)inflated,
+ inflate_buf_size,
+ callback_data)) {
+ exit_by_callback = 1;
+ }
+ mg_free(inflated);
+ }
+ } else
+#endif
+ if (!ws_data_handler(conn,
+ mop,
+ (char *)data,
+ (size_t)data_len,
+ callback_data)) {
+ exit_by_callback = 1;
+ }
}
}
@@ -12727,7 +13097,8 @@ read_websocket(struct mg_connection *conn,
/* Reset open PING count */
ping_count = 0;
} else {
- if (!conn->phys_ctx->stop_flag && !conn->must_close) {
+ if (STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)
+ && (!conn->must_close)) {
if (ping_count > MG_MAX_UNANSWERED_PING) {
/* Stop sending PING */
DEBUG_TRACE("Too many (%i) unanswered ping from %s:%u "
@@ -12787,7 +13158,53 @@ mg_websocket_write_exec(struct mg_connection *conn,
#pragma GCC diagnostic ignored "-Wconversion"
#endif
- header[0] = 0x80u | (unsigned char)((unsigned)opcode & 0xf);
+ /* Note that POSIX/Winsock's send() is threadsafe
+ * http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid
+ * but mongoose's mg_printf/mg_write is not (because of the loop in
+ * push(), although that is only a problem if the packet is large or
+ * outgoing buffer is full). */
+
+ /* TODO: Check if this lock should be moved to user land.
+ * Currently the server sets this lock for websockets, but
+ * not for any other connection. It must be set for every
+ * conn read/written by more than one thread, no matter if
+ * it is a websocket or regular connection. */
+ (void)mg_lock_connection(conn);
+
+#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
+ size_t deflated_size;
+ Bytef *deflated;
+ // Deflate websocket messages over 100kb
+ int use_deflate = dataLen > 100 * 1024 && conn->accept_gzip;
+
+ if (use_deflate) {
+ if (!conn->websocket_deflate_initialized) {
+ if (websocket_deflate_initialize(conn, 1) != Z_OK)
+ return 0;
+ }
+
+ // Deflating the message
+ header[0] = 0xC0u | (unsigned char)((unsigned)opcode & 0xf);
+ conn->websocket_deflate_state.avail_in = (uInt)dataLen;
+ conn->websocket_deflate_state.next_in = (unsigned char *)data;
+ deflated_size = compressBound((uLong)dataLen);
+ deflated = mg_calloc(deflated_size, sizeof(Bytef));
+ if (deflated == NULL) {
+ mg_cry_internal(
+ conn,
+ "Out of memory: Cannot allocate deflate buffer of %i bytes",
+ deflated_size);
+ mg_unlock_connection(conn);
+ return -1;
+ }
+ conn->websocket_deflate_state.avail_out = (uInt)deflated_size;
+ conn->websocket_deflate_state.next_out = deflated;
+ deflate(&conn->websocket_deflate_state, conn->websocket_deflate_flush);
+ dataLen = deflated_size - conn->websocket_deflate_state.avail_out
+ - 4; // Strip trailing 0x00 0x00 0xff 0xff bytes
+ } else
+#endif
+ header[0] = 0x80u | (unsigned char)((unsigned)opcode & 0xf);
#if defined(GCC_DIAGNOSTIC)
#pragma GCC diagnostic pop
@@ -12821,26 +13238,19 @@ mg_websocket_write_exec(struct mg_connection *conn,
headerLen += 4;
}
- /* Note that POSIX/Winsock's send() is threadsafe
- * http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid
- * but mongoose's mg_printf/mg_write is not (because of the loop in
- * push(), although that is only a problem if the packet is large or
- * outgoing buffer is full). */
-
- /* TODO: Check if this lock should be moved to user land.
- * Currently the server sets this lock for websockets, but
- * not for any other connection. It must be set for every
- * conn read/written by more than one thread, no matter if
- * it is a websocket or regular connection. */
- (void)mg_lock_connection(conn);
-
retval = mg_write(conn, header, headerLen);
if (retval != (int)headerLen) {
/* Did not send complete header */
retval = -1;
} else {
if (dataLen > 0) {
- retval = mg_write(conn, data, dataLen);
+#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
+ if (use_deflate) {
+ retval = mg_write(conn, deflated, dataLen);
+ mg_free(deflated);
+ } else
+#endif
+ retval = mg_write(conn, data, dataLen);
}
/* if dataLen == 0, the header length (2) is returned */
}
@@ -13011,7 +13421,6 @@ handle_websocket_request(struct mg_connection *conn,
; // ignore leading whitespaces
protocol = sep;
-
for (idx = 0; idx < subprotocols->nb_subprotocols; idx++) {
if ((strlen(subprotocols->subprotocols[idx]) == len)
&& (strncmp(curSubProtocol,
@@ -13057,6 +13466,10 @@ handle_websocket_request(struct mg_connection *conn,
}
}
+#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
+ websocket_deflate_negotiate(conn);
+#endif
+
if ((ws_connect_handler != NULL)
&& (ws_connect_handler(conn, cbData) != 0)) {
/* C callback has returned non-zero, do not proceed with
@@ -13073,10 +13486,8 @@ handle_websocket_request(struct mg_connection *conn,
else {
/* Step 3.1: Check if Lua is responsible. */
if (conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS]) {
- lua_websock = match_prefix(
- conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS],
- strlen(conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS]),
- path);
+ lua_websock = match_prefix_strlen(
+ conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS], path);
}
if (lua_websock) {
@@ -13129,15 +13540,30 @@ handle_websocket_request(struct mg_connection *conn,
#endif
}
- /* Step 8: Call the close handler */
+#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
+ /* Step 8: Close the deflate & inflate buffers */
+ if (conn->websocket_deflate_initialized) {
+ deflateEnd(&conn->websocket_deflate_state);
+ inflateEnd(&conn->websocket_inflate_state);
+ }
+#endif
+
+ /* Step 9: Call the close handler */
if (ws_close_handler) {
ws_close_handler(conn, cbData);
}
}
+#endif /* !USE_WEBSOCKET */
+/* Is upgrade request:
+ * 0 = regular HTTP/1.0 or HTTP/1.1 request
+ * 1 = upgrade to websocket
+ * 2 = upgrade to HTTP/2
+ * -1 = upgrade to unknown protocol
+ */
static int
-is_websocket_protocol(const struct mg_connection *conn)
+should_switch_to_protocol(const struct mg_connection *conn)
{
const char *upgrade, *connection;
@@ -13147,69 +13573,147 @@ is_websocket_protocol(const struct mg_connection *conn)
* Upgrade: Websocket
*/
- upgrade = mg_get_header(conn, "Upgrade");
- if (upgrade == NULL) {
- return 0; /* fail early, don't waste time checking other header
- * fields
- */
- }
- DEBUG_TRACE("Upgrade: %s", upgrade);
- if (!mg_strcasestr(upgrade, "websocket")) {
- return 0;
- }
-
connection = mg_get_header(conn, "Connection");
if (connection == NULL) {
- return 0;
+ return PROTOCOL_TYPE_HTTP1;
}
if (!mg_strcasestr(connection, "upgrade")) {
- return 0;
+ return PROTOCOL_TYPE_HTTP1;
}
- /* The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and
- * "Sec-WebSocket-Version" are also required.
- * Don't check them here, since even an unsupported websocket protocol
- * request still IS a websocket request (in contrast to a standard HTTP
- * request). It will fail later in handle_websocket_request.
- */
-
- return 1;
-}
-#endif /* !USE_WEBSOCKET */
-
-
-static int
-isbyte(int n)
-{
- return (n >= 0) && (n <= 255);
-}
-
-
-static int
-parse_net(const char *spec, uint32_t *net, uint32_t *mask)
-{
- int n, a, b, c, d, slash = 32, len = 0;
-
- if (((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5)
- || (sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4))
- && isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && (slash >= 0)
- && (slash < 33)) {
- len = n;
- *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8)
- | (uint32_t)d;
- *mask = slash ? (0xffffffffU << (32 - slash)) : 0;
+ upgrade = mg_get_header(conn, "Upgrade");
+ if (upgrade == NULL) {
+ /* "Connection: Upgrade" without "Upgrade" Header --> Error */
+ return -1;
}
- return len;
+ /* Upgrade to ... */
+ if (0 != mg_strcasestr(upgrade, "websocket")) {
+ /* The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and
+ * "Sec-WebSocket-Version" are also required.
+ * Don't check them here, since even an unsupported websocket protocol
+ * request still IS a websocket request (in contrast to a standard HTTP
+ * request). It will fail later in handle_websocket_request.
+ */
+ return PROTOCOL_TYPE_WEBSOCKET; /* Websocket */
+ }
+ if (0 != mg_strcasestr(upgrade, "h2")) {
+ return PROTOCOL_TYPE_HTTP2; /* Websocket */
+ }
+
+ /* Upgrade to another protocol */
+ return -1;
}
static int
-set_throttle(const char *spec, uint32_t remote_ip, const char *uri)
+parse_match_net(const struct vec *vec, const union usa *sa, int no_strict)
+{
+ int n;
+ unsigned int a, b, c, d, slash;
+
+ if (sscanf(vec->ptr, "%u.%u.%u.%u/%u%n", &a, &b, &c, &d, &slash, &n) != 5) {
+ slash = 32;
+ if (sscanf(vec->ptr, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n) != 4) {
+ n = 0;
+ }
+ }
+
+ if ((n > 0) && ((size_t)n == vec->len)) {
+ if ((a < 256) && (b < 256) && (c < 256) && (d < 256) && (slash < 33)) {
+ /* IPv4 format */
+ if (sa->sa.sa_family == AF_INET) {
+ uint32_t ip = (uint32_t)ntohl(sa->sin.sin_addr.s_addr);
+ uint32_t net = ((uint32_t)a << 24) | ((uint32_t)b << 16)
+ | ((uint32_t)c << 8) | (uint32_t)d;
+ uint32_t mask = slash ? (0xFFFFFFFFu << (32 - slash)) : 0;
+ return (ip & mask) == net;
+ }
+ return 0;
+ }
+ }
+#if defined(USE_IPV6)
+ else {
+ char ad[50];
+ const char *p;
+
+ if (sscanf(vec->ptr, "[%49[^]]]/%u%n", ad, &slash, &n) != 2) {
+ slash = 128;
+ if (sscanf(vec->ptr, "[%49[^]]]%n", ad, &n) != 1) {
+ n = 0;
+ }
+ }
+
+ if ((n <= 0) && no_strict) {
+ /* no square brackets? */
+ p = strchr(vec->ptr, '/');
+ if (p && (p < (vec->ptr + vec->len))) {
+ if (((size_t)(p - vec->ptr) < sizeof(ad))
+ && (sscanf(p, "/%u%n", &slash, &n) == 1)) {
+ n += (int)(p - vec->ptr);
+ mg_strlcpy(ad, vec->ptr, (size_t)(p - vec->ptr) + 1);
+ } else {
+ n = 0;
+ }
+ } else if (vec->len < sizeof(ad)) {
+ n = (int)vec->len;
+ slash = 128;
+ mg_strlcpy(ad, vec->ptr, vec->len + 1);
+ }
+ }
+
+ if ((n > 0) && ((size_t)n == vec->len) && (slash < 129)) {
+ p = ad;
+ c = 0;
+ /* zone indexes are unsupported, at least two colons are needed */
+ while (isxdigit((unsigned char)*p) || (*p == '.') || (*p == ':')) {
+ if (*(p++) == ':') {
+ c++;
+ }
+ }
+ if ((*p == '\0') && (c >= 2)) {
+ struct sockaddr_in6 sin6;
+ unsigned int i;
+
+ /* for strict validation, an actual IPv6 argument is needed */
+ if (sa->sa.sa_family != AF_INET6) {
+ return 0;
+ }
+ if (mg_inet_pton(AF_INET6, ad, &sin6, sizeof(sin6), 0)) {
+ /* IPv6 format */
+ for (i = 0; i < 16; i++) {
+ uint8_t ip = sa->sin6.sin6_addr.s6_addr[i];
+ uint8_t net = sin6.sin6_addr.s6_addr[i];
+ uint8_t mask = 0;
+
+ if (8 * i + 8 < slash) {
+ mask = 0xFFu;
+ } else if (8 * i < slash) {
+ mask = (uint8_t)(0xFFu << (8 * i + 8 - slash));
+ }
+ if ((ip & mask) != net) {
+ return 0;
+ }
+ }
+ return 1;
+ }
+ }
+ }
+ }
+#else
+ (void)no_strict;
+#endif
+
+ /* malformed */
+ return -1;
+}
+
+
+static int
+set_throttle(const char *spec, const union usa *rsa, const char *uri)
{
int throttle = 0;
struct vec vec, val;
- uint32_t net, mask;
char mult;
double v;
@@ -13226,12 +13730,16 @@ set_throttle(const char *spec, uint32_t remote_ip, const char *uri)
: ((lowercase(&mult) == 'm') ? 1048576 : 1);
if (vec.len == 1 && vec.ptr[0] == '*') {
throttle = (int)v;
- } else if (parse_net(vec.ptr, &net, &mask) > 0) {
- if ((remote_ip & mask) == net) {
+ } else {
+ int matched = parse_match_net(&vec, rsa, 0);
+ if (matched >= 0) {
+ /* a valid IP subnet */
+ if (matched) {
+ throttle = (int)v;
+ }
+ } else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
throttle = (int)v;
}
- } else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
- throttle = (int)v;
}
}
@@ -13239,118 +13747,10 @@ set_throttle(const char *spec, uint32_t remote_ip, const char *uri)
}
-static uint32_t
-get_remote_ip(const struct mg_connection *conn)
-{
- if (!conn) {
- return 0;
- }
- return ntohl(*(const uint32_t *)&conn->client.rsa.sin.sin_addr);
-}
-
-
/* The mg_upload function is superseeded by mg_handle_form_request. */
#include "handle_form.inl"
-#if defined(MG_LEGACY_INTERFACE)
-/* Implement the deprecated mg_upload function by calling the new
- * mg_handle_form_request function. While mg_upload could only handle
- * HTML forms sent as POST request in multipart/form-data format
- * containing only file input elements, mg_handle_form_request can
- * handle all form input elements and all standard request methods. */
-struct mg_upload_user_data {
- struct mg_connection *conn;
- const char *destination_dir;
- int num_uploaded_files;
-};
-
-
-/* Helper function for deprecated mg_upload. */
-static int
-mg_upload_field_found(const char *key,
- const char *filename,
- char *path,
- size_t pathlen,
- void *user_data)
-{
- int truncated = 0;
- struct mg_upload_user_data *fud = (struct mg_upload_user_data *)user_data;
- (void)key;
-
- if (!filename) {
- mg_cry_internal(fud->conn, "%s: No filename set", __func__);
- return FORM_FIELD_STORAGE_ABORT;
- }
- mg_snprintf(fud->conn,
- &truncated,
- path,
- pathlen - 1,
- "%s/%s",
- fud->destination_dir,
- filename);
- if (truncated) {
- mg_cry_internal(fud->conn, "%s: File path too long", __func__);
- return FORM_FIELD_STORAGE_ABORT;
- }
- return FORM_FIELD_STORAGE_STORE;
-}
-
-
-/* Helper function for deprecated mg_upload. */
-static int
-mg_upload_field_get(const char *key,
- const char *value,
- size_t value_size,
- void *user_data)
-{
- /* Function should never be called */
- (void)key;
- (void)value;
- (void)value_size;
- (void)user_data;
-
- return 0;
-}
-
-
-/* Helper function for deprecated mg_upload. */
-static int
-mg_upload_field_stored(const char *path, long long file_size, void *user_data)
-{
- struct mg_upload_user_data *fud = (struct mg_upload_user_data *)user_data;
- (void)file_size;
-
- fud->num_uploaded_files++;
- fud->conn->phys_ctx->callbacks.upload(fud->conn, path);
-
- return 0;
-}
-
-
-/* Deprecated function mg_upload - use mg_handle_form_request instead. */
-int
-mg_upload(struct mg_connection *conn, const char *destination_dir)
-{
- struct mg_upload_user_data fud = {conn, destination_dir, 0};
- struct mg_form_data_handler fdh = {mg_upload_field_found,
- mg_upload_field_get,
- mg_upload_field_stored,
- 0};
- int ret;
-
- fdh.user_data = (void *)&fud;
- ret = mg_handle_form_request(conn, &fdh);
-
- if (ret < 0) {
- mg_cry_internal(conn, "%s: Error while parsing the request", __func__);
- }
-
- return fud.num_uploaded_files;
-}
-#endif
-
-
static int
get_first_ssl_listener_index(const struct mg_context *ctx)
{
@@ -13366,68 +13766,76 @@ get_first_ssl_listener_index(const struct mg_context *ctx)
/* Return host (without port) */
-/* Use mg_free to free the result */
-static const char *
-alloc_get_host(struct mg_connection *conn)
+static void
+get_host_from_request_info(struct vec *host, const struct mg_request_info *ri)
{
- char buf[1025];
- size_t buflen = sizeof(buf);
- const char *host_header = get_header(conn->request_info.http_headers,
- conn->request_info.num_headers,
- "Host");
- char *host;
+ const char *host_header =
+ get_header(ri->http_headers, ri->num_headers, "Host");
+
+ host->ptr = NULL;
+ host->len = 0;
if (host_header != NULL) {
char *pos;
- /* Create a local copy of the "Host" header, since it might be
- * modified here. */
- mg_strlcpy(buf, host_header, buflen);
- buf[buflen - 1] = '\0';
- host = buf;
- while (isspace((unsigned char)*host)) {
- host++;
- }
-
/* If the "Host" is an IPv6 address, like [::1], parse until ]
* is found. */
- if (*host == '[') {
- pos = strchr(host, ']');
+ if (*host_header == '[') {
+ pos = strchr(host_header, ']');
if (!pos) {
/* Malformed hostname starts with '[', but no ']' found */
DEBUG_TRACE("%s", "Host name format error '[' without ']'");
- return NULL;
+ return;
}
/* terminate after ']' */
- pos[1] = 0;
+ host->ptr = host_header;
+ host->len = (size_t)(pos + 1 - host_header);
} else {
/* Otherwise, a ':' separates hostname and port number */
- pos = strchr(host, ':');
+ pos = strchr(host_header, ':');
if (pos != NULL) {
- *pos = '\0';
+ host->len = (size_t)(pos - host_header);
+ } else {
+ host->len = strlen(host_header);
}
+ host->ptr = host_header;
}
+ }
+}
+
+static int
+switch_domain_context(struct mg_connection *conn)
+{
+ struct vec host;
+
+ get_host_from_request_info(&host, &conn->request_info);
+
+ if (host.ptr) {
if (conn->ssl) {
/* This is a HTTPS connection, maybe we have a hostname
* from SNI (set in ssl_servername_callback). */
const char *sslhost = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
if (sslhost && (conn->dom_ctx != &(conn->phys_ctx->dd))) {
/* We are not using the default domain */
- if (mg_strcasecmp(host, sslhost)) {
+ if ((strlen(sslhost) != host.len)
+ || mg_strncasecmp(host.ptr, sslhost, host.len)) {
/* Mismatch between SNI domain and HTTP domain */
- DEBUG_TRACE("Host mismatch: SNI: %s, HTTPS: %s",
+ DEBUG_TRACE("Host mismatch: SNI: %s, HTTPS: %.*s",
sslhost,
- host);
- return NULL;
+ (int)host.len,
+ host.ptr);
+ return 0;
}
}
- DEBUG_TRACE("HTTPS Host: %s", host);
} else {
struct mg_domain_context *dom = &(conn->phys_ctx->dd);
while (dom) {
- if (!mg_strcasecmp(host, dom->config[AUTHENTICATION_DOMAIN])) {
+ if ((strlen(dom->config[AUTHENTICATION_DOMAIN]) == host.len)
+ && !mg_strncasecmp(host.ptr,
+ dom->config[AUTHENTICATION_DOMAIN],
+ host.len)) {
/* Found matching domain */
DEBUG_TRACE("HTTP domain %s found",
@@ -13437,102 +13845,75 @@ alloc_get_host(struct mg_connection *conn)
conn->dom_ctx = dom;
break;
}
+ mg_lock_context(conn->phys_ctx);
dom = dom->next;
+ mg_unlock_context(conn->phys_ctx);
}
-
- DEBUG_TRACE("HTTP Host: %s", host);
}
} else {
- sockaddr_to_string(buf, buflen, &conn->client.lsa);
- host = buf;
-
- DEBUG_TRACE("IP: %s", host);
+ DEBUG_TRACE("HTTP%s Host is not set", conn->ssl ? "S" : "");
+ return 1;
}
- return mg_strdup_ctx(host, conn->phys_ctx);
+ DEBUG_TRACE("HTTP%s Host: %.*s",
+ conn->ssl ? "S" : "",
+ (int)host.len,
+ host.ptr);
+ return 1;
}
+static int mg_construct_local_link(const struct mg_connection *conn,
+ char *buf,
+ size_t buflen,
+ const char *define_proto,
+ int define_port,
+ const char *define_uri);
+
+
static void
-redirect_to_https_port(struct mg_connection *conn, int ssl_index)
+redirect_to_https_port(struct mg_connection *conn, int port)
{
char target_url[MG_BUF_LEN];
int truncated = 0;
+ const char *expect_proto =
+ (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET) ? "wss" : "https";
+ /* Use "308 Permanent Redirect" */
+ int redirect_code = 308;
+
+ /* In any case, close the current connection */
conn->must_close = 1;
/* Send host, port, uri and (if it exists) ?query_string */
- if (conn->host) {
-
- /* Use "308 Permanent Redirect" */
- int redirect_code = 308;
-
- /* Create target URL */
- mg_snprintf(
- conn,
- &truncated,
- target_url,
- sizeof(target_url),
- "https://%s:%d%s%s%s",
-
- conn->host,
-#if defined(USE_IPV6)
- (conn->phys_ctx->listening_sockets[ssl_index].lsa.sa.sa_family
- == AF_INET6)
- ? (int)ntohs(conn->phys_ctx->listening_sockets[ssl_index]
- .lsa.sin6.sin6_port)
- :
-#endif
- (int)ntohs(conn->phys_ctx->listening_sockets[ssl_index]
- .lsa.sin.sin_port),
- conn->request_info.local_uri,
- (conn->request_info.query_string == NULL) ? "" : "?",
- (conn->request_info.query_string == NULL)
- ? ""
- : conn->request_info.query_string);
-
- /* Check overflow in location buffer (will not occur if MG_BUF_LEN
- * is used as buffer size) */
- if (truncated) {
- mg_send_http_error(conn, 500, "%s", "Redirect URL too long");
- return;
+ if (mg_construct_local_link(
+ conn, target_url, sizeof(target_url), expect_proto, port, NULL)
+ < 0) {
+ truncated = 1;
+ } else if (conn->request_info.query_string != NULL) {
+ size_t slen1 = strlen(target_url);
+ size_t slen2 = strlen(conn->request_info.query_string);
+ if ((slen1 + slen2 + 2) < sizeof(target_url)) {
+ target_url[slen1] = '?';
+ memcpy(target_url + slen1 + 1,
+ conn->request_info.query_string,
+ slen2);
+ target_url[slen1 + slen2 + 1] = 0;
+ } else {
+ truncated = 1;
}
-
- /* Use redirect helper function */
- mg_send_http_redirect(conn, target_url, redirect_code);
}
-}
-
-static void
-handler_info_acquire(struct mg_handler_info *handler_info)
-{
- pthread_mutex_lock(&handler_info->refcount_mutex);
- handler_info->refcount++;
- pthread_mutex_unlock(&handler_info->refcount_mutex);
-}
-
-
-static void
-handler_info_release(struct mg_handler_info *handler_info)
-{
- pthread_mutex_lock(&handler_info->refcount_mutex);
- handler_info->refcount--;
- pthread_cond_signal(&handler_info->refcount_cond);
- pthread_mutex_unlock(&handler_info->refcount_mutex);
-}
-
-
-static void
-handler_info_wait_unused(struct mg_handler_info *handler_info)
-{
- pthread_mutex_lock(&handler_info->refcount_mutex);
- while (handler_info->refcount) {
- pthread_cond_wait(&handler_info->refcount_cond,
- &handler_info->refcount_mutex);
+ /* Check overflow in location buffer (will not occur if MG_BUF_LEN
+ * is used as buffer size) */
+ if (truncated) {
+ mg_send_http_error(conn, 500, "%s", "Redirect URL too long");
+ return;
}
- pthread_mutex_unlock(&handler_info->refcount_mutex);
+
+ /* Use redirect helper function */
+ mg_send_http_redirect(conn, target_url, redirect_code);
}
@@ -13553,8 +13934,6 @@ mg_set_handler_type(struct mg_context *phys_ctx,
{
struct mg_handler_info *tmp_rh, **lastref;
size_t urilen = strlen(uri);
- struct mg_workerTLS tls;
- int is_tls_set = 0;
if (handler_type == WEBSOCKET_HANDLER) {
DEBUG_ASSERT(handler == NULL);
@@ -13611,35 +13990,26 @@ mg_set_handler_type(struct mg_context *phys_ctx,
return;
}
- /* Internal callbacks have their contexts set
- * if called from non-related thread, context must be set
- * since internal function assumes it exists.
- * For an example see how handler_info_wait_unused()
- * waits for reference to become zero
- */
- if (NULL == pthread_getspecific(sTlsKey)) {
- is_tls_set = 1;
- tls.is_master = -1;
- tls.thread_idx = phys_ctx->starter_thread_idx;
-#if defined(_WIN32)
- tls.pthread_cond_helper_mutex = NULL;
-#endif
- pthread_setspecific(sTlsKey, &tls);
- }
-
mg_lock_context(phys_ctx);
/* first try to find an existing handler */
- lastref = &(dom_ctx->handlers);
- for (tmp_rh = dom_ctx->handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
- if (tmp_rh->handler_type == handler_type) {
- if ((urilen == tmp_rh->uri_len) && !strcmp(tmp_rh->uri, uri)) {
+ do {
+ lastref = &(dom_ctx->handlers);
+ for (tmp_rh = dom_ctx->handlers; tmp_rh != NULL;
+ tmp_rh = tmp_rh->next) {
+ if (tmp_rh->handler_type == handler_type
+ && (urilen == tmp_rh->uri_len) && !strcmp(tmp_rh->uri, uri)) {
if (!is_delete_request) {
/* update existing handler */
if (handler_type == REQUEST_HANDLER) {
/* Wait for end of use before updating */
- handler_info_wait_unused(tmp_rh);
-
+ if (tmp_rh->refcount) {
+ mg_unlock_context(phys_ctx);
+ mg_sleep(1);
+ mg_lock_context(phys_ctx);
+ /* tmp_rh might have been freed, search again. */
+ break;
+ }
/* Ok, the handler is no more use -> Update it */
tmp_rh->handler = handler;
} else if (handler_type == WEBSOCKET_HANDLER) {
@@ -13656,34 +14026,31 @@ mg_set_handler_type(struct mg_context *phys_ctx,
/* remove existing handler */
if (handler_type == REQUEST_HANDLER) {
/* Wait for end of use before removing */
- handler_info_wait_unused(tmp_rh);
-
- /* Ok, the handler is no more used -> Destroy resources
- */
- pthread_cond_destroy(&tmp_rh->refcount_cond);
- pthread_mutex_destroy(&tmp_rh->refcount_mutex);
+ if (tmp_rh->refcount) {
+ tmp_rh->removing = 1;
+ mg_unlock_context(phys_ctx);
+ mg_sleep(1);
+ mg_lock_context(phys_ctx);
+ /* tmp_rh might have been freed, search again. */
+ break;
+ }
+ /* Ok, the handler is no more used */
}
*lastref = tmp_rh->next;
mg_free(tmp_rh->uri);
mg_free(tmp_rh);
}
mg_unlock_context(phys_ctx);
- if (is_tls_set) {
- pthread_setspecific(sTlsKey, NULL);
- }
return;
}
+ lastref = &(tmp_rh->next);
}
- lastref = &(tmp_rh->next);
- }
+ } while (tmp_rh != NULL);
if (is_delete_request) {
/* no handler to set, this was a remove request to a non-existing
* handler */
mg_unlock_context(phys_ctx);
- if (is_tls_set) {
- pthread_setspecific(sTlsKey, NULL);
- }
return;
}
@@ -13696,9 +14063,6 @@ mg_set_handler_type(struct mg_context *phys_ctx,
mg_cry_ctx_internal(phys_ctx,
"%s",
"Cannot create new request handler struct, OOM");
- if (is_tls_set) {
- pthread_setspecific(sTlsKey, NULL);
- }
return;
}
tmp_rh->uri = mg_strdup_ctx(uri, phys_ctx);
@@ -13708,34 +14072,12 @@ mg_set_handler_type(struct mg_context *phys_ctx,
mg_cry_ctx_internal(phys_ctx,
"%s",
"Cannot create new request handler struct, OOM");
- if (is_tls_set) {
- pthread_setspecific(sTlsKey, NULL);
- }
return;
}
tmp_rh->uri_len = urilen;
if (handler_type == REQUEST_HANDLER) {
- /* Init refcount mutex and condition */
- if (0 != pthread_mutex_init(&tmp_rh->refcount_mutex, NULL)) {
- mg_unlock_context(phys_ctx);
- mg_free(tmp_rh);
- mg_cry_ctx_internal(phys_ctx, "%s", "Cannot init refcount mutex");
- if (is_tls_set) {
- pthread_setspecific(sTlsKey, NULL);
- }
- return;
- }
- if (0 != pthread_cond_init(&tmp_rh->refcount_cond, NULL)) {
- mg_unlock_context(phys_ctx);
- pthread_mutex_destroy(&tmp_rh->refcount_mutex);
- mg_free(tmp_rh);
- mg_cry_ctx_internal(phys_ctx, "%s", "Cannot init refcount cond");
- if (is_tls_set) {
- pthread_setspecific(sTlsKey, NULL);
- }
- return;
- }
tmp_rh->refcount = 0;
+ tmp_rh->removing = 0;
tmp_rh->handler = handler;
} else if (handler_type == WEBSOCKET_HANDLER) {
tmp_rh->subprotocols = subprotocols;
@@ -13752,9 +14094,6 @@ mg_set_handler_type(struct mg_context *phys_ctx,
*lastref = tmp_rh;
mg_unlock_context(phys_ctx);
- if (is_tls_set) {
- pthread_setspecific(sTlsKey, NULL);
- }
}
@@ -13870,6 +14209,7 @@ get_request_handler(struct mg_connection *conn,
const char *uri = request_info->local_uri;
size_t urilen = strlen(uri);
struct mg_handler_info *tmp_rh;
+ int step, matched;
if (!conn || !conn->phys_ctx || !conn->dom_ctx) {
return 0;
@@ -13877,64 +14217,29 @@ get_request_handler(struct mg_connection *conn,
mg_lock_context(conn->phys_ctx);
- /* first try for an exact match */
- for (tmp_rh = conn->dom_ctx->handlers; tmp_rh != NULL;
- tmp_rh = tmp_rh->next) {
- if (tmp_rh->handler_type == handler_type) {
- if ((urilen == tmp_rh->uri_len) && !strcmp(tmp_rh->uri, uri)) {
- if (handler_type == WEBSOCKET_HANDLER) {
- *subprotocols = tmp_rh->subprotocols;
- *connect_handler = tmp_rh->connect_handler;
- *ready_handler = tmp_rh->ready_handler;
- *data_handler = tmp_rh->data_handler;
- *close_handler = tmp_rh->close_handler;
- } else if (handler_type == REQUEST_HANDLER) {
- *handler = tmp_rh->handler;
- /* Acquire handler and give it back */
- handler_info_acquire(tmp_rh);
- *handler_info = tmp_rh;
- } else { /* AUTH_HANDLER */
- *auth_handler = tmp_rh->auth_handler;
- }
- *cbdata = tmp_rh->cbdata;
- mg_unlock_context(conn->phys_ctx);
- return 1;
+ for (step = 0; step < 3; step++) {
+ for (tmp_rh = conn->dom_ctx->handlers; tmp_rh != NULL;
+ tmp_rh = tmp_rh->next) {
+ if (tmp_rh->handler_type != handler_type) {
+ continue;
}
- }
- }
-
- /* next try for a partial match, we will accept uri/something */
- for (tmp_rh = conn->dom_ctx->handlers; tmp_rh != NULL;
- tmp_rh = tmp_rh->next) {
- if (tmp_rh->handler_type == handler_type) {
- if ((tmp_rh->uri_len < urilen) && (uri[tmp_rh->uri_len] == '/')
- && (memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0)) {
- if (handler_type == WEBSOCKET_HANDLER) {
- *subprotocols = tmp_rh->subprotocols;
- *connect_handler = tmp_rh->connect_handler;
- *ready_handler = tmp_rh->ready_handler;
- *data_handler = tmp_rh->data_handler;
- *close_handler = tmp_rh->close_handler;
- } else if (handler_type == REQUEST_HANDLER) {
- *handler = tmp_rh->handler;
- /* Acquire handler and give it back */
- handler_info_acquire(tmp_rh);
- *handler_info = tmp_rh;
- } else { /* AUTH_HANDLER */
- *auth_handler = tmp_rh->auth_handler;
- }
- *cbdata = tmp_rh->cbdata;
- mg_unlock_context(conn->phys_ctx);
- return 1;
+ if (step == 0) {
+ /* first try for an exact match */
+ matched = (tmp_rh->uri_len == urilen)
+ && (strcmp(tmp_rh->uri, uri) == 0);
+ } else if (step == 1) {
+ /* next try for a partial match, we will accept
+ uri/something */
+ matched =
+ (tmp_rh->uri_len < urilen)
+ && (uri[tmp_rh->uri_len] == '/')
+ && (memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0);
+ } else {
+ /* finally try for pattern match */
+ matched =
+ match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0;
}
- }
- }
-
- /* finally try for pattern match */
- for (tmp_rh = conn->dom_ctx->handlers; tmp_rh != NULL;
- tmp_rh = tmp_rh->next) {
- if (tmp_rh->handler_type == handler_type) {
- if (match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0) {
+ if (matched) {
if (handler_type == WEBSOCKET_HANDLER) {
*subprotocols = tmp_rh->subprotocols;
*connect_handler = tmp_rh->connect_handler;
@@ -13942,9 +14247,14 @@ get_request_handler(struct mg_connection *conn,
*data_handler = tmp_rh->data_handler;
*close_handler = tmp_rh->close_handler;
} else if (handler_type == REQUEST_HANDLER) {
+ if (tmp_rh->removing) {
+ /* Treat as none found */
+ step = 2;
+ break;
+ }
*handler = tmp_rh->handler;
/* Acquire handler and give it back */
- handler_info_acquire(tmp_rh);
+ tmp_rh->refcount++;
*handler_info = tmp_rh;
} else { /* AUTH_HANDLER */
*auth_handler = tmp_rh->auth_handler;
@@ -13977,37 +14287,13 @@ is_in_script_path(const struct mg_connection *conn, const char *path)
}
-#if defined(USE_WEBSOCKET) \
- && (defined(MG_LEGACY_INTERFACE) || defined(MG_EXPERIMENTAL_INTERFACES))
+#if defined(USE_WEBSOCKET) && defined(MG_EXPERIMENTAL_INTERFACES)
static int
-deprecated_websocket_connect_wrapper(const struct mg_connection *conn,
- void *cbdata)
-{
- struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
- if (pcallbacks->websocket_connect) {
- return pcallbacks->websocket_connect(conn);
- }
- /* No handler set - assume "OK" */
- return 0;
-}
-
-
-static void
-deprecated_websocket_ready_wrapper(struct mg_connection *conn, void *cbdata)
-{
- struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
- if (pcallbacks->websocket_ready) {
- pcallbacks->websocket_ready(conn);
- }
-}
-
-
-static int
-deprecated_websocket_data_wrapper(struct mg_connection *conn,
- int bits,
- char *data,
- size_t len,
- void *cbdata)
+experimental_websocket_client_data_wrapper(struct mg_connection *conn,
+ int bits,
+ char *data,
+ size_t len,
+ void *cbdata)
{
struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
if (pcallbacks->websocket_data) {
@@ -14019,8 +14305,8 @@ deprecated_websocket_data_wrapper(struct mg_connection *conn,
static void
-deprecated_websocket_close_wrapper(const struct mg_connection *conn,
- void *cbdata)
+experimental_websocket_client_close_wrapper(const struct mg_connection *conn,
+ void *cbdata)
{
struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
if (pcallbacks->connection_close) {
@@ -14041,7 +14327,8 @@ handle_request(struct mg_connection *conn)
char path[PATH_MAX];
int uri_len, ssl_index;
int is_found = 0, is_script_resource = 0, is_websocket_request = 0,
- is_put_or_delete_request = 0, is_callback_resource = 0;
+ is_put_or_delete_request = 0, is_callback_resource = 0,
+ is_template_text_file = 0;
int i;
struct mg_file file = STRUCT_FILE_INITIALIZER;
mg_request_handler callback_handler = NULL;
@@ -14060,6 +14347,9 @@ handle_request(struct mg_connection *conn)
path[0] = 0;
+ /* 0. Reset internal state (required for HTTP/2 proxy) */
+ conn->request_state = 0;
+
/* 1. get the request url */
/* 1.1. split into url and query string */
if ((conn->request_info.query_string = strchr(ri->request_uri, '?'))
@@ -14071,7 +14361,9 @@ handle_request(struct mg_connection *conn)
if (!conn->client.is_ssl && conn->client.ssl_redir) {
ssl_index = get_first_ssl_listener_index(conn->phys_ctx);
if (ssl_index >= 0) {
- redirect_to_https_port(conn, ssl_index);
+ int port = (int)ntohs(USA_IN_PORT_UNSAFE(
+ &(conn->phys_ctx->listening_sockets[ssl_index].lsa)));
+ redirect_to_https_port(conn, port);
} else {
/* A http to https forward port has been specified,
* but no https port to forward to. */
@@ -14091,6 +14383,10 @@ handle_request(struct mg_connection *conn)
if (should_decode_url(conn)) {
mg_url_decode(
ri->local_uri, uri_len, (char *)ri->local_uri, uri_len + 1, 0);
+
+ if (conn->request_info.query_string) {
+ url_decode_in_place((char *)conn->request_info.query_string);
+ }
}
/* 1.4. clean URIs, so a path like allowed_dir/../forbidden_file is
@@ -14103,7 +14399,7 @@ handle_request(struct mg_connection *conn)
/* 2. if this ip has limited speed, set it for this connection */
conn->throttle = set_throttle(conn->dom_ctx->config[THROTTLE],
- get_remote_ip(conn),
+ &conn->client.rsa,
ri->local_uri);
/* 3. call a "handle everything" callback, if registered */
@@ -14155,8 +14451,7 @@ handle_request(struct mg_connection *conn)
&& (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0)
&& (cors_origin != NULL) && (cors_acrm != NULL)) {
/* This is a valid CORS preflight, and the server is configured
- * to
- * handle it automatically. */
+ * to handle it automatically. */
const char *cors_acrh =
get_header(ri->http_headers,
ri->num_headers,
@@ -14204,12 +14499,17 @@ handle_request(struct mg_connection *conn)
/* 5.1. first test, if the request targets the regular http(s)://
* protocol namespace or the websocket ws(s):// protocol namespace.
*/
- is_websocket_request = is_websocket_protocol(conn);
+ is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET);
#if defined(USE_WEBSOCKET)
handler_type = is_websocket_request ? WEBSOCKET_HANDLER : REQUEST_HANDLER;
#else
handler_type = REQUEST_HANDLER;
#endif /* defined(USE_WEBSOCKET) */
+
+ if (is_websocket_request) {
+ HTTP1_only;
+ }
+
/* 5.2. check if the request will be handled by a callback */
if (get_request_handler(conn,
handler_type,
@@ -14223,9 +14523,8 @@ handle_request(struct mg_connection *conn)
&callback_data,
&handler_info)) {
/* 5.2.1. A callback will handle this request. All requests
- * handled
- * by a callback have to be considered as requests to a script
- * resource. */
+ * handled by a callback have to be considered as requests
+ * to a script resource. */
is_callback_resource = 1;
is_script_resource = 1;
is_put_or_delete_request = is_put_or_delete_method(conn);
@@ -14243,7 +14542,8 @@ handle_request(struct mg_connection *conn)
&is_found,
&is_script_resource,
&is_websocket_request,
- &is_put_or_delete_request);
+ &is_put_or_delete_request,
+ &is_template_text_file);
}
/* 6. authorization check */
@@ -14264,6 +14564,7 @@ handle_request(struct mg_connection *conn)
}
} else if (is_put_or_delete_request && !is_script_resource
&& !is_callback_resource) {
+ HTTP1_only;
/* 6.2. this request is a PUT/DELETE to a real file */
/* 6.2.1. thus, the server must have real files */
#if defined(NO_FILES)
@@ -14304,11 +14605,14 @@ handle_request(struct mg_connection *conn)
/* 7. check if there are request handlers for this uri */
if (is_callback_resource) {
+ HTTP1_only;
if (!is_websocket_request) {
i = callback_handler(conn, callback_data);
/* Callback handler will not be used anymore. Release it */
- handler_info_release(handler_info);
+ mg_lock_context(conn->phys_ctx);
+ handler_info->refcount--;
+ mg_unlock_context(conn->phys_ctx);
if (i > 0) {
/* Do nothing, callback has served the request. Store
@@ -14330,7 +14634,7 @@ handle_request(struct mg_connection *conn)
*
* TODO: What would be the best reaction here?
* (Note: The reaction may change, if there is a better
- *idea.)
+ * idea.)
*/
/* For the moment, use option c: We look for a proper file,
@@ -14343,7 +14647,8 @@ handle_request(struct mg_connection *conn)
&is_found,
&is_script_resource,
&is_websocket_request,
- &is_put_or_delete_request);
+ &is_put_or_delete_request,
+ &is_template_text_file);
callback_handler = NULL;
/* Here we are at a dead end:
@@ -14376,6 +14681,7 @@ handle_request(struct mg_connection *conn)
/* 8. handle websocket requests */
#if defined(USE_WEBSOCKET)
if (is_websocket_request) {
+ HTTP1_only;
if (is_script_resource) {
if (is_in_script_path(conn, path)) {
@@ -14394,20 +14700,7 @@ handle_request(struct mg_connection *conn)
mg_send_http_error(conn, 403, "%s", "Forbidden");
}
} else {
-#if defined(MG_LEGACY_INTERFACE)
- handle_websocket_request(
- conn,
- path,
- !is_script_resource /* could be deprecated global callback */,
- NULL,
- deprecated_websocket_connect_wrapper,
- deprecated_websocket_ready_wrapper,
- deprecated_websocket_data_wrapper,
- NULL,
- conn->phys_ctx->callbacks);
-#else
mg_send_http_error(conn, 404, "%s", "Not found");
-#endif
}
return;
} else
@@ -14429,12 +14722,14 @@ handle_request(struct mg_connection *conn)
/* 10. Request is handled by a script */
if (is_script_resource) {
+ HTTP1_only;
handle_file_based_request(conn, path, &file);
return;
}
/* 11. Handle put/delete/mkcol requests */
if (is_put_or_delete_request) {
+ HTTP1_only;
/* 11.1. PUT method */
if (!strcmp(ri->request_method, "PUT")) {
put_file(conn, path);
@@ -14470,19 +14765,17 @@ handle_request(struct mg_connection *conn)
/* 12. Directory uris should end with a slash */
if (file.stat.is_directory && (uri_len > 0)
&& (ri->local_uri[uri_len - 1] != '/')) {
- gmt_time_string(date, sizeof(date), &curtime);
- mg_printf(conn,
- "HTTP/1.1 301 Moved Permanently\r\n"
- "Location: %s/\r\n"
- "Date: %s\r\n"
- /* "Cache-Control: private\r\n" (= default) */
- "Content-Length: 0\r\n"
- "Connection: %s\r\n",
- ri->request_uri,
- date,
- suggest_connection_header(conn));
- send_additional_header(conn);
- mg_printf(conn, "\r\n");
+
+ size_t len = strlen(ri->request_uri);
+ char *new_path = mg_malloc_ctx(len + 2, conn->phys_ctx);
+ if (!new_path) {
+ mg_send_http_error(conn, 500, "out or memory");
+ } else {
+ memcpy(new_path, ri->request_uri, len);
+ new_path[len] = '/';
+ new_path[len + 1] = 0;
+ mg_send_http_redirect(conn, new_path, 301);
+ }
return;
}
@@ -14529,8 +14822,25 @@ handle_request(struct mg_connection *conn)
return;
}
- /* 15. read a normal file with GET or HEAD */
- handle_file_based_request(conn, path, &file);
+ /* 15. Files with search/replace patterns: LSP and SSI */
+ if (is_template_text_file) {
+ HTTP1_only;
+ handle_file_based_request(conn, path, &file);
+ return;
+ }
+
+ /* 16. Static file - maybe cached */
+#if !defined(NO_CACHING)
+ if ((!conn->in_error_handler) && is_not_modified(conn, &file.stat)) {
+ /* Send 304 "Not Modified" - this must not send any body data */
+ handle_not_modified_static_file_request(conn, &file);
+ return;
+ }
+#endif /* !NO_CACHING */
+
+ /* 17. Static file - not cached */
+ handle_static_file_request(conn, path, &file, NULL, NULL);
+
#endif /* !defined(NO_FILES) */
}
@@ -14547,26 +14857,20 @@ handle_file_based_request(struct mg_connection *conn,
if (0) {
#if defined(USE_LUA)
- } else if (match_prefix(
- conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
- strlen(conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS]),
- path)
+ } else if (match_prefix_strlen(
+ conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS], path)
> 0) {
if (is_in_script_path(conn, path)) {
/* Lua server page: an SSI like page containing mostly plain
- * html
- * code
- * plus some tags with server generated contents. */
+ * html code plus some tags with server generated contents. */
handle_lsp_request(conn, path, file, NULL);
} else {
/* Script was in an illegal path */
mg_send_http_error(conn, 403, "%s", "Forbidden");
}
- } else if (match_prefix(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS],
- strlen(
- conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS]),
- path)
+ } else if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS],
+ path)
> 0) {
if (is_in_script_path(conn, path)) {
/* Lua in-server module script: a CGI like script used to
@@ -14580,10 +14884,8 @@ handle_file_based_request(struct mg_connection *conn,
}
#endif
#if defined(USE_DUKTAPE)
- } else if (match_prefix(
- conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
- strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
- path)
+ } else if (match_prefix_strlen(
+ conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS], path)
> 0) {
if (is_in_script_path(conn, path)) {
/* Call duktape to generate the page */
@@ -14594,9 +14896,7 @@ handle_file_based_request(struct mg_connection *conn,
}
#endif
#if !defined(NO_CGI)
- } else if (match_prefix(conn->dom_ctx->config[CGI_EXTENSIONS],
- strlen(conn->dom_ctx->config[CGI_EXTENSIONS]),
- path)
+ } else if (match_prefix_strlen(conn->dom_ctx->config[CGI_EXTENSIONS], path)
> 0) {
if (is_in_script_path(conn, path)) {
/* CGI scripts may support all HTTP methods */
@@ -14606,9 +14906,7 @@ handle_file_based_request(struct mg_connection *conn,
mg_send_http_error(conn, 403, "%s", "Forbidden");
}
#endif /* !NO_CGI */
- } else if (match_prefix(conn->dom_ctx->config[SSI_EXTENSIONS],
- strlen(conn->dom_ctx->config[SSI_EXTENSIONS]),
- path)
+ } else if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path)
> 0) {
if (is_in_script_path(conn, path)) {
handle_ssi_file_request(conn, path, file);
@@ -14665,7 +14963,9 @@ close_all_listening_sockets(struct mg_context *ctx)
static int
parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
{
- unsigned int a, b, c, d, port;
+ unsigned int a, b, c, d;
+ unsigned port;
+ unsigned long portUL;
int ch, len;
const char *cb;
char *endptr;
@@ -14680,7 +14980,7 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
so->lsa.sin.sin_family = AF_INET;
*ip_version = 0;
- /* Initialize port and len as invalid. */
+ /* Initialize len as invalid. */
port = 0;
len = 0;
@@ -14695,8 +14995,9 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
#if defined(USE_IPV6)
} else if (sscanf(vec->ptr, "[%49[^]]]:%u%n", buf, &port, &len) == 2
+ && ((size_t)len <= vec->len)
&& mg_inet_pton(
- AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6))) {
+ AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6), 0)) {
/* IPv6 address, examples: see above */
/* so->lsa.sin6.sin6_family = AF_INET6; already set by mg_inet_pton
*/
@@ -14722,9 +15023,10 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
*ip_version = 4;
#endif
- } else if (is_valid_port(port = strtoul(vec->ptr, &endptr, 0))
- && vec->ptr != endptr) {
- len = endptr - vec->ptr;
+ } else if (is_valid_port(portUL = strtoul(vec->ptr, &endptr, 0))
+ && (vec->ptr != endptr)) {
+ len = (int)(endptr - vec->ptr);
+ port = (uint16_t)portUL;
/* If only port is specified, bind to IPv4, INADDR_ANY */
so->lsa.sin.sin_port = htons((uint16_t)port);
*ip_version = 4;
@@ -14742,62 +15044,57 @@ parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
char hostname[256];
size_t hostnlen = (size_t)(cb - vec->ptr);
- if (hostnlen >= sizeof(hostname)) {
+ if ((hostnlen >= vec->len) || (hostnlen >= sizeof(hostname))) {
/* This would be invalid in any case */
*ip_version = 0;
return 0;
}
- memcpy(hostname, vec->ptr, hostnlen);
- hostname[hostnlen] = 0;
+ mg_strlcpy(hostname, vec->ptr, hostnlen + 1);
if (mg_inet_pton(
- AF_INET, hostname, &so->lsa.sin, sizeof(so->lsa.sin))) {
+ AF_INET, hostname, &so->lsa.sin, sizeof(so->lsa.sin), 1)) {
if (sscanf(cb + 1, "%u%n", &port, &len) == 1) {
*ip_version = 4;
- so->lsa.sin.sin_family = AF_INET;
so->lsa.sin.sin_port = htons((uint16_t)port);
len += (int)(hostnlen + 1);
} else {
- port = 0;
len = 0;
}
#if defined(USE_IPV6)
} else if (mg_inet_pton(AF_INET6,
hostname,
&so->lsa.sin6,
- sizeof(so->lsa.sin6))) {
+ sizeof(so->lsa.sin6),
+ 1)) {
if (sscanf(cb + 1, "%u%n", &port, &len) == 1) {
*ip_version = 6;
- so->lsa.sin6.sin6_family = AF_INET6;
- so->lsa.sin.sin_port = htons((uint16_t)port);
+ so->lsa.sin6.sin6_port = htons((uint16_t)port);
len += (int)(hostnlen + 1);
} else {
- port = 0;
len = 0;
}
#endif
+ } else {
+ len = 0;
}
-
} else {
/* Parsing failure. */
+ len = 0;
}
/* sscanf and the option splitting code ensure the following condition
- */
- if ((len < 0) && ((unsigned)len > (unsigned)vec->len)) {
- *ip_version = 0;
- return 0;
- }
- ch = vec->ptr[len]; /* Next character after the port number */
- so->is_ssl = (ch == 's');
- so->ssl_redir = (ch == 'r');
-
- /* Make sure the port is valid and vector ends with 's', 'r' or ',' */
- if (is_valid_port(port)
- && ((ch == '\0') || (ch == 's') || (ch == 'r') || (ch == ','))) {
- return 1;
+ * Make sure the port is valid and vector ends with the port, 's' or 'r' */
+ if ((len > 0) && is_valid_port(port)
+ && (((size_t)len == vec->len) || ((size_t)(len + 1) == vec->len))) {
+ /* Next character after the port number */
+ ch = ((size_t)len < vec->len) ? vec->ptr[len] : '\0';
+ so->is_ssl = (ch == 's');
+ so->ssl_redir = (ch == 'r');
+ if ((ch == '\0') || (ch == 's') || (ch == 'r')) {
+ return 1;
+ }
}
/* Reset ip_version to 0 if there is an error */
@@ -14839,8 +15136,8 @@ is_ssl_port_used(const char *ports)
* reading it as a list element for element and parsing with an
* algorithm equivalent to parse_port_string.
*
- * In fact, we use local interface names here, not arbitrary hostnames,
- * so in most cases the only name will be "localhost".
+ * In fact, we use local interface names here, not arbitrary
+ * hostnames, so in most cases the only name will be "localhost".
*
* So, for now, we use this simple algorithm, that may still return
* a false positive in bizarre cases.
@@ -14981,10 +15278,10 @@ set_ports_option(struct mg_context *phys_ctx)
!= 0) {
/* Set IPv6 only option, but don't abort on errors. */
- mg_cry_ctx_internal(
- phys_ctx,
- "cannot set socket option IPV6_V6ONLY=off (entry %i)",
- portsTotal);
+ mg_cry_ctx_internal(phys_ctx,
+ "cannot set socket option "
+ "IPV6_V6ONLY=off (entry %i)",
+ portsTotal);
}
} else {
if (so.lsa.sa.sa_family == AF_INET6
@@ -14996,10 +15293,10 @@ set_ports_option(struct mg_context *phys_ctx)
!= 0) {
/* Set IPv6 only option, but don't abort on errors. */
- mg_cry_ctx_internal(
- phys_ctx,
- "cannot set socket option IPV6_V6ONLY=on (entry %i)",
- portsTotal);
+ mg_cry_ctx_internal(phys_ctx,
+ "cannot set socket option "
+ "IPV6_V6ONLY=on (entry %i)",
+ portsTotal);
}
}
#else
@@ -15059,6 +15356,8 @@ set_ports_option(struct mg_context *phys_ctx)
"%s value \"%s\" is invalid",
config_options[LISTEN_BACKLOG_SIZE].name,
opt_txt);
+ closesocket(so.sock);
+ so.sock = INVALID_SOCKET;
continue;
}
@@ -15263,10 +15562,9 @@ log_access(const struct mg_connection *conn)
* Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
*/
static int
-check_acl(struct mg_context *phys_ctx, uint32_t remote_ip)
+check_acl(struct mg_context *phys_ctx, const union usa *sa)
{
- int allowed, flag;
- uint32_t net, mask;
+ int allowed, flag, matched;
struct vec vec;
if (phys_ctx) {
@@ -15277,15 +15575,19 @@ check_acl(struct mg_context *phys_ctx, uint32_t remote_ip)
while ((list = next_option(list, &vec, NULL)) != NULL) {
flag = vec.ptr[0];
- if ((flag != '+' && flag != '-')
- || (parse_net(&vec.ptr[1], &net, &mask) == 0)) {
+ matched = -1;
+ if ((vec.len > 0) && ((flag == '+') || (flag == '-'))) {
+ vec.ptr++;
+ vec.len--;
+ matched = parse_match_net(&vec, sa, 1);
+ }
+ if (matched < 0) {
mg_cry_ctx_internal(phys_ctx,
- "%s: subnet must be [+|-]x.x.x.x[/x]",
+ "%s: subnet must be [+|-]IP-addr[/x]",
__func__);
return -1;
}
-
- if (net == (remote_ip & mask)) {
+ if (matched) {
allowed = flag;
}
}
@@ -15379,12 +15681,8 @@ static const char *ssl_error(void);
static int
refresh_trust(struct mg_connection *conn)
{
- static int reload_lock = 0;
- static long int data_check = 0;
- volatile int *p_reload_lock = (volatile int *)&reload_lock;
-
struct stat cert_buf;
- long int t;
+ int64_t t = 0;
const char *pem;
const char *chain;
int should_verify_peer;
@@ -15403,13 +15701,13 @@ refresh_trust(struct mg_connection *conn)
chain = NULL;
}
- t = data_check;
if (stat(pem, &cert_buf) != -1) {
- t = (long int)cert_buf.st_mtime;
+ t = (int64_t)cert_buf.st_mtime;
}
- if (data_check != t) {
- data_check = t;
+ mg_lock_context(conn->phys_ctx);
+ if ((t != 0) && (conn->dom_ctx->ssl_cert_last_mtime != t)) {
+ conn->dom_ctx->ssl_cert_last_mtime = t;
should_verify_peer = 0;
if (conn->dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) {
@@ -15430,6 +15728,7 @@ refresh_trust(struct mg_connection *conn)
ca_file,
ca_path)
!= 1) {
+ mg_unlock_context(conn->phys_ctx);
mg_cry_ctx_internal(
conn->phys_ctx,
"SSL_CTX_load_verify_locations error: %s "
@@ -15442,18 +15741,12 @@ refresh_trust(struct mg_connection *conn)
}
}
- if (1 == mg_atomic_inc(p_reload_lock)) {
- if (ssl_use_pem_file(conn->phys_ctx, conn->dom_ctx, pem, chain)
- == 0) {
- return 0;
- }
- *p_reload_lock = 0;
+ if (ssl_use_pem_file(conn->phys_ctx, conn->dom_ctx, pem, chain) == 0) {
+ mg_unlock_context(conn->phys_ctx);
+ return 0;
}
}
- /* lock while cert is reloading */
- while (*p_reload_lock) {
- sleep(1);
- }
+ mg_unlock_context(conn->phys_ctx);
return 1;
}
@@ -15465,9 +15758,7 @@ static pthread_mutex_t *ssl_mutexes;
static int
sslize(struct mg_connection *conn,
- SSL_CTX *s,
int (*func)(SSL *),
- volatile int *stop_server,
const struct mg_client_options *client_options)
{
int ret, err;
@@ -15490,16 +15781,19 @@ sslize(struct mg_connection *conn,
}
}
- conn->ssl = SSL_new(s);
+ mg_lock_context(conn->phys_ctx);
+ conn->ssl = SSL_new(conn->dom_ctx->ssl_ctx);
+ mg_unlock_context(conn->phys_ctx);
if (conn->ssl == NULL) {
+ mg_cry_internal(conn, "sslize error: %s", ssl_error());
+ OPENSSL_REMOVE_THREAD_STATE();
return 0;
}
SSL_set_app_data(conn->ssl, (char *)conn);
ret = SSL_set_fd(conn->ssl, conn->client.sock);
if (ret != 1) {
- err = SSL_get_error(conn->ssl, ret);
- mg_cry_internal(conn, "SSL error %i, destroying SSL context", err);
+ mg_cry_internal(conn, "sslize error: %s", ssl_error());
SSL_free(conn->ssl);
conn->ssl = NULL;
OPENSSL_REMOVE_THREAD_STATE();
@@ -15516,13 +15810,18 @@ sslize(struct mg_connection *conn,
if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
/* NOTE: The loop below acts as a back-off, so we can end
* up sleeping for more (or less) than the REQUEST_TIMEOUT. */
- timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]);
+ int to = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]);
+ if (to >= 0) {
+ timeout = (unsigned)to;
+ }
}
/* SSL functions may fail and require to be called again:
* see https://www.openssl.org/docs/manmaster/ssl/SSL_get_error.html
* Here "func" could be SSL_connect or SSL_accept. */
for (i = 0; i <= timeout; i += 50) {
+ ERR_clear_error();
+ /* conn->dom_ctx may be changed here (see ssl_servername_callback) */
ret = func(conn->ssl);
if (ret != 1) {
err = SSL_get_error(conn->ssl, ret);
@@ -15530,7 +15829,7 @@ sslize(struct mg_connection *conn,
|| (err == SSL_ERROR_WANT_ACCEPT)
|| (err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)
|| (err == SSL_ERROR_WANT_X509_LOOKUP)) {
- if (*stop_server) {
+ if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
/* Don't wait if the server is going to be stopped. */
break;
}
@@ -15548,7 +15847,8 @@ sslize(struct mg_connection *conn,
|| (err == SSL_ERROR_WANT_WRITE))
? POLLOUT
: POLLIN;
- pollres = mg_poll(&pfd, 1, 50, stop_server);
+ pollres =
+ mg_poll(&pfd, 1, 50, &(conn->phys_ctx->stop_flag));
if (pollres < 0) {
/* Break if error occured (-1)
* or server shutdown (-2) */
@@ -15558,8 +15858,7 @@ sslize(struct mg_connection *conn,
} else if (err == SSL_ERROR_SYSCALL) {
/* This is an IO error. Look at errno. */
- err = errno;
- mg_cry_internal(conn, "SSL syscall error %i", err);
+ mg_cry_internal(conn, "SSL syscall error %i", ERRNO);
break;
} else {
@@ -15567,13 +15866,13 @@ sslize(struct mg_connection *conn,
mg_cry_internal(conn, "sslize error: %s", ssl_error());
break;
}
- ERR_clear_error();
} else {
/* success */
break;
}
}
+ ERR_clear_error();
if (ret != 1) {
SSL_free(conn->ssl);
@@ -15622,14 +15921,13 @@ hexdump2string(void *mem, int memlen, char *buf, int buflen)
}
-static void
-ssl_get_client_cert_info(struct mg_connection *conn)
+static int
+ssl_get_client_cert_info(const struct mg_connection *conn,
+ struct mg_client_cert *client_cert)
{
X509 *cert = SSL_get_peer_certificate(conn->ssl);
if (cert) {
- char str_subject[1024];
- char str_issuer[1024];
- char str_finger[1024];
+ char str_buf[1024];
unsigned char buf[256];
char *str_serial = NULL;
unsigned int ulen;
@@ -15649,12 +15947,18 @@ ssl_get_client_cert_info(struct mg_connection *conn)
/* Translate serial number to a hex string */
BIGNUM *serial_bn = ASN1_INTEGER_to_BN(serial, NULL);
- str_serial = BN_bn2hex(serial_bn);
- BN_free(serial_bn);
+ if (serial_bn) {
+ str_serial = BN_bn2hex(serial_bn);
+ BN_free(serial_bn);
+ }
+ client_cert->serial =
+ str_serial ? mg_strdup_ctx(str_serial, conn->phys_ctx) : NULL;
/* Translate subject and issuer to a string */
- (void)X509_NAME_oneline(subj, str_subject, (int)sizeof(str_subject));
- (void)X509_NAME_oneline(iss, str_issuer, (int)sizeof(str_issuer));
+ (void)X509_NAME_oneline(subj, str_buf, (int)sizeof(str_buf));
+ client_cert->subject = mg_strdup_ctx(str_buf, conn->phys_ctx);
+ (void)X509_NAME_oneline(iss, str_buf, (int)sizeof(str_buf));
+ client_cert->issuer = mg_strdup_ctx(str_buf, conn->phys_ctx);
/* Calculate SHA1 fingerprint and store as a hex string */
ulen = 0;
@@ -15676,34 +15980,19 @@ ssl_get_client_cert_info(struct mg_connection *conn)
mg_free(tmp_buf);
}
- if (!hexdump2string(
- buf, (int)ulen, str_finger, (int)sizeof(str_finger))) {
- *str_finger = 0;
+ if (!hexdump2string(buf, (int)ulen, str_buf, (int)sizeof(str_buf))) {
+ *str_buf = 0;
}
+ client_cert->finger = mg_strdup_ctx(str_buf, conn->phys_ctx);
- conn->request_info.client_cert = (struct mg_client_cert *)
- mg_malloc_ctx(sizeof(struct mg_client_cert), conn->phys_ctx);
- if (conn->request_info.client_cert) {
- conn->request_info.client_cert->peer_cert = (void *)cert;
- conn->request_info.client_cert->subject =
- mg_strdup_ctx(str_subject, conn->phys_ctx);
- conn->request_info.client_cert->issuer =
- mg_strdup_ctx(str_issuer, conn->phys_ctx);
- conn->request_info.client_cert->serial =
- mg_strdup_ctx(str_serial, conn->phys_ctx);
- conn->request_info.client_cert->finger =
- mg_strdup_ctx(str_finger, conn->phys_ctx);
- } else {
- mg_cry_internal(conn,
- "%s",
- "Out of memory: Cannot allocate memory for client "
- "certificate");
- }
+ client_cert->peer_cert = (void *)cert;
/* Strings returned from bn_bn2hex must be freed using OPENSSL_free,
* see https://linux.die.net/man/3/bn_bn2hex */
OPENSSL_free(str_serial);
+ return 1;
}
+ return 0;
}
@@ -15726,8 +16015,13 @@ ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
#if !defined(NO_SSL_DL)
+/* Load a DLL/Shared Object with a TLS/SSL implementation. */
static void *
-load_dll(char *ebuf, size_t ebuf_len, const char *dll_name, struct ssl_func *sw)
+load_tls_dll(char *ebuf,
+ size_t ebuf_len,
+ const char *dll_name,
+ struct ssl_func *sw,
+ int *feature_missing)
{
union {
void *p;
@@ -15760,36 +16054,47 @@ load_dll(char *ebuf, size_t ebuf_len, const char *dll_name, struct ssl_func *sw)
* cast. */
u.p = dlsym(dll_handle, fp->name);
#endif /* _WIN32 */
+
+ /* Set pointer (might be NULL) */
+ fp->ptr = u.fp;
+
if (u.fp == NULL) {
- if (ok) {
- mg_snprintf(NULL,
- &truncated,
- ebuf,
- ebuf_len,
- "%s: %s: cannot find %s",
- __func__,
- dll_name,
- fp->name);
- ok = 0;
- } else {
- size_t cur_len = strlen(ebuf);
- if (!truncated) {
+ DEBUG_TRACE("Missing function: %s\n", fp->name);
+ if (feature_missing) {
+ feature_missing[fp->required]++;
+ }
+ if (fp->required == TLS_Mandatory) {
+ /* Mandatory function is missing */
+ if (ok) {
+ /* This is the first missing function.
+ * Create a new error message. */
mg_snprintf(NULL,
&truncated,
- ebuf + cur_len,
- ebuf_len - cur_len - 3,
- ", %s",
+ ebuf,
+ ebuf_len,
+ "%s: %s: cannot find %s",
+ __func__,
+ dll_name,
fp->name);
- if (truncated) {
- /* If truncated, add "..." */
- strcat(ebuf, "...");
+ ok = 0;
+ } else {
+ /* This is yet anothermissing function.
+ * Append existing error message. */
+ size_t cur_len = strlen(ebuf);
+ if (!truncated && ((ebuf_len - cur_len) > 3)) {
+ mg_snprintf(NULL,
+ &truncated,
+ ebuf + cur_len,
+ ebuf_len - cur_len - 3,
+ ", %s",
+ fp->name);
+ if (truncated) {
+ /* If truncated, add "..." */
+ strcat(ebuf, "...");
+ }
}
}
}
- /* Debug:
- * printf("Missing function: %s\n", fp->name); */
- } else {
- fp->ptr = u.fp;
}
}
@@ -15809,44 +16114,21 @@ static void *cryptolib_dll_handle; /* Store the crypto library handle. */
#if defined(SSL_ALREADY_INITIALIZED)
-static int cryptolib_users = 1; /* Reference counter for crypto library. */
+static volatile ptrdiff_t cryptolib_users =
+ 1; /* Reference counter for crypto library. */
#else
-static int cryptolib_users = 0; /* Reference counter for crypto library. */
+static volatile ptrdiff_t cryptolib_users =
+ 0; /* Reference counter for crypto library. */
#endif
static int
initialize_ssl(char *ebuf, size_t ebuf_len)
{
-#if defined(OPENSSL_API_1_1)
- if (ebuf_len > 0) {
- ebuf[0] = 0;
- }
-
-#if !defined(NO_SSL_DL)
- if (!cryptolib_dll_handle) {
- cryptolib_dll_handle = load_dll(ebuf, ebuf_len, CRYPTO_LIB, crypto_sw);
- if (!cryptolib_dll_handle) {
- mg_snprintf(NULL,
- NULL, /* No truncation check for ebuf */
- ebuf,
- ebuf_len,
- "%s: error loading library %s",
- __func__,
- CRYPTO_LIB);
- DEBUG_TRACE("%s", ebuf);
- return 0;
- }
- }
-#endif /* NO_SSL_DL */
-
- if (mg_atomic_inc(&cryptolib_users) > 1) {
- return 1;
- }
-
-#else /* not OPENSSL_API_1_1 */
+#if !defined(OPENSSL_API_1_1)
int i, num_locks;
size_t size;
+#endif
if (ebuf_len > 0) {
ebuf[0] = 0;
@@ -15854,7 +16136,9 @@ initialize_ssl(char *ebuf, size_t ebuf_len)
#if !defined(NO_SSL_DL)
if (!cryptolib_dll_handle) {
- cryptolib_dll_handle = load_dll(ebuf, ebuf_len, CRYPTO_LIB, crypto_sw);
+ memset(tls_feature_missing, 0, sizeof(tls_feature_missing));
+ cryptolib_dll_handle = load_tls_dll(
+ ebuf, ebuf_len, CRYPTO_LIB, crypto_sw, tls_feature_missing);
if (!cryptolib_dll_handle) {
mg_snprintf(NULL,
NULL, /* No truncation check for ebuf */
@@ -15873,6 +16157,7 @@ initialize_ssl(char *ebuf, size_t ebuf_len)
return 1;
}
+#if !defined(OPENSSL_API_1_1)
/* Initialize locking callbacks, needed for thread safety.
* http://www.openssl.org/support/faq.html#PROG1
*/
@@ -15927,7 +16212,8 @@ initialize_ssl(char *ebuf, size_t ebuf_len)
#if !defined(NO_SSL_DL)
if (!ssllib_dll_handle) {
- ssllib_dll_handle = load_dll(ebuf, ebuf_len, SSL_LIB, ssl_sw);
+ ssllib_dll_handle =
+ load_tls_dll(ebuf, ebuf_len, SSL_LIB, ssl_sw, tls_feature_missing);
if (!ssllib_dll_handle) {
#if !defined(OPENSSL_API_1_1)
mg_free(ssl_mutexes);
@@ -16034,7 +16320,7 @@ ssl_get_protocol(int version_id)
static long
ssl_get_protocol(int version_id)
{
- long ret = (long)SSL_OP_ALL;
+ unsigned long ret = (unsigned long)SSL_OP_ALL;
if (version_id > 0)
ret |= SSL_OP_NO_SSLv2;
if (version_id > 1)
@@ -16049,7 +16335,7 @@ ssl_get_protocol(int version_id)
if (version_id > 5)
ret |= SSL_OP_NO_TLSv1_3;
#endif
- return ret;
+ return (long)ret;
}
#endif /* OPENSSL_API_1_1 */
@@ -16059,11 +16345,11 @@ ssl_get_protocol(int version_id)
* https://wiki.openssl.org/index.php/Manual:SSL_CTX_set_info_callback(3)
* https://linux.die.net/man/3/ssl_set_info_callback */
/* Note: There is no "const" for the first argument in the documentation
- * examples, however some (maybe most, but not all) headers of OpenSSL versions
- * / OpenSSL compatibility layers have it. Having a different definition will
- * cause a warning in C and an error in C++. Use "const SSL *", while
- * automatical conversion from "SSL *" works for all compilers, but not other
- * way around */
+ * examples, however some (maybe most, but not all) headers of OpenSSL
+ * versions / OpenSSL compatibility layers have it. Having a different
+ * definition will cause a warning in C and an error in C++. Use "const SSL
+ * *", while automatical conversion from "SSL *" works for all compilers,
+ * but not other way around */
static void
ssl_info_callback(const SSL *ssl, int what, int ret)
{
@@ -16083,10 +16369,6 @@ ssl_info_callback(const SSL *ssl, int what, int ret)
static int
ssl_servername_callback(SSL *ssl, int *ad, void *arg)
{
- struct mg_context *ctx = (struct mg_context *)arg;
- struct mg_domain_context *dom =
- (struct mg_domain_context *)ctx ? &(ctx->dd) : NULL;
-
#if defined(GCC_DIAGNOSTIC)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
@@ -16102,11 +16384,13 @@ ssl_servername_callback(SSL *ssl, int *ad, void *arg)
const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
(void)ad;
+ (void)arg;
- if ((ctx == NULL) || (conn->phys_ctx == ctx)) {
- DEBUG_TRACE("%s", "internal error - assertion failed");
+ if ((conn == NULL) || (conn->phys_ctx == NULL)) {
+ DEBUG_ASSERT(0);
return SSL_TLSEXT_ERR_NOACK;
}
+ conn->dom_ctx = &(conn->phys_ctx->dd);
/* Old clients (Win XP) will not support SNI. Then, there
* is no server name available in the request - we can
@@ -16116,35 +16400,144 @@ ssl_servername_callback(SSL *ssl, int *ad, void *arg)
*/
if ((servername == NULL) || (*servername == 0)) {
DEBUG_TRACE("%s", "SSL connection not supporting SNI");
- conn->dom_ctx = &(ctx->dd);
+ mg_lock_context(conn->phys_ctx);
SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx);
+ mg_unlock_context(conn->phys_ctx);
return SSL_TLSEXT_ERR_NOACK;
}
DEBUG_TRACE("TLS connection to host %s", servername);
- while (dom) {
- if (!mg_strcasecmp(servername, dom->config[AUTHENTICATION_DOMAIN])) {
-
+ while (conn->dom_ctx) {
+ if (!mg_strcasecmp(servername,
+ conn->dom_ctx->config[AUTHENTICATION_DOMAIN])) {
/* Found matching domain */
DEBUG_TRACE("TLS domain %s found",
- dom->config[AUTHENTICATION_DOMAIN]);
- SSL_set_SSL_CTX(ssl, dom->ssl_ctx);
- conn->dom_ctx = dom;
- return SSL_TLSEXT_ERR_OK;
+ conn->dom_ctx->config[AUTHENTICATION_DOMAIN]);
+ break;
}
- dom = dom->next;
+ mg_lock_context(conn->phys_ctx);
+ conn->dom_ctx = conn->dom_ctx->next;
+ mg_unlock_context(conn->phys_ctx);
}
- /* Default domain */
- DEBUG_TRACE("TLS default domain %s used",
- ctx->dd.config[AUTHENTICATION_DOMAIN]);
- conn->dom_ctx = &(ctx->dd);
+ if (conn->dom_ctx == NULL) {
+ /* Default domain */
+ DEBUG_TRACE("TLS default domain %s used",
+ conn->phys_ctx->dd.config[AUTHENTICATION_DOMAIN]);
+ conn->dom_ctx = &(conn->phys_ctx->dd);
+ }
+ mg_lock_context(conn->phys_ctx);
SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx);
+ mg_unlock_context(conn->phys_ctx);
return SSL_TLSEXT_ERR_OK;
}
+#if defined(USE_ALPN)
+static const char alpn_proto_list[] = "\x02h2\x08http/1.1\x08http/1.0";
+static const char *alpn_proto_order_http1[] = {alpn_proto_list + 3,
+ alpn_proto_list + 3 + 8,
+ NULL};
+#if defined(USE_HTTP2)
+static const char *alpn_proto_order_http2[] = {alpn_proto_list,
+ alpn_proto_list + 3,
+ alpn_proto_list + 3 + 8,
+ NULL};
+#endif
+
+static int
+alpn_select_cb(SSL *ssl,
+ const unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen,
+ void *arg)
+{
+ struct mg_domain_context *dom_ctx = (struct mg_domain_context *)arg;
+ unsigned int i, j, enable_http2 = 0;
+ const char **alpn_proto_order = alpn_proto_order_http1;
+
+ struct mg_workerTLS *tls =
+ (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
+
+ (void)ssl;
+
+ if (tls == NULL) {
+ /* Need to store protocol in Thread Local Storage */
+ /* If there is no Thread Local Storage, don't use ALPN */
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+#if defined(USE_HTTP2)
+ enable_http2 = (0 == strcmp(dom_ctx->config[ENABLE_HTTP2], "yes"));
+ if (enable_http2) {
+ alpn_proto_order = alpn_proto_order_http2;
+ }
+#endif
+
+ for (j = 0; alpn_proto_order[j] != NULL; j++) {
+ /* check all accepted protocols in this order */
+ const char *alpn_proto = alpn_proto_order[j];
+ /* search input for matching protocol */
+ for (i = 0; i < inlen; i++) {
+ if (!memcmp(in + i, alpn_proto, (unsigned char)alpn_proto[0])) {
+ *out = in + i + 1;
+ *outlen = in[i];
+ tls->alpn_proto = alpn_proto;
+ return SSL_TLSEXT_ERR_OK;
+ }
+ }
+ }
+
+ /* Nothing found */
+ return SSL_TLSEXT_ERR_NOACK;
+}
+
+
+static int
+next_protos_advertised_cb(SSL *ssl,
+ const unsigned char **data,
+ unsigned int *len,
+ void *arg)
+{
+ struct mg_domain_context *dom_ctx = (struct mg_domain_context *)arg;
+ *data = (const unsigned char *)alpn_proto_list;
+ *len = (unsigned int)strlen((const char *)data);
+
+ (void)ssl;
+ (void)dom_ctx;
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+
+static int
+init_alpn(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
+{
+ unsigned int alpn_len = (unsigned int)strlen((char *)alpn_proto_list);
+ int ret = SSL_CTX_set_alpn_protos(dom_ctx->ssl_ctx,
+ (const unsigned char *)alpn_proto_list,
+ alpn_len);
+ if (ret != 0) {
+ mg_cry_ctx_internal(phys_ctx,
+ "SSL_CTX_set_alpn_protos error: %s",
+ ssl_error());
+ }
+
+ SSL_CTX_set_alpn_select_cb(dom_ctx->ssl_ctx,
+ alpn_select_cb,
+ (void *)dom_ctx);
+
+ SSL_CTX_set_next_protos_advertised_cb(dom_ctx->ssl_ctx,
+ next_protos_advertised_cb,
+ (void *)dom_ctx);
+
+ return ret;
+}
+#endif
+
+
/* Setup SSL CTX as required by CivetWeb */
static int
init_ssl_ctx_impl(struct mg_context *phys_ctx,
@@ -16163,6 +16556,7 @@ init_ssl_ctx_impl(struct mg_context *phys_ctx,
md5_byte_t ssl_context_id[16];
md5_state_t md5state;
int protocol_ver;
+ int ssl_cache_timeout;
#if defined(OPENSSL_API_1_1)
if ((dom_ctx->ssl_ctx = SSL_CTX_new(TLS_server_method())) == NULL) {
@@ -16199,8 +16593,8 @@ init_ssl_ctx_impl(struct mg_context *phys_ctx,
SSL_CTX_set_ecdh_auto(dom_ctx->ssl_ctx, 1);
#endif /* NO_SSL_DL */
- /* In SSL documentation examples callback defined without const specifier
- * 'void (*)(SSL *, int, int)' See:
+ /* In SSL documentation examples callback defined without const
+ * specifier 'void (*)(SSL *, int, int)' See:
* https://www.openssl.org/docs/man1.0.2/ssl/ssl.html
* https://www.openssl.org/docs/man1.1.0/ssl/ssl.html
* But in the source code const SSL is used:
@@ -16216,7 +16610,6 @@ init_ssl_ctx_impl(struct mg_context *phys_ctx,
SSL_CTX_set_tlsext_servername_callback(dom_ctx->ssl_ctx,
ssl_servername_callback);
- SSL_CTX_set_tlsext_servername_arg(dom_ctx->ssl_ctx, phys_ctx);
/* If a callback has been specified, call it. */
callback_ret = (phys_ctx->callbacks.init_ssl == NULL)
@@ -16293,7 +16686,6 @@ init_ssl_ctx_impl(struct mg_context *phys_ctx,
if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER], "yes") == 0) {
/* Yes, they are mandatory */
should_verify_peer = 1;
- peer_certificate_optional = 0;
} else if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER],
"optional")
== 0) {
@@ -16356,6 +16748,27 @@ init_ssl_ctx_impl(struct mg_context *phys_ctx,
}
}
+ /* SSL session caching */
+ ssl_cache_timeout = ((dom_ctx->config[SSL_CACHE_TIMEOUT] != NULL)
+ ? atoi(dom_ctx->config[SSL_CACHE_TIMEOUT])
+ : 0);
+ if (ssl_cache_timeout > 0) {
+ SSL_CTX_set_session_cache_mode(dom_ctx->ssl_ctx, SSL_SESS_CACHE_BOTH);
+ /* SSL_CTX_sess_set_cache_size(dom_ctx->ssl_ctx, 10000); ... use
+ * default */
+ SSL_CTX_set_timeout(dom_ctx->ssl_ctx, (long)ssl_cache_timeout);
+ }
+
+#if defined(USE_ALPN)
+ /* Initialize ALPN only of TLS library (OpenSSL version) supports ALPN */
+#if !defined(NO_SSL_DL)
+ if (!tls_feature_missing[TLS_ALPN])
+#endif
+ {
+ init_alpn(phys_ctx, dom_ctx);
+ }
+#endif
+
return 1;
}
@@ -16393,11 +16806,14 @@ init_ssl_ctx(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
phys_ctx->user_data));
if (callback_ret < 0) {
+ /* Callback exists and returns <0: Initializing failed. */
mg_cry_ctx_internal(phys_ctx,
"external_ssl_ctx callback returned error: %i",
callback_ret);
return 0;
} else if (callback_ret > 0) {
+ /* Callback exists and returns >0: Initializing complete,
+ * civetweb should not modify the SSL context. */
dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx;
if (!initialize_ssl(ebuf, sizeof(ebuf))) {
mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
@@ -16405,8 +16821,10 @@ init_ssl_ctx(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
}
return 1;
}
+ /* If the callback does not exist or return 0, civetweb must initialize
+ * the SSL context. Handle "domain" callback next. */
- /* Check for external domain SSL_CTX */
+ /* Check for external domain SSL_CTX callback. */
callback_ret = (phys_ctx->callbacks.external_ssl_ctx_domain == NULL)
? 0
: (phys_ctx->callbacks.external_ssl_ctx_domain(
@@ -16415,12 +16833,14 @@ init_ssl_ctx(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
phys_ctx->user_data));
if (callback_ret < 0) {
+ /* Callback < 0: Error. Abort init. */
mg_cry_ctx_internal(
phys_ctx,
"external_ssl_ctx_domain callback returned error: %i",
callback_ret);
return 0;
} else if (callback_ret > 0) {
+ /* Callback > 0: Consider init done. */
dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx;
if (!initialize_ssl(ebuf, sizeof(ebuf))) {
mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
@@ -16428,8 +16848,8 @@ init_ssl_ctx(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
}
return 1;
}
- /* else: external_ssl_ctx/external_ssl_ctx_domain do not exist or return 0,
- * CivetWeb should continue initializing SSL */
+ /* else: external_ssl_ctx/external_ssl_ctx_domain do not exist or return
+ * 0, CivetWeb should continue initializing SSL */
/* If PEM file is not specified and the init_ssl callbacks
* are not specified, setup will fail. */
@@ -16445,11 +16865,14 @@ init_ssl_ctx(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
return 0;
}
+ /* If a certificate chain is configured, use it. */
chain = dom_ctx->config[SSL_CERTIFICATE_CHAIN];
if (chain == NULL) {
+ /* Default: certificate chain in PEM file */
chain = pem;
}
if ((chain != NULL) && (*chain == 0)) {
+ /* If the chain is an empty string, don't use it. */
chain = NULL;
}
@@ -16533,7 +16956,14 @@ set_gpass_option(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
static int
set_acl_option(struct mg_context *phys_ctx)
{
- return check_acl(phys_ctx, (uint32_t)0x7f000001UL) != -1;
+ union usa sa;
+ memset(&sa, 0, sizeof(sa));
+#if defined(USE_IPV6)
+ sa.sin6.sin6_family = AF_INET6;
+#else
+ sa.sin.sin_family = AF_INET;
+#endif
+ return check_acl(phys_ctx, &sa) != -1;
}
@@ -16543,8 +16973,6 @@ reset_per_request_attributes(struct mg_connection *conn)
if (!conn) {
return;
}
- conn->connection_type =
- CONNECTION_TYPE_INVALID; /* Not yet a valid request/response */
conn->num_bytes_sent = conn->consumed_content = 0;
@@ -16554,6 +16982,7 @@ reset_per_request_attributes(struct mg_connection *conn)
conn->is_chunked = 0;
conn->must_close = 0;
conn->request_len = 0;
+ conn->request_state = 0;
conn->throttle = 0;
conn->accept_gzip = 0;
@@ -16773,11 +17202,6 @@ close_connection(struct mg_connection *conn)
conn->client.sock = INVALID_SOCKET;
}
- if (conn->host) {
- mg_free((void *)conn->host);
- conn->host = NULL;
- }
-
mg_unlock_connection(conn);
#if defined(USE_SERVER_STATS)
@@ -16806,7 +17230,7 @@ mg_close_connection(struct mg_connection *conn)
unsigned int i;
/* client context: loops must end */
- conn->phys_ctx->stop_flag = 1;
+ STOP_FLAG_ASSIGN(&conn->phys_ctx->stop_flag, 1);
conn->must_close = 1;
/* We need to get the client thread out of the select/recv call
@@ -16837,10 +17261,12 @@ mg_close_connection(struct mg_connection *conn)
(void)pthread_mutex_destroy(&conn->mutex);
mg_free(conn);
} else if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) {
+ (void)pthread_mutex_destroy(&conn->mutex);
mg_free(conn);
}
#else
if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) { /* Client */
+ (void)pthread_mutex_destroy(&conn->mutex);
mg_free(conn);
}
#endif /* defined(USE_WEBSOCKET) */
@@ -17025,11 +17451,7 @@ mg_connect_client_impl(const struct mg_client_options *client_options,
SSL_CTX_set_verify(conn->dom_ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
}
- if (!sslize(conn,
- conn->dom_ctx->ssl_ctx,
- SSL_connect,
- &(conn->phys_ctx->stop_flag),
- client_options)) {
+ if (!sslize(conn, SSL_connect, client_options)) {
mg_snprintf(NULL,
NULL, /* No truncation check for ebuf */
ebuf,
@@ -17135,22 +17557,23 @@ mg_connect_client2(const char *host,
return NULL;
}
- /* TODO: The current implementation here just calls the old implementations,
- * without using any new options. This is just a first step to test the new
- * interfaces. */
+ /* TODO: The current implementation here just calls the old
+ * implementations, without using any new options. This is just a first
+ * step to test the new interfaces. */
#if defined(USE_WEBSOCKET)
if (is_ws) {
/* TODO: implement all options */
- return mg_connect_websocket_client(host,
- port,
- is_ssl,
- ((error != NULL) ? error->text : NULL),
- ((error != NULL) ? error->text_buffer_size : 0),
- (path ? path : ""),
- NULL /* TODO: origin */,
- deprecated_websocket_data_wrapper,
- deprecated_websocket_close_wrapper,
- (void *)init->callbacks);
+ return mg_connect_websocket_client(
+ host,
+ port,
+ is_ssl,
+ ((error != NULL) ? error->text : NULL),
+ ((error != NULL) ? error->text_buffer_size : 0),
+ (path ? path : ""),
+ NULL /* TODO: origin */,
+ experimental_websocket_client_data_wrapper,
+ experimental_websocket_client_close_wrapper,
+ (void *)init->callbacks);
}
#endif
@@ -17162,7 +17585,8 @@ mg_connect_client2(const char *host,
return mg_connect_client_impl(&opts,
is_ssl,
((error != NULL) ? error->text : NULL),
- ((error != NULL) ? error->text_buffer_size : 0));
+ ((error != NULL) ? error->text_buffer_size
+ : 0));
}
#endif
@@ -17212,26 +17636,7 @@ get_uri_type(const char *uri)
/* control characters and spaces are invalid */
return 0;
}
- if (uri[i] > 126) {
- /* non-ascii characters must be % encoded */
- return 0;
- } else {
- switch (uri[i]) {
- case '"': /* 34 */
- case '<': /* 60 */
- case '>': /* 62 */
- case '\\': /* 92 */
- case '^': /* 94 */
- case '`': /* 96 */
- case '{': /* 123 */
- case '|': /* 124 */
- case '}': /* 125 */
- return 0;
- default:
- /* character is ok */
- break;
- }
- }
+ /* Allow everything else here (See #894) */
}
/* A relative uri starts with a / character */
@@ -17324,21 +17729,11 @@ get_rel_url_at_current_server(const char *uri, const struct mg_connection *conn)
return 0;
}
-/* Check if the request is directed to a different server. */
-/* First check if the port is the same (IPv4 and IPv6). */
-#if defined(USE_IPV6)
- if (conn->client.lsa.sa.sa_family == AF_INET6) {
- if (ntohs(conn->client.lsa.sin6.sin6_port) != port) {
- /* Request is directed to a different port */
- return 0;
- }
- } else
-#endif
- {
- if (ntohs(conn->client.lsa.sin.sin_port) != port) {
- /* Request is directed to a different port */
- return 0;
- }
+ /* Check if the request is directed to a different server. */
+ /* First check if the port is the same. */
+ if (ntohs(USA_IN_PORT_UNSAFE(&conn->client.lsa)) != port) {
+ /* Request is directed to a different port */
+ return 0;
}
/* Finally check if the server corresponds to the authentication
@@ -17466,6 +17861,10 @@ static int
get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
{
const char *cl;
+
+ conn->connection_type =
+ CONNECTION_TYPE_REQUEST; /* request (valid of not) */
+
if (!get_message(conn, ebuf, ebuf_len, err)) {
return 0;
}
@@ -17484,12 +17883,7 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
/* Message is a valid request */
- /* Is there a "host" ? */
- if (conn->host != NULL) {
- mg_free((void *)conn->host);
- }
- conn->host = alloc_get_host(conn);
- if (!conn->host) {
+ if (!switch_domain_context(conn)) {
mg_snprintf(conn,
NULL, /* No truncation check for ebuf */
ebuf,
@@ -17541,7 +17935,6 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
conn->content_len = 0;
}
- conn->connection_type = CONNECTION_TYPE_REQUEST; /* Valid request */
return 1;
}
@@ -17551,6 +17944,10 @@ static int
get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
{
const char *cl;
+
+ conn->connection_type =
+ CONNECTION_TYPE_RESPONSE; /* response (valid or not) */
+
if (!get_message(conn, ebuf, ebuf_len, err)) {
return 0;
}
@@ -17624,7 +18021,6 @@ get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
}
}
- conn->connection_type = CONNECTION_TYPE_RESPONSE; /* Valid response */
return 1;
}
@@ -17792,7 +18188,7 @@ websocket_client_thread(void *data)
/* The websocket_client context has only this thread. If it runs out,
set the stop_flag to 2 (= "stopped"). */
- cdata->conn->phys_ctx->stop_flag = 2;
+ STOP_FLAG_ASSIGN(&cdata->conn->phys_ctx->stop_flag, 2);
if (cdata->conn->phys_ctx->callbacks.exit_thread) {
cdata->conn->phys_ctx->callbacks.exit_thread(cdata->conn->phys_ctx,
@@ -17811,24 +18207,26 @@ websocket_client_thread(void *data)
#endif
-struct mg_connection *
-mg_connect_websocket_client(const char *host,
- int port,
- int use_ssl,
- char *error_buffer,
- size_t error_buffer_size,
- const char *path,
- const char *origin,
- mg_websocket_data_handler data_func,
- mg_websocket_close_handler close_func,
- void *user_data)
+static struct mg_connection *
+mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
+ int use_ssl,
+ char *error_buffer,
+ size_t error_buffer_size,
+ const char *path,
+ const char *origin,
+ mg_websocket_data_handler data_func,
+ mg_websocket_close_handler close_func,
+ void *user_data)
{
struct mg_connection *conn = NULL;
#if defined(USE_WEBSOCKET)
struct websocket_client_thread_data *thread_data;
static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
- static const char *handshake_req;
+ const char *handshake_req;
+
+ const char *host = client_options->host;
+ int i;
if (origin != NULL) {
handshake_req = "GET %s HTTP/1.1\r\n"
@@ -17855,25 +18253,16 @@ mg_connect_websocket_client(const char *host,
#endif
/* Establish the client connection and request upgrade */
- conn = mg_download(host,
- port,
- use_ssl,
- error_buffer,
- error_buffer_size,
- handshake_req,
- path,
- host,
- magic,
- origin);
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
+ conn = mg_connect_client_impl(client_options,
+ use_ssl,
+ error_buffer,
+ error_buffer_size);
/* Connection object will be null if something goes wrong */
if (conn == NULL) {
- if (!*error_buffer) {
- /* There should be already an error message */
+ /* error_buffer should be already filled ... */
+ if (!error_buffer[0]) {
+ /* ... if not add an error message */
mg_snprintf(conn,
NULL, /* No truncation check for ebuf */
error_buffer,
@@ -17883,6 +18272,29 @@ mg_connect_websocket_client(const char *host,
return NULL;
}
+ i = mg_printf(conn, handshake_req, path, host, magic, origin);
+ if (i <= 0) {
+ mg_snprintf(conn,
+ NULL, /* No truncation check for ebuf */
+ error_buffer,
+ error_buffer_size,
+ "%s",
+ "Error sending request");
+ mg_close_connection(conn);
+ return NULL;
+ }
+
+ conn->data_len = 0;
+ if (!get_response(conn, error_buffer, error_buffer_size, &i)) {
+ mg_close_connection(conn);
+ return NULL;
+ }
+ conn->request_info.local_uri = conn->request_info.request_uri;
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
if (conn->response_info.status_code != 101) {
/* We sent an "upgrade" request. For a correct websocket
* protocol handshake, we expect a "101 Continue" response.
@@ -17946,8 +18358,7 @@ mg_connect_websocket_client(const char *host,
#else
/* Appease "unused parameter" warnings */
- (void)host;
- (void)port;
+ (void)client_options;
(void)use_ssl;
(void)error_buffer;
(void)error_buffer_size;
@@ -17962,6 +18373,61 @@ mg_connect_websocket_client(const char *host,
}
+struct mg_connection *
+mg_connect_websocket_client(const char *host,
+ int port,
+ int use_ssl,
+ char *error_buffer,
+ size_t error_buffer_size,
+ const char *path,
+ const char *origin,
+ mg_websocket_data_handler data_func,
+ mg_websocket_close_handler close_func,
+ void *user_data)
+{
+ struct mg_client_options client_options;
+ memset(&client_options, 0, sizeof(client_options));
+ client_options.host = host;
+ client_options.port = port;
+
+ return mg_connect_websocket_client_impl(&client_options,
+ use_ssl,
+ error_buffer,
+ error_buffer_size,
+ path,
+ origin,
+ data_func,
+ close_func,
+ user_data);
+}
+
+
+struct mg_connection *
+mg_connect_websocket_client_secure(
+ const struct mg_client_options *client_options,
+ char *error_buffer,
+ size_t error_buffer_size,
+ const char *path,
+ const char *origin,
+ mg_websocket_data_handler data_func,
+ mg_websocket_close_handler close_func,
+ void *user_data)
+{
+ if (!client_options) {
+ return NULL;
+ }
+ return mg_connect_websocket_client_impl(client_options,
+ 1,
+ error_buffer,
+ error_buffer_size,
+ path,
+ origin,
+ data_func,
+ close_func,
+ user_data);
+}
+
+
/* Prepare connection data structure */
static void
init_connection(struct mg_connection *conn)
@@ -17978,6 +18444,7 @@ init_connection(struct mg_connection *conn)
* goes to crule42. */
conn->data_len = 0;
conn->handled_requests = 0;
+ conn->connection_type = CONNECTION_TYPE_INVALID;
mg_set_user_connection_data(conn, NULL);
#if defined(USE_SERVER_STATS)
@@ -18010,17 +18477,11 @@ process_new_connection(struct mg_connection *conn)
int reqerr, uri_type;
#if defined(USE_SERVER_STATS)
- int mcon = mg_atomic_inc(&(conn->phys_ctx->active_connections));
+ ptrdiff_t mcon = mg_atomic_inc(&(conn->phys_ctx->active_connections));
mg_atomic_add(&(conn->phys_ctx->total_connections), 1);
- if (mcon > (conn->phys_ctx->max_active_connections)) {
- /* could use atomic compare exchange, but this
- * seems overkill for statistics data */
- conn->phys_ctx->max_active_connections = mcon;
- }
+ mg_atomic_max(&(conn->phys_ctx->max_active_connections), mcon);
#endif
- init_connection(conn);
-
DEBUG_TRACE("Start processing connection from %s",
conn->request_info.remote_addr);
@@ -18043,8 +18504,10 @@ process_new_connection(struct mg_connection *conn)
DEBUG_ASSERT(ebuf[0] != '\0');
mg_send_http_error(conn, reqerr, "%s", ebuf);
}
+
} else if (strcmp(ri->http_version, "1.0")
&& strcmp(ri->http_version, "1.1")) {
+ /* HTTP/2 is not allowed here */
mg_snprintf(conn,
NULL, /* No truncation check for ebuf */
ebuf,
@@ -18059,7 +18522,8 @@ process_new_connection(struct mg_connection *conn)
switch (uri_type) {
case 1:
/* Asterisk */
- conn->request_info.local_uri = NULL;
+ conn->request_info.local_uri = 0;
+ /* TODO: Deal with '*'. */
break;
case 2:
/* relative uri */
@@ -18093,6 +18557,24 @@ process_new_connection(struct mg_connection *conn)
#endif
}
+ if (ebuf[0] != '\0') {
+ conn->protocol_type = -1;
+
+ } else {
+ /* HTTP/1 allows protocol upgrade */
+ conn->protocol_type = should_switch_to_protocol(conn);
+
+ if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
+ /* This will occur, if a HTTP/1.1 request should be upgraded
+ * to HTTP/2 - but not if HTTP/2 is negotiated using ALPN.
+ * Since most (all?) major browsers only support HTTP/2 using
+ * ALPN, this is hard to test and very low priority.
+ * Deactivate it (at least for now).
+ */
+ conn->protocol_type = PROTOCOL_TYPE_HTTP1;
+ }
+ }
+
DEBUG_TRACE("http: %s, error: %s",
(ri->http_version ? ri->http_version : "none"),
(ebuf[0] ? ebuf : "none"));
@@ -18109,10 +18591,10 @@ process_new_connection(struct mg_connection *conn)
#if defined(USE_SERVER_STATS)
conn->conn_state = 5; /* processed */
- mg_atomic_add(&(conn->phys_ctx->total_data_read),
- conn->consumed_content);
- mg_atomic_add(&(conn->phys_ctx->total_data_written),
- conn->num_bytes_sent);
+ mg_atomic_add64(&(conn->phys_ctx->total_data_read),
+ conn->consumed_content);
+ mg_atomic_add64(&(conn->phys_ctx->total_data_written),
+ conn->num_bytes_sent);
#endif
DEBUG_TRACE("%s", "handle_request done");
@@ -18123,6 +18605,7 @@ process_new_connection(struct mg_connection *conn)
DEBUG_TRACE("%s", "end_request callback done");
}
log_access(conn);
+
} else {
/* TODO: handle non-local request (PROXY) */
conn->must_close = 1;
@@ -18131,6 +18614,9 @@ process_new_connection(struct mg_connection *conn)
conn->must_close = 1;
}
+ /* Response complete. Free header buffer */
+ free_buffered_response_header_list(conn);
+
if (ri->remote_user != NULL) {
mg_free((void *)ri->remote_user);
/* Important! When having connections with and without auth
@@ -18144,13 +18630,15 @@ process_new_connection(struct mg_connection *conn)
* Therefore, memorize should_keep_alive() result now for later
* use in loop exit condition. */
/* Enable it only if this request is completely discardable. */
- keep_alive = (conn->phys_ctx->stop_flag == 0) && should_keep_alive(conn)
- && (conn->content_len >= 0) && (conn->request_len > 0)
+ keep_alive = STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)
+ && should_keep_alive(conn) && (conn->content_len >= 0)
+ && (conn->request_len > 0)
&& ((conn->is_chunked == 4)
|| (!conn->is_chunked
&& ((conn->consumed_content == conn->content_len)
|| ((conn->request_len + conn->content_len)
- <= conn->data_len))));
+ <= conn->data_len))))
+ && (conn->protocol_type == PROTOCOL_TYPE_HTTP1);
if (keep_alive) {
/* Discard all buffered data for this request */
@@ -18269,7 +18757,8 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
DEBUG_TRACE("%s", "going idle");
/* If the queue is empty, wait. We're idle at this point. */
- while ((ctx->sq_head == ctx->sq_tail) && (ctx->stop_flag == 0)) {
+ while ((ctx->sq_head == ctx->sq_tail)
+ && (STOP_FLAG_IS_ZERO(&ctx->stop_flag))) {
pthread_cond_wait(&ctx->sq_full, &ctx->thread_mutex);
}
@@ -18291,7 +18780,7 @@ consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
(void)pthread_cond_signal(&ctx->sq_empty);
(void)pthread_mutex_unlock(&ctx->thread_mutex);
- return !ctx->stop_flag;
+ return STOP_FLAG_IS_ZERO(&ctx->stop_flag);
}
/* Master thread adds accepted socket to a queue */
@@ -18305,8 +18794,9 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
queue_filled = ctx->sq_head - ctx->sq_tail;
/* If the queue is full, wait */
- while ((ctx->stop_flag == 0) && (queue_filled >= ctx->sq_size)) {
- ctx->sq_blocked = 1; /* Status information: All threads bussy */
+ while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
+ && (queue_filled >= ctx->sq_size)) {
+ ctx->sq_blocked = 1; /* Status information: All threads busy */
#if defined(USE_SERVER_STATS)
if (queue_filled > ctx->sq_max_fill) {
ctx->sq_max_fill = queue_filled;
@@ -18344,10 +18834,6 @@ worker_thread_run(struct mg_connection *conn)
int thread_index;
struct mg_workerTLS tls;
-#if defined(MG_LEGACY_INTERFACE)
- uint32_t addr;
-#endif
-
mg_set_thread_name("worker");
tls.is_master = 0;
@@ -18361,8 +18847,8 @@ worker_thread_run(struct mg_connection *conn)
/* Check if there is a user callback */
if (ctx->callbacks.init_thread) {
- /* call init_thread for a worker thread (type 1), and store the return
- * value */
+ /* call init_thread for a worker thread (type 1), and store the
+ * return value */
tls.user_ptr = ctx->callbacks.init_thread(ctx, 1);
} else {
/* No callback: set user pointer to NULL */
@@ -18393,7 +18879,6 @@ worker_thread_run(struct mg_connection *conn)
conn->buf_size = (int)ctx->max_request_size;
conn->dom_ctx = &(ctx->dd); /* Use default domain and default host */
- conn->host = NULL; /* until we have more information. */
conn->tls_user_ptr = tls.user_ptr; /* store ptr for quick access */
@@ -18410,32 +18895,26 @@ worker_thread_run(struct mg_connection *conn)
#if defined(USE_SERVER_STATS)
conn->conn_state = 1; /* not consumed */
#endif
- conn->cookie_header = NULL; // <--- Pi-hole modification
/* Call consume_socket() even when ctx->stop_flag > 0, to let it
* signal sq_empty condvar to wake up the master waiting in
* produce_socket() */
while (consume_socket(ctx, &conn->client, thread_index)) {
+ /* New connections must start with new protocol negotiation */
+ tls.alpn_proto = NULL;
+
#if defined(USE_SERVER_STATS)
conn->conn_close_time = 0;
#endif
conn->conn_birth_time = time(NULL);
-/* Fill in IP, port info early so even if SSL setup below fails,
- * error handler would have the corresponding info.
- * Thanks to Johannes Winkelmann for the patch.
- */
-#if defined(USE_IPV6)
- if (conn->client.rsa.sa.sa_family == AF_INET6) {
- conn->request_info.remote_port =
- ntohs(conn->client.rsa.sin6.sin6_port);
- } else
-#endif
- {
- conn->request_info.remote_port =
- ntohs(conn->client.rsa.sin.sin_port);
- }
+ /* Fill in IP, port info early so even if SSL setup below fails,
+ * error handler would have the corresponding info.
+ * Thanks to Johannes Winkelmann for the patch.
+ */
+ conn->request_info.remote_port =
+ ntohs(USA_IN_PORT_UNSAFE(&conn->client.rsa));
sockaddr_to_string(conn->request_info.remote_addr,
sizeof(conn->request_info.remote_addr),
@@ -18449,18 +18928,37 @@ worker_thread_run(struct mg_connection *conn)
if (conn->client.is_ssl) {
#if !defined(NO_SSL)
/* HTTPS connection */
- if (sslize(conn,
- conn->dom_ctx->ssl_ctx,
- SSL_accept,
- &(conn->phys_ctx->stop_flag),
- NULL)) {
+ if (sslize(conn, SSL_accept, NULL)) {
/* conn->dom_ctx is set in get_request */
/* Get SSL client certificate information (if set) */
- ssl_get_client_cert_info(conn);
+ struct mg_client_cert client_cert;
+ if (ssl_get_client_cert_info(conn, &client_cert)) {
+ conn->request_info.client_cert = &client_cert;
+ }
/* process HTTPS connection */
- process_new_connection(conn);
+#if defined(USE_HTTP2)
+ if ((tls.alpn_proto != NULL)
+ && (!memcmp(tls.alpn_proto, "\x02h2", 3))) {
+ /* process HTTPS/2 connection */
+ init_connection(conn);
+ conn->connection_type = CONNECTION_TYPE_REQUEST;
+ conn->protocol_type = PROTOCOL_TYPE_HTTP2;
+ conn->content_len =
+ -1; /* content length is not predefined */
+ conn->is_chunked = 0; /* HTTP2 is never chunked */
+ process_new_http2_connection(conn);
+ } else
+#endif
+ {
+ /* process HTTPS/1.x or WEBSOCKET-SECURE connection */
+ init_connection(conn);
+ conn->connection_type = CONNECTION_TYPE_REQUEST;
+ /* Start with HTTP, WS will be an "upgrade" request later */
+ conn->protocol_type = PROTOCOL_TYPE_HTTP1;
+ process_new_connection(conn);
+ }
/* Free client certificate info */
if (conn->request_info.client_cert) {
@@ -18476,7 +18974,6 @@ worker_thread_run(struct mg_connection *conn)
conn->request_info.client_cert->issuer = 0;
conn->request_info.client_cert->serial = 0;
conn->request_info.client_cert->finger = 0;
- mg_free(conn->request_info.client_cert);
conn->request_info.client_cert = 0;
}
} else {
@@ -18486,6 +18983,10 @@ worker_thread_run(struct mg_connection *conn)
#endif
} else {
/* process HTTP connection */
+ init_connection(conn);
+ conn->connection_type = CONNECTION_TYPE_REQUEST;
+ /* Start with HTTP, WS will be an "upgrade" request later */
+ conn->protocol_type = PROTOCOL_TYPE_HTTP1;
process_new_connection(conn);
}
@@ -18496,7 +18997,6 @@ worker_thread_run(struct mg_connection *conn)
#endif
}
-
/* Call exit thread user callback */
if (ctx->callbacks.exit_thread) {
ctx->callbacks.exit_thread(ctx, 1, tls.user_ptr);
@@ -18559,10 +19059,11 @@ accept_new_connection(const struct socket *listener, struct mg_context *ctx)
#if !defined(__ZEPHYR__)
int on = 1;
#endif
+ memset(&so, 0, sizeof(so));
if ((so.sock = accept(listener->sock, &so.rsa.sa, &len))
== INVALID_SOCKET) {
- } else if (!check_acl(ctx, ntohl(*(uint32_t *)&so.rsa.sin.sin_addr))) {
+ } else if (check_acl(ctx, &so.rsa) != 1) {
sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
mg_cry_ctx_internal(ctx,
"%s: %s is not allowed to connect",
@@ -18681,7 +19182,7 @@ master_thread_run(struct mg_context *ctx)
/* Start the server */
pfd = ctx->listening_socket_fds;
- while (ctx->stop_flag == 0) {
+ while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
for (i = 0; i < ctx->num_listening_sockets; i++) {
pfd[i].fd = ctx->listening_sockets[i].sock;
pfd[i].events = POLLIN;
@@ -18694,7 +19195,8 @@ master_thread_run(struct mg_context *ctx)
* (POLLRDNORM | POLLRDBAND)
* Therefore, we're checking pfd[i].revents & POLLIN, not
* pfd[i].revents == POLLIN. */
- if ((ctx->stop_flag == 0) && (pfd[i].revents & POLLIN)) {
+ if (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
+ && (pfd[i].revents & POLLIN)) {
accept_new_connection(&ctx->listening_sockets[i], ctx);
}
}
@@ -18757,7 +19259,7 @@ master_thread_run(struct mg_context *ctx)
/* Signal mg_stop() that we're done.
* WARNING: This must be the very last thing this
* thread does, as ctx becomes invalid after this line. */
- ctx->stop_flag = 2;
+ STOP_FLAG_ASSIGN(&ctx->stop_flag, 2);
}
@@ -18797,6 +19299,7 @@ free_context(struct mg_context *ctx)
return;
}
+ /* Call user callback */
if (ctx->callbacks.exit_context) {
ctx->callbacks.exit_context(ctx);
}
@@ -18823,10 +19326,6 @@ free_context(struct mg_context *ctx)
/* Destroy other context global data structures mutex */
(void)pthread_mutex_destroy(&ctx->nonce_mutex);
-#if defined(USE_TIMERS)
- timers_exit(ctx);
-#endif
-
/* Deallocate config parameters */
for (i = 0; i < NUM_OPTIONS; i++) {
if (ctx->dd.config[i] != NULL) {
@@ -18841,10 +19340,6 @@ free_context(struct mg_context *ctx)
while (ctx->dd.handlers) {
tmp_rh = ctx->dd.handlers;
ctx->dd.handlers = tmp_rh->next;
- if (tmp_rh->handler_type == REQUEST_HANDLER) {
- pthread_cond_destroy(&tmp_rh->refcount_cond);
- pthread_mutex_destroy(&tmp_rh->refcount_mutex);
- }
mg_free(tmp_rh->uri);
mg_free(tmp_rh);
}
@@ -18898,19 +19393,28 @@ mg_stop(struct mg_context *ctx)
ctx->masterthreadid = 0;
/* Set stop flag, so all threads know they have to exit. */
- ctx->stop_flag = 1;
+ STOP_FLAG_ASSIGN(&ctx->stop_flag, 1);
+
+ /* Join timer thread */
+#if defined(USE_TIMERS)
+ timers_exit(ctx);
+#endif
/* Wait until everything has stopped. */
- while (ctx->stop_flag != 2) {
+ while (!STOP_FLAG_IS_TWO(&ctx->stop_flag)) {
(void)mg_sleep(10);
}
+ /* Wait to stop master thread */
mg_join_thread(mt);
- free_context(ctx);
-#if defined(_WIN32)
- (void)WSACleanup();
-#endif /* _WIN32 */
+ /* Close remaining Lua states */
+#if defined(USE_LUA)
+ lua_ctx_exit(ctx);
+#endif
+
+ /* Free memory */
+ free_context(ctx);
}
@@ -19010,11 +19514,6 @@ static
struct mg_workerTLS tls;
-#if defined(_WIN32)
- WSADATA data;
- WSAStartup(MAKEWORD(2, 2), &data);
-#endif /* _WIN32 */
-
if (error != NULL) {
error->code = 0;
if (error->text_buffer_size > 0) {
@@ -19024,9 +19523,21 @@ static
if (mg_init_library_called == 0) {
/* Legacy INIT, if mg_start is called without mg_init_library.
- * Note: This will cause a memory leak when unloading the library. */
+ * Note: This will cause a memory leak when unloading the library.
+ */
legacy_init(options);
}
+ if (mg_init_library_called == 0) {
+ if ((error != NULL) && (error->text_buffer_size > 0)) {
+ mg_snprintf(NULL,
+ NULL, /* No truncation check for error buffers */
+ error->text,
+ error->text_buffer_size,
+ "%s",
+ "Library uninitialized");
+ }
+ return NULL;
+ }
/* Allocate context and initialize reasonable general case defaults. */
if ((ctx = (struct mg_context *)mg_calloc(1, sizeof(*ctx))) == NULL) {
@@ -19089,16 +19600,16 @@ static
ctx->callbacks = *init->callbacks;
exit_callback = init->callbacks->exit_context;
/* The exit callback is activated once the context is successfully
- * created. It should not be called, if an incomplete context object is
- * deleted during a failed initialization. */
+ * created. It should not be called, if an incomplete context object
+ * is deleted during a failed initialization. */
ctx->callbacks.exit_context = 0;
}
ctx->user_data = ((init != NULL) ? (init->user_data) : (NULL));
ctx->dd.handlers = NULL;
ctx->dd.next = NULL;
-#if defined(USE_LUA) && defined(USE_WEBSOCKET)
- ctx->dd.shared_lua_websockets = NULL;
+#if defined(USE_LUA)
+ lua_ctx_init(ctx);
#endif
/* Store options */
@@ -19187,7 +19698,8 @@ static
pthread_setspecific(sTlsKey, NULL);
return NULL;
}
- ctx->squeue = (struct socket *)mg_calloc(itmp, sizeof(struct socket));
+ ctx->squeue =
+ (struct socket *)mg_calloc((unsigned int)itmp, sizeof(struct socket));
if (ctx->squeue == NULL) {
mg_cry_ctx_internal(ctx,
"Out of memory: Cannot allocate %s",
@@ -19537,9 +20049,6 @@ static
ctx->callbacks.exit_context = exit_callback;
ctx->context_type = CONTEXT_SERVER; /* server context */
- /* Start master (listening) thread */
- mg_start_thread_with_id(master_thread, ctx, &ctx->masterthreadid);
-
/* Start worker threads */
for (i = 0; i < ctx->cfg_worker_threads; i++) {
/* worker_thread sets up the other fields */
@@ -19560,9 +20069,9 @@ static
i + 1,
error_no);
- /* If the server initialization should stop here, all threads
- * that have already been created must be stopped first, before
- * any free_context(ctx) call.
+ /* If the server initialization should stop here, all
+ * threads that have already been created must be stopped
+ * first, before any free_context(ctx) call.
*/
} else {
@@ -19589,6 +20098,9 @@ static
}
}
+ /* Start master (listening) thread */
+ mg_start_thread_with_id(master_thread, ctx, &ctx->masterthreadid);
+
pthread_setspecific(sTlsKey, NULL);
return ctx;
}
@@ -19641,7 +20153,7 @@ mg_start_domain2(struct mg_context *ctx,
return -1;
}
- if (ctx->stop_flag != 0) {
+ if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
if ((error != NULL) && (error->text_buffer_size > 0)) {
mg_snprintf(NULL,
NULL, /* No truncation check for error buffers */
@@ -19740,6 +20252,7 @@ mg_start_domain2(struct mg_context *ctx,
new_dom->shared_lua_websockets = NULL;
#endif
+#if !defined(NO_SSL)
if (!init_ssl_ctx(ctx, new_dom)) {
/* Init SSL failed */
if ((error != NULL) && (error->text_buffer_size > 0)) {
@@ -19753,6 +20266,7 @@ mg_start_domain2(struct mg_context *ctx,
mg_free(new_dom);
return -3;
}
+#endif
/* Add element to linked list. */
mg_lock_context(ctx);
@@ -19911,7 +20425,9 @@ mg_get_system_info(char *buffer, int buflen)
if (buflen > (int)(sizeof(eoobj) - 1)) {
/* has enough space to append eoobj */
append_eoobj = buffer;
- end -= sizeof(eoobj) - 1;
+ if (end) {
+ end -= sizeof(eoobj) - 1;
+ }
}
system_info_length += mg_str_append(&buffer, end, "{");
@@ -20058,13 +20574,14 @@ mg_get_system_info(char *buffer, int buflen)
#pragma GCC diagnostic ignored "-Wdate-time"
#endif
#endif
- mg_snprintf(NULL,
- NULL,
- block,
- sizeof(block),
- ",%s\"build\" : \"%s\"",
- eol,
- __DATE__);
+#ifdef BUILD_DATE
+ const char *bd = BUILD_DATE;
+#else
+ const char *bd = __DATE__;
+#endif
+
+ mg_snprintf(
+ NULL, NULL, block, sizeof(block), ",%s\"build\" : \"%s\"", eol, bd);
#if defined(GCC_DIAGNOSTIC)
#if GCC_VERSION >= 40900
@@ -20248,6 +20765,13 @@ mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen)
if (ms) { /* <-- should be always true */
/* Memory information */
+ int blockCount = (int)ms->blockCount;
+ int64_t totalMemUsed = ms->totalMemUsed;
+ int64_t maxMemUsed = ms->maxMemUsed;
+ if (totalMemUsed > maxMemUsed) {
+ maxMemUsed = totalMemUsed;
+ }
+
mg_snprintf(NULL,
NULL,
block,
@@ -20259,11 +20783,11 @@ mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen)
"}",
eol,
eol,
- ms->blockCount,
+ blockCount,
eol,
- ms->totalMemUsed,
+ totalMemUsed,
eol,
- ms->maxMemUsed,
+ maxMemUsed,
eol);
context_info_length += mg_str_append(&buffer, end, block);
}
@@ -20275,6 +20799,16 @@ mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen)
char now_str[64] = {0};
time_t start_time = ctx->start_time;
time_t now = time(NULL);
+ int64_t total_data_read, total_data_written;
+ int active_connections = (int)ctx->active_connections;
+ int max_active_connections = (int)ctx->max_active_connections;
+ int total_connections = (int)ctx->total_connections;
+ if (active_connections > max_active_connections) {
+ max_active_connections = active_connections;
+ }
+ if (active_connections > total_connections) {
+ total_connections = active_connections;
+ }
/* Connections information */
mg_snprintf(NULL,
@@ -20284,15 +20818,15 @@ mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen)
",%s\"connections\" : {%s"
"\"active\" : %i,%s"
"\"maxActive\" : %i,%s"
- "\"total\" : %" INT64_FMT "%s"
+ "\"total\" : %i%s"
"}",
eol,
eol,
- ctx->active_connections,
+ active_connections,
eol,
- ctx->max_active_connections,
+ max_active_connections,
eol,
- ctx->total_connections,
+ total_connections,
eol);
context_info_length += mg_str_append(&buffer, end, block);
@@ -20327,15 +20861,19 @@ mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen)
block,
sizeof(block),
",%s\"requests\" : {%s"
- "\"total\" : %" INT64_FMT "%s"
+ "\"total\" : %lu%s"
"}",
eol,
eol,
- ctx->total_requests,
+ (unsigned long)ctx->total_requests,
eol);
context_info_length += mg_str_append(&buffer, end, block);
/* Data information */
+ total_data_read =
+ mg_atomic_add64((volatile int64_t *)&ctx->total_data_read, 0);
+ total_data_written =
+ mg_atomic_add64((volatile int64_t *)&ctx->total_data_written, 0);
mg_snprintf(NULL,
NULL,
block,
@@ -20346,9 +20884,9 @@ mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen)
"}",
eol,
eol,
- ctx->total_data_read,
+ total_data_read,
eol,
- ctx->total_data_written,
+ total_data_written,
eol);
context_info_length += mg_str_append(&buffer, end, block);
@@ -20547,12 +21085,15 @@ mg_get_connection_info(const struct mg_context *ctx,
char start_time_str[64] = {0};
char close_time_str[64] = {0};
time_t start_time = conn->conn_birth_time;
- time_t close_time = conn->conn_close_time;
+ time_t close_time = 0;
double time_diff;
gmt_time_string(start_time_str,
sizeof(start_time_str) - 1,
&start_time);
+#if defined(USE_SERVER_STATS)
+ close_time = conn->conn_close_time;
+#endif
if (close_time != 0) {
time_diff = difftime(close_time, start_time);
gmt_time_string(close_time_str,
@@ -20649,10 +21190,6 @@ mg_get_connection_info(const struct mg_context *ctx,
unsigned
mg_init_library(unsigned features)
{
-#if !defined(NO_SSL)
- char ebuf[128];
-#endif
-
unsigned features_to_init = mg_check_feature(features & 0xFFu);
unsigned features_inited = features_to_init;
@@ -20666,19 +21203,54 @@ mg_init_library(unsigned features)
mg_global_lock();
if (mg_init_library_called <= 0) {
- if (0 != pthread_key_create(&sTlsKey, tls_dtor)) {
- /* Fatal error - abort start. However, this situation should
- * never occur in practice. */
- mg_global_unlock();
- return 0;
+#if defined(_WIN32)
+ int file_mutex_init = 1;
+ int wsa = 1;
+#else
+ int mutexattr_init = 1;
+#endif
+ int failed = 1;
+ int key_create = pthread_key_create(&sTlsKey, tls_dtor);
+
+ if (key_create == 0) {
+#if defined(_WIN32)
+ file_mutex_init =
+ pthread_mutex_init(&global_log_file_lock, &pthread_mutex_attr);
+ if (file_mutex_init == 0) {
+ /* Start WinSock */
+ WSADATA data;
+ failed = wsa = WSAStartup(MAKEWORD(2, 2), &data);
+ }
+#else
+ mutexattr_init = pthread_mutexattr_init(&pthread_mutex_attr);
+ if (mutexattr_init == 0) {
+ failed = pthread_mutexattr_settype(&pthread_mutex_attr,
+ PTHREAD_MUTEX_RECURSIVE);
+ }
+#endif
}
+
+ if (failed) {
#if defined(_WIN32)
- (void)pthread_mutex_init(&global_log_file_lock, &pthread_mutex_attr);
+ if (wsa == 0) {
+ (void)WSACleanup();
+ }
+ if (file_mutex_init == 0) {
+ (void)pthread_mutex_destroy(&global_log_file_lock);
+ }
#else
- pthread_mutexattr_init(&pthread_mutex_attr);
- pthread_mutexattr_settype(&pthread_mutex_attr, PTHREAD_MUTEX_RECURSIVE);
+ if (mutexattr_init == 0) {
+ (void)pthread_mutexattr_destroy(&pthread_mutex_attr);
+ }
#endif
+ if (key_create == 0) {
+ (void)pthread_key_delete(sTlsKey);
+ }
+ mg_global_unlock();
+ (void)pthread_mutex_destroy(&global_lock_mutex);
+ return 0;
+ }
#if defined(USE_LUA)
lua_init_optional_libraries();
@@ -20690,6 +21262,7 @@ mg_init_library(unsigned features)
#if !defined(NO_SSL)
if (features_to_init & MG_FEATURES_SSL) {
if (!mg_ssl_initialized) {
+ char ebuf[128];
if (initialize_ssl(ebuf, sizeof(ebuf))) {
mg_ssl_initialized = 1;
} else {
@@ -20703,13 +21276,8 @@ mg_init_library(unsigned features)
}
#endif
- /* Start WinSock for Windows */
mg_global_lock();
if (mg_init_library_called <= 0) {
-#if defined(_WIN32)
- WSADATA data;
- WSAStartup(MAKEWORD(2, 2), &data);
-#endif /* _WIN32 */
mg_init_library_called = 1;
} else {
mg_init_library_called++;
@@ -20732,9 +21300,6 @@ mg_exit_library(void)
mg_init_library_called--;
if (mg_init_library_called == 0) {
-#if defined(_WIN32)
- (void)WSACleanup();
-#endif /* _WIN32 */
#if !defined(NO_SSL)
if (mg_ssl_initialized) {
uninitialize_ssl();
@@ -20743,6 +21308,7 @@ mg_exit_library(void)
#endif
#if defined(_WIN32)
+ (void)WSACleanup();
(void)pthread_mutex_destroy(&global_log_file_lock);
#else
(void)pthread_mutexattr_destroy(&pthread_mutex_attr);
diff --git a/src/civetweb/civetweb.h b/src/civetweb/civetweb.h
index 521ef8ae..662f8ab2 100644
--- a/src/civetweb/civetweb.h
+++ b/src/civetweb/civetweb.h
@@ -942,9 +942,11 @@ CIVETWEB_API int mg_send_http_error(struct mg_connection *conn,
/************************************** Pi-hole method **************************************/
void my_send_http_error_headers(struct mg_connection *conn,
int status, const char* mime_type,
-// const char *additional_headers,
long long content_length);
-void my_set_cookie_header(struct mg_connection *conn, const char *cookie_header);
+
+// Buffer used for additional "Set-Cookie" headers
+#define PIHOLE_HEADERS_MAXLEN 1024
+extern char pi_hole_extra_headers[PIHOLE_HEADERS_MAXLEN];
/********************************************************************************************/
diff --git a/src/webserver/http-common.c b/src/webserver/http-common.c
index d98a73c3..e5782247 100644
--- a/src/webserver/http-common.c
+++ b/src/webserver/http-common.c
@@ -14,6 +14,8 @@
#include "../log.h"
#include "json_macros.h"
+char pi_hole_extra_headers[PIHOLE_HEADERS_MAXLEN] = { 0 };
+
// Provides a compile-time flag for JSON formatting
// This should never be needed as all modern browsers
// tyoically contain a JSON explorer