Github User Fetcher 1.0.0
C Application with Server and GUI
Loading...
Searching...
No Matches
civetweb.c
Go to the documentation of this file.
1/* Copyright (c) 2013-2021 the Civetweb developers
2 * Copyright (c) 2004-2013 Sergey Lyubka
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#if defined(__GNUC__) || defined(__MINGW32__)
24#ifndef GCC_VERSION
25#define GCC_VERSION \
26 (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
27#endif
28#if GCC_VERSION >= 40500
29/* gcc diagnostic pragmas available */
30#define GCC_DIAGNOSTIC
31#endif
32#endif
33
34#if defined(GCC_DIAGNOSTIC)
35/* Disable unused macros warnings - not all defines are required
36 * for all systems and all compilers. */
37#pragma GCC diagnostic ignored "-Wunused-macros"
38/* A padding warning is just plain useless */
39#pragma GCC diagnostic ignored "-Wpadded"
40#endif
41
42#if defined(__clang__) /* GCC does not (yet) support this pragma */
43/* We must set some flags for the headers we include. These flags
44 * are reserved ids according to C99, so we need to disable a
45 * warning for that. */
46#pragma GCC diagnostic push
47#pragma GCC diagnostic ignored "-Wreserved-id-macro"
48#endif
49
50#if defined(_WIN32)
51#if !defined(_CRT_SECURE_NO_WARNINGS)
52#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
53#endif
54#if !defined(_WIN32_WINNT) /* defined for tdm-gcc so we can use getnameinfo */
55#define _WIN32_WINNT 0x0502
56#endif
57#else
58#if !defined(_GNU_SOURCE)
59#define _GNU_SOURCE /* for setgroups(), pthread_setname_np() */
60#endif
61#if defined(__linux__) && !defined(_XOPEN_SOURCE)
62#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */
63#endif
64#if defined(__LSB_VERSION__) || defined(__sun)
65#define NEED_TIMEGM
66#define NO_THREAD_NAME
67#endif
68#if !defined(_LARGEFILE_SOURCE)
69#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */
70#endif
71#if !defined(_FILE_OFFSET_BITS)
72#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */
73#endif
74#if !defined(__STDC_FORMAT_MACROS)
75#define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */
76#endif
77#if !defined(__STDC_LIMIT_MACROS)
78#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */
79#endif
80#if !defined(_DARWIN_UNLIMITED_SELECT)
81#define _DARWIN_UNLIMITED_SELECT
82#endif
83#if defined(__sun)
84#define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */
85#define __inline inline /* not recognized on older compiler versions */
86#endif
87#endif
88
89#if defined(__clang__)
90/* Enable reserved-id-macro warning again. */
91#pragma GCC diagnostic pop
92#endif
93
94
95#if defined(USE_LUA)
96#define USE_TIMERS
97#endif
98
99#if defined(_MSC_VER)
100/* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */
101#pragma warning(disable : 4306)
102/* conditional expression is constant: introduced by FD_SET(..) */
103#pragma warning(disable : 4127)
104/* non-constant aggregate initializer: issued due to missing C99 support */
105#pragma warning(disable : 4204)
106/* padding added after data member */
107#pragma warning(disable : 4820)
108/* not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */
109#pragma warning(disable : 4668)
110/* no function prototype given: converting '()' to '(void)' */
111#pragma warning(disable : 4255)
112/* function has been selected for automatic inline expansion */
113#pragma warning(disable : 4711)
114#endif
115
116
117/* This code uses static_assert to check some conditions.
118 * Unfortunately some compilers still do not support it, so we have a
119 * replacement function here. */
120#if defined(__STDC_VERSION__) && __STDC_VERSION__ > 201100L
121#define mg_static_assert _Static_assert
122#elif defined(__cplusplus) && __cplusplus >= 201103L
123#define mg_static_assert static_assert
124#else
126#define mg_static_assert(cond, txt) \
127 extern char static_assert_replacement[(cond) ? 1 : -1]
128#endif
129
130mg_static_assert(sizeof(int) == 4 || sizeof(int) == 8,
131 "int data type size check");
132mg_static_assert(sizeof(void *) == 4 || sizeof(void *) == 8,
133 "pointer data type size check");
134mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
135
136
137/* Select queue implementation. Diagnosis features originally only implemented
138 * for the "ALTERNATIVE_QUEUE" have been ported to the previous queue
139 * implementation (NO_ALTERNATIVE_QUEUE) as well. The new configuration value
140 * "CONNECTION_QUEUE_SIZE" is only available for the previous queue
141 * implementation, since the queue length is independent from the number of
142 * worker threads there, while the new queue is one element per worker thread.
143 *
144 */
145#if defined(NO_ALTERNATIVE_QUEUE) && defined(ALTERNATIVE_QUEUE)
146/* The queues are exclusive or - only one can be used. */
147#error \
148 "Define ALTERNATIVE_QUEUE or NO_ALTERNATIVE_QUEUE (or none of them), but not both"
149#endif
150#if !defined(NO_ALTERNATIVE_QUEUE) && !defined(ALTERNATIVE_QUEUE)
151/* Use a default implementation */
152#define NO_ALTERNATIVE_QUEUE
153#endif
154
155#if defined(NO_FILESYSTEMS) && !defined(NO_FILES)
156/* File system access:
157 * NO_FILES = do not serve any files from the file system automatically.
158 * However, with NO_FILES CivetWeb may still write log files, read access
159 * control files, default error page files or use API functions like
160 * mg_send_file in callbacks to send files from the server local
161 * file system.
162 * NO_FILES only disables the automatic mapping between URLs and local
163 * file names.
164 * NO_FILESYSTEM = do not access any file at all. Useful for embedded
165 * devices without file system. Logging to files in not available
166 * (use callbacks instead) and API functions like mg_send_file are not
167 * available.
168 * If NO_FILESYSTEM is set, NO_FILES must be set as well.
169 */
170#error "Inconsistent build flags, NO_FILESYSTEMS requires NO_FILES"
171#endif
172
173/* DTL -- including winsock2.h works better if lean and mean */
174#if !defined(WIN32_LEAN_AND_MEAN)
175#define WIN32_LEAN_AND_MEAN
176#endif
177
178#if defined(__SYMBIAN32__)
179/* According to https://en.wikipedia.org/wiki/Symbian#History,
180 * Symbian is no longer maintained since 2014-01-01.
181 * Support for Symbian has been removed from CivetWeb
182 */
183#error "Symbian is no longer maintained. CivetWeb no longer supports Symbian."
184#endif /* __SYMBIAN32__ */
185
186#if defined(__ZEPHYR__)
187#include <ctype.h>
188#include <fcntl.h>
189#include <netdb.h>
190#include <poll.h>
191#include <pthread.h>
192#include <stdio.h>
193#include <stdlib.h>
194#include <string.h>
195#include <sys/socket.h>
196#include <time.h>
197
198#include <zephyr/kernel.h>
199
200/* Max worker threads is the max of pthreads minus the main application thread
201 * and minus the main civetweb thread, thus -2
202 */
203#define MAX_WORKER_THREADS (CONFIG_MAX_PTHREAD_COUNT - 2)
204
205#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
206#define ZEPHYR_STACK_SIZE USE_STACK_SIZE
207#else
208#define ZEPHYR_STACK_SIZE (1024 * 16)
209#endif
210
211K_THREAD_STACK_DEFINE(civetweb_main_stack, ZEPHYR_STACK_SIZE);
212K_THREAD_STACK_ARRAY_DEFINE(civetweb_worker_stacks,
214 ZEPHYR_STACK_SIZE);
215
216static int zephyr_worker_stack_index;
217
218#endif
219
220#if !defined(CIVETWEB_HEADER_INCLUDED)
221/* Include the header file here, so the CivetWeb interface is defined for the
222 * entire implementation, including the following forward definitions. */
223#include "civetweb.h"
224#endif
225
226#if !defined(DEBUG_TRACE)
227#if defined(DEBUG)
228static void DEBUG_TRACE_FUNC(const char *func,
229 unsigned line,
230 PRINTF_FORMAT_STRING(const char *fmt),
231 ...) PRINTF_ARGS(3, 4);
232
233#define DEBUG_TRACE(fmt, ...) \
234 DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__)
235
236#define NEED_DEBUG_TRACE_FUNC
237#if !defined(DEBUG_TRACE_STREAM)
238#define DEBUG_TRACE_STREAM stdout
239#endif
240
241#else
242#define DEBUG_TRACE(fmt, ...) \
243 do { \
244 } while (0)
245#endif /* DEBUG */
246#endif /* DEBUG_TRACE */
247
248
249#if !defined(DEBUG_ASSERT)
250#if defined(DEBUG)
251#include <stdlib.h>
252#define DEBUG_ASSERT(cond) \
253 do { \
254 if (!(cond)) { \
255 DEBUG_TRACE("ASSERTION FAILED: %s", #cond); \
256 exit(2); /* Exit with error */ \
257 } \
258 } while (0)
259#else
260#define DEBUG_ASSERT(cond)
261#endif /* DEBUG */
262#endif
263
264
265#if defined(__GNUC__) && defined(GCC_INSTRUMENTATION)
266void __cyg_profile_func_enter(void *this_fn, void *call_site)
267 __attribute__((no_instrument_function));
268
269void __cyg_profile_func_exit(void *this_fn, void *call_site)
270 __attribute__((no_instrument_function));
271
272void
273__cyg_profile_func_enter(void *this_fn, void *call_site)
274{
275 if ((void *)this_fn != (void *)printf) {
276 printf("E %p %p\n", this_fn, call_site);
277 }
278}
279
280void
281__cyg_profile_func_exit(void *this_fn, void *call_site)
282{
283 if ((void *)this_fn != (void *)printf) {
284 printf("X %p %p\n", this_fn, call_site);
285 }
286}
287#endif
288
289
290#if !defined(IGNORE_UNUSED_RESULT)
291#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1))
292#endif
293
294
295#if defined(__GNUC__) || defined(__MINGW32__)
296
297/* GCC unused function attribute seems fundamentally broken.
298 * Several attempts to tell the compiler "THIS FUNCTION MAY BE USED
299 * OR UNUSED" for individual functions failed.
300 * Either the compiler creates an "unused-function" warning if a
301 * function is not marked with __attribute__((unused)).
302 * On the other hand, if the function is marked with this attribute,
303 * but is used, the compiler raises a completely idiotic
304 * "used-but-marked-unused" warning - and
305 * #pragma GCC diagnostic ignored "-Wused-but-marked-unused"
306 * raises error: unknown option after "#pragma GCC diagnostic".
307 * Disable this warning completely, until the GCC guys sober up
308 * again.
309 */
310
311#pragma GCC diagnostic ignored "-Wunused-function"
312
313#define FUNCTION_MAY_BE_UNUSED /* __attribute__((unused)) */
314
315#else
316#define FUNCTION_MAY_BE_UNUSED
317#endif
318
319
320/* Some ANSI #includes are not available on Windows CE and Zephyr */
321#if !defined(_WIN32_WCE) && !defined(__ZEPHYR__)
322#include <errno.h>
323#include <fcntl.h>
324#include <signal.h>
325#include <stdlib.h>
326#include <sys/stat.h>
327#include <sys/types.h>
328#endif /* !_WIN32_WCE */
329
330
331#if defined(__clang__)
332/* When using -Weverything, clang does not accept it's own headers
333 * in a release build configuration. Disable what is too much in
334 * -Weverything. */
335#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
336#endif
337
338#if defined(__GNUC__) || defined(__MINGW32__)
339/* Who on earth came to the conclusion, using __DATE__ should rise
340 * an "expansion of date or time macro is not reproducible"
341 * warning. That's exactly what was intended by using this macro.
342 * Just disable this nonsense warning. */
343
344/* And disabling them does not work either:
345 * #pragma clang diagnostic ignored "-Wno-error=date-time"
346 * #pragma clang diagnostic ignored "-Wdate-time"
347 * So we just have to disable ALL warnings for some lines
348 * of code.
349 * This seems to be a known GCC bug, not resolved since 2012:
350 * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431
351 */
352#endif
353
354
355#if defined(__MACH__) && defined(__APPLE__) /* Apple OSX section */
356
357#if defined(__clang__)
358#if (__clang_major__ == 3) && ((__clang_minor__ == 7) || (__clang_minor__ == 8))
359/* Avoid warnings for Xcode 7. It seems it does no longer exist in Xcode 8 */
360#pragma clang diagnostic ignored "-Wno-reserved-id-macro"
361#pragma clang diagnostic ignored "-Wno-keyword-macro"
362#endif
363#endif
364
365#ifndef CLOCK_MONOTONIC
366#define CLOCK_MONOTONIC (1)
367#endif
368#ifndef CLOCK_REALTIME
369#define CLOCK_REALTIME (2)
370#endif
371
372#include <mach/clock.h>
373#include <mach/mach.h>
374#include <mach/mach_time.h>
375#include <sys/errno.h>
376#include <sys/time.h>
377
378/* clock_gettime is not implemented on OSX prior to 10.12 */
379static int
380_civet_clock_gettime(int clk_id, struct timespec *t)
381{
382 memset(t, 0, sizeof(*t));
383 if (clk_id == CLOCK_REALTIME) {
384 struct timeval now;
385 int rv = gettimeofday(&now, NULL);
386 if (rv) {
387 return rv;
388 }
389 t->tv_sec = now.tv_sec;
390 t->tv_nsec = now.tv_usec * 1000;
391 return 0;
392
393 } else if (clk_id == CLOCK_MONOTONIC) {
394 static uint64_t clock_start_time = 0;
395 static mach_timebase_info_data_t timebase_ifo = {0, 0};
396
397 uint64_t now = mach_absolute_time();
398
399 if (clock_start_time == 0) {
400 kern_return_t mach_status = mach_timebase_info(&timebase_ifo);
401 DEBUG_ASSERT(mach_status == KERN_SUCCESS);
402
403 /* appease "unused variable" warning for release builds */
404 (void)mach_status;
405
406 clock_start_time = now;
407 }
408
409 now = (uint64_t)((double)(now - clock_start_time)
410 * (double)timebase_ifo.numer
411 / (double)timebase_ifo.denom);
412
413 t->tv_sec = now / 1000000000;
414 t->tv_nsec = now % 1000000000;
415 return 0;
416 }
417 return -1; /* EINVAL - Clock ID is unknown */
418}
419
420/* if clock_gettime is declared, then __CLOCK_AVAILABILITY will be defined */
421#if defined(__CLOCK_AVAILABILITY)
422/* If we compiled with Mac OSX 10.12 or later, then clock_gettime will be
423 * declared but it may be NULL at runtime. So we need to check before using
424 * it. */
425static int
426_civet_safe_clock_gettime(int clk_id, struct timespec *t)
427{
428 if (clock_gettime) {
429 return clock_gettime(clk_id, t);
430 }
431 return _civet_clock_gettime(clk_id, t);
432}
433#define clock_gettime _civet_safe_clock_gettime
434#else
435#define clock_gettime _civet_clock_gettime
436#endif
437
438#endif
439
440
441#if defined(_WIN32)
442#define ERROR_TRY_AGAIN(err) ((err) == WSAEWOULDBLOCK)
443#else
444/* Unix might return different error codes indicating to try again.
445 * For Linux EAGAIN==EWOULDBLOCK, maybe EAGAIN!=EWOULDBLOCK is history from
446 * decades ago, but better check both and let the compiler optimize it. */
447#define ERROR_TRY_AGAIN(err) \
448 (((err) == EAGAIN) || ((err) == EWOULDBLOCK) || ((err) == EINTR))
449#endif
450
451#if defined(USE_ZLIB)
452#include "zconf.h"
453#include "zlib.h"
454#endif
455
456
457/********************************************************************/
458/* CivetWeb configuration defines */
459/********************************************************************/
460
461/* Maximum number of threads that can be configured.
462 * The number of threads actually created depends on the "num_threads"
463 * configuration parameter, but this is the upper limit. */
464#if !defined(MAX_WORKER_THREADS)
465#define MAX_WORKER_THREADS (1024 * 64) /* in threads (count) */
466#endif
467
468/* Timeout interval for select/poll calls.
469 * The timeouts depend on "*_timeout_ms" configuration values, but long
470 * timeouts are split into timouts as small as SOCKET_TIMEOUT_QUANTUM.
471 * This reduces the time required to stop the server. */
472#if !defined(SOCKET_TIMEOUT_QUANTUM)
473#define SOCKET_TIMEOUT_QUANTUM (2000) /* in ms */
474#endif
475
476/* Do not try to compress files smaller than this limit. */
477#if !defined(MG_FILE_COMPRESSION_SIZE_LIMIT)
478#define MG_FILE_COMPRESSION_SIZE_LIMIT (1024) /* in bytes */
479#endif
480
481#if !defined(PASSWORDS_FILE_NAME)
482#define PASSWORDS_FILE_NAME ".htpasswd"
483#endif
484
485/* Initial buffer size for all CGI environment variables. In case there is
486 * not enough space, another block is allocated. */
487#if !defined(CGI_ENVIRONMENT_SIZE)
488#define CGI_ENVIRONMENT_SIZE (4096) /* in bytes */
489#endif
490
491/* Maximum number of environment variables. */
492#if !defined(MAX_CGI_ENVIR_VARS)
493#define MAX_CGI_ENVIR_VARS (256) /* in variables (count) */
494#endif
495
496/* General purpose buffer size. */
497#if !defined(MG_BUF_LEN) /* in bytes */
498#define MG_BUF_LEN (1024 * 8)
499#endif
500
501
502/********************************************************************/
503
504/* Helper macros */
505#if !defined(ARRAY_SIZE)
506#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
507#endif
508
509#include <stdint.h>
510
511/* Standard defines */
512#if !defined(INT64_MAX)
513#define INT64_MAX (9223372036854775807)
514#endif
515
516#define SHUTDOWN_RD (0)
517#define SHUTDOWN_WR (1)
518#define SHUTDOWN_BOTH (2)
519
521 "worker threads must be a positive number");
522
523mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8,
524 "size_t data type size check");
525
526
527#if defined(_WIN32) /* WINDOWS include block */
528#include <malloc.h> /* *alloc( */
529#include <stdlib.h> /* *alloc( */
530#include <time.h> /* struct timespec */
531#include <windows.h>
532#include <winsock2.h> /* DTL add for SO_EXCLUSIVE */
533#include <ws2tcpip.h>
534
535typedef const char *SOCK_OPT_TYPE;
536
537/* For a detailed description of these *_PATH_MAX defines, see
538 * https://github.com/civetweb/civetweb/issues/937. */
539
540/* UTF8_PATH_MAX is a char buffer size for 259 BMP characters in UTF-8 plus
541 * null termination, rounded up to the next 4 bytes boundary */
542#define UTF8_PATH_MAX (3 * 260)
543/* UTF16_PATH_MAX is the 16-bit wchar_t buffer size required for 259 BMP
544 * characters plus termination. (Note: wchar_t is 16 bit on Windows) */
545#define UTF16_PATH_MAX (260)
546
547#if !defined(_IN_PORT_T)
548#if !defined(in_port_t)
549#define in_port_t u_short
550#endif
551#endif
552
553#if defined(_WIN32_WCE)
554#error "WinCE support has ended"
555#endif
556
557#include <direct.h>
558#include <io.h>
559#include <process.h>
560
561
562#define MAKEUQUAD(lo, hi) \
563 ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32))
564#define RATE_DIFF (10000000) /* 100 nsecs */
565#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de))
566#define SYS2UNIX_TIME(lo, hi) \
567 ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF))
568
569/* Visual Studio 6 does not know __func__ or __FUNCTION__
570 * The rest of MS compilers use __FUNCTION__, not C99 __func__
571 * Also use _strtoui64 on modern M$ compilers */
572#if defined(_MSC_VER)
573#if (_MSC_VER < 1300)
574#define STRX(x) #x
575#define STR(x) STRX(x)
576#define __func__ __FILE__ ":" STR(__LINE__)
577#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x))
578#define strtoll(x, y, z) (_atoi64(x))
579#else
580#define __func__ __FUNCTION__
581#define strtoull(x, y, z) (_strtoui64(x, y, z))
582#define strtoll(x, y, z) (_strtoi64(x, y, z))
583#endif
584#endif /* _MSC_VER */
585
586
587#define ERRNO ((int)(GetLastError()))
588#define NO_SOCKLEN_T
589
590
591#if defined(_WIN64) || defined(__MINGW64__)
592#if !defined(SSL_LIB)
593
594#if defined(OPENSSL_API_3_0)
595#define SSL_LIB "libssl-3-x64.dll"
596#define CRYPTO_LIB "libcrypto-3-x64.dll"
597#endif
598
599#if defined(OPENSSL_API_1_1)
600#define SSL_LIB "libssl-1_1-x64.dll"
601#define CRYPTO_LIB "libcrypto-1_1-x64.dll"
602#endif /* OPENSSL_API_1_1 */
603
604#if defined(OPENSSL_API_1_0)
605#define SSL_LIB "ssleay64.dll"
606#define CRYPTO_LIB "libeay64.dll"
607#endif /* OPENSSL_API_1_0 */
608
609#endif
610#else /* defined(_WIN64) || defined(__MINGW64__) */
611#if !defined(SSL_LIB)
612
613#if defined(OPENSSL_API_3_0)
614#define SSL_LIB "libssl-3.dll"
615#define CRYPTO_LIB "libcrypto-3.dll"
616#endif
617
618#if defined(OPENSSL_API_1_1)
619#define SSL_LIB "libssl-1_1.dll"
620#define CRYPTO_LIB "libcrypto-1_1.dll"
621#endif /* OPENSSL_API_1_1 */
622
623#if defined(OPENSSL_API_1_0)
624#define SSL_LIB "ssleay32.dll"
625#define CRYPTO_LIB "libeay32.dll"
626#endif /* OPENSSL_API_1_0 */
627
628#endif /* SSL_LIB */
629#endif /* defined(_WIN64) || defined(__MINGW64__) */
630
631
632#define O_NONBLOCK (0)
633#if !defined(W_OK)
634#define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */
635#endif
636#define _POSIX_
637#define INT64_FMT "I64d"
638#define UINT64_FMT "I64u"
639
640#define WINCDECL __cdecl
641#define vsnprintf_impl _vsnprintf
642#define access _access
643#define mg_sleep(x) (Sleep(x))
644
645#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
646#if !defined(popen)
647#define popen(x, y) (_popen(x, y))
648#endif
649#if !defined(pclose)
650#define pclose(x) (_pclose(x))
651#endif
652#define close(x) (_close(x))
653#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y)))
654#define RTLD_LAZY (0)
655#define fseeko(x, y, z) ((_lseeki64(_fileno(x), (y), (z)) == -1) ? -1 : 0)
656#define fdopen(x, y) (_fdopen((x), (y)))
657#define write(x, y, z) (_write((x), (y), (unsigned)z))
658#define read(x, y, z) (_read((x), (y), (unsigned)z))
659#define flockfile(x) ((void)pthread_mutex_lock(&global_log_file_lock))
660#define funlockfile(x) ((void)pthread_mutex_unlock(&global_log_file_lock))
661#define sleep(x) (Sleep((x)*1000))
662#define rmdir(x) (_rmdir(x))
663#if defined(_WIN64) || !defined(__MINGW32__)
664/* Only MinGW 32 bit is missing this function */
665#define timegm(x) (_mkgmtime(x))
666#else
667time_t timegm(struct tm *tm);
668#define NEED_TIMEGM
669#endif
670
671
672#if !defined(fileno)
673#define fileno(x) (_fileno(x))
674#endif /* !fileno MINGW #defines fileno */
675
676typedef struct {
677 CRITICAL_SECTION sec; /* Immovable */
678} pthread_mutex_t;
679typedef DWORD pthread_key_t;
680typedef HANDLE pthread_t;
681typedef struct {
682 pthread_mutex_t threadIdSec;
683 struct mg_workerTLS *waiting_thread; /* The chain of threads */
684} pthread_cond_t;
685
686#if !defined(__clockid_t_defined)
687typedef DWORD clockid_t;
688#endif
689#if !defined(CLOCK_MONOTONIC)
690#define CLOCK_MONOTONIC (1)
691#endif
692#if !defined(CLOCK_REALTIME)
693#define CLOCK_REALTIME (2)
694#endif
695#if !defined(CLOCK_THREAD)
696#define CLOCK_THREAD (3)
697#endif
698#if !defined(CLOCK_PROCESS)
699#define CLOCK_PROCESS (4)
700#endif
701
702
703#if defined(_MSC_VER) && (_MSC_VER >= 1900)
704#define _TIMESPEC_DEFINED
705#endif
706#if !defined(_TIMESPEC_DEFINED)
707struct timespec {
708 time_t tv_sec; /* seconds */
709 long tv_nsec; /* nanoseconds */
710};
711#endif
712
713#if !defined(WIN_PTHREADS_TIME_H)
714#define MUST_IMPLEMENT_CLOCK_GETTIME
715#endif
716
717#if defined(MUST_IMPLEMENT_CLOCK_GETTIME)
718#define clock_gettime mg_clock_gettime
719static int
720clock_gettime(clockid_t clk_id, struct timespec *tp)
721{
722 FILETIME ft;
723 ULARGE_INTEGER li, li2;
724 BOOL ok = FALSE;
725 double d;
726 static double perfcnt_per_sec = 0.0;
727 static BOOL initialized = FALSE;
728
729 if (!initialized) {
730 QueryPerformanceFrequency((LARGE_INTEGER *)&li);
731 perfcnt_per_sec = 1.0 / li.QuadPart;
732 initialized = TRUE;
733 }
734
735 if (tp) {
736 memset(tp, 0, sizeof(*tp));
737
738 if (clk_id == CLOCK_REALTIME) {
739
740 /* BEGIN: CLOCK_REALTIME = wall clock (date and time) */
741 GetSystemTimeAsFileTime(&ft);
742 li.LowPart = ft.dwLowDateTime;
743 li.HighPart = ft.dwHighDateTime;
744 li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */
745 tp->tv_sec = (time_t)(li.QuadPart / 10000000);
746 tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
747 ok = TRUE;
748 /* END: CLOCK_REALTIME */
749
750 } else if (clk_id == CLOCK_MONOTONIC) {
751
752 /* BEGIN: CLOCK_MONOTONIC = stopwatch (time differences) */
753 QueryPerformanceCounter((LARGE_INTEGER *)&li);
754 d = li.QuadPart * perfcnt_per_sec;
755 tp->tv_sec = (time_t)d;
756 d -= (double)tp->tv_sec;
757 tp->tv_nsec = (long)(d * 1.0E9);
758 ok = TRUE;
759 /* END: CLOCK_MONOTONIC */
760
761 } else if (clk_id == CLOCK_THREAD) {
762
763 /* BEGIN: CLOCK_THREAD = CPU usage of thread */
764 FILETIME t_create, t_exit, t_kernel, t_user;
765 if (GetThreadTimes(GetCurrentThread(),
766 &t_create,
767 &t_exit,
768 &t_kernel,
769 &t_user)) {
770 li.LowPart = t_user.dwLowDateTime;
771 li.HighPart = t_user.dwHighDateTime;
772 li2.LowPart = t_kernel.dwLowDateTime;
773 li2.HighPart = t_kernel.dwHighDateTime;
774 li.QuadPart += li2.QuadPart;
775 tp->tv_sec = (time_t)(li.QuadPart / 10000000);
776 tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
777 ok = TRUE;
778 }
779 /* END: CLOCK_THREAD */
780
781 } else if (clk_id == CLOCK_PROCESS) {
782
783 /* BEGIN: CLOCK_PROCESS = CPU usage of process */
784 FILETIME t_create, t_exit, t_kernel, t_user;
785 if (GetProcessTimes(GetCurrentProcess(),
786 &t_create,
787 &t_exit,
788 &t_kernel,
789 &t_user)) {
790 li.LowPart = t_user.dwLowDateTime;
791 li.HighPart = t_user.dwHighDateTime;
792 li2.LowPart = t_kernel.dwLowDateTime;
793 li2.HighPart = t_kernel.dwHighDateTime;
794 li.QuadPart += li2.QuadPart;
795 tp->tv_sec = (time_t)(li.QuadPart / 10000000);
796 tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
797 ok = TRUE;
798 }
799 /* END: CLOCK_PROCESS */
800
801 } else {
802
803 /* BEGIN: unknown clock */
804 /* ok = FALSE; already set by init */
805 /* END: unknown clock */
806 }
807 }
808
809 return ok ? 0 : -1;
810}
811#endif
812
813
814#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */
815
816static int pthread_mutex_lock(pthread_mutex_t *);
817static int pthread_mutex_unlock(pthread_mutex_t *);
818static void path_to_unicode(const struct mg_connection *conn,
819 const char *path,
820 wchar_t *wbuf,
821 size_t wbuf_len);
822
823/* All file operations need to be rewritten to solve #246. */
824
825struct mg_file;
826
827static const char *mg_fgets(char *buf, size_t size, struct mg_file *filep);
828
829
830/* POSIX dirent interface */
831struct dirent {
832 char d_name[UTF8_PATH_MAX];
833};
834
835typedef struct DIR {
836 HANDLE handle;
837 WIN32_FIND_DATAW info;
838 struct dirent result;
839} DIR;
840
841#if defined(HAVE_POLL)
842#define mg_pollfd pollfd
843#else
844struct mg_pollfd {
845 SOCKET fd;
846 short events;
847 short revents;
848};
849#endif
850
851/* Mark required libraries */
852#if defined(_MSC_VER)
853#pragma comment(lib, "Ws2_32.lib")
854#endif
855
856#else /* defined(_WIN32) - WINDOWS vs UNIX include block */
857
858#include <inttypes.h>
859
860/* Linux & co. internally use UTF8 */
861#define UTF8_PATH_MAX (PATH_MAX)
862
863typedef const void *SOCK_OPT_TYPE;
864
865#if defined(ANDROID)
866typedef unsigned short int in_port_t;
867#endif
868
869#if !defined(__ZEPHYR__)
870#include <arpa/inet.h>
871#include <ctype.h>
872#include <dirent.h>
873#include <grp.h>
874#include <limits.h>
875#include <netdb.h>
876#include <netinet/in.h>
877#include <netinet/tcp.h>
878#include <poll.h>
879#include <pthread.h>
880#include <pwd.h>
881#include <stdarg.h>
882#include <stddef.h>
883#include <stdio.h>
884#include <stdlib.h>
885#include <string.h>
886#include <sys/socket.h>
887#include <sys/time.h>
888#include <sys/utsname.h>
889#include <sys/wait.h>
890#include <time.h>
891#include <unistd.h>
892#if defined(USE_X_DOM_SOCKET)
893#include <sys/un.h>
894#endif
895#endif
896
897#define vsnprintf_impl vsnprintf
898
899#if !defined(NO_SSL_DL) && !defined(NO_SSL)
900#include <dlfcn.h>
901#endif
902
903#if defined(__MACH__) && defined(__APPLE__)
904#define SSL_LIB "libssl.dylib"
905#define CRYPTO_LIB "libcrypto.dylib"
906#else
907#if !defined(SSL_LIB)
908#define SSL_LIB "libssl.so"
909#endif
910#if !defined(CRYPTO_LIB)
911#define CRYPTO_LIB "libcrypto.so"
912#endif
913#endif
914#if !defined(O_BINARY)
915#define O_BINARY (0)
916#endif /* O_BINARY */
917#define closesocket(a) (close(a))
918#define mg_mkdir(conn, path, mode) (mkdir(path, mode))
919#define mg_remove(conn, x) (remove(x))
920#define mg_sleep(x) (usleep((x)*1000))
921#define mg_opendir(conn, x) (opendir(x))
922#define mg_closedir(x) (closedir(x))
923#define mg_readdir(x) (readdir(x))
924#define ERRNO (errno)
925#define INVALID_SOCKET (-1)
926#define INT64_FMT PRId64
927#define UINT64_FMT PRIu64
928typedef int SOCKET;
929#define WINCDECL
930
931#if defined(__hpux)
932/* HPUX 11 does not have monotonic, fall back to realtime */
933#if !defined(CLOCK_MONOTONIC)
934#define CLOCK_MONOTONIC CLOCK_REALTIME
935#endif
936
937/* HPUX defines socklen_t incorrectly as size_t which is 64bit on
938 * Itanium. Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED
939 * the prototypes use int* rather than socklen_t* which matches the
940 * actual library expectation. When called with the wrong size arg
941 * accept() returns a zero client inet addr and check_acl() always
942 * fails. Since socklen_t is widely used below, just force replace
943 * their typedef with int. - DTL
944 */
945#define socklen_t int
946#endif /* hpux */
947
948#define mg_pollfd pollfd
949
950#endif /* defined(_WIN32) - WINDOWS vs UNIX include block */
951
952/* In case our C library is missing "timegm", provide an implementation */
953#if defined(NEED_TIMEGM)
954static inline int
955is_leap(int y)
956{
957 return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
958}
959
960static inline int
961count_leap(int y)
962{
963 return (y - 1969) / 4 - (y - 1901) / 100 + (y - 1601) / 400;
964}
965
966time_t
967timegm(struct tm *tm)
968{
969 static const unsigned short ydays[] = {
970 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
971 int year = tm->tm_year + 1900;
972 int mon = tm->tm_mon;
973 int mday = tm->tm_mday - 1;
974 int hour = tm->tm_hour;
975 int min = tm->tm_min;
976 int sec = tm->tm_sec;
977
978 if (year < 1970 || mon < 0 || mon > 11 || mday < 0
979 || (mday >= ydays[mon + 1] - ydays[mon]
980 + (mon == 1 && is_leap(year) ? 1 : 0))
981 || hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 60)
982 return -1;
983
984 time_t res = year - 1970;
985 res *= 365;
986 res += mday;
987 res += ydays[mon] + (mon > 1 && is_leap(year) ? 1 : 0);
988 res += count_leap(year);
989
990 res *= 24;
991 res += hour;
992 res *= 60;
993 res += min;
994 res *= 60;
995 res += sec;
996 return res;
997}
998#endif /* NEED_TIMEGM */
999
1000
1001/* va_copy should always be a macro, C99 and C++11 - DTL */
1002#if !defined(va_copy)
1003#define va_copy(x, y) ((x) = (y))
1004#endif
1005
1006
1007#if defined(_WIN32)
1008/* Create substitutes for POSIX functions in Win32. */
1009
1010#if defined(GCC_DIAGNOSTIC)
1011/* Show no warning in case system functions are not used. */
1012#pragma GCC diagnostic push
1013#pragma GCC diagnostic ignored "-Wunused-function"
1014#endif
1015
1016
1017static pthread_mutex_t global_log_file_lock;
1018
1020static DWORD
1021pthread_self(void)
1022{
1023 return GetCurrentThreadId();
1024}
1025
1026
1028static int
1029pthread_key_create(
1030 pthread_key_t *key,
1031 void (*_ignored)(void *) /* destructor not supported for Windows */
1032)
1033{
1034 (void)_ignored;
1035
1036 if ((key != 0)) {
1037 *key = TlsAlloc();
1038 return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1;
1039 }
1040 return -2;
1041}
1042
1043
1045static int
1046pthread_key_delete(pthread_key_t key)
1047{
1048 return TlsFree(key) ? 0 : 1;
1049}
1050
1051
1053static int
1054pthread_setspecific(pthread_key_t key, void *value)
1055{
1056 return TlsSetValue(key, value) ? 0 : 1;
1057}
1058
1059
1061static void *
1062pthread_getspecific(pthread_key_t key)
1063{
1064 return TlsGetValue(key);
1065}
1066
1067#if defined(GCC_DIAGNOSTIC)
1068/* Enable unused function warning again */
1069#pragma GCC diagnostic pop
1070#endif
1071
1072static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL;
1073#else
1074static pthread_mutexattr_t pthread_mutex_attr;
1075#endif /* _WIN32 */
1076
1077
1078#if defined(GCC_DIAGNOSTIC)
1079/* Show no warning in case system functions are not used. */
1080#pragma GCC diagnostic push
1081#pragma GCC diagnostic ignored "-Wunused-function"
1082#endif /* defined(GCC_DIAGNOSTIC) */
1083#if defined(__clang__)
1084/* Show no warning in case system functions are not used. */
1085#pragma clang diagnostic push
1086#pragma clang diagnostic ignored "-Wunused-function"
1087#endif
1088
1089static pthread_mutex_t global_lock_mutex;
1090
1091
1093static void
1095{
1096 (void)pthread_mutex_lock(&global_lock_mutex);
1097}
1098
1099
1101static void
1103{
1104 (void)pthread_mutex_unlock(&global_lock_mutex);
1105}
1106
1107
1108#if defined(_WIN64)
1109mg_static_assert(SIZE_MAX == 0xFFFFFFFFFFFFFFFFu, "Mismatch for atomic types");
1110#elif defined(_WIN32)
1111mg_static_assert(SIZE_MAX == 0xFFFFFFFFu, "Mismatch for atomic types");
1112#endif
1113
1114
1115/* Atomic functions working on ptrdiff_t ("signed size_t").
1116 * Operations: Increment, Decrement, Add, Maximum.
1117 * Up to size_t, they do not an atomic "load" operation.
1118 */
1120static ptrdiff_t
1121mg_atomic_inc(volatile ptrdiff_t *addr)
1122{
1123 ptrdiff_t ret;
1124
1125#if defined(_WIN64) && !defined(NO_ATOMICS)
1126 ret = InterlockedIncrement64(addr);
1127#elif defined(_WIN32) && !defined(NO_ATOMICS)
1128 ret = InterlockedIncrement(addr);
1129#elif defined(__GNUC__) \
1130 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1131 && !defined(NO_ATOMICS)
1132 ret = __sync_add_and_fetch(addr, 1);
1133#else
1135 ret = (++(*addr));
1137#endif
1138 return ret;
1139}
1140
1141
1143static ptrdiff_t
1144mg_atomic_dec(volatile ptrdiff_t *addr)
1145{
1146 ptrdiff_t ret;
1147
1148#if defined(_WIN64) && !defined(NO_ATOMICS)
1149 ret = InterlockedDecrement64(addr);
1150#elif defined(_WIN32) && !defined(NO_ATOMICS)
1151 ret = InterlockedDecrement(addr);
1152#elif defined(__GNUC__) \
1153 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1154 && !defined(NO_ATOMICS)
1155 ret = __sync_sub_and_fetch(addr, 1);
1156#else
1158 ret = (--(*addr));
1160#endif
1161 return ret;
1162}
1163
1164
1165#if defined(USE_SERVER_STATS) || defined(STOP_FLAG_NEEDS_LOCK)
1166static ptrdiff_t
1167mg_atomic_add(volatile ptrdiff_t *addr, ptrdiff_t value)
1168{
1169 ptrdiff_t ret;
1170
1171#if defined(_WIN64) && !defined(NO_ATOMICS)
1172 ret = InterlockedAdd64(addr, value);
1173#elif defined(_WIN32) && !defined(NO_ATOMICS)
1174 ret = InterlockedExchangeAdd(addr, value) + value;
1175#elif defined(__GNUC__) \
1176 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1177 && !defined(NO_ATOMICS)
1178 ret = __sync_add_and_fetch(addr, value);
1179#else
1181 *addr += value;
1182 ret = (*addr);
1184#endif
1185 return ret;
1186}
1187
1188
1190static ptrdiff_t
1191mg_atomic_compare_and_swap(volatile ptrdiff_t *addr,
1192 ptrdiff_t oldval,
1193 ptrdiff_t newval)
1194{
1195 ptrdiff_t ret;
1196
1197#if defined(_WIN64) && !defined(NO_ATOMICS)
1198 ret = InterlockedCompareExchange64(addr, newval, oldval);
1199#elif defined(_WIN32) && !defined(NO_ATOMICS)
1200 ret = InterlockedCompareExchange(addr, newval, oldval);
1201#elif defined(__GNUC__) \
1202 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1203 && !defined(NO_ATOMICS)
1204 ret = __sync_val_compare_and_swap(addr, oldval, newval);
1205#else
1207 ret = *addr;
1208 if ((ret != newval) && (ret == oldval)) {
1209 *addr = newval;
1210 }
1212#endif
1213 return ret;
1214}
1215
1216
1217static void
1218mg_atomic_max(volatile ptrdiff_t *addr, ptrdiff_t value)
1219{
1220 register ptrdiff_t tmp = *addr;
1221
1222#if defined(_WIN64) && !defined(NO_ATOMICS)
1223 while (tmp < value) {
1224 tmp = InterlockedCompareExchange64(addr, value, tmp);
1225 }
1226#elif defined(_WIN32) && !defined(NO_ATOMICS)
1227 while (tmp < value) {
1228 tmp = InterlockedCompareExchange(addr, value, tmp);
1229 }
1230#elif defined(__GNUC__) \
1231 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1232 && !defined(NO_ATOMICS)
1233 while (tmp < value) {
1234 tmp = __sync_val_compare_and_swap(addr, tmp, value);
1235 }
1236#else
1238 if (*addr < value) {
1239 *addr = value;
1240 }
1242#endif
1243}
1244
1245
1246static int64_t
1247mg_atomic_add64(volatile int64_t *addr, int64_t value)
1248{
1249 int64_t ret;
1250
1251#if defined(_WIN64) && !defined(NO_ATOMICS)
1252 ret = InterlockedAdd64(addr, value);
1253#elif defined(_WIN32) && !defined(NO_ATOMICS)
1254 ret = InterlockedExchangeAdd64(addr, value) + value;
1255#elif defined(__GNUC__) \
1256 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1257 && !defined(NO_ATOMICS)
1258 ret = __sync_add_and_fetch(addr, value);
1259#else
1261 *addr += value;
1262 ret = (*addr);
1264#endif
1265 return ret;
1266}
1267#endif
1268
1269
1270#if defined(GCC_DIAGNOSTIC)
1271/* Show no warning in case system functions are not used. */
1272#pragma GCC diagnostic pop
1273#endif /* defined(GCC_DIAGNOSTIC) */
1274#if defined(__clang__)
1275/* Show no warning in case system functions are not used. */
1276#pragma clang diagnostic pop
1277#endif
1278
1279
1280#if defined(USE_SERVER_STATS)
1281
1282struct mg_memory_stat {
1283 volatile ptrdiff_t totalMemUsed;
1284 volatile ptrdiff_t maxMemUsed;
1285 volatile ptrdiff_t blockCount;
1286};
1287
1288
1289static struct mg_memory_stat *get_memory_stat(struct mg_context *ctx);
1290
1291
1292static void *
1293mg_malloc_ex(size_t size,
1294 struct mg_context *ctx,
1295 const char *file,
1296 unsigned line)
1297{
1298 void *data = malloc(size + 2 * sizeof(uintptr_t));
1299 void *memory = 0;
1300 struct mg_memory_stat *mstat = get_memory_stat(ctx);
1301
1302#if defined(MEMORY_DEBUGGING)
1303 char mallocStr[256];
1304#else
1305 (void)file;
1306 (void)line;
1307#endif
1308
1309 if (data) {
1310 ptrdiff_t mmem = mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)size);
1311 mg_atomic_max(&mstat->maxMemUsed, mmem);
1312
1313 mg_atomic_inc(&mstat->blockCount);
1314 ((uintptr_t *)data)[0] = size;
1315 ((uintptr_t *)data)[1] = (uintptr_t)mstat;
1316 memory = (void *)(((char *)data) + 2 * sizeof(uintptr_t));
1317 }
1318
1319#if defined(MEMORY_DEBUGGING)
1320 sprintf(mallocStr,
1321 "MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n",
1322 memory,
1323 (unsigned long)size,
1324 (unsigned long)mstat->totalMemUsed,
1325 (unsigned long)mstat->blockCount,
1326 file,
1327 line);
1328 DEBUG_TRACE("%s", mallocStr);
1329#endif
1330
1331 return memory;
1332}
1333
1334
1335static void *
1336mg_calloc_ex(size_t count,
1337 size_t size,
1338 struct mg_context *ctx,
1339 const char *file,
1340 unsigned line)
1341{
1342 void *data = mg_malloc_ex(size * count, ctx, file, line);
1343
1344 if (data) {
1345 memset(data, 0, size * count);
1346 }
1347 return data;
1348}
1349
1350
1351static void
1352mg_free_ex(void *memory, const char *file, unsigned line)
1353{
1354#if defined(MEMORY_DEBUGGING)
1355 char mallocStr[256];
1356#else
1357 (void)file;
1358 (void)line;
1359#endif
1360
1361 if (memory) {
1362 void *data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t));
1363 uintptr_t size = ((uintptr_t *)data)[0];
1364 struct mg_memory_stat *mstat =
1365 (struct mg_memory_stat *)(((uintptr_t *)data)[1]);
1366 mg_atomic_add(&mstat->totalMemUsed, -(ptrdiff_t)size);
1367 mg_atomic_dec(&mstat->blockCount);
1368
1369#if defined(MEMORY_DEBUGGING)
1370 sprintf(mallocStr,
1371 "MEM: %p %5lu free %7lu %4lu --- %s:%u\n",
1372 memory,
1373 (unsigned long)size,
1374 (unsigned long)mstat->totalMemUsed,
1375 (unsigned long)mstat->blockCount,
1376 file,
1377 line);
1378 DEBUG_TRACE("%s", mallocStr);
1379#endif
1380 free(data);
1381 }
1382}
1383
1384
1385static void *
1386mg_realloc_ex(void *memory,
1387 size_t newsize,
1388 struct mg_context *ctx,
1389 const char *file,
1390 unsigned line)
1391{
1392 void *data;
1393 void *_realloc;
1394 uintptr_t oldsize;
1395
1396#if defined(MEMORY_DEBUGGING)
1397 char mallocStr[256];
1398#else
1399 (void)file;
1400 (void)line;
1401#endif
1402
1403 if (newsize) {
1404 if (memory) {
1405 /* Reallocate existing block */
1406 struct mg_memory_stat *mstat;
1407 data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t));
1408 oldsize = ((uintptr_t *)data)[0];
1409 mstat = (struct mg_memory_stat *)((uintptr_t *)data)[1];
1410 _realloc = realloc(data, newsize + 2 * sizeof(uintptr_t));
1411 if (_realloc) {
1412 data = _realloc;
1413 mg_atomic_add(&mstat->totalMemUsed, -(ptrdiff_t)oldsize);
1414#if defined(MEMORY_DEBUGGING)
1415 sprintf(mallocStr,
1416 "MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n",
1417 memory,
1418 (unsigned long)oldsize,
1419 (unsigned long)mstat->totalMemUsed,
1420 (unsigned long)mstat->blockCount,
1421 file,
1422 line);
1423 DEBUG_TRACE("%s", mallocStr);
1424#endif
1425 mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)newsize);
1426
1427#if defined(MEMORY_DEBUGGING)
1428 sprintf(mallocStr,
1429 "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n",
1430 memory,
1431 (unsigned long)newsize,
1432 (unsigned long)mstat->totalMemUsed,
1433 (unsigned long)mstat->blockCount,
1434 file,
1435 line);
1436 DEBUG_TRACE("%s", mallocStr);
1437#endif
1438 *(uintptr_t *)data = newsize;
1439 data = (void *)(((char *)data) + 2 * sizeof(uintptr_t));
1440 } else {
1441#if defined(MEMORY_DEBUGGING)
1442 DEBUG_TRACE("%s", "MEM: realloc failed\n");
1443#endif
1444 return _realloc;
1445 }
1446 } else {
1447 /* Allocate new block */
1448 data = mg_malloc_ex(newsize, ctx, file, line);
1449 }
1450 } else {
1451 /* Free existing block */
1452 data = 0;
1453 mg_free_ex(memory, file, line);
1454 }
1455
1456 return data;
1457}
1458
1459
1460#define mg_malloc(a) mg_malloc_ex(a, NULL, __FILE__, __LINE__)
1461#define mg_calloc(a, b) mg_calloc_ex(a, b, NULL, __FILE__, __LINE__)
1462#define mg_realloc(a, b) mg_realloc_ex(a, b, NULL, __FILE__, __LINE__)
1463#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__)
1464
1465#define mg_malloc_ctx(a, c) mg_malloc_ex(a, c, __FILE__, __LINE__)
1466#define mg_calloc_ctx(a, b, c) mg_calloc_ex(a, b, c, __FILE__, __LINE__)
1467#define mg_realloc_ctx(a, b, c) mg_realloc_ex(a, b, c, __FILE__, __LINE__)
1468
1469
1470#else /* USE_SERVER_STATS */
1471
1472
1473static __inline void *
1475{
1476 return malloc(a);
1477}
1478
1479static __inline void *
1480mg_calloc(size_t a, size_t b)
1481{
1482 return calloc(a, b);
1483}
1484
1485static __inline void *
1486mg_realloc(void *a, size_t b)
1487{
1488 return realloc(a, b);
1489}
1490
1491static __inline void
1493{
1494 free(a);
1495}
1496
1497#define mg_malloc_ctx(a, c) mg_malloc(a)
1498#define mg_calloc_ctx(a, b, c) mg_calloc(a, b)
1499#define mg_realloc_ctx(a, b, c) mg_realloc(a, b)
1500#define mg_free_ctx(a, c) mg_free(a)
1501
1502#endif /* USE_SERVER_STATS */
1503
1504
1505static void mg_vsnprintf(const struct mg_connection *conn,
1506 int *truncated,
1507 char *buf,
1508 size_t buflen,
1509 const char *fmt,
1510 va_list ap);
1511
1512static void mg_snprintf(const struct mg_connection *conn,
1513 int *truncated,
1514 char *buf,
1515 size_t buflen,
1516 PRINTF_FORMAT_STRING(const char *fmt),
1517 ...) PRINTF_ARGS(5, 6);
1518
1519/* This following lines are just meant as a reminder to use the mg-functions
1520 * for memory management */
1521#if defined(malloc)
1522#undef malloc
1523#endif
1524#if defined(calloc)
1525#undef calloc
1526#endif
1527#if defined(realloc)
1528#undef realloc
1529#endif
1530#if defined(free)
1531#undef free
1532#endif
1533#if defined(snprintf)
1534#undef snprintf
1535#endif
1536#if defined(vsnprintf)
1537#undef vsnprintf
1538#endif
1539#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
1540#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
1541#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
1542#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
1543#define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
1544#if defined(_WIN32)
1545/* vsnprintf must not be used in any system,
1546 * but this define only works well for Windows. */
1547#define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf
1548#endif
1549
1550
1551/* mg_init_library counter */
1553
1554#if !defined(NO_SSL)
1555#if defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1) \
1556 || defined(OPENSSL_API_3_0)
1557static int mg_openssl_initialized = 0;
1558#endif
1559#if !defined(OPENSSL_API_1_0) && !defined(OPENSSL_API_1_1) \
1560 && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS)
1561#error "Please define OPENSSL_API_#_# or USE_MBEDTLS"
1562#endif
1563#if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_1_1)
1564#error "Multiple OPENSSL_API versions defined"
1565#endif
1566#if defined(OPENSSL_API_1_1) && defined(OPENSSL_API_3_0)
1567#error "Multiple OPENSSL_API versions defined"
1568#endif
1569#if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_3_0)
1570#error "Multiple OPENSSL_API versions defined"
1571#endif
1572#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1) \
1573 || defined(OPENSSL_API_3_0)) \
1574 && defined(USE_MBEDTLS)
1575#error "Multiple SSL libraries defined"
1576#endif
1577#endif
1578
1579
1580static pthread_key_t sTlsKey; /* Thread local storage index */
1581static volatile ptrdiff_t thread_idx_max = 0;
1582
1583#if defined(MG_LEGACY_INTERFACE)
1584#define MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE
1585#endif
1586
1589 unsigned long thread_idx;
1591#if defined(_WIN32)
1592 HANDLE pthread_cond_helper_mutex;
1593 struct mg_workerTLS *next_waiting_thread;
1594#endif
1595 const char *alpn_proto;
1596#if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE)
1597 char txtbuf[4];
1598#endif
1599};
1600
1601
1602#if defined(GCC_DIAGNOSTIC)
1603/* Show no warning in case system functions are not used. */
1604#pragma GCC diagnostic push
1605#pragma GCC diagnostic ignored "-Wunused-function"
1606#endif /* defined(GCC_DIAGNOSTIC) */
1607#if defined(__clang__)
1608/* Show no warning in case system functions are not used. */
1609#pragma clang diagnostic push
1610#pragma clang diagnostic ignored "-Wunused-function"
1611#endif
1612
1613
1614/* Get a unique thread ID as unsigned long, independent from the data type
1615 * of thread IDs defined by the operating system API.
1616 * If two calls to mg_current_thread_id return the same value, they calls
1617 * are done from the same thread. If they return different values, they are
1618 * done from different threads. (Provided this function is used in the same
1619 * process context and threads are not repeatedly created and deleted, but
1620 * CivetWeb does not do that).
1621 * This function must match the signature required for SSL id callbacks:
1622 * CRYPTO_set_id_callback
1623 */
1625static unsigned long
1627{
1628#if defined(_WIN32)
1629 return GetCurrentThreadId();
1630#else
1631
1632#if defined(__clang__)
1633#pragma clang diagnostic push
1634#pragma clang diagnostic ignored "-Wunreachable-code"
1635 /* For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)"
1636 * or not, so one of the two conditions will be unreachable by construction.
1637 * Unfortunately the C standard does not define a way to check this at
1638 * compile time, since the #if preprocessor conditions can not use the
1639 * sizeof operator as an argument. */
1640#endif
1641
1642 if (sizeof(pthread_t) > sizeof(unsigned long)) {
1643 /* This is the problematic case for CRYPTO_set_id_callback:
1644 * The OS pthread_t can not be cast to unsigned long. */
1645 struct mg_workerTLS *tls =
1646 (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
1647 if (tls == NULL) {
1648 /* SSL called from an unknown thread: Create some thread index.
1649 */
1650 tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct mg_workerTLS));
1651 tls->is_master = -2; /* -2 means "3rd party thread" */
1652 tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
1653 pthread_setspecific(sTlsKey, tls);
1654 }
1655 return tls->thread_idx;
1656 } else {
1657 /* pthread_t may be any data type, so a simple cast to unsigned long
1658 * can rise a warning/error, depending on the platform.
1659 * Here memcpy is used as an anything-to-anything cast. */
1660 unsigned long ret = 0;
1661 pthread_t t = pthread_self();
1662 memcpy(&ret, &t, sizeof(pthread_t));
1663 return ret;
1664 }
1665
1666#if defined(__clang__)
1667#pragma clang diagnostic pop
1668#endif
1669
1670#endif
1671}
1672
1673
1675static uint64_t
1677{
1678 struct timespec tsnow;
1679 clock_gettime(CLOCK_REALTIME, &tsnow);
1680 return (((uint64_t)tsnow.tv_sec) * 1000000000) + (uint64_t)tsnow.tv_nsec;
1681}
1682
1683
1684#if defined(GCC_DIAGNOSTIC)
1685/* Show no warning in case system functions are not used. */
1686#pragma GCC diagnostic pop
1687#endif /* defined(GCC_DIAGNOSTIC) */
1688#if defined(__clang__)
1689/* Show no warning in case system functions are not used. */
1690#pragma clang diagnostic pop
1691#endif
1692
1693
1694#if defined(NEED_DEBUG_TRACE_FUNC)
1695static void
1696DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...)
1697{
1698 va_list args;
1699 struct timespec tsnow;
1700
1701 /* Get some operating system independent thread id */
1702 unsigned long thread_id = mg_current_thread_id();
1703
1704 clock_gettime(CLOCK_REALTIME, &tsnow);
1705
1706 flockfile(DEBUG_TRACE_STREAM);
1707 fprintf(DEBUG_TRACE_STREAM,
1708 "*** %lu.%09lu %lu %s:%u: ",
1709 (unsigned long)tsnow.tv_sec,
1710 (unsigned long)tsnow.tv_nsec,
1711 thread_id,
1712 func,
1713 line);
1714 va_start(args, fmt);
1715 vfprintf(DEBUG_TRACE_STREAM, fmt, args);
1716 va_end(args);
1717 putc('\n', DEBUG_TRACE_STREAM);
1718 fflush(DEBUG_TRACE_STREAM);
1719 funlockfile(DEBUG_TRACE_STREAM);
1720}
1721#endif /* NEED_DEBUG_TRACE_FUNC */
1722
1723
1724#define MD5_STATIC static
1725#include "md5.inl"
1726
1727/* Darwin prior to 7.0 and Win32 do not have socklen_t */
1728#if defined(NO_SOCKLEN_T)
1729typedef int socklen_t;
1730#endif /* NO_SOCKLEN_T */
1731
1732#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */
1733
1734#if !defined(MSG_NOSIGNAL)
1735#define MSG_NOSIGNAL (0)
1736#endif
1737
1738
1739/* SSL: mbedTLS vs. no-ssl vs. OpenSSL */
1740#if defined(USE_MBEDTLS)
1741/* mbedTLS */
1742#include "mod_mbedtls.inl"
1743
1744#elif defined(NO_SSL)
1745/* no SSL */
1746typedef struct SSL SSL; /* dummy for SSL argument to push/pull */
1747typedef struct SSL_CTX SSL_CTX;
1748
1749#elif defined(NO_SSL_DL)
1750/* OpenSSL without dynamic loading */
1751#include <openssl/bn.h>
1752#include <openssl/conf.h>
1753#include <openssl/crypto.h>
1754#include <openssl/dh.h>
1755#include <openssl/engine.h>
1756#include <openssl/err.h>
1757#include <openssl/opensslv.h>
1758#include <openssl/pem.h>
1759#include <openssl/ssl.h>
1760#include <openssl/tls1.h>
1761#include <openssl/x509.h>
1762
1763#if defined(WOLFSSL_VERSION)
1764/* Additional defines for WolfSSL, see
1765 * https://github.com/civetweb/civetweb/issues/583 */
1766#include "wolfssl_extras.inl"
1767#endif
1768
1769#if defined(OPENSSL_IS_BORINGSSL)
1770/* From boringssl/src/include/openssl/mem.h:
1771 *
1772 * OpenSSL has, historically, had a complex set of malloc debugging options.
1773 * However, that was written in a time before Valgrind and ASAN. Since we now
1774 * have those tools, the OpenSSL allocation functions are simply macros around
1775 * the standard memory functions.
1776 *
1777 * #define OPENSSL_free free */
1778#define free free
1779// disable for boringssl
1780#define CONF_modules_unload(a) ((void)0)
1781#define ENGINE_cleanup() ((void)0)
1782#endif
1783
1784/* If OpenSSL headers are included, automatically select the API version */
1785#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
1786#if !defined(OPENSSL_API_3_0)
1787#define OPENSSL_API_3_0
1788#endif
1789#define OPENSSL_REMOVE_THREAD_STATE()
1790#else
1791#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
1792#if !defined(OPENSSL_API_1_1)
1793#define OPENSSL_API_1_1
1794#endif
1795#define OPENSSL_REMOVE_THREAD_STATE()
1796#else
1797#if !defined(OPENSSL_API_1_0)
1798#define OPENSSL_API_1_0
1799#endif
1800#define OPENSSL_REMOVE_THREAD_STATE() ERR_remove_thread_state(NULL)
1801#endif
1802#endif
1803
1804
1805#else
1806/* SSL loaded dynamically from DLL / shared object */
1807/* Add all prototypes here, to be independent from OpenSSL source
1808 * installation. */
1809#include "openssl_dl.inl"
1810
1811#endif /* Various SSL bindings */
1812
1813
1814#if !defined(NO_CACHING)
1815static const char month_names[][4] = {"Jan",
1816 "Feb",
1817 "Mar",
1818 "Apr",
1819 "May",
1820 "Jun",
1821 "Jul",
1822 "Aug",
1823 "Sep",
1824 "Oct",
1825 "Nov",
1826 "Dec"};
1827#endif /* !NO_CACHING */
1828
1829
1830/* Unified socket address. For IPv6 support, add IPv6 address structure in
1831 * the union u. */
1832union usa {
1833 struct sockaddr sa;
1834 struct sockaddr_in sin;
1835#if defined(USE_IPV6)
1836 struct sockaddr_in6 sin6;
1837#endif
1838#if defined(USE_X_DOM_SOCKET)
1839 struct sockaddr_un sun;
1840#endif
1841};
1842
1843#if defined(USE_X_DOM_SOCKET)
1844static unsigned short
1845USA_IN_PORT_UNSAFE(union usa *s)
1846{
1847 if (s->sa.sa_family == AF_INET)
1848 return s->sin.sin_port;
1849#if defined(USE_IPV6)
1850 if (s->sa.sa_family == AF_INET6)
1851 return s->sin6.sin6_port;
1852#endif
1853 return 0;
1854}
1855#endif
1856#if defined(USE_IPV6)
1857#define USA_IN_PORT_UNSAFE(s) \
1858 (((s)->sa.sa_family == AF_INET6) ? (s)->sin6.sin6_port : (s)->sin.sin_port)
1859#else
1860#define USA_IN_PORT_UNSAFE(s) ((s)->sin.sin_port)
1861#endif
1862
1863/* Describes a string (chunk of memory). */
1864struct vec {
1865 const char *ptr;
1866 size_t len;
1867};
1868
1870 /* File properties filled by mg_stat: */
1871 uint64_t size;
1873 int is_directory; /* Set to 1 if mg_stat is called for a directory */
1874 int is_gzipped; /* Set to 1 if the content is gzipped, in which
1875 * case we need a "Content-Eencoding: gzip" header */
1876 int location; /* 0 = nowhere, 1 = on disk, 2 = in memory */
1877};
1878
1879
1881 /* File properties filled by mg_fopen: */
1882 FILE *fp;
1883};
1884
1889
1890
1891#define STRUCT_FILE_INITIALIZER \
1892 { \
1893 {(uint64_t)0, (time_t)0, 0, 0, 0}, \
1894 { \
1895 (FILE *)NULL \
1896 } \
1897 }
1898
1899
1900/* Describes listening socket, or socket which was accept()-ed by the master
1901 * thread and queued for future handling by the worker thread. */
1902struct socket {
1903 SOCKET sock; /* Listening socket */
1904 union usa lsa; /* Local socket address */
1905 union usa rsa; /* Remote socket address */
1906 unsigned char is_ssl; /* Is port SSL-ed */
1907 unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL
1908 * port */
1909 unsigned char in_use; /* 0: invalid, 1: valid, 2: free */
1910};
1911
1912
1913/* Enum const for all options must be in sync with
1914 * static struct mg_option config_options[]
1915 * This is tested in the unit test (test/private.c)
1916 * "Private Config Options"
1917 */
1918enum {
1919 /* Once for each server */
1923 CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
1924 * socket option typedef TCP_NODELAY. */
1929#if defined(__linux__)
1930 ALLOW_SENDFILE_CALL,
1931#endif
1932#if defined(_WIN32)
1933 CASE_SENSITIVE_FILES,
1934#endif
1939#if defined(USE_WEBSOCKET)
1940 WEBSOCKET_TIMEOUT,
1941 ENABLE_WEBSOCKET_PING_PONG,
1942#endif
1945#if defined(USE_LUA)
1946 LUA_BACKGROUND_SCRIPT,
1947 LUA_BACKGROUND_SCRIPT_PARAMS,
1948#endif
1949#if defined(USE_HTTP2)
1950 ENABLE_HTTP2,
1951#endif
1952
1953 /* Once for each domain */
1955
1958
1963#if defined(USE_TIMERS)
1964 CGI_TIMEOUT,
1965#endif
1967
1972#if defined(USE_TIMERS)
1973 CGI2_TIMEOUT,
1974#endif
1976
1977#if defined(USE_4_CGI)
1978 CGI3_EXTENSIONS,
1979 CGI3_ENVIRONMENT,
1980 CGI3_INTERPRETER,
1981 CGI3_INTERPRETER_ARGS,
1982#if defined(USE_TIMERS)
1983 CGI3_TIMEOUT,
1984#endif
1985 CGI3_BUFFERING,
1986
1987 CGI4_EXTENSIONS,
1988 CGI4_ENVIRONMENT,
1989 CGI4_INTERPRETER,
1990 CGI4_INTERPRETER_ARGS,
1991#if defined(USE_TIMERS)
1992 CGI4_TIMEOUT,
1993#endif
1994 CGI4_BUFFERING,
1995#endif
1996
1997 PUT_DELETE_PASSWORDS_FILE, /* must follow CGI_* */
2021
2022#if defined(USE_LUA)
2023 LUA_PRELOAD_FILE,
2024 LUA_SCRIPT_EXTENSIONS,
2025 LUA_SERVER_PAGE_EXTENSIONS,
2026#if defined(MG_EXPERIMENTAL_INTERFACES)
2027 LUA_DEBUG_PARAMS,
2028#endif
2029#endif
2030#if defined(USE_DUKTAPE)
2031 DUKTAPE_SCRIPT_EXTENSIONS,
2032#endif
2033
2034#if defined(USE_WEBSOCKET)
2035 WEBSOCKET_ROOT,
2036#endif
2037#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2038 LUA_WEBSOCKET_EXTENSIONS,
2039#endif
2040
2045#if !defined(NO_CACHING)
2048#endif
2049#if !defined(NO_SSL)
2051#endif
2054
2057
2058
2059/* Config option name, config types, default value.
2060 * Must be in the same order as the enum const above.
2061 */
2062static const struct mg_option config_options[] = {
2063
2064 /* Once for each server */
2065 {"listening_ports", MG_CONFIG_TYPE_STRING_LIST, "8080"},
2066 {"num_threads", MG_CONFIG_TYPE_NUMBER, "50"},
2067 {"run_as_user", MG_CONFIG_TYPE_STRING, NULL},
2068 {"tcp_nodelay", MG_CONFIG_TYPE_NUMBER, "0"},
2069 {"max_request_size", MG_CONFIG_TYPE_NUMBER, "16384"},
2070 {"linger_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2071 {"connection_queue", MG_CONFIG_TYPE_NUMBER, "20"},
2072 {"listen_backlog", MG_CONFIG_TYPE_NUMBER, "200"},
2073#if defined(__linux__)
2074 {"allow_sendfile_call", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2075#endif
2076#if defined(_WIN32)
2077 {"case_sensitive", MG_CONFIG_TYPE_BOOLEAN, "no"},
2078#endif
2079 {"throttle", MG_CONFIG_TYPE_STRING_LIST, NULL},
2080 {"enable_keep_alive", MG_CONFIG_TYPE_BOOLEAN, "no"},
2081 {"request_timeout_ms", MG_CONFIG_TYPE_NUMBER, "30000"},
2082 {"keep_alive_timeout_ms", MG_CONFIG_TYPE_NUMBER, "500"},
2083#if defined(USE_WEBSOCKET)
2084 {"websocket_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2085 {"enable_websocket_ping_pong", MG_CONFIG_TYPE_BOOLEAN, "no"},
2086#endif
2087 {"decode_url", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2088 {"decode_query_string", MG_CONFIG_TYPE_BOOLEAN, "no"},
2089#if defined(USE_LUA)
2090 {"lua_background_script", MG_CONFIG_TYPE_FILE, NULL},
2091 {"lua_background_script_params", MG_CONFIG_TYPE_STRING_LIST, NULL},
2092#endif
2093#if defined(USE_HTTP2)
2094 {"enable_http2", MG_CONFIG_TYPE_BOOLEAN, "no"},
2095#endif
2096
2097 /* Once for each domain */
2098 {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2099
2100 {"access_log_file", MG_CONFIG_TYPE_FILE, NULL},
2101 {"error_log_file", MG_CONFIG_TYPE_FILE, NULL},
2102
2103 {"cgi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"},
2104 {"cgi_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2105 {"cgi_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2106 {"cgi_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2107#if defined(USE_TIMERS)
2108 {"cgi_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2109#endif
2110 {"cgi_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2111
2112 {"cgi2_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2113 {"cgi2_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2114 {"cgi2_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2115 {"cgi2_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2116#if defined(USE_TIMERS)
2117 {"cgi2_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2118#endif
2119 {"cgi2_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2120
2121#if defined(USE_4_CGI)
2122 {"cgi3_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2123 {"cgi3_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2124 {"cgi3_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2125 {"cgi3_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2126#if defined(USE_TIMERS)
2127 {"cgi3_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2128#endif
2129 {"cgi3_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2130
2131 {"cgi4_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2132 {"cgi4_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2133 {"cgi4_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2134 {"cgi4_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2135#if defined(USE_TIMERS)
2136 {"cgi4_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2137#endif
2138 {"cgi4_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2139
2140#endif
2141
2142 {"put_delete_auth_file", MG_CONFIG_TYPE_FILE, NULL},
2143 {"protect_uri", MG_CONFIG_TYPE_STRING_LIST, NULL},
2144 {"authentication_domain", MG_CONFIG_TYPE_STRING, "mydomain.com"},
2145 {"enable_auth_domain_check", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2146 {"ssi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"},
2147 {"enable_directory_listing", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2148 {"enable_webdav", MG_CONFIG_TYPE_BOOLEAN, "no"},
2149 {"global_auth_file", MG_CONFIG_TYPE_FILE, NULL},
2150 {"index_files",
2152#if defined(USE_LUA)
2153 "index.xhtml,index.html,index.htm,"
2154 "index.lp,index.lsp,index.lua,index.cgi,"
2155 "index.shtml,index.php"},
2156#else
2157 "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"},
2158#endif
2159 {"access_control_list", MG_CONFIG_TYPE_STRING_LIST, NULL},
2160 {"extra_mime_types", MG_CONFIG_TYPE_STRING_LIST, NULL},
2161 {"ssl_certificate", MG_CONFIG_TYPE_FILE, NULL},
2162 {"ssl_certificate_chain", MG_CONFIG_TYPE_FILE, NULL},
2163 {"url_rewrite_patterns", MG_CONFIG_TYPE_STRING_LIST, NULL},
2164 {"hide_files_patterns", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2165
2166 {"ssl_verify_peer", MG_CONFIG_TYPE_YES_NO_OPTIONAL, "no"},
2167 {"ssl_cache_timeout", MG_CONFIG_TYPE_NUMBER, "-1"},
2168
2169 {"ssl_ca_path", MG_CONFIG_TYPE_DIRECTORY, NULL},
2170 {"ssl_ca_file", MG_CONFIG_TYPE_FILE, NULL},
2171 {"ssl_verify_depth", MG_CONFIG_TYPE_NUMBER, "9"},
2172 {"ssl_default_verify_paths", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2173 {"ssl_cipher_list", MG_CONFIG_TYPE_STRING, NULL},
2174
2175 /* HTTP2 requires ALPN, and anyway TLS1.2 should be considered
2176 * as a minimum in 2020 */
2177 {"ssl_protocol_version", MG_CONFIG_TYPE_NUMBER, "4"},
2178
2179 {"ssl_short_trust", MG_CONFIG_TYPE_BOOLEAN, "no"},
2180
2181#if defined(USE_LUA)
2182 {"lua_preload_file", MG_CONFIG_TYPE_FILE, NULL},
2183 {"lua_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
2184 {"lua_server_page_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"},
2185#if defined(MG_EXPERIMENTAL_INTERFACES)
2186 {"lua_debug", MG_CONFIG_TYPE_STRING, NULL},
2187#endif
2188#endif
2189#if defined(USE_DUKTAPE)
2190 /* The support for duktape is still in alpha version state.
2191 * The name of this config option might change. */
2192 {"duktape_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"},
2193#endif
2194
2195#if defined(USE_WEBSOCKET)
2196 {"websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2197#endif
2198#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2199 {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
2200#endif
2201 {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"},
2202 {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"},
2203 {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"},
2204 {"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL},
2205#if !defined(NO_CACHING)
2206 {"static_file_max_age", MG_CONFIG_TYPE_NUMBER, "3600"},
2207 {"static_file_cache_control", MG_CONFIG_TYPE_STRING, NULL},
2208#endif
2209#if !defined(NO_SSL)
2210 {"strict_transport_security_max_age", MG_CONFIG_TYPE_NUMBER, NULL},
2211#endif
2212 {"additional_header", MG_CONFIG_TYPE_STRING_MULTILINE, NULL},
2213 {"allow_index_script_resource", MG_CONFIG_TYPE_BOOLEAN, "no"},
2214
2216
2217
2218/* Check if the config_options and the corresponding enum have compatible
2219 * sizes. */
2221 == (NUM_OPTIONS + 1),
2222 "config_options and enum not sync");
2223
2224
2226
2227
2229 /* Name/Pattern of the URI. */
2230 char *uri;
2231 size_t uri_len;
2232
2233 /* handler type */
2235
2236 /* Handler for http/https or requests. */
2238 unsigned int refcount;
2240
2241 /* Handler for ws/wss (websocket) requests. */
2246
2247 /* accepted subprotocols for ws/wss requests. */
2249
2250 /* Handler for authorization requests */
2252
2253 /* User supplied argument for the handler function. */
2254 void *cbdata;
2255
2256 /* next handler in a linked list */
2258};
2259
2260
2261enum {
2267
2268
2270 SSL_CTX *ssl_ctx; /* SSL context */
2271 char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */
2272 struct mg_handler_info *handlers; /* linked list of uri handlers */
2274
2275 /* Server nonce */
2276 uint64_t auth_nonce_mask; /* Mask for all nonce values */
2277 unsigned long nonce_count; /* Used nonces, used for authentication */
2278
2279#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2280 /* linked list of shared lua websockets */
2281 struct mg_shared_lua_websocket_list *shared_lua_websockets;
2282#endif
2283
2284 /* Linked list of domains */
2286};
2287
2288
2289/* Stop flag can be "volatile" or require a lock.
2290 * MSDN uses volatile for "Interlocked" operations, but also explicitly
2291 * states a read operation for int is always atomic. */
2292#if defined(STOP_FLAG_NEEDS_LOCK)
2293
2294typedef ptrdiff_t volatile stop_flag_t;
2295
2296static int
2298{
2299 stop_flag_t sf = mg_atomic_add((stop_flag_t *)f, 0);
2300 return (sf == 0);
2301}
2302
2303static int
2305{
2306 stop_flag_t sf = mg_atomic_add(f, 0);
2307 return (sf == 2);
2308}
2309
2310static void
2312{
2313 stop_flag_t sf;
2314 do {
2315 sf = mg_atomic_compare_and_swap(f, *f, v);
2316 } while (sf != v);
2317}
2318
2319#else /* STOP_FLAG_NEEDS_LOCK */
2320
2321typedef int volatile stop_flag_t;
2322#define STOP_FLAG_IS_ZERO(f) ((*(f)) == 0)
2323#define STOP_FLAG_IS_TWO(f) ((*(f)) == 2)
2324#define STOP_FLAG_ASSIGN(f, v) ((*(f)) = (v))
2325
2326#endif /* STOP_FLAG_NEEDS_LOCK */
2327
2328
2329#if !defined(NUM_WEBDAV_LOCKS)
2330#define NUM_WEBDAV_LOCKS 10
2331#endif
2332#if !defined(LOCK_DURATION_S)
2333#define LOCK_DURATION_S 60
2334#endif
2335
2336
2338 uint64_t locktime;
2339 char token[33];
2342};
2343
2344
2346
2347 /* Part 1 - Physical context:
2348 * This holds threads, ports, timeouts, ...
2349 * set for the entire server, independent from the
2350 * addressed hostname.
2351 */
2352
2353 /* Connection related */
2354 int context_type; /* See CONTEXT_* above */
2355
2359
2360 struct mg_connection *worker_connections; /* The connection struct, pre-
2361 * allocated for each worker */
2362
2363#if defined(USE_SERVER_STATS)
2364 volatile ptrdiff_t active_connections;
2365 volatile ptrdiff_t max_active_connections;
2366 volatile ptrdiff_t total_connections;
2367 volatile ptrdiff_t total_requests;
2368 volatile int64_t total_data_read;
2369 volatile int64_t total_data_written;
2370#endif
2371
2372 /* Thread related */
2373 stop_flag_t stop_flag; /* Should we stop event loop */
2374 pthread_mutex_t thread_mutex; /* Protects client_socks or queue */
2375
2376 pthread_t masterthreadid; /* The master thread ID */
2377 unsigned int
2378 cfg_worker_threads; /* The number of configured worker threads. */
2379 pthread_t *worker_threadids; /* The worker thread IDs */
2380 unsigned long starter_thread_idx; /* thread index which called mg_start */
2381
2382 /* Connection to thread dispatching */
2383#if defined(ALTERNATIVE_QUEUE)
2384 struct socket *client_socks;
2385 void **client_wait_events;
2386#else
2387 struct socket *squeue; /* Socket queue (sq) : accepted sockets waiting for a
2388 worker thread */
2389 volatile int sq_head; /* Head of the socket queue */
2390 volatile int sq_tail; /* Tail of the socket queue */
2391 pthread_cond_t sq_full; /* Signaled when socket is produced */
2392 pthread_cond_t sq_empty; /* Signaled when socket is consumed */
2393 volatile int sq_blocked; /* Status information: sq is full */
2394 int sq_size; /* No of elements in socket queue */
2395#if defined(USE_SERVER_STATS)
2396 int sq_max_fill;
2397#endif /* USE_SERVER_STATS */
2398#endif /* ALTERNATIVE_QUEUE */
2399
2400 /* Memory related */
2401 unsigned int max_request_size; /* The max request size */
2402
2403#if defined(USE_SERVER_STATS)
2404 struct mg_memory_stat ctx_memory;
2405#endif
2406
2407 /* WebDAV lock structures */
2409
2410 /* Operating system related */
2411 char *systemName; /* What operating system is running */
2412 time_t start_time; /* Server start time, used for authentication
2413 * and for diagnstics. */
2414
2415#if defined(USE_TIMERS)
2416 struct ttimers *timers;
2417#endif
2418
2419 /* Lua specific: Background operations and shared websockets */
2420#if defined(USE_LUA)
2421 void *lua_background_state; /* lua_State (here as void *) */
2422 pthread_mutex_t lua_bg_mutex; /* Protect background state */
2423 int lua_bg_log_available; /* Use Lua background state for access log */
2424#endif
2425
2426 /* Server nonce */
2427 pthread_mutex_t nonce_mutex; /* Protects ssl_ctx, handlers,
2428 * ssl_cert_last_mtime, nonce_count, and
2429 * next (linked list) */
2430
2431 /* Server callbacks */
2432 struct mg_callbacks callbacks; /* User-defined callback function */
2433 void *user_data; /* User-defined data */
2434
2435 /* Part 2 - Logical domain:
2436 * This holds hostname, TLS certificate, document root, ...
2437 * set for a domain hosted at the server.
2438 * There may be multiple domains hosted at one physical server.
2439 * The default domain "dd" is the first element of a list of
2440 * domains.
2441 */
2442 struct mg_domain_context dd; /* default domain */
2443};
2444
2445
2446#if defined(USE_SERVER_STATS)
2447static struct mg_memory_stat mg_common_memory = {0, 0, 0};
2448
2449static struct mg_memory_stat *
2450get_memory_stat(struct mg_context *ctx)
2451{
2452 if (ctx) {
2453 return &(ctx->ctx_memory);
2454 }
2455 return &mg_common_memory;
2456}
2457#endif
2458
2459enum {
2464
2465enum {
2470
2471
2472#if defined(USE_HTTP2)
2473#if !defined(HTTP2_DYN_TABLE_SIZE)
2474#define HTTP2_DYN_TABLE_SIZE (256)
2475#endif
2476
2477struct mg_http2_connection {
2478 uint32_t stream_id;
2479 uint32_t dyn_table_size;
2480 struct mg_header dyn_table[HTTP2_DYN_TABLE_SIZE];
2481};
2482#endif
2483
2484
2486 int connection_type; /* see CONNECTION_TYPE_* above */
2487 int protocol_type; /* see PROTOCOL_TYPE_*: 0=http/1.x, 1=ws, 2=http/2 */
2488 int request_state; /* 0: nothing sent, 1: header partially sent, 2: header
2489 fully sent */
2490#if defined(USE_HTTP2)
2491 struct mg_http2_connection http2;
2492#endif
2493
2496
2499
2500#if defined(USE_SERVER_STATS)
2501 int conn_state; /* 0 = undef, numerical value may change in different
2502 * versions. For the current definition, see
2503 * mg_get_connection_info_impl */
2504#endif
2505
2506 SSL *ssl; /* SSL descriptor */
2507 struct socket client; /* Connected client */
2508 time_t conn_birth_time; /* Time (wall clock) when connection was
2509 * established */
2510#if defined(USE_SERVER_STATS)
2511 time_t conn_close_time; /* Time (wall clock) when connection was
2512 * closed (or 0 if still open) */
2513 double processing_time; /* Processing time for one request. */
2514#endif
2515 struct timespec req_time; /* Time (since system start) when the request
2516 * was received */
2517 int64_t num_bytes_sent; /* Total bytes sent to client */
2518 int64_t content_len; /* How many bytes of content can be read
2519 * !is_chunked: Content-Length header value
2520 * or -1 (until connection closed,
2521 * not allowed for a request)
2522 * is_chunked: >= 0, appended gradually
2523 */
2524 int64_t consumed_content; /* How many bytes of content have been read */
2525 int is_chunked; /* Transfer-Encoding is chunked:
2526 * 0 = not chunked,
2527 * 1 = chunked, not yet, or some data read,
2528 * 2 = chunked, has error,
2529 * 3 = chunked, all data read except trailer,
2530 * 4 = chunked, all data read
2531 */
2532 char *buf; /* Buffer for received data */
2533 char *path_info; /* PATH_INFO part of the URL */
2534
2535 int must_close; /* 1 if connection must be closed */
2536 int accept_gzip; /* 1 if gzip encoding is accepted */
2537 int in_error_handler; /* 1 if in handler for user defined error
2538 * pages */
2539#if defined(USE_WEBSOCKET)
2540 int in_websocket_handling; /* 1 if in read_websocket */
2541#endif
2542#if defined(USE_ZLIB) && defined(USE_WEBSOCKET) \
2543 && defined(MG_EXPERIMENTAL_INTERFACES)
2544 /* Parameters for websocket data compression according to rfc7692 */
2545 int websocket_deflate_server_max_windows_bits;
2546 int websocket_deflate_client_max_windows_bits;
2547 int websocket_deflate_server_no_context_takeover;
2548 int websocket_deflate_client_no_context_takeover;
2549 int websocket_deflate_initialized;
2550 int websocket_deflate_flush;
2551 z_stream websocket_deflate_state;
2552 z_stream websocket_inflate_state;
2553#endif
2554 int handled_requests; /* Number of requests handled by this connection
2555 */
2556 int buf_size; /* Buffer size */
2557 int request_len; /* Size of the request + headers in a buffer */
2558 int data_len; /* Total size of data in a buffer */
2559 int status_code; /* HTTP reply status code, e.g. 200 */
2560 int throttle; /* Throttling, bytes/sec. <= 0 means no
2561 * throttle */
2562
2563 time_t last_throttle_time; /* Last time throttled data was sent */
2564 int last_throttle_bytes; /* Bytes sent this second */
2565 pthread_mutex_t mutex; /* Used by mg_(un)lock_connection to ensure
2566 * atomic transmissions for websockets */
2567#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2568 void *lua_websocket_state; /* Lua_State for a websocket connection */
2569#endif
2570
2571 void *tls_user_ptr; /* User defined pointer in thread local storage,
2572 * for quick access */
2573};
2574
2575
2576/* Directory entry */
2577struct de {
2580};
2581
2582
2583#define mg_cry_internal(conn, fmt, ...) \
2584 mg_cry_internal_wrap(conn, NULL, __func__, __LINE__, fmt, __VA_ARGS__)
2585
2586#define mg_cry_ctx_internal(ctx, fmt, ...) \
2587 mg_cry_internal_wrap(NULL, ctx, __func__, __LINE__, fmt, __VA_ARGS__)
2588
2589static void mg_cry_internal_wrap(const struct mg_connection *conn,
2590 struct mg_context *ctx,
2591 const char *func,
2592 unsigned line,
2593 const char *fmt,
2594 ...) PRINTF_ARGS(5, 6);
2595
2596
2597#if !defined(NO_THREAD_NAME)
2598#if defined(_WIN32) && defined(_MSC_VER)
2599/* Set the thread name for debugging purposes in Visual Studio
2600 * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
2601 */
2602#pragma pack(push, 8)
2603typedef struct tagTHREADNAME_INFO {
2604 DWORD dwType; /* Must be 0x1000. */
2605 LPCSTR szName; /* Pointer to name (in user addr space). */
2606 DWORD dwThreadID; /* Thread ID (-1=caller thread). */
2607 DWORD dwFlags; /* Reserved for future use, must be zero. */
2608} THREADNAME_INFO;
2609#pragma pack(pop)
2610
2611#elif defined(__linux__)
2612
2613#include <sys/prctl.h>
2614#include <sys/sendfile.h>
2615#if defined(ALTERNATIVE_QUEUE)
2616#include <sys/eventfd.h>
2617#endif /* ALTERNATIVE_QUEUE */
2618
2619
2620#if defined(ALTERNATIVE_QUEUE)
2621
2622static void *
2623event_create(void)
2624{
2625 int evhdl = eventfd(0, EFD_CLOEXEC);
2626 int *ret;
2627
2628 if (evhdl == -1) {
2629 /* Linux uses -1 on error, Windows NULL. */
2630 /* However, Linux does not return 0 on success either. */
2631 return 0;
2632 }
2633
2634 ret = (int *)mg_malloc(sizeof(int));
2635 if (ret) {
2636 *ret = evhdl;
2637 } else {
2638 (void)close(evhdl);
2639 }
2640
2641 return (void *)ret;
2642}
2643
2644
2645static int
2646event_wait(void *eventhdl)
2647{
2648 uint64_t u;
2649 int evhdl, s;
2650
2651 if (!eventhdl) {
2652 /* error */
2653 return 0;
2654 }
2655 evhdl = *(int *)eventhdl;
2656
2657 s = (int)read(evhdl, &u, sizeof(u));
2658 if (s != sizeof(u)) {
2659 /* error */
2660 return 0;
2661 }
2662 (void)u; /* the value is not required */
2663 return 1;
2664}
2665
2666
2667static int
2668event_signal(void *eventhdl)
2669{
2670 uint64_t u = 1;
2671 int evhdl, s;
2672
2673 if (!eventhdl) {
2674 /* error */
2675 return 0;
2676 }
2677 evhdl = *(int *)eventhdl;
2678
2679 s = (int)write(evhdl, &u, sizeof(u));
2680 if (s != sizeof(u)) {
2681 /* error */
2682 return 0;
2683 }
2684 return 1;
2685}
2686
2687
2688static void
2689event_destroy(void *eventhdl)
2690{
2691 int evhdl;
2692
2693 if (!eventhdl) {
2694 /* error */
2695 return;
2696 }
2697 evhdl = *(int *)eventhdl;
2698
2699 close(evhdl);
2700 mg_free(eventhdl);
2701}
2702
2703
2704#endif
2705
2706#endif
2707
2708
2709#if !defined(__linux__) && !defined(_WIN32) && defined(ALTERNATIVE_QUEUE)
2710
2711struct posix_event {
2712 pthread_mutex_t mutex;
2713 pthread_cond_t cond;
2714 int signaled;
2715};
2716
2717
2718static void *
2719event_create(void)
2720{
2721 struct posix_event *ret = mg_malloc(sizeof(struct posix_event));
2722 if (ret == 0) {
2723 /* out of memory */
2724 return 0;
2725 }
2726 if (0 != pthread_mutex_init(&(ret->mutex), NULL)) {
2727 /* pthread mutex not available */
2728 mg_free(ret);
2729 return 0;
2730 }
2731 if (0 != pthread_cond_init(&(ret->cond), NULL)) {
2732 /* pthread cond not available */
2733 pthread_mutex_destroy(&(ret->mutex));
2734 mg_free(ret);
2735 return 0;
2736 }
2737 ret->signaled = 0;
2738 return (void *)ret;
2739}
2740
2741
2742static int
2743event_wait(void *eventhdl)
2744{
2745 struct posix_event *ev = (struct posix_event *)eventhdl;
2746 pthread_mutex_lock(&(ev->mutex));
2747 while (!ev->signaled) {
2748 pthread_cond_wait(&(ev->cond), &(ev->mutex));
2749 }
2750 ev->signaled = 0;
2751 pthread_mutex_unlock(&(ev->mutex));
2752 return 1;
2753}
2754
2755
2756static int
2757event_signal(void *eventhdl)
2758{
2759 struct posix_event *ev = (struct posix_event *)eventhdl;
2760 pthread_mutex_lock(&(ev->mutex));
2761 pthread_cond_signal(&(ev->cond));
2762 ev->signaled = 1;
2763 pthread_mutex_unlock(&(ev->mutex));
2764 return 1;
2765}
2766
2767
2768static void
2769event_destroy(void *eventhdl)
2770{
2771 struct posix_event *ev = (struct posix_event *)eventhdl;
2772 pthread_cond_destroy(&(ev->cond));
2773 pthread_mutex_destroy(&(ev->mutex));
2774 mg_free(ev);
2775}
2776#endif
2777
2778
2779static void
2781{
2782 char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */
2783
2785 NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name);
2786
2787#if defined(_WIN32)
2788#if defined(_MSC_VER)
2789 /* Windows and Visual Studio Compiler */
2790 __try {
2791 THREADNAME_INFO info;
2792 info.dwType = 0x1000;
2793 info.szName = threadName;
2794 info.dwThreadID = ~0U;
2795 info.dwFlags = 0;
2796
2797 RaiseException(0x406D1388,
2798 0,
2799 sizeof(info) / sizeof(ULONG_PTR),
2800 (ULONG_PTR *)&info);
2801 } __except (EXCEPTION_EXECUTE_HANDLER) {
2802 }
2803#elif defined(__MINGW32__)
2804 /* No option known to set thread name for MinGW known */
2805#endif
2806#elif defined(_GNU_SOURCE) && defined(__GLIBC__) \
2807 && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12)))
2808 /* pthread_setname_np first appeared in glibc in version 2.12 */
2809#if defined(__MACH__) && defined(__APPLE__)
2810 /* OS X only current thread name can be changed */
2811 (void)pthread_setname_np(threadName);
2812#else
2813 (void)pthread_setname_np(pthread_self(), threadName);
2814#endif
2815#elif defined(__linux__)
2816 /* On Linux we can use the prctl function.
2817 * When building for Linux Standard Base (LSB) use
2818 * NO_THREAD_NAME. However, thread names are a big
2819 * help for debugging, so the stadard is to set them.
2820 */
2821 (void)prctl(PR_SET_NAME, threadName, 0, 0, 0);
2822#endif
2823}
2824#else /* !defined(NO_THREAD_NAME) */
2825static void
2826mg_set_thread_name(const char *threadName)
2827{
2828}
2829#endif
2830
2831
2832CIVETWEB_API const struct mg_option *
2834{
2835 return config_options;
2836}
2837
2838
2839/* Do not open file (unused) */
2840#define MG_FOPEN_MODE_NONE (0)
2841
2842/* Open file for read only access */
2843#define MG_FOPEN_MODE_READ (1)
2844
2845/* Open file for writing, create and overwrite */
2846#define MG_FOPEN_MODE_WRITE (2)
2847
2848/* Open file for writing, create and append */
2849#define MG_FOPEN_MODE_APPEND (4)
2850
2851
2852static int
2853is_file_opened(const struct mg_file_access *fileacc)
2854{
2855 if (!fileacc) {
2856 return 0;
2857 }
2858
2859 return (fileacc->fp != NULL);
2860}
2861
2862
2863#if !defined(NO_FILESYSTEMS)
2864static int mg_stat(const struct mg_connection *conn,
2865 const char *path,
2866 struct mg_file_stat *filep);
2867
2868
2869/* Reject files with special characters (for Windows) */
2870static int
2871mg_path_suspicious(const struct mg_connection *conn, const char *path)
2872{
2873 const uint8_t *c = (const uint8_t *)path;
2874 (void)conn; /* not used */
2875
2876 if ((c == NULL) || (c[0] == 0)) {
2877 /* Null pointer or empty path --> suspicious */
2878 return 1;
2879 }
2880
2881#if defined(_WIN32)
2882 while (*c) {
2883 if (*c < 32) {
2884 /* Control character */
2885 return 1;
2886 }
2887 if ((*c == '>') || (*c == '<') || (*c == '|')) {
2888 /* stdin/stdout redirection character */
2889 return 1;
2890 }
2891 if ((*c == '*') || (*c == '?')) {
2892 /* Wildcard character */
2893 return 1;
2894 }
2895 if (*c == '"') {
2896 /* Windows quotation */
2897 return 1;
2898 }
2899 c++;
2900 }
2901#endif
2902
2903 /* Nothing suspicious found */
2904 return 0;
2905}
2906
2907
2908/* mg_fopen will open a file either in memory or on the disk.
2909 * The input parameter path is a string in UTF-8 encoding.
2910 * The input parameter mode is MG_FOPEN_MODE_*
2911 * On success, fp will be set in the output struct mg_file.
2912 * All status members will also be set.
2913 * The function returns 1 on success, 0 on error. */
2914static int
2915mg_fopen(const struct mg_connection *conn,
2916 const char *path,
2917 int mode,
2918 struct mg_file *filep)
2919{
2920 int found;
2921
2922 if (!filep) {
2923 return 0;
2924 }
2925 filep->access.fp = NULL;
2926
2927 if (mg_path_suspicious(conn, path)) {
2928 return 0;
2929 }
2930
2931 /* filep is initialized in mg_stat: all fields with memset to,
2932 * some fields like size and modification date with values */
2933 found = mg_stat(conn, path, &(filep->stat));
2934
2935 if ((mode == MG_FOPEN_MODE_READ) && (!found)) {
2936 /* file does not exist and will not be created */
2937 return 0;
2938 }
2939
2940#if defined(_WIN32)
2941 {
2942 wchar_t wbuf[UTF16_PATH_MAX];
2943 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
2944 switch (mode) {
2945 case MG_FOPEN_MODE_READ:
2946 filep->access.fp = _wfopen(wbuf, L"rb");
2947 break;
2949 filep->access.fp = _wfopen(wbuf, L"wb");
2950 break;
2952 filep->access.fp = _wfopen(wbuf, L"ab");
2953 break;
2954 }
2955 }
2956#else
2957 /* Linux et al already use unicode. No need to convert. */
2958 switch (mode) {
2959 case MG_FOPEN_MODE_READ:
2960 filep->access.fp = fopen(path, "r");
2961 break;
2963 filep->access.fp = fopen(path, "w");
2964 break;
2966 filep->access.fp = fopen(path, "a");
2967 break;
2968 }
2969
2970#endif
2971 if (!found) {
2972 /* File did not exist before fopen was called.
2973 * Maybe it has been created now. Get stat info
2974 * like creation time now. */
2975 found = mg_stat(conn, path, &(filep->stat));
2976 (void)found;
2977 }
2978
2979 /* return OK if file is opened */
2980 return (filep->access.fp != NULL);
2981}
2982
2983
2984/* return 0 on success, just like fclose */
2985static int
2987{
2988 int ret = -1;
2989 if (fileacc != NULL) {
2990 if (fileacc->fp != NULL) {
2991 ret = fclose(fileacc->fp);
2992 }
2993 /* reset all members of fileacc */
2994 memset(fileacc, 0, sizeof(*fileacc));
2995 }
2996 return ret;
2997}
2998#endif /* NO_FILESYSTEMS */
2999
3000
3001static void
3002mg_strlcpy(char *dst, const char *src, size_t n)
3003{
3004 for (; *src != '\0' && n > 1; n--) {
3005 *dst++ = *src++;
3006 }
3007 *dst = '\0';
3008}
3009
3010
3011static int
3012lowercase(const char *s)
3013{
3014 return tolower((unsigned char)*s);
3015}
3016
3017
3018CIVETWEB_API int
3019mg_strncasecmp(const char *s1, const char *s2, size_t len)
3020{
3021 int diff = 0;
3022
3023 if (len > 0) {
3024 do {
3025 diff = lowercase(s1++) - lowercase(s2++);
3026 } while (diff == 0 && s1[-1] != '\0' && --len > 0);
3027 }
3028
3029 return diff;
3030}
3031
3032
3033CIVETWEB_API int
3034mg_strcasecmp(const char *s1, const char *s2)
3035{
3036 int diff;
3037
3038 do {
3039 diff = lowercase(s1++) - lowercase(s2++);
3040 } while (diff == 0 && s1[-1] != '\0');
3041
3042 return diff;
3043}
3044
3045
3046static char *
3047mg_strndup_ctx(const char *ptr, size_t len, struct mg_context *ctx)
3048{
3049 char *p;
3050 (void)ctx; /* Avoid Visual Studio warning if USE_SERVER_STATS is not
3051 * defined */
3052
3053 if ((p = (char *)mg_malloc_ctx(len + 1, ctx)) != NULL) {
3054 mg_strlcpy(p, ptr, len + 1);
3055 }
3056
3057 return p;
3058}
3059
3060
3061static char *
3062mg_strdup_ctx(const char *str, struct mg_context *ctx)
3063{
3064 return mg_strndup_ctx(str, strlen(str), ctx);
3065}
3066
3067static char *
3068mg_strdup(const char *str)
3069{
3070 return mg_strndup_ctx(str, strlen(str), NULL);
3071}
3072
3073
3074static const char *
3075mg_strcasestr(const char *big_str, const char *small_str)
3076{
3077 size_t i, big_len = strlen(big_str), small_len = strlen(small_str);
3078
3079 if (big_len >= small_len) {
3080 for (i = 0; i <= (big_len - small_len); i++) {
3081 if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) {
3082 return big_str + i;
3083 }
3084 }
3085 }
3086
3087 return NULL;
3088}
3089
3090
3091/* Return null terminated string of given maximum length.
3092 * Report errors if length is exceeded. */
3093static void
3094mg_vsnprintf(const struct mg_connection *conn,
3095 int *truncated,
3096 char *buf,
3097 size_t buflen,
3098 const char *fmt,
3099 va_list ap)
3100{
3101 int n, ok;
3102
3103 if (buflen == 0) {
3104 if (truncated) {
3105 *truncated = 1;
3106 }
3107 return;
3108 }
3109
3110#if defined(__clang__)
3111#pragma clang diagnostic push
3112#pragma clang diagnostic ignored "-Wformat-nonliteral"
3113 /* Using fmt as a non-literal is intended here, since it is mostly called
3114 * indirectly by mg_snprintf */
3115#endif
3116
3117 n = (int)vsnprintf_impl(buf, buflen, fmt, ap);
3118 ok = (n >= 0) && ((size_t)n < buflen);
3119
3120#if defined(__clang__)
3121#pragma clang diagnostic pop
3122#endif
3123
3124 if (ok) {
3125 if (truncated) {
3126 *truncated = 0;
3127 }
3128 } else {
3129 if (truncated) {
3130 *truncated = 1;
3131 }
3132 mg_cry_internal(conn,
3133 "truncating vsnprintf buffer: [%.*s]",
3134 (int)((buflen > 200) ? 200 : (buflen - 1)),
3135 buf);
3136 n = (int)buflen - 1;
3137 }
3138 buf[n] = '\0';
3139}
3140
3141
3142static void
3143mg_snprintf(const struct mg_connection *conn,
3144 int *truncated,
3145 char *buf,
3146 size_t buflen,
3147 const char *fmt,
3148 ...)
3149{
3150 va_list ap;
3151
3152 va_start(ap, fmt);
3153 mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap);
3154 va_end(ap);
3155}
3156
3157
3158static int
3160{
3161 int i;
3162
3163 for (i = 0; config_options[i].name != NULL; i++) {
3164 if (strcmp(config_options[i].name, name) == 0) {
3165 return i;
3166 }
3167 }
3168 return -1;
3169}
3170
3171
3172CIVETWEB_API const char *
3173mg_get_option(const struct mg_context *ctx, const char *name)
3174{
3175 int i;
3176 if ((i = get_option_index(name)) == -1) {
3177 return NULL;
3178 } else if (!ctx || ctx->dd.config[i] == NULL) {
3179 return "";
3180 } else {
3181 return ctx->dd.config[i];
3182 }
3183}
3184
3185#define mg_get_option DO_NOT_USE_THIS_FUNCTION_INTERNALLY__access_directly
3186
3187CIVETWEB_API struct mg_context *
3189{
3190 return (conn == NULL) ? (struct mg_context *)NULL : (conn->phys_ctx);
3191}
3192
3193
3194CIVETWEB_API void *
3196{
3197 return (ctx == NULL) ? NULL : ctx->user_data;
3198}
3199
3200
3201CIVETWEB_API void *
3203{
3204 return mg_get_user_data(mg_get_context(conn));
3205}
3206
3207
3208CIVETWEB_API void *
3210{
3211 /* both methods should return the same pointer */
3212 if (conn) {
3213 /* quick access, in case conn is known */
3214 return conn->tls_user_ptr;
3215 } else {
3216 /* otherwise get pointer from thread local storage (TLS) */
3217 struct mg_workerTLS *tls =
3218 (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
3219 return tls->user_ptr;
3220 }
3221}
3222
3223
3224CIVETWEB_API void
3225mg_set_user_connection_data(const struct mg_connection *const_conn, void *data)
3226{
3227 if (const_conn != NULL) {
3228 /* Const cast, since "const struct mg_connection *" does not mean
3229 * the connection object is not modified. Here "const" is used,
3230 * to indicate mg_read/mg_write/mg_send/.. must not be called. */
3231 struct mg_connection *conn = (struct mg_connection *)const_conn;
3232 conn->request_info.conn_data = data;
3233 }
3234}
3235
3236
3237CIVETWEB_API void *
3239{
3240 if (conn != NULL) {
3241 return conn->request_info.conn_data;
3242 }
3243 return NULL;
3244}
3245
3246
3247CIVETWEB_API int
3249 int size,
3250 struct mg_server_port *ports)
3251{
3252 int i, cnt = 0;
3253
3254 if (size <= 0) {
3255 return -1;
3256 }
3257 memset(ports, 0, sizeof(*ports) * (size_t)size);
3258 if (!ctx) {
3259 return -1;
3260 }
3261 if (!ctx->listening_sockets) {
3262 return -1;
3263 }
3264
3265 for (i = 0; (i < size) && (i < (int)ctx->num_listening_sockets); i++) {
3266
3267 ports[cnt].port =
3268 ntohs(USA_IN_PORT_UNSAFE(&(ctx->listening_sockets[i].lsa)));
3269 ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
3270 ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
3271
3272 if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
3273 /* IPv4 */
3274 ports[cnt].protocol = 1;
3275 cnt++;
3276 } else if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6) {
3277 /* IPv6 */
3278 ports[cnt].protocol = 3;
3279 cnt++;
3280 }
3281 }
3282
3283 return cnt;
3284}
3285
3286
3287#if defined(USE_X_DOM_SOCKET) && !defined(UNIX_DOMAIN_SOCKET_SERVER_NAME)
3288#define UNIX_DOMAIN_SOCKET_SERVER_NAME "*"
3289#endif
3290
3291static void
3292sockaddr_to_string(char *buf, size_t len, const union usa *usa)
3293{
3294 buf[0] = '\0';
3295
3296 if (!usa) {
3297 return;
3298 }
3299
3300 if (usa->sa.sa_family == AF_INET) {
3301 getnameinfo(&usa->sa,
3302 sizeof(usa->sin),
3303 buf,
3304 (unsigned)len,
3305 NULL,
3306 0,
3307 NI_NUMERICHOST);
3308 }
3309#if defined(USE_IPV6)
3310 else if (usa->sa.sa_family == AF_INET6) {
3311 getnameinfo(&usa->sa,
3312 sizeof(usa->sin6),
3313 buf,
3314 (unsigned)len,
3315 NULL,
3316 0,
3317 NI_NUMERICHOST);
3318 }
3319#endif
3320#if defined(USE_X_DOM_SOCKET)
3321 else if (usa->sa.sa_family == AF_UNIX) {
3322 /* TODO: Define a remote address for unix domain sockets.
3323 * This code will always return "localhost", identical to http+tcp:
3324 getnameinfo(&usa->sa,
3325 sizeof(usa->sun),
3326 buf,
3327 (unsigned)len,
3328 NULL,
3329 0,
3330 NI_NUMERICHOST);
3331 */
3332 mg_strlcpy(buf, UNIX_DOMAIN_SOCKET_SERVER_NAME, len);
3333 }
3334#endif
3335}
3336
3337
3338/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be
3339 * included in all responses other than 100, 101, 5xx. */
3340static void
3341gmt_time_string(char *buf, size_t buf_len, time_t *t)
3342{
3343#if !defined(REENTRANT_TIME)
3344 struct tm *tm;
3345
3346 tm = ((t != NULL) ? gmtime(t) : NULL);
3347 if (tm != NULL) {
3348#else
3349 struct tm _tm;
3350 struct tm *tm = &_tm;
3351
3352 if (t != NULL) {
3353 gmtime_r(t, tm);
3354#endif
3355 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm);
3356 } else {
3357 mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
3358 }
3359}
3360
3361
3362/* difftime for struct timespec. Return value is in seconds. */
3363static double
3364mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before)
3365{
3366 return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9
3367 + (double)(ts_now->tv_sec - ts_before->tv_sec);
3368}
3369
3370
3371#if defined(MG_EXTERNAL_FUNCTION_mg_cry_internal_impl)
3372static void mg_cry_internal_impl(const struct mg_connection *conn,
3373 const char *func,
3374 unsigned line,
3375 const char *fmt,
3376 va_list ap);
3377#include "external_mg_cry_internal_impl.inl"
3378#elif !defined(NO_FILESYSTEMS)
3379
3380/* Print error message to the opened error log stream. */
3381static void
3383 const char *func,
3384 unsigned line,
3385 const char *fmt,
3386 va_list ap)
3387{
3388 char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
3389 struct mg_file fi;
3390 time_t timestamp;
3391
3392 /* Unused, in the RELEASE build */
3393 (void)func;
3394 (void)line;
3395
3396#if defined(GCC_DIAGNOSTIC)
3397#pragma GCC diagnostic push
3398#pragma GCC diagnostic ignored "-Wformat-nonliteral"
3399#endif
3400
3401 IGNORE_UNUSED_RESULT(vsnprintf_impl(buf, sizeof(buf), fmt, ap));
3402
3403#if defined(GCC_DIAGNOSTIC)
3404#pragma GCC diagnostic pop
3405#endif
3406
3407 buf[sizeof(buf) - 1] = 0;
3408
3409 DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf);
3410
3411 if (!conn) {
3412 puts(buf);
3413 return;
3414 }
3415
3416 /* Do not lock when getting the callback value, here and below.
3417 * I suppose this is fine, since function cannot disappear in the
3418 * same way string option can. */
3419 if ((conn->phys_ctx->callbacks.log_message == NULL)
3420 || (conn->phys_ctx->callbacks.log_message(conn, buf) == 0)) {
3421
3422 if (conn->dom_ctx->config[ERROR_LOG_FILE] != NULL) {
3423 if (mg_fopen(conn,
3426 &fi)
3427 == 0) {
3428 fi.access.fp = NULL;
3429 }
3430 } else {
3431 fi.access.fp = NULL;
3432 }
3433
3434 if (fi.access.fp != NULL) {
3435 flockfile(fi.access.fp);
3436 timestamp = time(NULL);
3437
3438 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
3439 fprintf(fi.access.fp,
3440 "[%010lu] [error] [client %s] ",
3441 (unsigned long)timestamp,
3442 src_addr);
3443
3444 if (conn->request_info.request_method != NULL) {
3445 fprintf(fi.access.fp,
3446 "%s %s: ",
3450 : "");
3451 }
3452
3453 fprintf(fi.access.fp, "%s", buf);
3454 fputc('\n', fi.access.fp);
3455 fflush(fi.access.fp);
3456 funlockfile(fi.access.fp);
3457 (void)mg_fclose(&fi.access); /* Ignore errors. We can't call
3458 * mg_cry here anyway ;-) */
3459 }
3460 }
3461}
3462#else
3463#error Must either enable filesystems or provide a custom mg_cry_internal_impl implementation
3464#endif /* Externally provided function */
3465
3466
3467/* Construct fake connection structure. Used for logging, if connection
3468 * is not applicable at the moment of logging. */
3469static struct mg_connection *
3471{
3472 static const struct mg_connection conn_zero = {0};
3473 *fc = conn_zero;
3474 fc->phys_ctx = ctx;
3475 fc->dom_ctx = &(ctx->dd);
3476 return fc;
3477}
3478
3479
3480static void
3482 struct mg_context *ctx,
3483 const char *func,
3484 unsigned line,
3485 const char *fmt,
3486 ...)
3487{
3488 va_list ap;
3489 va_start(ap, fmt);
3490 if (!conn && ctx) {
3491 struct mg_connection fc;
3492 mg_cry_internal_impl(fake_connection(&fc, ctx), func, line, fmt, ap);
3493 } else {
3494 mg_cry_internal_impl(conn, func, line, fmt, ap);
3495 }
3496 va_end(ap);
3497}
3498
3499
3500CIVETWEB_API void
3501mg_cry(const struct mg_connection *conn, const char *fmt, ...)
3502{
3503 va_list ap;
3504 va_start(ap, fmt);
3505 mg_cry_internal_impl(conn, "user", 0, fmt, ap);
3506 va_end(ap);
3507}
3508
3509
3510#define mg_cry DO_NOT_USE_THIS_FUNCTION__USE_mg_cry_internal
3511
3512
3513CIVETWEB_API const char *
3515{
3516 return CIVETWEB_VERSION;
3517}
3518
3519
3520CIVETWEB_API const struct mg_request_info *
3522{
3523 if (!conn) {
3524 return NULL;
3525 }
3526#if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE)
3528 char txt[16];
3529 struct mg_workerTLS *tls =
3530 (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
3531
3532 sprintf(txt, "%03i", conn->response_info.status_code);
3533 if (strlen(txt) == 3) {
3534 memcpy(tls->txtbuf, txt, 4);
3535 } else {
3536 strcpy(tls->txtbuf, "ERR");
3537 }
3538
3539 ((struct mg_connection *)conn)->request_info.local_uri =
3540 tls->txtbuf; /* use thread safe buffer */
3541 ((struct mg_connection *)conn)->request_info.local_uri_raw =
3542 tls->txtbuf; /* use the same thread safe buffer */
3543 ((struct mg_connection *)conn)->request_info.request_uri =
3544 tls->txtbuf; /* use the same thread safe buffer */
3545
3546 ((struct mg_connection *)conn)->request_info.num_headers =
3548 memcpy(((struct mg_connection *)conn)->request_info.http_headers,
3550 sizeof(conn->response_info.http_headers));
3551 } else
3552#endif
3554 return NULL;
3555 }
3556 return &conn->request_info;
3557}
3558
3559
3560CIVETWEB_API const struct mg_response_info *
3562{
3563 if (!conn) {
3564 return NULL;
3565 }
3567 return NULL;
3568 }
3569 return &conn->response_info;
3570}
3571
3572
3573static const char *
3575{
3576#if defined(__clang__)
3577#pragma clang diagnostic push
3578#pragma clang diagnostic ignored "-Wunreachable-code"
3579 /* Depending on USE_WEBSOCKET and NO_SSL, some oft the protocols might be
3580 * not supported. Clang raises an "unreachable code" warning for parts of ?:
3581 * unreachable, but splitting into four different #ifdef clauses here is
3582 * more complicated.
3583 */
3584#endif
3585
3586 const struct mg_request_info *ri = &conn->request_info;
3587
3588 const char *proto = ((conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)
3589 ? (ri->is_ssl ? "wss" : "ws")
3590 : (ri->is_ssl ? "https" : "http"));
3591
3592 return proto;
3593
3594#if defined(__clang__)
3595#pragma clang diagnostic pop
3596#endif
3597}
3598
3599
3600static int
3602 char *buf,
3603 size_t buflen,
3604 const char *define_proto,
3605 int define_port,
3606 const char *define_uri)
3607{
3608 if ((buflen < 1) || (buf == 0) || (conn == 0)) {
3609 return -1;
3610 } else {
3611 int i, j;
3612 int truncated = 0;
3613 const struct mg_request_info *ri = &conn->request_info;
3614
3615 const char *proto =
3616 (define_proto != NULL) ? define_proto : get_proto_name(conn);
3617 const char *uri =
3618 (define_uri != NULL)
3619 ? define_uri
3620 : ((ri->request_uri != NULL) ? ri->request_uri : ri->local_uri);
3621 int port = (define_port > 0) ? define_port : ri->server_port;
3622 int default_port = 80;
3623 char *uri_encoded;
3624 size_t uri_encoded_len;
3625
3626 if (uri == NULL) {
3627 return -1;
3628 }
3629
3630 uri_encoded_len = strlen(uri) * 3 + 1;
3631 uri_encoded = (char *)mg_malloc_ctx(uri_encoded_len, conn->phys_ctx);
3632 if (uri_encoded == NULL) {
3633 return -1;
3634 }
3635 mg_url_encode(uri, uri_encoded, uri_encoded_len);
3636
3637 /* Directory separator should be preserved. */
3638 for (i = j = 0; uri_encoded[i]; j++) {
3639 if (!strncmp(uri_encoded + i, "%2f", 3)) {
3640 uri_encoded[j] = '/';
3641 i += 3;
3642 } else {
3643 uri_encoded[j] = uri_encoded[i++];
3644 }
3645 }
3646 uri_encoded[j] = '\0';
3647
3648#if defined(USE_X_DOM_SOCKET)
3649 if (conn->client.lsa.sa.sa_family == AF_UNIX) {
3650 /* TODO: Define and document a link for UNIX domain sockets. */
3651 /* There seems to be no official standard for this.
3652 * Common uses seem to be "httpunix://", "http.unix://" or
3653 * "http+unix://" as a protocol definition string, followed by
3654 * "localhost" or "127.0.0.1" or "/tmp/unix/path" or
3655 * "%2Ftmp%2Funix%2Fpath" (url % encoded) or
3656 * "localhost:%2Ftmp%2Funix%2Fpath" (domain socket path as port) or
3657 * "" (completely skipping the server name part). In any case, the
3658 * last part is the server local path. */
3659 const char *server_name = UNIX_DOMAIN_SOCKET_SERVER_NAME;
3660 mg_snprintf(conn,
3661 &truncated,
3662 buf,
3663 buflen,
3664 "%s.unix://%s%s",
3665 proto,
3666 server_name,
3667 ri->local_uri);
3668 default_port = 0;
3669 mg_free(uri_encoded);
3670 return 0;
3671 }
3672#endif
3673
3674 if (define_proto) {
3675 /* If we got a protocol name, use the default port accordingly. */
3676 if ((0 == strcmp(define_proto, "https"))
3677 || (0 == strcmp(define_proto, "wss"))) {
3678 default_port = 443;
3679 }
3680 } else if (ri->is_ssl) {
3681 /* If we did not get a protocol name, use TLS as default if it is
3682 * already used. */
3683 default_port = 443;
3684 }
3685
3686 {
3687#if defined(USE_IPV6)
3688 int is_ipv6 = (conn->client.lsa.sa.sa_family == AF_INET6);
3689#endif
3690 int auth_domain_check_enabled =
3692 && (!mg_strcasecmp(
3693 conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK], "yes"));
3694
3695 const char *server_domain =
3697
3698 char portstr[16];
3699 char server_ip[48];
3700
3701 if (port != default_port) {
3702 sprintf(portstr, ":%u", (unsigned)port);
3703 } else {
3704 portstr[0] = 0;
3705 }
3706
3707 if (!auth_domain_check_enabled || !server_domain) {
3708
3709 sockaddr_to_string(server_ip,
3710 sizeof(server_ip),
3711 &conn->client.lsa);
3712
3713 server_domain = server_ip;
3714 }
3715
3716 mg_snprintf(conn,
3717 &truncated,
3718 buf,
3719 buflen,
3720#if defined(USE_IPV6)
3721 "%s://%s%s%s%s%s",
3722 proto,
3723 (is_ipv6 && (server_domain == server_ip)) ? "[" : "",
3724 server_domain,
3725 (is_ipv6 && (server_domain == server_ip)) ? "]" : "",
3726#else
3727 "%s://%s%s%s",
3728 proto,
3729 server_domain,
3730#endif
3731 portstr,
3732 uri_encoded);
3733
3734 mg_free(uri_encoded);
3735 if (truncated) {
3736 return -1;
3737 }
3738 return 0;
3739 }
3740 }
3741}
3742
3743
3744CIVETWEB_API int
3745mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen)
3746{
3747 return mg_construct_local_link(conn, buf, buflen, NULL, -1, NULL);
3748}
3749
3750
3751/* Skip the characters until one of the delimiters characters found.
3752 * 0-terminate resulting word. Skip the delimiter and following whitespaces.
3753 * Advance pointer to buffer to the next word. Return found 0-terminated
3754 * word.
3755 * Delimiters can be quoted with quotechar. */
3756static char *
3757skip_quoted(char **buf,
3758 const char *delimiters,
3759 const char *whitespace,
3760 char quotechar)
3761{
3762 char *p, *begin_word, *end_word, *end_whitespace;
3763
3764 begin_word = *buf;
3765 end_word = begin_word + strcspn(begin_word, delimiters);
3766
3767 /* Check for quotechar */
3768 if (end_word > begin_word) {
3769 p = end_word - 1;
3770 while (*p == quotechar) {
3771 /* While the delimiter is quoted, look for the next delimiter. */
3772 /* This happens, e.g., in calls from parse_auth_header,
3773 * if the user name contains a " character. */
3774
3775 /* If there is anything beyond end_word, copy it. */
3776 if (*end_word != '\0') {
3777 size_t end_off = strcspn(end_word + 1, delimiters);
3778 memmove(p, end_word, end_off + 1);
3779 p += end_off; /* p must correspond to end_word - 1 */
3780 end_word += end_off + 1;
3781 } else {
3782 *p = '\0';
3783 break;
3784 }
3785 }
3786 for (p++; p < end_word; p++) {
3787 *p = '\0';
3788 }
3789 }
3790
3791 if (*end_word == '\0') {
3792 *buf = end_word;
3793 } else {
3794
3795#if defined(GCC_DIAGNOSTIC)
3796 /* Disable spurious conversion warning for GCC */
3797#pragma GCC diagnostic push
3798#pragma GCC diagnostic ignored "-Wsign-conversion"
3799#endif /* defined(GCC_DIAGNOSTIC) */
3800
3801 end_whitespace = end_word + strspn(&end_word[1], whitespace) + 1;
3802
3803#if defined(GCC_DIAGNOSTIC)
3804#pragma GCC diagnostic pop
3805#endif /* defined(GCC_DIAGNOSTIC) */
3806
3807 for (p = end_word; p < end_whitespace; p++) {
3808 *p = '\0';
3809 }
3810
3811 *buf = end_whitespace;
3812 }
3813
3814 return begin_word;
3815}
3816
3817
3818/* Return HTTP header value, or NULL if not found. */
3819static const char *
3820get_header(const struct mg_header *hdr, int num_hdr, const char *name)
3821{
3822 int i;
3823 for (i = 0; i < num_hdr; i++) {
3824 if (!mg_strcasecmp(name, hdr[i].name)) {
3825 return hdr[i].value;
3826 }
3827 }
3828
3829 return NULL;
3830}
3831
3832
3833/* Retrieve requested HTTP header multiple values, and return the number of
3834 * found occurrences */
3835static int
3837 const char *name,
3838 const char **output,
3839 int output_max_size)
3840{
3841 int i;
3842 int cnt = 0;
3843 if (ri) {
3844 for (i = 0; i < ri->num_headers && cnt < output_max_size; i++) {
3845 if (!mg_strcasecmp(name, ri->http_headers[i].name)) {
3846 output[cnt++] = ri->http_headers[i].value;
3847 }
3848 }
3849 }
3850 return cnt;
3851}
3852
3853
3854CIVETWEB_API const char *
3855mg_get_header(const struct mg_connection *conn, const char *name)
3856{
3857 if (!conn) {
3858 return NULL;
3859 }
3860
3864 name);
3865 }
3869 name);
3870 }
3871 return NULL;
3872}
3873
3874
3875static const char *
3877{
3878 if (!conn) {
3879 return NULL;
3880 }
3881
3883 return conn->request_info.http_version;
3884 }
3886 return conn->response_info.http_version;
3887 }
3888 return NULL;
3889}
3890
3891
3892/* A helper function for traversing a comma separated list of values.
3893 * It returns a list pointer shifted to the next value, or NULL if the end
3894 * of the list found.
3895 * Value is stored in val vector. If value has form "x=y", then eq_val
3896 * vector is initialized to point to the "y" part, and val vector length
3897 * is adjusted to point only to "x". */
3898static const char *
3899next_option(const char *list, struct vec *val, struct vec *eq_val)
3900{
3901 int end;
3902
3903reparse:
3904 if (val == NULL || list == NULL || *list == '\0') {
3905 /* End of the list */
3906 return NULL;
3907 }
3908
3909 /* Skip over leading LWS */
3910 while (*list == ' ' || *list == '\t')
3911 list++;
3912
3913 val->ptr = list;
3914 if ((list = strchr(val->ptr, ',')) != NULL) {
3915 /* Comma found. Store length and shift the list ptr */
3916 val->len = ((size_t)(list - val->ptr));
3917 list++;
3918 } else {
3919 /* This value is the last one */
3920 list = val->ptr + strlen(val->ptr);
3921 val->len = ((size_t)(list - val->ptr));
3922 }
3923
3924 /* Adjust length for trailing LWS */
3925 end = (int)val->len - 1;
3926 while (end >= 0 && ((val->ptr[end] == ' ') || (val->ptr[end] == '\t')))
3927 end--;
3928 val->len = (size_t)(end) + (size_t)(1);
3929
3930 if (val->len == 0) {
3931 /* Ignore any empty entries. */
3932 goto reparse;
3933 }
3934
3935 if (eq_val != NULL) {
3936 /* Value has form "x=y", adjust pointers and lengths
3937 * so that val points to "x", and eq_val points to "y". */
3938 eq_val->len = 0;
3939 eq_val->ptr = (const char *)memchr(val->ptr, '=', val->len);
3940 if (eq_val->ptr != NULL) {
3941 eq_val->ptr++; /* Skip over '=' character */
3942 eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len;
3943 val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1;
3944 }
3945 }
3946
3947 return list;
3948}
3949
3950
3951/* A helper function for checking if a comma separated list of values
3952 * contains
3953 * the given option (case insensitvely).
3954 * 'header' can be NULL, in which case false is returned. */
3955static int
3956header_has_option(const char *header, const char *option)
3957{
3958 struct vec opt_vec;
3959 struct vec eq_vec;
3960
3961 DEBUG_ASSERT(option != NULL);
3962 DEBUG_ASSERT(option[0] != '\0');
3963
3964 while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) {
3965 if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0)
3966 return 1;
3967 }
3968
3969 return 0;
3970}
3971
3972
3973/* Sorting function implemented in a separate file */
3974#include "sort.inl"
3975
3976/* Pattern matching has been reimplemented in a new file */
3977#include "match.inl"
3978
3979
3980/* HTTP 1.1 assumes keep alive if "Connection:" header is not set
3981 * This function must tolerate situations when connection info is not
3982 * set up, for example if request parsing failed. */
3983static int
3985{
3986 const char *http_version;
3987 const char *header;
3988
3989 /* First satisfy needs of the server */
3990 if ((conn == NULL) || conn->must_close) {
3991 /* Close, if civetweb framework needs to close */
3992 return 0;
3993 }
3994
3995 if (mg_strcasecmp(conn->dom_ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0) {
3996 /* Close, if keep alive is not enabled */
3997 return 0;
3998 }
3999
4000 /* Check explicit wish of the client */
4001 header = mg_get_header(conn, "Connection");
4002 if (header) {
4003 /* If there is a connection header from the client, obey */
4004 if (header_has_option(header, "keep-alive")) {
4005 return 1;
4006 }
4007 return 0;
4008 }
4009
4010 /* Use default of the standard */
4011 http_version = get_http_version(conn);
4012 if (http_version && (0 == strcmp(http_version, "1.1"))) {
4013 /* HTTP 1.1 default is keep alive */
4014 return 1;
4015 }
4016
4017 /* HTTP 1.0 (and earlier) default is to close the connection */
4018 return 0;
4019}
4020
4021
4022static int
4024{
4025 if (!conn || !conn->dom_ctx) {
4026 return 0;
4027 }
4028
4029 return (mg_strcasecmp(conn->dom_ctx->config[DECODE_URL], "yes") == 0);
4030}
4031
4032
4033static int
4035{
4036 if (!conn || !conn->dom_ctx) {
4037 return 0;
4038 }
4039
4040 return (mg_strcasecmp(conn->dom_ctx->config[DECODE_QUERY_STRING], "yes")
4041 == 0);
4042}
4043
4044
4045static const char *
4047{
4048 return should_keep_alive(conn) ? "keep-alive" : "close";
4049}
4050
4051
4052#include "response.inl"
4053
4054
4055static void
4057{
4058 /* Send all current and obsolete cache opt-out directives. */
4060 "Cache-Control",
4061 "no-cache, no-store, "
4062 "must-revalidate, private, max-age=0",
4063 -1);
4064 mg_response_header_add(conn, "Expires", "0", -1);
4065
4066 if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
4067 /* Obsolete, but still send it for HTTP/1.0 */
4068 mg_response_header_add(conn, "Pragma", "no-cache", -1);
4069 }
4070}
4071
4072
4073static void
4075{
4076#if !defined(NO_CACHING)
4077 int max_age;
4078 char val[64];
4079
4080 const char *cache_control =
4082
4083 /* If there is a full cache-control option configured,0 use it */
4084 if (cache_control != NULL) {
4085 mg_response_header_add(conn, "Cache-Control", cache_control, -1);
4086 return;
4087 }
4088
4089 /* Read the server config to check how long a file may be cached.
4090 * The configuration is in seconds. */
4091 max_age = atoi(conn->dom_ctx->config[STATIC_FILE_MAX_AGE]);
4092 if (max_age <= 0) {
4093 /* 0 means "do not cache". All values <0 are reserved
4094 * and may be used differently in the future. */
4095 /* If a file should not be cached, do not only send
4096 * max-age=0, but also pragmas and Expires headers. */
4098 return;
4099 }
4100
4101 /* Use "Cache-Control: max-age" instead of "Expires" header.
4102 * Reason: see https://www.mnot.net/blog/2007/05/15/expires_max-age */
4103 /* See also https://www.mnot.net/cache_docs/ */
4104 /* According to RFC 2616, Section 14.21, caching times should not exceed
4105 * one year. A year with 365 days corresponds to 31536000 seconds, a
4106 * leap
4107 * year to 31622400 seconds. For the moment, we just send whatever has
4108 * been configured, still the behavior for >1 year should be considered
4109 * as undefined. */
4111 conn, NULL, val, sizeof(val), "max-age=%lu", (unsigned long)max_age);
4112 mg_response_header_add(conn, "Cache-Control", val, -1);
4113
4114#else /* NO_CACHING */
4115
4117#endif /* !NO_CACHING */
4118}
4119
4120
4121static void
4123{
4124 const char *header = conn->dom_ctx->config[ADDITIONAL_HEADER];
4125
4126#if !defined(NO_SSL)
4127 if (conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]) {
4128 long max_age = atol(conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]);
4129 if (max_age >= 0) {
4130 char val[64];
4131 mg_snprintf(conn,
4132 NULL,
4133 val,
4134 sizeof(val),
4135 "max-age=%lu",
4136 (unsigned long)max_age);
4137 mg_response_header_add(conn, "Strict-Transport-Security", val, -1);
4138 }
4139 }
4140#endif
4141
4142 if (header && header[0]) {
4143 mg_response_header_add_lines(conn, header);
4144 }
4145}
4146
4147
4148static void
4150{
4151 const char *origin_hdr = mg_get_header(conn, "Origin");
4152 const char *cors_orig_cfg =
4154
4155 if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) {
4156 /* Cross-origin resource sharing (CORS), see
4157 * http://www.html5rocks.com/en/tutorials/cors/,
4158 * http://www.html5rocks.com/static/images/cors_server_flowchart.png
4159 * CORS preflight is not supported for files. */
4161 "Access-Control-Allow-Origin",
4162 cors_orig_cfg,
4163 -1);
4164 }
4165}
4166
4167
4168#if !defined(NO_FILESYSTEMS)
4169static void handle_file_based_request(struct mg_connection *conn,
4170 const char *path,
4171 struct mg_file *filep);
4172#endif /* NO_FILESYSTEMS */
4173
4174
4175CIVETWEB_API const char *
4176mg_get_response_code_text(const struct mg_connection *conn, int response_code)
4177{
4178 /* See IANA HTTP status code assignment:
4179 * http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
4180 */
4181
4182 switch (response_code) {
4183 /* RFC2616 Section 10.1 - Informational 1xx */
4184 case 100:
4185 return "Continue"; /* RFC2616 Section 10.1.1 */
4186 case 101:
4187 return "Switching Protocols"; /* RFC2616 Section 10.1.2 */
4188 case 102:
4189 return "Processing"; /* RFC2518 Section 10.1 */
4190
4191 /* RFC2616 Section 10.2 - Successful 2xx */
4192 case 200:
4193 return "OK"; /* RFC2616 Section 10.2.1 */
4194 case 201:
4195 return "Created"; /* RFC2616 Section 10.2.2 */
4196 case 202:
4197 return "Accepted"; /* RFC2616 Section 10.2.3 */
4198 case 203:
4199 return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */
4200 case 204:
4201 return "No Content"; /* RFC2616 Section 10.2.5 */
4202 case 205:
4203 return "Reset Content"; /* RFC2616 Section 10.2.6 */
4204 case 206:
4205 return "Partial Content"; /* RFC2616 Section 10.2.7 */
4206 case 207:
4207 return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1
4208 */
4209 case 208:
4210 return "Already Reported"; /* RFC5842 Section 7.1 */
4211
4212 case 226:
4213 return "IM used"; /* RFC3229 Section 10.4.1 */
4214
4215 /* RFC2616 Section 10.3 - Redirection 3xx */
4216 case 300:
4217 return "Multiple Choices"; /* RFC2616 Section 10.3.1 */
4218 case 301:
4219 return "Moved Permanently"; /* RFC2616 Section 10.3.2 */
4220 case 302:
4221 return "Found"; /* RFC2616 Section 10.3.3 */
4222 case 303:
4223 return "See Other"; /* RFC2616 Section 10.3.4 */
4224 case 304:
4225 return "Not Modified"; /* RFC2616 Section 10.3.5 */
4226 case 305:
4227 return "Use Proxy"; /* RFC2616 Section 10.3.6 */
4228 case 307:
4229 return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */
4230 case 308:
4231 return "Permanent Redirect"; /* RFC7238 Section 3 */
4232
4233 /* RFC2616 Section 10.4 - Client Error 4xx */
4234 case 400:
4235 return "Bad Request"; /* RFC2616 Section 10.4.1 */
4236 case 401:
4237 return "Unauthorized"; /* RFC2616 Section 10.4.2 */
4238 case 402:
4239 return "Payment Required"; /* RFC2616 Section 10.4.3 */
4240 case 403:
4241 return "Forbidden"; /* RFC2616 Section 10.4.4 */
4242 case 404:
4243 return "Not Found"; /* RFC2616 Section 10.4.5 */
4244 case 405:
4245 return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */
4246 case 406:
4247 return "Not Acceptable"; /* RFC2616 Section 10.4.7 */
4248 case 407:
4249 return "Proxy Authentication Required"; /* RFC2616 Section 10.4.8 */
4250 case 408:
4251 return "Request Time-out"; /* RFC2616 Section 10.4.9 */
4252 case 409:
4253 return "Conflict"; /* RFC2616 Section 10.4.10 */
4254 case 410:
4255 return "Gone"; /* RFC2616 Section 10.4.11 */
4256 case 411:
4257 return "Length Required"; /* RFC2616 Section 10.4.12 */
4258 case 412:
4259 return "Precondition Failed"; /* RFC2616 Section 10.4.13 */
4260 case 413:
4261 return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */
4262 case 414:
4263 return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */
4264 case 415:
4265 return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */
4266 case 416:
4267 return "Requested range not satisfiable"; /* RFC2616 Section 10.4.17
4268 */
4269 case 417:
4270 return "Expectation Failed"; /* RFC2616 Section 10.4.18 */
4271
4272 case 421:
4273 return "Misdirected Request"; /* RFC7540 Section 9.1.2 */
4274 case 422:
4275 return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918
4276 * Section 11.2 */
4277 case 423:
4278 return "Locked"; /* RFC2518 Section 10.4, RFC4918 Section 11.3 */
4279 case 424:
4280 return "Failed Dependency"; /* RFC2518 Section 10.5, RFC4918
4281 * Section 11.4 */
4282
4283 case 426:
4284 return "Upgrade Required"; /* RFC 2817 Section 4 */
4285
4286 case 428:
4287 return "Precondition Required"; /* RFC 6585, Section 3 */
4288 case 429:
4289 return "Too Many Requests"; /* RFC 6585, Section 4 */
4290
4291 case 431:
4292 return "Request Header Fields Too Large"; /* RFC 6585, Section 5 */
4293
4294 case 451:
4295 return "Unavailable For Legal Reasons"; /* draft-tbray-http-legally-restricted-status-05,
4296 * Section 3 */
4297
4298 /* RFC2616 Section 10.5 - Server Error 5xx */
4299 case 500:
4300 return "Internal Server Error"; /* RFC2616 Section 10.5.1 */
4301 case 501:
4302 return "Not Implemented"; /* RFC2616 Section 10.5.2 */
4303 case 502:
4304 return "Bad Gateway"; /* RFC2616 Section 10.5.3 */
4305 case 503:
4306 return "Service Unavailable"; /* RFC2616 Section 10.5.4 */
4307 case 504:
4308 return "Gateway Time-out"; /* RFC2616 Section 10.5.5 */
4309 case 505:
4310 return "HTTP Version not supported"; /* RFC2616 Section 10.5.6 */
4311 case 506:
4312 return "Variant Also Negotiates"; /* RFC 2295, Section 8.1 */
4313 case 507:
4314 return "Insufficient Storage"; /* RFC2518 Section 10.6, RFC4918
4315 * Section 11.5 */
4316 case 508:
4317 return "Loop Detected"; /* RFC5842 Section 7.1 */
4318
4319 case 510:
4320 return "Not Extended"; /* RFC 2774, Section 7 */
4321 case 511:
4322 return "Network Authentication Required"; /* RFC 6585, Section 6 */
4323
4324 /* Other status codes, not shown in the IANA HTTP status code
4325 * assignment.
4326 * E.g., "de facto" standards due to common use, ... */
4327 case 418:
4328 return "I am a teapot"; /* RFC2324 Section 2.3.2 */
4329 case 419:
4330 return "Authentication Timeout"; /* common use */
4331 case 420:
4332 return "Enhance Your Calm"; /* common use */
4333 case 440:
4334 return "Login Timeout"; /* common use */
4335 case 509:
4336 return "Bandwidth Limit Exceeded"; /* common use */
4337
4338 default:
4339 /* This error code is unknown. This should not happen. */
4340 if (conn) {
4341 mg_cry_internal(conn,
4342 "Unknown HTTP response code: %u",
4343 response_code);
4344 }
4345
4346 /* Return at least a category according to RFC 2616 Section 10. */
4347 if (response_code >= 100 && response_code < 200) {
4348 /* Unknown informational status code */
4349 return "Information";
4350 }
4351 if (response_code >= 200 && response_code < 300) {
4352 /* Unknown success code */
4353 return "Success";
4354 }
4355 if (response_code >= 300 && response_code < 400) {
4356 /* Unknown redirection code */
4357 return "Redirection";
4358 }
4359 if (response_code >= 400 && response_code < 500) {
4360 /* Unknown request error code */
4361 return "Client Error";
4362 }
4363 if (response_code >= 500 && response_code < 600) {
4364 /* Unknown server error code */
4365 return "Server Error";
4366 }
4367
4368 /* Response code not even within reasonable range */
4369 return "";
4370 }
4371}
4372
4373
4374static int
4376 int status,
4377 const char *fmt,
4378 va_list args)
4379{
4380 char errmsg_buf[MG_BUF_LEN];
4381 va_list ap;
4382 int has_body;
4383
4384#if !defined(NO_FILESYSTEMS)
4385 char path_buf[UTF8_PATH_MAX];
4386 int len, i, page_handler_found, scope, truncated;
4387 const char *error_handler = NULL;
4388 struct mg_file error_page_file = STRUCT_FILE_INITIALIZER;
4389 const char *error_page_file_ext, *tstr;
4390#endif /* NO_FILESYSTEMS */
4391 int handled_by_callback = 0;
4392
4393 if ((conn == NULL) || (fmt == NULL)) {
4394 return -2;
4395 }
4396
4397 /* Set status (for log) */
4398 conn->status_code = status;
4399
4400 /* Errors 1xx, 204 and 304 MUST NOT send a body */
4401 has_body = ((status > 199) && (status != 204) && (status != 304));
4402
4403 /* Prepare message in buf, if required */
4404 if (has_body
4405 || (!conn->in_error_handler
4406 && (conn->phys_ctx->callbacks.http_error != NULL))) {
4407 /* Store error message in errmsg_buf */
4408 va_copy(ap, args);
4409 mg_vsnprintf(conn, NULL, errmsg_buf, sizeof(errmsg_buf), fmt, ap);
4410 va_end(ap);
4411 /* In a debug build, print all html errors */
4412 DEBUG_TRACE("Error %i - [%s]", status, errmsg_buf);
4413 }
4414
4415 /* If there is a http_error callback, call it.
4416 * But don't do it recursively, if callback calls mg_send_http_error again.
4417 */
4418 if (!conn->in_error_handler
4419 && (conn->phys_ctx->callbacks.http_error != NULL)) {
4420 /* Mark in_error_handler to avoid recursion and call user callback. */
4421 conn->in_error_handler = 1;
4422 handled_by_callback =
4423 (conn->phys_ctx->callbacks.http_error(conn, status, errmsg_buf)
4424 == 0);
4425 conn->in_error_handler = 0;
4426 }
4427
4428 if (!handled_by_callback) {
4429 /* Check for recursion */
4430 if (conn->in_error_handler) {
4432 "Recursion when handling error %u - fall back to default",
4433 status);
4434#if !defined(NO_FILESYSTEMS)
4435 } else {
4436 /* Send user defined error pages, if defined */
4437 error_handler = conn->dom_ctx->config[ERROR_PAGES];
4438 error_page_file_ext = conn->dom_ctx->config[INDEX_FILES];
4439 page_handler_found = 0;
4440
4441 if (error_handler != NULL) {
4442 for (scope = 1; (scope <= 3) && !page_handler_found; scope++) {
4443 switch (scope) {
4444 case 1: /* Handler for specific error, e.g. 404 error */
4445 mg_snprintf(conn,
4446 &truncated,
4447 path_buf,
4448 sizeof(path_buf) - 32,
4449 "%serror%03u.",
4450 error_handler,
4451 status);
4452 break;
4453 case 2: /* Handler for error group, e.g., 5xx error
4454 * handler
4455 * for all server errors (500-599) */
4456 mg_snprintf(conn,
4457 &truncated,
4458 path_buf,
4459 sizeof(path_buf) - 32,
4460 "%serror%01uxx.",
4461 error_handler,
4462 status / 100);
4463 break;
4464 default: /* Handler for all errors */
4465 mg_snprintf(conn,
4466 &truncated,
4467 path_buf,
4468 sizeof(path_buf) - 32,
4469 "%serror.",
4470 error_handler);
4471 break;
4472 }
4473
4474 /* String truncation in buf may only occur if
4475 * error_handler is too long. This string is
4476 * from the config, not from a client. */
4477 (void)truncated;
4478
4479 /* The following code is redundant, but it should avoid
4480 * false positives in static source code analyzers and
4481 * vulnerability scanners.
4482 */
4483 path_buf[sizeof(path_buf) - 32] = 0;
4484 len = (int)strlen(path_buf);
4485 if (len > (int)sizeof(path_buf) - 32) {
4486 len = (int)sizeof(path_buf) - 32;
4487 }
4488
4489 /* Start with the file extension from the configuration. */
4490 tstr = strchr(error_page_file_ext, '.');
4491
4492 while (tstr) {
4493 for (i = 1;
4494 (i < 32) && (tstr[i] != 0) && (tstr[i] != ',');
4495 i++) {
4496 /* buffer overrun is not possible here, since
4497 * (i < 32) && (len < sizeof(path_buf) - 32)
4498 * ==> (i + len) < sizeof(path_buf) */
4499 path_buf[len + i - 1] = tstr[i];
4500 }
4501 /* buffer overrun is not possible here, since
4502 * (i <= 32) && (len < sizeof(path_buf) - 32)
4503 * ==> (i + len) <= sizeof(path_buf) */
4504 path_buf[len + i - 1] = 0;
4505
4506 if (mg_stat(conn, path_buf, &error_page_file.stat)) {
4507 DEBUG_TRACE("Check error page %s - found",
4508 path_buf);
4509 page_handler_found = 1;
4510 break;
4511 }
4512 DEBUG_TRACE("Check error page %s - not found",
4513 path_buf);
4514
4515 /* Continue with the next file extension from the
4516 * configuration (if there is a next one). */
4517 tstr = strchr(tstr + i, '.');
4518 }
4519 }
4520 }
4521
4522 if (page_handler_found) {
4523 conn->in_error_handler = 1;
4524 handle_file_based_request(conn, path_buf, &error_page_file);
4525 conn->in_error_handler = 0;
4526 return 0;
4527 }
4528#endif /* NO_FILESYSTEMS */
4529 }
4530
4531 /* No custom error page. Send default error page. */
4532 conn->must_close = 1;
4533 mg_response_header_start(conn, status);
4536 send_cors_header(conn);
4537 if (has_body) {
4539 "Content-Type",
4540 "text/plain; charset=utf-8",
4541 -1);
4542 }
4544
4545 /* HTTP responses 1xx, 204 and 304 MUST NOT send a body */
4546 if (has_body) {
4547 /* For other errors, send a generic error message. */
4548 const char *status_text = mg_get_response_code_text(conn, status);
4549 mg_printf(conn, "Error %d: %s\n", status, status_text);
4550 mg_write(conn, errmsg_buf, strlen(errmsg_buf));
4551
4552 } else {
4553 /* No body allowed. Close the connection. */
4554 DEBUG_TRACE("Error %i", status);
4555 }
4556 }
4557 return 0;
4558}
4559
4560
4561CIVETWEB_API int
4562mg_send_http_error(struct mg_connection *conn, int status, const char *fmt, ...)
4563{
4564 va_list ap;
4565 int ret;
4566
4567 va_start(ap, fmt);
4568 ret = mg_send_http_error_impl(conn, status, fmt, ap);
4569 va_end(ap);
4570
4571 return ret;
4572}
4573
4574
4575CIVETWEB_API int
4577 const char *mime_type,
4578 long long content_length)
4579{
4580 if ((mime_type == NULL) || (*mime_type == 0)) {
4581 /* No content type defined: default to text/html */
4582 mime_type = "text/html";
4583 }
4584
4585 mg_response_header_start(conn, 200);
4588 send_cors_header(conn);
4589 mg_response_header_add(conn, "Content-Type", mime_type, -1);
4590 if (content_length < 0) {
4591 /* Size not known. Use chunked encoding (HTTP/1.x) */
4592 if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
4593 /* Only HTTP/1.x defines "chunked" encoding, HTTP/2 does not*/
4594 mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1);
4595 }
4596 } else {
4597 char len[32];
4598 int trunc = 0;
4599 mg_snprintf(conn,
4600 &trunc,
4601 len,
4602 sizeof(len),
4603 "%" UINT64_FMT,
4604 (uint64_t)content_length);
4605 if (!trunc) {
4606 /* Since 32 bytes is enough to hold any 64 bit decimal number,
4607 * !trunc is always true */
4608 mg_response_header_add(conn, "Content-Length", len, -1);
4609 }
4610 }
4612
4613 return 0;
4614}
4615
4616
4617CIVETWEB_API int
4619 const char *target_url,
4620 int redirect_code)
4621{
4622 /* Send a 30x redirect response.
4623 *
4624 * Redirect types (status codes):
4625 *
4626 * Status | Perm/Temp | Method | Version
4627 * 301 | permanent | POST->GET undefined | HTTP/1.0
4628 * 302 | temporary | POST->GET undefined | HTTP/1.0
4629 * 303 | temporary | always use GET | HTTP/1.1
4630 * 307 | temporary | always keep method | HTTP/1.1
4631 * 308 | permanent | always keep method | HTTP/1.1
4632 */
4633
4634#if defined(MG_SEND_REDIRECT_BODY)
4635 char redirect_body[MG_BUF_LEN];
4636 size_t content_len = 0;
4637 char content_len_text[32];
4638#endif
4639
4640 /* In case redirect_code=0, use 307. */
4641 if (redirect_code == 0) {
4642 redirect_code = 307;
4643 }
4644
4645 /* In case redirect_code is none of the above, return error. */
4646 if ((redirect_code != 301) && (redirect_code != 302)
4647 && (redirect_code != 303) && (redirect_code != 307)
4648 && (redirect_code != 308)) {
4649 /* Parameter error */
4650 return -2;
4651 }
4652
4653 /* If target_url is not defined, redirect to "/". */
4654 if ((target_url == NULL) || (*target_url == 0)) {
4655 target_url = "/";
4656 }
4657
4658#if defined(MG_SEND_REDIRECT_BODY)
4659 /* TODO: condition name? */
4660
4661 /* Prepare a response body with a hyperlink.
4662 *
4663 * According to RFC2616 (and RFC1945 before):
4664 * Unless the request method was HEAD, the entity of the
4665 * response SHOULD contain a short hypertext note with a hyperlink to
4666 * the new URI(s).
4667 *
4668 * However, this response body is not useful in M2M communication.
4669 * Probably the original reason in the RFC was, clients not supporting
4670 * a 30x HTTP redirect could still show the HTML page and let the user
4671 * press the link. Since current browsers support 30x HTTP, the additional
4672 * HTML body does not seem to make sense anymore.
4673 *
4674 * The new RFC7231 (Section 6.4) does no longer recommend it ("SHOULD"),
4675 * but it only notes:
4676 * The server's response payload usually contains a short
4677 * hypertext note with a hyperlink to the new URI(s).
4678 *
4679 * Deactivated by default. If you need the 30x body, set the define.
4680 */
4682 conn,
4683 NULL /* ignore truncation */,
4684 redirect_body,
4685 sizeof(redirect_body),
4686 "<html><head>%s</head><body><a href=\"%s\">%s</a></body></html>",
4687 redirect_text,
4688 target_url,
4689 target_url);
4690 content_len = strlen(reply);
4691 sprintf(content_len_text, "%lu", (unsigned long)content_len);
4692#endif
4693
4694 /* Send all required headers */
4695 mg_response_header_start(conn, redirect_code);
4696 mg_response_header_add(conn, "Location", target_url, -1);
4697 if ((redirect_code == 301) || (redirect_code == 308)) {
4698 /* Permanent redirect */
4700 } else {
4701 /* Temporary redirect */
4703 }
4705 send_cors_header(conn);
4706#if defined(MG_SEND_REDIRECT_BODY)
4707 mg_response_header_add(conn, "Content-Type", "text/html", -1);
4708 mg_response_header_add(conn, "Content-Length", content_len_text, -1);
4709#else
4710 mg_response_header_add(conn, "Content-Length", "0", 1);
4711#endif
4713
4714#if defined(MG_SEND_REDIRECT_BODY)
4715 /* Send response body */
4716 /* ... unless it is a HEAD request */
4717 if (0 != strcmp(conn->request_info.request_method, "HEAD")) {
4718 ret = mg_write(conn, redirect_body, content_len);
4719 }
4720#endif
4721
4722 return 1;
4723}
4724
4725
4726#if defined(_WIN32)
4727/* Create substitutes for POSIX functions in Win32. */
4728
4729#if defined(GCC_DIAGNOSTIC)
4730/* Show no warning in case system functions are not used. */
4731#pragma GCC diagnostic push
4732#pragma GCC diagnostic ignored "-Wunused-function"
4733#endif
4734
4735
4736static int
4737pthread_mutex_init(pthread_mutex_t *mutex, void *unused)
4738{
4739 (void)unused;
4740 /* Always initialize as PTHREAD_MUTEX_RECURSIVE */
4741 InitializeCriticalSection(&mutex->sec);
4742 return 0;
4743}
4744
4745
4746static int
4747pthread_mutex_destroy(pthread_mutex_t *mutex)
4748{
4749 DeleteCriticalSection(&mutex->sec);
4750 return 0;
4751}
4752
4753
4754static int
4755pthread_mutex_lock(pthread_mutex_t *mutex)
4756{
4757 EnterCriticalSection(&mutex->sec);
4758 return 0;
4759}
4760
4761
4762static int
4763pthread_mutex_unlock(pthread_mutex_t *mutex)
4764{
4765 LeaveCriticalSection(&mutex->sec);
4766 return 0;
4767}
4768
4769
4771static int
4772pthread_cond_init(pthread_cond_t *cv, const void *unused)
4773{
4774 (void)unused;
4775 (void)pthread_mutex_init(&cv->threadIdSec, &pthread_mutex_attr);
4776 cv->waiting_thread = NULL;
4777 return 0;
4778}
4779
4780
4782static int
4783pthread_cond_timedwait(pthread_cond_t *cv,
4784 pthread_mutex_t *mutex,
4785 FUNCTION_MAY_BE_UNUSED const struct timespec *abstime)
4786{
4787 struct mg_workerTLS **ptls,
4788 *tls = (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
4789 int ok;
4790 uint64_t nsnow, nswaitabs;
4791 int64_t nswaitrel;
4792 DWORD mswaitrel;
4793
4794 pthread_mutex_lock(&cv->threadIdSec);
4795 /* Add this thread to cv's waiting list */
4796 ptls = &cv->waiting_thread;
4797 for (; *ptls != NULL; ptls = &(*ptls)->next_waiting_thread)
4798 ;
4799 tls->next_waiting_thread = NULL;
4800 *ptls = tls;
4801 pthread_mutex_unlock(&cv->threadIdSec);
4802
4803 if (abstime) {
4804 nsnow = mg_get_current_time_ns();
4805 nswaitabs =
4806 (((uint64_t)abstime->tv_sec) * 1000000000) + abstime->tv_nsec;
4807 nswaitrel = nswaitabs - nsnow;
4808 if (nswaitrel < 0) {
4809 nswaitrel = 0;
4810 }
4811 mswaitrel = (DWORD)(nswaitrel / 1000000);
4812 } else {
4813 mswaitrel = (DWORD)INFINITE;
4814 }
4815
4816 pthread_mutex_unlock(mutex);
4817 ok = (WAIT_OBJECT_0
4818 == WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel));
4819 if (!ok) {
4820 ok = 1;
4821 pthread_mutex_lock(&cv->threadIdSec);
4822 ptls = &cv->waiting_thread;
4823 for (; *ptls != NULL; ptls = &(*ptls)->next_waiting_thread) {
4824 if (*ptls == tls) {
4825 *ptls = tls->next_waiting_thread;
4826 ok = 0;
4827 break;
4828 }
4829 }
4830 pthread_mutex_unlock(&cv->threadIdSec);
4831 if (ok) {
4832 WaitForSingleObject(tls->pthread_cond_helper_mutex,
4833 (DWORD)INFINITE);
4834 }
4835 }
4836 /* This thread has been removed from cv's waiting list */
4837 pthread_mutex_lock(mutex);
4838
4839 return ok ? 0 : -1;
4840}
4841
4842
4844static int
4845pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex)
4846{
4847 return pthread_cond_timedwait(cv, mutex, NULL);
4848}
4849
4850
4852static int
4853pthread_cond_signal(pthread_cond_t *cv)
4854{
4855 HANDLE wkup = NULL;
4856 BOOL ok = FALSE;
4857
4858 pthread_mutex_lock(&cv->threadIdSec);
4859 if (cv->waiting_thread) {
4860 wkup = cv->waiting_thread->pthread_cond_helper_mutex;
4861 cv->waiting_thread = cv->waiting_thread->next_waiting_thread;
4862
4863 ok = SetEvent(wkup);
4864 DEBUG_ASSERT(ok);
4865 }
4866 pthread_mutex_unlock(&cv->threadIdSec);
4867
4868 return ok ? 0 : 1;
4869}
4870
4871
4873static int
4874pthread_cond_broadcast(pthread_cond_t *cv)
4875{
4876 pthread_mutex_lock(&cv->threadIdSec);
4877 while (cv->waiting_thread) {
4878 pthread_cond_signal(cv);
4879 }
4880 pthread_mutex_unlock(&cv->threadIdSec);
4881
4882 return 0;
4883}
4884
4885
4887static int
4888pthread_cond_destroy(pthread_cond_t *cv)
4889{
4890 pthread_mutex_lock(&cv->threadIdSec);
4891 DEBUG_ASSERT(cv->waiting_thread == NULL);
4892 pthread_mutex_unlock(&cv->threadIdSec);
4893 pthread_mutex_destroy(&cv->threadIdSec);
4894
4895 return 0;
4896}
4897
4898
4899#if defined(ALTERNATIVE_QUEUE)
4901static void *
4902event_create(void)
4903{
4904 return (void *)CreateEvent(NULL, FALSE, FALSE, NULL);
4905}
4906
4907
4909static int
4910event_wait(void *eventhdl)
4911{
4912 int res = WaitForSingleObject((HANDLE)eventhdl, (DWORD)INFINITE);
4913 return (res == WAIT_OBJECT_0);
4914}
4915
4916
4918static int
4919event_signal(void *eventhdl)
4920{
4921 return (int)SetEvent((HANDLE)eventhdl);
4922}
4923
4924
4926static void
4927event_destroy(void *eventhdl)
4928{
4929 CloseHandle((HANDLE)eventhdl);
4930}
4931#endif
4932
4933
4934#if defined(GCC_DIAGNOSTIC)
4935/* Enable unused function warning again */
4936#pragma GCC diagnostic pop
4937#endif
4938
4939
4940/* For Windows, change all slashes to backslashes in path names. */
4941static void
4942change_slashes_to_backslashes(char *path)
4943{
4944 int i;
4945
4946 for (i = 0; path[i] != '\0'; i++) {
4947 if (path[i] == '/') {
4948 path[i] = '\\';
4949 }
4950
4951 /* remove double backslash (check i > 0 to preserve UNC paths,
4952 * like \\server\file.txt) */
4953 if ((i > 0) && (path[i] == '\\')) {
4954 while ((path[i + 1] == '\\') || (path[i + 1] == '/')) {
4955 (void)memmove(path + i + 1, path + i + 2, strlen(path + i + 1));
4956 }
4957 }
4958 }
4959}
4960
4961
4962static int
4963mg_wcscasecmp(const wchar_t *s1, const wchar_t *s2)
4964{
4965 int diff;
4966
4967 do {
4968 diff = ((*s1 >= L'A') && (*s1 <= L'Z') ? (*s1 - L'A' + L'a') : *s1)
4969 - ((*s2 >= L'A') && (*s2 <= L'Z') ? (*s2 - L'A' + L'a') : *s2);
4970 s1++;
4971 s2++;
4972 } while ((diff == 0) && (s1[-1] != L'\0'));
4973
4974 return diff;
4975}
4976
4977
4978/* Encode 'path' which is assumed UTF-8 string, into UNICODE string.
4979 * wbuf and wbuf_len is a target buffer and its length. */
4980static void
4981path_to_unicode(const struct mg_connection *conn,
4982 const char *path,
4983 wchar_t *wbuf,
4984 size_t wbuf_len)
4985{
4986 char buf[UTF8_PATH_MAX], buf2[UTF8_PATH_MAX];
4987 wchar_t wbuf2[UTF16_PATH_MAX + 1];
4988 DWORD long_len, err;
4989 int (*fcompare)(const wchar_t *, const wchar_t *) = mg_wcscasecmp;
4990
4991 mg_strlcpy(buf, path, sizeof(buf));
4992 change_slashes_to_backslashes(buf);
4993
4994 /* Convert to Unicode and back. If doubly-converted string does not
4995 * match the original, something is fishy, reject. */
4996 memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
4997 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int)wbuf_len);
4998 WideCharToMultiByte(
4999 CP_UTF8, 0, wbuf, (int)wbuf_len, buf2, sizeof(buf2), NULL, NULL);
5000 if (strcmp(buf, buf2) != 0) {
5001 wbuf[0] = L'\0';
5002 }
5003
5004 /* Windows file systems are not case sensitive, but you can still use
5005 * uppercase and lowercase letters (on all modern file systems).
5006 * The server can check if the URI uses the same upper/lowercase
5007 * letters an the file system, effectively making Windows servers
5008 * case sensitive (like Linux servers are). It is still not possible
5009 * to use two files with the same name in different cases on Windows
5010 * (like /a and /A) - this would be possible in Linux.
5011 * As a default, Windows is not case sensitive, but the case sensitive
5012 * file name check can be activated by an additional configuration. */
5013 if (conn) {
5014 if (conn->dom_ctx->config[CASE_SENSITIVE_FILES]
5015 && !mg_strcasecmp(conn->dom_ctx->config[CASE_SENSITIVE_FILES],
5016 "yes")) {
5017 /* Use case sensitive compare function */
5018 fcompare = wcscmp;
5019 }
5020 }
5021 (void)conn; /* conn is currently unused */
5022
5023 /* Only accept a full file path, not a Windows short (8.3) path. */
5024 memset(wbuf2, 0, ARRAY_SIZE(wbuf2) * sizeof(wchar_t));
5025 long_len = GetLongPathNameW(wbuf, wbuf2, ARRAY_SIZE(wbuf2) - 1);
5026 if (long_len == 0) {
5027 err = GetLastError();
5028 if (err == ERROR_FILE_NOT_FOUND) {
5029 /* File does not exist. This is not always a problem here. */
5030 return;
5031 }
5032 }
5033 if ((long_len >= ARRAY_SIZE(wbuf2)) || (fcompare(wbuf, wbuf2) != 0)) {
5034 /* Short name is used. */
5035 wbuf[0] = L'\0';
5036 }
5037}
5038
5039
5040#if !defined(NO_FILESYSTEMS)
5041/* Get file information, return 1 if file exists, 0 if not */
5042static int
5043mg_stat(const struct mg_connection *conn,
5044 const char *path,
5045 struct mg_file_stat *filep)
5046{
5047 wchar_t wbuf[UTF16_PATH_MAX];
5048 WIN32_FILE_ATTRIBUTE_DATA info;
5049 time_t creation_time;
5050 size_t len;
5051
5052 if (!filep) {
5053 return 0;
5054 }
5055 memset(filep, 0, sizeof(*filep));
5056
5057 if (mg_path_suspicious(conn, path)) {
5058 return 0;
5059 }
5060
5061 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
5062 /* Windows happily opens files with some garbage at the end of file name.
5063 * For example, fopen("a.cgi ", "r") on Windows successfully opens
5064 * "a.cgi", despite one would expect an error back. */
5065 len = strlen(path);
5066 if ((len > 0) && (path[len - 1] != ' ') && (path[len - 1] != '.')
5067 && (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0)) {
5068 filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
5069 filep->last_modified =
5070 SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime,
5071 info.ftLastWriteTime.dwHighDateTime);
5072
5073 /* On Windows, the file creation time can be higher than the
5074 * modification time, e.g. when a file is copied.
5075 * Since the Last-Modified timestamp is used for caching
5076 * it should be based on the most recent timestamp. */
5077 creation_time = SYS2UNIX_TIME(info.ftCreationTime.dwLowDateTime,
5078 info.ftCreationTime.dwHighDateTime);
5079 if (creation_time > filep->last_modified) {
5080 filep->last_modified = creation_time;
5081 }
5082
5083 filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
5084 return 1;
5085 }
5086
5087 return 0;
5088}
5089#endif
5090
5091
5092static int
5093mg_remove(const struct mg_connection *conn, const char *path)
5094{
5095 wchar_t wbuf[UTF16_PATH_MAX];
5096 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
5097 return DeleteFileW(wbuf) ? 0 : -1;
5098}
5099
5100
5101static int
5102mg_mkdir(const struct mg_connection *conn, const char *path, int mode)
5103{
5104 wchar_t wbuf[UTF16_PATH_MAX];
5105 (void)mode;
5106 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
5107 return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
5108}
5109
5110
5111/* Create substitutes for POSIX functions in Win32. */
5112
5113#if defined(GCC_DIAGNOSTIC)
5114/* Show no warning in case system functions are not used. */
5115#pragma GCC diagnostic push
5116#pragma GCC diagnostic ignored "-Wunused-function"
5117#endif
5118
5119
5120/* Implementation of POSIX opendir/closedir/readdir for Windows. */
5122static DIR *
5123mg_opendir(const struct mg_connection *conn, const char *name)
5124{
5125 DIR *dir = NULL;
5126 wchar_t wpath[UTF16_PATH_MAX];
5127 DWORD attrs;
5128
5129 if (name == NULL) {
5130 SetLastError(ERROR_BAD_ARGUMENTS);
5131 } else if ((dir = (DIR *)mg_malloc(sizeof(*dir))) == NULL) {
5132 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5133 } else {
5134 path_to_unicode(conn, name, wpath, ARRAY_SIZE(wpath));
5135 attrs = GetFileAttributesW(wpath);
5136 if ((wcslen(wpath) + 2 < ARRAY_SIZE(wpath)) && (attrs != 0xFFFFFFFF)
5137 && ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0)) {
5138 (void)wcscat(wpath, L"\\*");
5139 dir->handle = FindFirstFileW(wpath, &dir->info);
5140 dir->result.d_name[0] = '\0';
5141 } else {
5142 mg_free(dir);
5143 dir = NULL;
5144 }
5145 }
5146
5147 return dir;
5148}
5149
5150
5152static int
5153mg_closedir(DIR *dir)
5154{
5155 int result = 0;
5156
5157 if (dir != NULL) {
5158 if (dir->handle != INVALID_HANDLE_VALUE)
5159 result = FindClose(dir->handle) ? 0 : -1;
5160
5161 mg_free(dir);
5162 } else {
5163 result = -1;
5164 SetLastError(ERROR_BAD_ARGUMENTS);
5165 }
5166
5167 return result;
5168}
5169
5170
5172static struct dirent *
5173mg_readdir(DIR *dir)
5174{
5175 struct dirent *result = 0;
5176
5177 if (dir) {
5178 if (dir->handle != INVALID_HANDLE_VALUE) {
5179 result = &dir->result;
5180 (void)WideCharToMultiByte(CP_UTF8,
5181 0,
5182 dir->info.cFileName,
5183 -1,
5184 result->d_name,
5185 sizeof(result->d_name),
5186 NULL,
5187 NULL);
5188
5189 if (!FindNextFileW(dir->handle, &dir->info)) {
5190 (void)FindClose(dir->handle);
5191 dir->handle = INVALID_HANDLE_VALUE;
5192 }
5193
5194 } else {
5195 SetLastError(ERROR_FILE_NOT_FOUND);
5196 }
5197 } else {
5198 SetLastError(ERROR_BAD_ARGUMENTS);
5199 }
5200
5201 return result;
5202}
5203
5204
5205#if !defined(HAVE_POLL)
5206#undef POLLIN
5207#undef POLLPRI
5208#undef POLLOUT
5209#undef POLLERR
5210#define POLLIN (1) /* Data ready - read will not block. */
5211#define POLLPRI (2) /* Priority data ready. */
5212#define POLLOUT (4) /* Send queue not full - write will not block. */
5213#define POLLERR (8) /* Error event */
5214
5216static int
5217poll(struct mg_pollfd *pfd, unsigned int n, int milliseconds)
5218{
5219 struct timeval tv;
5220 fd_set rset;
5221 fd_set wset;
5222 fd_set eset;
5223 unsigned int i;
5224 int result;
5225 SOCKET maxfd = 0;
5226
5227 memset(&tv, 0, sizeof(tv));
5228 tv.tv_sec = milliseconds / 1000;
5229 tv.tv_usec = (milliseconds % 1000) * 1000;
5230 FD_ZERO(&rset);
5231 FD_ZERO(&wset);
5232 FD_ZERO(&eset);
5233
5234 for (i = 0; i < n; i++) {
5235 if (pfd[i].events & (POLLIN | POLLOUT | POLLERR)) {
5236 if (pfd[i].events & POLLIN) {
5237 FD_SET(pfd[i].fd, &rset);
5238 }
5239 if (pfd[i].events & POLLOUT) {
5240 FD_SET(pfd[i].fd, &wset);
5241 }
5242 /* Check for errors for any FD in the set */
5243 FD_SET(pfd[i].fd, &eset);
5244 }
5245 pfd[i].revents = 0;
5246
5247 if (pfd[i].fd > maxfd) {
5248 maxfd = pfd[i].fd;
5249 }
5250 }
5251
5252 if ((result = select((int)maxfd + 1, &rset, &wset, &eset, &tv)) > 0) {
5253 for (i = 0; i < n; i++) {
5254 if (FD_ISSET(pfd[i].fd, &rset)) {
5255 pfd[i].revents |= POLLIN;
5256 }
5257 if (FD_ISSET(pfd[i].fd, &wset)) {
5258 pfd[i].revents |= POLLOUT;
5259 }
5260 if (FD_ISSET(pfd[i].fd, &eset)) {
5261 pfd[i].revents |= POLLERR;
5262 }
5263 }
5264 }
5265
5266 /* We should subtract the time used in select from remaining
5267 * "milliseconds", in particular if called from mg_poll with a
5268 * timeout quantum.
5269 * Unfortunately, the remaining time is not stored in "tv" in all
5270 * implementations, so the result in "tv" must be considered undefined.
5271 * See http://man7.org/linux/man-pages/man2/select.2.html */
5272
5273 return result;
5274}
5275#endif /* HAVE_POLL */
5276
5277
5278#if defined(GCC_DIAGNOSTIC)
5279/* Enable unused function warning again */
5280#pragma GCC diagnostic pop
5281#endif
5282
5283
5284static void
5286 const struct mg_connection *conn /* may be null */,
5287 struct mg_context *ctx /* may be null */)
5288{
5289 (void)conn; /* Unused. */
5290 (void)ctx;
5291
5292 (void)SetHandleInformation((HANDLE)(intptr_t)sock, HANDLE_FLAG_INHERIT, 0);
5293}
5294
5295
5296CIVETWEB_API int
5298{
5299#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5300 /* Compile-time option to control stack size, e.g.
5301 * -DUSE_STACK_SIZE=16384
5302 */
5303 return ((_beginthread((void(__cdecl *)(void *))f, USE_STACK_SIZE, p)
5304 == ((uintptr_t)(-1L)))
5305 ? -1
5306 : 0);
5307#else
5308 return (
5309 (_beginthread((void(__cdecl *)(void *))f, 0, p) == ((uintptr_t)(-1L)))
5310 ? -1
5311 : 0);
5312#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */
5313}
5314
5315
5316/* Start a thread storing the thread context. */
5317static int
5318mg_start_thread_with_id(unsigned(__stdcall *f)(void *),
5319 void *p,
5320 pthread_t *threadidptr)
5321{
5322 uintptr_t uip;
5323 HANDLE threadhandle;
5324 int result = -1;
5325
5326 uip = _beginthreadex(NULL, 0, f, p, 0, NULL);
5327 threadhandle = (HANDLE)uip;
5328 if ((uip != 0) && (threadidptr != NULL)) {
5329 *threadidptr = threadhandle;
5330 result = 0;
5331 }
5332
5333 return result;
5334}
5335
5336
5337/* Wait for a thread to finish. */
5338static int
5339mg_join_thread(pthread_t threadid)
5340{
5341 int result;
5342 DWORD dwevent;
5343
5344 result = -1;
5345 dwevent = WaitForSingleObject(threadid, (DWORD)INFINITE);
5346 if (dwevent == WAIT_FAILED) {
5347 DEBUG_TRACE("WaitForSingleObject() failed, error %d", ERRNO);
5348 } else {
5349 if (dwevent == WAIT_OBJECT_0) {
5350 CloseHandle(threadid);
5351 result = 0;
5352 }
5353 }
5354
5355 return result;
5356}
5357
5358#if !defined(NO_SSL_DL) && !defined(NO_SSL)
5359/* If SSL is loaded dynamically, dlopen/dlclose is required. */
5360/* Create substitutes for POSIX functions in Win32. */
5361
5362#if defined(GCC_DIAGNOSTIC)
5363/* Show no warning in case system functions are not used. */
5364#pragma GCC diagnostic push
5365#pragma GCC diagnostic ignored "-Wunused-function"
5366#endif
5367
5368
5370static HANDLE
5371dlopen(const char *dll_name, int flags)
5372{
5373 wchar_t wbuf[UTF16_PATH_MAX];
5374 (void)flags;
5375 path_to_unicode(NULL, dll_name, wbuf, ARRAY_SIZE(wbuf));
5376 return LoadLibraryW(wbuf);
5377}
5378
5379
5381static int
5382dlclose(void *handle)
5383{
5384 int result;
5385
5386 if (FreeLibrary((HMODULE)handle) != 0) {
5387 result = 0;
5388 } else {
5389 result = -1;
5390 }
5391
5392 return result;
5393}
5394
5395
5396#if defined(GCC_DIAGNOSTIC)
5397/* Enable unused function warning again */
5398#pragma GCC diagnostic pop
5399#endif
5400
5401#endif
5402
5403
5404#if !defined(NO_CGI)
5405#define SIGKILL (0)
5406
5407
5408static int
5409kill(pid_t pid, int sig_num)
5410{
5411 (void)TerminateProcess((HANDLE)pid, (UINT)sig_num);
5412 (void)CloseHandle((HANDLE)pid);
5413 return 0;
5414}
5415
5416
5417#if !defined(WNOHANG)
5418#define WNOHANG (1)
5419#endif
5420
5421
5422static pid_t
5423waitpid(pid_t pid, int *status, int flags)
5424{
5425 DWORD timeout = INFINITE;
5426 DWORD waitres;
5427
5428 (void)status; /* Currently not used by any client here */
5429
5430 if ((flags | WNOHANG) == WNOHANG) {
5431 timeout = 0;
5432 }
5433
5434 waitres = WaitForSingleObject((HANDLE)pid, timeout);
5435 if (waitres == WAIT_OBJECT_0) {
5436 return pid;
5437 }
5438 if (waitres == WAIT_TIMEOUT) {
5439 return 0;
5440 }
5441 return (pid_t)-1;
5442}
5443
5444
5445static void
5446trim_trailing_whitespaces(char *s)
5447{
5448 char *e = s + strlen(s);
5449 while ((e > s) && isspace((unsigned char)e[-1])) {
5450 *(--e) = '\0';
5451 }
5452}
5453
5454
5455static pid_t
5456spawn_process(struct mg_connection *conn,
5457 const char *prog,
5458 char *envblk,
5459 char *envp[],
5460 int fdin[2],
5461 int fdout[2],
5462 int fderr[2],
5463 const char *dir,
5464 int cgi_config_idx)
5465{
5466 HANDLE me;
5467 char *interp;
5468 char *interp_arg = 0;
5469 char full_dir[UTF8_PATH_MAX], cmdline[UTF8_PATH_MAX], buf[UTF8_PATH_MAX];
5470 int truncated;
5471 struct mg_file file = STRUCT_FILE_INITIALIZER;
5472 STARTUPINFOA si;
5473 PROCESS_INFORMATION pi = {0};
5474
5475 (void)envp;
5476
5477 memset(&si, 0, sizeof(si));
5478 si.cb = sizeof(si);
5479
5480 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
5481 si.wShowWindow = SW_HIDE;
5482
5483 me = GetCurrentProcess();
5484 DuplicateHandle(me,
5485 (HANDLE)_get_osfhandle(fdin[0]),
5486 me,
5487 &si.hStdInput,
5488 0,
5489 TRUE,
5490 DUPLICATE_SAME_ACCESS);
5491 DuplicateHandle(me,
5492 (HANDLE)_get_osfhandle(fdout[1]),
5493 me,
5494 &si.hStdOutput,
5495 0,
5496 TRUE,
5497 DUPLICATE_SAME_ACCESS);
5498 DuplicateHandle(me,
5499 (HANDLE)_get_osfhandle(fderr[1]),
5500 me,
5501 &si.hStdError,
5502 0,
5503 TRUE,
5504 DUPLICATE_SAME_ACCESS);
5505
5506 /* Mark handles that should not be inherited. See
5507 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx
5508 */
5509 SetHandleInformation((HANDLE)_get_osfhandle(fdin[1]),
5510 HANDLE_FLAG_INHERIT,
5511 0);
5512 SetHandleInformation((HANDLE)_get_osfhandle(fdout[0]),
5513 HANDLE_FLAG_INHERIT,
5514 0);
5515 SetHandleInformation((HANDLE)_get_osfhandle(fderr[0]),
5516 HANDLE_FLAG_INHERIT,
5517 0);
5518
5519 /* First check, if there is a CGI interpreter configured for all CGI
5520 * scripts. */
5521 interp = conn->dom_ctx->config[CGI_INTERPRETER + cgi_config_idx];
5522 if (interp != NULL) {
5523 /* If there is a configured interpreter, check for additional arguments
5524 */
5525 interp_arg =
5526 conn->dom_ctx->config[CGI_INTERPRETER_ARGS + cgi_config_idx];
5527 } else {
5528 /* Otherwise, the interpreter must be stated in the first line of the
5529 * CGI script file, after a #! (shebang) mark. */
5530 buf[0] = buf[1] = '\0';
5531
5532 /* Get the full script path */
5534 conn, &truncated, cmdline, sizeof(cmdline), "%s/%s", dir, prog);
5535
5536 if (truncated) {
5537 pi.hProcess = (pid_t)-1;
5538 goto spawn_cleanup;
5539 }
5540
5541 /* Open the script file, to read the first line */
5542 if (mg_fopen(conn, cmdline, MG_FOPEN_MODE_READ, &file)) {
5543
5544 /* Read the first line of the script into the buffer */
5545 mg_fgets(buf, sizeof(buf), &file);
5546 (void)mg_fclose(&file.access); /* ignore error on read only file */
5547 buf[sizeof(buf) - 1] = '\0';
5548 }
5549
5550 if ((buf[0] == '#') && (buf[1] == '!')) {
5551 trim_trailing_whitespaces(buf + 2);
5552 } else {
5553 buf[2] = '\0';
5554 }
5555 interp = buf + 2;
5556 }
5557
5558 GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL);
5559
5560 if (interp[0] != '\0') {
5561 /* This is an interpreted script file. We must call the interpreter. */
5562 if ((interp_arg != 0) && (interp_arg[0] != 0)) {
5563 mg_snprintf(conn,
5564 &truncated,
5565 cmdline,
5566 sizeof(cmdline),
5567 "\"%s\" %s \"%s\\%s\"",
5568 interp,
5569 interp_arg,
5570 full_dir,
5571 prog);
5572 } else {
5573 mg_snprintf(conn,
5574 &truncated,
5575 cmdline,
5576 sizeof(cmdline),
5577 "\"%s\" \"%s\\%s\"",
5578 interp,
5579 full_dir,
5580 prog);
5581 }
5582 } else {
5583 /* This is (probably) a compiled program. We call it directly. */
5584 mg_snprintf(conn,
5585 &truncated,
5586 cmdline,
5587 sizeof(cmdline),
5588 "\"%s\\%s\"",
5589 full_dir,
5590 prog);
5591 }
5592
5593 if (truncated) {
5594 pi.hProcess = (pid_t)-1;
5595 goto spawn_cleanup;
5596 }
5597
5598 DEBUG_TRACE("Running [%s]", cmdline);
5599 if (CreateProcessA(NULL,
5600 cmdline,
5601 NULL,
5602 NULL,
5603 TRUE,
5604 CREATE_NEW_PROCESS_GROUP,
5605 envblk,
5606 NULL,
5607 &si,
5608 &pi)
5609 == 0) {
5611 conn, "%s: CreateProcess(%s): %ld", __func__, cmdline, (long)ERRNO);
5612 pi.hProcess = (pid_t)-1;
5613 /* goto spawn_cleanup; */
5614 }
5615
5616spawn_cleanup:
5617 (void)CloseHandle(si.hStdOutput);
5618 (void)CloseHandle(si.hStdError);
5619 (void)CloseHandle(si.hStdInput);
5620 if (pi.hThread != NULL) {
5621 (void)CloseHandle(pi.hThread);
5622 }
5623
5624 return (pid_t)pi.hProcess;
5625}
5626#endif /* !NO_CGI */
5627
5628
5629static int
5631{
5632 unsigned long non_blocking = 0;
5633 return ioctlsocket(sock, (long)FIONBIO, &non_blocking);
5634}
5635
5636
5637static int
5639{
5640 unsigned long non_blocking = 1;
5641 return ioctlsocket(sock, (long)FIONBIO, &non_blocking);
5642}
5643
5644
5645#else
5646
5647
5648#if !defined(NO_FILESYSTEMS)
5649static int
5650mg_stat(const struct mg_connection *conn,
5651 const char *path,
5652 struct mg_file_stat *filep)
5653{
5654 struct stat st;
5655 if (!filep) {
5656 return 0;
5657 }
5658 memset(filep, 0, sizeof(*filep));
5659
5660 if (mg_path_suspicious(conn, path)) {
5661 return 0;
5662 }
5663
5664 if (0 == stat(path, &st)) {
5665 filep->size = (uint64_t)(st.st_size);
5666 filep->last_modified = st.st_mtime;
5667 filep->is_directory = S_ISDIR(st.st_mode);
5668 return 1;
5669 }
5670
5671 return 0;
5672}
5673#endif /* NO_FILESYSTEMS */
5674
5675
5676static void
5678 const struct mg_connection *conn /* may be null */,
5679 struct mg_context *ctx /* may be null */)
5680{
5681#if defined(__ZEPHYR__)
5682 (void)fd;
5683 (void)conn;
5684 (void)ctx;
5685#else
5686 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
5687 if (conn || ctx) {
5688 struct mg_connection fc;
5689 mg_cry_internal((conn ? conn : fake_connection(&fc, ctx)),
5690 "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
5691 __func__,
5692 strerror(ERRNO));
5693 }
5694 }
5695#endif
5696}
5697
5698
5699CIVETWEB_API int
5701{
5702 pthread_t thread_id;
5703 pthread_attr_t attr;
5704 int result;
5705
5706 (void)pthread_attr_init(&attr);
5707 (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5708
5709#if defined(__ZEPHYR__)
5710 pthread_attr_setstack(&attr, &civetweb_main_stack, ZEPHYR_STACK_SIZE);
5711#elif defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5712 /* Compile-time option to control stack size,
5713 * e.g. -DUSE_STACK_SIZE=16384 */
5714 (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
5715#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */
5716
5717 result = pthread_create(&thread_id, &attr, func, param);
5718 pthread_attr_destroy(&attr);
5719
5720 return result;
5721}
5722
5723
5724/* Start a thread storing the thread context. */
5725static int
5727 void *param,
5728 pthread_t *threadidptr)
5729{
5730 pthread_t thread_id;
5731 pthread_attr_t attr;
5732 int result;
5733
5734 (void)pthread_attr_init(&attr);
5735
5736#if defined(__ZEPHYR__)
5737 pthread_attr_setstack(&attr,
5738 &civetweb_worker_stacks[zephyr_worker_stack_index++],
5739 ZEPHYR_STACK_SIZE);
5740#elif defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5741 /* Compile-time option to control stack size,
5742 * e.g. -DUSE_STACK_SIZE=16384 */
5743 (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
5744#endif /* defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 */
5745
5746 result = pthread_create(&thread_id, &attr, func, param);
5747 pthread_attr_destroy(&attr);
5748 if ((result == 0) && (threadidptr != NULL)) {
5749 *threadidptr = thread_id;
5750 }
5751 return result;
5752}
5753
5754
5755/* Wait for a thread to finish. */
5756static int
5758{
5759 int result;
5760
5761 result = pthread_join(threadid, NULL);
5762 return result;
5763}
5764
5765
5766#if !defined(NO_CGI)
5767static pid_t
5769 const char *prog,
5770 char *envblk,
5771 char *envp[],
5772 int fdin[2],
5773 int fdout[2],
5774 int fderr[2],
5775 const char *dir,
5776 int cgi_config_idx)
5777{
5778 pid_t pid;
5779 const char *interp;
5780
5781 (void)envblk;
5782
5783 if ((pid = fork()) == -1) {
5784 /* Parent */
5785 mg_cry_internal(conn, "%s: fork(): %s", __func__, strerror(ERRNO));
5786 } else if (pid != 0) {
5787 /* Make sure children close parent-side descriptors.
5788 * The caller will close the child-side immediately. */
5789 set_close_on_exec(fdin[1], conn, NULL); /* stdin write */
5790 set_close_on_exec(fdout[0], conn, NULL); /* stdout read */
5791 set_close_on_exec(fderr[0], conn, NULL); /* stderr read */
5792 } else {
5793 /* Child */
5794 if (chdir(dir) != 0) {
5796 conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
5797 } else if (dup2(fdin[0], 0) == -1) {
5798 mg_cry_internal(conn,
5799 "%s: dup2(%d, 0): %s",
5800 __func__,
5801 fdin[0],
5802 strerror(ERRNO));
5803 } else if (dup2(fdout[1], 1) == -1) {
5804 mg_cry_internal(conn,
5805 "%s: dup2(%d, 1): %s",
5806 __func__,
5807 fdout[1],
5808 strerror(ERRNO));
5809 } else if (dup2(fderr[1], 2) == -1) {
5810 mg_cry_internal(conn,
5811 "%s: dup2(%d, 2): %s",
5812 __func__,
5813 fderr[1],
5814 strerror(ERRNO));
5815 } else {
5816 struct sigaction sa;
5817
5818 /* Keep stderr and stdout in two different pipes.
5819 * Stdout will be sent back to the client,
5820 * stderr should go into a server error log. */
5821 (void)close(fdin[0]);
5822 (void)close(fdout[1]);
5823 (void)close(fderr[1]);
5824
5825 /* Close write end fdin and read end fdout and fderr */
5826 (void)close(fdin[1]);
5827 (void)close(fdout[0]);
5828 (void)close(fderr[0]);
5829
5830 /* After exec, all signal handlers are restored to their default
5831 * values, with one exception of SIGCHLD. According to
5832 * POSIX.1-2001 and Linux's implementation, SIGCHLD's handler
5833 * will leave unchanged after exec if it was set to be ignored.
5834 * Restore it to default action. */
5835 memset(&sa, 0, sizeof(sa));
5836 sa.sa_handler = SIG_DFL;
5837 sigaction(SIGCHLD, &sa, NULL);
5838
5839 interp = conn->dom_ctx->config[CGI_INTERPRETER + cgi_config_idx];
5840 if (interp == NULL) {
5841 /* no interpreter configured, call the program directly */
5842 (void)execle(prog, prog, NULL, envp);
5843 mg_cry_internal(conn,
5844 "%s: execle(%s): %s",
5845 __func__,
5846 prog,
5847 strerror(ERRNO));
5848 } else {
5849 /* call the configured interpreter */
5850 const char *interp_args =
5851 conn->dom_ctx
5852 ->config[CGI_INTERPRETER_ARGS + cgi_config_idx];
5853
5854 if ((interp_args != NULL) && (interp_args[0] != 0)) {
5855 (void)execle(interp, interp, interp_args, prog, NULL, envp);
5856 } else {
5857 (void)execle(interp, interp, prog, NULL, envp);
5858 }
5859 mg_cry_internal(conn,
5860 "%s: execle(%s %s): %s",
5861 __func__,
5862 interp,
5863 prog,
5864 strerror(ERRNO));
5865 }
5866 }
5867 exit(EXIT_FAILURE);
5868 }
5869
5870 return pid;
5871}
5872#endif /* !NO_CGI */
5873
5874
5875static int
5877{
5878 int flags = fcntl(sock, F_GETFL, 0);
5879 if (flags < 0) {
5880 return -1;
5881 }
5882
5883 if (fcntl(sock, F_SETFL, (flags | O_NONBLOCK)) < 0) {
5884 return -1;
5885 }
5886 return 0;
5887}
5888
5889static int
5891{
5892 int flags = fcntl(sock, F_GETFL, 0);
5893 if (flags < 0) {
5894 return -1;
5895 }
5896
5897 if (fcntl(sock, F_SETFL, flags & (~(int)(O_NONBLOCK))) < 0) {
5898 return -1;
5899 }
5900 return 0;
5901}
5902#endif /* _WIN32 / else */
5903
5904/* End of initial operating system specific define block. */
5905
5906
5907/* Get a random number (independent of C rand function) */
5908static uint64_t
5910{
5911 static uint64_t lfsr = 0; /* Linear feedback shift register */
5912 static uint64_t lcg = 0; /* Linear congruential generator */
5913 uint64_t now = mg_get_current_time_ns();
5914
5915 if (lfsr == 0) {
5916 /* lfsr will be only 0 if has not been initialized,
5917 * so this code is called only once. */
5918 lfsr = mg_get_current_time_ns();
5919 lcg = mg_get_current_time_ns();
5920 } else {
5921 /* Get the next step of both random number generators. */
5922 lfsr = (lfsr >> 1)
5923 | ((((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1)
5924 << 63);
5925 lcg = lcg * 6364136223846793005LL + 1442695040888963407LL;
5926 }
5927
5928 /* Combining two pseudo-random number generators and a high resolution
5929 * part
5930 * of the current server time will make it hard (impossible?) to guess
5931 * the
5932 * next number. */
5933 return (lfsr ^ lcg ^ now);
5934}
5935
5936
5937static int
5938mg_poll(struct mg_pollfd *pfd,
5939 unsigned int n,
5940 int milliseconds,
5941 const stop_flag_t *stop_flag)
5942{
5943 /* Call poll, but only for a maximum time of a few seconds.
5944 * This will allow to stop the server after some seconds, instead
5945 * of having to wait for a long socket timeout. */
5946 int ms_now = SOCKET_TIMEOUT_QUANTUM; /* Sleep quantum in ms */
5947
5948 int check_pollerr = 0;
5949 if ((n == 1) && ((pfd[0].events & POLLERR) == 0)) {
5950 /* If we wait for only one file descriptor, wait on error as well */
5951 pfd[0].events |= POLLERR;
5952 check_pollerr = 1;
5953 }
5954
5955 do {
5956 int result;
5957
5958 if (!STOP_FLAG_IS_ZERO(&*stop_flag)) {
5959 /* Shut down signal */
5960 return -2;
5961 }
5962
5963 if ((milliseconds >= 0) && (milliseconds < ms_now)) {
5964 ms_now = milliseconds;
5965 }
5966
5967 result = poll(pfd, n, ms_now);
5968 if (result != 0) {
5969 int err = ERRNO;
5970 if ((result == 1) || (!ERROR_TRY_AGAIN(err))) {
5971 /* Poll returned either success (1) or error (-1).
5972 * Forward both to the caller. */
5973 if ((check_pollerr)
5974 && ((pfd[0].revents & (POLLIN | POLLOUT | POLLERR))
5975 == POLLERR)) {
5976 /* One and only file descriptor returned error */
5977 return -1;
5978 }
5979 return result;
5980 }
5981 }
5982
5983 /* Poll returned timeout (0). */
5984 if (milliseconds > 0) {
5985 milliseconds -= ms_now;
5986 }
5987
5988 } while (milliseconds > 0);
5989
5990 /* timeout: return 0 */
5991 return 0;
5992}
5993
5994
5995/* Write data to the IO channel - opened file descriptor, socket or SSL
5996 * descriptor.
5997 * Return value:
5998 * >=0 .. number of bytes successfully written
5999 * -1 .. timeout
6000 * -2 .. error
6001 */
6002static int
6004 FILE *fp,
6005 SOCKET sock,
6006 SSL *ssl,
6007 const char *buf,
6008 int len,
6009 double timeout)
6010{
6011 uint64_t start = 0, now = 0, timeout_ns = 0;
6012 int n, err;
6013 unsigned ms_wait = SOCKET_TIMEOUT_QUANTUM; /* Sleep quantum in ms */
6014
6015#if defined(_WIN32)
6016 typedef int len_t;
6017#else
6018 typedef size_t len_t;
6019#endif
6020
6021 if (timeout > 0) {
6022 now = mg_get_current_time_ns();
6023 start = now;
6024 timeout_ns = (uint64_t)(timeout * 1.0E9);
6025 }
6026
6027 if (ctx == NULL) {
6028 return -2;
6029 }
6030
6031#if defined(NO_SSL) && !defined(USE_MBEDTLS)
6032 if (ssl) {
6033 return -2;
6034 }
6035#endif
6036
6037 /* Try to read until it succeeds, fails, times out, or the server
6038 * shuts down. */
6039 for (;;) {
6040
6041#if defined(USE_MBEDTLS)
6042 if (ssl != NULL) {
6043 n = mbed_ssl_write(ssl, (const unsigned char *)buf, len);
6044 if (n <= 0) {
6045 if ((n == MBEDTLS_ERR_SSL_WANT_READ)
6046 || (n == MBEDTLS_ERR_SSL_WANT_WRITE)
6047 || n == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) {
6048 n = 0;
6049 } else {
6050 fprintf(stderr, "SSL write failed, error %d\n", n);
6051 return -2;
6052 }
6053 } else {
6054 err = 0;
6055 }
6056 } else
6057#elif !defined(NO_SSL)
6058 if (ssl != NULL) {
6059 ERR_clear_error();
6060 n = SSL_write(ssl, buf, len);
6061 if (n <= 0) {
6062 err = SSL_get_error(ssl, n);
6063 if ((err == SSL_ERROR_SYSCALL) && (n == -1)) {
6064 err = ERRNO;
6065 } else if ((err == SSL_ERROR_WANT_READ)
6066 || (err == SSL_ERROR_WANT_WRITE)) {
6067 n = 0;
6068 } else {
6069 DEBUG_TRACE("SSL_write() failed, error %d", err);
6070 ERR_clear_error();
6071 return -2;
6072 }
6073 ERR_clear_error();
6074 } else {
6075 err = 0;
6076 }
6077 } else
6078#endif
6079
6080 if (fp != NULL) {
6081 n = (int)fwrite(buf, 1, (size_t)len, fp);
6082 if (ferror(fp)) {
6083 n = -1;
6084 err = ERRNO;
6085 } else {
6086 err = 0;
6087 }
6088 } else {
6089 n = (int)send(sock, buf, (len_t)len, MSG_NOSIGNAL);
6090 err = (n < 0) ? ERRNO : 0;
6091 if (ERROR_TRY_AGAIN(err)) {
6092 err = 0;
6093 n = 0;
6094 }
6095 if (n < 0) {
6096 /* shutdown of the socket at client side */
6097 return -2;
6098 }
6099 }
6100
6101 if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
6102 return -2;
6103 }
6104
6105 if ((n > 0) || ((n == 0) && (len == 0))) {
6106 /* some data has been read, or no data was requested */
6107 return n;
6108 }
6109 if (n < 0) {
6110 /* socket error - check errno */
6111 DEBUG_TRACE("send() failed, error %d", err);
6112
6113 /* TODO (mid): error handling depending on the error code.
6114 * These codes are different between Windows and Linux.
6115 * Currently there is no problem with failing send calls,
6116 * if there is a reproducible situation, it should be
6117 * investigated in detail.
6118 */
6119 return -2;
6120 }
6121
6122 /* Only in case n=0 (timeout), repeat calling the write function */
6123
6124 /* If send failed, wait before retry */
6125 if (fp != NULL) {
6126 /* For files, just wait a fixed time.
6127 * Maybe it helps, maybe not. */
6128 mg_sleep(5);
6129 } else {
6130 /* For sockets, wait for the socket using poll */
6131 struct mg_pollfd pfd[1];
6132 int pollres;
6133
6134 pfd[0].fd = sock;
6135 pfd[0].events = POLLOUT;
6136 pollres = mg_poll(pfd, 1, (int)(ms_wait), &(ctx->stop_flag));
6137 if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
6138 return -2;
6139 }
6140 if (pollres > 0) {
6141 continue;
6142 }
6143 }
6144
6145 if (timeout > 0) {
6146 now = mg_get_current_time_ns();
6147 if ((now - start) > timeout_ns) {
6148 /* Timeout */
6149 break;
6150 }
6151 }
6152 }
6153
6154 (void)err; /* Avoid unused warning if NO_SSL is set and DEBUG_TRACE is not
6155 used */
6156
6157 return -1;
6158}
6159
6160
6161static int
6163 FILE *fp,
6164 SOCKET sock,
6165 SSL *ssl,
6166 const char *buf,
6167 int len)
6168{
6169 double timeout = -1.0;
6170 int n, nwritten = 0;
6171
6172 if (ctx == NULL) {
6173 return -1;
6174 }
6175
6176 if (ctx->dd.config[REQUEST_TIMEOUT]) {
6177 timeout = atoi(ctx->dd.config[REQUEST_TIMEOUT]) / 1000.0;
6178 }
6179 if (timeout <= 0.0) {
6180 timeout = strtod(config_options[REQUEST_TIMEOUT].default_value, NULL)
6181 / 1000.0;
6182 }
6183
6184 while ((len > 0) && STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
6185 n = push_inner(ctx, fp, sock, ssl, buf + nwritten, len, timeout);
6186 if (n < 0) {
6187 if (nwritten == 0) {
6188 nwritten = -1; /* Propagate the error */
6189 }
6190 break;
6191 } else if (n == 0) {
6192 break; /* No more data to write */
6193 } else {
6194 nwritten += n;
6195 len -= n;
6196 }
6197 }
6198
6199 return nwritten;
6200}
6201
6202
6203/* Read from IO channel - opened file descriptor, socket, or SSL descriptor.
6204 * Return value:
6205 * >=0 .. number of bytes successfully read
6206 * -1 .. timeout
6207 * -2 .. error
6208 */
6209static int
6210pull_inner(FILE *fp,
6211 struct mg_connection *conn,
6212 char *buf,
6213 int len,
6214 double timeout)
6215{
6216 int nread, err = 0;
6217
6218#if defined(_WIN32)
6219 typedef int len_t;
6220#else
6221 typedef size_t len_t;
6222#endif
6223
6224 /* We need an additional wait loop around this, because in some cases
6225 * with TLSwe may get data from the socket but not from SSL_read.
6226 * In this case we need to repeat at least once.
6227 */
6228
6229 if (fp != NULL) {
6230 /* Use read() instead of fread(), because if we're reading from the
6231 * CGI pipe, fread() may block until IO buffer is filled up. We
6232 * cannot afford to block and must pass all read bytes immediately
6233 * to the client. */
6234 nread = (int)read(fileno(fp), buf, (size_t)len);
6235
6236 err = (nread < 0) ? ERRNO : 0;
6237 if ((nread == 0) && (len > 0)) {
6238 /* Should get data, but got EOL */
6239 return -2;
6240 }
6241
6242#if defined(USE_MBEDTLS)
6243 } else if (conn->ssl != NULL) {
6244 struct mg_pollfd pfd[1];
6245 int to_read;
6246 int pollres;
6247
6248 to_read = mbedtls_ssl_get_bytes_avail(conn->ssl);
6249
6250 if (to_read > 0) {
6251 /* We already know there is no more data buffered in conn->buf
6252 * but there is more available in the SSL layer. So don't poll
6253 * conn->client.sock yet. */
6254
6255 pollres = 1;
6256 if (to_read > len)
6257 to_read = len;
6258 } else {
6259 pfd[0].fd = conn->client.sock;
6260 pfd[0].events = POLLIN;
6261
6262 to_read = len;
6263
6264 pollres = mg_poll(pfd,
6265 1,
6266 (int)(timeout * 1000.0),
6267 &(conn->phys_ctx->stop_flag));
6268
6269 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6270 return -2;
6271 }
6272 }
6273
6274 if (pollres > 0) {
6275 nread = mbed_ssl_read(conn->ssl, (unsigned char *)buf, to_read);
6276 if (nread <= 0) {
6277 if ((nread == MBEDTLS_ERR_SSL_WANT_READ)
6278 || (nread == MBEDTLS_ERR_SSL_WANT_WRITE)
6279 || nread == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) {
6280 nread = 0;
6281 } else {
6282 fprintf(stderr, "SSL read failed, error %d\n", nread);
6283 return -2;
6284 }
6285 } else {
6286 err = 0;
6287 }
6288
6289 } else if (pollres < 0) {
6290 /* Error */
6291 return -2;
6292 } else {
6293 /* pollres = 0 means timeout */
6294 nread = 0;
6295 }
6296
6297#elif !defined(NO_SSL)
6298 } else if (conn->ssl != NULL) {
6299 int ssl_pending;
6300 struct mg_pollfd pfd[1];
6301 int pollres;
6302
6303 if ((ssl_pending = SSL_pending(conn->ssl)) > 0) {
6304 /* We already know there is no more data buffered in conn->buf
6305 * but there is more available in the SSL layer. So don't poll
6306 * conn->client.sock yet. */
6307 if (ssl_pending > len) {
6308 ssl_pending = len;
6309 }
6310 pollres = 1;
6311 } else {
6312 pfd[0].fd = conn->client.sock;
6313 pfd[0].events = POLLIN;
6314 pollres = mg_poll(pfd,
6315 1,
6316 (int)(timeout * 1000.0),
6317 &(conn->phys_ctx->stop_flag));
6318 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6319 return -2;
6320 }
6321 }
6322 if (pollres > 0) {
6323 ERR_clear_error();
6324 nread =
6325 SSL_read(conn->ssl, buf, (ssl_pending > 0) ? ssl_pending : len);
6326 if (nread <= 0) {
6327 err = SSL_get_error(conn->ssl, nread);
6328 if ((err == SSL_ERROR_SYSCALL) && (nread == -1)) {
6329 err = ERRNO;
6330 } else if ((err == SSL_ERROR_WANT_READ)
6331 || (err == SSL_ERROR_WANT_WRITE)) {
6332 nread = 0;
6333 } else {
6334 /* All errors should return -2 */
6335 DEBUG_TRACE("SSL_read() failed, error %d", err);
6336 ERR_clear_error();
6337 return -2;
6338 }
6339 ERR_clear_error();
6340 } else {
6341 err = 0;
6342 }
6343 } else if (pollres < 0) {
6344 /* Error */
6345 return -2;
6346 } else {
6347 /* pollres = 0 means timeout */
6348 nread = 0;
6349 }
6350#endif
6351
6352 } else {
6353 struct mg_pollfd pfd[1];
6354 int pollres;
6355
6356 pfd[0].fd = conn->client.sock;
6357 pfd[0].events = POLLIN;
6358 pollres = mg_poll(pfd,
6359 1,
6360 (int)(timeout * 1000.0),
6361 &(conn->phys_ctx->stop_flag));
6362 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6363 return -2;
6364 }
6365 if (pollres > 0) {
6366 nread = (int)recv(conn->client.sock, buf, (len_t)len, 0);
6367 err = (nread < 0) ? ERRNO : 0;
6368 if (nread <= 0) {
6369 /* shutdown of the socket at client side */
6370 return -2;
6371 }
6372 } else if (pollres < 0) {
6373 /* error calling poll */
6374 return -2;
6375 } else {
6376 /* pollres = 0 means timeout */
6377 nread = 0;
6378 }
6379 }
6380
6381 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6382 return -2;
6383 }
6384
6385 if ((nread > 0) || ((nread == 0) && (len == 0))) {
6386 /* some data has been read, or no data was requested */
6387 return nread;
6388 }
6389
6390 if (nread < 0) {
6391 /* socket error - check errno */
6392#if defined(_WIN32)
6393 if (err == WSAEWOULDBLOCK) {
6394 /* TODO (low): check if this is still required */
6395 /* standard case if called from close_socket_gracefully */
6396 return -2;
6397 } else if (err == WSAETIMEDOUT) {
6398 /* TODO (low): check if this is still required */
6399 /* timeout is handled by the while loop */
6400 return 0;
6401 } else if (err == WSAECONNABORTED) {
6402 /* See https://www.chilkatsoft.com/p/p_299.asp */
6403 return -2;
6404 } else {
6405 DEBUG_TRACE("recv() failed, error %d", err);
6406 return -2;
6407 }
6408#else
6409 /* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases,
6410 * if the timeout is reached and if the socket was set to non-
6411 * blocking in close_socket_gracefully, so we can not distinguish
6412 * here. We have to wait for the timeout in both cases for now.
6413 */
6414 if (ERROR_TRY_AGAIN(err)) {
6415 /* TODO (low): check if this is still required */
6416 /* EAGAIN/EWOULDBLOCK:
6417 * standard case if called from close_socket_gracefully
6418 * => should return -1 */
6419 /* or timeout occurred
6420 * => the code must stay in the while loop */
6421
6422 /* EINTR can be generated on a socket with a timeout set even
6423 * when SA_RESTART is effective for all relevant signals
6424 * (see signal(7)).
6425 * => stay in the while loop */
6426 } else {
6427 DEBUG_TRACE("recv() failed, error %d", err);
6428 return -2;
6429 }
6430#endif
6431 }
6432
6433 /* Timeout occurred, but no data available. */
6434 return -1;
6435}
6436
6437
6438static int
6439pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
6440{
6441 int n, nread = 0;
6442 double timeout = -1.0;
6443 uint64_t start_time = 0, now = 0, timeout_ns = 0;
6444
6445 if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
6446 timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
6447 }
6448 if (timeout <= 0.0) {
6449 timeout = strtod(config_options[REQUEST_TIMEOUT].default_value, NULL)
6450 / 1000.0;
6451 }
6452 start_time = mg_get_current_time_ns();
6453 timeout_ns = (uint64_t)(timeout * 1.0E9);
6454
6455 while ((len > 0) && STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6456 n = pull_inner(fp, conn, buf + nread, len, timeout);
6457 if (n == -2) {
6458 if (nread == 0) {
6459 nread = -1; /* Propagate the error */
6460 }
6461 break;
6462 } else if (n == -1) {
6463 /* timeout */
6464 if (timeout >= 0.0) {
6465 now = mg_get_current_time_ns();
6466 if ((now - start_time) <= timeout_ns) {
6467 continue;
6468 }
6469 }
6470 break;
6471 } else if (n == 0) {
6472 break; /* No more data to read */
6473 } else {
6474 nread += n;
6475 len -= n;
6476 }
6477 }
6478
6479 return nread;
6480}
6481
6482
6483static void
6485{
6486 char buf[MG_BUF_LEN];
6487
6488 while (mg_read(conn, buf, sizeof(buf)) > 0)
6489 ;
6490}
6491
6492
6493static int
6494mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
6495{
6496 int64_t content_len, n, buffered_len, nread;
6497 int64_t len64 =
6498 (int64_t)((len > INT_MAX) ? INT_MAX : len); /* since the return value is
6499 * int, we may not read more
6500 * bytes */
6501 const char *body;
6502
6503 if (conn == NULL) {
6504 return 0;
6505 }
6506
6507 /* If Content-Length is not set for a response with body data,
6508 * we do not know in advance how much data should be read. */
6509 content_len = conn->content_len;
6510 if (content_len < 0) {
6511 /* The body data is completed when the connection is closed. */
6512 content_len = INT64_MAX;
6513 }
6514
6515 nread = 0;
6516 if (conn->consumed_content < content_len) {
6517 /* Adjust number of bytes to read. */
6518 int64_t left_to_read = content_len - conn->consumed_content;
6519 if (left_to_read < len64) {
6520 /* Do not read more than the total content length of the
6521 * request.
6522 */
6523 len64 = left_to_read;
6524 }
6525
6526 /* Return buffered data */
6527 buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len
6528 - conn->consumed_content;
6529 if (buffered_len > 0) {
6530 if (len64 < buffered_len) {
6531 buffered_len = len64;
6532 }
6533 body = conn->buf + conn->request_len + conn->consumed_content;
6534 memcpy(buf, body, (size_t)buffered_len);
6535 len64 -= buffered_len;
6536 conn->consumed_content += buffered_len;
6537 nread += buffered_len;
6538 buf = (char *)buf + buffered_len;
6539 }
6540
6541 /* We have returned all buffered data. Read new data from the remote
6542 * socket.
6543 */
6544 if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) {
6545 conn->consumed_content += n;
6546 nread += n;
6547 } else {
6548 nread = ((nread > 0) ? nread : n);
6549 }
6550 }
6551 return (int)nread;
6552}
6553
6554
6555/* Forward declarations */
6556static void handle_request(struct mg_connection *);
6557static void log_access(const struct mg_connection *);
6558
6559
6560/* Handle request, update statistics and call access log */
6561static void
6563{
6564#if defined(USE_SERVER_STATS)
6565 struct timespec tnow;
6566 conn->conn_state = 4; /* processing */
6567#endif
6568
6569 handle_request(conn);
6570
6571
6572#if defined(USE_SERVER_STATS)
6573 conn->conn_state = 5; /* processed */
6574
6575 clock_gettime(CLOCK_MONOTONIC, &tnow);
6576 conn->processing_time = mg_difftimespec(&tnow, &(conn->req_time));
6577
6578 mg_atomic_add64(&(conn->phys_ctx->total_data_read), conn->consumed_content);
6579 mg_atomic_add64(&(conn->phys_ctx->total_data_written),
6580 conn->num_bytes_sent);
6581#endif
6582
6583 DEBUG_TRACE("%s", "handle_request done");
6584
6585 if (conn->phys_ctx->callbacks.end_request != NULL) {
6586 conn->phys_ctx->callbacks.end_request(conn, conn->status_code);
6587 DEBUG_TRACE("%s", "end_request callback done");
6588 }
6589 log_access(conn);
6590}
6591
6592
6593#if defined(USE_HTTP2)
6594#if defined(NO_SSL)
6595#error "HTTP2 requires ALPN, ALPN requires SSL/TLS"
6596#endif
6597#define USE_ALPN
6598#include "http2.inl"
6599/* Not supported with HTTP/2 */
6600#define HTTP1_only \
6601 { \
6602 if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) { \
6603 http2_must_use_http1(conn); \
6604 DEBUG_TRACE("%s", "must use HTTP/1.x"); \
6605 return; \
6606 } \
6607 }
6608#else
6609#define HTTP1_only
6610#endif
6611
6612
6613CIVETWEB_API int
6614mg_read(struct mg_connection *conn, void *buf, size_t len)
6615{
6616 if (len > INT_MAX) {
6617 len = INT_MAX;
6618 }
6619
6620 if (conn == NULL) {
6621 return 0;
6622 }
6623
6624 if (conn->is_chunked) {
6625 size_t all_read = 0;
6626
6627 while (len > 0) {
6628 if (conn->is_chunked >= 3) {
6629 /* No more data left to read */
6630 return 0;
6631 }
6632 if (conn->is_chunked != 1) {
6633 /* Has error */
6634 return -1;
6635 }
6636
6637 if (conn->consumed_content != conn->content_len) {
6638 /* copy from the current chunk */
6639 int read_ret = mg_read_inner(conn, (char *)buf + all_read, len);
6640
6641 if (read_ret < 1) {
6642 /* read error */
6643 conn->is_chunked = 2;
6644 return -1;
6645 }
6646
6647 all_read += (size_t)read_ret;
6648 len -= (size_t)read_ret;
6649
6650 if (conn->consumed_content == conn->content_len) {
6651 /* Add data bytes in the current chunk have been read,
6652 * so we are expecting \r\n now. */
6653 char x[2];
6654 conn->content_len += 2;
6655 if ((mg_read_inner(conn, x, 2) != 2) || (x[0] != '\r')
6656 || (x[1] != '\n')) {
6657 /* Protocol violation */
6658 conn->is_chunked = 2;
6659 return -1;
6660 }
6661 }
6662
6663 } else {
6664 /* fetch a new chunk */
6665 size_t i;
6666 char lenbuf[64];
6667 char *end = NULL;
6668 unsigned long chunkSize = 0;
6669
6670 for (i = 0; i < (sizeof(lenbuf) - 1); i++) {
6671 conn->content_len++;
6672 if (mg_read_inner(conn, lenbuf + i, 1) != 1) {
6673 lenbuf[i] = 0;
6674 }
6675 if ((i > 0) && (lenbuf[i] == ';')) {
6676 // chunk extension --> skip chars until next CR
6677 //
6678 // RFC 2616, 3.6.1 Chunked Transfer Coding
6679 // (https://www.rfc-editor.org/rfc/rfc2616#page-25)
6680 //
6681 // chunk = chunk-size [ chunk-extension ] CRLF
6682 // chunk-data CRLF
6683 // ...
6684 // chunk-extension= *( ";" chunk-ext-name [ "="
6685 // chunk-ext-val ] )
6686 do
6687 ++conn->content_len;
6688 while (mg_read_inner(conn, lenbuf + i, 1) == 1
6689 && lenbuf[i] != '\r');
6690 }
6691 if ((i > 0) && (lenbuf[i] == '\r')
6692 && (lenbuf[i - 1] != '\r')) {
6693 continue;
6694 }
6695 if ((i > 1) && (lenbuf[i] == '\n')
6696 && (lenbuf[i - 1] == '\r')) {
6697 lenbuf[i + 1] = 0;
6698 chunkSize = strtoul(lenbuf, &end, 16);
6699 if (chunkSize == 0) {
6700 /* regular end of content */
6701 conn->is_chunked = 3;
6702 }
6703 break;
6704 }
6705 if (!isxdigit((unsigned char)lenbuf[i])) {
6706 /* illegal character for chunk length */
6707 conn->is_chunked = 2;
6708 return -1;
6709 }
6710 }
6711 if ((end == NULL) || (*end != '\r')) {
6712 /* chunksize not set correctly */
6713 conn->is_chunked = 2;
6714 return -1;
6715 }
6716 if (conn->is_chunked == 3) {
6717 /* try discarding trailer for keep-alive */
6718
6719 // We found the last chunk (length 0) including the
6720 // CRLF that terminates that chunk. Now follows a possibly
6721 // empty trailer and a final CRLF.
6722 //
6723 // see RFC 2616, 3.6.1 Chunked Transfer Coding
6724 // (https://www.rfc-editor.org/rfc/rfc2616#page-25)
6725 //
6726 // Chunked-Body = *chunk
6727 // last-chunk
6728 // trailer
6729 // CRLF
6730 // ...
6731 // last-chunk = 1*("0") [ chunk-extension ] CRLF
6732 // ...
6733 // trailer = *(entity-header CRLF)
6734
6735 int crlf_count = 2; // one CRLF already determined
6736
6737 while (crlf_count < 4 && conn->is_chunked == 3) {
6738 ++conn->content_len;
6739 if (mg_read_inner(conn, lenbuf, 1) == 1) {
6740 if ((crlf_count == 0 || crlf_count == 2)) {
6741 if (lenbuf[0] == '\r')
6742 ++crlf_count;
6743 else
6744 crlf_count = 0;
6745 } else {
6746 // previous character was a CR
6747 // --> next character must be LF
6748
6749 if (lenbuf[0] == '\n')
6750 ++crlf_count;
6751 else
6752 conn->is_chunked = 2;
6753 }
6754 } else
6755 // premature end of trailer
6756 conn->is_chunked = 2;
6757 }
6758
6759 if (conn->is_chunked == 2)
6760 return -1;
6761 else
6762 conn->is_chunked = 4;
6763
6764 break;
6765 }
6766
6767 /* append a new chunk */
6768 conn->content_len += (int64_t)chunkSize;
6769 }
6770 }
6771
6772 return (int)all_read;
6773 }
6774 return mg_read_inner(conn, buf, len);
6775}
6776
6777
6778CIVETWEB_API int
6779mg_write(struct mg_connection *conn, const void *buf, size_t len)
6780{
6781 time_t now;
6782 int n, total, allowed;
6783
6784 if (conn == NULL) {
6785 return 0;
6786 }
6787 if (len > INT_MAX) {
6788 return -1;
6789 }
6790
6791 /* Mark connection as "data sent" */
6792 conn->request_state = 10;
6793#if defined(USE_HTTP2)
6794 if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
6795 http2_data_frame_head(conn, len, 0);
6796 }
6797#endif
6798
6799 if (conn->throttle > 0) {
6800 if ((now = time(NULL)) != conn->last_throttle_time) {
6801 conn->last_throttle_time = now;
6802 conn->last_throttle_bytes = 0;
6803 }
6804 allowed = conn->throttle - conn->last_throttle_bytes;
6805 if (allowed > (int)len) {
6806 allowed = (int)len;
6807 }
6808
6809 total = push_all(conn->phys_ctx,
6810 NULL,
6811 conn->client.sock,
6812 conn->ssl,
6813 (const char *)buf,
6814 allowed);
6815
6816 if (total == allowed) {
6817
6818 buf = (const char *)buf + total;
6819 conn->last_throttle_bytes += total;
6820 while ((total < (int)len)
6821 && STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6822 allowed = (conn->throttle > ((int)len - total))
6823 ? (int)len - total
6824 : conn->throttle;
6825
6826 n = push_all(conn->phys_ctx,
6827 NULL,
6828 conn->client.sock,
6829 conn->ssl,
6830 (const char *)buf,
6831 allowed);
6832
6833 if (n != allowed) {
6834 break;
6835 }
6836 sleep(1);
6837 conn->last_throttle_bytes = allowed;
6838 conn->last_throttle_time = time(NULL);
6839 buf = (const char *)buf + n;
6840 total += n;
6841 }
6842 }
6843 } else {
6844 total = push_all(conn->phys_ctx,
6845 NULL,
6846 conn->client.sock,
6847 conn->ssl,
6848 (const char *)buf,
6849 (int)len);
6850 }
6851 if (total > 0) {
6852 conn->num_bytes_sent += total;
6853 }
6854 return total;
6855}
6856
6857
6858/* Send a chunk, if "Transfer-Encoding: chunked" is used */
6859CIVETWEB_API int
6861 const char *chunk,
6862 unsigned int chunk_len)
6863{
6864 char lenbuf[16];
6865 size_t lenbuf_len;
6866 int ret;
6867 int t;
6868
6869 /* First store the length information in a text buffer. */
6870 sprintf(lenbuf, "%x\r\n", chunk_len);
6871 lenbuf_len = strlen(lenbuf);
6872
6873 /* Then send length information, chunk and terminating \r\n. */
6874 ret = mg_write(conn, lenbuf, lenbuf_len);
6875 if (ret != (int)lenbuf_len) {
6876 return -1;
6877 }
6878 t = ret;
6879
6880 ret = mg_write(conn, chunk, chunk_len);
6881 if (ret != (int)chunk_len) {
6882 return -1;
6883 }
6884 t += ret;
6885
6886 ret = mg_write(conn, "\r\n", 2);
6887 if (ret != 2) {
6888 return -1;
6889 }
6890 t += ret;
6891
6892 return t;
6893}
6894
6895
6896#if defined(GCC_DIAGNOSTIC)
6897/* This block forwards format strings to printf implementations,
6898 * so we need to disable the format-nonliteral warning. */
6899#pragma GCC diagnostic push
6900#pragma GCC diagnostic ignored "-Wformat-nonliteral"
6901#endif
6902
6903
6904/* Alternative alloc_vprintf() for non-compliant C runtimes */
6905static int
6906alloc_vprintf2(char **buf, const char *fmt, va_list ap)
6907{
6908 va_list ap_copy;
6909 size_t size = MG_BUF_LEN / 4;
6910 int len = -1;
6911
6912 *buf = NULL;
6913 while (len < 0) {
6914 if (*buf) {
6915 mg_free(*buf);
6916 }
6917
6918 size *= 4;
6919 *buf = (char *)mg_malloc(size);
6920 if (!*buf) {
6921 break;
6922 }
6923
6924 va_copy(ap_copy, ap);
6925 len = vsnprintf_impl(*buf, size - 1, fmt, ap_copy);
6926 va_end(ap_copy);
6927 (*buf)[size - 1] = 0;
6928 }
6929
6930 return len;
6931}
6932
6933
6934/* Print message to buffer. If buffer is large enough to hold the message,
6935 * return buffer. If buffer is to small, allocate large enough buffer on
6936 * heap,
6937 * and return allocated buffer. */
6938static int
6939alloc_vprintf(char **out_buf,
6940 char *prealloc_buf,
6941 size_t prealloc_size,
6942 const char *fmt,
6943 va_list ap)
6944{
6945 va_list ap_copy;
6946 int len;
6947
6948 /* Windows is not standard-compliant, and vsnprintf() returns -1 if
6949 * buffer is too small. Also, older versions of msvcrt.dll do not have
6950 * _vscprintf(). However, if size is 0, vsnprintf() behaves correctly.
6951 * Therefore, we make two passes: on first pass, get required message
6952 * length.
6953 * On second pass, actually print the message. */
6954 va_copy(ap_copy, ap);
6955 len = vsnprintf_impl(NULL, 0, fmt, ap_copy);
6956 va_end(ap_copy);
6957
6958 if (len < 0) {
6959 /* C runtime is not standard compliant, vsnprintf() returned -1.
6960 * Switch to alternative code path that uses incremental
6961 * allocations.
6962 */
6963 va_copy(ap_copy, ap);
6964 len = alloc_vprintf2(out_buf, fmt, ap_copy);
6965 va_end(ap_copy);
6966
6967 } else if ((size_t)(len) >= prealloc_size) {
6968 /* The pre-allocated buffer not large enough. */
6969 /* Allocate a new buffer. */
6970 *out_buf = (char *)mg_malloc((size_t)(len) + 1);
6971 if (!*out_buf) {
6972 /* Allocation failed. Return -1 as "out of memory" error. */
6973 return -1;
6974 }
6975 /* Buffer allocation successful. Store the string there. */
6976 va_copy(ap_copy, ap);
6978 vsnprintf_impl(*out_buf, (size_t)(len) + 1, fmt, ap_copy));
6979 va_end(ap_copy);
6980
6981 } else {
6982 /* The pre-allocated buffer is large enough.
6983 * Use it to store the string and return the address. */
6984 va_copy(ap_copy, ap);
6986 vsnprintf_impl(prealloc_buf, prealloc_size, fmt, ap_copy));
6987 va_end(ap_copy);
6988 *out_buf = prealloc_buf;
6989 }
6990
6991 return len;
6992}
6993
6994
6995static int
6996alloc_printf(char **out_buf, const char *fmt, ...)
6997{
6998 va_list ap;
6999 int result;
7000
7001 va_start(ap, fmt);
7002 result = alloc_vprintf(out_buf, NULL, 0, fmt, ap);
7003 va_end(ap);
7004
7005 return result;
7006}
7007
7008
7009#if defined(GCC_DIAGNOSTIC)
7010/* Enable format-nonliteral warning again. */
7011#pragma GCC diagnostic pop
7012#endif
7013
7014
7015static int
7016mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap)
7017{
7018 char mem[MG_BUF_LEN];
7019 char *buf = NULL;
7020 int len;
7021
7022 if ((len = alloc_vprintf(&buf, mem, sizeof(mem), fmt, ap)) > 0) {
7023 len = mg_write(conn, buf, (size_t)len);
7024 }
7025 if (buf != mem) {
7026 mg_free(buf);
7027 }
7028
7029 return len;
7030}
7031
7032
7033CIVETWEB_API int
7034mg_printf(struct mg_connection *conn, const char *fmt, ...)
7035{
7036 va_list ap;
7037 int result;
7038
7039 va_start(ap, fmt);
7040 result = mg_vprintf(conn, fmt, ap);
7041 va_end(ap);
7042
7043 return result;
7044}
7045
7046
7047CIVETWEB_API int
7048mg_url_decode(const char *src,
7049 int src_len,
7050 char *dst,
7051 int dst_len,
7052 int is_form_url_encoded)
7053{
7054 int i, j, a, b;
7055#define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W'))
7056
7057 for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) {
7058 if ((i < src_len - 2) && (src[i] == '%')
7059 && isxdigit((unsigned char)src[i + 1])
7060 && isxdigit((unsigned char)src[i + 2])) {
7061 a = tolower((unsigned char)src[i + 1]);
7062 b = tolower((unsigned char)src[i + 2]);
7063 dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b));
7064 i += 2;
7065 } else if (is_form_url_encoded && (src[i] == '+')) {
7066 dst[j] = ' ';
7067 } else {
7068 dst[j] = src[i];
7069 }
7070 }
7071
7072 dst[j] = '\0'; /* Null-terminate the destination */
7073
7074 return (i >= src_len) ? j : -1;
7075}
7076
7077
7078/* form url decoding of an entire string */
7079static void
7081{
7082 int len = (int)strlen(buf);
7083 (void)mg_url_decode(buf, len, buf, len + 1, 1);
7084}
7085
7086
7087CIVETWEB_API int
7088mg_get_var(const char *data,
7089 size_t data_len,
7090 const char *name,
7091 char *dst,
7092 size_t dst_len)
7093{
7094 return mg_get_var2(data, data_len, name, dst, dst_len, 0);
7095}
7096
7097
7098CIVETWEB_API int
7099mg_get_var2(const char *data,
7100 size_t data_len,
7101 const char *name,
7102 char *dst,
7103 size_t dst_len,
7104 size_t occurrence)
7105{
7106 const char *p, *e, *s;
7107 size_t name_len;
7108 int len;
7109
7110 if ((dst == NULL) || (dst_len == 0)) {
7111 len = -2;
7112 } else if ((data == NULL) || (name == NULL) || (data_len == 0)) {
7113 len = -1;
7114 dst[0] = '\0';
7115 } else {
7116 name_len = strlen(name);
7117 e = data + data_len;
7118 len = -1;
7119 dst[0] = '\0';
7120
7121 /* data is "var1=val1&var2=val2...". Find variable first */
7122 for (p = data; p + name_len < e; p++) {
7123 if (((p == data) || (p[-1] == '&')) && (p[name_len] == '=')
7124 && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
7125 /* Point p to variable value */
7126 p += name_len + 1;
7127
7128 /* Point s to the end of the value */
7129 s = (const char *)memchr(p, '&', (size_t)(e - p));
7130 if (s == NULL) {
7131 s = e;
7132 }
7133 DEBUG_ASSERT(s >= p);
7134 if (s < p) {
7135 return -3;
7136 }
7137
7138 /* Decode variable into destination buffer */
7139 len = mg_url_decode(p, (int)(s - p), dst, (int)dst_len, 1);
7140
7141 /* Redirect error code from -1 to -2 (destination buffer too
7142 * small). */
7143 if (len == -1) {
7144 len = -2;
7145 }
7146 break;
7147 }
7148 }
7149 }
7150
7151 return len;
7152}
7153
7154
7155/* split a string "key1=val1&key2=val2" into key/value pairs */
7156CIVETWEB_API int
7158 struct mg_header *form_fields,
7159 unsigned num_form_fields)
7160{
7161 char *b;
7162 int i;
7163 int num = 0;
7164
7165 if (data == NULL) {
7166 /* parameter error */
7167 return -1;
7168 }
7169
7170 if ((form_fields == NULL) && (num_form_fields == 0)) {
7171 /* determine the number of expected fields */
7172 if (data[0] == 0) {
7173 return 0;
7174 }
7175 /* count number of & to return the number of key-value-pairs */
7176 num = 1;
7177 while (*data) {
7178 if (*data == '&') {
7179 num++;
7180 }
7181 data++;
7182 }
7183 return num;
7184 }
7185
7186 if ((form_fields == NULL) || ((int)num_form_fields <= 0)) {
7187 /* parameter error */
7188 return -1;
7189 }
7190
7191 for (i = 0; i < (int)num_form_fields; i++) {
7192 /* extract key-value pairs from input data */
7193 while ((*data == ' ') || (*data == '\t')) {
7194 /* skip initial spaces */
7195 data++;
7196 }
7197 if (*data == 0) {
7198 /* end of string reached */
7199 break;
7200 }
7201 form_fields[num].name = data;
7202
7203 /* find & or = */
7204 b = data;
7205 while ((*b != 0) && (*b != '&') && (*b != '=')) {
7206 b++;
7207 }
7208
7209 if (*b == 0) {
7210 /* last key without value */
7211 form_fields[num].value = NULL;
7212 } else if (*b == '&') {
7213 /* mid key without value */
7214 form_fields[num].value = NULL;
7215 } else {
7216 /* terminate string */
7217 *b = 0;
7218 /* value starts after '=' */
7219 data = b + 1;
7220 form_fields[num].value = data;
7221 }
7222
7223 /* new field is stored */
7224 num++;
7225
7226 /* find a next key */
7227 b = strchr(data, '&');
7228 if (b == 0) {
7229 /* no more data */
7230 break;
7231 } else {
7232 /* terminate value of last field at '&' */
7233 *b = 0;
7234 /* next key-value-pairs starts after '&' */
7235 data = b + 1;
7236 }
7237 }
7238
7239 /* Decode all values */
7240 for (i = 0; i < num; i++) {
7241 if (form_fields[i].name) {
7242 url_decode_in_place((char *)form_fields[i].name);
7243 }
7244 if (form_fields[i].value) {
7245 url_decode_in_place((char *)form_fields[i].value);
7246 }
7247 }
7248
7249 /* return number of fields found */
7250 return num;
7251}
7252
7253
7254/* HCP24: some changes to compare whole var_name */
7255CIVETWEB_API int
7256mg_get_cookie(const char *cookie_header,
7257 const char *var_name,
7258 char *dst,
7259 size_t dst_size)
7260{
7261 const char *s, *p, *end;
7262 int name_len, len = -1;
7263
7264 if ((dst == NULL) || (dst_size == 0)) {
7265 return -2;
7266 }
7267
7268 dst[0] = '\0';
7269 if ((var_name == NULL) || ((s = cookie_header) == NULL)) {
7270 return -1;
7271 }
7272
7273 name_len = (int)strlen(var_name);
7274 end = s + strlen(s);
7275 for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) {
7276 if (s[name_len] == '=') {
7277 /* HCP24: now check is it a substring or a full cookie name */
7278 if ((s == cookie_header) || (s[-1] == ' ')) {
7279 s += name_len + 1;
7280 if ((p = strchr(s, ' ')) == NULL) {
7281 p = end;
7282 }
7283 if (p[-1] == ';') {
7284 p--;
7285 }
7286 if ((*s == '"') && (p[-1] == '"') && (p > s + 1)) {
7287 s++;
7288 p--;
7289 }
7290 if ((size_t)(p - s) < dst_size) {
7291 len = (int)(p - s);
7292 mg_strlcpy(dst, s, (size_t)len + 1);
7293 } else {
7294 len = -3;
7295 }
7296 break;
7297 }
7298 }
7299 }
7300 return len;
7301}
7302
7303
7304CIVETWEB_API int
7305mg_base64_encode(const unsigned char *src,
7306 size_t src_len,
7307 char *dst,
7308 size_t *dst_len)
7309{
7310 static const char *b64 =
7311 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
7312 size_t i, j;
7313 int a, b, c;
7314
7315 if (dst_len != NULL) {
7316 /* Expected length including 0 termination: */
7317 /* IN 1 -> OUT 5, IN 2 -> OUT 5, IN 3 -> OUT 5, IN 4 -> OUT 9,
7318 * IN 5 -> OUT 9, IN 6 -> OUT 9, IN 7 -> OUT 13, etc. */
7319 size_t expected_len = ((src_len + 2) / 3) * 4 + 1;
7320 if (*dst_len < expected_len) {
7321 if (*dst_len > 0) {
7322 dst[0] = '\0';
7323 }
7324 *dst_len = expected_len;
7325 return 0;
7326 }
7327 }
7328
7329 for (i = j = 0; i < src_len; i += 3) {
7330 a = src[i];
7331 b = ((i + 1) >= src_len) ? 0 : src[i + 1];
7332 c = ((i + 2) >= src_len) ? 0 : src[i + 2];
7333
7334 dst[j++] = b64[a >> 2];
7335 dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
7336 if (i + 1 < src_len) {
7337 dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
7338 }
7339 if (i + 2 < src_len) {
7340 dst[j++] = b64[c & 63];
7341 }
7342 }
7343 while (j % 4 != 0) {
7344 dst[j++] = '=';
7345 }
7346 dst[j++] = '\0';
7347
7348 if (dst_len != NULL) {
7349 *dst_len = (size_t)j;
7350 }
7351
7352 /* Return -1 for "OK" */
7353 return -1;
7354}
7355
7356
7357static unsigned char
7358b64reverse(char letter)
7359{
7360 if ((letter >= 'A') && (letter <= 'Z')) {
7361 return (unsigned char)(letter - 'A');
7362 }
7363 if ((letter >= 'a') && (letter <= 'z')) {
7364 return (unsigned char)(letter - 'a' + 26);
7365 }
7366 if ((letter >= '0') && (letter <= '9')) {
7367 return (unsigned char)(letter - '0' + 52);
7368 }
7369 if (letter == '+') {
7370 return 62;
7371 }
7372 if (letter == '/') {
7373 return 63;
7374 }
7375 if (letter == '=') {
7376 return 255; /* normal end */
7377 }
7378 return 254; /* error */
7379}
7380
7381
7382CIVETWEB_API int
7383mg_base64_decode(const char *src,
7384 size_t src_len,
7385 unsigned char *dst,
7386 size_t *dst_len)
7387{
7388 size_t i;
7389 unsigned char a, b, c, d;
7390 size_t dst_len_limit = (size_t)-1;
7391 size_t dst_len_used = 0;
7392
7393 if (dst_len != NULL) {
7394 dst_len_limit = *dst_len;
7395 *dst_len = 0;
7396 }
7397
7398 for (i = 0; i < src_len; i += 4) {
7399 /* Read 4 characters from BASE64 string */
7400 a = b64reverse(src[i]);
7401 if (a >= 254) {
7402 return (int)i;
7403 }
7404
7405 b = b64reverse(((i + 1) >= src_len) ? 0 : src[i + 1]);
7406 if (b >= 254) {
7407 return (int)i + 1;
7408 }
7409
7410 c = b64reverse(((i + 2) >= src_len) ? 0 : src[i + 2]);
7411 if (c == 254) {
7412 return (int)i + 2;
7413 }
7414
7415 d = b64reverse(((i + 3) >= src_len) ? 0 : src[i + 3]);
7416 if (d == 254) {
7417 return (int)i + 3;
7418 }
7419
7420 /* Add first (of 3) decoded character */
7421 if (dst_len_used < dst_len_limit) {
7422 dst[dst_len_used] = (unsigned char)((unsigned char)(a << 2)
7423 + (unsigned char)(b >> 4));
7424 }
7425 dst_len_used++;
7426
7427 if (c != 255) {
7428 if (dst_len_used < dst_len_limit) {
7429
7430 dst[dst_len_used] = (unsigned char)((unsigned char)(b << 4)
7431 + (unsigned char)(c >> 2));
7432 }
7433 dst_len_used++;
7434 if (d != 255) {
7435 if (dst_len_used < dst_len_limit) {
7436 dst[dst_len_used] =
7437 (unsigned char)((unsigned char)(c << 6) + d);
7438 }
7439 dst_len_used++;
7440 }
7441 }
7442 }
7443
7444 /* Add terminating zero */
7445 if (dst_len_used < dst_len_limit) {
7446 dst[dst_len_used] = '\0';
7447 }
7448 dst_len_used++;
7449 if (dst_len != NULL) {
7450 *dst_len = dst_len_used;
7451 }
7452
7453 if (dst_len_used > dst_len_limit) {
7454 /* Out of memory */
7455 return 0;
7456 }
7457
7458 /* Return -1 for "OK" */
7459 return -1;
7460}
7461
7462
7463static int
7465{
7466 if (conn) {
7467 const char *s = conn->request_info.request_method;
7468 if (s != NULL) {
7469 /* PUT, DELETE, MKCOL, PATCH, LOCK, UNLOCK, PROPPATCH, MOVE, COPY */
7470 return (!strcmp(s, "PUT") || !strcmp(s, "DELETE")
7471 || !strcmp(s, "MKCOL") || !strcmp(s, "PATCH")
7472 || !strcmp(s, "LOCK") || !strcmp(s, "UNLOCK")
7473 || !strcmp(s, "PROPPATCH") || !strcmp(s, "MOVE")
7474 || !strcmp(s, "COPY"));
7475 }
7476 }
7477 return 0;
7478}
7479
7480
7481static int
7483{
7484 /* Note: Here we only have to identify the WebDav methods that need special
7485 * handling in the CivetWeb code - not all methods used in WebDav. In
7486 * particular, methods used on directories (when using Windows Explorer as
7487 * WebDav client).
7488 */
7489 if (conn) {
7490 const char *s = conn->request_info.request_method;
7491 if (s != NULL) {
7492 /* These are the civetweb builtin DAV methods */
7493 return (!strcmp(s, "PROPFIND") || !strcmp(s, "PROPPATCH")
7494 || !strcmp(s, "LOCK") || !strcmp(s, "UNLOCK")
7495 || !strcmp(s, "MOVE") || !strcmp(s, "COPY"));
7496 }
7497 }
7498 return 0;
7499}
7500
7501
7502#if !defined(NO_FILES)
7503static int
7505 struct mg_connection *conn, /* in: request (must be valid) */
7506 const char *filename /* in: filename (must be valid) */
7507)
7508{
7509#if !defined(NO_CGI)
7510 int cgi_config_idx, inc, max;
7511#endif
7512
7513#if defined(USE_LUA)
7514 if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS],
7515 filename)
7516 > 0) {
7517 return 1;
7518 }
7519#endif
7520#if defined(USE_DUKTAPE)
7521 if (match_prefix_strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
7522 filename)
7523 > 0) {
7524 return 1;
7525 }
7526#endif
7527#if !defined(NO_CGI)
7530 for (cgi_config_idx = 0; cgi_config_idx < max; cgi_config_idx += inc) {
7531 if ((conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx] != NULL)
7532 && (match_prefix_strlen(
7533 conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx],
7534 filename)
7535 > 0)) {
7536 return 1;
7537 }
7538 }
7539#endif
7540 /* filename and conn could be unused, if all preocessor conditions
7541 * are false (no script language supported). */
7542 (void)filename;
7543 (void)conn;
7544
7545 return 0;
7546}
7547
7548
7549static int
7551 struct mg_connection *conn, /* in: request (must be valid) */
7552 const char *filename /* in: filename (must be valid) */
7553)
7554{
7555#if defined(USE_LUA)
7556 if (match_prefix_strlen(conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
7557 filename)
7558 > 0) {
7559 return 1;
7560 }
7561#endif
7562 if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], filename)
7563 > 0) {
7564 return 1;
7565 }
7566 return 0;
7567}
7568
7569
7570/* For given directory path, substitute it to valid index file.
7571 * Return 1 if index file has been found, 0 if not found.
7572 * If the file is found, it's stats is returned in stp. */
7573static int
7575 char *path,
7576 size_t path_len,
7577 struct mg_file_stat *filestat)
7578{
7579 const char *list = conn->dom_ctx->config[INDEX_FILES];
7580 struct vec filename_vec;
7581 size_t n = strlen(path);
7582 int found = 0;
7583
7584 /* The 'path' given to us points to the directory. Remove all trailing
7585 * directory separator characters from the end of the path, and
7586 * then append single directory separator character. */
7587 while ((n > 0) && (path[n - 1] == '/')) {
7588 n--;
7589 }
7590 path[n] = '/';
7591
7592 /* Traverse index files list. For each entry, append it to the given
7593 * path and see if the file exists. If it exists, break the loop */
7594 while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
7595 /* Ignore too long entries that may overflow path buffer */
7596 if ((filename_vec.len + 1) > (path_len - (n + 1))) {
7597 continue;
7598 }
7599
7600 /* Prepare full path to the index file */
7601 mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
7602
7603 /* Does it exist? */
7604 if (mg_stat(conn, path, filestat)) {
7605 /* Yes it does, break the loop */
7606 found = 1;
7607 break;
7608 }
7609 }
7610
7611 /* If no index file exists, restore directory path */
7612 if (!found) {
7613 path[n] = '\0';
7614 }
7615
7616 return found;
7617}
7618#endif
7619
7620
7621static void
7622interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
7623 char *filename, /* out: filename */
7624 size_t filename_buf_len, /* in: size of filename buffer */
7625 struct mg_file_stat *filestat, /* out: file status structure */
7626 int *is_found, /* out: file found (directly) */
7627 int *is_script_resource, /* out: handled by a script? */
7628 int *is_websocket_request, /* out: websocket connection? */
7629 int *is_put_or_delete_request, /* out: put/delete a file? */
7630 int *is_webdav_request, /* out: webdav request? */
7631 int *is_template_text /* out: SSI file or LSP file? */
7632)
7633{
7634 char const *accept_encoding;
7635
7636#if !defined(NO_FILES)
7637 const char *uri = conn->request_info.local_uri;
7638 const char *root = conn->dom_ctx->config[DOCUMENT_ROOT];
7639 const char *rewrite;
7640 struct vec a, b;
7641 ptrdiff_t match_len;
7642 char gz_path[UTF8_PATH_MAX];
7643 int truncated;
7644#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
7645 char *tmp_str;
7646 size_t tmp_str_len, sep_pos;
7647 int allow_substitute_script_subresources;
7648#endif
7649#else
7650 (void)filename_buf_len; /* unused if NO_FILES is defined */
7651#endif
7652
7653 /* Step 1: Set all initially unknown outputs to zero */
7654 memset(filestat, 0, sizeof(*filestat));
7655 *filename = 0;
7656 *is_found = 0;
7657 *is_script_resource = 0;
7658 *is_template_text = 0;
7659
7660 /* Step 2: Classify the request method */
7661 /* Step 2a: Check if the request attempts to modify the file system */
7662 *is_put_or_delete_request = is_put_or_delete_method(conn);
7663 /* Step 2b: Check if the request uses WebDav method that requires special
7664 * handling */
7665 *is_webdav_request = is_civetweb_webdav_method(conn);
7666
7667 /* Step 3: Check if it is a websocket request, and modify the document
7668 * root if required */
7669#if defined(USE_WEBSOCKET)
7670 *is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET);
7671#if !defined(NO_FILES)
7672 if ((*is_websocket_request) && conn->dom_ctx->config[WEBSOCKET_ROOT]) {
7673 root = conn->dom_ctx->config[WEBSOCKET_ROOT];
7674 }
7675#endif /* !NO_FILES */
7676#else /* USE_WEBSOCKET */
7677 *is_websocket_request = 0;
7678#endif /* USE_WEBSOCKET */
7679
7680 /* Step 4: Check if gzip encoded response is allowed */
7681 conn->accept_gzip = 0;
7682 if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) {
7683 if (strstr(accept_encoding, "gzip") != NULL) {
7684 conn->accept_gzip = 1;
7685 }
7686 }
7687
7688#if !defined(NO_FILES)
7689 /* Step 5: If there is no root directory, don't look for files. */
7690 /* Note that root == NULL is a regular use case here. This occurs,
7691 * if all requests are handled by callbacks, so the WEBSOCKET_ROOT
7692 * config is not required. */
7693 if (root == NULL) {
7694 /* all file related outputs have already been set to 0, just return
7695 */
7696 return;
7697 }
7698
7699 /* Step 6: Determine the local file path from the root path and the
7700 * request uri. */
7701 /* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift
7702 * part of the path one byte on the right. */
7703 truncated = 0;
7705 conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri);
7706
7707 if (truncated) {
7708 goto interpret_cleanup;
7709 }
7710
7711 /* Step 7: URI rewriting */
7712 rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN];
7713 while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
7714 if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
7715 mg_snprintf(conn,
7716 &truncated,
7717 filename,
7718 filename_buf_len - 1,
7719 "%.*s%s",
7720 (int)b.len,
7721 b.ptr,
7722 uri + match_len);
7723 break;
7724 }
7725 }
7726
7727 if (truncated) {
7728 goto interpret_cleanup;
7729 }
7730
7731 /* Step 8: Check if the file exists at the server */
7732 /* Local file path and name, corresponding to requested URI
7733 * is now stored in "filename" variable. */
7734 if (mg_stat(conn, filename, filestat)) {
7735 int uri_len = (int)strlen(uri);
7736 int is_uri_end_slash = (uri_len > 0) && (uri[uri_len - 1] == '/');
7737
7738 /* 8.1: File exists. */
7739 *is_found = 1;
7740
7741 /* 8.2: Check if it is a script type. */
7742 if (extention_matches_script(conn, filename)) {
7743 /* The request addresses a CGI resource, Lua script or
7744 * server-side javascript.
7745 * The URI corresponds to the script itself (like
7746 * /path/script.cgi), and there is no additional resource
7747 * path (like /path/script.cgi/something).
7748 * Requests that modify (replace or delete) a resource, like
7749 * PUT and DELETE requests, should replace/delete the script
7750 * file.
7751 * Requests that read or write from/to a resource, like GET and
7752 * POST requests, should call the script and return the
7753 * generated response. */
7754 *is_script_resource = (!*is_put_or_delete_request);
7755 }
7756
7757 /* 8.3: Check for SSI and LSP files */
7758 if (extention_matches_template_text(conn, filename)) {
7759 /* Same as above, but for *.lsp and *.shtml files. */
7760 /* A "template text" is a file delivered directly to the client,
7761 * but with some text tags replaced by dynamic content.
7762 * E.g. a Server Side Include (SSI) or Lua Page/Lua Server Page
7763 * (LP, LSP) file. */
7764 *is_template_text = (!*is_put_or_delete_request);
7765 }
7766
7767 /* 8.4: If the request target is a directory, there could be
7768 * a substitute file (index.html, index.cgi, ...). */
7769 /* But do not substitute a directory for a WebDav request */
7770 if (filestat->is_directory && is_uri_end_slash
7771 && (!*is_webdav_request)) {
7772 /* Use a local copy here, since substitute_index_file will
7773 * change the content of the file status */
7774 struct mg_file_stat tmp_filestat;
7775 memset(&tmp_filestat, 0, sizeof(tmp_filestat));
7776
7778 conn, filename, filename_buf_len, &tmp_filestat)) {
7779
7780 /* Substitute file found. Copy stat to the output, then
7781 * check if the file is a script file */
7782 *filestat = tmp_filestat;
7783
7784 if (extention_matches_script(conn, filename)) {
7785 /* Substitute file is a script file */
7786 *is_script_resource = 1;
7787 } else if (extention_matches_template_text(conn, filename)) {
7788 /* Substitute file is a LSP or SSI file */
7789 *is_template_text = 1;
7790 } else {
7791 /* Substitute file is a regular file */
7792 *is_script_resource = 0;
7793 *is_found = (mg_stat(conn, filename, filestat) ? 1 : 0);
7794 }
7795 }
7796 /* If there is no substitute file, the server could return
7797 * a directory listing in a later step */
7798 }
7799 return;
7800 }
7801
7802 /* Step 9: Check for zipped files: */
7803 /* If we can't find the actual file, look for the file
7804 * with the same name but a .gz extension. If we find it,
7805 * use that and set the gzipped flag in the file struct
7806 * to indicate that the response need to have the content-
7807 * encoding: gzip header.
7808 * We can only do this if the browser declares support. */
7809 if (conn->accept_gzip) {
7811 conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", filename);
7812
7813 if (truncated) {
7814 goto interpret_cleanup;
7815 }
7816
7817 if (mg_stat(conn, gz_path, filestat)) {
7818 if (filestat) {
7819 filestat->is_gzipped = 1;
7820 *is_found = 1;
7821 }
7822 /* Currently gz files can not be scripts. */
7823 return;
7824 }
7825 }
7826
7827#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
7828 /* Step 10: Script resources may handle sub-resources */
7829 /* Support PATH_INFO for CGI scripts. */
7830 tmp_str_len = strlen(filename);
7831 tmp_str =
7832 (char *)mg_malloc_ctx(tmp_str_len + UTF8_PATH_MAX + 1, conn->phys_ctx);
7833 if (!tmp_str) {
7834 /* Out of memory */
7835 goto interpret_cleanup;
7836 }
7837 memcpy(tmp_str, filename, tmp_str_len + 1);
7838
7839 /* Check config, if index scripts may have sub-resources */
7840 allow_substitute_script_subresources =
7842 "yes");
7843 if (*is_webdav_request) {
7844 /* TO BE DEFINED: Should scripts handle special WebDAV methods lile
7845 * PROPFIND for their subresources? */
7846 /* allow_substitute_script_subresources = 0; */
7847 }
7848
7849 sep_pos = tmp_str_len;
7850 while (sep_pos > 0) {
7851 sep_pos--;
7852 if (tmp_str[sep_pos] == '/') {
7853 int is_script = 0, does_exist = 0;
7854
7855 tmp_str[sep_pos] = 0;
7856 if (tmp_str[0]) {
7857 is_script = extention_matches_script(conn, tmp_str);
7858 does_exist = mg_stat(conn, tmp_str, filestat);
7859 }
7860
7861 if (does_exist && is_script) {
7862 filename[sep_pos] = 0;
7863 memmove(filename + sep_pos + 2,
7864 filename + sep_pos + 1,
7865 strlen(filename + sep_pos + 1) + 1);
7866 conn->path_info = filename + sep_pos + 1;
7867 filename[sep_pos + 1] = '/';
7868 *is_script_resource = 1;
7869 *is_found = 1;
7870 break;
7871 }
7872
7873 if (allow_substitute_script_subresources) {
7875 conn, tmp_str, tmp_str_len + UTF8_PATH_MAX, filestat)) {
7876
7877 /* some intermediate directory has an index file */
7878 if (extention_matches_script(conn, tmp_str)) {
7879
7880 size_t script_name_len = strlen(tmp_str);
7881
7882 /* subres_name read before this memory locatio will be
7883 overwritten */
7884 char *subres_name = filename + sep_pos;
7885 size_t subres_name_len = strlen(subres_name);
7886
7887 DEBUG_TRACE("Substitute script %s serving path %s",
7888 tmp_str,
7889 filename);
7890
7891 /* this index file is a script */
7892 if ((script_name_len + subres_name_len + 2)
7893 >= filename_buf_len) {
7894 mg_free(tmp_str);
7895 goto interpret_cleanup;
7896 }
7897
7898 conn->path_info =
7899 filename + script_name_len + 1; /* new target */
7900 memmove(conn->path_info, subres_name, subres_name_len);
7901 conn->path_info[subres_name_len] = 0;
7902 memcpy(filename, tmp_str, script_name_len + 1);
7903
7904 *is_script_resource = 1;
7905 *is_found = 1;
7906 break;
7907
7908 } else {
7909
7910 DEBUG_TRACE("Substitute file %s serving path %s",
7911 tmp_str,
7912 filename);
7913
7914 /* non-script files will not have sub-resources */
7915 filename[sep_pos] = 0;
7916 conn->path_info = 0;
7917 *is_script_resource = 0;
7918 *is_found = 0;
7919 break;
7920 }
7921 }
7922 }
7923
7924 tmp_str[sep_pos] = '/';
7925 }
7926 }
7927
7928 mg_free(tmp_str);
7929
7930#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */
7931#endif /* !defined(NO_FILES) */
7932 return;
7933
7934#if !defined(NO_FILES)
7935/* Reset all outputs */
7936interpret_cleanup:
7937 memset(filestat, 0, sizeof(*filestat));
7938 *filename = 0;
7939 *is_found = 0;
7940 *is_script_resource = 0;
7941 *is_websocket_request = 0;
7942 *is_put_or_delete_request = 0;
7943#endif /* !defined(NO_FILES) */
7944}
7945
7946
7947/* Check whether full request is buffered. Return:
7948 * -1 if request or response is malformed
7949 * 0 if request or response is not yet fully buffered
7950 * >0 actual request length, including last \r\n\r\n */
7951static int
7952get_http_header_len(const char *buf, int buflen)
7953{
7954 int i;
7955 for (i = 0; i < buflen; i++) {
7956 /* Do an unsigned comparison in some conditions below */
7957 const unsigned char c = (unsigned char)buf[i];
7958
7959 if ((c < 128) && ((char)c != '\r') && ((char)c != '\n')
7960 && !isprint(c)) {
7961 /* abort scan as soon as one malformed character is found */
7962 return -1;
7963 }
7964
7965 if (i < buflen - 1) {
7966 if ((buf[i] == '\n') && (buf[i + 1] == '\n')) {
7967 /* Two newline, no carriage return - not standard compliant,
7968 * but it should be accepted */
7969 return i + 2;
7970 }
7971 }
7972
7973 if (i < buflen - 3) {
7974 if ((buf[i] == '\r') && (buf[i + 1] == '\n') && (buf[i + 2] == '\r')
7975 && (buf[i + 3] == '\n')) {
7976 /* Two \r\n - standard compliant */
7977 return i + 4;
7978 }
7979 }
7980 }
7981
7982 return 0;
7983}
7984
7985
7986#if !defined(NO_CACHING)
7987/* Convert month to the month number. Return -1 on error, or month number */
7988static int
7989get_month_index(const char *s)
7990{
7991 size_t i;
7992
7993 for (i = 0; i < ARRAY_SIZE(month_names); i++) {
7994 if (!strcmp(s, month_names[i])) {
7995 return (int)i;
7996 }
7997 }
7998
7999 return -1;
8000}
8001
8002
8003/* Parse UTC date-time string, and return the corresponding time_t value. */
8004static time_t
8005parse_date_string(const char *datetime)
8006{
8007 char month_str[32] = {0};
8008 int second, minute, hour, day, month, year;
8009 time_t result = (time_t)0;
8010 struct tm tm;
8011
8012 if ((sscanf(datetime,
8013 "%d/%3s/%d %d:%d:%d",
8014 &day,
8015 month_str,
8016 &year,
8017 &hour,
8018 &minute,
8019 &second)
8020 == 6)
8021 || (sscanf(datetime,
8022 "%d %3s %d %d:%d:%d",
8023 &day,
8024 month_str,
8025 &year,
8026 &hour,
8027 &minute,
8028 &second)
8029 == 6)
8030 || (sscanf(datetime,
8031 "%*3s, %d %3s %d %d:%d:%d",
8032 &day,
8033 month_str,
8034 &year,
8035 &hour,
8036 &minute,
8037 &second)
8038 == 6)
8039 || (sscanf(datetime,
8040 "%d-%3s-%d %d:%d:%d",
8041 &day,
8042 month_str,
8043 &year,
8044 &hour,
8045 &minute,
8046 &second)
8047 == 6)) {
8048 month = get_month_index(month_str);
8049 if ((month >= 0) && (year >= 1970)) {
8050 memset(&tm, 0, sizeof(tm));
8051 tm.tm_year = year - 1900;
8052 tm.tm_mon = month;
8053 tm.tm_mday = day;
8054 tm.tm_hour = hour;
8055 tm.tm_min = minute;
8056 tm.tm_sec = second;
8057 result = timegm(&tm);
8058 }
8059 }
8060
8061 return result;
8062}
8063#endif /* !NO_CACHING */
8064
8065
8066/* Pre-process URIs according to RFC + protect against directory disclosure
8067 * attacks by removing '..', excessive '/' and '\' characters */
8068static void
8070{
8071 /* Windows backend protection
8072 * (https://tools.ietf.org/html/rfc3986#section-7.3): Replace backslash
8073 * in URI by slash */
8074 char *out_end = inout;
8075 char *in = inout;
8076
8077 if (!in) {
8078 /* Param error. */
8079 return;
8080 }
8081
8082 while (*in) {
8083 if (*in == '\\') {
8084 *in = '/';
8085 }
8086 in++;
8087 }
8088
8089 /* Algorithm "remove_dot_segments" from
8090 * https://tools.ietf.org/html/rfc3986#section-5.2.4 */
8091 /* Step 1:
8092 * The input buffer is initialized.
8093 * The output buffer is initialized to the empty string.
8094 */
8095 in = inout;
8096
8097 /* Step 2:
8098 * While the input buffer is not empty, loop as follows:
8099 */
8100 /* Less than out_end of the inout buffer is used as output, so keep
8101 * condition: out_end <= in */
8102 while (*in) {
8103 /* Step 2a:
8104 * If the input buffer begins with a prefix of "../" or "./",
8105 * then remove that prefix from the input buffer;
8106 */
8107 if (!strncmp(in, "../", 3)) {
8108 in += 3;
8109 } else if (!strncmp(in, "./", 2)) {
8110 in += 2;
8111 }
8112 /* otherwise */
8113 /* Step 2b:
8114 * if the input buffer begins with a prefix of "/./" or "/.",
8115 * where "." is a complete path segment, then replace that
8116 * prefix with "/" in the input buffer;
8117 */
8118 else if (!strncmp(in, "/./", 3)) {
8119 in += 2;
8120 } else if (!strcmp(in, "/.")) {
8121 in[1] = 0;
8122 }
8123 /* otherwise */
8124 /* Step 2c:
8125 * if the input buffer begins with a prefix of "/../" or "/..",
8126 * where ".." is a complete path segment, then replace that
8127 * prefix with "/" in the input buffer and remove the last
8128 * segment and its preceding "/" (if any) from the output
8129 * buffer;
8130 */
8131 else if (!strncmp(in, "/../", 4)) {
8132 in += 3;
8133 if (inout != out_end) {
8134 /* remove last segment */
8135 do {
8136 out_end--;
8137 } while ((inout != out_end) && (*out_end != '/'));
8138 }
8139 } else if (!strcmp(in, "/..")) {
8140 in[1] = 0;
8141 if (inout != out_end) {
8142 /* remove last segment */
8143 do {
8144 out_end--;
8145 } while ((inout != out_end) && (*out_end != '/'));
8146 }
8147 }
8148 /* otherwise */
8149 /* Step 2d:
8150 * if the input buffer consists only of "." or "..", then remove
8151 * that from the input buffer;
8152 */
8153 else if (!strcmp(in, ".") || !strcmp(in, "..")) {
8154 *in = 0;
8155 }
8156 /* otherwise */
8157 /* Step 2e:
8158 * move the first path segment in the input buffer to the end of
8159 * the output buffer, including the initial "/" character (if
8160 * any) and any subsequent characters up to, but not including,
8161 * the next "/" character or the end of the input buffer.
8162 */
8163 else {
8164 do {
8165 *out_end = *in;
8166 out_end++;
8167 in++;
8168 } while ((*in != 0) && (*in != '/'));
8169 }
8170 }
8171
8172 /* Step 3:
8173 * Finally, the output buffer is returned as the result of
8174 * remove_dot_segments.
8175 */
8176 /* Terminate output */
8177 *out_end = 0;
8178
8179 /* For Windows, the files/folders "x" and "x." (with a dot but without
8180 * extension) are identical. Replace all "./" by "/" and remove a "." at
8181 * the end. Also replace all "//" by "/". Repeat until there is no "./"
8182 * or "//" anymore.
8183 */
8184 out_end = in = inout;
8185 while (*in) {
8186 if (*in == '.') {
8187 /* remove . at the end or preceding of / */
8188 char *in_ahead = in;
8189 do {
8190 in_ahead++;
8191 } while (*in_ahead == '.');
8192 if (*in_ahead == '/') {
8193 in = in_ahead;
8194 if ((out_end != inout) && (out_end[-1] == '/')) {
8195 /* remove generated // */
8196 out_end--;
8197 }
8198 } else if (*in_ahead == 0) {
8199 in = in_ahead;
8200 } else {
8201 do {
8202 *out_end++ = '.';
8203 in++;
8204 } while (in != in_ahead);
8205 }
8206 } else if (*in == '/') {
8207 /* replace // by / */
8208 *out_end++ = '/';
8209 do {
8210 in++;
8211 } while (*in == '/');
8212 } else {
8213 *out_end++ = *in;
8214 in++;
8215 }
8216 }
8217 *out_end = 0;
8218}
8219
8220
8221static const struct {
8222 const char *extension;
8223 size_t ext_len;
8224 const char *mime_type;
8225} builtin_mime_types[] = {
8226 /* IANA registered MIME types
8227 * (http://www.iana.org/assignments/media-types)
8228 * application types */
8229 {".bin", 4, "application/octet-stream"},
8230 {".deb", 4, "application/octet-stream"},
8231 {".dmg", 4, "application/octet-stream"},
8232 {".dll", 4, "application/octet-stream"},
8233 {".doc", 4, "application/msword"},
8234 {".eps", 4, "application/postscript"},
8235 {".exe", 4, "application/octet-stream"},
8236 {".iso", 4, "application/octet-stream"},
8237 {".js", 3, "application/javascript"},
8238 {".json", 5, "application/json"},
8239 {".msi", 4, "application/octet-stream"},
8240 {".pdf", 4, "application/pdf"},
8241 {".ps", 3, "application/postscript"},
8242 {".rtf", 4, "application/rtf"},
8243 {".xhtml", 6, "application/xhtml+xml"},
8244 {".xsl", 4, "application/xml"},
8245 {".xslt", 5, "application/xml"},
8246
8247 /* fonts */
8248 {".ttf", 4, "application/font-sfnt"},
8249 {".cff", 4, "application/font-sfnt"},
8250 {".otf", 4, "application/font-sfnt"},
8251 {".aat", 4, "application/font-sfnt"},
8252 {".sil", 4, "application/font-sfnt"},
8253 {".pfr", 4, "application/font-tdpfr"},
8254 {".woff", 5, "application/font-woff"},
8255 {".woff2", 6, "application/font-woff2"},
8256
8257 /* audio */
8258 {".mp3", 4, "audio/mpeg"},
8259 {".oga", 4, "audio/ogg"},
8260 {".ogg", 4, "audio/ogg"},
8261
8262 /* image */
8263 {".gif", 4, "image/gif"},
8264 {".ief", 4, "image/ief"},
8265 {".jpeg", 5, "image/jpeg"},
8266 {".jpg", 4, "image/jpeg"},
8267 {".jpm", 4, "image/jpm"},
8268 {".jpx", 4, "image/jpx"},
8269 {".png", 4, "image/png"},
8270 {".svg", 4, "image/svg+xml"},
8271 {".tif", 4, "image/tiff"},
8272 {".tiff", 5, "image/tiff"},
8273
8274 /* model */
8275 {".wrl", 4, "model/vrml"},
8276
8277 /* text */
8278 {".css", 4, "text/css"},
8279 {".csv", 4, "text/csv"},
8280 {".htm", 4, "text/html"},
8281 {".html", 5, "text/html"},
8282 {".sgm", 4, "text/sgml"},
8283 {".shtm", 5, "text/html"},
8284 {".shtml", 6, "text/html"},
8285 {".txt", 4, "text/plain"},
8286 {".xml", 4, "text/xml"},
8287
8288 /* video */
8289 {".mov", 4, "video/quicktime"},
8290 {".mp4", 4, "video/mp4"},
8291 {".mpeg", 5, "video/mpeg"},
8292 {".mpg", 4, "video/mpeg"},
8293 {".ogv", 4, "video/ogg"},
8294 {".qt", 3, "video/quicktime"},
8295
8296 /* not registered types
8297 * (http://reference.sitepoint.com/html/mime-types-full,
8298 * http://www.hansenb.pdx.edu/DMKB/dict/tutorials/mime_typ.php, ..) */
8299 {".arj", 4, "application/x-arj-compressed"},
8300 {".gz", 3, "application/x-gunzip"},
8301 {".rar", 4, "application/x-arj-compressed"},
8302 {".swf", 4, "application/x-shockwave-flash"},
8303 {".tar", 4, "application/x-tar"},
8304 {".tgz", 4, "application/x-tar-gz"},
8305 {".torrent", 8, "application/x-bittorrent"},
8306 {".ppt", 4, "application/x-mspowerpoint"},
8307 {".xls", 4, "application/x-msexcel"},
8308 {".zip", 4, "application/x-zip-compressed"},
8309 {".aac",
8310 4,
8311 "audio/aac"}, /* http://en.wikipedia.org/wiki/Advanced_Audio_Coding */
8312 {".flac", 5, "audio/flac"},
8313 {".aif", 4, "audio/x-aif"},
8314 {".m3u", 4, "audio/x-mpegurl"},
8315 {".mid", 4, "audio/x-midi"},
8316 {".ra", 3, "audio/x-pn-realaudio"},
8317 {".ram", 4, "audio/x-pn-realaudio"},
8318 {".wav", 4, "audio/x-wav"},
8319 {".bmp", 4, "image/bmp"},
8320 {".ico", 4, "image/x-icon"},
8321 {".pct", 4, "image/x-pct"},
8322 {".pict", 5, "image/pict"},
8323 {".rgb", 4, "image/x-rgb"},
8324 {".webm", 5, "video/webm"}, /* http://en.wikipedia.org/wiki/WebM */
8325 {".asf", 4, "video/x-ms-asf"},
8326 {".avi", 4, "video/x-msvideo"},
8327 {".m4v", 4, "video/x-m4v"},
8328 {NULL, 0, NULL}};
8329
8330
8331CIVETWEB_API const char *
8333{
8334 const char *ext;
8335 size_t i, path_len;
8336
8337 path_len = strlen(path);
8338
8339 for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
8340 ext = path + (path_len - builtin_mime_types[i].ext_len);
8341 if ((path_len > builtin_mime_types[i].ext_len)
8342 && (mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0)) {
8343 return builtin_mime_types[i].mime_type;
8344 }
8345 }
8346
8347 return "text/plain";
8348}
8349
8350
8351/* Look at the "path" extension and figure what mime type it has.
8352 * Store mime type in the vector. */
8353static void
8354get_mime_type(struct mg_connection *conn, const char *path, struct vec *vec)
8355{
8356 struct vec ext_vec, mime_vec;
8357 const char *list, *ext;
8358 size_t path_len;
8359
8360 path_len = strlen(path);
8361
8362 if ((conn == NULL) || (vec == NULL)) {
8363 if (vec != NULL) {
8364 memset(vec, '\0', sizeof(struct vec));
8365 }
8366 return;
8367 }
8368
8369 /* Scan user-defined mime types first, in case user wants to
8370 * override default mime types. */
8371 list = conn->dom_ctx->config[EXTRA_MIME_TYPES];
8372 while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
8373 /* ext now points to the path suffix */
8374 ext = path + path_len - ext_vec.len;
8375 if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
8376 *vec = mime_vec;
8377 return;
8378 }
8379 }
8380
8382 vec->len = strlen(vec->ptr);
8383}
8384
8385
8386/* Stringify binary data. Output buffer must be twice as big as input,
8387 * because each byte takes 2 bytes in string representation */
8388static void
8389bin2str(char *to, const unsigned char *p, size_t len)
8390{
8391 static const char *hex = "0123456789abcdef";
8392
8393 for (; len--; p++) {
8394 *to++ = hex[p[0] >> 4];
8395 *to++ = hex[p[0] & 0x0f];
8396 }
8397 *to = '\0';
8398}
8399
8400
8401/* Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
8402 */
8403CIVETWEB_API char *
8404mg_md5(char buf[33], ...)
8405{
8406 md5_byte_t hash[16];
8407 const char *p;
8408 va_list ap;
8409 md5_state_t ctx;
8410
8411 md5_init(&ctx);
8412
8413 va_start(ap, buf);
8414 while ((p = va_arg(ap, const char *)) != NULL) {
8415 md5_append(&ctx, (const md5_byte_t *)p, strlen(p));
8416 }
8417 va_end(ap);
8418
8419 md5_finish(&ctx, hash);
8420 bin2str(buf, hash, sizeof(hash));
8421 return buf;
8422}
8423
8424
8425/* Check the user's password, return 1 if OK */
8426static int
8427check_password_digest(const char *method,
8428 const char *ha1,
8429 const char *uri,
8430 const char *nonce,
8431 const char *nc,
8432 const char *cnonce,
8433 const char *qop,
8434 const char *response)
8435{
8436 char ha2[32 + 1], expected_response[32 + 1];
8437
8438 /* Some of the parameters may be NULL */
8439 if ((method == NULL) || (nonce == NULL) || (nc == NULL) || (cnonce == NULL)
8440 || (qop == NULL) || (response == NULL)) {
8441 return 0;
8442 }
8443
8444 /* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */
8445 if (strlen(response) != 32) {
8446 return 0;
8447 }
8448
8449 mg_md5(ha2, method, ":", uri, NULL);
8450 mg_md5(expected_response,
8451 ha1,
8452 ":",
8453 nonce,
8454 ":",
8455 nc,
8456 ":",
8457 cnonce,
8458 ":",
8459 qop,
8460 ":",
8461 ha2,
8462 NULL);
8463
8464 return mg_strcasecmp(response, expected_response) == 0;
8465}
8466
8467
8468#if !defined(NO_FILESYSTEMS)
8469/* Use the global passwords file, if specified by auth_gpass option,
8470 * or search for .htpasswd in the requested directory. */
8471static void
8473 const char *path,
8474 struct mg_file *filep)
8475{
8476 if ((conn != NULL) && (conn->dom_ctx != NULL)) {
8477 char name[UTF8_PATH_MAX];
8478 const char *p, *e,
8479 *gpass = conn->dom_ctx->config[GLOBAL_PASSWORDS_FILE];
8480 int truncated;
8481
8482 if (gpass != NULL) {
8483 /* Use global passwords file */
8484 if (!mg_fopen(conn, gpass, MG_FOPEN_MODE_READ, filep)) {
8485#if defined(DEBUG)
8486 /* Use mg_cry_internal here, since gpass has been
8487 * configured. */
8488 mg_cry_internal(conn, "fopen(%s): %s", gpass, strerror(ERRNO));
8489#endif
8490 }
8491 /* Important: using local struct mg_file to test path for
8492 * is_directory flag. If filep is used, mg_stat() makes it
8493 * appear as if auth file was opened.
8494 * TODO(mid): Check if this is still required after rewriting
8495 * mg_stat */
8496 } else if (mg_stat(conn, path, &filep->stat)
8497 && filep->stat.is_directory) {
8498 mg_snprintf(conn,
8499 &truncated,
8500 name,
8501 sizeof(name),
8502 "%s/%s",
8503 path,
8505
8506 if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
8507#if defined(DEBUG)
8508 /* Don't use mg_cry_internal here, but only a trace, since
8509 * this is a typical case. It will occur for every directory
8510 * without a password file. */
8511 DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO));
8512#endif
8513 }
8514 } else {
8515 /* Try to find .htpasswd in requested directory. */
8516 for (p = path, e = p + strlen(p) - 1; e > p; e--) {
8517 if (e[0] == '/') {
8518 break;
8519 }
8520 }
8521 mg_snprintf(conn,
8522 &truncated,
8523 name,
8524 sizeof(name),
8525 "%.*s/%s",
8526 (int)(e - p),
8527 p,
8529
8530 if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
8531#if defined(DEBUG)
8532 /* Don't use mg_cry_internal here, but only a trace, since
8533 * this is a typical case. It will occur for every directory
8534 * without a password file. */
8535 DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO));
8536#endif
8537 }
8538 }
8539 }
8540}
8541#endif /* NO_FILESYSTEMS */
8542
8543
8544/* Parsed Authorization header */
8545struct ah {
8546 char *user;
8547 int type; /* 1 = basic, 2 = digest */
8548 char *plain_password; /* Basic only */
8549 char *uri, *cnonce, *response, *qop, *nc, *nonce; /* Digest only */
8550};
8551
8552
8553/* Return 1 on success. Always initializes the ah structure. */
8554static int
8556 char *buf,
8557 size_t buf_size,
8558 struct ah *ah)
8559{
8560 char *name, *value, *s;
8561 const char *auth_header;
8562 uint64_t nonce;
8563
8564 if (!ah || !conn) {
8565 return 0;
8566 }
8567
8568 (void)memset(ah, 0, sizeof(*ah));
8569 auth_header = mg_get_header(conn, "Authorization");
8570
8571 if (auth_header == NULL) {
8572 /* No Authorization header at all */
8573 return 0;
8574 }
8575 if (0 == mg_strncasecmp(auth_header, "Basic ", 6)) {
8576 /* Basic Auth (we never asked for this, but some client may send it) */
8577 char *split;
8578 const char *userpw_b64 = auth_header + 6;
8579 size_t userpw_b64_len = strlen(userpw_b64);
8580 size_t buf_len_r = buf_size;
8581 if (mg_base64_decode(
8582 userpw_b64, userpw_b64_len, (unsigned char *)buf, &buf_len_r)
8583 != -1) {
8584 return 0; /* decode error */
8585 }
8586 split = strchr(buf, ':');
8587 if (!split) {
8588 return 0; /* Format error */
8589 }
8590
8591 /* Separate string at ':' */
8592 *split = 0;
8593
8594 /* User name is before ':', Password is after ':' */
8595 ah->user = buf;
8596 ah->type = 1;
8597 ah->plain_password = split + 1;
8598
8599 return 1;
8600
8601 } else if (0 == mg_strncasecmp(auth_header, "Digest ", 7)) {
8602 /* Digest Auth ... implemented below */
8603 ah->type = 2;
8604
8605 } else {
8606 /* Unknown or invalid Auth method */
8607 return 0;
8608 }
8609
8610 /* Make modifiable copy of the auth header */
8611 (void)mg_strlcpy(buf, auth_header + 7, buf_size);
8612 s = buf;
8613
8614 /* Parse authorization header */
8615 for (;;) {
8616 /* Gobble initial spaces */
8617 while (isspace((unsigned char)*s)) {
8618 s++;
8619 }
8620 name = skip_quoted(&s, "=", " ", 0);
8621 /* Value is either quote-delimited, or ends at first comma or space.
8622 */
8623 if (s[0] == '\"') {
8624 s++;
8625 value = skip_quoted(&s, "\"", " ", '\\');
8626 if (s[0] == ',') {
8627 s++;
8628 }
8629 } else {
8630 value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF
8631 * uses spaces */
8632 }
8633 if (*name == '\0') {
8634 break;
8635 }
8636
8637 if (!strcmp(name, "username")) {
8638 ah->user = value;
8639 } else if (!strcmp(name, "cnonce")) {
8640 ah->cnonce = value;
8641 } else if (!strcmp(name, "response")) {
8642 ah->response = value;
8643 } else if (!strcmp(name, "uri")) {
8644 ah->uri = value;
8645 } else if (!strcmp(name, "qop")) {
8646 ah->qop = value;
8647 } else if (!strcmp(name, "nc")) {
8648 ah->nc = value;
8649 } else if (!strcmp(name, "nonce")) {
8650 ah->nonce = value;
8651 }
8652 }
8653
8654#if !defined(NO_NONCE_CHECK)
8655 /* Read the nonce from the response. */
8656 if (ah->nonce == NULL) {
8657 return 0;
8658 }
8659 s = NULL;
8660 nonce = strtoull(ah->nonce, &s, 10);
8661 if ((s == NULL) || (*s != 0)) {
8662 return 0;
8663 }
8664
8665 /* Convert the nonce from the client to a number. */
8666 nonce ^= conn->dom_ctx->auth_nonce_mask;
8667
8668 /* The converted number corresponds to the time the nounce has been
8669 * created. This should not be earlier than the server start. */
8670 /* Server side nonce check is valuable in all situations but one:
8671 * if the server restarts frequently, but the client should not see
8672 * that, so the server should accept nonces from previous starts. */
8673 /* However, the reasonable default is to not accept a nonce from a
8674 * previous start, so if anyone changed the access rights between
8675 * two restarts, a new login is required. */
8676 if (nonce < (uint64_t)conn->phys_ctx->start_time) {
8677 /* nonce is from a previous start of the server and no longer valid
8678 * (replay attack?) */
8679 return 0;
8680 }
8681 /* Check if the nonce is too high, so it has not (yet) been used by the
8682 * server. */
8683 if (nonce >= ((uint64_t)conn->phys_ctx->start_time
8684 + conn->dom_ctx->nonce_count)) {
8685 return 0;
8686 }
8687#else
8688 (void)nonce;
8689#endif
8690
8691 return (ah->user != NULL);
8692}
8693
8694
8695static const char *
8696mg_fgets(char *buf, size_t size, struct mg_file *filep)
8697{
8698 if (!filep) {
8699 return NULL;
8700 }
8701
8702 if (filep->access.fp != NULL) {
8703 return fgets(buf, (int)size, filep->access.fp);
8704 } else {
8705 return NULL;
8706 }
8707}
8708
8709/* Define the initial recursion depth for procesesing htpasswd files that
8710 * include other htpasswd
8711 * (or even the same) files. It is not difficult to provide a file or files
8712 * s.t. they force civetweb
8713 * to infinitely recurse and then crash.
8714 */
8715#define INITIAL_DEPTH 9
8716#if INITIAL_DEPTH <= 0
8717#error Bad INITIAL_DEPTH for recursion, set to at least 1
8718#endif
8719
8720#if !defined(NO_FILESYSTEMS)
8723 struct ah ah;
8724 const char *domain;
8725 char buf[256 + 256 + 40];
8726 const char *f_user;
8727 const char *f_domain;
8728 const char *f_ha1;
8729};
8730
8731
8732static int
8734 struct read_auth_file_struct *workdata,
8735 int depth)
8736{
8737 int is_authorized = 0;
8738 struct mg_file fp;
8739 size_t l;
8740
8741 if (!filep || !workdata || (0 == depth)) {
8742 return 0;
8743 }
8744
8745 /* Loop over passwords file */
8746 while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep) != NULL) {
8747 l = strlen(workdata->buf);
8748 while (l > 0) {
8749 if (isspace((unsigned char)workdata->buf[l - 1])
8750 || iscntrl((unsigned char)workdata->buf[l - 1])) {
8751 l--;
8752 workdata->buf[l] = 0;
8753 } else
8754 break;
8755 }
8756 if (l < 1) {
8757 continue;
8758 }
8759
8760 workdata->f_user = workdata->buf;
8761
8762 if (workdata->f_user[0] == ':') {
8763 /* user names may not contain a ':' and may not be empty,
8764 * so lines starting with ':' may be used for a special purpose
8765 */
8766 if (workdata->f_user[1] == '#') {
8767 /* :# is a comment */
8768 continue;
8769 } else if (!strncmp(workdata->f_user + 1, "include=", 8)) {
8770 if (mg_fopen(workdata->conn,
8771 workdata->f_user + 9,
8773 &fp)) {
8774 is_authorized = read_auth_file(&fp, workdata, depth - 1);
8775 (void)mg_fclose(
8776 &fp.access); /* ignore error on read only file */
8777
8778 /* No need to continue processing files once we have a
8779 * match, since nothing will reset it back
8780 * to 0.
8781 */
8782 if (is_authorized) {
8783 return is_authorized;
8784 }
8785 } else {
8786 mg_cry_internal(workdata->conn,
8787 "%s: cannot open authorization file: %s",
8788 __func__,
8789 workdata->buf);
8790 }
8791 continue;
8792 }
8793 /* everything is invalid for the moment (might change in the
8794 * future) */
8795 mg_cry_internal(workdata->conn,
8796 "%s: syntax error in authorization file: %s",
8797 __func__,
8798 workdata->buf);
8799 continue;
8800 }
8801
8802 workdata->f_domain = strchr(workdata->f_user, ':');
8803 if (workdata->f_domain == NULL) {
8804 mg_cry_internal(workdata->conn,
8805 "%s: syntax error in authorization file: %s",
8806 __func__,
8807 workdata->buf);
8808 continue;
8809 }
8810 *(char *)(workdata->f_domain) = 0;
8811 (workdata->f_domain)++;
8812
8813 workdata->f_ha1 = strchr(workdata->f_domain, ':');
8814 if (workdata->f_ha1 == NULL) {
8815 mg_cry_internal(workdata->conn,
8816 "%s: syntax error in authorization file: %s",
8817 __func__,
8818 workdata->buf);
8819 continue;
8820 }
8821 *(char *)(workdata->f_ha1) = 0;
8822 (workdata->f_ha1)++;
8823
8824 if (!strcmp(workdata->ah.user, workdata->f_user)
8825 && !strcmp(workdata->domain, workdata->f_domain)) {
8826 switch (workdata->ah.type) {
8827 case 1: /* Basic */
8828 {
8829 char md5[33];
8830 mg_md5(md5,
8831 workdata->f_user,
8832 ":",
8833 workdata->domain,
8834 ":",
8835 workdata->ah.plain_password,
8836 NULL);
8837 return 0 == memcmp(workdata->f_ha1, md5, 33);
8838 }
8839 case 2: /* Digest */
8840 return check_password_digest(
8841 workdata->conn->request_info.request_method,
8842 workdata->f_ha1,
8843 workdata->ah.uri,
8844 workdata->ah.nonce,
8845 workdata->ah.nc,
8846 workdata->ah.cnonce,
8847 workdata->ah.qop,
8848 workdata->ah.response);
8849 default: /* None/Other/Unknown */
8850 return 0;
8851 }
8852 }
8853 }
8854
8855 return is_authorized;
8856}
8857
8858
8859/* Authorize against the opened passwords file. Return 1 if authorized. */
8860static int
8861authorize(struct mg_connection *conn, struct mg_file *filep, const char *realm)
8862{
8863 struct read_auth_file_struct workdata;
8864 char buf[MG_BUF_LEN];
8865
8866 if (!conn || !conn->dom_ctx) {
8867 return 0;
8868 }
8869
8870 memset(&workdata, 0, sizeof(workdata));
8871 workdata.conn = conn;
8872
8873 if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) {
8874 return 0;
8875 }
8876
8877 /* CGI needs it as REMOTE_USER */
8879 mg_strdup_ctx(workdata.ah.user, conn->phys_ctx);
8880
8881 if (realm) {
8882 workdata.domain = realm;
8883 } else {
8885 }
8886
8887 return read_auth_file(filep, &workdata, INITIAL_DEPTH);
8888}
8889
8890
8891/* Public function to check http digest authentication header */
8892CIVETWEB_API int
8894 const char *realm,
8895 const char *filename)
8896{
8897 struct mg_file file = STRUCT_FILE_INITIALIZER;
8898 int auth;
8899
8900 if (!conn || !filename) {
8901 return -1;
8902 }
8903 if (!mg_fopen(conn, filename, MG_FOPEN_MODE_READ, &file)) {
8904 return -2;
8905 }
8906
8907 auth = authorize(conn, &file, realm);
8908
8909 mg_fclose(&file.access);
8910
8911 return auth;
8912}
8913#endif /* NO_FILESYSTEMS */
8914
8915
8916/* Return 1 if request is authorised, 0 otherwise. */
8917static int
8918check_authorization(struct mg_connection *conn, const char *path)
8919{
8920#if !defined(NO_FILESYSTEMS)
8921 char fname[UTF8_PATH_MAX];
8922 struct vec uri_vec, filename_vec;
8923 const char *list;
8924 struct mg_file file = STRUCT_FILE_INITIALIZER;
8925 int authorized = 1, truncated;
8926
8927 if (!conn || !conn->dom_ctx) {
8928 return 0;
8929 }
8930
8931 list = conn->dom_ctx->config[PROTECT_URI];
8932 while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
8933 if (!memcmp(conn->request_info.local_uri, uri_vec.ptr, uri_vec.len)) {
8934 mg_snprintf(conn,
8935 &truncated,
8936 fname,
8937 sizeof(fname),
8938 "%.*s",
8939 (int)filename_vec.len,
8940 filename_vec.ptr);
8941
8942 if (truncated
8943 || !mg_fopen(conn, fname, MG_FOPEN_MODE_READ, &file)) {
8944 mg_cry_internal(conn,
8945 "%s: cannot open %s: %s",
8946 __func__,
8947 fname,
8948 strerror(errno));
8949 }
8950 break;
8951 }
8952 }
8953
8954 if (!is_file_opened(&file.access)) {
8955 open_auth_file(conn, path, &file);
8956 }
8957
8958 if (is_file_opened(&file.access)) {
8959 authorized = authorize(conn, &file, NULL);
8960 (void)mg_fclose(&file.access); /* ignore error on read only file */
8961 }
8962
8963 return authorized;
8964#else
8965 (void)conn;
8966 (void)path;
8967 return 1;
8968#endif /* NO_FILESYSTEMS */
8969}
8970
8971
8972/* Internal function. Assumes conn is valid */
8973static void
8974send_authorization_request(struct mg_connection *conn, const char *realm)
8975{
8976 uint64_t nonce = (uint64_t)(conn->phys_ctx->start_time);
8977 int trunc = 0;
8978 char buf[128];
8979
8980 if (!realm) {
8981 realm = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
8982 }
8983
8985 nonce += conn->dom_ctx->nonce_count;
8986 ++conn->dom_ctx->nonce_count;
8988
8989 nonce ^= conn->dom_ctx->auth_nonce_mask;
8990 conn->must_close = 1;
8991
8992 /* Create 401 response */
8993 mg_response_header_start(conn, 401);
8996 mg_response_header_add(conn, "Content-Length", "0", -1);
8997
8998 /* Content for "WWW-Authenticate" header */
8999 mg_snprintf(conn,
9000 &trunc,
9001 buf,
9002 sizeof(buf),
9003 "Digest qop=\"auth\", realm=\"%s\", "
9004 "nonce=\"%" UINT64_FMT "\"",
9005 realm,
9006 nonce);
9007
9008 if (!trunc) {
9009 /* !trunc should always be true */
9010 mg_response_header_add(conn, "WWW-Authenticate", buf, -1);
9011 }
9012
9013 /* Send all headers */
9015}
9016
9017
9018/* Interface function. Parameters are provided by the user, so do
9019 * at least some basic checks.
9020 */
9021CIVETWEB_API int
9023 const char *realm)
9024{
9025 if (conn && conn->dom_ctx) {
9026 send_authorization_request(conn, realm);
9027 return 0;
9028 }
9029 return -1;
9030}
9031
9032
9033#if !defined(NO_FILES)
9034static int
9036{
9037 int ret = 0;
9038
9039 if (conn) {
9040 struct mg_file file = STRUCT_FILE_INITIALIZER;
9041 const char *passfile = conn->dom_ctx->config[PUT_DELETE_PASSWORDS_FILE];
9042
9043 if (passfile != NULL
9044 && mg_fopen(conn, passfile, MG_FOPEN_MODE_READ, &file)) {
9045 ret = authorize(conn, &file, NULL);
9046 (void)mg_fclose(&file.access); /* ignore error on read only file */
9047 }
9048 }
9049
9050 DEBUG_TRACE("file write authorization: %i", ret);
9051 return ret;
9052}
9053#endif
9054
9055
9056CIVETWEB_API int
9058 const char *domain,
9059 const char *user,
9060 const char *ha1)
9061{
9062 int found = 0, i, result = 1;
9063 char line[512], u[256], d[256], h[256];
9064 struct stat st = {0};
9065 FILE *fp = NULL;
9066 char *temp_file = NULL;
9067 int temp_file_offs = 0;
9068
9069 /* Regard empty password as no password - remove user record. */
9070 if ((ha1 != NULL) && (ha1[0] == '\0')) {
9071 ha1 = NULL;
9072 }
9073
9074 /* Other arguments must not be empty */
9075 if ((fname == NULL) || (domain == NULL) || (user == NULL)) {
9076 return 0;
9077 }
9078
9079 /* Using the given file format, user name and domain must not contain
9080 * the ':' character */
9081 if (strchr(user, ':') != NULL) {
9082 return 0;
9083 }
9084 if (strchr(domain, ':') != NULL) {
9085 return 0;
9086 }
9087
9088 /* Do not allow control characters like newline in user name and domain.
9089 * Do not allow excessively long names either. */
9090 for (i = 0; ((i < 255) && (user[i] != 0)); i++) {
9091 if (iscntrl((unsigned char)user[i])) {
9092 return 0;
9093 }
9094 }
9095 if (user[i]) {
9096 return 0; /* user name too long */
9097 }
9098 for (i = 0; ((i < 255) && (domain[i] != 0)); i++) {
9099 if (iscntrl((unsigned char)domain[i])) {
9100 return 0;
9101 }
9102 }
9103 if (domain[i]) {
9104 return 0; /* domain name too long */
9105 }
9106
9107 /* The maximum length of the path to the password file is limited */
9108 if (strlen(fname) >= UTF8_PATH_MAX) {
9109 return 0;
9110 }
9111
9112 /* Check if the file exists, and get file size */
9113 if (0 == stat(fname, &st)) {
9114 int temp_buf_len;
9115 if (st.st_size > 10485760) {
9116 /* Some funster provided a >10 MB text file */
9117 return 0;
9118 }
9119
9120 /* Add enough space for one more line */
9121 temp_buf_len = (int)st.st_size + 1024;
9122
9123 /* Allocate memory (instead of using a temporary file) */
9124 temp_file = (char *)mg_calloc((size_t)temp_buf_len, 1);
9125 if (!temp_file) {
9126 /* Out of memory */
9127 return 0;
9128 }
9129
9130 /* File exists. Read it into a memory buffer. */
9131 fp = fopen(fname, "r");
9132 if (fp == NULL) {
9133 /* Cannot read file. No permission? */
9134 mg_free(temp_file);
9135 return 0;
9136 }
9137
9138 /* Read content and store in memory */
9139 while ((fgets(line, sizeof(line), fp) != NULL)
9140 && ((temp_file_offs + 600) < temp_buf_len)) {
9141 /* file format is "user:domain:hash\n" */
9142 if (sscanf(line, "%255[^:]:%255[^:]:%255s", u, d, h) != 3) {
9143 continue;
9144 }
9145 u[255] = 0;
9146 d[255] = 0;
9147 h[255] = 0;
9148
9149 if (!strcmp(u, user) && !strcmp(d, domain)) {
9150 /* Found the user: change the password hash or drop the user
9151 */
9152 if ((ha1 != NULL) && (!found)) {
9153 i = sprintf(temp_file + temp_file_offs,
9154 "%s:%s:%s\n",
9155 user,
9156 domain,
9157 ha1);
9158 if (i < 1) {
9159 fclose(fp);
9160 mg_free(temp_file);
9161 return 0;
9162 }
9163 temp_file_offs += i;
9164 }
9165 found = 1;
9166 } else {
9167 /* Copy existing user, including password hash */
9168 i = sprintf(temp_file + temp_file_offs, "%s:%s:%s\n", u, d, h);
9169 if (i < 1) {
9170 fclose(fp);
9171 mg_free(temp_file);
9172 return 0;
9173 }
9174 temp_file_offs += i;
9175 }
9176 }
9177 fclose(fp);
9178 }
9179
9180 /* Create new file */
9181 fp = fopen(fname, "w");
9182 if (!fp) {
9183 mg_free(temp_file);
9184 return 0;
9185 }
9186
9187#if !defined(_WIN32)
9188 /* On Linux & co., restrict file read/write permissions to the owner */
9189 if (fchmod(fileno(fp), S_IRUSR | S_IWUSR) != 0) {
9190 result = 0;
9191 }
9192#endif
9193
9194 if ((temp_file != NULL) && (temp_file_offs > 0)) {
9195 /* Store buffered content of old file */
9196 if (fwrite(temp_file, 1, (size_t)temp_file_offs, fp)
9197 != (size_t)temp_file_offs) {
9198 result = 0;
9199 }
9200 }
9201
9202 /* If new user, just add it */
9203 if ((ha1 != NULL) && (!found)) {
9204 if (fprintf(fp, "%s:%s:%s\n", user, domain, ha1) < 6) {
9205 result = 0;
9206 }
9207 }
9208
9209 /* All data written */
9210 if (fclose(fp) != 0) {
9211 result = 0;
9212 }
9213
9214 mg_free(temp_file);
9215 return result;
9216}
9217
9218
9219CIVETWEB_API int
9221 const char *domain,
9222 const char *user,
9223 const char *pass)
9224{
9225 char ha1buf[33];
9226 if ((fname == NULL) || (domain == NULL) || (user == NULL)) {
9227 return 0;
9228 }
9229 if ((pass == NULL) || (pass[0] == 0)) {
9230 return mg_modify_passwords_file_ha1(fname, domain, user, NULL);
9231 }
9232
9233 mg_md5(ha1buf, user, ":", domain, ":", pass, NULL);
9234 return mg_modify_passwords_file_ha1(fname, domain, user, ha1buf);
9235}
9236
9237
9238static int
9239is_valid_port(unsigned long port)
9240{
9241 return (port <= 0xffff);
9242}
9243
9244
9245static int
9246mg_inet_pton(int af, const char *src, void *dst, size_t dstlen, int resolve_src)
9247{
9248 struct addrinfo hints, *res, *ressave;
9249 int func_ret = 0;
9250 int gai_ret;
9251
9252 memset(&hints, 0, sizeof(struct addrinfo));
9253 hints.ai_family = af;
9254 if (!resolve_src) {
9255 hints.ai_flags = AI_NUMERICHOST;
9256 }
9257
9258 gai_ret = getaddrinfo(src, NULL, &hints, &res);
9259 if (gai_ret != 0) {
9260 /* gai_strerror could be used to convert gai_ret to a string */
9261 /* POSIX return values: see
9262 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html
9263 */
9264 /* Windows return values: see
9265 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx
9266 */
9267 return 0;
9268 }
9269
9270 ressave = res;
9271
9272 while (res) {
9273 if ((dstlen >= (size_t)res->ai_addrlen)
9274 && (res->ai_addr->sa_family == af)) {
9275 memcpy(dst, res->ai_addr, res->ai_addrlen);
9276 func_ret = 1;
9277 }
9278 res = res->ai_next;
9279 }
9280
9281 freeaddrinfo(ressave);
9282 return func_ret;
9283}
9284
9285
9286static int
9288 struct mg_context *ctx /* may be NULL */,
9289 const char *host,
9290 int port, /* 1..65535, or -99 for domain sockets (may be changed) */
9291 int use_ssl, /* 0 or 1 */
9292 struct mg_error_data *error,
9293 SOCKET *sock /* output: socket, must not be NULL */,
9294 union usa *sa /* output: socket address, must not be NULL */
9295)
9296{
9297 int ip_ver = 0;
9298 int conn_ret = -1;
9299 int sockerr = 0;
9300 *sock = INVALID_SOCKET;
9301 memset(sa, 0, sizeof(*sa));
9302
9303 if (host == NULL) {
9304 if (error != NULL) {
9307 NULL, /* No truncation check for ebuf */
9308 error->text,
9309 error->text_buffer_size,
9310 "%s",
9311 "NULL host");
9312 }
9313 return 0;
9314 }
9315
9316#if defined(USE_X_DOM_SOCKET)
9317 if (port == -99) {
9318 /* Unix domain socket */
9319 size_t hostlen = strlen(host);
9320 if (hostlen >= sizeof(sa->sun.sun_path)) {
9321 if (error != NULL) {
9324 NULL, /* No truncation check for ebuf */
9325 error->text,
9326 error->text_buffer_size,
9327 "%s",
9328 "host length exceeds limit");
9329 }
9330 return 0;
9331 }
9332 } else
9333#endif
9334 if ((port <= 0) || !is_valid_port((unsigned)port)) {
9335 if (error != NULL) {
9338 NULL, /* No truncation check for ebuf */
9339 error->text,
9340 error->text_buffer_size,
9341 "%s",
9342 "invalid port");
9343 }
9344 return 0;
9345 }
9346
9347#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(NO_SSL_DL)
9348#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
9349 if (use_ssl && (TLS_client_method == NULL)) {
9350 if (error != NULL) {
9353 NULL, /* No truncation check for ebuf */
9354 error->text,
9355 error->text_buffer_size,
9356 "%s",
9357 "SSL is not initialized");
9358 }
9359 return 0;
9360 }
9361#else
9362 if (use_ssl && (SSLv23_client_method == NULL)) {
9363 if (error != 0) {
9366 NULL, /* No truncation check for ebuf */
9367 error->text,
9368 error->text_buffer_size,
9369 "%s",
9370 "SSL is not initialized");
9371 }
9372 return 0;
9373 }
9374#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0*/
9375#else
9376 (void)use_ssl;
9377#endif /* NO SSL */
9378
9379#if defined(USE_X_DOM_SOCKET)
9380 if (port == -99) {
9381 size_t hostlen = strlen(host);
9382 /* check (hostlen < sizeof(sun.sun_path)) already passed above */
9383 ip_ver = -99;
9384 sa->sun.sun_family = AF_UNIX;
9385 memset(sa->sun.sun_path, 0, sizeof(sa->sun.sun_path));
9386 memcpy(sa->sun.sun_path, host, hostlen);
9387 } else
9388#endif
9389 if (mg_inet_pton(AF_INET, host, &sa->sin, sizeof(sa->sin), 1)) {
9390 sa->sin.sin_port = htons((uint16_t)port);
9391 ip_ver = 4;
9392#if defined(USE_IPV6)
9393 } else if (mg_inet_pton(AF_INET6, host, &sa->sin6, sizeof(sa->sin6), 1)) {
9394 sa->sin6.sin6_port = htons((uint16_t)port);
9395 ip_ver = 6;
9396 } else if (host[0] == '[') {
9397 /* While getaddrinfo on Windows will work with [::1],
9398 * getaddrinfo on Linux only works with ::1 (without []). */
9399 size_t l = strlen(host + 1);
9400 char *h = (l > 1) ? mg_strdup_ctx(host + 1, ctx) : NULL;
9401 if (h) {
9402 h[l - 1] = 0;
9403 if (mg_inet_pton(AF_INET6, h, &sa->sin6, sizeof(sa->sin6), 0)) {
9404 sa->sin6.sin6_port = htons((uint16_t)port);
9405 ip_ver = 6;
9406 }
9407 mg_free(h);
9408 }
9409#endif
9410 }
9411
9412 if (ip_ver == 0) {
9413 if (error != NULL) {
9416 NULL, /* No truncation check for ebuf */
9417 error->text,
9418 error->text_buffer_size,
9419 "%s",
9420 "host not found");
9421 }
9422 return 0;
9423 }
9424
9425 if (ip_ver == 4) {
9426 *sock = socket(PF_INET, SOCK_STREAM, 0);
9427 }
9428#if defined(USE_IPV6)
9429 else if (ip_ver == 6) {
9430 *sock = socket(PF_INET6, SOCK_STREAM, 0);
9431 }
9432#endif
9433#if defined(USE_X_DOM_SOCKET)
9434 else if (ip_ver == -99) {
9435 *sock = socket(AF_UNIX, SOCK_STREAM, 0);
9436 }
9437#endif
9438
9439 if (*sock == INVALID_SOCKET) {
9440 if (error != NULL) {
9442 error->code_sub = (unsigned)ERRNO;
9444 NULL, /* No truncation check for ebuf */
9445 error->text,
9446 error->text_buffer_size,
9447 "socket(): %s",
9448 strerror(ERRNO));
9449 }
9450 return 0;
9451 }
9452
9453 if (0 != set_non_blocking_mode(*sock)) {
9454 if (error != NULL) {
9456 error->code_sub = (unsigned)ERRNO;
9458 NULL, /* No truncation check for ebuf */
9459 error->text,
9460 error->text_buffer_size,
9461 "Cannot set socket to non-blocking: %s",
9462 strerror(ERRNO));
9463 }
9464 closesocket(*sock);
9465 *sock = INVALID_SOCKET;
9466 return 0;
9467 }
9468
9469 set_close_on_exec(*sock, NULL, ctx);
9470
9471 if (ip_ver == 4) {
9472 /* connected with IPv4 */
9473 conn_ret = connect(*sock,
9474 (struct sockaddr *)((void *)&sa->sin),
9475 sizeof(sa->sin));
9476 }
9477#if defined(USE_IPV6)
9478 else if (ip_ver == 6) {
9479 /* connected with IPv6 */
9480 conn_ret = connect(*sock,
9481 (struct sockaddr *)((void *)&sa->sin6),
9482 sizeof(sa->sin6));
9483 }
9484#endif
9485#if defined(USE_X_DOM_SOCKET)
9486 else if (ip_ver == -99) {
9487 /* connected to domain socket */
9488 conn_ret = connect(*sock,
9489 (struct sockaddr *)((void *)&sa->sun),
9490 sizeof(sa->sun));
9491 }
9492#endif
9493
9494 if (conn_ret != 0) {
9495 sockerr = ERRNO;
9496 }
9497
9498#if defined(_WIN32)
9499 if ((conn_ret != 0) && (sockerr == WSAEWOULDBLOCK)) {
9500#else
9501 if ((conn_ret != 0) && (sockerr == EINPROGRESS)) {
9502#endif
9503 /* Data for getsockopt */
9504 void *psockerr = &sockerr;
9505 int ret;
9506
9507#if defined(_WIN32)
9508 int len = (int)sizeof(sockerr);
9509#else
9510 socklen_t len = (socklen_t)sizeof(sockerr);
9511#endif
9512
9513 /* Data for poll */
9514 struct mg_pollfd pfd[1];
9515 int pollres;
9516 int ms_wait = 10000; /* 10 second timeout */
9517 stop_flag_t nonstop;
9518 STOP_FLAG_ASSIGN(&nonstop, 0);
9519
9520 /* For a non-blocking socket, the connect sequence is:
9521 * 1) call connect (will not block)
9522 * 2) wait until the socket is ready for writing (select or poll)
9523 * 3) check connection state with getsockopt
9524 */
9525 pfd[0].fd = *sock;
9526 pfd[0].events = POLLOUT;
9527 pollres = mg_poll(pfd, 1, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop);
9528
9529 if (pollres != 1) {
9530 /* Not connected */
9531 if (error != NULL) {
9534 NULL, /* No truncation check for ebuf */
9535 error->text,
9536 error->text_buffer_size,
9537 "connect(%s:%d): timeout",
9538 host,
9539 port);
9540 }
9541 closesocket(*sock);
9542 *sock = INVALID_SOCKET;
9543 return 0;
9544 }
9545
9546#if defined(_WIN32)
9547 ret = getsockopt(*sock, SOL_SOCKET, SO_ERROR, (char *)psockerr, &len);
9548#else
9549 ret = getsockopt(*sock, SOL_SOCKET, SO_ERROR, psockerr, &len);
9550#endif
9551
9552 if ((ret == 0) && (sockerr == 0)) {
9553 conn_ret = 0;
9554 }
9555 }
9556
9557 if (conn_ret != 0) {
9558 /* Not connected */
9559 if (error != NULL) {
9561 error->code_sub = (unsigned)ERRNO;
9563 NULL, /* No truncation check for ebuf */
9564 error->text,
9565 error->text_buffer_size,
9566 "connect(%s:%d): error %s",
9567 host,
9568 port,
9569 strerror(sockerr));
9570 }
9571 closesocket(*sock);
9572 *sock = INVALID_SOCKET;
9573 return 0;
9574 }
9575
9576 return 1;
9577}
9578
9579
9580CIVETWEB_API int
9581mg_url_encode(const char *src, char *dst, size_t dst_len)
9582{
9583 static const char *dont_escape = "._-$,;~()";
9584 static const char *hex = "0123456789abcdef";
9585 char *pos = dst;
9586 const char *end = dst + dst_len - 1;
9587
9588 for (; ((*src != '\0') && (pos < end)); src++, pos++) {
9589 if (isalnum((unsigned char)*src)
9590 || (strchr(dont_escape, *src) != NULL)) {
9591 *pos = *src;
9592 } else if (pos + 2 < end) {
9593 pos[0] = '%';
9594 pos[1] = hex[(unsigned char)*src >> 4];
9595 pos[2] = hex[(unsigned char)*src & 0xf];
9596 pos += 2;
9597 } else {
9598 break;
9599 }
9600 }
9601
9602 *pos = '\0';
9603 return (*src == '\0') ? (int)(pos - dst) : -1;
9604}
9605
9606/* Return 0 on success, non-zero if an error occurs. */
9607
9608static int
9609print_dir_entry(struct mg_connection *conn, struct de *de)
9610{
9611 size_t namesize, escsize, i;
9612 char *href, *esc, *p;
9613 char size[64], mod[64];
9614#if defined(REENTRANT_TIME)
9615 struct tm _tm;
9616 struct tm *tm = &_tm;
9617#else
9618 struct tm *tm;
9619#endif
9620
9621 /* Estimate worst case size for encoding and escaping */
9622 namesize = strlen(de->file_name) + 1;
9623 escsize = de->file_name[strcspn(de->file_name, "&<>")] ? namesize * 5 : 0;
9624 href = (char *)mg_malloc(namesize * 3 + escsize);
9625 if (href == NULL) {
9626 return -1;
9627 }
9628 mg_url_encode(de->file_name, href, namesize * 3);
9629 esc = NULL;
9630 if (escsize > 0) {
9631 /* HTML escaping needed */
9632 esc = href + namesize * 3;
9633 for (i = 0, p = esc; de->file_name[i]; i++, p += strlen(p)) {
9634 mg_strlcpy(p, de->file_name + i, 2);
9635 if (*p == '&') {
9636 strcpy(p, "&amp;");
9637 } else if (*p == '<') {
9638 strcpy(p, "&lt;");
9639 } else if (*p == '>') {
9640 strcpy(p, "&gt;");
9641 }
9642 }
9643 }
9644
9645 if (de->file.is_directory) {
9646 mg_snprintf(conn,
9647 NULL, /* Buffer is big enough */
9648 size,
9649 sizeof(size),
9650 "%s",
9651 "[DIRECTORY]");
9652 } else {
9653 /* We use (signed) cast below because MSVC 6 compiler cannot
9654 * convert unsigned __int64 to double. Sigh. */
9655 if (de->file.size < 1024) {
9656 mg_snprintf(conn,
9657 NULL, /* Buffer is big enough */
9658 size,
9659 sizeof(size),
9660 "%d",
9661 (int)de->file.size);
9662 } else if (de->file.size < 0x100000) {
9663 mg_snprintf(conn,
9664 NULL, /* Buffer is big enough */
9665 size,
9666 sizeof(size),
9667 "%.1fk",
9668 (double)de->file.size / 1024.0);
9669 } else if (de->file.size < 0x40000000) {
9670 mg_snprintf(conn,
9671 NULL, /* Buffer is big enough */
9672 size,
9673 sizeof(size),
9674 "%.1fM",
9675 (double)de->file.size / 1048576);
9676 } else {
9677 mg_snprintf(conn,
9678 NULL, /* Buffer is big enough */
9679 size,
9680 sizeof(size),
9681 "%.1fG",
9682 (double)de->file.size / 1073741824);
9683 }
9684 }
9685
9686 /* Note: mg_snprintf will not cause a buffer overflow above.
9687 * So, string truncation checks are not required here. */
9688
9689#if defined(REENTRANT_TIME)
9690 localtime_r(&de->file.last_modified, tm);
9691#else
9692 tm = localtime(&de->file.last_modified);
9693#endif
9694 if (tm != NULL) {
9695 strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm);
9696 } else {
9697 mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod));
9698 }
9699 mg_printf(conn,
9700 "<tr><td><a href=\"%s%s\">%s%s</a></td>"
9701 "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
9702 href,
9703 de->file.is_directory ? "/" : "",
9704 esc ? esc : de->file_name,
9705 de->file.is_directory ? "/" : "",
9706 mod,
9707 size);
9708 mg_free(href);
9709 return 0;
9710}
9711
9712
9713/* This function is called from send_directory() and used for
9714 * sorting directory entries by size, name, or modification time. */
9715static int
9716compare_dir_entries(const void *p1, const void *p2, void *arg)
9717{
9718 const char *query_string = (const char *)(arg != NULL ? arg : "");
9719 if (p1 && p2) {
9720 const struct de *a = (const struct de *)p1, *b = (const struct de *)p2;
9721 int cmp_result = 0;
9722
9723 if ((query_string == NULL) || (query_string[0] == '\0')) {
9724 query_string = "n";
9725 }
9726
9727 /* Sort Directories vs Files */
9728 if (a->file.is_directory && !b->file.is_directory) {
9729 return -1; /* Always put directories on top */
9730 } else if (!a->file.is_directory && b->file.is_directory) {
9731 return 1; /* Always put directories on top */
9732 }
9733
9734 /* Sort by size or date */
9735 if (*query_string == 's') {
9736 cmp_result = (a->file.size == b->file.size)
9737 ? 0
9738 : ((a->file.size > b->file.size) ? 1 : -1);
9739 } else if (*query_string == 'd') {
9740 cmp_result =
9741 (a->file.last_modified == b->file.last_modified)
9742 ? 0
9743 : ((a->file.last_modified > b->file.last_modified) ? 1
9744 : -1);
9745 }
9746
9747 /* Sort by name:
9748 * if (*query_string == 'n') ...
9749 * but also sort files of same size/date by name as secondary criterion.
9750 */
9751 if (cmp_result == 0) {
9752 cmp_result = strcmp(a->file_name, b->file_name);
9753 }
9754
9755 /* For descending order, invert result */
9756 return (query_string[1] == 'd') ? -cmp_result : cmp_result;
9757 }
9758 return 0;
9759}
9760
9761
9762static int
9763must_hide_file(struct mg_connection *conn, const char *path)
9764{
9765 if (conn && conn->dom_ctx) {
9766 const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
9767 const char *pattern = conn->dom_ctx->config[HIDE_FILES];
9768 return (match_prefix_strlen(pw_pattern, path) > 0)
9769 || (match_prefix_strlen(pattern, path) > 0);
9770 }
9771 return 0;
9772}
9773
9774
9775#if !defined(NO_FILESYSTEMS)
9776static int
9778 const char *dir,
9779 void *data,
9780 int (*cb)(struct de *, void *))
9781{
9782 char path[UTF8_PATH_MAX];
9783 struct dirent *dp;
9784 DIR *dirp;
9785 struct de de;
9786 int truncated;
9787
9788 if ((dirp = mg_opendir(conn, dir)) == NULL) {
9789 return 0;
9790 } else {
9791
9792 while ((dp = mg_readdir(dirp)) != NULL) {
9793 /* Do not show current dir and hidden files */
9794 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")
9795 || must_hide_file(conn, dp->d_name)) {
9796 continue;
9797 }
9798
9800 conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name);
9801
9802 /* If we don't memset stat structure to zero, mtime will have
9803 * garbage and strftime() will segfault later on in
9804 * print_dir_entry(). memset is required only if mg_stat()
9805 * fails. For more details, see
9806 * http://code.google.com/p/mongoose/issues/detail?id=79 */
9807 memset(&de.file, 0, sizeof(de.file));
9808
9809 if (truncated) {
9810 /* If the path is not complete, skip processing. */
9811 continue;
9812 }
9813
9814 if (!mg_stat(conn, path, &de.file)) {
9815 mg_cry_internal(conn,
9816 "%s: mg_stat(%s) failed: %s",
9817 __func__,
9818 path,
9819 strerror(ERRNO));
9820 }
9821 de.file_name = dp->d_name;
9822 if (cb(&de, data)) {
9823 /* stopped */
9824 break;
9825 }
9826 }
9827 (void)mg_closedir(dirp);
9828 }
9829 return 1;
9830}
9831#endif /* NO_FILESYSTEMS */
9832
9833
9834#if !defined(NO_FILES)
9835static int
9836remove_directory(struct mg_connection *conn, const char *dir)
9837{
9838 char path[UTF8_PATH_MAX];
9839 struct dirent *dp;
9840 DIR *dirp;
9841 struct de de;
9842 int truncated;
9843 int ok = 1;
9844
9845 if ((dirp = mg_opendir(conn, dir)) == NULL) {
9846 return 0;
9847 } else {
9848
9849 while ((dp = mg_readdir(dirp)) != NULL) {
9850 /* Do not show current dir (but show hidden files as they will
9851 * also be removed) */
9852 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
9853 continue;
9854 }
9855
9857 conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name);
9858
9859 /* If we don't memset stat structure to zero, mtime will have
9860 * garbage and strftime() will segfault later on in
9861 * print_dir_entry(). memset is required only if mg_stat()
9862 * fails. For more details, see
9863 * http://code.google.com/p/mongoose/issues/detail?id=79 */
9864 memset(&de.file, 0, sizeof(de.file));
9865
9866 if (truncated) {
9867 /* Do not delete anything shorter */
9868 ok = 0;
9869 continue;
9870 }
9871
9872 if (!mg_stat(conn, path, &de.file)) {
9873 mg_cry_internal(conn,
9874 "%s: mg_stat(%s) failed: %s",
9875 __func__,
9876 path,
9877 strerror(ERRNO));
9878 ok = 0;
9879 }
9880
9881 if (de.file.is_directory) {
9882 if (remove_directory(conn, path) == 0) {
9883 ok = 0;
9884 }
9885 } else {
9886 /* This will fail file is the file is in memory */
9887 if (mg_remove(conn, path) == 0) {
9888 ok = 0;
9889 }
9890 }
9891 }
9892 (void)mg_closedir(dirp);
9893
9894 IGNORE_UNUSED_RESULT(rmdir(dir));
9895 }
9896
9897 return ok;
9898}
9899#endif
9900
9901
9903 struct de *entries;
9905 size_t arr_size;
9906};
9907
9908
9909#if !defined(NO_FILESYSTEMS)
9910static int
9911dir_scan_callback(struct de *de, void *data)
9912{
9913 struct dir_scan_data *dsd = (struct dir_scan_data *)data;
9914 struct de *entries = dsd->entries;
9915
9916 if ((entries == NULL) || (dsd->num_entries >= dsd->arr_size)) {
9917 /* Here "entries" is a temporary pointer and can be replaced,
9918 * "dsd->entries" is the original pointer */
9919 entries =
9920 (struct de *)mg_realloc(entries,
9921 dsd->arr_size * 2 * sizeof(entries[0]));
9922 if (entries == NULL) {
9923 /* stop scan */
9924 return 1;
9925 }
9926 dsd->entries = entries;
9927 dsd->arr_size *= 2;
9928 }
9929 entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
9930 if (entries[dsd->num_entries].file_name == NULL) {
9931 /* stop scan */
9932 return 1;
9933 }
9934 entries[dsd->num_entries].file = de->file;
9935 dsd->num_entries++;
9936
9937 return 0;
9938}
9939
9940
9941static void
9942handle_directory_request(struct mg_connection *conn, const char *dir)
9943{
9944 size_t i;
9945 int sort_direction;
9946 struct dir_scan_data data = {NULL, 0, 128};
9947 char date[64], *esc, *p;
9948 const char *title;
9949 time_t curtime = time(NULL);
9950
9951 if (!conn) {
9952 return;
9953 }
9954
9955 if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
9956 mg_send_http_error(conn,
9957 500,
9958 "Error: Cannot open directory\nopendir(%s): %s",
9959 dir,
9960 strerror(ERRNO));
9961 return;
9962 }
9963
9964 gmt_time_string(date, sizeof(date), &curtime);
9965
9966 esc = NULL;
9967 title = conn->request_info.local_uri;
9968 if (title[strcspn(title, "&<>")]) {
9969 /* HTML escaping needed */
9970 esc = (char *)mg_malloc(strlen(title) * 5 + 1);
9971 if (esc) {
9972 for (i = 0, p = esc; title[i]; i++, p += strlen(p)) {
9973 mg_strlcpy(p, title + i, 2);
9974 if (*p == '&') {
9975 strcpy(p, "&amp;");
9976 } else if (*p == '<') {
9977 strcpy(p, "&lt;");
9978 } else if (*p == '>') {
9979 strcpy(p, "&gt;");
9980 }
9981 }
9982 } else {
9983 title = "";
9984 }
9985 }
9986
9987 sort_direction = ((conn->request_info.query_string != NULL)
9988 && (conn->request_info.query_string[0] != '\0')
9989 && (conn->request_info.query_string[1] == 'd'))
9990 ? 'a'
9991 : 'd';
9992
9993 conn->must_close = 1;
9994
9995 /* Create 200 OK response */
9996 mg_response_header_start(conn, 200);
10000 "Content-Type",
10001 "text/html; charset=utf-8",
10002 -1);
10003
10004 /* Send all headers */
10006
10007 /* Body */
10008 mg_printf(conn,
10009 "<!DOCTYPE html>"
10010 "<html><head><title>Index of %s</title>"
10011 "<style>th {text-align: left;}</style></head>"
10012 "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
10013 "<tr><th><a href=\"?n%c\">Name</a></th>"
10014 "<th><a href=\"?d%c\">Modified</a></th>"
10015 "<th><a href=\"?s%c\">Size</a></th></tr>"
10016 "<tr><td colspan=\"3\"><hr></td></tr>",
10017 esc ? esc : title,
10018 esc ? esc : title,
10019 sort_direction,
10020 sort_direction,
10021 sort_direction);
10022 mg_free(esc);
10023
10024 /* Print first entry - link to a parent directory */
10025 mg_printf(conn,
10026 "<tr><td><a href=\"%s\">%s</a></td>"
10027 "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
10028 "..",
10029 "Parent directory",
10030 "-",
10031 "-");
10032
10033 /* Sort and print directory entries */
10034 if (data.entries != NULL) {
10035 mg_sort(data.entries,
10036 data.num_entries,
10037 sizeof(data.entries[0]),
10039 (void *)conn->request_info.query_string);
10040 for (i = 0; i < data.num_entries; i++) {
10041 print_dir_entry(conn, &data.entries[i]);
10042 mg_free(data.entries[i].file_name);
10043 }
10044 mg_free(data.entries);
10045 }
10046
10047 mg_printf(conn, "%s", "</table></pre></body></html>");
10048 conn->status_code = 200;
10049}
10050#endif /* NO_FILESYSTEMS */
10051
10052
10053/* Send len bytes from the opened file to the client. */
10054static void
10056 struct mg_file *filep,
10057 int64_t offset,
10058 int64_t len,
10059 int no_buffering)
10060{
10061 char buf[MG_BUF_LEN];
10062 int to_read, num_read, num_written;
10063 int64_t size;
10064
10065 if (!filep || !conn) {
10066 return;
10067 }
10068
10069 /* Sanity check the offset */
10070 size = (filep->stat.size > INT64_MAX) ? INT64_MAX
10071 : (int64_t)(filep->stat.size);
10072 offset = (offset < 0) ? 0 : ((offset > size) ? size : offset);
10073
10074 if (len > 0 && filep->access.fp != NULL) {
10075 /* file stored on disk */
10076#if defined(__linux__)
10077 /* sendfile is only available for Linux */
10078 if ((conn->ssl == 0) && (conn->throttle == 0)
10079 && (!mg_strcasecmp(conn->dom_ctx->config[ALLOW_SENDFILE_CALL],
10080 "yes"))) {
10081 off_t sf_offs = (off_t)offset;
10082 ssize_t sf_sent;
10083 int sf_file = fileno(filep->access.fp);
10084 int loop_cnt = 0;
10085
10086 do {
10087 /* 2147479552 (0x7FFFF000) is a limit found by experiment on
10088 * 64 bit Linux (2^31 minus one memory page of 4k?). */
10089 size_t sf_tosend =
10090 (size_t)((len < 0x7FFFF000) ? len : 0x7FFFF000);
10091 sf_sent =
10092 sendfile(conn->client.sock, sf_file, &sf_offs, sf_tosend);
10093 if (sf_sent > 0) {
10094 len -= sf_sent;
10095 offset += sf_sent;
10096 } else if (loop_cnt == 0) {
10097 /* This file can not be sent using sendfile.
10098 * This might be the case for pseudo-files in the
10099 * /sys/ and /proc/ file system.
10100 * Use the regular user mode copy code instead. */
10101 break;
10102 } else if (sf_sent == 0) {
10103 /* No error, but 0 bytes sent. May be EOF? */
10104 return;
10105 }
10106 loop_cnt++;
10107
10108 } while ((len > 0) && (sf_sent >= 0));
10109
10110 if (sf_sent > 0) {
10111 return; /* OK */
10112 }
10113
10114 /* sf_sent<0 means error, thus fall back to the classic way */
10115 /* This is always the case, if sf_file is not a "normal" file,
10116 * e.g., for sending data from the output of a CGI process. */
10117 offset = (int64_t)sf_offs;
10118 }
10119#endif
10120 if ((offset > 0) && (fseeko(filep->access.fp, offset, SEEK_SET) != 0)) {
10121 mg_cry_internal(conn,
10122 "%s: fseeko() failed: %s",
10123 __func__,
10124 strerror(ERRNO));
10126 conn,
10127 500,
10128 "%s",
10129 "Error: Unable to access file at requested position.");
10130 } else {
10131 while (len > 0) {
10132 /* Calculate how much to read from the file into the buffer. */
10133 /* If no_buffering is set, we should not wait until the
10134 * CGI->Server buffer is filled, but send everything
10135 * immediately. In theory buffering could be turned off using
10136 * setbuf(filep->access.fp, NULL);
10137 * setvbuf(filep->access.fp, NULL, _IONBF, 0);
10138 * but in practice this does not work. A "Linux only" solution
10139 * may be to use select(). The only portable way is to read byte
10140 * by byte, but this is quite inefficient from a performance
10141 * point of view. */
10142 to_read = no_buffering ? 1 : sizeof(buf);
10143 if ((int64_t)to_read > len) {
10144 to_read = (int)len;
10145 }
10146
10147 /* Read from file, exit the loop on error */
10148 if ((num_read =
10149 (int)fread(buf, 1, (size_t)to_read, filep->access.fp))
10150 <= 0) {
10151 break;
10152 }
10153
10154 /* Send read bytes to the client, exit the loop on error */
10155 if ((num_written = mg_write(conn, buf, (size_t)num_read))
10156 != num_read) {
10157 break;
10158 }
10159
10160 /* Both read and were successful, adjust counters */
10161 len -= num_written;
10162 }
10163 }
10164 }
10165}
10166
10167
10168static int
10169parse_range_header(const char *header, int64_t *a, int64_t *b)
10170{
10171 return sscanf(header,
10172 "bytes=%" INT64_FMT "-%" INT64_FMT,
10173 a,
10174 b); // NOLINT(cert-err34-c) 'sscanf' used to convert a string
10175 // to an integer value, but function will not report
10176 // conversion errors; consider using 'strtol' instead
10177}
10178
10179
10180static void
10181construct_etag(char *buf, size_t buf_len, const struct mg_file_stat *filestat)
10182{
10183 if ((filestat != NULL) && (buf != NULL)) {
10185 NULL, /* All calls to construct_etag use 64 byte buffer */
10186 buf,
10187 buf_len,
10188 "\"%lx.%" INT64_FMT "\"",
10189 (unsigned long)filestat->last_modified,
10190 filestat->size);
10191 }
10192}
10193
10194
10195static void
10196fclose_on_exec(struct mg_file_access *filep, struct mg_connection *conn)
10197{
10198 if (filep != NULL && filep->fp != NULL) {
10199#if defined(_WIN32)
10200 (void)conn; /* Unused. */
10201#else
10202 if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) {
10203 mg_cry_internal(conn,
10204 "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
10205 __func__,
10206 strerror(ERRNO));
10207 }
10208#endif
10209 }
10210}
10211
10212
10213#if defined(USE_ZLIB)
10214#include "mod_zlib.inl"
10215#endif
10216
10217
10218#if !defined(NO_FILESYSTEMS)
10219static void
10221 const char *path,
10222 struct mg_file *filep,
10223 const char *mime_type,
10224 const char *additional_headers)
10225{
10226 char lm[64], etag[64];
10227 char range[128]; /* large enough, so there will be no overflow */
10228 const char *range_hdr;
10229 int64_t cl, r1, r2;
10230 struct vec mime_vec;
10231 int n, truncated;
10232 char gz_path[UTF8_PATH_MAX];
10233 const char *encoding = 0;
10234 int is_head_request;
10235
10236#if defined(USE_ZLIB)
10237 /* Compression is allowed, unless there is a reason not to use
10238 * compression. If the file is already compressed, too small or a
10239 * "range" request was made, on the fly compression is not possible. */
10240 int allow_on_the_fly_compression = 1;
10241#endif
10242
10243 if ((conn == NULL) || (conn->dom_ctx == NULL) || (filep == NULL)) {
10244 return;
10245 }
10246
10247 is_head_request = !strcmp(conn->request_info.request_method, "HEAD");
10248
10249 if (mime_type == NULL) {
10250 get_mime_type(conn, path, &mime_vec);
10251 } else {
10252 mime_vec.ptr = mime_type;
10253 mime_vec.len = strlen(mime_type);
10254 }
10255 if (filep->stat.size > INT64_MAX) {
10256 mg_send_http_error(conn,
10257 500,
10258 "Error: File size is too large to send\n%" INT64_FMT,
10259 filep->stat.size);
10260 return;
10261 }
10262 cl = (int64_t)filep->stat.size;
10263 conn->status_code = 200;
10264 range[0] = '\0';
10265
10266#if defined(USE_ZLIB)
10267 /* if this file is in fact a pre-gzipped file, rewrite its filename
10268 * it's important to rewrite the filename after resolving
10269 * the mime type from it, to preserve the actual file's type */
10270 if (!conn->accept_gzip) {
10271 allow_on_the_fly_compression = 0;
10272 }
10273#endif
10274
10275 /* Check if there is a range header */
10276 range_hdr = mg_get_header(conn, "Range");
10277
10278 /* For gzipped files, add *.gz */
10279 if (filep->stat.is_gzipped) {
10280 mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path);
10281
10282 if (truncated) {
10283 mg_send_http_error(conn,
10284 500,
10285 "Error: Path of zipped file too long (%s)",
10286 path);
10287 return;
10288 }
10289
10290 path = gz_path;
10291 encoding = "gzip";
10292
10293#if defined(USE_ZLIB)
10294 /* File is already compressed. No "on the fly" compression. */
10295 allow_on_the_fly_compression = 0;
10296#endif
10297 } else if ((conn->accept_gzip) && (range_hdr == NULL)
10298 && (filep->stat.size >= MG_FILE_COMPRESSION_SIZE_LIMIT)) {
10299 struct mg_file_stat file_stat;
10300
10301 mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path);
10302
10303 if (!truncated && mg_stat(conn, gz_path, &file_stat)
10304 && !file_stat.is_directory) {
10305 file_stat.is_gzipped = 1;
10306 filep->stat = file_stat;
10307 cl = (int64_t)filep->stat.size;
10308 path = gz_path;
10309 encoding = "gzip";
10310
10311#if defined(USE_ZLIB)
10312 /* File is already compressed. No "on the fly" compression. */
10313 allow_on_the_fly_compression = 0;
10314#endif
10315 }
10316 }
10317
10318 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) {
10319 mg_send_http_error(conn,
10320 500,
10321 "Error: Cannot open file\nfopen(%s): %s",
10322 path,
10323 strerror(ERRNO));
10324 return;
10325 }
10326
10327 fclose_on_exec(&filep->access, conn);
10328
10329 /* If "Range" request was made: parse header, send only selected part
10330 * of the file. */
10331 r1 = r2 = 0;
10332 if ((range_hdr != NULL)
10333 && ((n = parse_range_header(range_hdr, &r1, &r2)) > 0) && (r1 >= 0)
10334 && (r2 >= 0)) {
10335 /* actually, range requests don't play well with a pre-gzipped
10336 * file (since the range is specified in the uncompressed space) */
10337 if (filep->stat.is_gzipped) {
10339 conn,
10340 416, /* 416 = Range Not Satisfiable */
10341 "%s",
10342 "Error: Range requests in gzipped files are not supported");
10343 (void)mg_fclose(
10344 &filep->access); /* ignore error on read only file */
10345 return;
10346 }
10347 conn->status_code = 206;
10348 cl = (n == 2) ? (((r2 > cl) ? cl : r2) - r1 + 1) : (cl - r1);
10349 mg_snprintf(conn,
10350 NULL, /* range buffer is big enough */
10351 range,
10352 sizeof(range),
10353 "bytes "
10354 "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT,
10355 r1,
10356 r1 + cl - 1,
10357 filep->stat.size);
10358
10359#if defined(USE_ZLIB)
10360 /* Do not compress ranges. */
10361 allow_on_the_fly_compression = 0;
10362#endif
10363 }
10364
10365 /* Do not compress small files. Small files do not benefit from file
10366 * compression, but there is still some overhead. */
10367#if defined(USE_ZLIB)
10369 /* File is below the size limit. */
10370 allow_on_the_fly_compression = 0;
10371 }
10372#endif
10373
10374 /* Prepare Etag, and Last-Modified headers. */
10375 gmt_time_string(lm, sizeof(lm), &filep->stat.last_modified);
10376 construct_etag(etag, sizeof(etag), &filep->stat);
10377
10378 /* Create 2xx (200, 206) response */
10382 send_cors_header(conn);
10384 "Content-Type",
10385 mime_vec.ptr,
10386 (int)mime_vec.len);
10387 mg_response_header_add(conn, "Last-Modified", lm, -1);
10388 mg_response_header_add(conn, "Etag", etag, -1);
10389
10390#if defined(USE_ZLIB)
10391 /* On the fly compression allowed */
10392 if (allow_on_the_fly_compression) {
10393 /* For on the fly compression, we don't know the content size in
10394 * advance, so we have to use chunked encoding */
10395 encoding = "gzip";
10396 if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
10397 /* HTTP/2 is always using "chunks" (frames) */
10398 mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1);
10399 }
10400
10401 } else
10402#endif
10403 {
10404 /* Without on-the-fly compression, we know the content-length
10405 * and we can use ranges (with on-the-fly compression we cannot).
10406 * So we send these response headers only in this case. */
10407 char len[32];
10408 int trunc = 0;
10409 mg_snprintf(conn, &trunc, len, sizeof(len), "%" INT64_FMT, cl);
10410
10411 if (!trunc) {
10412 mg_response_header_add(conn, "Content-Length", len, -1);
10413 }
10414
10415 mg_response_header_add(conn, "Accept-Ranges", "bytes", -1);
10416 }
10417
10418 if (encoding) {
10419 mg_response_header_add(conn, "Content-Encoding", encoding, -1);
10420 }
10421 if (range[0] != 0) {
10422 mg_response_header_add(conn, "Content-Range", range, -1);
10423 }
10424
10425 /* The code above does not add any header starting with X- to make
10426 * sure no one of the additional_headers is included twice */
10427 if ((additional_headers != NULL) && (*additional_headers != 0)) {
10428 mg_response_header_add_lines(conn, additional_headers);
10429 }
10430
10431 /* Send all headers */
10433
10434 if (!is_head_request) {
10435#if defined(USE_ZLIB)
10436 if (allow_on_the_fly_compression) {
10437 /* Compress and send */
10438 send_compressed_data(conn, filep);
10439 } else
10440#endif
10441 {
10442 /* Send file directly */
10443 send_file_data(conn, filep, r1, cl, 0); /* send static file */
10444 }
10445 }
10446 (void)mg_fclose(&filep->access); /* ignore error on read only file */
10447}
10448
10449
10450CIVETWEB_API int
10451mg_send_file_body(struct mg_connection *conn, const char *path)
10452{
10453 struct mg_file file = STRUCT_FILE_INITIALIZER;
10454 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, &file)) {
10455 return -1;
10456 }
10457 fclose_on_exec(&file.access, conn);
10458 send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
10459 (void)mg_fclose(&file.access); /* Ignore errors for readonly files */
10460 return 0; /* >= 0 for OK */
10461}
10462#endif /* NO_FILESYSTEMS */
10463
10464
10465#if !defined(NO_CACHING)
10466/* Return True if we should reply 304 Not Modified. */
10467static int
10469 const struct mg_file_stat *filestat)
10470{
10471 char etag[64];
10472 const char *ims = mg_get_header(conn, "If-Modified-Since");
10473 const char *inm = mg_get_header(conn, "If-None-Match");
10474 construct_etag(etag, sizeof(etag), filestat);
10475
10476 return ((inm != NULL) && !mg_strcasecmp(etag, inm))
10477 || ((ims != NULL)
10478 && (filestat->last_modified <= parse_date_string(ims)));
10479}
10480
10481
10482static void
10484 struct mg_file *filep)
10485{
10486 char lm[64], etag[64];
10487
10488 if ((conn == NULL) || (filep == NULL)) {
10489 return;
10490 }
10491
10492 gmt_time_string(lm, sizeof(lm), &filep->stat.last_modified);
10493 construct_etag(etag, sizeof(etag), &filep->stat);
10494
10495 /* Create 304 "not modified" response */
10496 mg_response_header_start(conn, 304);
10499 mg_response_header_add(conn, "Last-Modified", lm, -1);
10500 mg_response_header_add(conn, "Etag", etag, -1);
10501
10502 /* Send all headers */
10504}
10505#endif
10506
10507
10508#if !defined(NO_FILESYSTEMS)
10509CIVETWEB_API void
10510mg_send_file(struct mg_connection *conn, const char *path)
10511{
10512 mg_send_mime_file2(conn, path, NULL, NULL);
10513}
10514
10515
10516CIVETWEB_API void
10518 const char *path,
10519 const char *mime_type)
10520{
10521 mg_send_mime_file2(conn, path, mime_type, NULL);
10522}
10523
10524
10525CIVETWEB_API void
10527 const char *path,
10528 const char *mime_type,
10529 const char *additional_headers)
10530{
10531 struct mg_file file = STRUCT_FILE_INITIALIZER;
10532
10533 if (!conn) {
10534 /* No conn */
10535 return;
10536 }
10537
10538 if (mg_stat(conn, path, &file.stat)) {
10539#if !defined(NO_CACHING)
10540 if (is_not_modified(conn, &file.stat)) {
10541 /* Send 304 "Not Modified" - this must not send any body data */
10543 } else
10544#endif /* NO_CACHING */
10545 if (file.stat.is_directory) {
10547 "yes")) {
10548 handle_directory_request(conn, path);
10549 } else {
10550 mg_send_http_error(conn,
10551 403,
10552 "%s",
10553 "Error: Directory listing denied");
10554 }
10555 } else {
10557 conn, path, &file, mime_type, additional_headers);
10558 }
10559 } else {
10560 mg_send_http_error(conn, 404, "%s", "Error: File not found");
10561 }
10562}
10563
10564
10565/* For a given PUT path, create all intermediate subdirectories.
10566 * Return 0 if the path itself is a directory.
10567 * Return 1 if the path leads to a file.
10568 * Return -1 for if the path is too long.
10569 * Return -2 if path can not be created.
10570 */
10571static int
10572put_dir(struct mg_connection *conn, const char *path)
10573{
10574 char buf[UTF8_PATH_MAX];
10575 const char *s, *p;
10576 struct mg_file file = STRUCT_FILE_INITIALIZER;
10577 size_t len;
10578 int res = 1;
10579
10580 for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
10581 len = (size_t)(p - path);
10582 if (len >= sizeof(buf)) {
10583 /* path too long */
10584 res = -1;
10585 break;
10586 }
10587 memcpy(buf, path, len);
10588 buf[len] = '\0';
10589
10590 /* Try to create intermediate directory */
10591 DEBUG_TRACE("mkdir(%s)", buf);
10592 if (!mg_stat(conn, buf, &file.stat) && mg_mkdir(conn, buf, 0755) != 0) {
10593 /* path does not exist and can not be created */
10594 res = -2;
10595 break;
10596 }
10597
10598 /* Is path itself a directory? */
10599 if (p[1] == '\0') {
10600 res = 0;
10601 }
10602 }
10603
10604 return res;
10605}
10606
10607
10608static void
10609remove_bad_file(const struct mg_connection *conn, const char *path)
10610{
10611 int r = mg_remove(conn, path);
10612 if (r != 0) {
10613 mg_cry_internal(conn,
10614 "%s: Cannot remove invalid file %s",
10615 __func__,
10616 path);
10617 }
10618}
10619
10620
10621CIVETWEB_API long long
10622mg_store_body(struct mg_connection *conn, const char *path)
10623{
10624 char buf[MG_BUF_LEN];
10625 long long len = 0;
10626 int ret, n;
10627 struct mg_file fi;
10628
10629 if (conn->consumed_content != 0) {
10630 mg_cry_internal(conn, "%s: Contents already consumed", __func__);
10631 return -11;
10632 }
10633
10634 ret = put_dir(conn, path);
10635 if (ret < 0) {
10636 /* -1 for path too long,
10637 * -2 for path can not be created. */
10638 return ret;
10639 }
10640 if (ret != 1) {
10641 /* Return 0 means, path itself is a directory. */
10642 return 0;
10643 }
10644
10645 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fi) == 0) {
10646 return -12;
10647 }
10648
10649 ret = mg_read(conn, buf, sizeof(buf));
10650 while (ret > 0) {
10651 n = (int)fwrite(buf, 1, (size_t)ret, fi.access.fp);
10652 if (n != ret) {
10653 (void)mg_fclose(
10654 &fi.access); /* File is bad and will be removed anyway. */
10655 remove_bad_file(conn, path);
10656 return -13;
10657 }
10658 len += ret;
10659 ret = mg_read(conn, buf, sizeof(buf));
10660 }
10661
10662 /* File is open for writing. If fclose fails, there was probably an
10663 * error flushing the buffer to disk, so the file on disk might be
10664 * broken. Delete it and return an error to the caller. */
10665 if (mg_fclose(&fi.access) != 0) {
10666 remove_bad_file(conn, path);
10667 return -14;
10668 }
10669
10670 return len;
10671}
10672#endif /* NO_FILESYSTEMS */
10673
10674
10675/* Parse a buffer:
10676 * Forward the string pointer till the end of a word, then
10677 * terminate it and forward till the begin of the next word.
10678 */
10679static int
10681{
10682 /* Forward until a space is found - use isgraph here */
10683 /* See http://www.cplusplus.com/reference/cctype/ */
10684 while (isgraph((unsigned char)**ppw)) {
10685 (*ppw)++;
10686 }
10687
10688 /* Check end of word */
10689 if (eol) {
10690 /* must be a end of line */
10691 if ((**ppw != '\r') && (**ppw != '\n')) {
10692 return -1;
10693 }
10694 } else {
10695 /* must be a end of a word, but not a line */
10696 if (**ppw != ' ') {
10697 return -1;
10698 }
10699 }
10700
10701 /* Terminate and forward to the next word */
10702 do {
10703 **ppw = 0;
10704 (*ppw)++;
10705 } while (isspace((unsigned char)**ppw));
10706
10707 /* Check after term */
10708 if (!eol) {
10709 /* if it's not the end of line, there must be a next word */
10710 if (!isgraph((unsigned char)**ppw)) {
10711 return -1;
10712 }
10713 }
10714
10715 /* ok */
10716 return 1;
10717}
10718
10719
10720/* Parse HTTP headers from the given buffer, advance buf pointer
10721 * to the point where parsing stopped.
10722 * All parameters must be valid pointers (not NULL).
10723 * Return <0 on error. */
10724static int
10726{
10727 int i;
10728 int num_headers = 0;
10729
10730 for (i = 0; i < (int)MG_MAX_HEADERS; i++) {
10731 char *dp = *buf;
10732
10733 /* Skip all ASCII characters (>SPACE, <127), to find a ':' */
10734 while ((*dp != ':') && (*dp >= 33) && (*dp <= 126)) {
10735 dp++;
10736 }
10737 if (dp == *buf) {
10738 /* End of headers reached. */
10739 break;
10740 }
10741
10742 /* Drop all spaces after header name before : */
10743 while (*dp == ' ') {
10744 *dp = 0;
10745 dp++;
10746 }
10747 if (*dp != ':') {
10748 /* This is not a valid field. */
10749 return -1;
10750 }
10751
10752 /* End of header key (*dp == ':') */
10753 /* Truncate here and set the key name */
10754 *dp = 0;
10755 hdr[i].name = *buf;
10756
10757 /* Skip all spaces */
10758 do {
10759 dp++;
10760 } while ((*dp == ' ') || (*dp == '\t'));
10761
10762 /* The rest of the line is the value */
10763 hdr[i].value = dp;
10764
10765 /* Find end of line */
10766 while ((*dp != 0) && (*dp != '\r') && (*dp != '\n')) {
10767 dp++;
10768 };
10769
10770 /* eliminate \r */
10771 if (*dp == '\r') {
10772 *dp = 0;
10773 dp++;
10774 if (*dp != '\n') {
10775 /* This is not a valid line. */
10776 return -1;
10777 }
10778 }
10779
10780 /* here *dp is either 0 or '\n' */
10781 /* in any case, we have a new header */
10782 num_headers = i + 1;
10783
10784 if (*dp) {
10785 *dp = 0;
10786 dp++;
10787 *buf = dp;
10788
10789 if ((dp[0] == '\r') || (dp[0] == '\n')) {
10790 /* This is the end of the header */
10791 break;
10792 }
10793 } else {
10794 *buf = dp;
10795 break;
10796 }
10797 }
10798 return num_headers;
10799}
10800
10801
10810
10811
10812/* https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods */
10813static const struct mg_http_method_info http_methods[] = {
10814 /* HTTP (RFC 2616) */
10815 {"GET", 0, 1, 1, 1, 1},
10816 {"POST", 1, 1, 0, 0, 0},
10817 {"PUT", 1, 0, 0, 1, 0},
10818 {"DELETE", 0, 0, 0, 1, 0},
10819 {"HEAD", 0, 0, 1, 1, 1},
10820 {"OPTIONS", 0, 0, 1, 1, 0},
10821 {"CONNECT", 1, 1, 0, 0, 0},
10822 /* TRACE method (RFC 2616) is not supported for security reasons */
10823
10824 /* PATCH method (RFC 5789) */
10825 {"PATCH", 1, 0, 0, 0, 0},
10826 /* PATCH method only allowed for CGI/Lua/LSP and callbacks. */
10827
10828 /* WEBDAV (RFC 2518) */
10829 {"PROPFIND", 0, 1, 1, 1, 0},
10830 /* http://www.webdav.org/specs/rfc4918.html, 9.1:
10831 * Some PROPFIND results MAY be cached, with care,
10832 * as there is no cache validation mechanism for
10833 * most properties. This method is both safe and
10834 * idempotent (see Section 9.1 of [RFC2616]). */
10835 {"MKCOL", 0, 0, 0, 1, 0},
10836 /* http://www.webdav.org/specs/rfc4918.html, 9.1:
10837 * When MKCOL is invoked without a request body,
10838 * the newly created collection SHOULD have no
10839 * members. A MKCOL request message may contain
10840 * a message body. The precise behavior of a MKCOL
10841 * request when the body is present is undefined,
10842 * ... ==> We do not support MKCOL with body data.
10843 * This method is idempotent, but not safe (see
10844 * Section 9.1 of [RFC2616]). Responses to this
10845 * method MUST NOT be cached. */
10846
10847 /* Methods for write access to files on WEBDAV (RFC 2518) */
10848 {"LOCK", 1, 1, 0, 0, 0},
10849 {"UNLOCK", 1, 0, 0, 0, 0},
10850 {"PROPPATCH", 1, 1, 0, 0, 0},
10851 {"COPY", 1, 0, 0, 0, 0},
10852 {"MOVE", 1, 1, 0, 0, 0},
10853
10854 /* Unsupported WEBDAV Methods: */
10855 /* + 11 methods from RFC 3253 */
10856 /* ORDERPATCH (RFC 3648) */
10857 /* ACL (RFC 3744) */
10858 /* SEARCH (RFC 5323) */
10859 /* + MicroSoft extensions
10860 * https://msdn.microsoft.com/en-us/library/aa142917.aspx */
10861
10862 /* REPORT method (RFC 3253) */
10863 {"REPORT", 1, 1, 1, 1, 1},
10864 /* REPORT method only allowed for CGI/Lua/LSP and callbacks. */
10865 /* It was defined for WEBDAV in RFC 3253, Sec. 3.6
10866 * (https://tools.ietf.org/html/rfc3253#section-3.6), but seems
10867 * to be useful for REST in case a "GET request with body" is
10868 * required. */
10869
10870 {NULL, 0, 0, 0, 0, 0}
10871 /* end of list */
10872};
10873
10874
10875/* All method names */
10876static char *all_methods = NULL; /* Built by mg_init_library */
10877
10878
10879static const struct mg_http_method_info *
10880get_http_method_info(const char *method)
10881{
10882 /* Check if the method is known to the server. The list of all known
10883 * HTTP methods can be found here at
10884 * http://www.iana.org/assignments/http-methods/http-methods.xhtml
10885 */
10886 const struct mg_http_method_info *m = http_methods;
10887
10888 while (m->name) {
10889 if (!strcmp(m->name, method)) {
10890 return m;
10891 }
10892 m++;
10893 }
10894 return NULL;
10895}
10896
10897
10898static int
10899is_valid_http_method(const char *method)
10900{
10901 return (get_http_method_info(method) != NULL);
10902}
10903
10904
10905/* Parse HTTP request, fill in mg_request_info structure.
10906 * This function modifies the buffer by NUL-terminating
10907 * HTTP request components, header names and header values.
10908 * Parameters:
10909 * buf (in/out): pointer to the HTTP header to parse and split
10910 * len (in): length of HTTP header buffer
10911 * re (out): parsed header as mg_request_info
10912 * buf and ri must be valid pointers (not NULL), len>0.
10913 * Returns <0 on error. */
10914static int
10915parse_http_request(char *buf, int len, struct mg_request_info *ri)
10916{
10917 int request_length;
10918 int init_skip = 0;
10919
10920 /* Reset attributes. DO NOT TOUCH is_ssl, remote_addr,
10921 * remote_port */
10922 ri->remote_user = ri->request_method = ri->request_uri = ri->http_version =
10923 NULL;
10924 ri->num_headers = 0;
10925
10926 /* RFC says that all initial whitespaces should be ignored */
10927 /* This included all leading \r and \n (isspace) */
10928 /* See table: http://www.cplusplus.com/reference/cctype/ */
10929 while ((len > 0) && isspace((unsigned char)*buf)) {
10930 buf++;
10931 len--;
10932 init_skip++;
10933 }
10934
10935 if (len == 0) {
10936 /* Incomplete request */
10937 return 0;
10938 }
10939
10940 /* Control characters are not allowed, including zero */
10941 if (iscntrl((unsigned char)*buf)) {
10942 return -1;
10943 }
10944
10945 /* Find end of HTTP header */
10946 request_length = get_http_header_len(buf, len);
10947 if (request_length <= 0) {
10948 return request_length;
10949 }
10950 buf[request_length - 1] = '\0';
10951
10952 if ((*buf == 0) || (*buf == '\r') || (*buf == '\n')) {
10953 return -1;
10954 }
10955
10956 /* The first word has to be the HTTP method */
10957 ri->request_method = buf;
10958
10959 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
10960 return -1;
10961 }
10962
10963 /* The second word is the URI */
10964 ri->request_uri = buf;
10965
10966 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
10967 return -1;
10968 }
10969
10970 /* Next would be the HTTP version */
10971 ri->http_version = buf;
10972
10973 if (skip_to_end_of_word_and_terminate(&buf, 1) <= 0) {
10974 return -1;
10975 }
10976
10977 /* Check for a valid HTTP version key */
10978 if (strncmp(ri->http_version, "HTTP/", 5) != 0) {
10979 /* Invalid request */
10980 return -1;
10981 }
10982 ri->http_version += 5;
10983
10984 /* Check for a valid http method */
10986 return -1;
10987 }
10988
10989 /* Parse all HTTP headers */
10991 if (ri->num_headers < 0) {
10992 /* Error while parsing headers */
10993 return -1;
10994 }
10995
10996 return request_length + init_skip;
10997}
10998
10999
11000static int
11001parse_http_response(char *buf, int len, struct mg_response_info *ri)
11002{
11003 int response_length;
11004 int init_skip = 0;
11005 char *tmp, *tmp2;
11006 long l;
11007
11008 /* Initialize elements. */
11009 ri->http_version = ri->status_text = NULL;
11010 ri->num_headers = ri->status_code = 0;
11011
11012 /* RFC says that all initial whitespaces should be ignored */
11013 /* This included all leading \r and \n (isspace) */
11014 /* See table: http://www.cplusplus.com/reference/cctype/ */
11015 while ((len > 0) && isspace((unsigned char)*buf)) {
11016 buf++;
11017 len--;
11018 init_skip++;
11019 }
11020
11021 if (len == 0) {
11022 /* Incomplete request */
11023 return 0;
11024 }
11025
11026 /* Control characters are not allowed, including zero */
11027 if (iscntrl((unsigned char)*buf)) {
11028 return -1;
11029 }
11030
11031 /* Find end of HTTP header */
11032 response_length = get_http_header_len(buf, len);
11033 if (response_length <= 0) {
11034 return response_length;
11035 }
11036 buf[response_length - 1] = '\0';
11037
11038 if ((*buf == 0) || (*buf == '\r') || (*buf == '\n')) {
11039 return -1;
11040 }
11041
11042 /* The first word is the HTTP version */
11043 /* Check for a valid HTTP version key */
11044 if (strncmp(buf, "HTTP/", 5) != 0) {
11045 /* Invalid request */
11046 return -1;
11047 }
11048 buf += 5;
11049 if (!isgraph((unsigned char)buf[0])) {
11050 /* Invalid request */
11051 return -1;
11052 }
11053 ri->http_version = buf;
11054
11055 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
11056 return -1;
11057 }
11058
11059 /* The second word is the status as a number */
11060 tmp = buf;
11061
11062 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
11063 return -1;
11064 }
11065
11066 l = strtol(tmp, &tmp2, 10);
11067 if ((l < 100) || (l >= 1000) || ((tmp2 - tmp) != 3) || (*tmp2 != 0)) {
11068 /* Everything else but a 3 digit code is invalid */
11069 return -1;
11070 }
11071 ri->status_code = (int)l;
11072
11073 /* The rest of the line is the status text */
11074 ri->status_text = buf;
11075
11076 /* Find end of status text */
11077 /* isgraph or isspace = isprint */
11078 while (isprint((unsigned char)*buf)) {
11079 buf++;
11080 }
11081 if ((*buf != '\r') && (*buf != '\n')) {
11082 return -1;
11083 }
11084 /* Terminate string and forward buf to next line */
11085 do {
11086 *buf = 0;
11087 buf++;
11088 } while (isspace((unsigned char)*buf));
11089
11090 /* Parse all HTTP headers */
11092 if (ri->num_headers < 0) {
11093 /* Error while parsing headers */
11094 return -1;
11095 }
11096
11097 return response_length + init_skip;
11098}
11099
11100
11101/* Keep reading the input (either opened file descriptor fd, or socket sock,
11102 * or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
11103 * buffer (which marks the end of HTTP request). Buffer buf may already
11104 * have some data. The length of the data is stored in nread.
11105 * Upon every read operation, increase nread by the number of bytes read. */
11106static int
11108 struct mg_connection *conn,
11109 char *buf,
11110 int bufsiz,
11111 int *nread)
11112{
11113 int request_len, n = 0;
11114 struct timespec last_action_time;
11115 double request_timeout;
11116
11117 if (!conn) {
11118 return 0;
11119 }
11120
11121 memset(&last_action_time, 0, sizeof(last_action_time));
11122
11123 if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
11124 /* value of request_timeout is in seconds, config in milliseconds */
11125 request_timeout =
11126 strtod(conn->dom_ctx->config[REQUEST_TIMEOUT], NULL) / 1000.0;
11127 } else {
11128 request_timeout =
11129 strtod(config_options[REQUEST_TIMEOUT].default_value, NULL)
11130 / 1000.0;
11131 }
11132 if (conn->handled_requests > 0) {
11133 if (conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT]) {
11134 request_timeout =
11135 strtod(conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT], NULL)
11136 / 1000.0;
11137 }
11138 }
11139
11140 request_len = get_http_header_len(buf, *nread);
11141
11142 while (request_len == 0) {
11143 /* Full request not yet received */
11144 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
11145 /* Server is to be stopped. */
11146 return -1;
11147 }
11148
11149 if (*nread >= bufsiz) {
11150 /* Request too long */
11151 return -2;
11152 }
11153
11154 n = pull_inner(
11155 fp, conn, buf + *nread, bufsiz - *nread, request_timeout);
11156 if (n == -2) {
11157 /* Receive error */
11158 return -1;
11159 }
11160
11161 /* update clock after every read request */
11162 clock_gettime(CLOCK_MONOTONIC, &last_action_time);
11163
11164 if (n > 0) {
11165 *nread += n;
11166 request_len = get_http_header_len(buf, *nread);
11167 }
11168
11169 if ((request_len == 0) && (request_timeout >= 0)) {
11170 if (mg_difftimespec(&last_action_time, &(conn->req_time))
11171 > request_timeout) {
11172 /* Timeout */
11173 return -1;
11174 }
11175 }
11176 }
11177
11178 return request_len;
11179}
11180
11181
11182#if !defined(NO_CGI) || !defined(NO_FILES)
11183static int
11184forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
11185{
11186 const char *expect;
11187 char buf[MG_BUF_LEN];
11188 int success = 0;
11189
11190 if (!conn) {
11191 return 0;
11192 }
11193
11194 expect = mg_get_header(conn, "Expect");
11195 DEBUG_ASSERT(fp != NULL);
11196 if (!fp) {
11197 mg_send_http_error(conn, 500, "%s", "Error: NULL File");
11198 return 0;
11199 }
11200
11201 if ((expect != NULL) && (mg_strcasecmp(expect, "100-continue") != 0)) {
11202 /* Client sent an "Expect: xyz" header and xyz is not 100-continue.
11203 */
11204 mg_send_http_error(conn, 417, "Error: Can not fulfill expectation");
11205 } else {
11206 if (expect != NULL) {
11207 (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
11208 conn->status_code = 100;
11209 } else {
11210 conn->status_code = 200;
11211 }
11212
11213 DEBUG_ASSERT(conn->consumed_content == 0);
11214
11215 if (conn->consumed_content != 0) {
11216 mg_send_http_error(conn, 500, "%s", "Error: Size mismatch");
11217 return 0;
11218 }
11219
11220 for (;;) {
11221 int nread = mg_read(conn, buf, sizeof(buf));
11222 if (nread <= 0) {
11223 success = (nread == 0);
11224 break;
11225 }
11226 if (push_all(conn->phys_ctx, fp, sock, ssl, buf, nread) != nread) {
11227 break;
11228 }
11229 }
11230
11231 /* Each error code path in this function must send an error */
11232 if (!success) {
11233 /* NOTE: Maybe some data has already been sent. */
11234 /* TODO (low): If some data has been sent, a correct error
11235 * reply can no longer be sent, so just close the connection */
11236 mg_send_http_error(conn, 500, "%s", "");
11237 }
11238 }
11239
11240 return success;
11241}
11242#endif
11243
11244
11245#if defined(USE_TIMERS)
11246
11247#define TIMER_API static
11248#include "timer.inl"
11249
11250#endif /* USE_TIMERS */
11251
11252
11253#if !defined(NO_CGI)
11254/* This structure helps to create an environment for the spawned CGI
11255 * program.
11256 * Environment is an array of "VARIABLE=VALUE\0" ASCII strings,
11257 * last element must be NULL.
11258 * However, on Windows there is a requirement that all these
11259 * VARIABLE=VALUE\0
11260 * strings must reside in a contiguous buffer. The end of the buffer is
11261 * marked by two '\0' characters.
11262 * We satisfy both worlds: we create an envp array (which is vars), all
11263 * entries are actually pointers inside buf. */
11266 /* Data block */
11267 char *buf; /* Environment buffer */
11268 size_t buflen; /* Space available in buf */
11269 size_t bufused; /* Space taken in buf */
11270 /* Index block */
11271 char **var; /* char **envp */
11272 size_t varlen; /* Number of variables available in var */
11273 size_t varused; /* Number of variables stored in var */
11274};
11275
11276
11277static void addenv(struct cgi_environment *env,
11278 PRINTF_FORMAT_STRING(const char *fmt),
11279 ...) PRINTF_ARGS(2, 3);
11280
11281/* Append VARIABLE=VALUE\0 string to the buffer, and add a respective
11282 * pointer into the vars array. Assumes env != NULL and fmt != NULL. */
11283static void
11284addenv(struct cgi_environment *env, const char *fmt, ...)
11285{
11286 size_t i, n, space;
11287 int truncated = 0;
11288 char *added;
11289 va_list ap;
11290
11291 if ((env->varlen - env->varused) < 2) {
11292 mg_cry_internal(env->conn,
11293 "%s: Cannot register CGI variable [%s]",
11294 __func__,
11295 fmt);
11296 return;
11297 }
11298
11299 /* Calculate how much space is left in the buffer */
11300 space = (env->buflen - env->bufused);
11301
11302 do {
11303 /* Space for "\0\0" is always needed. */
11304 if (space <= 2) {
11305 /* Allocate new buffer */
11306 n = env->buflen + CGI_ENVIRONMENT_SIZE;
11307 added = (char *)mg_realloc_ctx(env->buf, n, env->conn->phys_ctx);
11308 if (!added) {
11309 /* Out of memory */
11311 env->conn,
11312 "%s: Cannot allocate memory for CGI variable [%s]",
11313 __func__,
11314 fmt);
11315 return;
11316 }
11317 /* Retarget pointers */
11318 env->buf = added;
11319 env->buflen = n;
11320 for (i = 0, n = 0; i < env->varused; i++) {
11321 env->var[i] = added + n;
11322 n += strlen(added + n) + 1;
11323 }
11324 space = (env->buflen - env->bufused);
11325 }
11326
11327 /* Make a pointer to the free space int the buffer */
11328 added = env->buf + env->bufused;
11329
11330 /* Copy VARIABLE=VALUE\0 string into the free space */
11331 va_start(ap, fmt);
11332 mg_vsnprintf(env->conn, &truncated, added, space - 1, fmt, ap);
11333 va_end(ap);
11334
11335 /* Do not add truncated strings to the environment */
11336 if (truncated) {
11337 /* Reallocate the buffer */
11338 space = 0;
11339 }
11340 } while (truncated);
11341
11342 /* Calculate number of bytes added to the environment */
11343 n = strlen(added) + 1;
11344 env->bufused += n;
11345
11346 /* Append a pointer to the added string into the envp array */
11347 env->var[env->varused] = added;
11348 env->varused++;
11349}
11350
11351/* Return 0 on success, non-zero if an error occurs. */
11352
11353static int
11355 const char *prog,
11356 struct cgi_environment *env,
11357 int cgi_config_idx)
11358{
11359 const char *s;
11360 struct vec var_vec;
11361 char *p, src_addr[IP_ADDR_STR_LEN], http_var_name[128];
11362 int i, truncated, uri_len;
11363
11364 if ((conn == NULL) || (prog == NULL) || (env == NULL)) {
11365 return -1;
11366 }
11367
11368 env->conn = conn;
11370 env->bufused = 0;
11371 env->buf = (char *)mg_malloc_ctx(env->buflen, conn->phys_ctx);
11372 if (env->buf == NULL) {
11373 mg_cry_internal(conn,
11374 "%s: Not enough memory for environmental buffer",
11375 __func__);
11376 return -1;
11377 }
11379 env->varused = 0;
11380 env->var =
11381 (char **)mg_malloc_ctx(env->varlen * sizeof(char *), conn->phys_ctx);
11382 if (env->var == NULL) {
11383 mg_cry_internal(conn,
11384 "%s: Not enough memory for environmental variables",
11385 __func__);
11386 mg_free(env->buf);
11387 return -1;
11388 }
11389
11390 addenv(env, "SERVER_NAME=%s", conn->dom_ctx->config[AUTHENTICATION_DOMAIN]);
11391 addenv(env, "SERVER_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
11392 addenv(env, "DOCUMENT_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
11393 addenv(env, "SERVER_SOFTWARE=CivetWeb/%s", mg_version());
11394
11395 /* Prepare the environment block */
11396 addenv(env, "%s", "GATEWAY_INTERFACE=CGI/1.1");
11397 addenv(env, "%s", "SERVER_PROTOCOL=HTTP/1.1");
11398 addenv(env, "%s", "REDIRECT_STATUS=200"); /* For PHP */
11399
11400 addenv(env, "SERVER_PORT=%d", conn->request_info.server_port);
11401
11402 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
11403 addenv(env, "REMOTE_ADDR=%s", src_addr);
11404
11405 addenv(env, "REQUEST_METHOD=%s", conn->request_info.request_method);
11406 addenv(env, "REMOTE_PORT=%d", conn->request_info.remote_port);
11407
11408 addenv(env, "REQUEST_URI=%s", conn->request_info.request_uri);
11409 addenv(env, "LOCAL_URI=%s", conn->request_info.local_uri);
11410 addenv(env, "LOCAL_URI_RAW=%s", conn->request_info.local_uri_raw);
11411
11412 /* SCRIPT_NAME */
11413 uri_len = (int)strlen(conn->request_info.local_uri);
11414 if (conn->path_info == NULL) {
11415 if (conn->request_info.local_uri[uri_len - 1] != '/') {
11416 /* URI: /path_to_script/script.cgi */
11417 addenv(env, "SCRIPT_NAME=%s", conn->request_info.local_uri);
11418 } else {
11419 /* URI: /path_to_script/ ... using index.cgi */
11420 const char *index_file = strrchr(prog, '/');
11421 if (index_file) {
11422 addenv(env,
11423 "SCRIPT_NAME=%s%s",
11424 conn->request_info.local_uri,
11425 index_file + 1);
11426 }
11427 }
11428 } else {
11429 /* URI: /path_to_script/script.cgi/path_info */
11430 addenv(env,
11431 "SCRIPT_NAME=%.*s",
11432 uri_len - (int)strlen(conn->path_info),
11433 conn->request_info.local_uri);
11434 }
11435
11436 addenv(env, "SCRIPT_FILENAME=%s", prog);
11437 if (conn->path_info == NULL) {
11438 addenv(env, "PATH_TRANSLATED=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
11439 } else {
11440 addenv(env,
11441 "PATH_TRANSLATED=%s%s",
11443 conn->path_info);
11444 }
11445
11446 addenv(env, "HTTPS=%s", (conn->ssl == NULL) ? "off" : "on");
11447
11448 if ((s = mg_get_header(conn, "Content-Type")) != NULL) {
11449 addenv(env, "CONTENT_TYPE=%s", s);
11450 }
11451 if (conn->request_info.query_string != NULL) {
11452 addenv(env, "QUERY_STRING=%s", conn->request_info.query_string);
11453 }
11454 if ((s = mg_get_header(conn, "Content-Length")) != NULL) {
11455 addenv(env, "CONTENT_LENGTH=%s", s);
11456 }
11457 if ((s = getenv("PATH")) != NULL) {
11458 addenv(env, "PATH=%s", s);
11459 }
11460 if (conn->path_info != NULL) {
11461 addenv(env, "PATH_INFO=%s", conn->path_info);
11462 }
11463
11464 if (conn->status_code > 0) {
11465 /* CGI error handler should show the status code */
11466 addenv(env, "STATUS=%d", conn->status_code);
11467 }
11468
11469#if defined(_WIN32)
11470 if ((s = getenv("COMSPEC")) != NULL) {
11471 addenv(env, "COMSPEC=%s", s);
11472 }
11473 if ((s = getenv("SYSTEMROOT")) != NULL) {
11474 addenv(env, "SYSTEMROOT=%s", s);
11475 }
11476 if ((s = getenv("SystemDrive")) != NULL) {
11477 addenv(env, "SystemDrive=%s", s);
11478 }
11479 if ((s = getenv("ProgramFiles")) != NULL) {
11480 addenv(env, "ProgramFiles=%s", s);
11481 }
11482 if ((s = getenv("ProgramFiles(x86)")) != NULL) {
11483 addenv(env, "ProgramFiles(x86)=%s", s);
11484 }
11485#else
11486 if ((s = getenv("LD_LIBRARY_PATH")) != NULL) {
11487 addenv(env, "LD_LIBRARY_PATH=%s", s);
11488 }
11489#endif /* _WIN32 */
11490
11491 if ((s = getenv("PERLLIB")) != NULL) {
11492 addenv(env, "PERLLIB=%s", s);
11493 }
11494
11495 if (conn->request_info.remote_user != NULL) {
11496 addenv(env, "REMOTE_USER=%s", conn->request_info.remote_user);
11497 addenv(env, "%s", "AUTH_TYPE=Digest");
11498 }
11499
11500 /* Add all headers as HTTP_* variables */
11501 for (i = 0; i < conn->request_info.num_headers; i++) {
11502
11503 (void)mg_snprintf(conn,
11504 &truncated,
11505 http_var_name,
11506 sizeof(http_var_name),
11507 "HTTP_%s",
11508 conn->request_info.http_headers[i].name);
11509
11510 if (truncated) {
11511 mg_cry_internal(conn,
11512 "%s: HTTP header variable too long [%s]",
11513 __func__,
11514 conn->request_info.http_headers[i].name);
11515 continue;
11516 }
11517
11518 /* Convert variable name into uppercase, and change - to _ */
11519 for (p = http_var_name; *p != '\0'; p++) {
11520 if (*p == '-') {
11521 *p = '_';
11522 }
11523 *p = (char)toupper((unsigned char)*p);
11524 }
11525
11526 addenv(env,
11527 "%s=%s",
11528 http_var_name,
11530 }
11531
11532 /* Add user-specified variables */
11533 s = conn->dom_ctx->config[CGI_ENVIRONMENT + cgi_config_idx];
11534 while ((s = next_option(s, &var_vec, NULL)) != NULL) {
11535 addenv(env, "%.*s", (int)var_vec.len, var_vec.ptr);
11536 }
11537
11538 env->var[env->varused] = NULL;
11539 env->buf[env->bufused] = '\0';
11540
11541 return 0;
11542}
11543
11544
11545/* Data for CGI process control: PID and number of references */
11547 pid_t pid;
11548 ptrdiff_t references;
11549};
11550
11551static int
11553{
11554 /* Waitpid checks for child status and won't work for a pid that does
11555 * not identify a child of the current process. Thus, if the pid is
11556 * reused, we will not affect a different process. */
11557 struct process_control_data *proc = (struct process_control_data *)data;
11558 int status = 0;
11559 ptrdiff_t refs;
11560 pid_t ret_pid;
11561
11562 ret_pid = waitpid(proc->pid, &status, WNOHANG);
11563 if ((ret_pid != (pid_t)-1) && (status == 0)) {
11564 /* Stop child process */
11565 DEBUG_TRACE("CGI timer: Stop child process %d\n", proc->pid);
11566 kill(proc->pid, SIGABRT);
11567
11568 /* Wait until process is terminated (don't leave zombies) */
11569 while (waitpid(proc->pid, &status, 0) != (pid_t)-1) /* nop */
11570 ;
11571 } else {
11572 DEBUG_TRACE("CGI timer: Child process %d already stopped\n", proc->pid);
11573 }
11574 /* Dec reference counter */
11575 refs = mg_atomic_dec(&proc->references);
11576 if (refs == 0) {
11577 /* no more references - free data */
11578 mg_free(data);
11579 }
11580
11581 return 0;
11582}
11583
11584
11585/* Local (static) function assumes all arguments are valid. */
11586static void
11588 const char *prog,
11589 int cgi_config_idx)
11590{
11591 char *buf;
11592 size_t buflen;
11593 int headers_len, data_len, i, truncated;
11594 int fdin[2] = {-1, -1}, fdout[2] = {-1, -1}, fderr[2] = {-1, -1};
11595 const char *status, *status_text;
11596 char *pbuf, dir[UTF8_PATH_MAX], *p;
11597 struct mg_request_info ri;
11598 struct cgi_environment blk;
11599 FILE *in = NULL, *out = NULL, *err = NULL;
11600 struct mg_file fout = STRUCT_FILE_INITIALIZER;
11601 pid_t pid = (pid_t)-1;
11602 struct process_control_data *proc = NULL;
11603 char *cfg_buffering = conn->dom_ctx->config[CGI_BUFFERING + cgi_config_idx];
11604 int no_buffering = 0;
11605
11606#if defined(USE_TIMERS)
11607 double cgi_timeout;
11608 if (conn->dom_ctx->config[CGI_TIMEOUT + cgi_config_idx]) {
11609 /* Get timeout in seconds */
11610 cgi_timeout =
11611 atof(conn->dom_ctx->config[CGI_TIMEOUT + cgi_config_idx]) * 0.001;
11612 } else {
11613 cgi_timeout =
11614 atof(config_options[REQUEST_TIMEOUT].default_value) * 0.001;
11615 }
11616#endif
11617 if (cfg_buffering != NULL) {
11618 if (!mg_strcasecmp(cfg_buffering, "no")) {
11619 no_buffering = 1;
11620 }
11621 }
11622
11623 buf = NULL;
11624 buflen = conn->phys_ctx->max_request_size;
11625 i = prepare_cgi_environment(conn, prog, &blk, cgi_config_idx);
11626 if (i != 0) {
11627 blk.buf = NULL;
11628 blk.var = NULL;
11629 goto done;
11630 }
11631
11632 /* CGI must be executed in its own directory. 'dir' must point to the
11633 * directory containing executable program, 'p' must point to the
11634 * executable program name relative to 'dir'. */
11635 (void)mg_snprintf(conn, &truncated, dir, sizeof(dir), "%s", prog);
11636
11637 if (truncated) {
11638 mg_cry_internal(conn, "Error: CGI program \"%s\": Path too long", prog);
11639 mg_send_http_error(conn, 500, "Error: %s", "CGI path too long");
11640 goto done;
11641 }
11642
11643 if ((p = strrchr(dir, '/')) != NULL) {
11644 *p++ = '\0';
11645 } else {
11646 dir[0] = '.';
11647 dir[1] = '\0';
11648 p = (char *)prog;
11649 }
11650
11651 if ((pipe(fdin) != 0) || (pipe(fdout) != 0) || (pipe(fderr) != 0)) {
11652 status = strerror(ERRNO);
11654 conn,
11655 "Error: CGI program \"%s\": Can not create CGI pipes: %s",
11656 prog,
11657 status);
11658 mg_send_http_error(conn,
11659 500,
11660 "Error: Cannot create CGI pipe: %s",
11661 status);
11662 goto done;
11663 }
11664
11665 proc = (struct process_control_data *)
11666 mg_malloc_ctx(sizeof(struct process_control_data), conn->phys_ctx);
11667 if (proc == NULL) {
11668 mg_cry_internal(conn, "Error: CGI program \"%s\": Out or memory", prog);
11669 mg_send_http_error(conn, 500, "Error: Out of memory [%s]", prog);
11670 goto done;
11671 }
11672
11673 DEBUG_TRACE("CGI: spawn %s %s\n", dir, p);
11675 conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir, cgi_config_idx);
11676
11677 if (pid == (pid_t)-1) {
11678 status = strerror(ERRNO);
11680 conn,
11681 "Error: CGI program \"%s\": Can not spawn CGI process: %s",
11682 prog,
11683 status);
11684 mg_send_http_error(conn, 500, "Error: Cannot spawn CGI process");
11685 mg_free(proc);
11686 proc = NULL;
11687 goto done;
11688 }
11689
11690 /* Store data in shared process_control_data */
11691 proc->pid = pid;
11692 proc->references = 1;
11693
11694#if defined(USE_TIMERS)
11695 if (cgi_timeout > 0.0) {
11696 proc->references = 2;
11697
11698 // Start a timer for CGI
11699 timer_add(conn->phys_ctx,
11700 cgi_timeout /* in seconds */,
11701 0.0,
11702 1,
11704 (void *)proc,
11705 NULL);
11706 }
11707#endif
11708
11709 /* Parent closes only one side of the pipes.
11710 * If we don't mark them as closed, close() attempt before
11711 * return from this function throws an exception on Windows.
11712 * Windows does not like when closed descriptor is closed again. */
11713 (void)close(fdin[0]);
11714 (void)close(fdout[1]);
11715 (void)close(fderr[1]);
11716 fdin[0] = fdout[1] = fderr[1] = -1;
11717
11718 if (((in = fdopen(fdin[1], "wb")) == NULL)
11719 || ((out = fdopen(fdout[0], "rb")) == NULL)
11720 || ((err = fdopen(fderr[0], "rb")) == NULL)) {
11721 status = strerror(ERRNO);
11722 mg_cry_internal(conn,
11723 "Error: CGI program \"%s\": Can not open fd: %s",
11724 prog,
11725 status);
11726 mg_send_http_error(conn,
11727 500,
11728 "Error: CGI can not open fd\nfdopen: %s",
11729 status);
11730 goto done;
11731 }
11732
11733 setbuf(in, NULL);
11734 setbuf(out, NULL);
11735 setbuf(err, NULL);
11736 fout.access.fp = out;
11737
11738 if ((conn->content_len != 0) || (conn->is_chunked)) {
11739 DEBUG_TRACE("CGI: send body data (%" INT64_FMT ")\n",
11740 conn->content_len);
11741
11742 /* This is a POST/PUT request, or another request with body data. */
11743 if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
11744 /* Error sending the body data */
11746 conn,
11747 "Error: CGI program \"%s\": Forward body data failed",
11748 prog);
11749 goto done;
11750 }
11751 }
11752
11753 /* Close so child gets an EOF. */
11754 fclose(in);
11755 in = NULL;
11756 fdin[1] = -1;
11757
11758 /* Now read CGI reply into a buffer. We need to set correct
11759 * status code, thus we need to see all HTTP headers first.
11760 * Do not send anything back to client, until we buffer in all
11761 * HTTP headers. */
11762 data_len = 0;
11763 buf = (char *)mg_malloc_ctx(buflen, conn->phys_ctx);
11764 if (buf == NULL) {
11765 mg_send_http_error(conn,
11766 500,
11767 "Error: Not enough memory for CGI buffer (%u bytes)",
11768 (unsigned int)buflen);
11770 conn,
11771 "Error: CGI program \"%s\": Not enough memory for buffer (%u "
11772 "bytes)",
11773 prog,
11774 (unsigned int)buflen);
11775 goto done;
11776 }
11777
11778 DEBUG_TRACE("CGI: %s", "wait for response");
11779 headers_len = read_message(out, conn, buf, (int)buflen, &data_len);
11780 DEBUG_TRACE("CGI: response: %li", (signed long)headers_len);
11781
11782 if (headers_len <= 0) {
11783
11784 /* Could not parse the CGI response. Check if some error message on
11785 * stderr. */
11786 i = pull_all(err, conn, buf, (int)buflen);
11787 if (i > 0) {
11788 /* CGI program explicitly sent an error */
11789 /* Write the error message to the internal log */
11790 mg_cry_internal(conn,
11791 "Error: CGI program \"%s\" sent error "
11792 "message: [%.*s]",
11793 prog,
11794 i,
11795 buf);
11796 /* Don't send the error message back to the client */
11797 mg_send_http_error(conn,
11798 500,
11799 "Error: CGI program \"%s\" failed.",
11800 prog);
11801 } else {
11802 /* CGI program did not explicitly send an error, but a broken
11803 * respon header */
11804 mg_cry_internal(conn,
11805 "Error: CGI program sent malformed or too big "
11806 "(>%u bytes) HTTP headers: [%.*s]",
11807 (unsigned)buflen,
11808 data_len,
11809 buf);
11810
11811 mg_send_http_error(conn,
11812 500,
11813 "Error: CGI program sent malformed or too big "
11814 "(>%u bytes) HTTP headers: [%.*s]",
11815 (unsigned)buflen,
11816 data_len,
11817 buf);
11818 }
11819
11820 /* in both cases, abort processing CGI */
11821 goto done;
11822 }
11823
11824 pbuf = buf;
11825 buf[headers_len - 1] = '\0';
11827
11828 /* Make up and send the status line */
11829 status_text = "OK";
11830 if ((status = get_header(ri.http_headers, ri.num_headers, "Status"))
11831 != NULL) {
11832 conn->status_code = atoi(status);
11833 status_text = status;
11834 while (isdigit((unsigned char)*status_text) || *status_text == ' ') {
11835 status_text++;
11836 }
11837 } else if (get_header(ri.http_headers, ri.num_headers, "Location")
11838 != NULL) {
11839 conn->status_code = 307;
11840 } else {
11841 conn->status_code = 200;
11842 }
11843
11844 if (!should_keep_alive(conn)) {
11845 conn->must_close = 1;
11846 }
11847
11848 DEBUG_TRACE("CGI: response %u %s", conn->status_code, status_text);
11849
11850 (void)mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, status_text);
11851
11852 /* Send headers */
11853 for (i = 0; i < ri.num_headers; i++) {
11854 DEBUG_TRACE("CGI header: %s: %s",
11855 ri.http_headers[i].name,
11856 ri.http_headers[i].value);
11857 mg_printf(conn,
11858 "%s: %s\r\n",
11859 ri.http_headers[i].name,
11860 ri.http_headers[i].value);
11861 }
11862 mg_write(conn, "\r\n", 2);
11863
11864 /* Send chunk of data that may have been read after the headers */
11865 mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len));
11866
11867 /* Read the rest of CGI output and send to the client */
11868 DEBUG_TRACE("CGI: %s", "forward all data");
11869 send_file_data(conn, &fout, 0, INT64_MAX, no_buffering); /* send CGI data */
11870 DEBUG_TRACE("CGI: %s", "all data sent");
11871
11872done:
11873 mg_free(blk.var);
11874 mg_free(blk.buf);
11875
11876 if (pid != (pid_t)-1) {
11877 abort_cgi_process((void *)proc);
11878 }
11879
11880 if (fdin[0] != -1) {
11881 close(fdin[0]);
11882 }
11883 if (fdout[1] != -1) {
11884 close(fdout[1]);
11885 }
11886 if (fderr[1] != -1) {
11887 close(fderr[1]);
11888 }
11889
11890 if (in != NULL) {
11891 fclose(in);
11892 } else if (fdin[1] != -1) {
11893 close(fdin[1]);
11894 }
11895
11896 if (out != NULL) {
11897 fclose(out);
11898 } else if (fdout[0] != -1) {
11899 close(fdout[0]);
11900 }
11901
11902 if (err != NULL) {
11903 fclose(err);
11904 } else if (fderr[0] != -1) {
11905 close(fderr[0]);
11906 }
11907
11908 mg_free(buf);
11909}
11910#endif /* !NO_CGI */
11911
11912
11913#if !defined(NO_FILES)
11914static void
11915dav_mkcol(struct mg_connection *conn, const char *path)
11916{
11917 int rc, body_len;
11918 struct de de;
11919
11920 if (conn == NULL) {
11921 return;
11922 }
11923
11924 /* TODO (mid): Check the mg_send_http_error situations in this function
11925 */
11926
11927 memset(&de.file, 0, sizeof(de.file));
11928 if (!mg_stat(conn, path, &de.file)) {
11929 mg_cry_internal(conn,
11930 "%s: mg_stat(%s) failed: %s",
11931 __func__,
11932 path,
11933 strerror(ERRNO));
11934 }
11935
11936 if (de.file.last_modified) {
11937 /* TODO (mid): This check does not seem to make any sense ! */
11938 /* TODO (mid): Add a webdav unit test first, before changing
11939 * anything here. */
11941 conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO));
11942 return;
11943 }
11944
11945 body_len = conn->data_len - conn->request_len;
11946 if (body_len > 0) {
11948 conn, 415, "Error: mkcol(%s): %s", path, strerror(ERRNO));
11949 return;
11950 }
11951
11952 rc = mg_mkdir(conn, path, 0755);
11953 DEBUG_TRACE("mkdir %s: %i", path, rc);
11954 if (rc == 0) {
11955 /* Create 201 "Created" response */
11956 mg_response_header_start(conn, 201);
11959 mg_response_header_add(conn, "Content-Length", "0", -1);
11960
11961 /* Send all headers - there is no body */
11963 } else {
11964 int http_status = 500;
11965 switch (errno) {
11966 case EEXIST:
11967 http_status = 405;
11968 break;
11969 case EACCES:
11970 http_status = 403;
11971 break;
11972 case ENOENT:
11973 http_status = 409;
11974 break;
11975 }
11976
11977 mg_send_http_error(conn,
11978 http_status,
11979 "Error processing %s: %s",
11980 path,
11981 strerror(ERRNO));
11982 }
11983}
11984
11985
11986/* Forward decrlaration */
11987static int get_uri_type(const char *uri);
11988static const char *
11989get_rel_url_at_current_server(const char *uri,
11990 const struct mg_connection *conn);
11991
11992
11993static void
11994dav_move_file(struct mg_connection *conn, const char *path, int do_copy)
11995{
11996 const char *overwrite_hdr;
11997 const char *destination_hdr;
11998 const char *root;
11999 int rc, dest_uri_type;
12000 int http_status = 400;
12001 int do_overwrite = 0;
12002 int destination_ok = 0;
12003 char dest_path[UTF8_PATH_MAX];
12004 struct mg_file_stat ignored;
12005
12006 if (conn == NULL) {
12007 return;
12008 }
12009
12010 root = conn->dom_ctx->config[DOCUMENT_ROOT];
12011 overwrite_hdr = mg_get_header(conn, "Overwrite");
12012 destination_hdr = mg_get_header(conn, "Destination");
12013 if ((overwrite_hdr != NULL) && (toupper(overwrite_hdr[0]) == 'T')) {
12014 do_overwrite = 1;
12015 }
12016
12017 if ((destination_hdr == NULL) || (destination_hdr[0] == 0)) {
12018 mg_send_http_error(conn, 400, "%s", "Missing destination");
12019 return;
12020 }
12021
12022 if (root != NULL) {
12023 char *local_dest = NULL;
12024 dest_uri_type = get_uri_type(destination_hdr);
12025 if (dest_uri_type == 2) {
12026 local_dest = mg_strdup_ctx(destination_hdr, conn->phys_ctx);
12027 } else if ((dest_uri_type == 3) || (dest_uri_type == 4)) {
12028 const char *h =
12029 get_rel_url_at_current_server(destination_hdr, conn);
12030 if (h) {
12031 size_t len = strlen(h);
12032 local_dest = mg_malloc_ctx(len + 1, conn->phys_ctx);
12033 mg_url_decode(h, (int)len, local_dest, (int)len + 1, 0);
12034 }
12035 }
12036 if (local_dest != NULL) {
12037 remove_dot_segments(local_dest);
12038 if (local_dest[0] == '/') {
12039 int trunc_check = 0;
12040 mg_snprintf(conn,
12041 &trunc_check,
12042 dest_path,
12043 sizeof(dest_path),
12044 "%s/%s",
12045 root,
12046 local_dest);
12047 if (trunc_check == 0) {
12048 destination_ok = 1;
12049 }
12050 }
12051 mg_free(local_dest);
12052 }
12053 }
12054
12055 if (!destination_ok) {
12056 mg_send_http_error(conn, 502, "%s", "Illegal destination");
12057 return;
12058 }
12059
12060 /* Check now if this file exists */
12061 if (mg_stat(conn, dest_path, &ignored)) {
12062 /* File exists */
12063 if (do_overwrite) {
12064 /* Overwrite allowed: delete the file first */
12065 if (0 != remove(dest_path)) {
12066 /* No overwrite: return error */
12067 mg_send_http_error(conn,
12068 403,
12069 "Cannot overwrite file: %s",
12070 dest_path);
12071 return;
12072 }
12073 } else {
12074 /* No overwrite: return error */
12075 mg_send_http_error(conn,
12076 412,
12077 "Destination already exists: %s",
12078 dest_path);
12079 return;
12080 }
12081 }
12082
12083 /* Copy / Move / Rename operation. */
12084 DEBUG_TRACE("%s %s to %s", (do_copy ? "copy" : "move"), path, dest_path);
12085#if defined(_WIN32)
12086 {
12087 /* For Windows, we need to convert from UTF-8 to UTF-16 first. */
12088 wchar_t wSource[UTF16_PATH_MAX];
12089 wchar_t wDest[UTF16_PATH_MAX];
12090 BOOL ok;
12091
12092 path_to_unicode(conn, path, wSource, ARRAY_SIZE(wSource));
12093 path_to_unicode(conn, dest_path, wDest, ARRAY_SIZE(wDest));
12094 if (do_copy) {
12095 ok = CopyFileW(wSource, wDest, do_overwrite ? FALSE : TRUE);
12096 } else {
12097 ok = MoveFileExW(wSource,
12098 wDest,
12099 do_overwrite ? MOVEFILE_REPLACE_EXISTING : 0);
12100 }
12101 if (ok) {
12102 rc = 0;
12103 } else {
12104 DWORD lastErr = GetLastError();
12105 if (lastErr == ERROR_ALREADY_EXISTS) {
12106 mg_send_http_error(conn,
12107 412,
12108 "Destination already exists: %s",
12109 dest_path);
12110 return;
12111 }
12112 rc = -1;
12113 http_status = 400;
12114 }
12115 }
12116
12117#else
12118 {
12119 /* Linux uses already UTF-8, we don't need to convert file names. */
12120
12121 if (do_copy) {
12122 /* TODO: COPY for Linux. */
12123 mg_send_http_error(conn, 403, "%s", "COPY forbidden");
12124 return;
12125 }
12126
12127 rc = rename(path, dest_path);
12128 if (rc) {
12129 switch (errno) {
12130 case EEXIST:
12131 http_status = 412;
12132 break;
12133 case EACCES:
12134 http_status = 403;
12135 break;
12136 case ENOENT:
12137 http_status = 409;
12138 break;
12139 }
12140 }
12141 }
12142#endif
12143
12144 if (rc == 0) {
12145 /* Create 204 "No Content" response */
12146 mg_response_header_start(conn, 204);
12147 mg_response_header_add(conn, "Content-Length", "0", -1);
12148
12149 /* Send all headers - there is no body */
12151 } else {
12152 mg_send_http_error(conn, http_status, "Operation failed");
12153 }
12154}
12155
12156
12157static void
12158put_file(struct mg_connection *conn, const char *path)
12159{
12160 struct mg_file file = STRUCT_FILE_INITIALIZER;
12161 const char *range;
12162 int64_t r1, r2;
12163 int rc;
12164
12165 if (conn == NULL) {
12166 return;
12167 }
12168
12169 DEBUG_TRACE("store %s", path);
12170
12171 if (mg_stat(conn, path, &file.stat)) {
12172 /* File already exists */
12173 conn->status_code = 200;
12174
12175 if (file.stat.is_directory) {
12176 /* This is an already existing directory,
12177 * so there is nothing to do for the server. */
12178 rc = 0;
12179
12180 } else {
12181 /* File exists and is not a directory. */
12182 /* Can it be replaced? */
12183
12184 /* Check if the server may write this file */
12185 if (access(path, W_OK) == 0) {
12186 /* Access granted */
12187 rc = 1;
12188 } else {
12190 conn,
12191 403,
12192 "Error: Put not possible\nReplacing %s is not allowed",
12193 path);
12194 return;
12195 }
12196 }
12197 } else {
12198 /* File should be created */
12199 conn->status_code = 201;
12200 rc = put_dir(conn, path);
12201 }
12202
12203 if (rc == 0) {
12204 /* put_dir returns 0 if path is a directory */
12205
12206 /* Create response */
12210 mg_response_header_add(conn, "Content-Length", "0", -1);
12211
12212 /* Send all headers - there is no body */
12214
12215 /* Request to create a directory has been fulfilled successfully.
12216 * No need to put a file. */
12217 return;
12218 }
12219
12220 if (rc == -1) {
12221 /* put_dir returns -1 if the path is too long */
12222 mg_send_http_error(conn,
12223 414,
12224 "Error: Path too long\nput_dir(%s): %s",
12225 path,
12226 strerror(ERRNO));
12227 return;
12228 }
12229
12230 if (rc == -2) {
12231 /* put_dir returns -2 if the directory can not be created */
12232 mg_send_http_error(conn,
12233 500,
12234 "Error: Can not create directory\nput_dir(%s): %s",
12235 path,
12236 strerror(ERRNO));
12237 return;
12238 }
12239
12240 /* A file should be created or overwritten. */
12241 /* Currently CivetWeb does not need read+write access. */
12242 if (!mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &file)
12243 || file.access.fp == NULL) {
12244 (void)mg_fclose(&file.access);
12245 mg_send_http_error(conn,
12246 500,
12247 "Error: Can not create file\nfopen(%s): %s",
12248 path,
12249 strerror(ERRNO));
12250 return;
12251 }
12252
12253 fclose_on_exec(&file.access, conn);
12254 range = mg_get_header(conn, "Content-Range");
12255 r1 = r2 = 0;
12256 if ((range != NULL) && parse_range_header(range, &r1, &r2) > 0) {
12257 conn->status_code = 206; /* Partial content */
12258 if (0 != fseeko(file.access.fp, r1, SEEK_SET)) {
12259 mg_send_http_error(conn,
12260 500,
12261 "Error: Internal error processing file %s",
12262 path);
12263 return;
12264 }
12265 }
12266
12267 if (!forward_body_data(conn, file.access.fp, INVALID_SOCKET, NULL)) {
12268 /* forward_body_data failed.
12269 * The error code has already been sent to the client,
12270 * and conn->status_code is already set. */
12271 (void)mg_fclose(&file.access);
12272 return;
12273 }
12274
12275 if (mg_fclose(&file.access) != 0) {
12276 /* fclose failed. This might have different reasons, but a likely
12277 * one is "no space on disk", http 507. */
12278 conn->status_code = 507;
12279 }
12280
12281 /* Create response (status_code has been set before) */
12285 mg_response_header_add(conn, "Content-Length", "0", -1);
12286
12287 /* Send all headers - there is no body */
12289}
12290
12291
12292static void
12293delete_file(struct mg_connection *conn, const char *path)
12294{
12295 struct de de;
12296 memset(&de.file, 0, sizeof(de.file));
12297 if (!mg_stat(conn, path, &de.file)) {
12298 /* mg_stat returns 0 if the file does not exist */
12299 mg_send_http_error(conn,
12300 404,
12301 "Error: Cannot delete file\nFile %s not found",
12302 path);
12303 return;
12304 }
12305
12306 DEBUG_TRACE("delete %s", path);
12307
12308 if (de.file.is_directory) {
12309 if (remove_directory(conn, path)) {
12310 /* Delete is successful: Return 204 without content. */
12311 mg_send_http_error(conn, 204, "%s", "");
12312 } else {
12313 /* Delete is not successful: Return 500 (Server error). */
12314 mg_send_http_error(conn, 500, "Error: Could not delete %s", path);
12315 }
12316 return;
12317 }
12318
12319 /* This is an existing file (not a directory).
12320 * Check if write permission is granted. */
12321 if (access(path, W_OK) != 0) {
12322 /* File is read only */
12324 conn,
12325 403,
12326 "Error: Delete not possible\nDeleting %s is not allowed",
12327 path);
12328 return;
12329 }
12330
12331 /* Try to delete it. */
12332 if (mg_remove(conn, path) == 0) {
12333 /* Delete was successful: Return 204 without content. */
12334 mg_response_header_start(conn, 204);
12337 mg_response_header_add(conn, "Content-Length", "0", -1);
12339
12340 } else {
12341 /* Delete not successful (file locked). */
12342 mg_send_http_error(conn,
12343 423,
12344 "Error: Cannot delete file\nremove(%s): %s",
12345 path,
12346 strerror(ERRNO));
12347 }
12348}
12349#endif /* !NO_FILES */
12350
12351
12352#if !defined(NO_FILESYSTEMS)
12353static void
12354send_ssi_file(struct mg_connection *, const char *, struct mg_file *, int);
12355
12356
12357static void
12359 const char *ssi,
12360 char *tag,
12361 int include_level)
12362{
12363 char file_name[MG_BUF_LEN], path[512], *p;
12364 struct mg_file file = STRUCT_FILE_INITIALIZER;
12365 size_t len;
12366 int truncated = 0;
12367
12368 if (conn == NULL) {
12369 return;
12370 }
12371
12372 /* sscanf() is safe here, since send_ssi_file() also uses buffer
12373 * of size MG_BUF_LEN to get the tag. So strlen(tag) is
12374 * always < MG_BUF_LEN. */
12375 if (sscanf(tag, " virtual=\"%511[^\"]\"", file_name) == 1) {
12376 /* File name is relative to the webserver root */
12377 file_name[511] = 0;
12378 (void)mg_snprintf(conn,
12379 &truncated,
12380 path,
12381 sizeof(path),
12382 "%s/%s",
12384 file_name);
12385
12386 } else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) {
12387 /* File name is relative to the webserver working directory
12388 * or it is absolute system path */
12389 file_name[511] = 0;
12390 (void)
12391 mg_snprintf(conn, &truncated, path, sizeof(path), "%s", file_name);
12392
12393 } else if ((sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1)
12394 || (sscanf(tag, " \"%511[^\"]\"", file_name) == 1)) {
12395 /* File name is relative to the current document */
12396 file_name[511] = 0;
12397 (void)mg_snprintf(conn, &truncated, path, sizeof(path), "%s", ssi);
12398
12399 if (!truncated) {
12400 if ((p = strrchr(path, '/')) != NULL) {
12401 p[1] = '\0';
12402 }
12403 len = strlen(path);
12404 (void)mg_snprintf(conn,
12405 &truncated,
12406 path + len,
12407 sizeof(path) - len,
12408 "%s",
12409 file_name);
12410 }
12411
12412 } else {
12413 mg_cry_internal(conn, "Bad SSI #include: [%s]", tag);
12414 return;
12415 }
12416
12417 if (truncated) {
12418 mg_cry_internal(conn, "SSI #include path length overflow: [%s]", tag);
12419 return;
12420 }
12421
12422 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, &file)) {
12423 mg_cry_internal(conn,
12424 "Cannot open SSI #include: [%s]: fopen(%s): %s",
12425 tag,
12426 path,
12427 strerror(ERRNO));
12428 } else {
12429 fclose_on_exec(&file.access, conn);
12430 if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path)
12431 > 0) {
12432 send_ssi_file(conn, path, &file, include_level + 1);
12433 } else {
12434 send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
12435 }
12436 (void)mg_fclose(&file.access); /* Ignore errors for readonly files */
12437 }
12438}
12439
12440
12441#if !defined(NO_POPEN)
12442static void
12443do_ssi_exec(struct mg_connection *conn, char *tag)
12444{
12445 char cmd[1024] = "";
12446 struct mg_file file = STRUCT_FILE_INITIALIZER;
12447
12448 if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) {
12449 mg_cry_internal(conn, "Bad SSI #exec: [%s]", tag);
12450 } else {
12451 cmd[1023] = 0;
12452 if ((file.access.fp = popen(cmd, "r")) == NULL) {
12453 mg_cry_internal(conn,
12454 "Cannot SSI #exec: [%s]: %s",
12455 cmd,
12456 strerror(ERRNO));
12457 } else {
12458 send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
12459 pclose(file.access.fp);
12460 }
12461 }
12462}
12463#endif /* !NO_POPEN */
12464
12465
12466static int
12467mg_fgetc(struct mg_file *filep)
12468{
12469 if (filep == NULL) {
12470 return EOF;
12471 }
12472
12473 if (filep->access.fp != NULL) {
12474 return fgetc(filep->access.fp);
12475 } else {
12476 return EOF;
12477 }
12478}
12479
12480
12481static void
12483 const char *path,
12484 struct mg_file *filep,
12485 int include_level)
12486{
12487 char buf[MG_BUF_LEN];
12488 int ch, len, in_tag, in_ssi_tag;
12489
12490 if (include_level > 10) {
12491 mg_cry_internal(conn, "SSI #include level is too deep (%s)", path);
12492 return;
12493 }
12494
12495 in_tag = in_ssi_tag = len = 0;
12496
12497 /* Read file, byte by byte, and look for SSI include tags */
12498 while ((ch = mg_fgetc(filep)) != EOF) {
12499
12500 if (in_tag) {
12501 /* We are in a tag, either SSI tag or html tag */
12502
12503 if (ch == '>') {
12504 /* Tag is closing */
12505 buf[len++] = '>';
12506
12507 if (in_ssi_tag) {
12508 /* Handle SSI tag */
12509 buf[len] = 0;
12510
12511 if ((len > 12) && !memcmp(buf + 5, "include", 7)) {
12512 do_ssi_include(conn, path, buf + 12, include_level + 1);
12513#if !defined(NO_POPEN)
12514 } else if ((len > 9) && !memcmp(buf + 5, "exec", 4)) {
12515 do_ssi_exec(conn, buf + 9);
12516#endif /* !NO_POPEN */
12517 } else {
12518 mg_cry_internal(conn,
12519 "%s: unknown SSI "
12520 "command: \"%s\"",
12521 path,
12522 buf);
12523 }
12524 len = 0;
12525 in_ssi_tag = in_tag = 0;
12526
12527 } else {
12528 /* Not an SSI tag */
12529 /* Flush buffer */
12530 (void)mg_write(conn, buf, (size_t)len);
12531 len = 0;
12532 in_tag = 0;
12533 }
12534
12535 } else {
12536 /* Tag is still open */
12537 buf[len++] = (char)(ch & 0xff);
12538
12539 if ((len == 5) && !memcmp(buf, "<!--#", 5)) {
12540 /* All SSI tags start with <!--# */
12541 in_ssi_tag = 1;
12542 }
12543
12544 if ((len + 2) > (int)sizeof(buf)) {
12545 /* Tag to long for buffer */
12546 mg_cry_internal(conn, "%s: tag is too large", path);
12547 return;
12548 }
12549 }
12550
12551 } else {
12552
12553 /* We are not in a tag yet. */
12554 if (ch == '<') {
12555 /* Tag is opening */
12556 in_tag = 1;
12557
12558 if (len > 0) {
12559 /* Flush current buffer.
12560 * Buffer is filled with "len" bytes. */
12561 (void)mg_write(conn, buf, (size_t)len);
12562 }
12563 /* Store the < */
12564 len = 1;
12565 buf[0] = '<';
12566
12567 } else {
12568 /* No Tag */
12569 /* Add data to buffer */
12570 buf[len++] = (char)(ch & 0xff);
12571 /* Flush if buffer is full */
12572 if (len == (int)sizeof(buf)) {
12573 mg_write(conn, buf, (size_t)len);
12574 len = 0;
12575 }
12576 }
12577 }
12578 }
12579
12580 /* Send the rest of buffered data */
12581 if (len > 0) {
12582 mg_write(conn, buf, (size_t)len);
12583 }
12584}
12585
12586
12587static void
12589 const char *path,
12590 struct mg_file *filep)
12591{
12592 char date[64];
12593 time_t curtime = time(NULL);
12594
12595 if ((conn == NULL) || (path == NULL) || (filep == NULL)) {
12596 return;
12597 }
12598
12599 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) {
12600 /* File exists (precondition for calling this function),
12601 * but can not be opened by the server. */
12602 mg_send_http_error(conn,
12603 500,
12604 "Error: Cannot read file\nfopen(%s): %s",
12605 path,
12606 strerror(ERRNO));
12607 } else {
12608 /* Set "must_close" for HTTP/1.x, since we do not know the
12609 * content length */
12610 conn->must_close = 1;
12611 gmt_time_string(date, sizeof(date), &curtime);
12612 fclose_on_exec(&filep->access, conn);
12613
12614 /* 200 OK response */
12615 mg_response_header_start(conn, 200);
12618 send_cors_header(conn);
12619 mg_response_header_add(conn, "Content-Type", "text/html", -1);
12621
12622 /* Header sent, now send body */
12623 send_ssi_file(conn, path, filep, 0);
12624 (void)mg_fclose(&filep->access); /* Ignore errors for readonly files */
12625 }
12626}
12627#endif /* NO_FILESYSTEMS */
12628
12629
12630#if !defined(NO_FILES)
12631static void
12633{
12634 if (!conn || !all_methods) {
12635 return;
12636 }
12637
12638 /* We do not set a "Cache-Control" header here, but leave the default.
12639 * Since browsers do not send an OPTIONS request, we can not test the
12640 * effect anyway. */
12641
12642 mg_response_header_start(conn, 200);
12643 mg_response_header_add(conn, "Content-Type", "text/html", -1);
12644
12645 if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
12646 /* Use the same as before */
12647 mg_response_header_add(conn, "Allow", all_methods, -1);
12648 mg_response_header_add(conn, "DAV", "1", -1);
12649 } else {
12650 /* TODO: Check this later for HTTP/2 */
12651 mg_response_header_add(conn, "Allow", "GET, POST", -1);
12652 }
12655}
12656
12657
12658/* Writes PROPFIND properties for a collection element */
12659static int
12661 const char *uri,
12662 const char *name,
12663 struct mg_file_stat *filep)
12664{
12665 size_t i;
12666 char mtime[64];
12667 char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
12668 char *link_concat;
12669 size_t link_concat_len;
12670
12671 if ((conn == NULL) || (uri == NULL) || (name == NULL) || (filep == NULL)) {
12672 return 0;
12673 }
12674
12675 link_concat_len = strlen(uri) + strlen(name) + 1;
12676 link_concat = mg_malloc_ctx(link_concat_len, conn->phys_ctx);
12677 if (!link_concat) {
12678 return 0;
12679 }
12680 strcpy(link_concat, uri);
12681 strcat(link_concat, name);
12682
12683 /* Get full link used in request */
12685 conn, link_buf, sizeof(link_buf), NULL, 0, link_concat);
12686
12687 /*
12688 OutputDebugStringA("print_props:\n uri: ");
12689 OutputDebugStringA(uri);
12690 OutputDebugStringA("\n name: ");
12691 OutputDebugStringA(name);
12692 OutputDebugStringA("\n link: ");
12693 OutputDebugStringA(link_buf);
12694 OutputDebugStringA("\n");
12695 */
12696
12697 gmt_time_string(mtime, sizeof(mtime), &filep->last_modified);
12698 mg_printf(conn,
12699 "<d:response>"
12700 "<d:href>%s</d:href>"
12701 "<d:propstat>"
12702 "<d:prop>"
12703 "<d:resourcetype>%s</d:resourcetype>"
12704 "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
12705 "<d:getlastmodified>%s</d:getlastmodified>"
12706 "<d:lockdiscovery>",
12707 link_buf,
12708 filep->is_directory ? "<d:collection/>" : "",
12709 filep->size,
12710 mtime);
12711
12712 for (i = 0; i < NUM_WEBDAV_LOCKS; i++) {
12713 struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
12714 if (!strcmp(dav_lock[i].path, link_buf)) {
12715 mg_printf(conn,
12716 "<d:activelock>"
12717 "<d:locktype><d:write/></d:locktype>"
12718 "<d:lockscope><d:exclusive/></d:lockscope>"
12719 "<d:depth>0</d:depth>"
12720 "<d:owner>%s</d:owner>"
12721 "<d:timeout>Second-%u</d:timeout>"
12722 "<d:locktoken>"
12723 "<d:href>%s</d:href>"
12724 "</d:locktoken>"
12725 "</d:activelock>\n",
12726 dav_lock[i].user,
12727 (unsigned)LOCK_DURATION_S,
12728 dav_lock[i].token);
12729 }
12730 }
12731
12732 mg_printf(conn,
12733 "</d:lockdiscovery>"
12734 "</d:prop>"
12735 "<d:status>HTTP/1.1 200 OK</d:status>"
12736 "</d:propstat>"
12737 "</d:response>\n");
12738
12739 mg_free(link_concat);
12740 return 1;
12741}
12742
12743
12744static int
12745print_dav_dir_entry(struct de *de, void *data)
12746{
12747 struct mg_connection *conn = (struct mg_connection *)data;
12748 if (!de || !conn
12749 || !print_props(
12750 conn, conn->request_info.local_uri, de->file_name, &de->file)) {
12751 /* stop scan */
12752 return 1;
12753 }
12754 return 0;
12755}
12756
12757
12758static void
12760 const char *path,
12761 struct mg_file_stat *filep)
12762{
12763 const char *depth = mg_get_header(conn, "Depth");
12764
12765 if (!conn || !path || !filep || !conn->dom_ctx) {
12766 return;
12767 }
12768
12769 /* return 207 "Multi-Status" */
12770 conn->must_close = 1;
12771 mg_response_header_start(conn, 207);
12775 "Content-Type",
12776 "application/xml; charset=utf-8",
12777 -1);
12779
12780 /* Content */
12781 mg_printf(conn,
12782 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
12783 "<d:multistatus xmlns:d='DAV:'>\n");
12784
12785 /* Print properties for the requested resource itself */
12786 print_props(conn, conn->request_info.local_uri, "", filep);
12787
12788 /* If it is a directory, print directory entries too if Depth is not 0
12789 */
12790 if (filep->is_directory
12792 "yes")
12793 && ((depth == NULL) || (strcmp(depth, "0") != 0))) {
12794 scan_directory(conn, path, conn, &print_dav_dir_entry);
12795 }
12796
12797 mg_printf(conn, "%s\n", "</d:multistatus>");
12798}
12799
12800
12801static void
12802dav_lock_file(struct mg_connection *conn, const char *path)
12803{
12804 /* internal function - therefore conn is assumed to be valid */
12805 char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
12806 uint64_t new_locktime;
12807 int lock_index = -1;
12808 int i;
12809 uint64_t LOCK_DURATION_NS =
12810 (uint64_t)(LOCK_DURATION_S) * (uint64_t)1000000000;
12811 struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
12812
12813 if (!path || !conn->dom_ctx || !conn->request_info.remote_user) {
12814 return;
12815 }
12816 mg_get_request_link(conn, link_buf, sizeof(link_buf));
12817
12818 /* const char *refresh = mg_get_header(conn, "If"); */
12819 /* Link refresh should have an "If" header:
12820 * http://www.webdav.org/specs/rfc2518.html#n-example---refreshing-a-write-lock
12821 * But it seems Windows Explorer does not send them.
12822 */
12823
12825 new_locktime = mg_get_current_time_ns();
12826
12827 /* Find a slot for a lock */
12828 while (lock_index < 0) {
12829 /* find existing lock */
12830 for (i = 0; i < NUM_WEBDAV_LOCKS; i++) {
12831 if (!strcmp(dav_lock[i].path, link_buf)) {
12832 if (!strcmp(conn->request_info.remote_user, dav_lock[i].user)) {
12833 /* locked by the same user */
12834 dav_lock[i].locktime = new_locktime;
12835 lock_index = i;
12836 break;
12837 } else {
12838 /* already locked by someone else */
12839 if (new_locktime
12840 > (dav_lock[i].locktime + LOCK_DURATION_NS)) {
12841 /* Lock expired */
12842 dav_lock[i].path[0] = 0;
12843 } else {
12844 /* Lock still valid */
12846 mg_send_http_error(conn, 423, "%s", "Already locked");
12847 return;
12848 }
12849 }
12850 }
12851 }
12852
12853 /* create new lock token */
12854 for (i = 0; i < NUM_WEBDAV_LOCKS; i++) {
12855 if (dav_lock[i].path[0] == 0) {
12856 char s[32];
12857 dav_lock[i].locktime = mg_get_current_time_ns();
12858 sprintf(s, "%" UINT64_FMT, (uint64_t)dav_lock[i].locktime);
12859 mg_md5(dav_lock[i].token,
12860 link_buf,
12861 "\x01",
12862 s,
12863 "\x01",
12865 NULL);
12866 mg_strlcpy(dav_lock[i].path,
12867 link_buf,
12868 sizeof(dav_lock[i].path));
12869 mg_strlcpy(dav_lock[i].user,
12871 sizeof(dav_lock[i].user));
12872 lock_index = i;
12873 break;
12874 }
12875 }
12876 if (lock_index < 0) {
12877 /* too many locks. Find oldest lock */
12878 uint64_t oldest_locktime = dav_lock[0].locktime;
12879 lock_index = 0;
12880 for (i = 1; i < NUM_WEBDAV_LOCKS; i++) {
12881 if (dav_lock[i].locktime < oldest_locktime) {
12882 oldest_locktime = dav_lock[i].locktime;
12883 lock_index = i;
12884 }
12885 }
12886 /* invalidate oldest lock */
12887 dav_lock[lock_index].path[0] = 0;
12888 }
12889 }
12891
12892 /* return 200 "OK" */
12893 conn->must_close = 1;
12894 mg_response_header_start(conn, 200);
12898 "Content-Type",
12899 "application/xml; charset=utf-8",
12900 -1);
12901 mg_response_header_add(conn, "Lock-Token", dav_lock[lock_index].token, -1);
12903
12904 /* Content */
12905 mg_printf(conn,
12906 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
12907 "<d:prop xmlns:d=\"DAV:\">\n"
12908 " <d:lockdiscovery>\n"
12909 " <d:activelock>\n"
12910 " <d:lockscope><d:exclusive/></d:lockscope>\n"
12911 " <d:locktype><d:write/></d:locktype>\n"
12912 " <d:owner>\n"
12913 " <d:href>%s</d:href>\n"
12914 " </d:owner>\n"
12915 " <d:timeout>Second-%u</d:timeout>\n"
12916 " <d:locktoken><d:href>%s</d:href></d:locktoken>\n"
12917 " <d:lockroot>\n"
12918 " <d:href>%s</d:href>\n"
12919 " </d:lockroot>\n"
12920 " </d:activelock>\n"
12921 " </d:lockdiscovery>\n"
12922 " </d:prop>\n",
12923 dav_lock[lock_index].user,
12925 dav_lock[lock_index].token,
12926 dav_lock[lock_index].path);
12927}
12928
12929
12930static void
12931dav_unlock_file(struct mg_connection *conn, const char *path)
12932{
12933 /* internal function - therefore conn is assumed to be valid */
12934 char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
12935 struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
12936 int lock_index;
12937
12938 if (!path || !conn->dom_ctx || !conn->request_info.remote_user) {
12939 return;
12940 }
12941
12942 mg_get_request_link(conn, link_buf, sizeof(link_buf));
12943
12945 /* find existing lock */
12946 for (lock_index = 0; lock_index < NUM_WEBDAV_LOCKS; lock_index++) {
12947 if (!strcmp(dav_lock[lock_index].path, link_buf)) {
12948 /* Success: return 204 "No Content" */
12950 conn->must_close = 1;
12951 mg_response_header_start(conn, 204);
12953 return;
12954 }
12955 }
12957
12958 /* Error: Cannot unlock a resource that is not locked */
12959 mg_send_http_error(conn, 423, "%s", "Lock not found");
12960}
12961
12962
12963static void
12964dav_proppatch(struct mg_connection *conn, const char *path)
12965{
12966 char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
12967
12968 if (!conn || !path || !conn->dom_ctx) {
12969 return;
12970 }
12971
12972 /* return 207 "Multi-Status" */
12973 conn->must_close = 1;
12974 mg_response_header_start(conn, 207);
12978 "Content-Type",
12979 "application/xml; charset=utf-8",
12980 -1);
12982
12983 mg_get_request_link(conn, link_buf, sizeof(link_buf));
12984
12985 /* Content */
12986 mg_printf(conn,
12987 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
12988 "<d:multistatus xmlns:d='DAV:'>\n"
12989 "<d:response>\n<d:href>%s</d:href>\n",
12990 link_buf);
12991 mg_printf(conn,
12992 "<d:propstat><d:status>HTTP/1.1 403 "
12993 "Forbidden</d:status></d:propstat>\n");
12994 mg_printf(conn, "%s\n", "</d:response></d:multistatus>");
12995}
12996#endif
12997
12998
12999CIVETWEB_API void
13001{
13002 if (conn) {
13003 (void)pthread_mutex_lock(&conn->mutex);
13004 }
13005}
13006
13007
13008CIVETWEB_API void
13010{
13011 if (conn) {
13012 (void)pthread_mutex_unlock(&conn->mutex);
13013 }
13014}
13015
13016
13017CIVETWEB_API void
13019{
13020 if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
13021 (void)pthread_mutex_lock(&ctx->nonce_mutex);
13022 }
13023}
13024
13025
13026CIVETWEB_API void
13028{
13029 if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
13030 (void)pthread_mutex_unlock(&ctx->nonce_mutex);
13031 }
13032}
13033
13034
13035#if defined(USE_LUA)
13036#include "mod_lua.inl"
13037#endif /* USE_LUA */
13038
13039#if defined(USE_DUKTAPE)
13040#include "mod_duktape.inl"
13041#endif /* USE_DUKTAPE */
13042
13043#if defined(USE_WEBSOCKET)
13044
13045#if !defined(NO_SSL_DL)
13046#if !defined(OPENSSL_API_3_0)
13047#define SHA_API static
13048#include "sha1.inl"
13049#endif
13050#endif
13051
13052static int
13053send_websocket_handshake(struct mg_connection *conn, const char *websock_key)
13054{
13055 static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
13056 char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
13057 size_t dst_len = sizeof(b64_sha);
13058#if !defined(OPENSSL_API_3_0)
13059 SHA_CTX sha_ctx;
13060#endif
13061 int truncated;
13062
13063 /* Calculate Sec-WebSocket-Accept reply from Sec-WebSocket-Key. */
13064 mg_snprintf(conn, &truncated, buf, sizeof(buf), "%s%s", websock_key, magic);
13065 if (truncated) {
13066 conn->must_close = 1;
13067 return 0;
13068 }
13069
13070 DEBUG_TRACE("%s", "Send websocket handshake");
13071
13072#if defined(OPENSSL_API_3_0)
13073 EVP_Digest((unsigned char *)buf,
13074 (uint32_t)strlen(buf),
13075 (unsigned char *)sha,
13076 NULL,
13077 EVP_get_digestbyname("sha1"),
13078 NULL);
13079#else
13080 SHA1_Init(&sha_ctx);
13081 SHA1_Update(&sha_ctx, (unsigned char *)buf, (uint32_t)strlen(buf));
13082 SHA1_Final((unsigned char *)sha, &sha_ctx);
13083#endif
13084 mg_base64_encode((unsigned char *)sha, sizeof(sha), b64_sha, &dst_len);
13085 mg_printf(conn,
13086 "HTTP/1.1 101 Switching Protocols\r\n"
13087 "Upgrade: websocket\r\n"
13088 "Connection: Upgrade\r\n"
13089 "Sec-WebSocket-Accept: %s\r\n",
13090 b64_sha);
13091
13092#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13093 // Send negotiated compression extension parameters
13094 websocket_deflate_response(conn);
13095#endif
13096
13098 mg_printf(conn,
13099 "Sec-WebSocket-Protocol: %s\r\n\r\n",
13101 } else {
13102 mg_printf(conn, "%s", "\r\n");
13103 }
13104
13105 return 1;
13106}
13107
13108
13109#if !defined(MG_MAX_UNANSWERED_PING)
13110/* Configuration of the maximum number of websocket PINGs that might
13111 * stay unanswered before the connection is considered broken.
13112 * Note: The name of this define may still change (until it is
13113 * defined as a compile parameter in a documentation).
13114 */
13115#define MG_MAX_UNANSWERED_PING (5)
13116#endif
13117
13118
13119static void
13120read_websocket(struct mg_connection *conn,
13121 mg_websocket_data_handler ws_data_handler,
13122 void *callback_data)
13123{
13124 /* Pointer to the beginning of the portion of the incoming websocket
13125 * message queue.
13126 * The original websocket upgrade request is never removed, so the queue
13127 * begins after it. */
13128 unsigned char *buf = (unsigned char *)conn->buf + conn->request_len;
13129 int n, error, exit_by_callback;
13130 int ret;
13131
13132 /* body_len is the length of the entire queue in bytes
13133 * len is the length of the current message
13134 * data_len is the length of the current message's data payload
13135 * header_len is the length of the current message's header */
13136 size_t i, len, mask_len = 0, header_len, body_len;
13137 uint64_t data_len = 0;
13138
13139 /* "The masking key is a 32-bit value chosen at random by the client."
13140 * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5
13141 */
13142 unsigned char mask[4];
13143
13144 /* data points to the place where the message is stored when passed to
13145 * the websocket_data callback. This is either mem on the stack, or a
13146 * dynamically allocated buffer if it is too large. */
13147 unsigned char mem[4096];
13148 unsigned char mop; /* mask flag and opcode */
13149
13150 /* Variables used for connection monitoring */
13151 double timeout = -1.0;
13152 int enable_ping_pong = 0;
13153 int ping_count = 0;
13154
13155 if (conn->dom_ctx->config[ENABLE_WEBSOCKET_PING_PONG]) {
13156 enable_ping_pong =
13157 !mg_strcasecmp(conn->dom_ctx->config[ENABLE_WEBSOCKET_PING_PONG],
13158 "yes");
13159 }
13160
13161 if (conn->dom_ctx->config[WEBSOCKET_TIMEOUT]) {
13162 timeout = atoi(conn->dom_ctx->config[WEBSOCKET_TIMEOUT]) / 1000.0;
13163 }
13164 if ((timeout <= 0.0) && (conn->dom_ctx->config[REQUEST_TIMEOUT])) {
13165 timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
13166 }
13167 if (timeout <= 0.0) {
13168 timeout = atof(config_options[REQUEST_TIMEOUT].default_value) / 1000.0;
13169 }
13170
13171 /* Enter data processing loop */
13172 DEBUG_TRACE("Websocket connection %s:%u start data processing loop",
13175 conn->in_websocket_handling = 1;
13176 mg_set_thread_name("wsock");
13177
13178 /* Loop continuously, reading messages from the socket, invoking the
13179 * callback, and waiting repeatedly until an error occurs. */
13180 while (STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)
13181 && (!conn->must_close)) {
13182 header_len = 0;
13183 DEBUG_ASSERT(conn->data_len >= conn->request_len);
13184 if ((body_len = (size_t)(conn->data_len - conn->request_len)) >= 2) {
13185 len = buf[1] & 127;
13186 mask_len = (buf[1] & 128) ? 4 : 0;
13187 if ((len < 126) && (body_len >= mask_len)) {
13188 /* inline 7-bit length field */
13189 data_len = len;
13190 header_len = 2 + mask_len;
13191 } else if ((len == 126) && (body_len >= (4 + mask_len))) {
13192 /* 16-bit length field */
13193 header_len = 4 + mask_len;
13194 data_len = ((((size_t)buf[2]) << 8) + buf[3]);
13195 } else if (body_len >= (10 + mask_len)) {
13196 /* 64-bit length field */
13197 uint32_t l1, l2;
13198 memcpy(&l1, &buf[2], 4); /* Use memcpy for alignment */
13199 memcpy(&l2, &buf[6], 4);
13200 header_len = 10 + mask_len;
13201 data_len = (((uint64_t)ntohl(l1)) << 32) + ntohl(l2);
13202
13203 if (data_len > (uint64_t)0x7FFF0000ul) {
13204 /* no can do */
13206 conn,
13207 "%s",
13208 "websocket out of memory; closing connection");
13209 break;
13210 }
13211 }
13212 }
13213
13214 if ((header_len > 0) && (body_len >= header_len)) {
13215 /* Allocate space to hold websocket payload */
13216 unsigned char *data = mem;
13217
13218 if ((size_t)data_len > (size_t)sizeof(mem)) {
13219 data = (unsigned char *)mg_malloc_ctx((size_t)data_len,
13220 conn->phys_ctx);
13221 if (data == NULL) {
13222 /* Allocation failed, exit the loop and then close the
13223 * connection */
13225 conn,
13226 "%s",
13227 "websocket out of memory; closing connection");
13228 break;
13229 }
13230 }
13231
13232 /* Copy the mask before we shift the queue and destroy it */
13233 if (mask_len > 0) {
13234 memcpy(mask, buf + header_len - mask_len, sizeof(mask));
13235 } else {
13236 memset(mask, 0, sizeof(mask));
13237 }
13238
13239 /* Read frame payload from the first message in the queue into
13240 * data and advance the queue by moving the memory in place. */
13241 DEBUG_ASSERT(body_len >= header_len);
13242 if (data_len + (uint64_t)header_len > (uint64_t)body_len) {
13243 mop = buf[0]; /* current mask and opcode */
13244 /* Overflow case */
13245 len = body_len - header_len;
13246 memcpy(data, buf + header_len, len);
13247 error = 0;
13248 while ((uint64_t)len < data_len) {
13249 n = pull_inner(NULL,
13250 conn,
13251 (char *)(data + len),
13252 (int)(data_len - len),
13253 timeout);
13254 if (n <= -2) {
13255 error = 1;
13256 break;
13257 } else if (n > 0) {
13258 len += (size_t)n;
13259 } else {
13260 /* Timeout: should retry */
13261 /* TODO: retry condition */
13262 }
13263 }
13264 if (error) {
13266 conn,
13267 "%s",
13268 "Websocket pull failed; closing connection");
13269 if (data != mem) {
13270 mg_free(data);
13271 }
13272 break;
13273 }
13274
13275 conn->data_len = conn->request_len;
13276
13277 } else {
13278
13279 mop = buf[0]; /* current mask and opcode, overwritten by
13280 * memmove() */
13281
13282 /* Length of the message being read at the front of the
13283 * queue. Cast to 31 bit is OK, since we limited
13284 * data_len before. */
13285 len = (size_t)data_len + header_len;
13286
13287 /* Copy the data payload into the data pointer for the
13288 * callback. Cast to 31 bit is OK, since we
13289 * limited data_len */
13290 memcpy(data, buf + header_len, (size_t)data_len);
13291
13292 /* Move the queue forward len bytes */
13293 memmove(buf, buf + len, body_len - len);
13294
13295 /* Mark the queue as advanced */
13296 conn->data_len -= (int)len;
13297 }
13298
13299 /* Apply mask if necessary */
13300 if (mask_len > 0) {
13301 for (i = 0; i < (size_t)data_len; i++) {
13302 data[i] ^= mask[i & 3];
13303 }
13304 }
13305
13306 exit_by_callback = 0;
13307 if (enable_ping_pong && ((mop & 0xF) == MG_WEBSOCKET_OPCODE_PONG)) {
13308 /* filter PONG messages */
13309 DEBUG_TRACE("PONG from %s:%u",
13312 /* No unanwered PINGs left */
13313 ping_count = 0;
13314 } else if (enable_ping_pong
13315 && ((mop & 0xF) == MG_WEBSOCKET_OPCODE_PING)) {
13316 /* reply PING messages */
13317 DEBUG_TRACE("Reply PING from %s:%u",
13320 ret = mg_websocket_write(conn,
13322 (char *)data,
13323 (size_t)data_len);
13324 if (ret <= 0) {
13325 /* Error: send failed */
13326 DEBUG_TRACE("Reply PONG failed (%i)", ret);
13327 break;
13328 }
13329
13330 } else {
13331 /* Exit the loop if callback signals to exit (server side),
13332 * or "connection close" opcode received (client side). */
13333 if (ws_data_handler != NULL) {
13334#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13335 if (mop & 0x40) {
13336 /* Inflate the data received if bit RSV1 is set. */
13337 if (!conn->websocket_deflate_initialized) {
13338 if (websocket_deflate_initialize(conn, 1) != Z_OK)
13339 exit_by_callback = 1;
13340 }
13341 if (!exit_by_callback) {
13342 size_t inflate_buf_size_old = 0;
13343 size_t inflate_buf_size =
13344 data_len
13345 * 4; // Initial guess of the inflated message
13346 // size. We double the memory when needed.
13347 Bytef *inflated = NULL;
13348 Bytef *new_mem = NULL;
13349 conn->websocket_inflate_state.avail_in =
13350 (uInt)(data_len + 4);
13351 conn->websocket_inflate_state.next_in = data;
13352 // Add trailing 0x00 0x00 0xff 0xff bytes
13353 data[data_len] = '\x00';
13354 data[data_len + 1] = '\x00';
13355 data[data_len + 2] = '\xff';
13356 data[data_len + 3] = '\xff';
13357 do {
13358 if (inflate_buf_size_old == 0) {
13359 new_mem =
13360 (Bytef *)mg_calloc(inflate_buf_size,
13361 sizeof(Bytef));
13362 } else {
13363 inflate_buf_size *= 2;
13364 new_mem =
13365 (Bytef *)mg_realloc(inflated,
13366 inflate_buf_size);
13367 }
13368 if (new_mem == NULL) {
13370 conn,
13371 "Out of memory: Cannot allocate "
13372 "inflate buffer of %lu bytes",
13373 (unsigned long)inflate_buf_size);
13374 exit_by_callback = 1;
13375 break;
13376 }
13377 inflated = new_mem;
13378 conn->websocket_inflate_state.avail_out =
13379 (uInt)(inflate_buf_size
13380 - inflate_buf_size_old);
13381 conn->websocket_inflate_state.next_out =
13382 inflated + inflate_buf_size_old;
13383 ret = inflate(&conn->websocket_inflate_state,
13384 Z_SYNC_FLUSH);
13385 if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR
13386 || ret == Z_MEM_ERROR) {
13388 conn,
13389 "ZLIB inflate error: %i %s",
13390 ret,
13391 (conn->websocket_inflate_state.msg
13392 ? conn->websocket_inflate_state.msg
13393 : "<no error message>"));
13394 exit_by_callback = 1;
13395 break;
13396 }
13397 inflate_buf_size_old = inflate_buf_size;
13398
13399 } while (conn->websocket_inflate_state.avail_out
13400 == 0);
13401 inflate_buf_size -=
13402 conn->websocket_inflate_state.avail_out;
13403 if (!ws_data_handler(conn,
13404 mop,
13405 (char *)inflated,
13406 inflate_buf_size,
13407 callback_data)) {
13408 exit_by_callback = 1;
13409 }
13410 mg_free(inflated);
13411 }
13412 } else
13413#endif
13414 if (!ws_data_handler(conn,
13415 mop,
13416 (char *)data,
13417 (size_t)data_len,
13418 callback_data)) {
13419 exit_by_callback = 1;
13420 }
13421 }
13422 }
13423
13424 /* It a buffer has been allocated, free it again */
13425 if (data != mem) {
13426 mg_free(data);
13427 }
13428
13429 if (exit_by_callback) {
13430 DEBUG_TRACE("Callback requests to close connection from %s:%u",
13433 break;
13434 }
13435 if ((mop & 0xf) == MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE) {
13436 /* Opcode == 8, connection close */
13437 DEBUG_TRACE("Message requests to close connection from %s:%u",
13440 break;
13441 }
13442
13443 /* Not breaking the loop, process next websocket frame. */
13444 } else {
13445 /* Read from the socket into the next available location in the
13446 * message queue. */
13447 n = pull_inner(NULL,
13448 conn,
13449 conn->buf + conn->data_len,
13450 conn->buf_size - conn->data_len,
13451 timeout);
13452 if (n <= -2) {
13453 /* Error, no bytes read */
13454 DEBUG_TRACE("PULL from %s:%u failed",
13457 break;
13458 }
13459 if (n > 0) {
13460 conn->data_len += n;
13461 /* Reset open PING count */
13462 ping_count = 0;
13463 } else {
13465 && (!conn->must_close)) {
13466 if (ping_count > MG_MAX_UNANSWERED_PING) {
13467 /* Stop sending PING */
13468 DEBUG_TRACE("Too many (%i) unanswered ping from %s:%u "
13469 "- closing connection",
13470 ping_count,
13473 break;
13474 }
13475 if (enable_ping_pong) {
13476 /* Send Websocket PING message */
13477 DEBUG_TRACE("PING to %s:%u",
13480 ret = mg_websocket_write(conn,
13482 NULL,
13483 0);
13484
13485 if (ret <= 0) {
13486 /* Error: send failed */
13487 DEBUG_TRACE("Send PING failed (%i)", ret);
13488 break;
13489 }
13490 ping_count++;
13491 }
13492 }
13493 /* Timeout: should retry */
13494 /* TODO: get timeout def */
13495 }
13496 }
13497 }
13498
13499 /* Leave data processing loop */
13500 mg_set_thread_name("worker");
13501 conn->in_websocket_handling = 0;
13502 DEBUG_TRACE("Websocket connection %s:%u left data processing loop",
13505}
13506
13507
13508static int
13509mg_websocket_write_exec(struct mg_connection *conn,
13510 int opcode,
13511 const char *data,
13512 size_t dataLen,
13513 uint32_t masking_key)
13514{
13515 unsigned char header[14];
13516 size_t headerLen;
13517 int retval;
13518
13519#if defined(GCC_DIAGNOSTIC)
13520 /* Disable spurious conversion warning for GCC */
13521#pragma GCC diagnostic push
13522#pragma GCC diagnostic ignored "-Wconversion"
13523#endif
13524
13525 /* Note that POSIX/Winsock's send() is threadsafe
13526 * http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid
13527 * but mongoose's mg_printf/mg_write is not (because of the loop in
13528 * push(), although that is only a problem if the packet is large or
13529 * outgoing buffer is full). */
13530
13531 /* TODO: Check if this lock should be moved to user land.
13532 * Currently the server sets this lock for websockets, but
13533 * not for any other connection. It must be set for every
13534 * conn read/written by more than one thread, no matter if
13535 * it is a websocket or regular connection. */
13536 (void)mg_lock_connection(conn);
13537
13538#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13539 size_t deflated_size = 0;
13540 Bytef *deflated = 0;
13541 // Deflate websocket messages over 100kb
13542 int use_deflate = dataLen > 100 * 1024 && conn->accept_gzip;
13543
13544 if (use_deflate) {
13545 if (!conn->websocket_deflate_initialized) {
13546 if (websocket_deflate_initialize(conn, 1) != Z_OK)
13547 return 0;
13548 }
13549
13550 // Deflating the message
13551 header[0] = 0xC0u | (unsigned char)((unsigned)opcode & 0xf);
13552 conn->websocket_deflate_state.avail_in = (uInt)dataLen;
13553 conn->websocket_deflate_state.next_in = (unsigned char *)data;
13554 deflated_size = (size_t)compressBound((uLong)dataLen);
13555 deflated = mg_calloc(deflated_size, sizeof(Bytef));
13556 if (deflated == NULL) {
13558 conn,
13559 "Out of memory: Cannot allocate deflate buffer of %lu bytes",
13560 (unsigned long)deflated_size);
13562 return -1;
13563 }
13564 conn->websocket_deflate_state.avail_out = (uInt)deflated_size;
13565 conn->websocket_deflate_state.next_out = deflated;
13566 deflate(&conn->websocket_deflate_state, conn->websocket_deflate_flush);
13567 dataLen = deflated_size - conn->websocket_deflate_state.avail_out
13568 - 4; // Strip trailing 0x00 0x00 0xff 0xff bytes
13569 } else
13570#endif
13571 header[0] = 0x80u | (unsigned char)((unsigned)opcode & 0xf);
13572
13573#if defined(GCC_DIAGNOSTIC)
13574#pragma GCC diagnostic pop
13575#endif
13576
13577 /* Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 */
13578 if (dataLen < 126) {
13579 /* inline 7-bit length field */
13580 header[1] = (unsigned char)dataLen;
13581 headerLen = 2;
13582 } else if (dataLen <= 0xFFFF) {
13583 /* 16-bit length field */
13584 uint16_t len = htons((uint16_t)dataLen);
13585 header[1] = 126;
13586 memcpy(header + 2, &len, 2);
13587 headerLen = 4;
13588 } else {
13589 /* 64-bit length field */
13590 uint32_t len1 = htonl((uint32_t)((uint64_t)dataLen >> 32));
13591 uint32_t len2 = htonl((uint32_t)(dataLen & 0xFFFFFFFFu));
13592 header[1] = 127;
13593 memcpy(header + 2, &len1, 4);
13594 memcpy(header + 6, &len2, 4);
13595 headerLen = 10;
13596 }
13597
13598 if (masking_key) {
13599 /* add mask */
13600 header[1] |= 0x80;
13601 memcpy(header + headerLen, &masking_key, 4);
13602 headerLen += 4;
13603 }
13604
13605 retval = mg_write(conn, header, headerLen);
13606 if (retval != (int)headerLen) {
13607 /* Did not send complete header */
13608 retval = -1;
13609 } else {
13610 if (dataLen > 0) {
13611#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13612 if (use_deflate) {
13613 retval = mg_write(conn, deflated, dataLen);
13614 mg_free(deflated);
13615 } else
13616#endif
13617 retval = mg_write(conn, data, dataLen);
13618 }
13619 /* if dataLen == 0, the header length (2) is returned */
13620 }
13621
13622 /* TODO: Remove this unlock as well, when lock is removed. */
13624
13625 return retval;
13626}
13627
13628
13629CIVETWEB_API int
13631 int opcode,
13632 const char *data,
13633 size_t dataLen)
13634{
13635 return mg_websocket_write_exec(conn, opcode, data, dataLen, 0);
13636}
13637
13638
13639static void
13640mask_data(const char *in, size_t in_len, uint32_t masking_key, char *out)
13641{
13642 size_t i = 0;
13643
13644 i = 0;
13645 if ((in_len > 3) && ((ptrdiff_t)in % 4) == 0) {
13646 /* Convert in 32 bit words, if data is 4 byte aligned */
13647 while (i < (in_len - 3)) {
13648 *(uint32_t *)(void *)(out + i) =
13649 *(uint32_t *)(void *)(in + i) ^ masking_key;
13650 i += 4;
13651 }
13652 }
13653 if (i != in_len) {
13654 /* convert 1-3 remaining bytes if ((dataLen % 4) != 0)*/
13655 while (i < in_len) {
13656 *(uint8_t *)(void *)(out + i) =
13657 *(uint8_t *)(void *)(in + i)
13658 ^ *(((uint8_t *)&masking_key) + (i % 4));
13659 i++;
13660 }
13661 }
13662}
13663
13664
13665CIVETWEB_API int
13667 int opcode,
13668 const char *data,
13669 size_t dataLen)
13670{
13671 int retval = -1;
13672 char *masked_data =
13673 (char *)mg_malloc_ctx(((dataLen + 7) / 4) * 4, conn->phys_ctx);
13674 uint32_t masking_key = 0;
13675
13676 if (masked_data == NULL) {
13677 /* Return -1 in an error case */
13678 mg_cry_internal(conn,
13679 "%s",
13680 "Cannot allocate buffer for masked websocket response: "
13681 "Out of memory");
13682 return -1;
13683 }
13684
13685 do {
13686 /* Get a masking key - but not 0 */
13687 masking_key = (uint32_t)get_random();
13688 } while (masking_key == 0);
13689
13690 mask_data(data, dataLen, masking_key, masked_data);
13691
13692 retval = mg_websocket_write_exec(
13693 conn, opcode, masked_data, dataLen, masking_key);
13694 mg_free(masked_data);
13695
13696 return retval;
13697}
13698
13699
13700static void
13701handle_websocket_request(struct mg_connection *conn,
13702 const char *path,
13703 int is_callback_resource,
13704 struct mg_websocket_subprotocols *subprotocols,
13705 mg_websocket_connect_handler ws_connect_handler,
13706 mg_websocket_ready_handler ws_ready_handler,
13707 mg_websocket_data_handler ws_data_handler,
13708 mg_websocket_close_handler ws_close_handler,
13709 void *cbData)
13710{
13711 const char *websock_key = mg_get_header(conn, "Sec-WebSocket-Key");
13712 const char *version = mg_get_header(conn, "Sec-WebSocket-Version");
13713 ptrdiff_t lua_websock = 0;
13714
13715#if !defined(USE_LUA)
13716 (void)path;
13717#endif
13718
13719 /* Step 1: Check websocket protocol version. */
13720 /* Step 1.1: Check Sec-WebSocket-Key. */
13721 if (!websock_key) {
13722 /* The RFC standard version (https://tools.ietf.org/html/rfc6455)
13723 * requires a Sec-WebSocket-Key header.
13724 */
13725 /* It could be the hixie draft version
13726 * (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76).
13727 */
13728 const char *key1 = mg_get_header(conn, "Sec-WebSocket-Key1");
13729 const char *key2 = mg_get_header(conn, "Sec-WebSocket-Key2");
13730 char key3[8];
13731
13732 if ((key1 != NULL) && (key2 != NULL)) {
13733 /* This version uses 8 byte body data in a GET request */
13734 conn->content_len = 8;
13735 if (8 == mg_read(conn, key3, 8)) {
13736 /* This is the hixie version */
13737 mg_send_http_error(conn,
13738 426,
13739 "%s",
13740 "Protocol upgrade to RFC 6455 required");
13741 return;
13742 }
13743 }
13744 /* This is an unknown version */
13745 mg_send_http_error(conn, 400, "%s", "Malformed websocket request");
13746 return;
13747 }
13748
13749 /* Step 1.2: Check websocket protocol version. */
13750 /* The RFC version (https://tools.ietf.org/html/rfc6455) is 13. */
13751 if ((version == NULL) || (strcmp(version, "13") != 0)) {
13752 /* Reject wrong versions */
13753 mg_send_http_error(conn, 426, "%s", "Protocol upgrade required");
13754 return;
13755 }
13756
13757 /* Step 1.3: Could check for "Host", but we do not really need this
13758 * value for anything, so just ignore it. */
13759
13760 /* Step 2: If a callback is responsible, call it. */
13761 if (is_callback_resource) {
13762 /* Step 2.1 check and select subprotocol */
13763 const char *protocols[64]; // max 64 headers
13764 int nbSubprotocolHeader = get_req_headers(&conn->request_info,
13765 "Sec-WebSocket-Protocol",
13766 protocols,
13767 64);
13768
13769 if ((nbSubprotocolHeader > 0) && subprotocols) {
13770
13771 int headerNo, idx;
13772 size_t len;
13773 const char *sep, *curSubProtocol,
13774 *acceptedWebSocketSubprotocol = NULL;
13775
13776 /* look for matching subprotocol */
13777 for (headerNo = 0; headerNo < nbSubprotocolHeader; headerNo++) {
13778 /* There might be multiple headers ... */
13779 const char *protocol = protocols[headerNo];
13780 curSubProtocol = protocol;
13781
13782 /* ... and in every header there might be a , separated list */
13783 while (!acceptedWebSocketSubprotocol && (*curSubProtocol)) {
13784
13785 while ((*curSubProtocol == ' ') || (*curSubProtocol == ','))
13786 curSubProtocol++;
13787 sep = strchr(curSubProtocol, ',');
13788 if (sep) {
13789 len = (size_t)(sep - curSubProtocol);
13790 } else {
13791 len = strlen(curSubProtocol);
13792 }
13793
13794 for (idx = 0; idx < subprotocols->nb_subprotocols; idx++) {
13795 // COMPARE: curSubProtocol ==
13796 // subprotocols->subprotocols[idx]
13797 if ((strlen(subprotocols->subprotocols[idx]) == len)
13798 && (strncmp(curSubProtocol,
13799 subprotocols->subprotocols[idx],
13800 len)
13801 == 0)) {
13802 acceptedWebSocketSubprotocol =
13803 subprotocols->subprotocols[idx];
13804 break;
13805 }
13806 }
13807 curSubProtocol += len;
13808 }
13809 }
13810
13812 acceptedWebSocketSubprotocol;
13813 }
13814
13815#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13816 websocket_deflate_negotiate(conn);
13817#endif
13818
13819 if ((ws_connect_handler != NULL)
13820 && (ws_connect_handler(conn, cbData) != 0)) {
13821 /* C callback has returned non-zero, do not proceed with
13822 * handshake.
13823 */
13824 /* Note that C callbacks are no longer called when Lua is
13825 * responsible, so C can no longer filter callbacks for Lua. */
13826 return;
13827 }
13828 }
13829
13830#if defined(USE_LUA)
13831 /* Step 3: No callback. Check if Lua is responsible. */
13832 else {
13833 /* Step 3.1: Check if Lua is responsible. */
13834 if (conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS]) {
13835 lua_websock = match_prefix_strlen(
13836 conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS], path);
13837 }
13838
13839 if (lua_websock > 0) {
13840 /* Step 3.2: Lua is responsible: call it. */
13841 conn->lua_websocket_state = lua_websocket_new(path, conn);
13842 if (!conn->lua_websocket_state) {
13843 /* Lua rejected the new client */
13844 return;
13845 }
13846 }
13847 }
13848#endif
13849
13850 /* Step 4: Check if there is a responsible websocket handler. */
13851 if (!is_callback_resource && !lua_websock) {
13852 /* There is no callback, and Lua is not responsible either. */
13853 /* Reply with a 404 Not Found. We are still at a standard
13854 * HTTP request here, before the websocket handshake, so
13855 * we can still send standard HTTP error replies. */
13856 mg_send_http_error(conn, 404, "%s", "Not found");
13857 return;
13858 }
13859
13860 /* Step 5: The websocket connection has been accepted */
13861 if (!send_websocket_handshake(conn, websock_key)) {
13862 mg_send_http_error(conn, 500, "%s", "Websocket handshake failed");
13863 return;
13864 }
13865
13866 /* Step 6: Call the ready handler */
13867 if (is_callback_resource) {
13868 if (ws_ready_handler != NULL) {
13869 ws_ready_handler(conn, cbData);
13870 }
13871#if defined(USE_LUA)
13872 } else if (lua_websock) {
13873 if (!lua_websocket_ready(conn, conn->lua_websocket_state)) {
13874 /* the ready handler returned false */
13875 return;
13876 }
13877#endif
13878 }
13879
13880 /* Step 7: Enter the read loop */
13881 if (is_callback_resource) {
13882 read_websocket(conn, ws_data_handler, cbData);
13883#if defined(USE_LUA)
13884 } else if (lua_websock) {
13885 read_websocket(conn, lua_websocket_data, conn->lua_websocket_state);
13886#endif
13887 }
13888
13889#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13890 /* Step 8: Close the deflate & inflate buffers */
13891 if (conn->websocket_deflate_initialized) {
13892 deflateEnd(&conn->websocket_deflate_state);
13893 inflateEnd(&conn->websocket_inflate_state);
13894 }
13895#endif
13896
13897 /* Step 9: Call the close handler */
13898 if (ws_close_handler) {
13899 ws_close_handler(conn, cbData);
13900 }
13901}
13902#endif /* !USE_WEBSOCKET */
13903
13904
13905/* Is upgrade request:
13906 * 0 = regular HTTP/1.0 or HTTP/1.1 request
13907 * 1 = upgrade to websocket
13908 * 2 = upgrade to HTTP/2
13909 * -1 = upgrade to unknown protocol
13910 */
13911static int
13913{
13914 const char *connection_headers[8];
13915 const char *upgrade_to;
13916 int connection_header_count, i, should_upgrade;
13917
13918 /* A websocket protocol has the following HTTP headers:
13919 *
13920 * Connection: Upgrade
13921 * Upgrade: Websocket
13922 *
13923 * It seems some clients use multiple headers:
13924 * see https://github.com/civetweb/civetweb/issues/1083
13925 */
13926 connection_header_count = get_req_headers(&conn->request_info,
13927 "Connection",
13928 connection_headers,
13929 8);
13930 should_upgrade = 0;
13931 for (i = 0; i < connection_header_count; i++) {
13932 if (0 != mg_strcasestr(connection_headers[i], "upgrade")) {
13933 should_upgrade = 1;
13934 }
13935 }
13936 if (!should_upgrade) {
13937 return PROTOCOL_TYPE_HTTP1;
13938 }
13939
13940 upgrade_to = mg_get_header(conn, "Upgrade");
13941 if (upgrade_to == NULL) {
13942 /* "Connection: Upgrade" without "Upgrade" Header --> Error */
13943 return -1;
13944 }
13945
13946 /* Upgrade to ... */
13947 if (0 != mg_strcasestr(upgrade_to, "websocket")) {
13948 /* The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and
13949 * "Sec-WebSocket-Version" are also required.
13950 * Don't check them here, since even an unsupported websocket protocol
13951 * request still IS a websocket request (in contrast to a standard HTTP
13952 * request). It will fail later in handle_websocket_request.
13953 */
13954 return PROTOCOL_TYPE_WEBSOCKET; /* Websocket */
13955 }
13956 if (0 != mg_strcasestr(upgrade_to, "h2")) {
13957 return PROTOCOL_TYPE_HTTP2; /* Websocket */
13958 }
13959
13960 /* Upgrade to another protocol */
13961 return -1;
13962}
13963
13964
13965static int
13966parse_match_net(const struct vec *vec, const union usa *sa, int no_strict)
13967{
13968 int n;
13969 unsigned int a, b, c, d, slash;
13970
13971 if (sscanf(vec->ptr, "%u.%u.%u.%u/%u%n", &a, &b, &c, &d, &slash, &n)
13972 != 5) { // NOLINT(cert-err34-c) 'sscanf' used to convert a string to an
13973 // integer value, but function will not report conversion
13974 // errors; consider using 'strtol' instead
13975 slash = 32;
13976 if (sscanf(vec->ptr, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n)
13977 != 4) { // NOLINT(cert-err34-c) 'sscanf' used to convert a string to
13978 // an integer value, but function will not report conversion
13979 // errors; consider using 'strtol' instead
13980 n = 0;
13981 }
13982 }
13983
13984 if ((n > 0) && ((size_t)n == vec->len)) {
13985 if ((a < 256) && (b < 256) && (c < 256) && (d < 256) && (slash < 33)) {
13986 /* IPv4 format */
13987 if (sa->sa.sa_family == AF_INET) {
13988 uint32_t ip = ntohl(sa->sin.sin_addr.s_addr);
13989 uint32_t net = ((uint32_t)a << 24) | ((uint32_t)b << 16)
13990 | ((uint32_t)c << 8) | (uint32_t)d;
13991 uint32_t mask = slash ? (0xFFFFFFFFu << (32 - slash)) : 0;
13992 return (ip & mask) == net;
13993 }
13994 return 0;
13995 }
13996 }
13997#if defined(USE_IPV6)
13998 else {
13999 char ad[50];
14000 const char *p;
14001
14002 if (sscanf(vec->ptr, "[%49[^]]]/%u%n", ad, &slash, &n) != 2) {
14003 slash = 128;
14004 if (sscanf(vec->ptr, "[%49[^]]]%n", ad, &n) != 1) {
14005 n = 0;
14006 }
14007 }
14008
14009 if ((n <= 0) && no_strict) {
14010 /* no square brackets? */
14011 p = strchr(vec->ptr, '/');
14012 if (p && (p < (vec->ptr + vec->len))) {
14013 if (((size_t)(p - vec->ptr) < sizeof(ad))
14014 && (sscanf(p, "/%u%n", &slash, &n) == 1)) {
14015 n += (int)(p - vec->ptr);
14016 mg_strlcpy(ad, vec->ptr, (size_t)(p - vec->ptr) + 1);
14017 } else {
14018 n = 0;
14019 }
14020 } else if (vec->len < sizeof(ad)) {
14021 n = (int)vec->len;
14022 slash = 128;
14023 mg_strlcpy(ad, vec->ptr, vec->len + 1);
14024 }
14025 }
14026
14027 if ((n > 0) && ((size_t)n == vec->len) && (slash < 129)) {
14028 p = ad;
14029 c = 0;
14030 /* zone indexes are unsupported, at least two colons are needed */
14031 while (isxdigit((unsigned char)*p) || (*p == '.') || (*p == ':')) {
14032 if (*(p++) == ':') {
14033 c++;
14034 }
14035 }
14036 if ((*p == '\0') && (c >= 2)) {
14037 struct sockaddr_in6 sin6;
14038 unsigned int i;
14039
14040 /* for strict validation, an actual IPv6 argument is needed */
14041 if (sa->sa.sa_family != AF_INET6) {
14042 return 0;
14043 }
14044 if (mg_inet_pton(AF_INET6, ad, &sin6, sizeof(sin6), 0)) {
14045 /* IPv6 format */
14046 for (i = 0; i < 16; i++) {
14047 uint8_t ip = sa->sin6.sin6_addr.s6_addr[i];
14048 uint8_t net = sin6.sin6_addr.s6_addr[i];
14049 uint8_t mask = 0;
14050
14051 if (8 * i + 8 < slash) {
14052 mask = 0xFFu;
14053 } else if (8 * i < slash) {
14054 mask = (uint8_t)(0xFFu << (8 * i + 8 - slash));
14055 }
14056 if ((ip & mask) != net) {
14057 return 0;
14058 }
14059 }
14060 return 1;
14061 }
14062 }
14063 }
14064 }
14065#else
14066 (void)no_strict;
14067#endif
14068
14069 /* malformed */
14070 return -1;
14071}
14072
14073
14074static int
14075set_throttle(const char *spec, const union usa *rsa, const char *uri)
14076{
14077 int throttle = 0;
14078 struct vec vec, val;
14079 char mult;
14080 double v;
14081
14082 while ((spec = next_option(spec, &vec, &val)) != NULL) {
14083 mult = ',';
14084 if ((val.ptr == NULL)
14085 || (sscanf(val.ptr, "%lf%c", &v, &mult)
14086 < 1) // NOLINT(cert-err34-c) 'sscanf' used to convert a string
14087 // to an integer value, but function will not report
14088 // conversion errors; consider using 'strtol' instead
14089 || (v < 0)
14090 || ((lowercase(&mult) != 'k') && (lowercase(&mult) != 'm')
14091 && (mult != ','))) {
14092 continue;
14093 }
14094 v *= (lowercase(&mult) == 'k')
14095 ? 1024
14096 : ((lowercase(&mult) == 'm') ? 1048576 : 1);
14097 if (vec.len == 1 && vec.ptr[0] == '*') {
14098 throttle = (int)v;
14099 } else {
14100 int matched = parse_match_net(&vec, rsa, 0);
14101 if (matched >= 0) {
14102 /* a valid IP subnet */
14103 if (matched) {
14104 throttle = (int)v;
14105 }
14106 } else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
14107 throttle = (int)v;
14108 }
14109 }
14110 }
14111
14112 return throttle;
14113}
14114
14115
14116/* The mg_upload function is superseded by mg_handle_form_request. */
14117#include "handle_form.inl"
14118
14119
14120static int
14122{
14123 unsigned int i;
14124 int idx = -1;
14125 if (ctx) {
14126 for (i = 0; ((idx == -1) && (i < ctx->num_listening_sockets)); i++) {
14127 idx = ctx->listening_sockets[i].is_ssl ? ((int)(i)) : -1;
14128 }
14129 }
14130 return idx;
14131}
14132
14133
14134/* Return host (without port) */
14135static void
14136get_host_from_request_info(struct vec *host, const struct mg_request_info *ri)
14137{
14138 const char *host_header =
14139 get_header(ri->http_headers, ri->num_headers, "Host");
14140
14141 host->ptr = NULL;
14142 host->len = 0;
14143
14144 if (host_header != NULL) {
14145 const char *pos;
14146
14147 /* If the "Host" is an IPv6 address, like [::1], parse until ]
14148 * is found. */
14149 if (*host_header == '[') {
14150 pos = strchr(host_header, ']');
14151 if (!pos) {
14152 /* Malformed hostname starts with '[', but no ']' found */
14153 DEBUG_TRACE("%s", "Host name format error '[' without ']'");
14154 return;
14155 }
14156 /* terminate after ']' */
14157 host->ptr = host_header;
14158 host->len = (size_t)(pos + 1 - host_header);
14159 } else {
14160 /* Otherwise, a ':' separates hostname and port number */
14161 pos = strchr(host_header, ':');
14162 if (pos != NULL) {
14163 host->len = (size_t)(pos - host_header);
14164 } else {
14165 host->len = strlen(host_header);
14166 }
14167 host->ptr = host_header;
14168 }
14169 }
14170}
14171
14172
14173static int
14175{
14176 struct vec host;
14177
14179
14180 if (host.ptr) {
14181 if (conn->ssl) {
14182 /* This is a HTTPS connection, maybe we have a hostname
14183 * from SNI (set in ssl_servername_callback). */
14184 const char *sslhost = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
14185 if (sslhost && (conn->dom_ctx != &(conn->phys_ctx->dd))) {
14186 /* We are not using the default domain */
14187 if ((strlen(sslhost) != host.len)
14188 || mg_strncasecmp(host.ptr, sslhost, host.len)) {
14189 /* Mismatch between SNI domain and HTTP domain */
14190 DEBUG_TRACE("Host mismatch: SNI: %s, HTTPS: %.*s",
14191 sslhost,
14192 (int)host.len,
14193 host.ptr);
14194 return 0;
14195 }
14196 }
14197
14198 } else {
14199 struct mg_domain_context *dom = &(conn->phys_ctx->dd);
14200 while (dom) {
14201 const char *domName = dom->config[AUTHENTICATION_DOMAIN];
14202 size_t domNameLen = strlen(domName);
14203 if ((domNameLen == host.len)
14204 && !mg_strncasecmp(host.ptr, domName, host.len)) {
14205
14206 /* Found matching domain */
14207 DEBUG_TRACE("HTTP domain %s found",
14209
14210 /* TODO: Check if this is a HTTP or HTTPS domain */
14211 conn->dom_ctx = dom;
14212 break;
14213 }
14215 dom = dom->next;
14217 }
14218 }
14219
14220 DEBUG_TRACE("HTTP%s Host: %.*s",
14221 conn->ssl ? "S" : "",
14222 (int)host.len,
14223 host.ptr);
14224
14225 } else {
14226 DEBUG_TRACE("HTTP%s Host is not set", conn->ssl ? "S" : "");
14227 return 1;
14228 }
14229
14230 return 1;
14231}
14232
14233
14234static void
14236{
14237 char target_url[MG_BUF_LEN];
14238 int truncated = 0;
14239 const char *expect_proto =
14240 (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET) ? "wss" : "https";
14241
14242 /* Use "308 Permanent Redirect" */
14243 int redirect_code = 308;
14244
14245 /* In any case, close the current connection */
14246 conn->must_close = 1;
14247
14248 /* Send host, port, uri and (if it exists) ?query_string */
14250 conn, target_url, sizeof(target_url), expect_proto, port, NULL)
14251 < 0) {
14252 truncated = 1;
14253 } else if (conn->request_info.query_string != NULL) {
14254 size_t slen1 = strlen(target_url);
14255 size_t slen2 = strlen(conn->request_info.query_string);
14256 if ((slen1 + slen2 + 2) < sizeof(target_url)) {
14257 target_url[slen1] = '?';
14258 memcpy(target_url + slen1 + 1,
14260 slen2);
14261 target_url[slen1 + slen2 + 1] = 0;
14262 } else {
14263 truncated = 1;
14264 }
14265 }
14266
14267 /* Check overflow in location buffer (will not occur if MG_BUF_LEN
14268 * is used as buffer size) */
14269 if (truncated) {
14270 mg_send_http_error(conn, 500, "%s", "Redirect URL too long");
14271 return;
14272 }
14273
14274 /* Use redirect helper function */
14275 mg_send_http_redirect(conn, target_url, redirect_code);
14276}
14277
14278
14279static void
14281 struct mg_domain_context *dom_ctx,
14282 const char *uri,
14283 int handler_type,
14284 int is_delete_request,
14285 mg_request_handler handler,
14286 struct mg_websocket_subprotocols *subprotocols,
14287 mg_websocket_connect_handler connect_handler,
14288 mg_websocket_ready_handler ready_handler,
14289 mg_websocket_data_handler data_handler,
14290 mg_websocket_close_handler close_handler,
14291 mg_authorization_handler auth_handler,
14292 void *cbdata)
14293{
14294 struct mg_handler_info *tmp_rh, **lastref;
14295 size_t urilen = strlen(uri);
14296
14299 DEBUG_ASSERT(is_delete_request || connect_handler != NULL
14301 || close_handler != NULL);
14302
14304 if (handler != NULL) {
14305 return;
14306 }
14307 if (!is_delete_request && (connect_handler == NULL)
14308 && (ready_handler == NULL) && (data_handler == NULL)
14309 && (close_handler == NULL)) {
14310 return;
14311 }
14312 if (auth_handler != NULL) {
14313 return;
14314 }
14315
14316 } else if (handler_type == REQUEST_HANDLER) {
14318 && data_handler == NULL && close_handler == NULL);
14319 DEBUG_ASSERT(is_delete_request || (handler != NULL));
14321
14322 if ((connect_handler != NULL) || (ready_handler != NULL)
14323 || (data_handler != NULL) || (close_handler != NULL)) {
14324 return;
14325 }
14326 if (!is_delete_request && (handler == NULL)) {
14327 return;
14328 }
14329 if (auth_handler != NULL) {
14330 return;
14331 }
14332
14333 } else if (handler_type == AUTH_HANDLER) {
14336 && data_handler == NULL && close_handler == NULL);
14337 DEBUG_ASSERT(is_delete_request || (auth_handler != NULL));
14338 if (handler != NULL) {
14339 return;
14340 }
14341 if ((connect_handler != NULL) || (ready_handler != NULL)
14342 || (data_handler != NULL) || (close_handler != NULL)) {
14343 return;
14344 }
14345 if (!is_delete_request && (auth_handler == NULL)) {
14346 return;
14347 }
14348 } else {
14349 /* Unknown handler type. */
14350 return;
14351 }
14352
14353 if (!phys_ctx || !dom_ctx) {
14354 /* no context available */
14355 return;
14356 }
14357
14358 mg_lock_context(phys_ctx);
14359
14360 /* first try to find an existing handler */
14361 do {
14362 lastref = &(dom_ctx->handlers);
14363 for (tmp_rh = dom_ctx->handlers; tmp_rh != NULL;
14364 tmp_rh = tmp_rh->next) {
14365 if (tmp_rh->handler_type == handler_type
14366 && (urilen == tmp_rh->uri_len) && !strcmp(tmp_rh->uri, uri)) {
14367 if (!is_delete_request) {
14368 /* update existing handler */
14370 /* Wait for end of use before updating */
14371 if (tmp_rh->refcount) {
14372 mg_unlock_context(phys_ctx);
14373 mg_sleep(1);
14374 mg_lock_context(phys_ctx);
14375 /* tmp_rh might have been freed, search again. */
14376 break;
14377 }
14378 /* Ok, the handler is no more use -> Update it */
14379 tmp_rh->handler = handler;
14380 } else if (handler_type == WEBSOCKET_HANDLER) {
14381 tmp_rh->subprotocols = subprotocols;
14383 tmp_rh->ready_handler = ready_handler;
14384 tmp_rh->data_handler = data_handler;
14385 tmp_rh->close_handler = close_handler;
14386 } else { /* AUTH_HANDLER */
14387 tmp_rh->auth_handler = auth_handler;
14388 }
14389 tmp_rh->cbdata = cbdata;
14390 } else {
14391 /* remove existing handler */
14393 /* Wait for end of use before removing */
14394 if (tmp_rh->refcount) {
14395 tmp_rh->removing = 1;
14396 mg_unlock_context(phys_ctx);
14397 mg_sleep(1);
14398 mg_lock_context(phys_ctx);
14399 /* tmp_rh might have been freed, search again. */
14400 break;
14401 }
14402 /* Ok, the handler is no more used */
14403 }
14404 *lastref = tmp_rh->next;
14405 mg_free(tmp_rh->uri);
14406 mg_free(tmp_rh);
14407 }
14408 mg_unlock_context(phys_ctx);
14409 return;
14410 }
14411 lastref = &(tmp_rh->next);
14412 }
14413 } while (tmp_rh != NULL);
14414
14415 if (is_delete_request) {
14416 /* no handler to set, this was a remove request to a non-existing
14417 * handler */
14418 mg_unlock_context(phys_ctx);
14419 return;
14420 }
14421
14422 tmp_rh =
14423 (struct mg_handler_info *)mg_calloc_ctx(1,
14424 sizeof(struct mg_handler_info),
14425 phys_ctx);
14426 if (tmp_rh == NULL) {
14427 mg_unlock_context(phys_ctx);
14428 mg_cry_ctx_internal(phys_ctx,
14429 "%s",
14430 "Cannot create new request handler struct, OOM");
14431 return;
14432 }
14433 tmp_rh->uri = mg_strdup_ctx(uri, phys_ctx);
14434 if (!tmp_rh->uri) {
14435 mg_unlock_context(phys_ctx);
14436 mg_free(tmp_rh);
14437 mg_cry_ctx_internal(phys_ctx,
14438 "%s",
14439 "Cannot create new request handler struct, OOM");
14440 return;
14441 }
14442 tmp_rh->uri_len = urilen;
14444 tmp_rh->refcount = 0;
14445 tmp_rh->removing = 0;
14446 tmp_rh->handler = handler;
14447 } else if (handler_type == WEBSOCKET_HANDLER) {
14448 tmp_rh->subprotocols = subprotocols;
14450 tmp_rh->ready_handler = ready_handler;
14451 tmp_rh->data_handler = data_handler;
14452 tmp_rh->close_handler = close_handler;
14453 } else { /* AUTH_HANDLER */
14454 tmp_rh->auth_handler = auth_handler;
14455 }
14456 tmp_rh->cbdata = cbdata;
14457 tmp_rh->handler_type = handler_type;
14458 tmp_rh->next = NULL;
14459
14460 *lastref = tmp_rh;
14461 mg_unlock_context(phys_ctx);
14462}
14463
14464
14465CIVETWEB_API void
14467 const char *uri,
14469 void *cbdata)
14470{
14472 &(ctx->dd),
14473 uri,
14475 handler == NULL,
14476 handler,
14477 NULL,
14478 NULL,
14479 NULL,
14480 NULL,
14481 NULL,
14482 NULL,
14483 cbdata);
14484}
14485
14486
14487CIVETWEB_API void
14505
14506
14507CIVETWEB_API void
14509 struct mg_context *ctx,
14510 const char *uri,
14516 void *cbdata)
14517{
14518 int is_delete_request = (connect_handler == NULL) && (ready_handler == NULL)
14519 && (data_handler == NULL)
14520 && (close_handler == NULL);
14522 &(ctx->dd),
14523 uri,
14525 is_delete_request,
14526 NULL,
14532 NULL,
14533 cbdata);
14534}
14535
14536
14537CIVETWEB_API void
14539 const char *uri,
14541 void *cbdata)
14542{
14544 &(ctx->dd),
14545 uri,
14547 handler == NULL,
14548 NULL,
14549 NULL,
14550 NULL,
14551 NULL,
14552 NULL,
14553 NULL,
14554 handler,
14555 cbdata);
14556}
14557
14558
14559static int
14561 int handler_type,
14569 void **cbdata,
14570 struct mg_handler_info **handler_info)
14571{
14572 const struct mg_request_info *request_info = mg_get_request_info(conn);
14573 if (request_info) {
14574 const char *uri = request_info->local_uri;
14575 size_t urilen = strlen(uri);
14576 struct mg_handler_info *tmp_rh;
14577 int step, matched;
14578
14579 if (!conn || !conn->phys_ctx || !conn->dom_ctx) {
14580 return 0;
14581 }
14582
14584
14585 for (step = 0; step < 3; step++) {
14586 for (tmp_rh = conn->dom_ctx->handlers; tmp_rh != NULL;
14587 tmp_rh = tmp_rh->next) {
14588 if (tmp_rh->handler_type != handler_type) {
14589 continue;
14590 }
14591 if (step == 0) {
14592 /* first try for an exact match */
14593 matched = (tmp_rh->uri_len == urilen)
14594 && (strcmp(tmp_rh->uri, uri) == 0);
14595 } else if (step == 1) {
14596 /* next try for a partial match, we will accept
14597 uri/something */
14598 matched =
14599 (tmp_rh->uri_len < urilen)
14600 && (uri[tmp_rh->uri_len] == '/')
14601 && (memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0);
14602 } else {
14603 /* finally try for pattern match */
14604 matched =
14605 match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0;
14606 }
14607 if (matched) {
14609 *subprotocols = tmp_rh->subprotocols;
14611 *ready_handler = tmp_rh->ready_handler;
14612 *data_handler = tmp_rh->data_handler;
14613 *close_handler = tmp_rh->close_handler;
14614 } else if (handler_type == REQUEST_HANDLER) {
14615 if (tmp_rh->removing) {
14616 /* Treat as none found */
14617 step = 2;
14618 break;
14619 }
14620 *handler = tmp_rh->handler;
14621 /* Acquire handler and give it back */
14622 tmp_rh->refcount++;
14623 *handler_info = tmp_rh;
14624 } else { /* AUTH_HANDLER */
14625 *auth_handler = tmp_rh->auth_handler;
14626 }
14627 *cbdata = tmp_rh->cbdata;
14629 return 1;
14630 }
14631 }
14632 }
14633
14635 }
14636 return 0; /* none found */
14637}
14638
14639
14640/* Check if the script file is in a path, allowed for script files.
14641 * This can be used if uploading files is possible not only for the server
14642 * admin, and the upload mechanism does not check the file extension.
14643 */
14644static int
14645is_in_script_path(const struct mg_connection *conn, const char *path)
14646{
14647 /* TODO (Feature): Add config value for allowed script path.
14648 * Default: All allowed. */
14649 (void)conn;
14650 (void)path;
14651 return 1;
14652}
14653
14654
14655#if defined(USE_WEBSOCKET) && defined(MG_EXPERIMENTAL_INTERFACES)
14656static int
14657experimental_websocket_client_data_wrapper(struct mg_connection *conn,
14658 int bits,
14659 char *data,
14660 size_t len,
14661 void *cbdata)
14662{
14663 struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
14664 if (pcallbacks->websocket_data) {
14665 return pcallbacks->websocket_data(conn, bits, data, len);
14666 }
14667 /* No handler set - assume "OK" */
14668 return 1;
14669}
14670
14671
14672static void
14673experimental_websocket_client_close_wrapper(const struct mg_connection *conn,
14674 void *cbdata)
14675{
14676 struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
14677 if (pcallbacks->connection_close) {
14678 pcallbacks->connection_close(conn);
14679 }
14680}
14681#endif
14682
14683
14684/* Decrement recount of handler. conn must not be NULL, handler_info may be NULL
14685 */
14686static void
14688 struct mg_handler_info *handler_info)
14689{
14690 if (handler_info != NULL) {
14691 /* Use context lock for ref counter */
14693 handler_info->refcount--;
14695 }
14696}
14697
14698
14699/* This is the heart of the Civetweb's logic.
14700 * This function is called when the request is read, parsed and validated,
14701 * and Civetweb must decide what action to take: serve a file, or
14702 * a directory, or call embedded function, etcetera. */
14703static void
14705{
14706 struct mg_request_info *ri = &conn->request_info;
14707 char path[UTF8_PATH_MAX];
14708 int uri_len, ssl_index;
14709 int is_found = 0, is_script_resource = 0, is_websocket_request = 0,
14710 is_put_or_delete_request = 0, is_callback_resource = 0,
14711 is_template_text_file = 0, is_webdav_request = 0;
14712 int i;
14713 struct mg_file file = STRUCT_FILE_INITIALIZER;
14714 mg_request_handler callback_handler = NULL;
14715 struct mg_handler_info *handler_info = NULL;
14717 mg_websocket_connect_handler ws_connect_handler = NULL;
14718 mg_websocket_ready_handler ws_ready_handler = NULL;
14719 mg_websocket_data_handler ws_data_handler = NULL;
14720 mg_websocket_close_handler ws_close_handler = NULL;
14721 void *callback_data = NULL;
14722 mg_authorization_handler auth_handler = NULL;
14723 void *auth_callback_data = NULL;
14724 int handler_type;
14725 time_t curtime = time(NULL);
14726 char date[64];
14727 char *tmp;
14728
14729 path[0] = 0;
14730
14731 /* 0. Reset internal state (required for HTTP/2 proxy) */
14732 conn->request_state = 0;
14733
14734 /* 1. get the request url */
14735 /* 1.1. split into url and query string */
14736 if ((conn->request_info.query_string = strchr(ri->request_uri, '?'))
14737 != NULL) {
14738 *((char *)conn->request_info.query_string++) = '\0';
14739 }
14740
14741 /* 1.2. do a https redirect, if required. Do not decode URIs yet. */
14742 if (!conn->client.is_ssl && conn->client.ssl_redir) {
14743 ssl_index = get_first_ssl_listener_index(conn->phys_ctx);
14744 if (ssl_index >= 0) {
14745 int port = (int)ntohs(USA_IN_PORT_UNSAFE(
14746 &(conn->phys_ctx->listening_sockets[ssl_index].lsa)));
14747 redirect_to_https_port(conn, port);
14748 } else {
14749 /* A http to https forward port has been specified,
14750 * but no https port to forward to. */
14751 mg_send_http_error(conn,
14752 503,
14753 "%s",
14754 "Error: SSL forward not configured properly");
14755 mg_cry_internal(conn,
14756 "%s",
14757 "Can not redirect to SSL, no SSL port available");
14758 }
14759 return;
14760 }
14761 uri_len = (int)strlen(ri->local_uri);
14762
14763 /* 1.3. decode url (if config says so) */
14764 if (should_decode_url(conn)) {
14765 url_decode_in_place((char *)ri->local_uri);
14766 }
14767
14768 /* URL decode the query-string only if explicitly set in the configuration
14769 */
14770 if (conn->request_info.query_string) {
14771 if (should_decode_query_string(conn)) {
14773 }
14774 }
14775
14776 /* 1.4. clean URIs, so a path like allowed_dir/../forbidden_file is not
14777 * possible. The fact that we cleaned the URI is stored in that the
14778 * pointer to ri->local_ur and ri->local_uri_raw are now different.
14779 * ri->local_uri_raw still points to memory allocated in
14780 * worker_thread_run(). ri->local_uri is private to the request so we
14781 * don't have to use preallocated memory here. */
14782 tmp = mg_strdup(ri->local_uri_raw);
14783 if (!tmp) {
14784 /* Out of memory. We cannot do anything reasonable here. */
14785 return;
14786 }
14788 ri->local_uri = tmp;
14789
14790 /* step 1. completed, the url is known now */
14791 DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri);
14792
14793 /* 2. if this ip has limited speed, set it for this connection */
14795 &conn->client.rsa,
14796 ri->local_uri);
14797
14798 /* 3. call a "handle everything" callback, if registered */
14799 if (conn->phys_ctx->callbacks.begin_request != NULL) {
14800 /* Note that since V1.7 the "begin_request" function is called
14801 * before an authorization check. If an authorization check is
14802 * required, use a request_handler instead. */
14803 i = conn->phys_ctx->callbacks.begin_request(conn);
14804 if (i > 0) {
14805 /* callback already processed the request. Store the
14806 return value as a status code for the access log. */
14807 conn->status_code = i;
14808 if (!conn->must_close) {
14810 }
14811 DEBUG_TRACE("%s", "begin_request handled request");
14812 return;
14813 } else if (i == 0) {
14814 /* civetweb should process the request */
14815 } else {
14816 /* unspecified - may change with the next version */
14817 DEBUG_TRACE("%s", "done (undocumented behavior)");
14818 return;
14819 }
14820 }
14821
14822 /* request not yet handled by a handler or redirect, so the request
14823 * is processed here */
14824
14825 /* 4. Check for CORS preflight requests and handle them (if configured).
14826 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
14827 */
14828 if (!strcmp(ri->request_method, "OPTIONS")) {
14829 /* Send a response to CORS preflights only if
14830 * access_control_allow_methods is not NULL and not an empty string.
14831 * In this case, scripts can still handle CORS. */
14832 const char *cors_meth_cfg =
14834 const char *cors_orig_cfg =
14836 const char *cors_origin =
14837 get_header(ri->http_headers, ri->num_headers, "Origin");
14838 const char *cors_acrm = get_header(ri->http_headers,
14839 ri->num_headers,
14840 "Access-Control-Request-Method");
14841
14842 /* Todo: check if cors_origin is in cors_orig_cfg.
14843 * Or, let the client check this. */
14844
14845 if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0)
14846 && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0)
14847 && (cors_origin != NULL) && (cors_acrm != NULL)) {
14848 /* This is a valid CORS preflight, and the server is configured
14849 * to handle it automatically. */
14850 const char *cors_acrh =
14852 ri->num_headers,
14853 "Access-Control-Request-Headers");
14854
14855 gmt_time_string(date, sizeof(date), &curtime);
14856 mg_printf(conn,
14857 "HTTP/1.1 200 OK\r\n"
14858 "Date: %s\r\n"
14859 "Access-Control-Allow-Origin: %s\r\n"
14860 "Access-Control-Allow-Methods: %s\r\n"
14861 "Content-Length: 0\r\n"
14862 "Connection: %s\r\n",
14863 date,
14864 cors_orig_cfg,
14865 ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg),
14867
14868 if (cors_acrh != NULL) {
14869 /* CORS request is asking for additional headers */
14870 const char *cors_hdr_cfg =
14872
14873 if ((cors_hdr_cfg != NULL) && (*cors_hdr_cfg != 0)) {
14874 /* Allow only if access_control_allow_headers is
14875 * not NULL and not an empty string. If this
14876 * configuration is set to *, allow everything.
14877 * Otherwise this configuration must be a list
14878 * of allowed HTTP header names. */
14879 mg_printf(conn,
14880 "Access-Control-Allow-Headers: %s\r\n",
14881 ((cors_hdr_cfg[0] == '*') ? cors_acrh
14882 : cors_hdr_cfg));
14883 }
14884 }
14885 mg_printf(conn, "Access-Control-Max-Age: 60\r\n");
14886 mg_printf(conn, "\r\n");
14887 DEBUG_TRACE("%s", "OPTIONS done");
14888 return;
14889 }
14890 }
14891
14892 /* 5. interpret the url to find out how the request must be handled
14893 */
14894 /* 5.1. first test, if the request targets the regular http(s)://
14895 * protocol namespace or the websocket ws(s):// protocol namespace.
14896 */
14897 is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET);
14898#if defined(USE_WEBSOCKET)
14899 handler_type = is_websocket_request ? WEBSOCKET_HANDLER : REQUEST_HANDLER;
14900#else
14901 handler_type = REQUEST_HANDLER;
14902#endif /* defined(USE_WEBSOCKET) */
14903
14904 if (is_websocket_request) {
14905 HTTP1_only;
14906 }
14907
14908 /* 5.2. check if the request will be handled by a callback */
14909 if (get_request_handler(conn,
14910 handler_type,
14911 &callback_handler,
14912 &subprotocols,
14913 &ws_connect_handler,
14914 &ws_ready_handler,
14915 &ws_data_handler,
14916 &ws_close_handler,
14917 NULL,
14918 &callback_data,
14919 &handler_info)) {
14920 /* 5.2.1. A callback will handle this request. All requests
14921 * handled by a callback have to be considered as requests
14922 * to a script resource. */
14923 is_callback_resource = 1;
14924 is_script_resource = 1;
14925 is_put_or_delete_request = is_put_or_delete_method(conn);
14926 /* Never handle a C callback according to File WebDav rules,
14927 * even if it is a webdav method */
14928 is_webdav_request = 0; /* is_civetweb_webdav_method(conn); */
14929 } else {
14930 no_callback_resource:
14931
14932 /* 5.2.2. No callback is responsible for this request. The URI
14933 * addresses a file based resource (static content or Lua/cgi
14934 * scripts in the file system). */
14935 is_callback_resource = 0;
14936 interpret_uri(conn,
14937 path,
14938 sizeof(path),
14939 &file.stat,
14940 &is_found,
14941 &is_script_resource,
14942 &is_websocket_request,
14943 &is_put_or_delete_request,
14944 &is_webdav_request,
14945 &is_template_text_file);
14946 }
14947
14948 /* 5.3. A webdav request (PROPFIND/PROPPATCH/LOCK/UNLOCK) */
14949 if (is_webdav_request) {
14950 /* TODO: Do we need a config option? */
14951 const char *webdav_enable = conn->dom_ctx->config[ENABLE_WEBDAV];
14952 if (webdav_enable[0] != 'y') {
14953 mg_send_http_error(conn,
14954 405,
14955 "%s method not allowed",
14957 DEBUG_TRACE("%s", "webdav rejected");
14958 return;
14959 }
14960 }
14961
14962 /* 6. authorization check */
14963 /* 6.1. a custom authorization handler is installed */
14964 if (get_request_handler(conn,
14966 NULL,
14967 NULL,
14968 NULL,
14969 NULL,
14970 NULL,
14971 NULL,
14972 &auth_handler,
14973 &auth_callback_data,
14974 NULL)) {
14975 if (!auth_handler(conn, auth_callback_data)) {
14976
14977 /* Callback handler will not be used anymore. Release it */
14978 release_handler_ref(conn, handler_info);
14979 DEBUG_TRACE("%s", "auth handler rejected request");
14980 return;
14981 }
14982 } else if (is_put_or_delete_request && !is_script_resource
14983 && !is_callback_resource) {
14984 HTTP1_only;
14985 /* 6.2. this request is a PUT/DELETE to a real file */
14986 /* 6.2.1. thus, the server must have real files */
14987#if defined(NO_FILES)
14988 if (1) {
14989#else
14990 if (conn->dom_ctx->config[DOCUMENT_ROOT] == NULL
14992#endif
14993 /* This code path will not be called for request handlers */
14994 DEBUG_ASSERT(handler_info == NULL);
14995
14996 /* This server does not have any real files, thus the
14997 * PUT/DELETE methods are not valid. */
14998 mg_send_http_error(conn,
14999 405,
15000 "%s method not allowed",
15002 DEBUG_TRACE("%s", "all file based put/delete requests rejected");
15003 return;
15004 }
15005
15006#if !defined(NO_FILES)
15007 /* 6.2.2. Check if put authorization for static files is
15008 * available.
15009 */
15010 if (!is_authorized_for_put(conn)) {
15012 DEBUG_TRACE("%s", "file write needs authorization");
15013 return;
15014 }
15015#endif
15016
15017 } else {
15018 /* 6.3. This is either a OPTIONS, GET, HEAD or POST request,
15019 * or it is a PUT or DELETE request to a resource that does not
15020 * correspond to a file. Check authorization. */
15021 if (!check_authorization(conn, path)) {
15023
15024 /* Callback handler will not be used anymore. Release it */
15025 release_handler_ref(conn, handler_info);
15026 DEBUG_TRACE("%s", "access authorization required");
15027 return;
15028 }
15029 }
15030
15031 /* request is authorized or does not need authorization */
15032
15033 /* 7. check if there are request handlers for this uri */
15034 if (is_callback_resource) {
15035 HTTP1_only;
15036 if (!is_websocket_request) {
15037 i = callback_handler(conn, callback_data);
15038
15039 /* Callback handler will not be used anymore. Release it */
15040 release_handler_ref(conn, handler_info);
15041
15042 if (i > 0) {
15043 /* Do nothing, callback has served the request. Store
15044 * then return value as status code for the log and discard
15045 * all data from the client not used by the callback. */
15046 conn->status_code = i;
15047 if (!conn->must_close) {
15049 }
15050 } else {
15051 /* The handler did NOT handle the request. */
15052 /* Some proper reactions would be:
15053 * a) close the connections without sending anything
15054 * b) send a 404 not found
15055 * c) try if there is a file matching the URI
15056 * It would be possible to do a, b or c in the callback
15057 * implementation, and return 1 - we cannot do anything
15058 * here, that is not possible in the callback.
15059 *
15060 * TODO: What would be the best reaction here?
15061 * (Note: The reaction may change, if there is a better
15062 * idea.)
15063 */
15064
15065 /* For the moment, use option c: We look for a proper file,
15066 * but since a file request is not always a script resource,
15067 * the authorization check might be different. */
15068 callback_handler = NULL;
15069
15070 /* Here we are at a dead end:
15071 * According to URI matching, a callback should be
15072 * responsible for handling the request,
15073 * we called it, but the callback declared itself
15074 * not responsible.
15075 * We use a goto here, to get out of this dead end,
15076 * and continue with the default handling.
15077 * A goto here is simpler and better to understand
15078 * than some curious loop. */
15079 goto no_callback_resource;
15080 }
15081 } else {
15082#if defined(USE_WEBSOCKET)
15083 handle_websocket_request(conn,
15084 path,
15085 is_callback_resource,
15087 ws_connect_handler,
15088 ws_ready_handler,
15089 ws_data_handler,
15090 ws_close_handler,
15091 callback_data);
15092#endif
15093 }
15094 DEBUG_TRACE("%s", "websocket handling done");
15095 return;
15096 }
15097
15098 /* 8. handle websocket requests */
15099#if defined(USE_WEBSOCKET)
15100 if (is_websocket_request) {
15101 HTTP1_only;
15102 if (is_script_resource) {
15103
15104 if (is_in_script_path(conn, path)) {
15105 /* Websocket Lua script */
15106 handle_websocket_request(conn,
15107 path,
15108 0 /* Lua Script */,
15109 NULL,
15110 NULL,
15111 NULL,
15112 NULL,
15113 NULL,
15114 conn->phys_ctx->user_data);
15115 } else {
15116 /* Script was in an illegal path */
15117 mg_send_http_error(conn, 403, "%s", "Forbidden");
15118 }
15119 } else {
15120 mg_send_http_error(conn, 404, "%s", "Not found");
15121 }
15122 DEBUG_TRACE("%s", "websocket script done");
15123 return;
15124 } else
15125#endif
15126
15127#if defined(NO_FILES)
15128 /* 9a. In case the server uses only callbacks, this uri is
15129 * unknown.
15130 * Then, all request handling ends here. */
15131 mg_send_http_error(conn, 404, "%s", "Not Found");
15132
15133#else
15134 /* 9b. This request is either for a static file or resource handled
15135 * by a script file. Thus, a DOCUMENT_ROOT must exist. */
15136 if (conn->dom_ctx->config[DOCUMENT_ROOT] == NULL) {
15137 mg_send_http_error(conn, 404, "%s", "Not Found");
15138 DEBUG_TRACE("%s", "no document root available");
15139 return;
15140 }
15141
15142 /* 10. Request is handled by a script */
15143 if (is_script_resource) {
15144 HTTP1_only;
15145 handle_file_based_request(conn, path, &file);
15146 DEBUG_TRACE("%s", "script handling done");
15147 return;
15148 }
15149
15150 /* Request was not handled by a callback or script. It will be
15151 * handled by a server internal method. */
15152
15153 /* 11. Handle put/delete/mkcol requests */
15154 if (is_put_or_delete_request) {
15155 HTTP1_only;
15156 /* 11.1. PUT method */
15157 if (!strcmp(ri->request_method, "PUT")) {
15158 put_file(conn, path);
15159 DEBUG_TRACE("handling %s request to %s done",
15160 ri->request_method,
15161 path);
15162 return;
15163 }
15164 /* 11.2. DELETE method */
15165 if (!strcmp(ri->request_method, "DELETE")) {
15166 delete_file(conn, path);
15167 DEBUG_TRACE("handling %s request to %s done",
15168 ri->request_method,
15169 path);
15170 return;
15171 }
15172 /* 11.3. MKCOL method */
15173 if (!strcmp(ri->request_method, "MKCOL")) {
15174 dav_mkcol(conn, path);
15175 DEBUG_TRACE("handling %s request to %s done",
15176 ri->request_method,
15177 path);
15178 return;
15179 }
15180 /* 11.4. MOVE method */
15181 if (!strcmp(ri->request_method, "MOVE")) {
15182 dav_move_file(conn, path, 0);
15183 DEBUG_TRACE("handling %s request to %s done",
15184 ri->request_method,
15185 path);
15186 return;
15187 }
15188 if (!strcmp(ri->request_method, "COPY")) {
15189 dav_move_file(conn, path, 1);
15190 DEBUG_TRACE("handling %s request to %s done",
15191 ri->request_method,
15192 path);
15193 return;
15194 }
15195 /* 11.5. LOCK method */
15196 if (!strcmp(ri->request_method, "LOCK")) {
15197 dav_lock_file(conn, path);
15198 DEBUG_TRACE("handling %s request to %s done",
15199 ri->request_method,
15200 path);
15201 return;
15202 }
15203 /* 11.6. UNLOCK method */
15204 if (!strcmp(ri->request_method, "UNLOCK")) {
15205 dav_unlock_file(conn, path);
15206 DEBUG_TRACE("handling %s request to %s done",
15207 ri->request_method,
15208 path);
15209 return;
15210 }
15211 /* 11.7. PROPPATCH method */
15212 if (!strcmp(ri->request_method, "PROPPATCH")) {
15213 dav_proppatch(conn, path);
15214 DEBUG_TRACE("handling %s request to %s done",
15215 ri->request_method,
15216 path);
15217 return;
15218 }
15219 /* 11.8. Other methods, e.g.: PATCH
15220 * This method is not supported for static resources,
15221 * only for scripts (Lua, CGI) and callbacks. */
15222 mg_send_http_error(conn,
15223 405,
15224 "%s method not allowed",
15226 DEBUG_TRACE("method %s on %s is not supported",
15227 ri->request_method,
15228 path);
15229 return;
15230 }
15231
15232 /* 11. File does not exist, or it was configured that it should be
15233 * hidden */
15234 if (!is_found || (must_hide_file(conn, path))) {
15235 mg_send_http_error(conn, 404, "%s", "Not found");
15236 DEBUG_TRACE("handling %s request to %s: file not found",
15237 ri->request_method,
15238 path);
15239 return;
15240 }
15241
15242 /* 12. Directory uris should end with a slash */
15243 if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0)
15244 && (ri->local_uri[uri_len - 1] != '/')) {
15245
15246 /* Path + server root */
15247 size_t buflen = UTF8_PATH_MAX * 2 + 2;
15248 char *new_path;
15249
15250 if (ri->query_string) {
15251 buflen += strlen(ri->query_string);
15252 }
15253 new_path = (char *)mg_malloc_ctx(buflen, conn->phys_ctx);
15254 if (!new_path) {
15255 mg_send_http_error(conn, 500, "out or memory");
15256 } else {
15257 mg_get_request_link(conn, new_path, buflen - 1);
15258 strcat(new_path, "/");
15259 if (ri->query_string) {
15260 /* Append ? and query string */
15261 strcat(new_path, "?");
15262 strcat(new_path, ri->query_string);
15263 }
15264 mg_send_http_redirect(conn, new_path, 301);
15265 mg_free(new_path);
15266 }
15267 DEBUG_TRACE("%s request to %s: directory redirection sent",
15268 ri->request_method,
15269 path);
15270 return;
15271 }
15272
15273 /* 13. Handle other methods than GET/HEAD */
15274 /* 13.1. Handle PROPFIND */
15275 if (!strcmp(ri->request_method, "PROPFIND")) {
15276 handle_propfind(conn, path, &file.stat);
15277 DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15278 return;
15279 }
15280 /* 13.2. Handle OPTIONS for files */
15281 if (!strcmp(ri->request_method, "OPTIONS")) {
15282 /* This standard handler is only used for real files.
15283 * Scripts should support the OPTIONS method themselves, to allow a
15284 * maximum flexibility.
15285 * Lua and CGI scripts may fully support CORS this way (including
15286 * preflights). */
15287 send_options(conn);
15288 DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15289 return;
15290 }
15291 /* 13.3. everything but GET and HEAD (e.g. POST) */
15292 if ((0 != strcmp(ri->request_method, "GET"))
15293 && (0 != strcmp(ri->request_method, "HEAD"))) {
15294 mg_send_http_error(conn,
15295 405,
15296 "%s method not allowed",
15298 DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15299 return;
15300 }
15301
15302 /* 14. directories */
15303 if (file.stat.is_directory) {
15304 /* Substitute files have already been handled above. */
15305 /* Here we can either generate and send a directory listing,
15306 * or send an "access denied" error. */
15308 "yes")) {
15309 handle_directory_request(conn, path);
15310 } else {
15311 mg_send_http_error(conn,
15312 403,
15313 "%s",
15314 "Error: Directory listing denied");
15315 }
15316 DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15317 return;
15318 }
15319
15320 /* 15. Files with search/replace patterns: LSP and SSI */
15321 if (is_template_text_file) {
15322 HTTP1_only;
15323 handle_file_based_request(conn, path, &file);
15324 DEBUG_TRACE("handling %s request to %s done (template)",
15325 ri->request_method,
15326 path);
15327 return;
15328 }
15329
15330 /* 16. Static file - maybe cached */
15331#if !defined(NO_CACHING)
15332 if ((!conn->in_error_handler) && is_not_modified(conn, &file.stat)) {
15333 /* Send 304 "Not Modified" - this must not send any body data */
15335 DEBUG_TRACE("handling %s request to %s done (not modified)",
15336 ri->request_method,
15337 path);
15338 return;
15339 }
15340#endif /* !NO_CACHING */
15341
15342 /* 17. Static file - not cached */
15343 handle_static_file_request(conn, path, &file, NULL, NULL);
15344 DEBUG_TRACE("handling %s request to %s done (static)",
15345 ri->request_method,
15346 path);
15347
15348#endif /* !defined(NO_FILES) */
15349}
15350
15351
15352#if !defined(NO_FILESYSTEMS)
15353static void
15355 const char *path,
15356 struct mg_file *file)
15357{
15358#if !defined(NO_CGI)
15359 int cgi_config_idx, inc, max;
15360#endif
15361
15362 if (!conn || !conn->dom_ctx) {
15363 return;
15364 }
15365
15366#if defined(USE_LUA)
15367 if (match_prefix_strlen(conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
15368 path)
15369 > 0) {
15370 if (is_in_script_path(conn, path)) {
15371 /* Lua server page: an SSI like page containing mostly plain
15372 * html code plus some tags with server generated contents. */
15373 handle_lsp_request(conn, path, file, NULL);
15374 } else {
15375 /* Script was in an illegal path */
15376 mg_send_http_error(conn, 403, "%s", "Forbidden");
15377 }
15378 return;
15379 }
15380
15381 if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS], path)
15382 > 0) {
15383 if (is_in_script_path(conn, path)) {
15384 /* Lua in-server module script: a CGI like script used to
15385 * generate the entire reply. */
15386 mg_exec_lua_script(conn, path, NULL);
15387 } else {
15388 /* Script was in an illegal path */
15389 mg_send_http_error(conn, 403, "%s", "Forbidden");
15390 }
15391 return;
15392 }
15393#endif
15394
15395#if defined(USE_DUKTAPE)
15396 if (match_prefix_strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
15397 path)
15398 > 0) {
15399 if (is_in_script_path(conn, path)) {
15400 /* Call duktape to generate the page */
15401 mg_exec_duktape_script(conn, path);
15402 } else {
15403 /* Script was in an illegal path */
15404 mg_send_http_error(conn, 403, "%s", "Forbidden");
15405 }
15406 return;
15407 }
15408#endif
15409
15410#if !defined(NO_CGI)
15413 for (cgi_config_idx = 0; cgi_config_idx < max; cgi_config_idx += inc) {
15414 if (conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx] != NULL) {
15415 if (match_prefix_strlen(
15416 conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx],
15417 path)
15418 > 0) {
15419 if (is_in_script_path(conn, path)) {
15420 /* CGI scripts may support all HTTP methods */
15421 handle_cgi_request(conn, path, cgi_config_idx);
15422 } else {
15423 /* Script was in an illegal path */
15424 mg_send_http_error(conn, 403, "%s", "Forbidden");
15425 }
15426 return;
15427 }
15428 }
15429 }
15430#endif /* !NO_CGI */
15431
15432 if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path) > 0) {
15433 if (is_in_script_path(conn, path)) {
15434 handle_ssi_file_request(conn, path, file);
15435 } else {
15436 /* Script was in an illegal path */
15437 mg_send_http_error(conn, 403, "%s", "Forbidden");
15438 }
15439 return;
15440 }
15441
15442#if !defined(NO_CACHING)
15443 if ((!conn->in_error_handler) && is_not_modified(conn, &file->stat)) {
15444 /* Send 304 "Not Modified" - this must not send any body data */
15446 return;
15447 }
15448#endif /* !NO_CACHING */
15449
15450 handle_static_file_request(conn, path, file, NULL, NULL);
15451}
15452#endif /* NO_FILESYSTEMS */
15453
15454
15455static void
15457{
15458 unsigned int i;
15459 if (!ctx) {
15460 return;
15461 }
15462
15463 for (i = 0; i < ctx->num_listening_sockets; i++) {
15465#if defined(USE_X_DOM_SOCKET)
15466 /* For unix domain sockets, the socket name represents a file that has
15467 * to be deleted. */
15468 /* See
15469 * https://stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix
15470 */
15471 if ((ctx->listening_sockets[i].lsa.sin.sin_family == AF_UNIX)
15472 && (ctx->listening_sockets[i].sock != INVALID_SOCKET)) {
15474 remove(ctx->listening_sockets[i].lsa.sun.sun_path));
15475 }
15476#endif
15478 }
15480 ctx->listening_sockets = NULL;
15483}
15484
15485
15486/* Valid listening port specification is: [ip_address:]port[s]
15487 * Examples for IPv4: 80, 443s, 127.0.0.1:3128, 192.0.2.3:8080s
15488 * Examples for IPv6: [::]:80, [::1]:80,
15489 * [2001:0db8:7654:3210:FEDC:BA98:7654:3210]:443s
15490 * see https://tools.ietf.org/html/rfc3513#section-2.2
15491 * In order to bind to both, IPv4 and IPv6, you can either add
15492 * both ports using 8080,[::]:8080, or the short form +8080.
15493 * Both forms differ in detail: 8080,[::]:8080 create two sockets,
15494 * one only accepting IPv4 the other only IPv6. +8080 creates
15495 * one socket accepting IPv4 and IPv6. Depending on the IPv6
15496 * environment, they might work differently, or might not work
15497 * at all - it must be tested what options work best in the
15498 * relevant network environment.
15499 */
15500static int
15501parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
15502{
15503 unsigned int a, b, c, d;
15504 unsigned port;
15505 unsigned long portUL;
15506 int ch, len;
15507 const char *cb;
15508 char *endptr;
15509#if defined(USE_IPV6)
15510 char buf[100] = {0};
15511#endif
15512
15513 /* MacOS needs that. If we do not zero it, subsequent bind() will fail.
15514 * Also, all-zeroes in the socket address means binding to all addresses
15515 * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). */
15516 memset(so, 0, sizeof(*so));
15517 so->lsa.sin.sin_family = AF_INET;
15518 *ip_version = 0;
15519
15520 /* Initialize len as invalid. */
15521 port = 0;
15522 len = 0;
15523
15524 /* Test for different ways to format this string */
15525 if (sscanf(vec->ptr,
15526 "%u.%u.%u.%u:%u%n",
15527 &a,
15528 &b,
15529 &c,
15530 &d,
15531 &port,
15532 &len) // NOLINT(cert-err34-c) 'sscanf' used to convert a string
15533 // to an integer value, but function will not report
15534 // conversion errors; consider using 'strtol' instead
15535 == 5) {
15536 /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */
15537 so->lsa.sin.sin_addr.s_addr =
15538 htonl((a << 24) | (b << 16) | (c << 8) | d);
15539 so->lsa.sin.sin_port = htons((uint16_t)port);
15540 *ip_version = 4;
15541
15542#if defined(USE_IPV6)
15543 } else if (sscanf(vec->ptr, "[%49[^]]]:%u%n", buf, &port, &len) == 2
15544 && ((size_t)len <= vec->len)
15545 && mg_inet_pton(
15546 AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6), 0)) {
15547 /* IPv6 address, examples: see above */
15548 /* so->lsa.sin6.sin6_family = AF_INET6; already set by mg_inet_pton
15549 */
15550 so->lsa.sin6.sin6_port = htons((uint16_t)port);
15551 *ip_version = 6;
15552#endif
15553
15554 } else if ((vec->ptr[0] == '+')
15555 && (sscanf(vec->ptr + 1, "%u%n", &port, &len)
15556 == 1)) { // NOLINT(cert-err34-c) 'sscanf' used to convert a
15557 // string to an integer value, but function will not
15558 // report conversion errors; consider using 'strtol'
15559 // instead
15560
15561 /* Port is specified with a +, bind to IPv6 and IPv4, INADDR_ANY */
15562 /* Add 1 to len for the + character we skipped before */
15563 len++;
15564
15565#if defined(USE_IPV6)
15566 /* Set socket family to IPv6, do not use IPV6_V6ONLY */
15567 so->lsa.sin6.sin6_family = AF_INET6;
15568 so->lsa.sin6.sin6_port = htons((uint16_t)port);
15569 *ip_version = 4 + 6;
15570#else
15571 /* Bind to IPv4 only, since IPv6 is not built in. */
15572 so->lsa.sin.sin_port = htons((uint16_t)port);
15573 *ip_version = 4;
15574#endif
15575
15576 } else if (is_valid_port(portUL = strtoul(vec->ptr, &endptr, 0))
15577 && (vec->ptr != endptr)) {
15578 len = (int)(endptr - vec->ptr);
15579 port = (uint16_t)portUL;
15580 /* If only port is specified, bind to IPv4, INADDR_ANY */
15581 so->lsa.sin.sin_port = htons((uint16_t)port);
15582 *ip_version = 4;
15583
15584 } else if ((cb = strchr(vec->ptr, ':')) != NULL) {
15585 /* String could be a hostname. This check algorithm
15586 * will only work for RFC 952 compliant hostnames,
15587 * starting with a letter, containing only letters,
15588 * digits and hyphen ('-'). Newer specs may allow
15589 * more, but this is not guaranteed here, since it
15590 * may interfere with rules for port option lists. */
15591
15592 /* According to RFC 1035, hostnames are restricted to 255 characters
15593 * in total (63 between two dots). */
15594 char hostname[256];
15595 size_t hostnlen = (size_t)(cb - vec->ptr);
15596
15597 if ((hostnlen >= vec->len) || (hostnlen >= sizeof(hostname))) {
15598 /* This would be invalid in any case */
15599 *ip_version = 0;
15600 return 0;
15601 }
15602
15603 mg_strlcpy(hostname, vec->ptr, hostnlen + 1);
15604
15605 if (mg_inet_pton(
15606 AF_INET, hostname, &so->lsa.sin, sizeof(so->lsa.sin), 1)) {
15607 if (sscanf(cb + 1, "%u%n", &port, &len)
15608 == 1) { // NOLINT(cert-err34-c) 'sscanf' used to convert a
15609 // string to an integer value, but function will not
15610 // report conversion errors; consider using 'strtol'
15611 // instead
15612 *ip_version = 4;
15613 so->lsa.sin.sin_port = htons((uint16_t)port);
15614 len += (int)(hostnlen + 1);
15615 } else {
15616 len = 0;
15617 }
15618#if defined(USE_IPV6)
15619 } else if (mg_inet_pton(AF_INET6,
15620 hostname,
15621 &so->lsa.sin6,
15622 sizeof(so->lsa.sin6),
15623 1)) {
15624 if (sscanf(cb + 1, "%u%n", &port, &len) == 1) {
15625 *ip_version = 6;
15626 so->lsa.sin6.sin6_port = htons((uint16_t)port);
15627 len += (int)(hostnlen + 1);
15628 } else {
15629 len = 0;
15630 }
15631#endif
15632 } else {
15633 len = 0;
15634 }
15635
15636#if defined(USE_X_DOM_SOCKET)
15637
15638 } else if (vec->ptr[0] == 'x') {
15639 /* unix (linux) domain socket */
15640 if (vec->len < sizeof(so->lsa.sun.sun_path)) {
15641 len = vec->len;
15642 so->lsa.sun.sun_family = AF_UNIX;
15643 memset(so->lsa.sun.sun_path, 0, sizeof(so->lsa.sun.sun_path));
15644 memcpy(so->lsa.sun.sun_path, (char *)vec->ptr + 1, vec->len - 1);
15645 port = 0;
15646 *ip_version = 99;
15647 } else {
15648 /* String too long */
15649 len = 0;
15650 }
15651#endif
15652
15653 } else {
15654 /* Parsing failure. */
15655 len = 0;
15656 }
15657
15658 /* sscanf and the option splitting code ensure the following condition
15659 * Make sure the port is valid and vector ends with the port, 's' or 'r' */
15660 if ((len > 0) && is_valid_port(port)
15661 && (((size_t)len == vec->len) || (((size_t)len + 1) == vec->len))) {
15662 /* Next character after the port number */
15663 ch = ((size_t)len < vec->len) ? vec->ptr[len] : '\0';
15664 so->is_ssl = (ch == 's');
15665 so->ssl_redir = (ch == 'r');
15666 if ((ch == '\0') || (ch == 's') || (ch == 'r')) {
15667 return 1;
15668 }
15669 }
15670
15671 /* Reset ip_version to 0 if there is an error */
15672 *ip_version = 0;
15673 return 0;
15674}
15675
15676
15677/* Is there any SSL port in use? */
15678static int
15679is_ssl_port_used(const char *ports)
15680{
15681 if (ports) {
15682 /* There are several different allowed syntax variants:
15683 * - "80" for a single port using every network interface
15684 * - "localhost:80" for a single port using only localhost
15685 * - "80,localhost:8080" for two ports, one bound to localhost
15686 * - "80,127.0.0.1:8084,[::1]:8086" for three ports, one bound
15687 * to IPv4 localhost, one to IPv6 localhost
15688 * - "+80" use port 80 for IPv4 and IPv6
15689 * - "+80r,+443s" port 80 (HTTP) is a redirect to port 443 (HTTPS),
15690 * for both: IPv4 and IPv4
15691 * - "+443s,localhost:8080" port 443 (HTTPS) for every interface,
15692 * additionally port 8080 bound to localhost connections
15693 *
15694 * If we just look for 's' anywhere in the string, "localhost:80"
15695 * will be detected as SSL (false positive).
15696 * Looking for 's' after a digit may cause false positives in
15697 * "my24service:8080".
15698 * Looking from 's' backward if there are only ':' and numbers
15699 * before will not work for "24service:8080" (non SSL, port 8080)
15700 * or "24s" (SSL, port 24).
15701 *
15702 * Remark: Initially hostnames were not allowed to start with a
15703 * digit (according to RFC 952), this was allowed later (RFC 1123,
15704 * Section 2.1).
15705 *
15706 * To get this correct, the entire string must be parsed as a whole,
15707 * reading it as a list element for element and parsing with an
15708 * algorithm equivalent to parse_port_string.
15709 *
15710 * In fact, we use local interface names here, not arbitrary
15711 * hostnames, so in most cases the only name will be "localhost".
15712 *
15713 * So, for now, we use this simple algorithm, that may still return
15714 * a false positive in bizarre cases.
15715 */
15716 int i;
15717 int portslen = (int)strlen(ports);
15718 char prevIsNumber = 0;
15719
15720 for (i = 0; i < portslen; i++) {
15721 if (prevIsNumber && (ports[i] == 's' || ports[i] == 'r')) {
15722 return 1;
15723 }
15724 if (ports[i] >= '0' && ports[i] <= '9') {
15725 prevIsNumber = 1;
15726 } else {
15727 prevIsNumber = 0;
15728 }
15729 }
15730 }
15731 return 0;
15732}
15733
15734
15735static int
15737{
15738 const char *list;
15739 int on = 1;
15740#if defined(USE_IPV6)
15741 int off = 0;
15742#endif
15743 struct vec vec;
15744 struct socket so, *ptr;
15745
15746 struct mg_pollfd *pfd;
15747 union usa usa;
15748 socklen_t len;
15749 int ip_version;
15750
15751 int portsTotal = 0;
15752 int portsOk = 0;
15753
15754 const char *opt_txt;
15755 long opt_listen_backlog;
15756
15757 if (!phys_ctx) {
15758 return 0;
15759 }
15760
15761 memset(&so, 0, sizeof(so));
15762 memset(&usa, 0, sizeof(usa));
15763 len = sizeof(usa);
15764 list = phys_ctx->dd.config[LISTENING_PORTS];
15765
15766 while ((list = next_option(list, &vec, NULL)) != NULL) {
15767
15768 portsTotal++;
15769
15770 if (!parse_port_string(&vec, &so, &ip_version)) {
15772 phys_ctx,
15773 "%.*s: invalid port spec (entry %i). Expecting list of: %s",
15774 (int)vec.len,
15775 vec.ptr,
15776 portsTotal,
15777 "[IP_ADDRESS:]PORT[s|r]");
15778 continue;
15779 }
15780
15781#if !defined(NO_SSL)
15782 if (so.is_ssl && phys_ctx->dd.ssl_ctx == NULL) {
15783
15784 mg_cry_ctx_internal(phys_ctx,
15785 "Cannot add SSL socket (entry %i)",
15786 portsTotal);
15787 continue;
15788 }
15789#endif
15790 /* Create socket. */
15791 /* For a list of protocol numbers (e.g., TCP==6) see:
15792 * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
15793 */
15794 if ((so.sock =
15795 socket(so.lsa.sa.sa_family,
15796 SOCK_STREAM,
15797 (ip_version == 99) ? (/* LOCAL */ 0) : (/* TCP */ 6)))
15798 == INVALID_SOCKET) {
15799
15800 mg_cry_ctx_internal(phys_ctx,
15801 "cannot create socket (entry %i)",
15802 portsTotal);
15803 continue;
15804 }
15805
15806#if defined(_WIN32)
15807 /* Windows SO_REUSEADDR lets many procs binds to a
15808 * socket, SO_EXCLUSIVEADDRUSE makes the bind fail
15809 * if someone already has the socket -- DTL */
15810 /* NOTE: If SO_EXCLUSIVEADDRUSE is used,
15811 * Windows might need a few seconds before
15812 * the same port can be used again in the
15813 * same process, so a short Sleep may be
15814 * required between mg_stop and mg_start.
15815 */
15816 if (setsockopt(so.sock,
15817 SOL_SOCKET,
15818 SO_EXCLUSIVEADDRUSE,
15819 (SOCK_OPT_TYPE)&on,
15820 sizeof(on))
15821 != 0) {
15822
15823 /* Set reuse option, but don't abort on errors. */
15825 phys_ctx,
15826 "cannot set socket option SO_EXCLUSIVEADDRUSE (entry %i)",
15827 portsTotal);
15828 }
15829#else
15830 if (setsockopt(so.sock,
15831 SOL_SOCKET,
15832 SO_REUSEADDR,
15833 (SOCK_OPT_TYPE)&on,
15834 sizeof(on))
15835 != 0) {
15836
15837 /* Set reuse option, but don't abort on errors. */
15839 phys_ctx,
15840 "cannot set socket option SO_REUSEADDR (entry %i)",
15841 portsTotal);
15842 }
15843#endif
15844
15845#if defined(USE_X_DOM_SOCKET)
15846 if (ip_version == 99) {
15847 /* Unix domain socket */
15848 } else
15849#endif
15850
15851 if (ip_version > 4) {
15852 /* Could be 6 for IPv6 onlyor 10 (4+6) for IPv4+IPv6 */
15853#if defined(USE_IPV6)
15854 if (ip_version > 6) {
15855 if (so.lsa.sa.sa_family == AF_INET6
15856 && setsockopt(so.sock,
15857 IPPROTO_IPV6,
15858 IPV6_V6ONLY,
15859 (void *)&off,
15860 sizeof(off))
15861 != 0) {
15862
15863 /* Set IPv6 only option, but don't abort on errors. */
15864 mg_cry_ctx_internal(phys_ctx,
15865 "cannot set socket option "
15866 "IPV6_V6ONLY=off (entry %i)",
15867 portsTotal);
15868 }
15869 } else {
15870 if (so.lsa.sa.sa_family == AF_INET6
15871 && setsockopt(so.sock,
15872 IPPROTO_IPV6,
15873 IPV6_V6ONLY,
15874 (void *)&on,
15875 sizeof(on))
15876 != 0) {
15877
15878 /* Set IPv6 only option, but don't abort on errors. */
15879 mg_cry_ctx_internal(phys_ctx,
15880 "cannot set socket option "
15881 "IPV6_V6ONLY=on (entry %i)",
15882 portsTotal);
15883 }
15884 }
15885#else
15886 mg_cry_ctx_internal(phys_ctx, "%s", "IPv6 not available");
15887 closesocket(so.sock);
15888 so.sock = INVALID_SOCKET;
15889 continue;
15890#endif
15891 }
15892
15893 if (so.lsa.sa.sa_family == AF_INET) {
15894
15895 len = sizeof(so.lsa.sin);
15896 if (bind(so.sock, &so.lsa.sa, len) != 0) {
15897 mg_cry_ctx_internal(phys_ctx,
15898 "cannot bind to %.*s: %d (%s)",
15899 (int)vec.len,
15900 vec.ptr,
15901 (int)ERRNO,
15902 strerror(errno));
15903 closesocket(so.sock);
15904 so.sock = INVALID_SOCKET;
15905 continue;
15906 }
15907 }
15908#if defined(USE_IPV6)
15909 else if (so.lsa.sa.sa_family == AF_INET6) {
15910
15911 len = sizeof(so.lsa.sin6);
15912 if (bind(so.sock, &so.lsa.sa, len) != 0) {
15913 mg_cry_ctx_internal(phys_ctx,
15914 "cannot bind to IPv6 %.*s: %d (%s)",
15915 (int)vec.len,
15916 vec.ptr,
15917 (int)ERRNO,
15918 strerror(errno));
15919 closesocket(so.sock);
15920 so.sock = INVALID_SOCKET;
15921 continue;
15922 }
15923 }
15924#endif
15925#if defined(USE_X_DOM_SOCKET)
15926 else if (so.lsa.sa.sa_family == AF_UNIX) {
15927
15928 len = sizeof(so.lsa.sun);
15929 if (bind(so.sock, &so.lsa.sa, len) != 0) {
15930 mg_cry_ctx_internal(phys_ctx,
15931 "cannot bind to unix socket %s: %d (%s)",
15932 so.lsa.sun.sun_path,
15933 (int)ERRNO,
15934 strerror(errno));
15935 closesocket(so.sock);
15936 so.sock = INVALID_SOCKET;
15937 continue;
15938 }
15939 }
15940#endif
15941 else {
15943 phys_ctx,
15944 "cannot bind: address family not supported (entry %i)",
15945 portsTotal);
15946 closesocket(so.sock);
15947 so.sock = INVALID_SOCKET;
15948 continue;
15949 }
15950
15951 opt_txt = phys_ctx->dd.config[LISTEN_BACKLOG_SIZE];
15952 opt_listen_backlog = strtol(opt_txt, NULL, 10);
15953 if ((opt_listen_backlog > INT_MAX) || (opt_listen_backlog < 1)) {
15954 mg_cry_ctx_internal(phys_ctx,
15955 "%s value \"%s\" is invalid",
15957 opt_txt);
15958 closesocket(so.sock);
15959 so.sock = INVALID_SOCKET;
15960 continue;
15961 }
15962
15963 if (listen(so.sock, (int)opt_listen_backlog) != 0) {
15964
15965 mg_cry_ctx_internal(phys_ctx,
15966 "cannot listen to %.*s: %d (%s)",
15967 (int)vec.len,
15968 vec.ptr,
15969 (int)ERRNO,
15970 strerror(errno));
15971 closesocket(so.sock);
15972 so.sock = INVALID_SOCKET;
15973 continue;
15974 }
15975
15976 if ((getsockname(so.sock, &(usa.sa), &len) != 0)
15977 || (usa.sa.sa_family != so.lsa.sa.sa_family)) {
15978
15979 int err = (int)ERRNO;
15980 mg_cry_ctx_internal(phys_ctx,
15981 "call to getsockname failed %.*s: %d (%s)",
15982 (int)vec.len,
15983 vec.ptr,
15984 err,
15985 strerror(errno));
15986 closesocket(so.sock);
15987 so.sock = INVALID_SOCKET;
15988 continue;
15989 }
15990
15991 /* Update lsa port in case of random free ports */
15992#if defined(USE_IPV6)
15993 if (so.lsa.sa.sa_family == AF_INET6) {
15994 so.lsa.sin6.sin6_port = usa.sin6.sin6_port;
15995 } else
15996#endif
15997 {
15998 so.lsa.sin.sin_port = usa.sin.sin_port;
15999 }
16000
16001 if ((ptr = (struct socket *)
16003 (phys_ctx->num_listening_sockets + 1)
16004 * sizeof(phys_ctx->listening_sockets[0]),
16005 phys_ctx))
16006 == NULL) {
16007
16008 mg_cry_ctx_internal(phys_ctx, "%s", "Out of memory");
16009 closesocket(so.sock);
16010 so.sock = INVALID_SOCKET;
16011 continue;
16012 }
16013
16014 if ((pfd = (struct mg_pollfd *)
16016 (phys_ctx->num_listening_sockets + 1)
16017 * sizeof(phys_ctx->listening_socket_fds[0]),
16018 phys_ctx))
16019 == NULL) {
16020
16021 mg_cry_ctx_internal(phys_ctx, "%s", "Out of memory");
16022 closesocket(so.sock);
16023 so.sock = INVALID_SOCKET;
16024 mg_free(ptr);
16025 continue;
16026 }
16027
16028 set_close_on_exec(so.sock, NULL, phys_ctx);
16029 phys_ctx->listening_sockets = ptr;
16030 phys_ctx->listening_sockets[phys_ctx->num_listening_sockets] = so;
16031 phys_ctx->listening_socket_fds = pfd;
16032 phys_ctx->num_listening_sockets++;
16033 portsOk++;
16034 }
16035
16036 if (portsOk != portsTotal) {
16038 portsOk = 0;
16039 }
16040
16041 return portsOk;
16042}
16043
16044
16045static const char *
16046header_val(const struct mg_connection *conn, const char *header)
16047{
16048 const char *header_value;
16049
16050 if ((header_value = mg_get_header(conn, header)) == NULL) {
16051 return "-";
16052 } else {
16053 return header_value;
16054 }
16055}
16056
16057
16058#if defined(MG_EXTERNAL_FUNCTION_log_access)
16059#include "external_log_access.inl"
16060#elif !defined(NO_FILESYSTEMS)
16061
16062static void
16063log_access(const struct mg_connection *conn)
16064{
16065 const struct mg_request_info *ri;
16066 struct mg_file fi;
16067 char date[64], src_addr[IP_ADDR_STR_LEN];
16068#if defined(REENTRANT_TIME)
16069 struct tm _tm;
16070 struct tm *tm = &_tm;
16071#else
16072 struct tm *tm;
16073#endif
16074
16075 const char *referer;
16076 const char *user_agent;
16077
16078 char log_buf[4096];
16079
16080 if (!conn || !conn->dom_ctx) {
16081 return;
16082 }
16083
16084 /* Set log message to "empty" */
16085 log_buf[0] = 0;
16086
16087#if defined(USE_LUA)
16088 if (conn->phys_ctx->lua_bg_log_available) {
16089 int ret;
16090 struct mg_context *ctx = conn->phys_ctx;
16091 lua_State *lstate = (lua_State *)ctx->lua_background_state;
16092 pthread_mutex_lock(&ctx->lua_bg_mutex);
16093 /* call "log()" in Lua */
16094 lua_getglobal(lstate, "log");
16095 prepare_lua_request_info_inner(conn, lstate);
16096 push_lua_response_log_data(conn, lstate);
16097
16098 ret = lua_pcall(lstate, /* args */ 2, /* results */ 1, 0);
16099 if (ret == 0) {
16100 int t = lua_type(lstate, -1);
16101 if (t == LUA_TBOOLEAN) {
16102 if (lua_toboolean(lstate, -1) == 0) {
16103 /* log() returned false: do not log */
16104 pthread_mutex_unlock(&ctx->lua_bg_mutex);
16105 return;
16106 }
16107 /* log returned true: continue logging */
16108 } else if (t == LUA_TSTRING) {
16109 size_t len;
16110 const char *txt = lua_tolstring(lstate, -1, &len);
16111 if ((len == 0) || (*txt == 0)) {
16112 /* log() returned empty string: do not log */
16113 pthread_mutex_unlock(&ctx->lua_bg_mutex);
16114 return;
16115 }
16116 /* Copy test from Lua into log_buf */
16117 if (len >= sizeof(log_buf)) {
16118 len = sizeof(log_buf) - 1;
16119 }
16120 memcpy(log_buf, txt, len);
16121 log_buf[len] = 0;
16122 }
16123 } else {
16124 lua_cry(conn, ret, lstate, "lua_background_script", "log");
16125 }
16126 pthread_mutex_unlock(&ctx->lua_bg_mutex);
16127 }
16128#endif
16129
16130 if (conn->dom_ctx->config[ACCESS_LOG_FILE] != NULL) {
16131 if (mg_fopen(conn,
16134 &fi)
16135 == 0) {
16136 fi.access.fp = NULL;
16137 }
16138 } else {
16139 fi.access.fp = NULL;
16140 }
16141
16142 /* Log is written to a file and/or a callback. If both are not set,
16143 * executing the rest of the function is pointless. */
16144 if ((fi.access.fp == NULL)
16145 && (conn->phys_ctx->callbacks.log_access == NULL)) {
16146 return;
16147 }
16148
16149 /* If we did not get a log message from Lua, create it here. */
16150 if (!log_buf[0]) {
16151#if defined(REENTRANT_TIME)
16152 localtime_r(&conn->conn_birth_time, tm);
16153#else
16154 tm = localtime(&conn->conn_birth_time);
16155#endif
16156 if (tm != NULL) {
16157 strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", tm);
16158 } else {
16159 mg_strlcpy(date, "01/Jan/1970:00:00:00 +0000", sizeof(date));
16160 }
16161
16162 ri = &conn->request_info;
16163
16164 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
16165 referer = header_val(conn, "Referer");
16166 user_agent = header_val(conn, "User-Agent");
16167
16168 mg_snprintf(conn,
16169 NULL, /* Ignore truncation in access log */
16170 log_buf,
16171 sizeof(log_buf),
16172 "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d %" INT64_FMT
16173 " %s %s",
16174 src_addr,
16175 (ri->remote_user == NULL) ? "-" : ri->remote_user,
16176 date,
16177 ri->request_method ? ri->request_method : "-",
16178 ri->request_uri ? ri->request_uri : "-",
16179 ri->query_string ? "?" : "",
16180 ri->query_string ? ri->query_string : "",
16181 ri->http_version,
16182 conn->status_code,
16183 conn->num_bytes_sent,
16184 referer,
16185 user_agent);
16186 }
16187
16188 /* Here we have a log message in log_buf. Call the callback */
16189 if (conn->phys_ctx->callbacks.log_access) {
16190 if (conn->phys_ctx->callbacks.log_access(conn, log_buf)) {
16191 /* do not log if callback returns non-zero */
16192 if (fi.access.fp) {
16193 mg_fclose(&fi.access);
16194 }
16195 return;
16196 }
16197 }
16198
16199 /* Store in file */
16200 if (fi.access.fp) {
16201 int ok = 1;
16202 flockfile(fi.access.fp);
16203 if (fprintf(fi.access.fp, "%s\n", log_buf) < 1) {
16204 ok = 0;
16205 }
16206 if (fflush(fi.access.fp) != 0) {
16207 ok = 0;
16208 }
16209 funlockfile(fi.access.fp);
16210 if (mg_fclose(&fi.access) != 0) {
16211 ok = 0;
16212 }
16213 if (!ok) {
16214 mg_cry_internal(conn,
16215 "Error writing log file %s",
16217 }
16218 }
16219}
16220#else
16221#error "Either enable filesystems or provide a custom log_access implementation"
16222#endif /* Externally provided function */
16223
16224
16225/* Verify given socket address against the ACL.
16226 * Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
16227 */
16228static int
16229check_acl(struct mg_context *phys_ctx, const union usa *sa)
16230{
16231 int allowed, flag, matched;
16232 struct vec vec;
16233
16234 if (phys_ctx) {
16235 const char *list = phys_ctx->dd.config[ACCESS_CONTROL_LIST];
16236
16237 /* If any ACL is set, deny by default */
16238 allowed = (list == NULL) ? '+' : '-';
16239
16240 while ((list = next_option(list, &vec, NULL)) != NULL) {
16241 flag = vec.ptr[0];
16242 matched = -1;
16243 if ((vec.len > 0) && ((flag == '+') || (flag == '-'))) {
16244 vec.ptr++;
16245 vec.len--;
16246 matched = parse_match_net(&vec, sa, 1);
16247 }
16248 if (matched < 0) {
16249 mg_cry_ctx_internal(phys_ctx,
16250 "%s: subnet must be [+|-]IP-addr[/x]",
16251 __func__);
16252 return -1;
16253 }
16254 if (matched) {
16255 allowed = flag;
16256 }
16257 }
16258
16259 return allowed == '+';
16260 }
16261 return -1;
16262}
16263
16264
16265#if !defined(_WIN32) && !defined(__ZEPHYR__)
16266static int
16268{
16269 int success = 0;
16270
16271 if (phys_ctx) {
16272 /* We are currently running as curr_uid. */
16273 const uid_t curr_uid = getuid();
16274 /* If set, we want to run as run_as_user. */
16275 const char *run_as_user = phys_ctx->dd.config[RUN_AS_USER];
16276 const struct passwd *to_pw = NULL;
16277
16278 if ((run_as_user != NULL) && (to_pw = getpwnam(run_as_user)) == NULL) {
16279 /* run_as_user does not exist on the system. We can't proceed
16280 * further. */
16281 mg_cry_ctx_internal(phys_ctx,
16282 "%s: unknown user [%s]",
16283 __func__,
16284 run_as_user);
16285 } else if ((run_as_user == NULL) || (curr_uid == to_pw->pw_uid)) {
16286 /* There was either no request to change user, or we're already
16287 * running as run_as_user. Nothing else to do.
16288 */
16289 success = 1;
16290 } else {
16291 /* Valid change request. */
16292 if (setgid(to_pw->pw_gid) == -1) {
16293 mg_cry_ctx_internal(phys_ctx,
16294 "%s: setgid(%s): %s",
16295 __func__,
16296 run_as_user,
16297 strerror(errno));
16298 } else if (setgroups(0, NULL) == -1) {
16299 mg_cry_ctx_internal(phys_ctx,
16300 "%s: setgroups(): %s",
16301 __func__,
16302 strerror(errno));
16303 } else if (setuid(to_pw->pw_uid) == -1) {
16304 mg_cry_ctx_internal(phys_ctx,
16305 "%s: setuid(%s): %s",
16306 __func__,
16307 run_as_user,
16308 strerror(errno));
16309 } else {
16310 success = 1;
16311 }
16312 }
16313 }
16314
16315 return success;
16316}
16317#endif /* !_WIN32 */
16318
16319
16320static void
16321tls_dtor(void *key)
16322{
16323 struct mg_workerTLS *tls = (struct mg_workerTLS *)key;
16324 /* key == pthread_getspecific(sTlsKey); */
16325
16326 if (tls) {
16327 if (tls->is_master == 2) {
16328 tls->is_master = -3; /* Mark memory as dead */
16329 mg_free(tls);
16330 }
16331 }
16332 pthread_setspecific(sTlsKey, NULL);
16333}
16334
16335
16336#if defined(USE_MBEDTLS)
16337/* Check if SSL is required.
16338 * If so, set up ctx->ssl_ctx pointer. */
16339static int
16340mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
16341{
16342 if (!phys_ctx) {
16343 return 0;
16344 }
16345
16346 if (!dom_ctx) {
16347 dom_ctx = &(phys_ctx->dd);
16348 }
16349
16350 if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
16351 /* No SSL port is set. No need to setup SSL. */
16352 return 1;
16353 }
16354
16355 dom_ctx->ssl_ctx = (SSL_CTX *)mg_calloc(1, sizeof(*dom_ctx->ssl_ctx));
16356 if (dom_ctx->ssl_ctx == NULL) {
16357 fprintf(stderr, "ssl_ctx malloc failed\n");
16358 return 0;
16359 }
16360
16361 return mbed_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE])
16362 == 0
16363 ? 1
16364 : 0;
16365}
16366
16367#elif !defined(NO_SSL)
16368
16369static int ssl_use_pem_file(struct mg_context *phys_ctx,
16370 struct mg_domain_context *dom_ctx,
16371 const char *pem,
16372 const char *chain);
16373static const char *ssl_error(void);
16374
16375
16376static int
16378{
16379 struct stat cert_buf;
16380 int64_t t = 0;
16381 const char *pem;
16382 const char *chain;
16383 int should_verify_peer;
16384
16385 if ((pem = conn->dom_ctx->config[SSL_CERTIFICATE]) == NULL) {
16386 /* If pem is NULL and conn->phys_ctx->callbacks.init_ssl is not,
16387 * refresh_trust still can not work. */
16388 return 0;
16389 }
16390 chain = conn->dom_ctx->config[SSL_CERTIFICATE_CHAIN];
16391 if (chain == NULL) {
16392 /* pem is not NULL here */
16393 chain = pem;
16394 }
16395 if (*chain == 0) {
16396 chain = NULL;
16397 }
16398
16399 if (stat(pem, &cert_buf) != -1) {
16400 t = (int64_t)cert_buf.st_mtime;
16401 }
16402
16404 if ((t != 0) && (conn->dom_ctx->ssl_cert_last_mtime != t)) {
16405 conn->dom_ctx->ssl_cert_last_mtime = t;
16406
16407 should_verify_peer = 0;
16408 if (conn->dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) {
16410 == 0) {
16411 should_verify_peer = 1;
16413 "optional")
16414 == 0) {
16415 should_verify_peer = 1;
16416 }
16417 }
16418
16419 if (should_verify_peer) {
16420 char *ca_path = conn->dom_ctx->config[SSL_CA_PATH];
16421 char *ca_file = conn->dom_ctx->config[SSL_CA_FILE];
16422 if (SSL_CTX_load_verify_locations(conn->dom_ctx->ssl_ctx,
16423 ca_file,
16424 ca_path)
16425 != 1) {
16428 conn->phys_ctx,
16429 "SSL_CTX_load_verify_locations error: %s "
16430 "ssl_verify_peer requires setting "
16431 "either ssl_ca_path or ssl_ca_file. Is any of them "
16432 "present in "
16433 "the .conf file?",
16434 ssl_error());
16435 return 0;
16436 }
16437 }
16438
16439 if (ssl_use_pem_file(conn->phys_ctx, conn->dom_ctx, pem, chain) == 0) {
16441 return 0;
16442 }
16443 }
16445
16446 return 1;
16447}
16448
16449#if defined(OPENSSL_API_1_1)
16450#else
16451static pthread_mutex_t *ssl_mutexes;
16452#endif /* OPENSSL_API_1_1 */
16453
16454static int
16456 int (*func)(SSL *),
16457 const struct mg_client_options *client_options)
16458{
16459 int ret, err;
16460 int short_trust;
16461 unsigned timeout = 1024;
16462 unsigned i;
16463
16464 if (!conn) {
16465 return 0;
16466 }
16467
16468 short_trust =
16469 (conn->dom_ctx->config[SSL_SHORT_TRUST] != NULL)
16470 && (mg_strcasecmp(conn->dom_ctx->config[SSL_SHORT_TRUST], "yes") == 0);
16471
16472 if (short_trust) {
16473 int trust_ret = refresh_trust(conn);
16474 if (!trust_ret) {
16475 return trust_ret;
16476 }
16477 }
16478
16480 conn->ssl = SSL_new(conn->dom_ctx->ssl_ctx);
16482 if (conn->ssl == NULL) {
16483 mg_cry_internal(conn, "sslize error: %s", ssl_error());
16484 OPENSSL_REMOVE_THREAD_STATE();
16485 return 0;
16486 }
16487 SSL_set_app_data(conn->ssl, (char *)conn);
16488
16489 ret = SSL_set_fd(conn->ssl, conn->client.sock);
16490 if (ret != 1) {
16491 mg_cry_internal(conn, "sslize error: %s", ssl_error());
16492 SSL_free(conn->ssl);
16493 conn->ssl = NULL;
16494 OPENSSL_REMOVE_THREAD_STATE();
16495 return 0;
16496 }
16497
16498 if (client_options) {
16499 if (client_options->host_name) {
16500 SSL_set_tlsext_host_name(conn->ssl, client_options->host_name);
16501 }
16502 }
16503
16504 /* Reuse the request timeout for the SSL_Accept/SSL_connect timeout */
16505 if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
16506 /* NOTE: The loop below acts as a back-off, so we can end
16507 * up sleeping for more (or less) than the REQUEST_TIMEOUT. */
16508 int to = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]);
16509 if (to >= 0) {
16510 timeout = (unsigned)to;
16511 }
16512 }
16513
16514 /* SSL functions may fail and require to be called again:
16515 * see https://www.openssl.org/docs/manmaster/ssl/SSL_get_error.html
16516 * Here "func" could be SSL_connect or SSL_accept. */
16517 for (i = 0; i <= timeout; i += 50) {
16518 ERR_clear_error();
16519 /* conn->dom_ctx may be changed here (see ssl_servername_callback) */
16520 ret = func(conn->ssl);
16521 if (ret != 1) {
16522 err = SSL_get_error(conn->ssl, ret);
16523 if ((err == SSL_ERROR_WANT_CONNECT)
16524 || (err == SSL_ERROR_WANT_ACCEPT)
16525 || (err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)
16526 || (err == SSL_ERROR_WANT_X509_LOOKUP)) {
16527 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
16528 /* Don't wait if the server is going to be stopped. */
16529 break;
16530 }
16531 if (err == SSL_ERROR_WANT_X509_LOOKUP) {
16532 /* Simply retry the function call. */
16533 mg_sleep(50);
16534 } else {
16535 /* Need to retry the function call "later".
16536 * See https://linux.die.net/man/3/ssl_get_error
16537 * This is typical for non-blocking sockets. */
16538 struct mg_pollfd pfd;
16539 int pollres;
16540 pfd.fd = conn->client.sock;
16541 pfd.events = ((err == SSL_ERROR_WANT_CONNECT)
16542 || (err == SSL_ERROR_WANT_WRITE))
16543 ? POLLOUT
16544 : POLLIN;
16545 pollres =
16546 mg_poll(&pfd, 1, 50, &(conn->phys_ctx->stop_flag));
16547 if (pollres < 0) {
16548 /* Break if error occurred (-1)
16549 * or server shutdown (-2) */
16550 break;
16551 }
16552 }
16553
16554 } else if (err == SSL_ERROR_SYSCALL) {
16555 /* This is an IO error. Look at errno. */
16556 mg_cry_internal(conn, "SSL syscall error %i", ERRNO);
16557 break;
16558
16559 } else {
16560 /* This is an SSL specific error, e.g. SSL_ERROR_SSL */
16561 mg_cry_internal(conn, "sslize error: %s", ssl_error());
16562 break;
16563 }
16564
16565 } else {
16566 /* success */
16567 break;
16568 }
16569 }
16570 ERR_clear_error();
16571
16572 if (ret != 1) {
16573 SSL_free(conn->ssl);
16574 conn->ssl = NULL;
16575 OPENSSL_REMOVE_THREAD_STATE();
16576 return 0;
16577 }
16578
16579 return 1;
16580}
16581
16582
16583/* Return OpenSSL error message (from CRYPTO lib) */
16584static const char *
16586{
16587 unsigned long err;
16588 err = ERR_get_error();
16589 return ((err == 0) ? "" : ERR_error_string(err, NULL));
16590}
16591
16592
16593static int
16594hexdump2string(void *mem, int memlen, char *buf, int buflen)
16595{
16596 int i;
16597 const char hexdigit[] = "0123456789abcdef";
16598
16599 if ((memlen <= 0) || (buflen <= 0)) {
16600 return 0;
16601 }
16602 if (buflen < (3 * memlen)) {
16603 return 0;
16604 }
16605
16606 for (i = 0; i < memlen; i++) {
16607 if (i > 0) {
16608 buf[3 * i - 1] = ' ';
16609 }
16610 buf[3 * i] = hexdigit[(((uint8_t *)mem)[i] >> 4) & 0xF];
16611 buf[3 * i + 1] = hexdigit[((uint8_t *)mem)[i] & 0xF];
16612 }
16613 buf[3 * memlen - 1] = 0;
16614
16615 return 1;
16616}
16617
16618
16619static int
16621 struct mg_client_cert *client_cert)
16622{
16623 X509 *cert = SSL_get_peer_certificate(conn->ssl);
16624 if (cert) {
16625 char str_buf[1024];
16626 unsigned char buf[256];
16627 char *str_serial = NULL;
16628 unsigned int ulen;
16629 int ilen;
16630 unsigned char *tmp_buf;
16631 unsigned char *tmp_p;
16632
16633 /* Handle to algorithm used for fingerprint */
16634 const EVP_MD *digest = EVP_get_digestbyname("sha1");
16635
16636 /* Get Subject and issuer */
16637 X509_NAME *subj = X509_get_subject_name(cert);
16638 X509_NAME *iss = X509_get_issuer_name(cert);
16639
16640 /* Get serial number */
16641 ASN1_INTEGER *serial = X509_get_serialNumber(cert);
16642
16643 /* Translate serial number to a hex string */
16644 BIGNUM *serial_bn = ASN1_INTEGER_to_BN(serial, NULL);
16645 if (serial_bn) {
16646 str_serial = BN_bn2hex(serial_bn);
16647 BN_free(serial_bn);
16648 }
16649 client_cert->serial =
16650 str_serial ? mg_strdup_ctx(str_serial, conn->phys_ctx) : NULL;
16651
16652 /* Translate subject and issuer to a string */
16653 (void)X509_NAME_oneline(subj, str_buf, (int)sizeof(str_buf));
16654 client_cert->subject = mg_strdup_ctx(str_buf, conn->phys_ctx);
16655 (void)X509_NAME_oneline(iss, str_buf, (int)sizeof(str_buf));
16656 client_cert->issuer = mg_strdup_ctx(str_buf, conn->phys_ctx);
16657
16658 /* Calculate SHA1 fingerprint and store as a hex string */
16659 ulen = 0;
16660
16661 /* ASN1_digest is deprecated. Do the calculation manually,
16662 * using EVP_Digest. */
16663 ilen = i2d_X509(cert, NULL);
16664 tmp_buf = (ilen > 0)
16665 ? (unsigned char *)mg_malloc_ctx((unsigned)ilen + 1,
16666 conn->phys_ctx)
16667 : NULL;
16668 if (tmp_buf) {
16669 tmp_p = tmp_buf;
16670 (void)i2d_X509(cert, &tmp_p);
16671 if (!EVP_Digest(
16672 tmp_buf, (unsigned)ilen, buf, &ulen, digest, NULL)) {
16673 ulen = 0;
16674 }
16675 mg_free(tmp_buf);
16676 }
16677
16678 if (!hexdump2string(buf, (int)ulen, str_buf, (int)sizeof(str_buf))) {
16679 *str_buf = 0;
16680 }
16681 client_cert->finger = mg_strdup_ctx(str_buf, conn->phys_ctx);
16682
16683 client_cert->peer_cert = (void *)cert;
16684
16685 /* Strings returned from bn_bn2hex must be freed using OPENSSL_free,
16686 * see https://linux.die.net/man/3/bn_bn2hex */
16687 OPENSSL_free(str_serial);
16688 return 1;
16689 }
16690 return 0;
16691}
16692
16693
16694#if defined(OPENSSL_API_1_1)
16695#else
16696static void
16697ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
16698{
16699 (void)line;
16700 (void)file;
16701
16702 if (mode & 1) {
16703 /* 1 is CRYPTO_LOCK */
16704 (void)pthread_mutex_lock(&ssl_mutexes[mutex_num]);
16705 } else {
16706 (void)pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
16707 }
16708}
16709#endif /* OPENSSL_API_1_1 */
16710
16711
16712#if !defined(NO_SSL_DL)
16713/* Load a DLL/Shared Object with a TLS/SSL implementation. */
16714static void *
16715load_tls_dll(char *ebuf,
16716 size_t ebuf_len,
16717 const char *dll_name,
16718 struct ssl_func *sw,
16719 int *feature_missing)
16720{
16721 union {
16722 void *p;
16723 void (*fp)(void);
16724 } u;
16725 void *dll_handle;
16726 struct ssl_func *fp;
16727 int ok;
16728 int truncated = 0;
16729
16730 if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
16732 NULL, /* No truncation check for ebuf */
16733 ebuf,
16734 ebuf_len,
16735 "%s: cannot load %s",
16736 __func__,
16737 dll_name);
16738 return NULL;
16739 }
16740
16741 ok = 1;
16742 for (fp = sw; fp->name != NULL; fp++) {
16743#if defined(_WIN32)
16744 /* GetProcAddress() returns pointer to function */
16745 u.fp = (void (*)(void))dlsym(dll_handle, fp->name);
16746#else
16747 /* dlsym() on UNIX returns void *. ISO C forbids casts of data
16748 * pointers to function pointers. We need to use a union to make a
16749 * cast. */
16750 u.p = dlsym(dll_handle, fp->name);
16751#endif /* _WIN32 */
16752
16753 /* Set pointer (might be NULL) */
16754 fp->ptr = u.fp;
16755
16756 if (u.fp == NULL) {
16757 DEBUG_TRACE("Missing function: %s\n", fp->name);
16758 if (feature_missing) {
16759 feature_missing[fp->required]++;
16760 }
16761 if (fp->required == TLS_Mandatory) {
16762 /* Mandatory function is missing */
16763 if (ok) {
16764 /* This is the first missing function.
16765 * Create a new error message. */
16767 &truncated,
16768 ebuf,
16769 ebuf_len,
16770 "%s: %s: cannot find %s",
16771 __func__,
16772 dll_name,
16773 fp->name);
16774 ok = 0;
16775 } else {
16776 /* This is yet anothermissing function.
16777 * Append existing error message. */
16778 size_t cur_len = strlen(ebuf);
16779 if (!truncated && ((ebuf_len - cur_len) > 3)) {
16781 &truncated,
16782 ebuf + cur_len,
16783 ebuf_len - cur_len - 3,
16784 ", %s",
16785 fp->name);
16786 if (truncated) {
16787 /* If truncated, add "..." */
16788 strcat(ebuf, "...");
16789 }
16790 }
16791 }
16792 }
16793 }
16794 }
16795
16796 if (!ok) {
16797 (void)dlclose(dll_handle);
16798 return NULL;
16799 }
16800
16801 return dll_handle;
16802}
16803
16804
16805static void *ssllib_dll_handle; /* Store the ssl library handle. */
16806static void *cryptolib_dll_handle; /* Store the crypto library handle. */
16807
16808#endif /* NO_SSL_DL */
16809
16810
16811#if defined(SSL_ALREADY_INITIALIZED)
16812static volatile ptrdiff_t cryptolib_users =
16813 1; /* Reference counter for crypto library. */
16814#else
16815static volatile ptrdiff_t cryptolib_users =
16816 0; /* Reference counter for crypto library. */
16817#endif
16818
16819
16820static int
16821initialize_openssl(char *ebuf, size_t ebuf_len)
16822{
16823#if !defined(OPENSSL_API_1_1) && !defined(OPENSSL_API_3_0)
16824 int i, num_locks;
16825 size_t size;
16826#endif
16827
16828 if (ebuf_len > 0) {
16829 ebuf[0] = 0;
16830 }
16831
16832#if !defined(NO_SSL_DL)
16833 if (!cryptolib_dll_handle) {
16834 memset(tls_feature_missing, 0, sizeof(tls_feature_missing));
16836 ebuf, ebuf_len, CRYPTO_LIB, crypto_sw, tls_feature_missing);
16837 if (!cryptolib_dll_handle) {
16839 NULL, /* No truncation check for ebuf */
16840 ebuf,
16841 ebuf_len,
16842 "%s: error loading library %s",
16843 __func__,
16844 CRYPTO_LIB);
16845 DEBUG_TRACE("%s", ebuf);
16846 return 0;
16847 }
16848 }
16849#endif /* NO_SSL_DL */
16850
16851 if (mg_atomic_inc(&cryptolib_users) > 1) {
16852 return 1;
16853 }
16854
16855#if !defined(OPENSSL_API_1_1) && !defined(OPENSSL_API_3_0)
16856 /* Initialize locking callbacks, needed for thread safety.
16857 * http://www.openssl.org/support/faq.html#PROG1
16858 */
16859 num_locks = CRYPTO_num_locks();
16860 if (num_locks < 0) {
16861 num_locks = 0;
16862 }
16863 size = sizeof(pthread_mutex_t) * ((size_t)(num_locks));
16864
16865 /* allocate mutex array, if required */
16866 if (num_locks == 0) {
16867 /* No mutex array required */
16868 ssl_mutexes = NULL;
16869 } else {
16870 /* Mutex array required - allocate it */
16871 ssl_mutexes = (pthread_mutex_t *)mg_malloc(size);
16872
16873 /* Check OOM */
16874 if (ssl_mutexes == NULL) {
16876 NULL, /* No truncation check for ebuf */
16877 ebuf,
16878 ebuf_len,
16879 "%s: cannot allocate mutexes: %s",
16880 __func__,
16881 ssl_error());
16882 DEBUG_TRACE("%s", ebuf);
16883 return 0;
16884 }
16885
16886 /* initialize mutex array */
16887 for (i = 0; i < num_locks; i++) {
16888 if (0 != pthread_mutex_init(&ssl_mutexes[i], &pthread_mutex_attr)) {
16890 NULL, /* No truncation check for ebuf */
16891 ebuf,
16892 ebuf_len,
16893 "%s: error initializing mutex %i of %i",
16894 __func__,
16895 i,
16896 num_locks);
16897 DEBUG_TRACE("%s", ebuf);
16899 return 0;
16900 }
16901 }
16902 }
16903
16904 CRYPTO_set_locking_callback(&ssl_locking_callback);
16905 CRYPTO_set_id_callback(&mg_current_thread_id);
16906#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
16907
16908#if !defined(NO_SSL_DL)
16909 if (!ssllib_dll_handle) {
16911 load_tls_dll(ebuf, ebuf_len, SSL_LIB, ssl_sw, tls_feature_missing);
16912 if (!ssllib_dll_handle) {
16913#if !defined(OPENSSL_API_1_1)
16915#endif
16916 DEBUG_TRACE("%s", ebuf);
16917 return 0;
16918 }
16919 }
16920#endif /* NO_SSL_DL */
16921
16922#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \
16923 && !defined(NO_SSL_DL)
16924 /* Initialize SSL library */
16925 OPENSSL_init_ssl(0, NULL);
16926 OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS
16927 | OPENSSL_INIT_LOAD_CRYPTO_STRINGS,
16928 NULL);
16929#else
16930 /* Initialize SSL library */
16931 SSL_library_init();
16932 SSL_load_error_strings();
16933#endif
16934
16935 return 1;
16936}
16937
16938
16939static int
16941 struct mg_domain_context *dom_ctx,
16942 const char *pem,
16943 const char *chain)
16944{
16945 if (SSL_CTX_use_certificate_file(dom_ctx->ssl_ctx, pem, 1) == 0) {
16946 mg_cry_ctx_internal(phys_ctx,
16947 "%s: cannot open certificate file %s: %s",
16948 __func__,
16949 pem,
16950 ssl_error());
16951 return 0;
16952 }
16953
16954 /* could use SSL_CTX_set_default_passwd_cb_userdata */
16955 if (SSL_CTX_use_PrivateKey_file(dom_ctx->ssl_ctx, pem, 1) == 0) {
16956 mg_cry_ctx_internal(phys_ctx,
16957 "%s: cannot open private key file %s: %s",
16958 __func__,
16959 pem,
16960 ssl_error());
16961 return 0;
16962 }
16963
16964 if (SSL_CTX_check_private_key(dom_ctx->ssl_ctx) == 0) {
16965 mg_cry_ctx_internal(phys_ctx,
16966 "%s: certificate and private key do not match: %s",
16967 __func__,
16968 pem);
16969 return 0;
16970 }
16971
16972 /* In contrast to OpenSSL, wolfSSL does not support certificate
16973 * chain files that contain private keys and certificates in
16974 * SSL_CTX_use_certificate_chain_file.
16975 * The CivetWeb-Server used pem-Files that contained both information.
16976 * In order to make wolfSSL work, it is split in two files.
16977 * One file that contains key and certificate used by the server and
16978 * an optional chain file for the ssl stack.
16979 */
16980 if (chain) {
16981 if (SSL_CTX_use_certificate_chain_file(dom_ctx->ssl_ctx, chain) == 0) {
16982 mg_cry_ctx_internal(phys_ctx,
16983 "%s: cannot use certificate chain file %s: %s",
16984 __func__,
16985 chain,
16986 ssl_error());
16987 return 0;
16988 }
16989 }
16990 return 1;
16991}
16992
16993
16994#if defined(OPENSSL_API_1_1)
16995static unsigned long
16996ssl_get_protocol(int version_id)
16997{
16998 long unsigned ret = (long unsigned)SSL_OP_ALL;
16999 if (version_id > 0)
17000 ret |= SSL_OP_NO_SSLv2;
17001 if (version_id > 1)
17002 ret |= SSL_OP_NO_SSLv3;
17003 if (version_id > 2)
17004 ret |= SSL_OP_NO_TLSv1;
17005 if (version_id > 3)
17006 ret |= SSL_OP_NO_TLSv1_1;
17007 if (version_id > 4)
17008 ret |= SSL_OP_NO_TLSv1_2;
17009#if defined(SSL_OP_NO_TLSv1_3)
17010 if (version_id > 5)
17011 ret |= SSL_OP_NO_TLSv1_3;
17012#endif
17013 return ret;
17014}
17015#else
17016static long
17017ssl_get_protocol(int version_id)
17018{
17019 unsigned long ret = (unsigned long)SSL_OP_ALL;
17020 if (version_id > 0)
17021 ret |= SSL_OP_NO_SSLv2;
17022 if (version_id > 1)
17023 ret |= SSL_OP_NO_SSLv3;
17024 if (version_id > 2)
17025 ret |= SSL_OP_NO_TLSv1;
17026 if (version_id > 3)
17027 ret |= SSL_OP_NO_TLSv1_1;
17028 if (version_id > 4)
17029 ret |= SSL_OP_NO_TLSv1_2;
17030#if defined(SSL_OP_NO_TLSv1_3)
17031 if (version_id > 5)
17032 ret |= SSL_OP_NO_TLSv1_3;
17033#endif
17034 return (long)ret;
17035}
17036#endif /* OPENSSL_API_1_1 */
17037
17038
17039/* SSL callback documentation:
17040 * https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_info_callback.html
17041 * https://wiki.openssl.org/index.php/Manual:SSL_CTX_set_info_callback(3)
17042 * https://linux.die.net/man/3/ssl_set_info_callback */
17043/* Note: There is no "const" for the first argument in the documentation
17044 * examples, however some (maybe most, but not all) headers of OpenSSL
17045 * versions / OpenSSL compatibility layers have it. Having a different
17046 * definition will cause a warning in C and an error in C++. Use "const SSL
17047 * *", while automatic conversion from "SSL *" works for all compilers,
17048 * but not other way around */
17049static void
17050ssl_info_callback(const SSL *ssl, int what, int ret)
17051{
17052 (void)ret;
17053
17054 if (what & SSL_CB_HANDSHAKE_START) {
17055 SSL_get_app_data(ssl);
17056 }
17057 if (what & SSL_CB_HANDSHAKE_DONE) {
17058 /* TODO: check for openSSL 1.1 */
17059 //#define SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS 0x0001
17060 // ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
17061 }
17062}
17063
17064
17065static int
17066ssl_servername_callback(SSL *ssl, int *ad, void *arg)
17067{
17068#if defined(GCC_DIAGNOSTIC)
17069#pragma GCC diagnostic push
17070#pragma GCC diagnostic ignored "-Wcast-align"
17071#endif /* defined(GCC_DIAGNOSTIC) */
17072
17073 /* We used an aligned pointer in SSL_set_app_data */
17074 struct mg_connection *conn = (struct mg_connection *)SSL_get_app_data(ssl);
17075
17076#if defined(GCC_DIAGNOSTIC)
17077#pragma GCC diagnostic pop
17078#endif /* defined(GCC_DIAGNOSTIC) */
17079
17080 const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
17081
17082 (void)ad;
17083 (void)arg;
17084
17085 if ((conn == NULL) || (conn->phys_ctx == NULL)) {
17086 DEBUG_ASSERT(0);
17087 return SSL_TLSEXT_ERR_NOACK;
17088 }
17089 conn->dom_ctx = &(conn->phys_ctx->dd);
17090
17091 /* Old clients (Win XP) will not support SNI. Then, there
17092 * is no server name available in the request - we can
17093 * only work with the default certificate.
17094 * Multiple HTTPS hosts on one IP+port are only possible
17095 * with a certificate containing all alternative names.
17096 */
17097 if ((servername == NULL) || (*servername == 0)) {
17098 DEBUG_TRACE("%s", "SSL connection not supporting SNI");
17100 SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx);
17102 return SSL_TLSEXT_ERR_NOACK;
17103 }
17104
17105 DEBUG_TRACE("TLS connection to host %s", servername);
17106
17107 while (conn->dom_ctx) {
17108 if (!mg_strcasecmp(servername,
17110 /* Found matching domain */
17111 DEBUG_TRACE("TLS domain %s found",
17113 break;
17114 }
17116 conn->dom_ctx = conn->dom_ctx->next;
17118 }
17119
17120 if (conn->dom_ctx == NULL) {
17121 /* Default domain */
17122 DEBUG_TRACE("TLS default domain %s used",
17124 conn->dom_ctx = &(conn->phys_ctx->dd);
17125 }
17127 SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx);
17129 return SSL_TLSEXT_ERR_OK;
17130}
17131
17132
17133#if defined(USE_ALPN)
17134static const char alpn_proto_list[] = "\x02h2\x08http/1.1\x08http/1.0";
17135static const char *alpn_proto_order_http1[] = {alpn_proto_list + 3,
17136 alpn_proto_list + 3 + 8,
17137 NULL};
17138#if defined(USE_HTTP2)
17139static const char *alpn_proto_order_http2[] = {alpn_proto_list,
17140 alpn_proto_list + 3,
17141 alpn_proto_list + 3 + 8,
17142 NULL};
17143#endif
17144
17145static int
17146alpn_select_cb(SSL *ssl,
17147 const unsigned char **out,
17148 unsigned char *outlen,
17149 const unsigned char *in,
17150 unsigned int inlen,
17151 void *arg)
17152{
17153 struct mg_domain_context *dom_ctx = (struct mg_domain_context *)arg;
17154 unsigned int i, j, enable_http2 = 0;
17155 const char **alpn_proto_order = alpn_proto_order_http1;
17156
17157 struct mg_workerTLS *tls =
17158 (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
17159
17160 (void)ssl;
17161
17162 if (tls == NULL) {
17163 /* Need to store protocol in Thread Local Storage */
17164 /* If there is no Thread Local Storage, don't use ALPN */
17165 return SSL_TLSEXT_ERR_NOACK;
17166 }
17167
17168#if defined(USE_HTTP2)
17169 enable_http2 = (0 == strcmp(dom_ctx->config[ENABLE_HTTP2], "yes"));
17170 if (enable_http2) {
17171 alpn_proto_order = alpn_proto_order_http2;
17172 }
17173#endif
17174
17175 for (j = 0; alpn_proto_order[j] != NULL; j++) {
17176 /* check all accepted protocols in this order */
17177 const char *alpn_proto = alpn_proto_order[j];
17178 /* search input for matching protocol */
17179 for (i = 0; i < inlen; i++) {
17180 if (!memcmp(in + i, alpn_proto, (unsigned char)alpn_proto[0])) {
17181 *out = in + i + 1;
17182 *outlen = in[i];
17183 tls->alpn_proto = alpn_proto;
17184 return SSL_TLSEXT_ERR_OK;
17185 }
17186 }
17187 }
17188
17189 /* Nothing found */
17190 return SSL_TLSEXT_ERR_NOACK;
17191}
17192
17193
17194static int
17195next_protos_advertised_cb(SSL *ssl,
17196 const unsigned char **data,
17197 unsigned int *len,
17198 void *arg)
17199{
17200 struct mg_domain_context *dom_ctx = (struct mg_domain_context *)arg;
17201 *data = (const unsigned char *)alpn_proto_list;
17202 *len = (unsigned int)strlen((const char *)data);
17203
17204 (void)ssl;
17205 (void)dom_ctx;
17206
17207 return SSL_TLSEXT_ERR_OK;
17208}
17209
17210
17211static int
17212init_alpn(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
17213{
17214 unsigned int alpn_len = (unsigned int)strlen((char *)alpn_proto_list);
17215 int ret = SSL_CTX_set_alpn_protos(dom_ctx->ssl_ctx,
17216 (const unsigned char *)alpn_proto_list,
17217 alpn_len);
17218 if (ret != 0) {
17219 mg_cry_ctx_internal(phys_ctx,
17220 "SSL_CTX_set_alpn_protos error: %s",
17221 ssl_error());
17222 }
17223
17224 SSL_CTX_set_alpn_select_cb(dom_ctx->ssl_ctx,
17225 alpn_select_cb,
17226 (void *)dom_ctx);
17227
17228 SSL_CTX_set_next_protos_advertised_cb(dom_ctx->ssl_ctx,
17229 next_protos_advertised_cb,
17230 (void *)dom_ctx);
17231
17232 return ret;
17233}
17234#endif
17235
17236
17237/* Setup SSL CTX as required by CivetWeb */
17238static int
17240 struct mg_domain_context *dom_ctx,
17241 const char *pem,
17242 const char *chain)
17243{
17244 int callback_ret;
17245 int should_verify_peer;
17246 int peer_certificate_optional;
17247 const char *ca_path;
17248 const char *ca_file;
17249 int use_default_verify_paths;
17250 int verify_depth;
17251 struct timespec now_mt;
17252 md5_byte_t ssl_context_id[16];
17253 md5_state_t md5state;
17254 int protocol_ver;
17255 int ssl_cache_timeout;
17256
17257#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \
17258 && !defined(NO_SSL_DL)
17259 if ((dom_ctx->ssl_ctx = SSL_CTX_new(TLS_server_method())) == NULL) {
17260 mg_cry_ctx_internal(phys_ctx,
17261 "SSL_CTX_new (server) error: %s",
17262 ssl_error());
17263 return 0;
17264 }
17265#else
17266 if ((dom_ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
17267 mg_cry_ctx_internal(phys_ctx,
17268 "SSL_CTX_new (server) error: %s",
17269 ssl_error());
17270 return 0;
17271 }
17272#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
17273
17274#if defined(SSL_OP_NO_TLSv1_3)
17275 SSL_CTX_clear_options(dom_ctx->ssl_ctx,
17276 SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1
17277 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2
17278 | SSL_OP_NO_TLSv1_3);
17279#else
17280 SSL_CTX_clear_options(dom_ctx->ssl_ctx,
17281 SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1
17282 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);
17283#endif
17284
17285 protocol_ver = atoi(dom_ctx->config[SSL_PROTOCOL_VERSION]);
17286 SSL_CTX_set_options(dom_ctx->ssl_ctx, ssl_get_protocol(protocol_ver));
17287 SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_SINGLE_DH_USE);
17288 SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
17289 SSL_CTX_set_options(dom_ctx->ssl_ctx,
17290 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
17291 SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_NO_COMPRESSION);
17292
17293#if defined(SSL_OP_NO_RENEGOTIATION)
17294 SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_NO_RENEGOTIATION);
17295#endif
17296
17297#if !defined(NO_SSL_DL)
17298 SSL_CTX_set_ecdh_auto(dom_ctx->ssl_ctx, 1);
17299#endif /* NO_SSL_DL */
17300
17301 /* In SSL documentation examples callback defined without const
17302 * specifier 'void (*)(SSL *, int, int)' See:
17303 * https://www.openssl.org/docs/man1.0.2/ssl/ssl.html
17304 * https://www.openssl.org/docs/man1.1.0/ssl/ssl.html
17305 * But in the source code const SSL is used:
17306 * 'void (*)(const SSL *, int, int)' See:
17307 * https://github.com/openssl/openssl/blob/1d97c8435171a7af575f73c526d79e1ef0ee5960/ssl/ssl.h#L1173
17308 * Problem about wrong documentation described, but not resolved:
17309 * https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/1147526
17310 * Wrong const cast ignored on C or can be suppressed by compiler flags.
17311 * But when compiled with modern C++ compiler, correct const should be
17312 * provided
17313 */
17314 SSL_CTX_set_info_callback(dom_ctx->ssl_ctx, ssl_info_callback);
17315
17316 SSL_CTX_set_tlsext_servername_callback(dom_ctx->ssl_ctx,
17318
17319 /* If a callback has been specified, call it. */
17320 callback_ret = (phys_ctx->callbacks.init_ssl == NULL)
17321 ? 0
17322 : (phys_ctx->callbacks.init_ssl(dom_ctx->ssl_ctx,
17323 phys_ctx->user_data));
17324
17325 /* If callback returns 0, civetweb sets up the SSL certificate.
17326 * If it returns 1, civetweb assumes the callback already did this.
17327 * If it returns -1, initializing ssl fails. */
17328 if (callback_ret < 0) {
17329 mg_cry_ctx_internal(phys_ctx,
17330 "SSL callback returned error: %i",
17331 callback_ret);
17332 return 0;
17333 }
17334 if (callback_ret > 0) {
17335 /* Callback did everything. */
17336 return 1;
17337 }
17338
17339 /* If a domain callback has been specified, call it. */
17340 callback_ret = (phys_ctx->callbacks.init_ssl_domain == NULL)
17341 ? 0
17342 : (phys_ctx->callbacks.init_ssl_domain(
17343 dom_ctx->config[AUTHENTICATION_DOMAIN],
17344 dom_ctx->ssl_ctx,
17345 phys_ctx->user_data));
17346
17347 /* If domain callback returns 0, civetweb sets up the SSL certificate.
17348 * If it returns 1, civetweb assumes the callback already did this.
17349 * If it returns -1, initializing ssl fails. */
17350 if (callback_ret < 0) {
17351 mg_cry_ctx_internal(phys_ctx,
17352 "Domain SSL callback returned error: %i",
17353 callback_ret);
17354 return 0;
17355 }
17356 if (callback_ret > 0) {
17357 /* Domain callback did everything. */
17358 return 1;
17359 }
17360
17361 /* Use some combination of start time, domain and port as a SSL
17362 * context ID. This should be unique on the current machine. */
17363 md5_init(&md5state);
17364 clock_gettime(CLOCK_MONOTONIC, &now_mt);
17365 md5_append(&md5state, (const md5_byte_t *)&now_mt, sizeof(now_mt));
17366 md5_append(&md5state,
17367 (const md5_byte_t *)phys_ctx->dd.config[LISTENING_PORTS],
17368 strlen(phys_ctx->dd.config[LISTENING_PORTS]));
17369 md5_append(&md5state,
17370 (const md5_byte_t *)dom_ctx->config[AUTHENTICATION_DOMAIN],
17371 strlen(dom_ctx->config[AUTHENTICATION_DOMAIN]));
17372 md5_append(&md5state, (const md5_byte_t *)phys_ctx, sizeof(*phys_ctx));
17373 md5_append(&md5state, (const md5_byte_t *)dom_ctx, sizeof(*dom_ctx));
17374 md5_finish(&md5state, ssl_context_id);
17375
17376 SSL_CTX_set_session_id_context(dom_ctx->ssl_ctx,
17377 (unsigned char *)ssl_context_id,
17378 sizeof(ssl_context_id));
17379
17380 if (pem != NULL) {
17381 if (!ssl_use_pem_file(phys_ctx, dom_ctx, pem, chain)) {
17382 return 0;
17383 }
17384 }
17385
17386 /* Should we support client certificates? */
17387 /* Default is "no". */
17388 should_verify_peer = 0;
17389 peer_certificate_optional = 0;
17390 if (dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) {
17391 if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER], "yes") == 0) {
17392 /* Yes, they are mandatory */
17393 should_verify_peer = 1;
17394 } else if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER],
17395 "optional")
17396 == 0) {
17397 /* Yes, they are optional */
17398 should_verify_peer = 1;
17399 peer_certificate_optional = 1;
17400 }
17401 }
17402
17403 use_default_verify_paths =
17404 (dom_ctx->config[SSL_DEFAULT_VERIFY_PATHS] != NULL)
17405 && (mg_strcasecmp(dom_ctx->config[SSL_DEFAULT_VERIFY_PATHS], "yes")
17406 == 0);
17407
17408 if (should_verify_peer) {
17409 ca_path = dom_ctx->config[SSL_CA_PATH];
17410 ca_file = dom_ctx->config[SSL_CA_FILE];
17411 if (SSL_CTX_load_verify_locations(dom_ctx->ssl_ctx, ca_file, ca_path)
17412 != 1) {
17413 mg_cry_ctx_internal(phys_ctx,
17414 "SSL_CTX_load_verify_locations error: %s "
17415 "ssl_verify_peer requires setting "
17416 "either ssl_ca_path or ssl_ca_file. "
17417 "Is any of them present in the "
17418 ".conf file?",
17419 ssl_error());
17420 return 0;
17421 }
17422
17423 if (peer_certificate_optional) {
17424 SSL_CTX_set_verify(dom_ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
17425 } else {
17426 SSL_CTX_set_verify(dom_ctx->ssl_ctx,
17427 SSL_VERIFY_PEER
17428 | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
17429 NULL);
17430 }
17431
17432 if (use_default_verify_paths
17433 && (SSL_CTX_set_default_verify_paths(dom_ctx->ssl_ctx) != 1)) {
17434 mg_cry_ctx_internal(phys_ctx,
17435 "SSL_CTX_set_default_verify_paths error: %s",
17436 ssl_error());
17437 return 0;
17438 }
17439
17440 if (dom_ctx->config[SSL_VERIFY_DEPTH]) {
17441 verify_depth = atoi(dom_ctx->config[SSL_VERIFY_DEPTH]);
17442 SSL_CTX_set_verify_depth(dom_ctx->ssl_ctx, verify_depth);
17443 }
17444 }
17445
17446 if (dom_ctx->config[SSL_CIPHER_LIST] != NULL) {
17447 if (SSL_CTX_set_cipher_list(dom_ctx->ssl_ctx,
17448 dom_ctx->config[SSL_CIPHER_LIST])
17449 != 1) {
17450 mg_cry_ctx_internal(phys_ctx,
17451 "SSL_CTX_set_cipher_list error: %s",
17452 ssl_error());
17453 }
17454 }
17455
17456 /* SSL session caching */
17457 ssl_cache_timeout = ((dom_ctx->config[SSL_CACHE_TIMEOUT] != NULL)
17458 ? atoi(dom_ctx->config[SSL_CACHE_TIMEOUT])
17459 : 0);
17460 if (ssl_cache_timeout > 0) {
17461 SSL_CTX_set_session_cache_mode(dom_ctx->ssl_ctx, SSL_SESS_CACHE_BOTH);
17462 /* SSL_CTX_sess_set_cache_size(dom_ctx->ssl_ctx, 10000); ... use
17463 * default */
17464 SSL_CTX_set_timeout(dom_ctx->ssl_ctx, (long)ssl_cache_timeout);
17465 }
17466
17467#if defined(USE_ALPN)
17468 /* Initialize ALPN only of TLS library (OpenSSL version) supports ALPN */
17469#if !defined(NO_SSL_DL)
17470 if (!tls_feature_missing[TLS_ALPN])
17471#endif
17472 {
17473 init_alpn(phys_ctx, dom_ctx);
17474 }
17475#endif
17476
17477 return 1;
17478}
17479
17480
17481/* Check if SSL is required.
17482 * If so, dynamically load SSL library
17483 * and set up ctx->ssl_ctx pointer. */
17484static int
17485init_ssl_ctx(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
17486{
17487 void *ssl_ctx = 0;
17488 int callback_ret;
17489 const char *pem;
17490 const char *chain;
17491 char ebuf[128];
17492
17493 if (!phys_ctx) {
17494 return 0;
17495 }
17496
17497 if (!dom_ctx) {
17498 dom_ctx = &(phys_ctx->dd);
17499 }
17500
17501 if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
17502 /* No SSL port is set. No need to setup SSL. */
17503 return 1;
17504 }
17505
17506 /* Check for external SSL_CTX */
17507 callback_ret =
17508 (phys_ctx->callbacks.external_ssl_ctx == NULL)
17509 ? 0
17510 : (phys_ctx->callbacks.external_ssl_ctx(&ssl_ctx,
17511 phys_ctx->user_data));
17512
17513 if (callback_ret < 0) {
17514 /* Callback exists and returns <0: Initializing failed. */
17515 mg_cry_ctx_internal(phys_ctx,
17516 "external_ssl_ctx callback returned error: %i",
17517 callback_ret);
17518 return 0;
17519 } else if (callback_ret > 0) {
17520 /* Callback exists and returns >0: Initializing complete,
17521 * civetweb should not modify the SSL context. */
17522 dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx;
17523 if (!initialize_openssl(ebuf, sizeof(ebuf))) {
17524 mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
17525 return 0;
17526 }
17527 return 1;
17528 }
17529 /* If the callback does not exist or return 0, civetweb must initialize
17530 * the SSL context. Handle "domain" callback next. */
17531
17532 /* Check for external domain SSL_CTX callback. */
17533 callback_ret = (phys_ctx->callbacks.external_ssl_ctx_domain == NULL)
17534 ? 0
17536 dom_ctx->config[AUTHENTICATION_DOMAIN],
17537 &ssl_ctx,
17538 phys_ctx->user_data));
17539
17540 if (callback_ret < 0) {
17541 /* Callback < 0: Error. Abort init. */
17543 phys_ctx,
17544 "external_ssl_ctx_domain callback returned error: %i",
17545 callback_ret);
17546 return 0;
17547 } else if (callback_ret > 0) {
17548 /* Callback > 0: Consider init done. */
17549 dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx;
17550 if (!initialize_openssl(ebuf, sizeof(ebuf))) {
17551 mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
17552 return 0;
17553 }
17554 return 1;
17555 }
17556 /* else: external_ssl_ctx/external_ssl_ctx_domain do not exist or return
17557 * 0, CivetWeb should continue initializing SSL */
17558
17559 /* If PEM file is not specified and the init_ssl callbacks
17560 * are not specified, setup will fail. */
17561 if (((pem = dom_ctx->config[SSL_CERTIFICATE]) == NULL)
17562 && (phys_ctx->callbacks.init_ssl == NULL)
17563 && (phys_ctx->callbacks.init_ssl_domain == NULL)) {
17564 /* No certificate and no init_ssl callbacks:
17565 * Essential data to set up TLS is missing.
17566 */
17567 mg_cry_ctx_internal(phys_ctx,
17568 "Initializing SSL failed: -%s is not set",
17570 return 0;
17571 }
17572
17573 /* If a certificate chain is configured, use it. */
17574 chain = dom_ctx->config[SSL_CERTIFICATE_CHAIN];
17575 if (chain == NULL) {
17576 /* Default: certificate chain in PEM file */
17577 chain = pem;
17578 }
17579 if ((chain != NULL) && (*chain == 0)) {
17580 /* If the chain is an empty string, don't use it. */
17581 chain = NULL;
17582 }
17583
17584 if (!initialize_openssl(ebuf, sizeof(ebuf))) {
17585 mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
17586 return 0;
17587 }
17588
17589 return init_ssl_ctx_impl(phys_ctx, dom_ctx, pem, chain);
17590}
17591
17592
17593static void
17595{
17596#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
17597
17598 if (mg_atomic_dec(&cryptolib_users) == 0) {
17599
17600 /* Shutdown according to
17601 * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
17602 * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl
17603 */
17604 CONF_modules_unload(1);
17605#else
17606 int i;
17607
17608 if (mg_atomic_dec(&cryptolib_users) == 0) {
17609
17610 /* Shutdown according to
17611 * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
17612 * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl
17613 */
17614 CRYPTO_set_locking_callback(NULL);
17615 CRYPTO_set_id_callback(NULL);
17616 ENGINE_cleanup();
17617 CONF_modules_unload(1);
17618 ERR_free_strings();
17619 EVP_cleanup();
17620 CRYPTO_cleanup_all_ex_data();
17621 OPENSSL_REMOVE_THREAD_STATE();
17622
17623 for (i = 0; i < CRYPTO_num_locks(); i++) {
17624 pthread_mutex_destroy(&ssl_mutexes[i]);
17625 }
17627 ssl_mutexes = NULL;
17628#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
17629 }
17630}
17631#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) */
17632
17633
17634#if !defined(NO_FILESYSTEMS)
17635static int
17636set_gpass_option(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
17637{
17638 if (phys_ctx) {
17639 struct mg_file file = STRUCT_FILE_INITIALIZER;
17640 const char *path;
17641 struct mg_connection fc;
17642 if (!dom_ctx) {
17643 dom_ctx = &(phys_ctx->dd);
17644 }
17646 if ((path != NULL)
17647 && !mg_stat(fake_connection(&fc, phys_ctx), path, &file.stat)) {
17649 "Cannot open %s: %s",
17650 path,
17651 strerror(ERRNO));
17652 return 0;
17653 }
17654 return 1;
17655 }
17656 return 0;
17657}
17658#endif /* NO_FILESYSTEMS */
17659
17660
17661static int
17663{
17664 union usa sa;
17665 memset(&sa, 0, sizeof(sa));
17666#if defined(USE_IPV6)
17667 sa.sin6.sin6_family = AF_INET6;
17668#else
17669 sa.sin.sin_family = AF_INET;
17670#endif
17671 return check_acl(phys_ctx, &sa) != -1;
17672}
17673
17674
17675static void
17677{
17678 if (!conn) {
17679 return;
17680 }
17681
17682 conn->num_bytes_sent = conn->consumed_content = 0;
17683
17684 conn->path_info = NULL;
17685 conn->status_code = -1;
17686 conn->content_len = -1;
17687 conn->is_chunked = 0;
17688 conn->must_close = 0;
17689 conn->request_len = 0;
17690 conn->request_state = 0;
17691 conn->throttle = 0;
17692 conn->accept_gzip = 0;
17693
17698 conn->response_info.status_code = 0;
17699
17703
17704 /* Free cleaned local URI (if any) */
17705 if (conn->request_info.local_uri != conn->request_info.local_uri_raw) {
17706 mg_free((void *)conn->request_info.local_uri);
17707 conn->request_info.local_uri = NULL;
17708 }
17709 conn->request_info.local_uri = NULL;
17710
17711#if defined(USE_SERVER_STATS)
17712 conn->processing_time = 0;
17713#endif
17714}
17715
17716
17717static int
17718set_tcp_nodelay(const struct socket *so, int nodelay_on)
17719{
17720 if ((so->lsa.sa.sa_family == AF_INET)
17721 || (so->lsa.sa.sa_family == AF_INET6)) {
17722 /* Only for TCP sockets */
17723 if (setsockopt(so->sock,
17724 IPPROTO_TCP,
17725 TCP_NODELAY,
17726 (SOCK_OPT_TYPE)&nodelay_on,
17727 sizeof(nodelay_on))
17728 != 0) {
17729 /* Error */
17730 return 1;
17731 }
17732 }
17733 /* OK */
17734 return 0;
17735}
17736
17737
17738#if !defined(__ZEPHYR__)
17739static void
17741{
17742#if defined(_WIN32)
17743 char buf[MG_BUF_LEN];
17744 int n;
17745#endif
17746 struct linger linger;
17747 int error_code = 0;
17748 int linger_timeout = -2;
17749 socklen_t opt_len = sizeof(error_code);
17750
17751 if (!conn) {
17752 return;
17753 }
17754
17755 /* http://msdn.microsoft.com/en-us/library/ms739165(v=vs.85).aspx:
17756 * "Note that enabling a nonzero timeout on a nonblocking socket
17757 * is not recommended.", so set it to blocking now */
17759
17760 /* Send FIN to the client */
17761 shutdown(conn->client.sock, SHUTDOWN_WR);
17762
17763#if defined(_WIN32)
17764 /* Read and discard pending incoming data. If we do not do that and
17765 * close
17766 * the socket, the data in the send buffer may be discarded. This
17767 * behaviour is seen on Windows, when client keeps sending data
17768 * when server decides to close the connection; then when client
17769 * does recv() it gets no data back. */
17770 do {
17771 n = pull_inner(NULL, conn, buf, sizeof(buf), /* Timeout in s: */ 1.0);
17772 } while (n > 0);
17773#endif
17774
17775 if (conn->dom_ctx->config[LINGER_TIMEOUT]) {
17776 linger_timeout = atoi(conn->dom_ctx->config[LINGER_TIMEOUT]);
17777 }
17778
17779 /* Set linger option according to configuration */
17780 if (linger_timeout >= 0) {
17781 /* Set linger option to avoid socket hanging out after close. This
17782 * prevent ephemeral port exhaust problem under high QPS. */
17783 linger.l_onoff = 1;
17784
17785#if defined(_MSC_VER)
17786#pragma warning(push)
17787#pragma warning(disable : 4244)
17788#endif
17789#if defined(GCC_DIAGNOSTIC)
17790#pragma GCC diagnostic push
17791#pragma GCC diagnostic ignored "-Wconversion"
17792#endif
17793 /* Data type of linger structure elements may differ,
17794 * so we don't know what cast we need here.
17795 * Disable type conversion warnings. */
17796
17797 linger.l_linger = (linger_timeout + 999) / 1000;
17798
17799#if defined(GCC_DIAGNOSTIC)
17800#pragma GCC diagnostic pop
17801#endif
17802#if defined(_MSC_VER)
17803#pragma warning(pop)
17804#endif
17805
17806 } else {
17807 linger.l_onoff = 0;
17808 linger.l_linger = 0;
17809 }
17810
17811 if (linger_timeout < -1) {
17812 /* Default: don't configure any linger */
17813 } else if (getsockopt(conn->client.sock,
17814 SOL_SOCKET,
17815 SO_ERROR,
17816#if defined(_WIN32) /* WinSock uses different data type here */
17817 (char *)&error_code,
17818#else
17819 &error_code,
17820#endif
17821 &opt_len)
17822 != 0) {
17823 /* Cannot determine if socket is already closed. This should
17824 * not occur and never did in a test. Log an error message
17825 * and continue. */
17826 mg_cry_internal(conn,
17827 "%s: getsockopt(SOL_SOCKET SO_ERROR) failed: %s",
17828 __func__,
17829 strerror(ERRNO));
17830#if defined(_WIN32)
17831 } else if (error_code == WSAECONNRESET) {
17832#else
17833 } else if (error_code == ECONNRESET) {
17834#endif
17835 /* Socket already closed by client/peer, close socket without linger
17836 */
17837 } else {
17838
17839 /* Set linger timeout */
17840 if (setsockopt(conn->client.sock,
17841 SOL_SOCKET,
17842 SO_LINGER,
17843 (char *)&linger,
17844 sizeof(linger))
17845 != 0) {
17847 conn,
17848 "%s: setsockopt(SOL_SOCKET SO_LINGER(%i,%i)) failed: %s",
17849 __func__,
17850 linger.l_onoff,
17851 linger.l_linger,
17852 strerror(ERRNO));
17853 }
17854 }
17855
17856 /* Now we know that our FIN is ACK-ed, safe to close */
17857 closesocket(conn->client.sock);
17858 conn->client.sock = INVALID_SOCKET;
17859}
17860#endif
17861
17862
17863static void
17865{
17866#if defined(USE_SERVER_STATS)
17867 conn->conn_state = 6; /* to close */
17868#endif
17869
17870#if defined(USE_LUA) && defined(USE_WEBSOCKET)
17871 if (conn->lua_websocket_state) {
17872 lua_websocket_close(conn, conn->lua_websocket_state);
17873 conn->lua_websocket_state = NULL;
17874 }
17875#endif
17876
17877 mg_lock_connection(conn);
17878
17879 /* Set close flag, so keep-alive loops will stop */
17880 conn->must_close = 1;
17881
17882 /* call the connection_close callback if assigned */
17883 if (conn->phys_ctx->callbacks.connection_close != NULL) {
17884 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
17885 conn->phys_ctx->callbacks.connection_close(conn);
17886 }
17887 }
17888
17889 /* Reset user data, after close callback is called.
17890 * Do not reuse it. If the user needs a destructor,
17891 * it must be done in the connection_close callback. */
17893
17894#if defined(USE_SERVER_STATS)
17895 conn->conn_state = 7; /* closing */
17896#endif
17897
17898#if defined(USE_MBEDTLS)
17899 if (conn->ssl != NULL) {
17900 mbed_ssl_close(conn->ssl);
17901 conn->ssl = NULL;
17902 }
17903#elif !defined(NO_SSL)
17904 if (conn->ssl != NULL) {
17905 /* Run SSL_shutdown twice to ensure completely close SSL connection
17906 */
17907 SSL_shutdown(conn->ssl);
17908 SSL_free(conn->ssl);
17909 OPENSSL_REMOVE_THREAD_STATE();
17910 conn->ssl = NULL;
17911 }
17912#endif
17913 if (conn->client.sock != INVALID_SOCKET) {
17914#if defined(__ZEPHYR__)
17915 closesocket(conn->client.sock);
17916#else
17918#endif
17919 conn->client.sock = INVALID_SOCKET;
17920 }
17921
17922 /* call the connection_closed callback if assigned */
17923 if (conn->phys_ctx->callbacks.connection_closed != NULL) {
17924 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
17926 }
17927 }
17928
17930
17931#if defined(USE_SERVER_STATS)
17932 conn->conn_state = 8; /* closed */
17933#endif
17934}
17935
17936
17937CIVETWEB_API void
17939{
17940 if ((conn == NULL) || (conn->phys_ctx == NULL)) {
17941 return;
17942 }
17943
17944#if defined(USE_WEBSOCKET)
17945 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
17946 if (conn->in_websocket_handling) {
17947 /* Set close flag, so the server thread can exit. */
17948 conn->must_close = 1;
17949 return;
17950 }
17951 }
17952 if (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT) {
17953
17954 unsigned int i;
17955
17956 /* client context: loops must end */
17958 conn->must_close = 1;
17959
17960 /* We need to get the client thread out of the select/recv call
17961 * here. */
17962 /* Since we use a sleep quantum of some seconds to check for recv
17963 * timeouts, we will just wait a few seconds in mg_join_thread. */
17964
17965 /* join worker thread */
17966 for (i = 0; i < conn->phys_ctx->cfg_worker_threads; i++) {
17968 }
17969 }
17970#endif /* defined(USE_WEBSOCKET) */
17971
17972 close_connection(conn);
17973
17974#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
17977 && (conn->phys_ctx->dd.ssl_ctx != NULL)) {
17978 SSL_CTX_free(conn->phys_ctx->dd.ssl_ctx);
17979 }
17980#endif
17981
17982#if defined(USE_WEBSOCKET)
17983 if (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT) {
17985 (void)pthread_mutex_destroy(&conn->mutex);
17986 mg_free(conn);
17987 } else if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) {
17988 (void)pthread_mutex_destroy(&conn->mutex);
17989 mg_free(conn);
17990 }
17991#else
17992 if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) { /* Client */
17993 (void)pthread_mutex_destroy(&conn->mutex);
17994 mg_free(conn);
17995 }
17996#endif /* defined(USE_WEBSOCKET) */
17997}
17998
17999
18000static struct mg_connection *
18001mg_connect_client_impl(const struct mg_client_options *client_options,
18002 int use_ssl,
18003 struct mg_init_data *init,
18004 struct mg_error_data *error)
18005{
18006 struct mg_connection *conn = NULL;
18007 SOCKET sock;
18008 union usa sa;
18009 struct sockaddr *psa;
18010 socklen_t len;
18011
18012 unsigned max_req_size =
18013 (unsigned)atoi(config_options[MAX_REQUEST_SIZE].default_value);
18014
18015 /* Size of structures, aligned to 8 bytes */
18016 size_t conn_size = ((sizeof(struct mg_connection) + 7) >> 3) << 3;
18017 size_t ctx_size = ((sizeof(struct mg_context) + 7) >> 3) << 3;
18018 size_t alloc_size = conn_size + ctx_size + max_req_size;
18019
18020 (void)init; /* TODO: Implement required options */
18021
18022 conn = (struct mg_connection *)mg_calloc(1, alloc_size);
18023
18024 if (error != NULL) {
18026 error->code_sub = 0;
18027 if (error->text_buffer_size > 0) {
18028 error->text[0] = 0;
18029 }
18030 }
18031
18032 if (conn == NULL) {
18033 if (error != NULL) {
18035 error->code_sub = (unsigned)alloc_size;
18037 NULL, /* No truncation check for ebuf */
18038 error->text,
18039 error->text_buffer_size,
18040 "calloc(): %s",
18041 strerror(ERRNO));
18042 }
18043 return NULL;
18044 }
18045
18046#if defined(GCC_DIAGNOSTIC)
18047#pragma GCC diagnostic push
18048#pragma GCC diagnostic ignored "-Wcast-align"
18049#endif /* defined(GCC_DIAGNOSTIC) */
18050 /* conn_size is aligned to 8 bytes */
18051
18052 conn->phys_ctx = (struct mg_context *)(((char *)conn) + conn_size);
18053
18054#if defined(GCC_DIAGNOSTIC)
18055#pragma GCC diagnostic pop
18056#endif /* defined(GCC_DIAGNOSTIC) */
18057
18058 conn->buf = (((char *)conn) + conn_size + ctx_size);
18059 conn->buf_size = (int)max_req_size;
18061 conn->dom_ctx = &(conn->phys_ctx->dd);
18062
18063 if (!connect_socket(conn->phys_ctx,
18064 client_options->host,
18065 client_options->port,
18066 use_ssl,
18067 error,
18068 &sock,
18069 &sa)) {
18070 /* "error" will be set by connect_socket. */
18071 /* free all memory and return NULL; */
18072 mg_free(conn);
18073 return NULL;
18074 }
18075
18076#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
18077#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \
18078 && !defined(NO_SSL_DL)
18079
18080 if (use_ssl
18081 && (conn->dom_ctx->ssl_ctx = SSL_CTX_new(TLS_client_method()))
18082 == NULL) {
18083 if (error != NULL) {
18086 NULL, /* No truncation check for ebuf */
18087 error->text,
18088 error->text_buffer_size,
18089 "SSL_CTX_new error: %s",
18090 ssl_error());
18091 }
18092
18093 closesocket(sock);
18094 mg_free(conn);
18095 return NULL;
18096 }
18097
18098#else
18099
18100 if (use_ssl
18101 && (conn->dom_ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method()))
18102 == NULL) {
18103 if (error != NULL) {
18106 NULL, /* No truncation check for ebuf */
18107 error->text,
18108 error->text_buffer_size,
18109 "SSL_CTX_new error: %s",
18110 ssl_error());
18111 }
18112
18113 closesocket(sock);
18114 mg_free(conn);
18115 return NULL;
18116 }
18117
18118#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
18119#endif /* NO_SSL */
18120
18121#if defined(USE_IPV6)
18122 len = (sa.sa.sa_family == AF_INET) ? sizeof(conn->client.rsa.sin)
18123 : sizeof(conn->client.rsa.sin6);
18124 psa = (sa.sa.sa_family == AF_INET)
18125 ? (struct sockaddr *)&(conn->client.rsa.sin)
18126 : (struct sockaddr *)&(conn->client.rsa.sin6);
18127#else
18128 len = sizeof(conn->client.rsa.sin);
18129 psa = (struct sockaddr *)&(conn->client.rsa.sin);
18130#endif
18131
18132 conn->client.sock = sock;
18133 conn->client.lsa = sa;
18134
18135 if (getsockname(sock, psa, &len) != 0) {
18136 mg_cry_internal(conn,
18137 "%s: getsockname() failed: %s",
18138 __func__,
18139 strerror(ERRNO));
18140 }
18141
18142 conn->client.is_ssl = use_ssl ? 1 : 0;
18143 if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) {
18144 if (error != NULL) {
18146 error->code_sub = (unsigned)ERRNO;
18148 NULL, /* No truncation check for ebuf */
18149 error->text,
18150 error->text_buffer_size,
18151 "Can not create mutex");
18152 }
18153#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
18154 SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18155#endif
18156 closesocket(sock);
18157 mg_free(conn);
18158 return NULL;
18159 }
18160
18161#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
18162 if (use_ssl) {
18163 /* TODO: Check ssl_verify_peer and ssl_ca_path here.
18164 * SSL_CTX_set_verify call is needed to switch off server
18165 * certificate checking, which is off by default in OpenSSL and
18166 * on in yaSSL. */
18167 /* TODO: SSL_CTX_set_verify(conn->dom_ctx,
18168 * SSL_VERIFY_PEER, verify_ssl_server); */
18169
18170 if (client_options->client_cert) {
18171 if (!ssl_use_pem_file(conn->phys_ctx,
18172 conn->dom_ctx,
18173 client_options->client_cert,
18174 NULL)) {
18175 if (error != NULL) {
18178 NULL, /* No truncation check for ebuf */
18179 error->text,
18180 error->text_buffer_size,
18181 "Can not use SSL client certificate");
18182 }
18183
18184 SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18185 closesocket(sock);
18186 mg_free(conn);
18187 return NULL;
18188 }
18189 }
18190
18191 if (client_options->server_cert) {
18192 if (SSL_CTX_load_verify_locations(conn->dom_ctx->ssl_ctx,
18193 client_options->server_cert,
18194 NULL)
18195 != 1) {
18196 if (error != NULL) {
18199 NULL, /* No truncation check for ebuf */
18200 error->text,
18201 error->text_buffer_size,
18202 "SSL_CTX_load_verify_locations error: %s",
18203 ssl_error());
18204 }
18205 SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18206 closesocket(sock);
18207 mg_free(conn);
18208 return NULL;
18209 }
18210 SSL_CTX_set_verify(conn->dom_ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
18211 } else {
18212 SSL_CTX_set_verify(conn->dom_ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
18213 }
18214
18215 if (!sslize(conn, SSL_connect, client_options)) {
18216 if (error != NULL) {
18219 NULL, /* No truncation check for ebuf */
18220 error->text,
18221 error->text_buffer_size,
18222 "SSL connection error");
18223 }
18224 SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18225 closesocket(sock);
18226 mg_free(conn);
18227 return NULL;
18228 }
18229 }
18230#endif
18231
18232 return conn;
18233}
18234
18235
18237mg_connect_client_secure(const struct mg_client_options *client_options,
18238 char *error_buffer,
18239 size_t error_buffer_size)
18240{
18241 struct mg_init_data init;
18242 struct mg_error_data error;
18243
18244 memset(&init, 0, sizeof(init));
18245 memset(&error, 0, sizeof(error));
18246 error.text_buffer_size = error_buffer_size;
18247 error.text = error_buffer;
18248 return mg_connect_client_impl(client_options, 1, &init, &error);
18249}
18250
18251
18253mg_connect_client(const char *host,
18254 int port,
18255 int use_ssl,
18256 char *error_buffer,
18257 size_t error_buffer_size)
18258{
18259 struct mg_client_options opts;
18260 struct mg_init_data init;
18261 struct mg_error_data error;
18262
18263 memset(&init, 0, sizeof(init));
18264
18265 memset(&error, 0, sizeof(error));
18266 error.text_buffer_size = error_buffer_size;
18267 error.text = error_buffer;
18268
18269 memset(&opts, 0, sizeof(opts));
18270 opts.host = host;
18271 opts.port = port;
18272 if (use_ssl) {
18273 opts.host_name = host;
18274 }
18275
18276 return mg_connect_client_impl(&opts, use_ssl, &init, &error);
18277}
18278
18279
18280#if defined(MG_EXPERIMENTAL_INTERFACES)
18282mg_connect_client2(const char *host,
18283 const char *protocol,
18284 int port,
18285 const char *path,
18286 struct mg_init_data *init,
18287 struct mg_error_data *error)
18288{
18289 (void)path;
18290
18291 int is_ssl, is_ws;
18292 /* void *user_data = (init != NULL) ? init->user_data : NULL; -- TODO */
18293
18294 if (error != NULL) {
18296 error->code_sub = 0;
18297 if (error->text_buffer_size > 0) {
18298 *error->text = 0;
18299 }
18300 }
18301
18302 if ((host == NULL) || (protocol == NULL)) {
18303 if (error != NULL) {
18306 NULL, /* No truncation check for error buffers */
18307 error->text,
18308 error->text_buffer_size,
18309 "%s",
18310 "Invalid parameters");
18311 }
18312 return NULL;
18313 }
18314
18315 /* check all known protocols */
18316 if (!mg_strcasecmp(protocol, "http")) {
18317 is_ssl = 0;
18318 is_ws = 0;
18319 } else if (!mg_strcasecmp(protocol, "https")) {
18320 is_ssl = 1;
18321 is_ws = 0;
18322#if defined(USE_WEBSOCKET)
18323 } else if (!mg_strcasecmp(protocol, "ws")) {
18324 is_ssl = 0;
18325 is_ws = 1;
18326 } else if (!mg_strcasecmp(protocol, "wss")) {
18327 is_ssl = 1;
18328 is_ws = 1;
18329#endif
18330 } else {
18331 if (error != NULL) {
18334 NULL, /* No truncation check for error buffers */
18335 error->text,
18336 error->text_buffer_size,
18337 "Protocol %s not supported",
18338 protocol);
18339 }
18340 return NULL;
18341 }
18342
18343 /* TODO: The current implementation here just calls the old
18344 * implementations, without using any new options. This is just a first
18345 * step to test the new interfaces. */
18346#if defined(USE_WEBSOCKET)
18347 if (is_ws) {
18348 /* TODO: implement all options */
18350 host,
18351 port,
18352 is_ssl,
18353 ((error != NULL) ? error->text : NULL),
18354 ((error != NULL) ? error->text_buffer_size : 0),
18355 (path ? path : ""),
18356 NULL /* TODO: origin */,
18357 experimental_websocket_client_data_wrapper,
18358 experimental_websocket_client_close_wrapper,
18359 (void *)init->callbacks);
18360 }
18361#else
18362 (void)is_ws;
18363#endif
18364
18365 /* TODO: all additional options */
18366 struct mg_client_options opts;
18367
18368 memset(&opts, 0, sizeof(opts));
18369 opts.host = host;
18370 opts.port = port;
18371
18372 return mg_connect_client_impl(&opts, is_ssl, init, error);
18373}
18374#endif
18375
18376
18377static const struct {
18378 const char *proto;
18381} abs_uri_protocols[] = {{"http://", 7, 80},
18382 {"https://", 8, 443},
18383 {"ws://", 5, 80},
18384 {"wss://", 6, 443},
18385 {NULL, 0, 0}};
18386
18387
18388/* Check if the uri is valid.
18389 * return 0 for invalid uri,
18390 * return 1 for *,
18391 * return 2 for relative uri,
18392 * return 3 for absolute uri without port,
18393 * return 4 for absolute uri with port */
18394static int
18395get_uri_type(const char *uri)
18396{
18397 int i;
18398 const char *hostend, *portbegin;
18399 char *portend;
18400 unsigned long port;
18401
18402 /* According to the HTTP standard
18403 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
18404 * URI can be an asterisk (*) or should start with slash (relative uri),
18405 * or it should start with the protocol (absolute uri). */
18406 if ((uri[0] == '*') && (uri[1] == '\0')) {
18407 /* asterisk */
18408 return 1;
18409 }
18410
18411 /* Valid URIs according to RFC 3986
18412 * (https://www.ietf.org/rfc/rfc3986.txt)
18413 * must only contain reserved characters :/?#[]@!$&'()*+,;=
18414 * and unreserved characters A-Z a-z 0-9 and -._~
18415 * and % encoded symbols.
18416 */
18417 for (i = 0; uri[i] != 0; i++) {
18418 if (uri[i] < 33) {
18419 /* control characters and spaces are invalid */
18420 return 0;
18421 }
18422 /* Allow everything else here (See #894) */
18423 }
18424
18425 /* A relative uri starts with a / character */
18426 if (uri[0] == '/') {
18427 /* relative uri */
18428 return 2;
18429 }
18430
18431 /* It could be an absolute uri: */
18432 /* This function only checks if the uri is valid, not if it is
18433 * addressing the current server. So civetweb can also be used
18434 * as a proxy server. */
18435 for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
18436 if (mg_strncasecmp(uri,
18439 == 0) {
18440
18441 hostend = strchr(uri + abs_uri_protocols[i].proto_len, '/');
18442 if (!hostend) {
18443 return 0;
18444 }
18445 portbegin = strchr(uri + abs_uri_protocols[i].proto_len, ':');
18446 if (!portbegin) {
18447 return 3;
18448 }
18449
18450 port = strtoul(portbegin + 1, &portend, 10);
18451 if ((portend != hostend) || (port <= 0) || !is_valid_port(port)) {
18452 return 0;
18453 }
18454
18455 return 4;
18456 }
18457 }
18458
18459 return 0;
18460}
18461
18462
18463/* Return NULL or the relative uri at the current server */
18464static const char *
18465get_rel_url_at_current_server(const char *uri, const struct mg_connection *conn)
18466{
18467 const char *server_domain;
18468 size_t server_domain_len;
18469 size_t request_domain_len = 0;
18470 unsigned long port = 0;
18471 int i, auth_domain_check_enabled;
18472 const char *hostbegin = NULL;
18473 const char *hostend = NULL;
18474 const char *portbegin;
18475 char *portend;
18476
18477 auth_domain_check_enabled =
18479
18480 /* DNS is case insensitive, so use case insensitive string compare here
18481 */
18482 for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
18483 if (mg_strncasecmp(uri,
18486 == 0) {
18487
18488 hostbegin = uri + abs_uri_protocols[i].proto_len;
18489 hostend = strchr(hostbegin, '/');
18490 if (!hostend) {
18491 return 0;
18492 }
18493 portbegin = strchr(hostbegin, ':');
18494 if ((!portbegin) || (portbegin > hostend)) {
18495 port = abs_uri_protocols[i].default_port;
18496 request_domain_len = (size_t)(hostend - hostbegin);
18497 } else {
18498 port = strtoul(portbegin + 1, &portend, 10);
18499 if ((portend != hostend) || (port <= 0)
18500 || !is_valid_port(port)) {
18501 return 0;
18502 }
18503 request_domain_len = (size_t)(portbegin - hostbegin);
18504 }
18505 /* protocol found, port set */
18506 break;
18507 }
18508 }
18509
18510 if (!port) {
18511 /* port remains 0 if the protocol is not found */
18512 return 0;
18513 }
18514
18515 /* Check if the request is directed to a different server. */
18516 /* First check if the port is the same. */
18517 if (ntohs(USA_IN_PORT_UNSAFE(&conn->client.lsa)) != port) {
18518 /* Request is directed to a different port */
18519 return 0;
18520 }
18521
18522 /* Finally check if the server corresponds to the authentication
18523 * domain of the server (the server domain).
18524 * Allow full matches (like http://mydomain.com/path/file.ext), and
18525 * allow subdomain matches (like http://www.mydomain.com/path/file.ext),
18526 * but do not allow substrings (like
18527 * http://notmydomain.com/path/file.ext
18528 * or http://mydomain.com.fake/path/file.ext).
18529 */
18530 if (auth_domain_check_enabled) {
18531 server_domain = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
18532 server_domain_len = strlen(server_domain);
18533 if ((server_domain_len == 0) || (hostbegin == NULL)) {
18534 return 0;
18535 }
18536 if ((request_domain_len == server_domain_len)
18537 && (!memcmp(server_domain, hostbegin, server_domain_len))) {
18538 /* Request is directed to this server - full name match. */
18539 } else {
18540 if (request_domain_len < (server_domain_len + 2)) {
18541 /* Request is directed to another server: The server name
18542 * is longer than the request name.
18543 * Drop this case here to avoid overflows in the
18544 * following checks. */
18545 return 0;
18546 }
18547 if (hostbegin[request_domain_len - server_domain_len - 1] != '.') {
18548 /* Request is directed to another server: It could be a
18549 * substring
18550 * like notmyserver.com */
18551 return 0;
18552 }
18553 if (0
18554 != memcmp(server_domain,
18555 hostbegin + request_domain_len - server_domain_len,
18556 server_domain_len)) {
18557 /* Request is directed to another server:
18558 * The server name is different. */
18559 return 0;
18560 }
18561 }
18562 }
18563
18564 return hostend;
18565}
18566
18567
18568static int
18569get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
18570{
18571 if (ebuf_len > 0) {
18572 ebuf[0] = '\0';
18573 }
18574 *err = 0;
18575
18577
18578 if (!conn) {
18579 mg_snprintf(conn,
18580 NULL, /* No truncation check for ebuf */
18581 ebuf,
18582 ebuf_len,
18583 "%s",
18584 "Internal error");
18585 *err = 500;
18586 return 0;
18587 }
18588
18589 /* Set the time the request was received. This value should be used for
18590 * timeouts. */
18591 clock_gettime(CLOCK_MONOTONIC, &(conn->req_time));
18592
18593 conn->request_len =
18594 read_message(NULL, conn, conn->buf, conn->buf_size, &conn->data_len);
18595 DEBUG_ASSERT(conn->request_len < 0 || conn->data_len >= conn->request_len);
18596 if ((conn->request_len >= 0) && (conn->data_len < conn->request_len)) {
18597 mg_snprintf(conn,
18598 NULL, /* No truncation check for ebuf */
18599 ebuf,
18600 ebuf_len,
18601 "%s",
18602 "Invalid message size");
18603 *err = 500;
18604 return 0;
18605 }
18606
18607 if ((conn->request_len == 0) && (conn->data_len == conn->buf_size)) {
18608 mg_snprintf(conn,
18609 NULL, /* No truncation check for ebuf */
18610 ebuf,
18611 ebuf_len,
18612 "%s",
18613 "Message too large");
18614 *err = 413;
18615 return 0;
18616 }
18617
18618 if (conn->request_len <= 0) {
18619 if (conn->data_len > 0) {
18620 mg_snprintf(conn,
18621 NULL, /* No truncation check for ebuf */
18622 ebuf,
18623 ebuf_len,
18624 "%s",
18625 "Malformed message");
18626 *err = 400;
18627 } else {
18628 /* Server did not recv anything -> just close the connection */
18629 conn->must_close = 1;
18630 mg_snprintf(conn,
18631 NULL, /* No truncation check for ebuf */
18632 ebuf,
18633 ebuf_len,
18634 "%s",
18635 "No data received");
18636 *err = 0;
18637 }
18638 return 0;
18639 }
18640 return 1;
18641}
18642
18643
18644static int
18645get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
18646{
18647 const char *cl;
18648
18649 conn->connection_type =
18650 CONNECTION_TYPE_REQUEST; /* request (valid of not) */
18651
18652 if (!get_message(conn, ebuf, ebuf_len, err)) {
18653 return 0;
18654 }
18655
18656 if (parse_http_request(conn->buf, conn->buf_size, &conn->request_info)
18657 <= 0) {
18658 mg_snprintf(conn,
18659 NULL, /* No truncation check for ebuf */
18660 ebuf,
18661 ebuf_len,
18662 "%s",
18663 "Bad request");
18664 *err = 400;
18665 return 0;
18666 }
18667
18668 /* Message is a valid request */
18669
18670 if (!switch_domain_context(conn)) {
18671 mg_snprintf(conn,
18672 NULL, /* No truncation check for ebuf */
18673 ebuf,
18674 ebuf_len,
18675 "%s",
18676 "Bad request: Host mismatch");
18677 *err = 400;
18678 return 0;
18679 }
18680
18681#if USE_ZLIB
18682 if (((cl = get_header(conn->request_info.http_headers,
18684 "Accept-Encoding"))
18685 != NULL)
18686 && strstr(cl, "gzip")) {
18687 conn->accept_gzip = 1;
18688 }
18689#endif
18690 if (((cl = get_header(conn->request_info.http_headers,
18692 "Transfer-Encoding"))
18693 != NULL)
18694 && mg_strcasecmp(cl, "identity")) {
18695 if (mg_strcasecmp(cl, "chunked")) {
18696 mg_snprintf(conn,
18697 NULL, /* No truncation check for ebuf */
18698 ebuf,
18699 ebuf_len,
18700 "%s",
18701 "Bad request");
18702 *err = 400;
18703 return 0;
18704 }
18705 conn->is_chunked = 1;
18706 conn->content_len = 0; /* not yet read */
18707 } else if ((cl = get_header(conn->request_info.http_headers,
18709 "Content-Length"))
18710 != NULL) {
18711 /* Request has content length set */
18712 char *endptr = NULL;
18713 conn->content_len = strtoll(cl, &endptr, 10);
18714 if ((endptr == cl) || (conn->content_len < 0)) {
18715 mg_snprintf(conn,
18716 NULL, /* No truncation check for ebuf */
18717 ebuf,
18718 ebuf_len,
18719 "%s",
18720 "Bad request");
18721 *err = 411;
18722 return 0;
18723 }
18724 /* Publish the content length back to the request info. */
18726 } else {
18727 /* There is no exception, see RFC7230. */
18728 conn->content_len = 0;
18729 }
18730
18731 return 1;
18732}
18733
18734
18735/* conn is assumed to be valid in this internal function */
18736static int
18737get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
18738{
18739 const char *cl;
18740
18741 conn->connection_type =
18742 CONNECTION_TYPE_RESPONSE; /* response (valid or not) */
18743
18744 if (!get_message(conn, ebuf, ebuf_len, err)) {
18745 return 0;
18746 }
18747
18748 if (parse_http_response(conn->buf, conn->buf_size, &conn->response_info)
18749 <= 0) {
18750 mg_snprintf(conn,
18751 NULL, /* No truncation check for ebuf */
18752 ebuf,
18753 ebuf_len,
18754 "%s",
18755 "Bad response");
18756 *err = 400;
18757 return 0;
18758 }
18759
18760 /* Message is a valid response */
18761
18762 if (((cl = get_header(conn->response_info.http_headers,
18764 "Transfer-Encoding"))
18765 != NULL)
18766 && mg_strcasecmp(cl, "identity")) {
18767 if (mg_strcasecmp(cl, "chunked")) {
18768 mg_snprintf(conn,
18769 NULL, /* No truncation check for ebuf */
18770 ebuf,
18771 ebuf_len,
18772 "%s",
18773 "Bad request");
18774 *err = 400;
18775 return 0;
18776 }
18777 conn->is_chunked = 1;
18778 conn->content_len = 0; /* not yet read */
18779 } else if ((cl = get_header(conn->response_info.http_headers,
18781 "Content-Length"))
18782 != NULL) {
18783 char *endptr = NULL;
18784 conn->content_len = strtoll(cl, &endptr, 10);
18785 if ((endptr == cl) || (conn->content_len < 0)) {
18786 mg_snprintf(conn,
18787 NULL, /* No truncation check for ebuf */
18788 ebuf,
18789 ebuf_len,
18790 "%s",
18791 "Bad request");
18792 *err = 411;
18793 return 0;
18794 }
18795 /* Publish the content length back to the response info. */
18797
18798 /* TODO: check if it is still used in response_info */
18800
18801 /* TODO: we should also consider HEAD method */
18802 if (conn->response_info.status_code == 304) {
18803 conn->content_len = 0;
18804 }
18805 } else {
18806 /* TODO: we should also consider HEAD method */
18807 if (((conn->response_info.status_code >= 100)
18808 && (conn->response_info.status_code <= 199))
18809 || (conn->response_info.status_code == 204)
18810 || (conn->response_info.status_code == 304)) {
18811 conn->content_len = 0;
18812 } else {
18813 conn->content_len = -1; /* unknown content length */
18814 }
18815 }
18816
18817 return 1;
18818}
18819
18820
18821CIVETWEB_API int
18823 char *ebuf,
18824 size_t ebuf_len,
18825 int timeout)
18826{
18827 int err, ret;
18828 char txt[32]; /* will not overflow */
18829 char *save_timeout;
18830 char *new_timeout;
18831
18832 if (ebuf_len > 0) {
18833 ebuf[0] = '\0';
18834 }
18835
18836 if (!conn) {
18837 mg_snprintf(conn,
18838 NULL, /* No truncation check for ebuf */
18839 ebuf,
18840 ebuf_len,
18841 "%s",
18842 "Parameter error");
18843 return -1;
18844 }
18845
18846 /* Reset the previous responses */
18847 conn->data_len = 0;
18848
18849 /* Implementation of API function for HTTP clients */
18850 save_timeout = conn->dom_ctx->config[REQUEST_TIMEOUT];
18851
18852 if (timeout >= 0) {
18853 mg_snprintf(conn, NULL, txt, sizeof(txt), "%i", timeout);
18854 new_timeout = txt;
18855 } else {
18856 new_timeout = NULL;
18857 }
18858
18859 conn->dom_ctx->config[REQUEST_TIMEOUT] = new_timeout;
18860 ret = get_response(conn, ebuf, ebuf_len, &err);
18861 conn->dom_ctx->config[REQUEST_TIMEOUT] = save_timeout;
18862
18863 /* TODO: here, the URI is the http response code */
18866
18867 /* TODO (mid): Define proper return values - maybe return length?
18868 * For the first test use <0 for error and >0 for OK */
18869 return (ret == 0) ? -1 : +1;
18870}
18871
18872
18874mg_download(const char *host,
18875 int port,
18876 int use_ssl,
18877 char *ebuf,
18878 size_t ebuf_len,
18879 const char *fmt,
18880 ...)
18881{
18882 struct mg_connection *conn;
18883 va_list ap;
18884 int i;
18885 int reqerr;
18886
18887 if (ebuf_len > 0) {
18888 ebuf[0] = '\0';
18889 }
18890
18891 va_start(ap, fmt);
18892
18893 /* open a connection */
18894 conn = mg_connect_client(host, port, use_ssl, ebuf, ebuf_len);
18895
18896 if (conn != NULL) {
18897 i = mg_vprintf(conn, fmt, ap);
18898 if (i <= 0) {
18899 mg_snprintf(conn,
18900 NULL, /* No truncation check for ebuf */
18901 ebuf,
18902 ebuf_len,
18903 "%s",
18904 "Error sending request");
18905 } else {
18906 /* make sure the buffer is clear */
18907 conn->data_len = 0;
18908 get_response(conn, ebuf, ebuf_len, &reqerr);
18909
18910 /* TODO: here, the URI is the http response code */
18912 }
18913 }
18914
18915 /* if an error occurred, close the connection */
18916 if ((ebuf[0] != '\0') && (conn != NULL)) {
18917 mg_close_connection(conn);
18918 conn = NULL;
18919 }
18920
18921 va_end(ap);
18922 return conn;
18923}
18924
18925
18932
18933
18934#if defined(USE_WEBSOCKET)
18935#if defined(_WIN32)
18936static unsigned __stdcall websocket_client_thread(void *data)
18937#else
18938static void *
18939websocket_client_thread(void *data)
18940#endif
18941{
18942 struct websocket_client_thread_data *cdata =
18943 (struct websocket_client_thread_data *)data;
18944
18945 void *user_thread_ptr = NULL;
18946
18947#if !defined(_WIN32) && !defined(__ZEPHYR__)
18948 struct sigaction sa;
18949
18950 /* Ignore SIGPIPE */
18951 memset(&sa, 0, sizeof(sa));
18952 sa.sa_handler = SIG_IGN;
18953 sigaction(SIGPIPE, &sa, NULL);
18954#endif
18955
18956 mg_set_thread_name("ws-clnt");
18957
18958 if (cdata->conn->phys_ctx) {
18959 if (cdata->conn->phys_ctx->callbacks.init_thread) {
18960 /* 3 indicates a websocket client thread */
18961 /* TODO: check if conn->phys_ctx can be set */
18962 user_thread_ptr = cdata->conn->phys_ctx->callbacks.init_thread(
18963 cdata->conn->phys_ctx, 3);
18964 }
18965 }
18966
18967 read_websocket(cdata->conn, cdata->data_handler, cdata->callback_data);
18968
18969 DEBUG_TRACE("%s", "Websocket client thread exited\n");
18970
18971 if (cdata->close_handler != NULL) {
18972 cdata->close_handler(cdata->conn, cdata->callback_data);
18973 }
18974
18975 /* The websocket_client context has only this thread. If it runs out,
18976 set the stop_flag to 2 (= "stopped"). */
18978
18979 if (cdata->conn->phys_ctx->callbacks.exit_thread) {
18981 3,
18982 user_thread_ptr);
18983 }
18984
18985 mg_free((void *)cdata);
18986
18987#if defined(_WIN32)
18988 return 0;
18989#else
18990 return NULL;
18991#endif
18992}
18993#endif
18994
18995
18996static struct mg_connection *
18998 int use_ssl,
18999 char *error_buffer,
19000 size_t error_buffer_size,
19001 const char *path,
19002 const char *origin,
19003 const char *extensions,
19004 mg_websocket_data_handler data_func,
19006 void *user_data)
19007{
19008 struct mg_connection *conn = NULL;
19009
19010#if defined(USE_WEBSOCKET)
19011 struct websocket_client_thread_data *thread_data;
19012 static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
19013
19014 const char *host = client_options->host;
19015 int i;
19016
19017 struct mg_init_data init;
19018 struct mg_error_data error;
19019
19020 memset(&init, 0, sizeof(init));
19021 memset(&error, 0, sizeof(error));
19022 error.text_buffer_size = error_buffer_size;
19023 error.text = error_buffer;
19024
19025#if defined(__clang__)
19026#pragma clang diagnostic push
19027#pragma clang diagnostic ignored "-Wformat-nonliteral"
19028#endif
19029
19030 /* Establish the client connection and request upgrade */
19031 conn = mg_connect_client_impl(client_options, use_ssl, &init, &error);
19032
19033 /* Connection object will be null if something goes wrong */
19034 if (conn == NULL) {
19035 /* error_buffer should be already filled ... */
19036 if (!error_buffer[0]) {
19037 /* ... if not add an error message */
19038 mg_snprintf(conn,
19039 NULL, /* No truncation check for ebuf */
19040 error_buffer,
19041 error_buffer_size,
19042 "Unexpected error");
19043 }
19044 return NULL;
19045 }
19046
19047 if (origin != NULL) {
19048 if (extensions != NULL) {
19049 i = mg_printf(conn,
19050 "GET %s HTTP/1.1\r\n"
19051 "Host: %s\r\n"
19052 "Upgrade: websocket\r\n"
19053 "Connection: Upgrade\r\n"
19054 "Sec-WebSocket-Key: %s\r\n"
19055 "Sec-WebSocket-Version: 13\r\n"
19056 "Sec-WebSocket-Extensions: %s\r\n"
19057 "Origin: %s\r\n"
19058 "\r\n",
19059 path,
19060 host,
19061 magic,
19062 extensions,
19063 origin);
19064 } else {
19065 i = mg_printf(conn,
19066 "GET %s HTTP/1.1\r\n"
19067 "Host: %s\r\n"
19068 "Upgrade: websocket\r\n"
19069 "Connection: Upgrade\r\n"
19070 "Sec-WebSocket-Key: %s\r\n"
19071 "Sec-WebSocket-Version: 13\r\n"
19072 "Origin: %s\r\n"
19073 "\r\n",
19074 path,
19075 host,
19076 magic,
19077 origin);
19078 }
19079 } else {
19080
19081 if (extensions != NULL) {
19082 i = mg_printf(conn,
19083 "GET %s HTTP/1.1\r\n"
19084 "Host: %s\r\n"
19085 "Upgrade: websocket\r\n"
19086 "Connection: Upgrade\r\n"
19087 "Sec-WebSocket-Key: %s\r\n"
19088 "Sec-WebSocket-Version: 13\r\n"
19089 "Sec-WebSocket-Extensions: %s\r\n"
19090 "\r\n",
19091 path,
19092 host,
19093 magic,
19094 extensions);
19095 } else {
19096 i = mg_printf(conn,
19097 "GET %s HTTP/1.1\r\n"
19098 "Host: %s\r\n"
19099 "Upgrade: websocket\r\n"
19100 "Connection: Upgrade\r\n"
19101 "Sec-WebSocket-Key: %s\r\n"
19102 "Sec-WebSocket-Version: 13\r\n"
19103 "\r\n",
19104 path,
19105 host,
19106 magic);
19107 }
19108 }
19109 if (i <= 0) {
19110 mg_snprintf(conn,
19111 NULL, /* No truncation check for ebuf */
19112 error_buffer,
19113 error_buffer_size,
19114 "%s",
19115 "Error sending request");
19116 mg_close_connection(conn);
19117 return NULL;
19118 }
19119
19120 conn->data_len = 0;
19121 if (!get_response(conn, error_buffer, error_buffer_size, &i)) {
19122 mg_close_connection(conn);
19123 return NULL;
19124 }
19127
19128#if defined(__clang__)
19129#pragma clang diagnostic pop
19130#endif
19131
19132 if (conn->response_info.status_code != 101) {
19133 /* We sent an "upgrade" request. For a correct websocket
19134 * protocol handshake, we expect a "101 Continue" response.
19135 * Otherwise it is a protocol violation. Maybe the HTTP
19136 * Server does not know websockets. */
19137 if (!*error_buffer) {
19138 /* set an error, if not yet set */
19139 mg_snprintf(conn,
19140 NULL, /* No truncation check for ebuf */
19141 error_buffer,
19142 error_buffer_size,
19143 "Unexpected server reply");
19144 }
19145
19146 DEBUG_TRACE("Websocket client connect error: %s\r\n", error_buffer);
19147 mg_close_connection(conn);
19148 return NULL;
19149 }
19150
19151 thread_data = (struct websocket_client_thread_data *)mg_calloc_ctx(
19152 1, sizeof(struct websocket_client_thread_data), conn->phys_ctx);
19153 if (!thread_data) {
19154 DEBUG_TRACE("%s\r\n", "Out of memory");
19156 return NULL;
19157 }
19158
19159 thread_data->conn = conn;
19160 thread_data->data_handler = data_func;
19161 thread_data->close_handler = close_func;
19162 thread_data->callback_data = user_data;
19163
19165 (pthread_t *)mg_calloc_ctx(1, sizeof(pthread_t), conn->phys_ctx);
19167 DEBUG_TRACE("%s\r\n", "Out of memory");
19168 mg_free(thread_data);
19170 return NULL;
19171 }
19172
19173 /* Now upgrade to ws/wss client context */
19174 conn->phys_ctx->user_data = user_data;
19176 conn->phys_ctx->cfg_worker_threads = 1; /* one worker thread */
19177
19178 /* Start a thread to read the websocket client connection
19179 * This thread will automatically stop when mg_disconnect is
19180 * called on the client connection */
19181 if (mg_start_thread_with_id(websocket_client_thread,
19182 thread_data,
19184 != 0) {
19186 mg_free(thread_data);
19188 conn = NULL;
19189 DEBUG_TRACE("%s",
19190 "Websocket client connect thread could not be started\r\n");
19191 }
19192
19193#else
19194 /* Appease "unused parameter" warnings */
19195 (void)client_options;
19196 (void)use_ssl;
19197 (void)error_buffer;
19198 (void)error_buffer_size;
19199 (void)path;
19200 (void)origin;
19201 (void)extensions;
19202 (void)user_data;
19203 (void)data_func;
19204 (void)close_func;
19205#endif
19206
19207 return conn;
19208}
19209
19210
19213 int port,
19214 int use_ssl,
19215 char *error_buffer,
19216 size_t error_buffer_size,
19217 const char *path,
19218 const char *origin,
19219 mg_websocket_data_handler data_func,
19221 void *user_data)
19222{
19223 struct mg_client_options client_options;
19224 memset(&client_options, 0, sizeof(client_options));
19225 client_options.host = host;
19226 client_options.port = port;
19227
19228 return mg_connect_websocket_client_impl(&client_options,
19229 use_ssl,
19230 error_buffer,
19231 error_buffer_size,
19232 path,
19233 origin,
19234 NULL,
19235 data_func,
19236 close_func,
19237 user_data);
19238}
19239
19240
19243 const struct mg_client_options *client_options,
19244 char *error_buffer,
19245 size_t error_buffer_size,
19246 const char *path,
19247 const char *origin,
19248 mg_websocket_data_handler data_func,
19250 void *user_data)
19251{
19252 if (!client_options) {
19253 return NULL;
19254 }
19255 return mg_connect_websocket_client_impl(client_options,
19256 1,
19257 error_buffer,
19258 error_buffer_size,
19259 path,
19260 origin,
19261 NULL,
19262 data_func,
19263 close_func,
19264 user_data);
19265}
19266
19267
19270 int port,
19271 int use_ssl,
19272 char *error_buffer,
19273 size_t error_buffer_size,
19274 const char *path,
19275 const char *origin,
19276 const char *extensions,
19277 mg_websocket_data_handler data_func,
19279 void *user_data)
19280{
19281 struct mg_client_options client_options;
19282 memset(&client_options, 0, sizeof(client_options));
19283 client_options.host = host;
19284 client_options.port = port;
19285
19286 return mg_connect_websocket_client_impl(&client_options,
19287 use_ssl,
19288 error_buffer,
19289 error_buffer_size,
19290 path,
19291 origin,
19292 extensions,
19293 data_func,
19294 close_func,
19295 user_data);
19296}
19297
19298
19301 const struct mg_client_options *client_options,
19302 char *error_buffer,
19303 size_t error_buffer_size,
19304 const char *path,
19305 const char *origin,
19306 const char *extensions,
19307 mg_websocket_data_handler data_func,
19309 void *user_data)
19310{
19311 if (!client_options) {
19312 return NULL;
19313 }
19314 return mg_connect_websocket_client_impl(client_options,
19315 1,
19316 error_buffer,
19317 error_buffer_size,
19318 path,
19319 origin,
19320 extensions,
19321 data_func,
19322 close_func,
19323 user_data);
19324}
19325
19326
19327/* Prepare connection data structure */
19328static void
19330{
19331 /* Is keep alive allowed by the server */
19332 int keep_alive_enabled =
19334
19335 if (!keep_alive_enabled) {
19336 conn->must_close = 1;
19337 }
19338
19339 /* Important: on new connection, reset the receiving buffer. Credit
19340 * goes to crule42. */
19341 conn->data_len = 0;
19342 conn->handled_requests = 0;
19346
19347#if defined(USE_SERVER_STATS)
19348 conn->conn_state = 2; /* init */
19349#endif
19350
19351 /* call the init_connection callback if assigned */
19352 if (conn->phys_ctx->callbacks.init_connection != NULL) {
19353 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
19354 void *conn_data = NULL;
19355 conn->phys_ctx->callbacks.init_connection(conn, &conn_data);
19356 mg_set_user_connection_data(conn, conn_data);
19357 }
19358 }
19359}
19360
19361
19362/* Process a connection - may handle multiple requests
19363 * using the same connection.
19364 * Must be called with a valid connection (conn and
19365 * conn->phys_ctx must be valid).
19366 */
19367static void
19369{
19370 struct mg_request_info *ri = &conn->request_info;
19371 int keep_alive, discard_len;
19372 char ebuf[100];
19373 const char *hostend;
19374 int reqerr, uri_type;
19375
19376#if defined(USE_SERVER_STATS)
19377 ptrdiff_t mcon = mg_atomic_inc(&(conn->phys_ctx->active_connections));
19378 mg_atomic_add(&(conn->phys_ctx->total_connections), 1);
19379 mg_atomic_max(&(conn->phys_ctx->max_active_connections), mcon);
19380#endif
19381
19382 DEBUG_TRACE("Start processing connection from %s",
19384
19385 /* Loop over multiple requests sent using the same connection
19386 * (while "keep alive"). */
19387 do {
19388 DEBUG_TRACE("calling get_request (%i times for this connection)",
19389 conn->handled_requests + 1);
19390
19391#if defined(USE_SERVER_STATS)
19392 conn->conn_state = 3; /* ready */
19393#endif
19394
19395 if (!get_request(conn, ebuf, sizeof(ebuf), &reqerr)) {
19396 /* The request sent by the client could not be understood by
19397 * the server, or it was incomplete or a timeout. Send an
19398 * error message and close the connection. */
19399 if (reqerr > 0) {
19400 DEBUG_ASSERT(ebuf[0] != '\0');
19401 mg_send_http_error(conn, reqerr, "%s", ebuf);
19402 }
19403
19404 } else if (strcmp(ri->http_version, "1.0")
19405 && strcmp(ri->http_version, "1.1")) {
19406 /* HTTP/2 is not allowed here */
19407 mg_snprintf(conn,
19408 NULL, /* No truncation check for ebuf */
19409 ebuf,
19410 sizeof(ebuf),
19411 "Bad HTTP version: [%s]",
19412 ri->http_version);
19413 mg_send_http_error(conn, 505, "%s", ebuf);
19414 }
19415
19416 if (ebuf[0] == '\0') {
19417 uri_type = get_uri_type(conn->request_info.request_uri);
19418 switch (uri_type) {
19419 case 1:
19420 /* Asterisk */
19421 conn->request_info.local_uri_raw = 0;
19422 /* TODO: Deal with '*'. */
19423 break;
19424 case 2:
19425 /* relative uri */
19428 break;
19429 case 3:
19430 case 4:
19431 /* absolute uri (with/without port) */
19433 conn->request_info.request_uri, conn);
19434 if (hostend) {
19435 conn->request_info.local_uri_raw = hostend;
19436 } else {
19438 }
19439 break;
19440 default:
19441 mg_snprintf(conn,
19442 NULL, /* No truncation check for ebuf */
19443 ebuf,
19444 sizeof(ebuf),
19445 "Invalid URI");
19446 mg_send_http_error(conn, 400, "%s", ebuf);
19448 break;
19449 }
19450 conn->request_info.local_uri =
19451 (char *)conn->request_info.local_uri_raw;
19452 }
19453
19454 if (ebuf[0] != '\0') {
19455 conn->protocol_type = -1;
19456
19457 } else {
19458 /* HTTP/1 allows protocol upgrade */
19460
19461 if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
19462 /* This will occur, if a HTTP/1.1 request should be upgraded
19463 * to HTTP/2 - but not if HTTP/2 is negotiated using ALPN.
19464 * Since most (all?) major browsers only support HTTP/2 using
19465 * ALPN, this is hard to test and very low priority.
19466 * Deactivate it (at least for now).
19467 */
19469 }
19470 }
19471
19472 DEBUG_TRACE("http: %s, error: %s",
19473 (ri->http_version ? ri->http_version : "none"),
19474 (ebuf[0] ? ebuf : "none"));
19475
19476 if (ebuf[0] == '\0') {
19477 if (conn->request_info.local_uri) {
19478
19479 /* handle request to local server */
19481
19482 } else {
19483 /* TODO: handle non-local request (PROXY) */
19484 conn->must_close = 1;
19485 }
19486 } else {
19487 conn->must_close = 1;
19488 }
19489
19490 /* Response complete. Free header buffer */
19491 free_buffered_response_header_list(conn);
19492
19493 if (ri->remote_user != NULL) {
19494 mg_free((void *)ri->remote_user);
19495 /* Important! When having connections with and without auth
19496 * would cause double free and then crash */
19497 ri->remote_user = NULL;
19498 }
19499
19500 /* NOTE(lsm): order is important here. should_keep_alive() call
19501 * is using parsed request, which will be invalid after
19502 * memmove's below.
19503 * Therefore, memorize should_keep_alive() result now for later
19504 * use in loop exit condition. */
19505 /* Enable it only if this request is completely discardable. */
19506 keep_alive = STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)
19507 && should_keep_alive(conn) && (conn->content_len >= 0)
19508 && (conn->request_len > 0)
19509 && ((conn->is_chunked == 4)
19510 || (!conn->is_chunked
19511 && ((conn->consumed_content == conn->content_len)
19512 || ((conn->request_len + conn->content_len)
19513 <= conn->data_len))))
19514 && (conn->protocol_type == PROTOCOL_TYPE_HTTP1);
19515
19516 if (keep_alive) {
19517 /* Discard all buffered data for this request */
19518 discard_len =
19519 ((conn->request_len + conn->content_len) < conn->data_len)
19520 ? (int)(conn->request_len + conn->content_len)
19521 : conn->data_len;
19522 conn->data_len -= discard_len;
19523
19524 if (conn->data_len > 0) {
19525 DEBUG_TRACE("discard_len = %d", discard_len);
19526 memmove(conn->buf,
19527 conn->buf + discard_len,
19528 (size_t)conn->data_len);
19529 }
19530 }
19531
19532 DEBUG_ASSERT(conn->data_len >= 0);
19533 DEBUG_ASSERT(conn->data_len <= conn->buf_size);
19534
19535 if ((conn->data_len < 0) || (conn->data_len > conn->buf_size)) {
19536 DEBUG_TRACE("internal error: data_len = %li, buf_size = %li",
19537 (long int)conn->data_len,
19538 (long int)conn->buf_size);
19539 break;
19540 }
19541 conn->handled_requests++;
19542 } while (keep_alive);
19543
19544 DEBUG_TRACE("Done processing connection from %s (%f sec)",
19546 difftime(time(NULL), conn->conn_birth_time));
19547
19548 close_connection(conn);
19549
19550#if defined(USE_SERVER_STATS)
19551 mg_atomic_add(&(conn->phys_ctx->total_requests), conn->handled_requests);
19552 mg_atomic_dec(&(conn->phys_ctx->active_connections));
19553#endif
19554}
19555
19556
19557#if defined(ALTERNATIVE_QUEUE)
19558
19559static void
19560produce_socket(struct mg_context *ctx, const struct socket *sp)
19561{
19562 unsigned int i;
19563
19564 while (!ctx->stop_flag) {
19565 for (i = 0; i < ctx->cfg_worker_threads; i++) {
19566 /* find a free worker slot and signal it */
19567 if (ctx->client_socks[i].in_use == 2) {
19568 (void)pthread_mutex_lock(&ctx->thread_mutex);
19569 if ((ctx->client_socks[i].in_use == 2) && !ctx->stop_flag) {
19570 ctx->client_socks[i] = *sp;
19571 ctx->client_socks[i].in_use = 1;
19572 /* socket has been moved to the consumer */
19573 (void)pthread_mutex_unlock(&ctx->thread_mutex);
19574 (void)event_signal(ctx->client_wait_events[i]);
19575 return;
19576 }
19577 (void)pthread_mutex_unlock(&ctx->thread_mutex);
19578 }
19579 }
19580 /* queue is full */
19581 mg_sleep(1);
19582 }
19583 /* must consume */
19585 closesocket(sp->sock);
19586}
19587
19588
19589static int
19590consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
19591{
19592 DEBUG_TRACE("%s", "going idle");
19593 (void)pthread_mutex_lock(&ctx->thread_mutex);
19594 ctx->client_socks[thread_index].in_use = 2;
19595 (void)pthread_mutex_unlock(&ctx->thread_mutex);
19596
19597 event_wait(ctx->client_wait_events[thread_index]);
19598
19599 (void)pthread_mutex_lock(&ctx->thread_mutex);
19600 *sp = ctx->client_socks[thread_index];
19601 if (ctx->stop_flag) {
19602 (void)pthread_mutex_unlock(&ctx->thread_mutex);
19603 if (sp->in_use == 1) {
19604 /* must consume */
19606 closesocket(sp->sock);
19607 }
19608 return 0;
19609 }
19610 (void)pthread_mutex_unlock(&ctx->thread_mutex);
19611 if (sp->in_use == 1) {
19612 DEBUG_TRACE("grabbed socket %d, going busy", sp->sock);
19613 return 1;
19614 }
19615 /* must not reach here */
19616 DEBUG_ASSERT(0);
19617 return 0;
19618}
19619
19620#else /* ALTERNATIVE_QUEUE */
19621
19622/* Worker threads take accepted socket from the queue */
19623static int
19624consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
19625{
19626 (void)thread_index;
19627
19628 (void)pthread_mutex_lock(&ctx->thread_mutex);
19629 DEBUG_TRACE("%s", "going idle");
19630
19631 /* If the queue is empty, wait. We're idle at this point. */
19632 while ((ctx->sq_head == ctx->sq_tail)
19633 && (STOP_FLAG_IS_ZERO(&ctx->stop_flag))) {
19634 pthread_cond_wait(&ctx->sq_full, &ctx->thread_mutex);
19635 }
19636
19637 /* If we're stopping, sq_head may be equal to sq_tail. */
19638 if (ctx->sq_head > ctx->sq_tail) {
19639 /* Copy socket from the queue and increment tail */
19640 *sp = ctx->squeue[ctx->sq_tail % ctx->sq_size];
19641 ctx->sq_tail++;
19642
19643 DEBUG_TRACE("grabbed socket %d, going busy", sp ? sp->sock : -1);
19644
19645 /* Wrap pointers if needed */
19646 while (ctx->sq_tail > ctx->sq_size) {
19647 ctx->sq_tail -= ctx->sq_size;
19648 ctx->sq_head -= ctx->sq_size;
19649 }
19650 }
19651
19652 (void)pthread_cond_signal(&ctx->sq_empty);
19653 (void)pthread_mutex_unlock(&ctx->thread_mutex);
19654
19655 return STOP_FLAG_IS_ZERO(&ctx->stop_flag);
19656}
19657
19658
19659/* Master thread adds accepted socket to a queue */
19660static void
19661produce_socket(struct mg_context *ctx, const struct socket *sp)
19662{
19663 int queue_filled;
19664
19665 (void)pthread_mutex_lock(&ctx->thread_mutex);
19666
19667 queue_filled = ctx->sq_head - ctx->sq_tail;
19668
19669 /* If the queue is full, wait */
19670 while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
19671 && (queue_filled >= ctx->sq_size)) {
19672 ctx->sq_blocked = 1; /* Status information: All threads busy */
19673#if defined(USE_SERVER_STATS)
19674 if (queue_filled > ctx->sq_max_fill) {
19675 ctx->sq_max_fill = queue_filled;
19676 }
19677#endif
19678 (void)pthread_cond_wait(&ctx->sq_empty, &ctx->thread_mutex);
19679 ctx->sq_blocked = 0; /* Not blocked now */
19680 queue_filled = ctx->sq_head - ctx->sq_tail;
19681 }
19682
19683 if (queue_filled < ctx->sq_size) {
19684 /* Copy socket to the queue and increment head */
19685 ctx->squeue[ctx->sq_head % ctx->sq_size] = *sp;
19686 ctx->sq_head++;
19687 DEBUG_TRACE("queued socket %d", sp ? sp->sock : -1);
19688 }
19689
19690 queue_filled = ctx->sq_head - ctx->sq_tail;
19691#if defined(USE_SERVER_STATS)
19692 if (queue_filled > ctx->sq_max_fill) {
19693 ctx->sq_max_fill = queue_filled;
19694 }
19695#endif
19696
19697 (void)pthread_cond_signal(&ctx->sq_full);
19698 (void)pthread_mutex_unlock(&ctx->thread_mutex);
19699}
19700#endif /* ALTERNATIVE_QUEUE */
19701
19702
19703static void
19705{
19706 struct mg_context *ctx = conn->phys_ctx;
19707 int thread_index;
19708 struct mg_workerTLS tls;
19709
19710 mg_set_thread_name("worker");
19711
19712 tls.is_master = 0;
19713 tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
19714#if defined(_WIN32)
19715 tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
19716#endif
19717
19718 /* Initialize thread local storage before calling any callback */
19719 pthread_setspecific(sTlsKey, &tls);
19720
19721 /* Check if there is a user callback */
19722 if (ctx->callbacks.init_thread) {
19723 /* call init_thread for a worker thread (type 1), and store the
19724 * return value */
19725 tls.user_ptr = ctx->callbacks.init_thread(ctx, 1);
19726 } else {
19727 /* No callback: set user pointer to NULL */
19728 tls.user_ptr = NULL;
19729 }
19730
19731 /* Connection structure has been pre-allocated */
19732 thread_index = (int)(conn - ctx->worker_connections);
19733 if ((thread_index < 0)
19734 || ((unsigned)thread_index >= (unsigned)ctx->cfg_worker_threads)) {
19736 "Internal error: Invalid worker index %i",
19737 thread_index);
19738 return;
19739 }
19740
19741 /* Request buffers are not pre-allocated. They are private to the
19742 * request and do not contain any state information that might be
19743 * of interest to anyone observing a server status. */
19744 conn->buf = (char *)mg_malloc_ctx(ctx->max_request_size, conn->phys_ctx);
19745 if (conn->buf == NULL) {
19747 ctx,
19748 "Out of memory: Cannot allocate buffer for worker %i",
19749 thread_index);
19750 return;
19751 }
19752 conn->buf_size = (int)ctx->max_request_size;
19753
19754 conn->dom_ctx = &(ctx->dd); /* Use default domain and default host */
19755
19756 conn->tls_user_ptr = tls.user_ptr; /* store ptr for quick access */
19757
19758 conn->request_info.user_data = ctx->user_data;
19759 /* Allocate a mutex for this connection to allow communication both
19760 * within the request handler and from elsewhere in the application
19761 */
19762 if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) {
19763 mg_free(conn->buf);
19764 mg_cry_ctx_internal(ctx, "%s", "Cannot create mutex");
19765 return;
19766 }
19767
19768#if defined(USE_SERVER_STATS)
19769 conn->conn_state = 1; /* not consumed */
19770#endif
19771
19772 /* Call consume_socket() even when ctx->stop_flag > 0, to let it
19773 * signal sq_empty condvar to wake up the master waiting in
19774 * produce_socket() */
19775 while (consume_socket(ctx, &conn->client, thread_index)) {
19776
19777 /* New connections must start with new protocol negotiation */
19778 tls.alpn_proto = NULL;
19779
19780#if defined(USE_SERVER_STATS)
19781 conn->conn_close_time = 0;
19782#endif
19783 conn->conn_birth_time = time(NULL);
19784
19785 /* Fill in IP, port info early so even if SSL setup below fails,
19786 * error handler would have the corresponding info.
19787 * Thanks to Johannes Winkelmann for the patch.
19788 */
19790 ntohs(USA_IN_PORT_UNSAFE(&conn->client.rsa));
19791
19793 ntohs(USA_IN_PORT_UNSAFE(&conn->client.lsa));
19794
19796 sizeof(conn->request_info.remote_addr),
19797 &conn->client.rsa);
19798
19799 DEBUG_TRACE("Incoming %sconnection from %s",
19800 (conn->client.is_ssl ? "SSL " : ""),
19802
19803 conn->request_info.is_ssl = conn->client.is_ssl;
19804
19805 if (conn->client.is_ssl) {
19806
19807#if defined(USE_MBEDTLS)
19808 /* HTTPS connection */
19809 if (mbed_ssl_accept(&(conn->ssl),
19810 conn->dom_ctx->ssl_ctx,
19811 (int *)&(conn->client.sock),
19812 conn->phys_ctx)
19813 == 0) {
19814 /* conn->dom_ctx is set in get_request */
19815 /* process HTTPS connection */
19816 init_connection(conn);
19820 } else {
19821 /* make sure the connection is cleaned up on SSL failure */
19822 close_connection(conn);
19823 }
19824
19825#elif !defined(NO_SSL)
19826 /* HTTPS connection */
19827 if (sslize(conn, SSL_accept, NULL)) {
19828 /* conn->dom_ctx is set in get_request */
19829
19830 /* Get SSL client certificate information (if set) */
19831 struct mg_client_cert client_cert;
19832 if (ssl_get_client_cert_info(conn, &client_cert)) {
19833 conn->request_info.client_cert = &client_cert;
19834 }
19835
19836 /* process HTTPS connection */
19837#if defined(USE_HTTP2)
19838 if ((tls.alpn_proto != NULL)
19839 && (!memcmp(tls.alpn_proto, "\x02h2", 3))) {
19840 /* process HTTPS/2 connection */
19841 init_connection(conn);
19844 conn->content_len =
19845 -1; /* content length is not predefined */
19846 conn->is_chunked = 0; /* HTTP2 is never chunked */
19847 process_new_http2_connection(conn);
19848 } else
19849#endif
19850 {
19851 /* process HTTPS/1.x or WEBSOCKET-SECURE connection */
19852 init_connection(conn);
19854 /* Start with HTTP, WS will be an "upgrade" request later */
19857 }
19858
19859 /* Free client certificate info */
19860 if (conn->request_info.client_cert) {
19861 mg_free((void *)(conn->request_info.client_cert->subject));
19862 mg_free((void *)(conn->request_info.client_cert->issuer));
19863 mg_free((void *)(conn->request_info.client_cert->serial));
19864 mg_free((void *)(conn->request_info.client_cert->finger));
19865 /* Free certificate memory */
19866 X509_free(
19867 (X509 *)conn->request_info.client_cert->peer_cert);
19869 conn->request_info.client_cert->subject = 0;
19870 conn->request_info.client_cert->issuer = 0;
19871 conn->request_info.client_cert->serial = 0;
19872 conn->request_info.client_cert->finger = 0;
19873 conn->request_info.client_cert = 0;
19874 }
19875 } else {
19876 /* make sure the connection is cleaned up on SSL failure */
19877 close_connection(conn);
19878 }
19879#endif
19880
19881 } else {
19882 /* process HTTP connection */
19883 init_connection(conn);
19885 /* Start with HTTP, WS will be an "upgrade" request later */
19888 }
19889
19890 DEBUG_TRACE("%s", "Connection closed");
19891
19892#if defined(USE_SERVER_STATS)
19893 conn->conn_close_time = time(NULL);
19894#endif
19895 }
19896
19897 /* Call exit thread user callback */
19898 if (ctx->callbacks.exit_thread) {
19899 ctx->callbacks.exit_thread(ctx, 1, tls.user_ptr);
19900 }
19901
19902 /* delete thread local storage objects */
19903 pthread_setspecific(sTlsKey, NULL);
19904#if defined(_WIN32)
19905 CloseHandle(tls.pthread_cond_helper_mutex);
19906#endif
19907 pthread_mutex_destroy(&conn->mutex);
19908
19909 /* Free the request buffer. */
19910 conn->buf_size = 0;
19911 mg_free(conn->buf);
19912 conn->buf = NULL;
19913
19914 /* Free cleaned URI (if any) */
19915 if (conn->request_info.local_uri != conn->request_info.local_uri_raw) {
19916 mg_free((void *)conn->request_info.local_uri);
19917 conn->request_info.local_uri = NULL;
19918 }
19919
19920#if defined(USE_SERVER_STATS)
19921 conn->conn_state = 9; /* done */
19922#endif
19923
19924 DEBUG_TRACE("%s", "exiting");
19925}
19926
19927
19928/* Threads have different return types on Windows and Unix. */
19929#if defined(_WIN32)
19930static unsigned __stdcall worker_thread(void *thread_func_param)
19931{
19932 worker_thread_run((struct mg_connection *)thread_func_param);
19933 return 0;
19934}
19935#else
19936static void *
19937worker_thread(void *thread_func_param)
19938{
19939#if !defined(__ZEPHYR__)
19940 struct sigaction sa;
19941
19942 /* Ignore SIGPIPE */
19943 memset(&sa, 0, sizeof(sa));
19944 sa.sa_handler = SIG_IGN;
19945 sigaction(SIGPIPE, &sa, NULL);
19946#endif
19947
19948 worker_thread_run((struct mg_connection *)thread_func_param);
19949 return NULL;
19950}
19951#endif /* _WIN32 */
19952
19953
19954/* This is an internal function, thus all arguments are expected to be
19955 * valid - a NULL check is not required. */
19956static void
19957accept_new_connection(const struct socket *listener, struct mg_context *ctx)
19958{
19959 struct socket so;
19960 char src_addr[IP_ADDR_STR_LEN];
19961 socklen_t len = sizeof(so.rsa);
19962#if !defined(__ZEPHYR__)
19963 int on = 1;
19964#endif
19965 memset(&so, 0, sizeof(so));
19966
19967 if ((so.sock = accept(listener->sock, &so.rsa.sa, &len))
19968 == INVALID_SOCKET) {
19969 } else if (check_acl(ctx, &so.rsa) != 1) {
19970 sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
19972 "%s: %s is not allowed to connect",
19973 __func__,
19974 src_addr);
19975 closesocket(so.sock);
19976 } else {
19977 /* Put so socket structure into the queue */
19978 DEBUG_TRACE("Accepted socket %d", (int)so.sock);
19979 set_close_on_exec(so.sock, NULL, ctx);
19980 so.is_ssl = listener->is_ssl;
19981 so.ssl_redir = listener->ssl_redir;
19982 if (getsockname(so.sock, &so.lsa.sa, &len) != 0) {
19984 "%s: getsockname() failed: %s",
19985 __func__,
19986 strerror(ERRNO));
19987 }
19988
19989#if !defined(__ZEPHYR__)
19990 if ((so.lsa.sa.sa_family == AF_INET)
19991 || (so.lsa.sa.sa_family == AF_INET6)) {
19992 /* Set TCP keep-alive for TCP sockets (IPv4 and IPv6).
19993 * This is needed because if HTTP-level keep-alive
19994 * is enabled, and client resets the connection, server won't get
19995 * TCP FIN or RST and will keep the connection open forever. With
19996 * TCP keep-alive, next keep-alive handshake will figure out that
19997 * the client is down and will close the server end.
19998 * Thanks to Igor Klopov who suggested the patch. */
19999 if (setsockopt(so.sock,
20000 SOL_SOCKET,
20001 SO_KEEPALIVE,
20002 (SOCK_OPT_TYPE)&on,
20003 sizeof(on))
20004 != 0) {
20006 ctx,
20007 "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
20008 __func__,
20009 strerror(ERRNO));
20010 }
20011 }
20012#endif
20013
20014 /* Disable TCP Nagle's algorithm. Normally TCP packets are coalesced
20015 * to effectively fill up the underlying IP packet payload and
20016 * reduce the overhead of sending lots of small buffers. However
20017 * this hurts the server's throughput (ie. operations per second)
20018 * when HTTP 1.1 persistent connections are used and the responses
20019 * are relatively small (eg. less than 1400 bytes).
20020 */
20021 if ((ctx->dd.config[CONFIG_TCP_NODELAY] != NULL)
20022 && (!strcmp(ctx->dd.config[CONFIG_TCP_NODELAY], "1"))) {
20023 if (set_tcp_nodelay(&so, 1) != 0) {
20025 ctx,
20026 "%s: setsockopt(IPPROTO_TCP TCP_NODELAY) failed: %s",
20027 __func__,
20028 strerror(ERRNO));
20029 }
20030 }
20031
20032 /* The "non blocking" property should already be
20033 * inherited from the parent socket. Set it for
20034 * non-compliant socket implementations. */
20036
20037 so.in_use = 0;
20038 produce_socket(ctx, &so);
20039 }
20040}
20041
20042
20043static void
20045{
20046 struct mg_workerTLS tls;
20047 struct mg_pollfd *pfd;
20048 unsigned int i;
20049 unsigned int workerthreadcount;
20050
20051 if (!ctx) {
20052 return;
20053 }
20054
20055 mg_set_thread_name("master");
20056
20057 /* Increase priority of the master thread */
20058#if defined(_WIN32)
20059 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
20060#elif defined(USE_MASTER_THREAD_PRIORITY)
20061 int min_prio = sched_get_priority_min(SCHED_RR);
20062 int max_prio = sched_get_priority_max(SCHED_RR);
20063 if ((min_prio >= 0) && (max_prio >= 0)
20064 && ((USE_MASTER_THREAD_PRIORITY) <= max_prio)
20065 && ((USE_MASTER_THREAD_PRIORITY) >= min_prio)) {
20066 struct sched_param sched_param = {0};
20067 sched_param.sched_priority = (USE_MASTER_THREAD_PRIORITY);
20068 pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
20069 }
20070#endif
20071
20072 /* Initialize thread local storage */
20073#if defined(_WIN32)
20074 tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
20075#endif
20076 tls.is_master = 1;
20077 pthread_setspecific(sTlsKey, &tls);
20078
20079 if (ctx->callbacks.init_thread) {
20080 /* Callback for the master thread (type 0) */
20081 tls.user_ptr = ctx->callbacks.init_thread(ctx, 0);
20082 } else {
20083 tls.user_ptr = NULL;
20084 }
20085
20086 /* Lua background script "start" event */
20087#if defined(USE_LUA)
20088 if (ctx->lua_background_state) {
20089 lua_State *lstate = (lua_State *)ctx->lua_background_state;
20090 pthread_mutex_lock(&ctx->lua_bg_mutex);
20091
20092 /* call "start()" in Lua */
20093 lua_getglobal(lstate, "start");
20094 if (lua_type(lstate, -1) == LUA_TFUNCTION) {
20095 int ret = lua_pcall(lstate, /* args */ 0, /* results */ 0, 0);
20096 if (ret != 0) {
20097 struct mg_connection fc;
20098 lua_cry(fake_connection(&fc, ctx),
20099 ret,
20100 lstate,
20101 "lua_background_script",
20102 "start");
20103 }
20104 } else {
20105 lua_pop(lstate, 1);
20106 }
20107
20108 /* determine if there is a "log()" function in Lua background script */
20109 lua_getglobal(lstate, "log");
20110 if (lua_type(lstate, -1) == LUA_TFUNCTION) {
20111 ctx->lua_bg_log_available = 1;
20112 }
20113 lua_pop(lstate, 1);
20114
20115 pthread_mutex_unlock(&ctx->lua_bg_mutex);
20116 }
20117#endif
20118
20119 /* Server starts *now* */
20120 ctx->start_time = time(NULL);
20121
20122 /* Server accept loop */
20123 pfd = ctx->listening_socket_fds;
20124 while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
20125 for (i = 0; i < ctx->num_listening_sockets; i++) {
20126 pfd[i].fd = ctx->listening_sockets[i].sock;
20127 pfd[i].events = POLLIN;
20128 }
20129
20130 if (mg_poll(pfd,
20133 &(ctx->stop_flag))
20134 > 0) {
20135 for (i = 0; i < ctx->num_listening_sockets; i++) {
20136 /* NOTE(lsm): on QNX, poll() returns POLLRDNORM after the
20137 * successful poll, and POLLIN is defined as
20138 * (POLLRDNORM | POLLRDBAND)
20139 * Therefore, we're checking pfd[i].revents & POLLIN, not
20140 * pfd[i].revents == POLLIN. */
20141 if (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
20142 && (pfd[i].revents & POLLIN)) {
20144 }
20145 }
20146 }
20147 }
20148
20149 /* Here stop_flag is 1 - Initiate shutdown. */
20150 DEBUG_TRACE("%s", "stopping workers");
20151
20152 /* Stop signal received: somebody called mg_stop. Quit. */
20154
20155 /* Wakeup workers that are waiting for connections to handle. */
20156#if defined(ALTERNATIVE_QUEUE)
20157 for (i = 0; i < ctx->cfg_worker_threads; i++) {
20158 event_signal(ctx->client_wait_events[i]);
20159 }
20160#else
20161 (void)pthread_mutex_lock(&ctx->thread_mutex);
20162 pthread_cond_broadcast(&ctx->sq_full);
20163 (void)pthread_mutex_unlock(&ctx->thread_mutex);
20164#endif
20165
20166 /* Join all worker threads to avoid leaking threads. */
20167 workerthreadcount = ctx->cfg_worker_threads;
20168 for (i = 0; i < workerthreadcount; i++) {
20169 if (ctx->worker_threadids[i] != 0) {
20171 }
20172 }
20173
20174#if defined(USE_LUA)
20175 /* Free Lua state of lua background task */
20176 if (ctx->lua_background_state) {
20177 lua_State *lstate = (lua_State *)ctx->lua_background_state;
20178 ctx->lua_bg_log_available = 0;
20179
20180 /* call "stop()" in Lua */
20181 pthread_mutex_lock(&ctx->lua_bg_mutex);
20182 lua_getglobal(lstate, "stop");
20183 if (lua_type(lstate, -1) == LUA_TFUNCTION) {
20184 int ret = lua_pcall(lstate, /* args */ 0, /* results */ 0, 0);
20185 if (ret != 0) {
20186 struct mg_connection fc;
20187 lua_cry(fake_connection(&fc, ctx),
20188 ret,
20189 lstate,
20190 "lua_background_script",
20191 "stop");
20192 }
20193 }
20194 DEBUG_TRACE("Close Lua background state %p", lstate);
20195 lua_close(lstate);
20196
20197 ctx->lua_background_state = 0;
20198 pthread_mutex_unlock(&ctx->lua_bg_mutex);
20199 }
20200#endif
20201
20202 DEBUG_TRACE("%s", "exiting");
20203
20204 /* call exit thread callback */
20205 if (ctx->callbacks.exit_thread) {
20206 /* Callback for the master thread (type 0) */
20207 ctx->callbacks.exit_thread(ctx, 0, tls.user_ptr);
20208 }
20209
20210#if defined(_WIN32)
20211 CloseHandle(tls.pthread_cond_helper_mutex);
20212#endif
20213 pthread_setspecific(sTlsKey, NULL);
20214
20215 /* Signal mg_stop() that we're done.
20216 * WARNING: This must be the very last thing this
20217 * thread does, as ctx becomes invalid after this line. */
20218 STOP_FLAG_ASSIGN(&ctx->stop_flag, 2);
20219}
20220
20221
20222/* Threads have different return types on Windows and Unix. */
20223#if defined(_WIN32)
20224static unsigned __stdcall master_thread(void *thread_func_param)
20225{
20226 master_thread_run((struct mg_context *)thread_func_param);
20227 return 0;
20228}
20229#else
20230static void *
20231master_thread(void *thread_func_param)
20232{
20233#if !defined(__ZEPHYR__)
20234 struct sigaction sa;
20235
20236 /* Ignore SIGPIPE */
20237 memset(&sa, 0, sizeof(sa));
20238 sa.sa_handler = SIG_IGN;
20239 sigaction(SIGPIPE, &sa, NULL);
20240#endif
20241
20242 master_thread_run((struct mg_context *)thread_func_param);
20243 return NULL;
20244}
20245#endif /* _WIN32 */
20246
20247
20248static void
20250{
20251 int i;
20252 struct mg_handler_info *tmp_rh;
20253
20254 if (ctx == NULL) {
20255 return;
20256 }
20257
20258 /* Call user callback */
20259 if (ctx->callbacks.exit_context) {
20260 ctx->callbacks.exit_context(ctx);
20261 }
20262
20263 /* All threads exited, no sync is needed. Destroy thread mutex and
20264 * condvars
20265 */
20266 (void)pthread_mutex_destroy(&ctx->thread_mutex);
20267
20268#if defined(ALTERNATIVE_QUEUE)
20269 mg_free(ctx->client_socks);
20270 if (ctx->client_wait_events != NULL) {
20271 for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) {
20272 event_destroy(ctx->client_wait_events[i]);
20273 }
20274 mg_free(ctx->client_wait_events);
20275 }
20276#else
20277 (void)pthread_cond_destroy(&ctx->sq_empty);
20278 (void)pthread_cond_destroy(&ctx->sq_full);
20279 mg_free(ctx->squeue);
20280#endif
20281
20282 /* Destroy other context global data structures mutex */
20283 (void)pthread_mutex_destroy(&ctx->nonce_mutex);
20284
20285#if defined(USE_LUA)
20286 (void)pthread_mutex_destroy(&ctx->lua_bg_mutex);
20287#endif
20288
20289 /* Deallocate config parameters */
20290 for (i = 0; i < NUM_OPTIONS; i++) {
20291 if (ctx->dd.config[i] != NULL) {
20292#if defined(_MSC_VER)
20293#pragma warning(suppress : 6001)
20294#endif
20295 mg_free(ctx->dd.config[i]);
20296 }
20297 }
20298
20299 /* Deallocate request handlers */
20300 while (ctx->dd.handlers) {
20301 tmp_rh = ctx->dd.handlers;
20302 ctx->dd.handlers = tmp_rh->next;
20303 mg_free(tmp_rh->uri);
20304 mg_free(tmp_rh);
20305 }
20306
20307#if defined(USE_MBEDTLS)
20308 if (ctx->dd.ssl_ctx != NULL) {
20309 mbed_sslctx_uninit(ctx->dd.ssl_ctx);
20310 mg_free(ctx->dd.ssl_ctx);
20311 ctx->dd.ssl_ctx = NULL;
20312 }
20313
20314#elif !defined(NO_SSL)
20315 /* Deallocate SSL context */
20316 if (ctx->dd.ssl_ctx != NULL) {
20317 void *ssl_ctx = (void *)ctx->dd.ssl_ctx;
20318 int callback_ret =
20320 ? 0
20321 : (ctx->callbacks.external_ssl_ctx(&ssl_ctx, ctx->user_data));
20322
20323 if (callback_ret == 0) {
20324 SSL_CTX_free(ctx->dd.ssl_ctx);
20325 }
20326 /* else: ignore error and omit SSL_CTX_free in case
20327 * callback_ret is 1 */
20328 }
20329#endif /* !NO_SSL */
20330
20331 /* Deallocate worker thread ID array */
20333
20334 /* Deallocate worker thread ID array */
20336
20337 /* deallocate system name string */
20338 mg_free(ctx->systemName);
20339
20340 /* Deallocate context itself */
20341 mg_free(ctx);
20342}
20343
20344
20345CIVETWEB_API void
20347{
20348 pthread_t mt;
20349 if (!ctx) {
20350 return;
20351 }
20352
20353 /* We don't use a lock here. Calling mg_stop with the same ctx from
20354 * two threads is not allowed. */
20355 mt = ctx->masterthreadid;
20356 if (mt == 0) {
20357 return;
20358 }
20359
20360 ctx->masterthreadid = 0;
20361
20362 /* Set stop flag, so all threads know they have to exit. */
20363 STOP_FLAG_ASSIGN(&ctx->stop_flag, 1);
20364
20365 /* Join timer thread */
20366#if defined(USE_TIMERS)
20367 timers_exit(ctx);
20368#endif
20369
20370 /* Wait until everything has stopped. */
20371 while (!STOP_FLAG_IS_TWO(&ctx->stop_flag)) {
20372 (void)mg_sleep(10);
20373 }
20374
20375 /* Wait to stop master thread */
20376 mg_join_thread(mt);
20377
20378 /* Close remaining Lua states */
20379#if defined(USE_LUA)
20380 lua_ctx_exit(ctx);
20381#endif
20382
20383 /* Free memory */
20384 free_context(ctx);
20385}
20386
20387
20388static void
20389get_system_name(char **sysName)
20390{
20391#if defined(_WIN32)
20392 char name[128];
20393 DWORD dwVersion = 0;
20394 DWORD dwMajorVersion = 0;
20395 DWORD dwMinorVersion = 0;
20396 DWORD dwBuild = 0;
20397 BOOL wowRet, isWoW = FALSE;
20398
20399#if defined(_MSC_VER)
20400#pragma warning(push)
20401 /* GetVersion was declared deprecated */
20402#pragma warning(disable : 4996)
20403#endif
20404 dwVersion = GetVersion();
20405#if defined(_MSC_VER)
20406#pragma warning(pop)
20407#endif
20408
20409 dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
20410 dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
20411 dwBuild = ((dwVersion < 0x80000000) ? (DWORD)(HIWORD(dwVersion)) : 0);
20412 (void)dwBuild;
20413
20414 wowRet = IsWow64Process(GetCurrentProcess(), &isWoW);
20415
20416 sprintf(name,
20417 "Windows %u.%u%s",
20418 (unsigned)dwMajorVersion,
20419 (unsigned)dwMinorVersion,
20420 (wowRet ? (isWoW ? " (WoW64)" : "") : " (?)"));
20421
20422 *sysName = mg_strdup(name);
20423
20424#elif defined(__ZEPHYR__)
20425 *sysName = mg_strdup("Zephyr OS");
20426#else
20427 struct utsname name;
20428 memset(&name, 0, sizeof(name));
20429 uname(&name);
20430 *sysName = mg_strdup(name.sysname);
20431#endif
20432}
20433
20434
20435static void
20436legacy_init(const char **options)
20437{
20438 const char *ports_option = config_options[LISTENING_PORTS].default_value;
20439
20440 if (options) {
20441 const char **run_options = options;
20442 const char *optname = config_options[LISTENING_PORTS].name;
20443
20444 /* Try to find the "listening_ports" option */
20445 while (*run_options) {
20446 if (!strcmp(*run_options, optname)) {
20447 ports_option = run_options[1];
20448 }
20449 run_options += 2;
20450 }
20451 }
20452
20453 if (is_ssl_port_used(ports_option)) {
20454 /* Initialize with SSL support */
20456 } else {
20457 /* Initialize without SSL support */
20459 }
20460}
20461
20462
20463CIVETWEB_API struct mg_context *
20465{
20466 struct mg_context *ctx;
20467 const char *name, *value, *default_value;
20468 int idx, ok, workerthreadcount;
20469 unsigned int i;
20470 int itmp;
20471 void (*exit_callback)(const struct mg_context *ctx) = 0;
20472 const char **options =
20473 ((init != NULL) ? (init->configuration_options) : (NULL));
20474
20475 struct mg_workerTLS tls;
20476
20477 if (error != NULL) {
20479 error->code_sub = 0;
20480 if (error->text_buffer_size > 0) {
20481 *error->text = 0;
20482 }
20483 }
20484
20485 if (mg_init_library_called == 0) {
20486 /* Legacy INIT, if mg_start is called without mg_init_library.
20487 * Note: This will cause a memory leak when unloading the library.
20488 */
20489 legacy_init(options);
20490 }
20491 if (mg_init_library_called == 0) {
20492 if (error != NULL) {
20495 NULL, /* No truncation check for error buffers */
20496 error->text,
20497 error->text_buffer_size,
20498 "%s",
20499 "Library uninitialized");
20500 }
20501 return NULL;
20502 }
20503
20504 /* Allocate context and initialize reasonable general case defaults. */
20505 ctx = (struct mg_context *)mg_calloc(1, sizeof(*ctx));
20506 if (ctx == NULL) {
20507 if (error != NULL) {
20509 error->code_sub = (unsigned)sizeof(*ctx);
20511 NULL, /* No truncation check for error buffers */
20512 error->text,
20513 error->text_buffer_size,
20514 "%s",
20515 "Out of memory");
20516 }
20517 return NULL;
20518 }
20519
20520 /* Random number generator will initialize at the first call */
20521 ctx->dd.auth_nonce_mask =
20522 (uint64_t)get_random() ^ (uint64_t)(ptrdiff_t)(options);
20523
20524 /* Save started thread index to reuse in other external API calls
20525 * For the sake of thread synchronization all non-civetweb threads
20526 * can be considered as single external thread */
20528 tls.is_master = -1; /* Thread calling mg_start */
20529 tls.thread_idx = ctx->starter_thread_idx;
20530#if defined(_WIN32)
20531 tls.pthread_cond_helper_mutex = NULL;
20532#endif
20533 pthread_setspecific(sTlsKey, &tls);
20534
20535 ok = (0 == pthread_mutex_init(&ctx->thread_mutex, &pthread_mutex_attr));
20536#if !defined(ALTERNATIVE_QUEUE)
20537 ok &= (0 == pthread_cond_init(&ctx->sq_empty, NULL));
20538 ok &= (0 == pthread_cond_init(&ctx->sq_full, NULL));
20539 ctx->sq_blocked = 0;
20540#endif
20541 ok &= (0 == pthread_mutex_init(&ctx->nonce_mutex, &pthread_mutex_attr));
20542#if defined(USE_LUA)
20543 ok &= (0 == pthread_mutex_init(&ctx->lua_bg_mutex, &pthread_mutex_attr));
20544#endif
20545 if (!ok) {
20546 unsigned error_id = (unsigned)ERRNO;
20547 const char *err_msg =
20548 "Cannot initialize thread synchronization objects";
20549 /* Fatal error - abort start. However, this situation should never
20550 * occur in practice. */
20551
20552 mg_cry_ctx_internal(ctx, "%s", err_msg);
20553 if (error != NULL) {
20555 error->code_sub = error_id;
20557 NULL, /* No truncation check for error buffers */
20558 error->text,
20559 error->text_buffer_size,
20560 "%s",
20561 err_msg);
20562 }
20563
20564 mg_free(ctx);
20565 pthread_setspecific(sTlsKey, NULL);
20566 return NULL;
20567 }
20568
20569 if ((init != NULL) && (init->callbacks != NULL)) {
20570 /* Set all callbacks except exit_context. */
20571 ctx->callbacks = *init->callbacks;
20572 exit_callback = init->callbacks->exit_context;
20573 /* The exit callback is activated once the context is successfully
20574 * created. It should not be called, if an incomplete context object
20575 * is deleted during a failed initialization. */
20576 ctx->callbacks.exit_context = 0;
20577 }
20578 ctx->user_data = ((init != NULL) ? (init->user_data) : (NULL));
20579 ctx->dd.handlers = NULL;
20580 ctx->dd.next = NULL;
20581
20582#if defined(USE_LUA)
20583 lua_ctx_init(ctx);
20584#endif
20585
20586 /* Store options */
20587 while (options && (name = *options++) != NULL) {
20588 idx = get_option_index(name);
20589 if (idx == -1) {
20590 mg_cry_ctx_internal(ctx, "Invalid option: %s", name);
20591 if (error != NULL) {
20593 error->code_sub = (unsigned)-1;
20595 NULL, /* No truncation check for error buffers */
20596 error->text,
20597 error->text_buffer_size,
20598 "Invalid configuration option: %s",
20599 name);
20600 }
20601
20602 free_context(ctx);
20603 pthread_setspecific(sTlsKey, NULL);
20604 return NULL;
20605
20606 } else if ((value = *options++) == NULL) {
20607 mg_cry_ctx_internal(ctx, "%s: option value cannot be NULL", name);
20608 if (error != NULL) {
20610 error->code_sub = (unsigned)idx;
20612 NULL, /* No truncation check for error buffers */
20613 error->text,
20614 error->text_buffer_size,
20615 "Invalid configuration option value: %s",
20616 name);
20617 }
20618
20619 free_context(ctx);
20620 pthread_setspecific(sTlsKey, NULL);
20621 return NULL;
20622 }
20623 if (ctx->dd.config[idx] != NULL) {
20624 /* A duplicate configuration option is not an error - the last
20625 * option value will be used. */
20626 mg_cry_ctx_internal(ctx, "warning: %s: duplicate option", name);
20627 mg_free(ctx->dd.config[idx]);
20628 }
20629 ctx->dd.config[idx] = mg_strdup_ctx(value, ctx);
20630 DEBUG_TRACE("[%s] -> [%s]", name, value);
20631 }
20632
20633 /* Set default value if needed */
20634 for (i = 0; config_options[i].name != NULL; i++) {
20635 default_value = config_options[i].default_value;
20636 if ((ctx->dd.config[i] == NULL) && (default_value != NULL)) {
20637 ctx->dd.config[i] = mg_strdup_ctx(default_value, ctx);
20638 }
20639 }
20640
20641 /* Request size option */
20642 itmp = atoi(ctx->dd.config[MAX_REQUEST_SIZE]);
20643 if (itmp < 1024) {
20645 "%s too small",
20647 if (error != NULL) {
20649 error->code_sub = (unsigned)MAX_REQUEST_SIZE;
20651 NULL, /* No truncation check for error buffers */
20652 error->text,
20653 error->text_buffer_size,
20654 "Invalid configuration option value: %s",
20656 }
20657
20658 free_context(ctx);
20659 pthread_setspecific(sTlsKey, NULL);
20660 return NULL;
20661 }
20662 ctx->max_request_size = (unsigned)itmp;
20663
20664 /* Queue length */
20665#if !defined(ALTERNATIVE_QUEUE)
20666 itmp = atoi(ctx->dd.config[CONNECTION_QUEUE_SIZE]);
20667 if (itmp < 1) {
20669 "%s too small",
20671 if (error != NULL) {
20673 error->code_sub = CONNECTION_QUEUE_SIZE;
20675 NULL, /* No truncation check for error buffers */
20676 error->text,
20677 error->text_buffer_size,
20678 "Invalid configuration option value: %s",
20680 }
20681
20682 free_context(ctx);
20683 pthread_setspecific(sTlsKey, NULL);
20684 return NULL;
20685 }
20686 ctx->squeue =
20687 (struct socket *)mg_calloc((unsigned int)itmp, sizeof(struct socket));
20688 if (ctx->squeue == NULL) {
20690 "Out of memory: Cannot allocate %s",
20692 if (error != NULL) {
20694 error->code_sub = (unsigned)itmp * (unsigned)sizeof(struct socket);
20696 NULL, /* No truncation check for error buffers */
20697 error->text,
20698 error->text_buffer_size,
20699 "Out of memory: Cannot allocate %s",
20701 }
20702
20703 free_context(ctx);
20704 pthread_setspecific(sTlsKey, NULL);
20705 return NULL;
20706 }
20707 ctx->sq_size = itmp;
20708#endif
20709
20710 /* Worker thread count option */
20711 workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]);
20712
20713 if ((workerthreadcount > MAX_WORKER_THREADS) || (workerthreadcount <= 0)) {
20714 if (workerthreadcount <= 0) {
20715 mg_cry_ctx_internal(ctx, "%s", "Invalid number of worker threads");
20716 } else {
20717 mg_cry_ctx_internal(ctx, "%s", "Too many worker threads");
20718 }
20719 if (error != NULL) {
20721 error->code_sub = NUM_THREADS;
20723 NULL, /* No truncation check for error buffers */
20724 error->text,
20725 error->text_buffer_size,
20726 "Invalid configuration option value: %s",
20728 }
20729
20730 free_context(ctx);
20731 pthread_setspecific(sTlsKey, NULL);
20732 return NULL;
20733 }
20734
20735 /* Document root */
20736#if defined(NO_FILES)
20737 if (ctx->dd.config[DOCUMENT_ROOT] != NULL) {
20738 mg_cry_ctx_internal(ctx, "%s", "Document root must not be set");
20739 if (error != NULL) {
20741 error->code_sub = (unsigned)DOCUMENT_ROOT;
20743 NULL, /* No truncation check for error buffers */
20744 error->text,
20745 error->text_buffer_size,
20746 "Invalid configuration option value: %s",
20748 }
20749
20750 free_context(ctx);
20751 pthread_setspecific(sTlsKey, NULL);
20752 return NULL;
20753 }
20754#endif
20755
20757
20758#if defined(USE_LUA)
20759 /* If a Lua background script has been configured, start it. */
20760 ctx->lua_bg_log_available = 0;
20761 if (ctx->dd.config[LUA_BACKGROUND_SCRIPT] != NULL) {
20762 char ebuf[256];
20763 struct vec opt_vec;
20764 struct vec eq_vec;
20765 const char *sparams;
20766
20767 memset(ebuf, 0, sizeof(ebuf));
20768 pthread_mutex_lock(&ctx->lua_bg_mutex);
20769
20770 /* Create a Lua state, load all standard libraries and the mg table */
20771 lua_State *state = mg_lua_context_script_prepare(
20772 ctx->dd.config[LUA_BACKGROUND_SCRIPT], ctx, ebuf, sizeof(ebuf));
20773 if (!state) {
20775 "lua_background_script load error: %s",
20776 ebuf);
20777 if (error != NULL) {
20780 NULL, /* No truncation check for error buffers */
20781 error->text,
20782 error->text_buffer_size,
20783 "Error in script %s: %s",
20784 config_options[LUA_BACKGROUND_SCRIPT].name,
20785 ebuf);
20786 }
20787
20788 pthread_mutex_unlock(&ctx->lua_bg_mutex);
20789
20790 free_context(ctx);
20791 pthread_setspecific(sTlsKey, NULL);
20792 return NULL;
20793 }
20794
20795 /* Add a table with parameters into mg.params */
20796 sparams = ctx->dd.config[LUA_BACKGROUND_SCRIPT_PARAMS];
20797 if (sparams && sparams[0]) {
20798 lua_getglobal(state, "mg");
20799 lua_pushstring(state, "params");
20800 lua_newtable(state);
20801
20802 while ((sparams = next_option(sparams, &opt_vec, &eq_vec))
20803 != NULL) {
20804 reg_llstring(
20805 state, opt_vec.ptr, opt_vec.len, eq_vec.ptr, eq_vec.len);
20806 if (mg_strncasecmp(sparams, opt_vec.ptr, opt_vec.len) == 0)
20807 break;
20808 }
20809 lua_rawset(state, -3);
20810 lua_pop(state, 1);
20811 }
20812
20813 /* Call script */
20814 state = mg_lua_context_script_run(state,
20815 ctx->dd.config[LUA_BACKGROUND_SCRIPT],
20816 ctx,
20817 ebuf,
20818 sizeof(ebuf));
20819 if (!state) {
20821 "lua_background_script start error: %s",
20822 ebuf);
20823 if (error != NULL) {
20826 NULL, /* No truncation check for error buffers */
20827 error->text,
20828 error->text_buffer_size,
20829 "Error in script %s: %s",
20831 ebuf);
20832 }
20833 pthread_mutex_unlock(&ctx->lua_bg_mutex);
20834
20835 free_context(ctx);
20836 pthread_setspecific(sTlsKey, NULL);
20837 return NULL;
20838 }
20839
20840 /* state remains valid */
20841 ctx->lua_background_state = (void *)state;
20842 pthread_mutex_unlock(&ctx->lua_bg_mutex);
20843
20844 } else {
20845 ctx->lua_background_state = 0;
20846 }
20847#endif
20848
20849 /* Step by step initialization of ctx - depending on build options */
20850#if !defined(NO_FILESYSTEMS)
20851 if (!set_gpass_option(ctx, NULL)) {
20852 const char *err_msg = "Invalid global password file";
20853 /* Fatal error - abort start. */
20854 mg_cry_ctx_internal(ctx, "%s", err_msg);
20855
20856 if (error != NULL) {
20859 NULL, /* No truncation check for error buffers */
20860 error->text,
20861 error->text_buffer_size,
20862 "%s",
20863 err_msg);
20864 }
20865 free_context(ctx);
20866 pthread_setspecific(sTlsKey, NULL);
20867 return NULL;
20868 }
20869#endif
20870
20871#if defined(USE_MBEDTLS)
20872 if (!mg_sslctx_init(ctx, NULL)) {
20873 const char *err_msg = "Error initializing SSL context";
20874 /* Fatal error - abort start. */
20875 mg_cry_ctx_internal(ctx, "%s", err_msg);
20876
20877 if (error != NULL) {
20880 NULL, /* No truncation check for error buffers */
20881 error->text,
20882 error->text_buffer_size,
20883 "%s",
20884 err_msg);
20885 }
20886
20887 free_context(ctx);
20888 pthread_setspecific(sTlsKey, NULL);
20889 return NULL;
20890 }
20891
20892#elif !defined(NO_SSL)
20893 if (!init_ssl_ctx(ctx, NULL)) {
20894 const char *err_msg = "Error initializing SSL context";
20895 /* Fatal error - abort start. */
20896 mg_cry_ctx_internal(ctx, "%s", err_msg);
20897
20898 if (error != NULL) {
20901 NULL, /* No truncation check for error buffers */
20902 error->text,
20903 error->text_buffer_size,
20904 "%s",
20905 err_msg);
20906 }
20907
20908 free_context(ctx);
20909 pthread_setspecific(sTlsKey, NULL);
20910 return NULL;
20911 }
20912#endif
20913
20914 if (!set_ports_option(ctx)) {
20915 const char *err_msg = "Failed to setup server ports";
20916 /* Fatal error - abort start. */
20917 mg_cry_ctx_internal(ctx, "%s", err_msg);
20918
20919 if (error != NULL) {
20922 NULL, /* No truncation check for error buffers */
20923 error->text,
20924 error->text_buffer_size,
20925 "%s",
20926 err_msg);
20927 }
20928
20929 free_context(ctx);
20930 pthread_setspecific(sTlsKey, NULL);
20931 return NULL;
20932 }
20933
20934#if !defined(_WIN32) && !defined(__ZEPHYR__)
20935 if (!set_uid_option(ctx)) {
20936 const char *err_msg = "Failed to run as configured user";
20937 /* Fatal error - abort start. */
20938 mg_cry_ctx_internal(ctx, "%s", err_msg);
20939
20940 if (error != NULL) {
20943 NULL, /* No truncation check for error buffers */
20944 error->text,
20945 error->text_buffer_size,
20946 "%s",
20947 err_msg);
20948 }
20949
20950 free_context(ctx);
20951 pthread_setspecific(sTlsKey, NULL);
20952 return NULL;
20953 }
20954#endif
20955
20956 if (!set_acl_option(ctx)) {
20957 const char *err_msg = "Failed to setup access control list";
20958 /* Fatal error - abort start. */
20959 mg_cry_ctx_internal(ctx, "%s", err_msg);
20960
20961 if (error != NULL) {
20964 NULL, /* No truncation check for error buffers */
20965 error->text,
20966 error->text_buffer_size,
20967 "%s",
20968 err_msg);
20969 }
20970
20971 free_context(ctx);
20972 pthread_setspecific(sTlsKey, NULL);
20973 return NULL;
20974 }
20975
20976 ctx->cfg_worker_threads = ((unsigned int)(workerthreadcount));
20977 ctx->worker_threadids = (pthread_t *)mg_calloc_ctx(ctx->cfg_worker_threads,
20978 sizeof(pthread_t),
20979 ctx);
20980
20981 if (ctx->worker_threadids == NULL) {
20982 const char *err_msg = "Not enough memory for worker thread ID array";
20983 mg_cry_ctx_internal(ctx, "%s", err_msg);
20984
20985 if (error != NULL) {
20987 error->code_sub =
20988 (unsigned)ctx->cfg_worker_threads * (unsigned)sizeof(pthread_t);
20990 NULL, /* No truncation check for error buffers */
20991 error->text,
20992 error->text_buffer_size,
20993 "%s",
20994 err_msg);
20995 }
20996
20997 free_context(ctx);
20998 pthread_setspecific(sTlsKey, NULL);
20999 return NULL;
21000 }
21001 ctx->worker_connections =
21003 sizeof(struct mg_connection),
21004 ctx);
21005 if (ctx->worker_connections == NULL) {
21006 const char *err_msg =
21007 "Not enough memory for worker thread connection array";
21008 mg_cry_ctx_internal(ctx, "%s", err_msg);
21009
21010 if (error != NULL) {
21012 error->code_sub = (unsigned)ctx->cfg_worker_threads
21013 * (unsigned)sizeof(struct mg_connection);
21015 NULL, /* No truncation check for error buffers */
21016 error->text,
21017 error->text_buffer_size,
21018 "%s",
21019 err_msg);
21020 }
21021
21022 free_context(ctx);
21023 pthread_setspecific(sTlsKey, NULL);
21024 return NULL;
21025 }
21026
21027#if defined(ALTERNATIVE_QUEUE)
21028 ctx->client_wait_events =
21029 (void **)mg_calloc_ctx(ctx->cfg_worker_threads,
21030 sizeof(ctx->client_wait_events[0]),
21031 ctx);
21032 if (ctx->client_wait_events == NULL) {
21033 const char *err_msg = "Not enough memory for worker event array";
21034 mg_cry_ctx_internal(ctx, "%s", err_msg);
21036
21037 if (error != NULL) {
21039 error->code_sub = (unsigned)ctx->cfg_worker_threads
21040 * (unsigned)sizeof(ctx->client_wait_events[0]);
21042 NULL, /* No truncation check for error buffers */
21043 error->text,
21044 error->text_buffer_size,
21045 "%s",
21046 err_msg);
21047 }
21048
21049 free_context(ctx);
21050 pthread_setspecific(sTlsKey, NULL);
21051 return NULL;
21052 }
21053
21054 ctx->client_socks =
21056 sizeof(ctx->client_socks[0]),
21057 ctx);
21058 if (ctx->client_socks == NULL) {
21059 const char *err_msg = "Not enough memory for worker socket array";
21060 mg_cry_ctx_internal(ctx, "%s", err_msg);
21061 mg_free(ctx->client_wait_events);
21063
21064 if (error != NULL) {
21066 error->code_sub = (unsigned)ctx->cfg_worker_threads
21067 * (unsigned)sizeof(ctx->client_socks[0]);
21069 NULL, /* No truncation check for error buffers */
21070 error->text,
21071 error->text_buffer_size,
21072 "%s",
21073 err_msg);
21074 }
21075
21076 free_context(ctx);
21077 pthread_setspecific(sTlsKey, NULL);
21078 return NULL;
21079 }
21080
21081 for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) {
21082 ctx->client_wait_events[i] = event_create();
21083 if (ctx->client_wait_events[i] == 0) {
21084 const char *err_msg = "Error creating worker event %i";
21085 mg_cry_ctx_internal(ctx, err_msg, i);
21086 while (i > 0) {
21087 i--;
21088 event_destroy(ctx->client_wait_events[i]);
21089 }
21090 mg_free(ctx->client_socks);
21091 mg_free(ctx->client_wait_events);
21093
21094 if (error != NULL) {
21096 error->code_sub = (unsigned)ERRNO;
21098 NULL, /* No truncation check for error buffers */
21099 error->text,
21100 error->text_buffer_size,
21101 err_msg,
21102 i);
21103 }
21104
21105 free_context(ctx);
21106 pthread_setspecific(sTlsKey, NULL);
21107 return NULL;
21108 }
21109 }
21110#endif
21111
21112#if defined(USE_TIMERS)
21113 if (timers_init(ctx) != 0) {
21114 const char *err_msg = "Error creating timers";
21115 mg_cry_ctx_internal(ctx, "%s", err_msg);
21116
21117 if (error != NULL) {
21119 error->code_sub = (unsigned)ERRNO;
21121 NULL, /* No truncation check for error buffers */
21122 error->text,
21123 error->text_buffer_size,
21124 "%s",
21125 err_msg);
21126 }
21127
21128 free_context(ctx);
21129 pthread_setspecific(sTlsKey, NULL);
21130 return NULL;
21131 }
21132#endif
21133
21134 /* Context has been created - init user libraries */
21135 if (ctx->callbacks.init_context) {
21136 ctx->callbacks.init_context(ctx);
21137 }
21138
21139 /* From now, the context is successfully created.
21140 * When it is destroyed, the exit callback should be called. */
21141 ctx->callbacks.exit_context = exit_callback;
21142 ctx->context_type = CONTEXT_SERVER; /* server context */
21143
21144 /* Start worker threads */
21145 for (i = 0; i < ctx->cfg_worker_threads; i++) {
21146 /* worker_thread sets up the other fields */
21147 ctx->worker_connections[i].phys_ctx = ctx;
21149 &ctx->worker_connections[i],
21150 &ctx->worker_threadids[i])
21151 != 0) {
21152
21153 long error_no = (long)ERRNO;
21154
21155 /* thread was not created */
21156 if (i > 0) {
21157 /* If the second, third, ... thread cannot be created, set a
21158 * warning, but keep running. */
21160 "Cannot start worker thread %i: error %ld",
21161 i + 1,
21162 error_no);
21163
21164 /* If the server initialization should stop here, all
21165 * threads that have already been created must be stopped
21166 * first, before any free_context(ctx) call.
21167 */
21168
21169 } else {
21170 /* If the first worker thread cannot be created, stop
21171 * initialization and free the entire server context. */
21173 "Cannot create threads: error %ld",
21174 error_no);
21175
21176 if (error != NULL) {
21178 error->code_sub = (unsigned)error_no;
21180 NULL,
21181 NULL, /* No truncation check for error buffers */
21182 error->text,
21183 error->text_buffer_size,
21184 "Cannot create first worker thread: error %ld",
21185 error_no);
21186 }
21187
21188 free_context(ctx);
21189 pthread_setspecific(sTlsKey, NULL);
21190 return NULL;
21191 }
21192 break;
21193 }
21194 }
21195
21196 /* Start master (listening) thread */
21198
21199 pthread_setspecific(sTlsKey, NULL);
21200 return ctx;
21201}
21202
21203
21204CIVETWEB_API struct mg_context *
21206 void *user_data,
21207 const char **options)
21208{
21209 struct mg_init_data init = {0};
21210 init.callbacks = callbacks;
21211 init.user_data = user_data;
21212 init.configuration_options = options;
21213
21214 return mg_start2(&init, NULL);
21215}
21216
21217
21218/* Add an additional domain to an already running web server. */
21219CIVETWEB_API int
21221 const char **options,
21222 struct mg_error_data *error)
21223{
21224 const char *name;
21225 const char *value;
21226 const char *default_value;
21227 struct mg_domain_context *new_dom;
21228 struct mg_domain_context *dom;
21229 int idx, i;
21230
21231 if (error != NULL) {
21233 error->code_sub = 0;
21234 if (error->text_buffer_size > 0) {
21235 *error->text = 0;
21236 }
21237 }
21238
21239 if ((ctx == NULL) || (options == NULL)) {
21240 if (error != NULL) {
21243 NULL, /* No truncation check for error buffers */
21244 error->text,
21245 error->text_buffer_size,
21246 "%s",
21247 "Invalid parameters");
21248 }
21249 return -1;
21250 }
21251
21252 if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
21253 if (error != NULL) {
21256 NULL, /* No truncation check for error buffers */
21257 error->text,
21258 error->text_buffer_size,
21259 "%s",
21260 "Server already stopped");
21261 }
21262 return -7;
21263 }
21264
21265 new_dom = (struct mg_domain_context *)
21266 mg_calloc_ctx(1, sizeof(struct mg_domain_context), ctx);
21267
21268 if (!new_dom) {
21269 /* Out of memory */
21270 if (error != NULL) {
21272 error->code_sub = (unsigned)sizeof(struct mg_domain_context);
21274 NULL, /* No truncation check for error buffers */
21275 error->text,
21276 error->text_buffer_size,
21277 "%s",
21278 "Out or memory");
21279 }
21280 return -6;
21281 }
21282
21283 /* Store options - TODO: unite duplicate code */
21284 while (options && (name = *options++) != NULL) {
21285 idx = get_option_index(name);
21286 if (idx == -1) {
21287 mg_cry_ctx_internal(ctx, "Invalid option: %s", name);
21288 if (error != NULL) {
21290 error->code_sub = (unsigned)-1;
21292 NULL, /* No truncation check for error buffers */
21293 error->text,
21294 error->text_buffer_size,
21295 "Invalid option: %s",
21296 name);
21297 }
21298 mg_free(new_dom);
21299 return -2;
21300 } else if ((value = *options++) == NULL) {
21301 mg_cry_ctx_internal(ctx, "%s: option value cannot be NULL", name);
21302 if (error != NULL) {
21304 error->code_sub = (unsigned)idx;
21306 NULL, /* No truncation check for error buffers */
21307 error->text,
21308 error->text_buffer_size,
21309 "Invalid option value: %s",
21310 name);
21311 }
21312 mg_free(new_dom);
21313 return -2;
21314 }
21315 if (new_dom->config[idx] != NULL) {
21316 /* Duplicate option: Later values overwrite earlier ones. */
21317 mg_cry_ctx_internal(ctx, "warning: %s: duplicate option", name);
21318 mg_free(new_dom->config[idx]);
21319 }
21320 new_dom->config[idx] = mg_strdup_ctx(value, ctx);
21321 DEBUG_TRACE("[%s] -> [%s]", name, value);
21322 }
21323
21324 /* Authentication domain is mandatory */
21325 /* TODO: Maybe use a new option hostname? */
21326 if (!new_dom->config[AUTHENTICATION_DOMAIN]) {
21327 mg_cry_ctx_internal(ctx, "%s", "authentication domain required");
21328 if (error != NULL) {
21330 error->code_sub = AUTHENTICATION_DOMAIN;
21332 NULL, /* No truncation check for error buffers */
21333 error->text,
21334 error->text_buffer_size,
21335 "Mandatory option %s missing",
21337 }
21338 mg_free(new_dom);
21339 return -4;
21340 }
21341
21342 /* Set default value if needed. Take the config value from
21343 * ctx as a default value. */
21344 for (i = 0; config_options[i].name != NULL; i++) {
21345 default_value = ctx->dd.config[i];
21346 if ((new_dom->config[i] == NULL) && (default_value != NULL)) {
21347 new_dom->config[i] = mg_strdup_ctx(default_value, ctx);
21348 }
21349 }
21350
21351 new_dom->handlers = NULL;
21352 new_dom->next = NULL;
21353 new_dom->nonce_count = 0;
21354 new_dom->auth_nonce_mask = get_random() ^ (get_random() << 31);
21355
21356#if defined(USE_LUA) && defined(USE_WEBSOCKET)
21357 new_dom->shared_lua_websockets = NULL;
21358#endif
21359
21360#if !defined(NO_SSL) && !defined(USE_MBEDTLS)
21361 if (!init_ssl_ctx(ctx, new_dom)) {
21362 /* Init SSL failed */
21363 if (error != NULL) {
21366 NULL, /* No truncation check for error buffers */
21367 error->text,
21368 error->text_buffer_size,
21369 "%s",
21370 "Initializing SSL context failed");
21371 }
21372 mg_free(new_dom);
21373 return -3;
21374 }
21375#endif
21376
21377 /* Add element to linked list. */
21378 mg_lock_context(ctx);
21379
21380 idx = 0;
21381 dom = &(ctx->dd);
21382 for (;;) {
21385 /* Domain collision */
21387 "domain %s already in use",
21388 new_dom->config[AUTHENTICATION_DOMAIN]);
21389 if (error != NULL) {
21392 NULL, /* No truncation check for error buffers */
21393 error->text,
21394 error->text_buffer_size,
21395 "Domain %s specified by %s is already in use",
21396 new_dom->config[AUTHENTICATION_DOMAIN],
21398 }
21399 mg_free(new_dom);
21400 mg_unlock_context(ctx);
21401 return -5;
21402 }
21403
21404 /* Count number of domains */
21405 idx++;
21406
21407 if (dom->next == NULL) {
21408 dom->next = new_dom;
21409 break;
21410 }
21411 dom = dom->next;
21412 }
21413
21414 mg_unlock_context(ctx);
21415
21416 /* Return domain number */
21417 return idx;
21418}
21419
21420
21421CIVETWEB_API int
21422mg_start_domain(struct mg_context *ctx, const char **options)
21423{
21424 return mg_start_domain2(ctx, options, NULL);
21425}
21426
21427
21428/* Feature check API function */
21429CIVETWEB_API unsigned
21430mg_check_feature(unsigned feature)
21431{
21432 static const unsigned feature_set = 0
21433 /* Set bits for available features according to API documentation.
21434 * This bit mask is created at compile time, according to the active
21435 * preprocessor defines. It is a single const value at runtime. */
21436#if !defined(NO_FILES)
21438#endif
21439#if !defined(NO_SSL) || defined(USE_MBEDTLS)
21441#endif
21442#if !defined(NO_CGI)
21444#endif
21445#if defined(USE_IPV6)
21447#endif
21448#if defined(USE_WEBSOCKET)
21450#endif
21451#if defined(USE_LUA)
21453#endif
21454#if defined(USE_DUKTAPE)
21456#endif
21457#if !defined(NO_CACHING)
21459#endif
21460#if defined(USE_SERVER_STATS)
21462#endif
21463#if defined(USE_ZLIB)
21465#endif
21466#if defined(USE_HTTP2)
21468#endif
21469#if defined(USE_X_DOM_SOCKET)
21471#endif
21472
21473 /* Set some extra bits not defined in the API documentation.
21474 * These bits may change without further notice. */
21475#if defined(MG_LEGACY_INTERFACE)
21476 | 0x80000000u
21477#endif
21478#if defined(MG_EXPERIMENTAL_INTERFACES)
21479 | 0x40000000u
21480#endif
21481#if !defined(NO_RESPONSE_BUFFERING)
21482 | 0x20000000u
21483#endif
21484#if defined(MEMORY_DEBUGGING)
21485 | 0x10000000u
21486#endif
21487 ;
21488 return (feature & feature_set);
21489}
21490
21491
21492static size_t
21493mg_str_append(char **dst, char *end, const char *src)
21494{
21495 size_t len = strlen(src);
21496 if (*dst != end) {
21497 /* Append src if enough space, or close dst. */
21498 if ((size_t)(end - *dst) > len) {
21499 strcpy(*dst, src);
21500 *dst += len;
21501 } else {
21502 *dst = end;
21503 }
21504 }
21505 return len;
21506}
21507
21508
21509/* Get system information. It can be printed or stored by the caller.
21510 * Return the size of available information. */
21511CIVETWEB_API int
21512mg_get_system_info(char *buffer, int buflen)
21513{
21514 char *end, *append_eoobj = NULL, block[256];
21515 size_t system_info_length = 0;
21516
21517#if defined(_WIN32)
21518 static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n";
21519#else
21520 static const char eol[] = "\n", eoobj[] = "\n}\n";
21521#endif
21522
21523 if ((buffer == NULL) || (buflen < 1)) {
21524 buflen = 0;
21525 end = buffer;
21526 } else {
21527 *buffer = 0;
21528 end = buffer + buflen;
21529 }
21530 if (buflen > (int)(sizeof(eoobj) - 1)) {
21531 /* has enough space to append eoobj */
21532 append_eoobj = buffer;
21533 if (end) {
21534 end -= sizeof(eoobj) - 1;
21535 }
21536 }
21537
21538 system_info_length += mg_str_append(&buffer, end, "{");
21539
21540 /* Server version */
21541 {
21542 const char *version = mg_version();
21544 NULL,
21545 block,
21546 sizeof(block),
21547 "%s\"version\" : \"%s\"",
21548 eol,
21549 version);
21550 system_info_length += mg_str_append(&buffer, end, block);
21551 }
21552
21553 /* System info */
21554 {
21555#if defined(_WIN32)
21556 DWORD dwVersion = 0;
21557 DWORD dwMajorVersion = 0;
21558 DWORD dwMinorVersion = 0;
21559 SYSTEM_INFO si;
21560
21561 GetSystemInfo(&si);
21562
21563#if defined(_MSC_VER)
21564#pragma warning(push)
21565 /* GetVersion was declared deprecated */
21566#pragma warning(disable : 4996)
21567#endif
21568 dwVersion = GetVersion();
21569#if defined(_MSC_VER)
21570#pragma warning(pop)
21571#endif
21572
21573 dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
21574 dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
21575
21577 NULL,
21578 block,
21579 sizeof(block),
21580 ",%s\"os\" : \"Windows %u.%u\"",
21581 eol,
21582 (unsigned)dwMajorVersion,
21583 (unsigned)dwMinorVersion);
21584 system_info_length += mg_str_append(&buffer, end, block);
21585
21587 NULL,
21588 block,
21589 sizeof(block),
21590 ",%s\"cpu\" : \"type %u, cores %u, mask %x\"",
21591 eol,
21592 (unsigned)si.wProcessorArchitecture,
21593 (unsigned)si.dwNumberOfProcessors,
21594 (unsigned)si.dwActiveProcessorMask);
21595 system_info_length += mg_str_append(&buffer, end, block);
21596#elif defined(__ZEPHYR__)
21598 NULL,
21599 block,
21600 sizeof(block),
21601 ",%s\"os\" : \"%s %s\"",
21602 eol,
21603 "Zephyr OS",
21604 ZEPHYR_VERSION);
21605 system_info_length += mg_str_append(&buffer, end, block);
21606#else
21607 struct utsname name;
21608 memset(&name, 0, sizeof(name));
21609 uname(&name);
21610
21612 NULL,
21613 block,
21614 sizeof(block),
21615 ",%s\"os\" : \"%s %s (%s) - %s\"",
21616 eol,
21617 name.sysname,
21618 name.version,
21619 name.release,
21620 name.machine);
21621 system_info_length += mg_str_append(&buffer, end, block);
21622#endif
21623 }
21624
21625 /* Features */
21626 {
21628 NULL,
21629 block,
21630 sizeof(block),
21631 ",%s\"features\" : %lu"
21632 ",%s\"feature_list\" : \"Server:%s%s%s%s%s%s%s%s%s\"",
21633 eol,
21634 (unsigned long)mg_check_feature(0xFFFFFFFFu),
21635 eol,
21636 mg_check_feature(MG_FEATURES_FILES) ? " Files" : "",
21637 mg_check_feature(MG_FEATURES_SSL) ? " HTTPS" : "",
21638 mg_check_feature(MG_FEATURES_CGI) ? " CGI" : "",
21639 mg_check_feature(MG_FEATURES_IPV6) ? " IPv6" : "",
21641 : "",
21642 mg_check_feature(MG_FEATURES_LUA) ? " Lua" : "",
21643 mg_check_feature(MG_FEATURES_SSJS) ? " JavaScript" : "",
21644 mg_check_feature(MG_FEATURES_CACHE) ? " Cache" : "",
21645 mg_check_feature(MG_FEATURES_STATS) ? " Stats" : "");
21646 system_info_length += mg_str_append(&buffer, end, block);
21647
21648#if defined(USE_LUA)
21650 NULL,
21651 block,
21652 sizeof(block),
21653 ",%s\"lua_version\" : \"%u (%s)\"",
21654 eol,
21655 (unsigned)LUA_VERSION_NUM,
21656 LUA_RELEASE);
21657 system_info_length += mg_str_append(&buffer, end, block);
21658#endif
21659#if defined(USE_DUKTAPE)
21661 NULL,
21662 block,
21663 sizeof(block),
21664 ",%s\"javascript\" : \"Duktape %u.%u.%u\"",
21665 eol,
21666 (unsigned)DUK_VERSION / 10000,
21667 ((unsigned)DUK_VERSION / 100) % 100,
21668 (unsigned)DUK_VERSION % 100);
21669 system_info_length += mg_str_append(&buffer, end, block);
21670#endif
21671 }
21672
21673 /* Build identifier. If BUILD_DATE is not set, __DATE__ will be used. */
21674 {
21675#if defined(BUILD_DATE)
21676 const char *bd = BUILD_DATE;
21677#else
21678#if defined(GCC_DIAGNOSTIC)
21679#if GCC_VERSION >= 40900
21680#pragma GCC diagnostic push
21681 /* Disable idiotic compiler warning -Wdate-time, appeared in gcc5. This
21682 * does not work in some versions. If "BUILD_DATE" is defined to some
21683 * string, it is used instead of __DATE__. */
21684#pragma GCC diagnostic ignored "-Wdate-time"
21685#endif
21686#endif
21687 const char *bd = __DATE__;
21688#if defined(GCC_DIAGNOSTIC)
21689#if GCC_VERSION >= 40900
21690#pragma GCC diagnostic pop
21691#endif
21692#endif
21693#endif
21694
21696 NULL, NULL, block, sizeof(block), ",%s\"build\" : \"%s\"", eol, bd);
21697
21698 system_info_length += mg_str_append(&buffer, end, block);
21699 }
21700
21701 /* Compiler information */
21702 /* http://sourceforge.net/p/predef/wiki/Compilers/ */
21703 {
21704#if defined(_MSC_VER)
21706 NULL,
21707 block,
21708 sizeof(block),
21709 ",%s\"compiler\" : \"MSC: %u (%u)\"",
21710 eol,
21711 (unsigned)_MSC_VER,
21712 (unsigned)_MSC_FULL_VER);
21713 system_info_length += mg_str_append(&buffer, end, block);
21714#elif defined(__MINGW64__)
21716 NULL,
21717 block,
21718 sizeof(block),
21719 ",%s\"compiler\" : \"MinGW64: %u.%u\"",
21720 eol,
21721 (unsigned)__MINGW64_VERSION_MAJOR,
21722 (unsigned)__MINGW64_VERSION_MINOR);
21723 system_info_length += mg_str_append(&buffer, end, block);
21725 NULL,
21726 block,
21727 sizeof(block),
21728 ",%s\"compiler\" : \"MinGW32: %u.%u\"",
21729 eol,
21730 (unsigned)__MINGW32_MAJOR_VERSION,
21731 (unsigned)__MINGW32_MINOR_VERSION);
21732 system_info_length += mg_str_append(&buffer, end, block);
21733#elif defined(__MINGW32__)
21735 NULL,
21736 block,
21737 sizeof(block),
21738 ",%s\"compiler\" : \"MinGW32: %u.%u\"",
21739 eol,
21740 (unsigned)__MINGW32_MAJOR_VERSION,
21741 (unsigned)__MINGW32_MINOR_VERSION);
21742 system_info_length += mg_str_append(&buffer, end, block);
21743#elif defined(__clang__)
21745 NULL,
21746 block,
21747 sizeof(block),
21748 ",%s\"compiler\" : \"clang: %u.%u.%u (%s)\"",
21749 eol,
21750 __clang_major__,
21751 __clang_minor__,
21752 __clang_patchlevel__,
21753 __clang_version__);
21754 system_info_length += mg_str_append(&buffer, end, block);
21755#elif defined(__GNUC__)
21757 NULL,
21758 block,
21759 sizeof(block),
21760 ",%s\"compiler\" : \"gcc: %u.%u.%u\"",
21761 eol,
21762 (unsigned)__GNUC__,
21763 (unsigned)__GNUC_MINOR__,
21764 (unsigned)__GNUC_PATCHLEVEL__);
21765 system_info_length += mg_str_append(&buffer, end, block);
21766#elif defined(__INTEL_COMPILER)
21768 NULL,
21769 block,
21770 sizeof(block),
21771 ",%s\"compiler\" : \"Intel C/C++: %u\"",
21772 eol,
21773 (unsigned)__INTEL_COMPILER);
21774 system_info_length += mg_str_append(&buffer, end, block);
21775#elif defined(__BORLANDC__)
21777 NULL,
21778 block,
21779 sizeof(block),
21780 ",%s\"compiler\" : \"Borland C: 0x%x\"",
21781 eol,
21782 (unsigned)__BORLANDC__);
21783 system_info_length += mg_str_append(&buffer, end, block);
21784#elif defined(__SUNPRO_C)
21786 NULL,
21787 block,
21788 sizeof(block),
21789 ",%s\"compiler\" : \"Solaris: 0x%x\"",
21790 eol,
21791 (unsigned)__SUNPRO_C);
21792 system_info_length += mg_str_append(&buffer, end, block);
21793#else
21795 NULL,
21796 block,
21797 sizeof(block),
21798 ",%s\"compiler\" : \"other\"",
21799 eol);
21800 system_info_length += mg_str_append(&buffer, end, block);
21801#endif
21802 }
21803
21804 /* Determine 32/64 bit data mode.
21805 * see https://en.wikipedia.org/wiki/64-bit_computing */
21806 {
21808 NULL,
21809 block,
21810 sizeof(block),
21811 ",%s\"data_model\" : \"int:%u/%u/%u/%u, float:%u/%u/%u, "
21812 "char:%u/%u, "
21813 "ptr:%u, size:%u, time:%u\"",
21814 eol,
21815 (unsigned)sizeof(short),
21816 (unsigned)sizeof(int),
21817 (unsigned)sizeof(long),
21818 (unsigned)sizeof(long long),
21819 (unsigned)sizeof(float),
21820 (unsigned)sizeof(double),
21821 (unsigned)sizeof(long double),
21822 (unsigned)sizeof(char),
21823 (unsigned)sizeof(wchar_t),
21824 (unsigned)sizeof(void *),
21825 (unsigned)sizeof(size_t),
21826 (unsigned)sizeof(time_t));
21827 system_info_length += mg_str_append(&buffer, end, block);
21828 }
21829
21830 /* Terminate string */
21831 if (append_eoobj) {
21832 strcat(append_eoobj, eoobj);
21833 }
21834 system_info_length += sizeof(eoobj) - 1;
21835
21836 return (int)system_info_length;
21837}
21838
21839
21840/* Get context information. It can be printed or stored by the caller.
21841 * Return the size of available information. */
21842CIVETWEB_API int
21843mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen)
21844{
21845#if defined(USE_SERVER_STATS)
21846 char *end, *append_eoobj = NULL, block[256];
21847 size_t context_info_length = 0;
21848
21849#if defined(_WIN32)
21850 static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n";
21851#else
21852 static const char eol[] = "\n", eoobj[] = "\n}\n";
21853#endif
21854 struct mg_memory_stat *ms = get_memory_stat((struct mg_context *)ctx);
21855
21856 if ((buffer == NULL) || (buflen < 1)) {
21857 buflen = 0;
21858 end = buffer;
21859 } else {
21860 *buffer = 0;
21861 end = buffer + buflen;
21862 }
21863 if (buflen > (int)(sizeof(eoobj) - 1)) {
21864 /* has enough space to append eoobj */
21865 append_eoobj = buffer;
21866 end -= sizeof(eoobj) - 1;
21867 }
21868
21869 context_info_length += mg_str_append(&buffer, end, "{");
21870
21871 if (ms) { /* <-- should be always true */
21872 /* Memory information */
21873 int blockCount = (int)ms->blockCount;
21874 int64_t totalMemUsed = ms->totalMemUsed;
21875 int64_t maxMemUsed = ms->maxMemUsed;
21876 if (totalMemUsed > maxMemUsed) {
21877 maxMemUsed = totalMemUsed;
21878 }
21879
21881 NULL,
21882 block,
21883 sizeof(block),
21884 "%s\"memory\" : {%s"
21885 "\"blocks\" : %i,%s"
21886 "\"used\" : %" INT64_FMT ",%s"
21887 "\"maxUsed\" : %" INT64_FMT "%s"
21888 "}",
21889 eol,
21890 eol,
21891 blockCount,
21892 eol,
21893 totalMemUsed,
21894 eol,
21895 maxMemUsed,
21896 eol);
21897 context_info_length += mg_str_append(&buffer, end, block);
21898 }
21899
21900 if (ctx) {
21901 /* Declare all variables at begin of the block, to comply
21902 * with old C standards. */
21903 char start_time_str[64] = {0};
21904 char now_str[64] = {0};
21905 time_t start_time = ctx->start_time;
21906 time_t now = time(NULL);
21907 int64_t total_data_read, total_data_written;
21908 int active_connections = (int)ctx->active_connections;
21909 int max_active_connections = (int)ctx->max_active_connections;
21910 int total_connections = (int)ctx->total_connections;
21911 if (active_connections > max_active_connections) {
21912 max_active_connections = active_connections;
21913 }
21914 if (active_connections > total_connections) {
21915 total_connections = active_connections;
21916 }
21917
21918 /* Connections information */
21920 NULL,
21921 block,
21922 sizeof(block),
21923 ",%s\"connections\" : {%s"
21924 "\"active\" : %i,%s"
21925 "\"maxActive\" : %i,%s"
21926 "\"total\" : %i%s"
21927 "}",
21928 eol,
21929 eol,
21930 active_connections,
21931 eol,
21932 max_active_connections,
21933 eol,
21934 total_connections,
21935 eol);
21936 context_info_length += mg_str_append(&buffer, end, block);
21937
21938 /* Queue information */
21939#if !defined(ALTERNATIVE_QUEUE)
21941 NULL,
21942 block,
21943 sizeof(block),
21944 ",%s\"queue\" : {%s"
21945 "\"length\" : %i,%s"
21946 "\"filled\" : %i,%s"
21947 "\"maxFilled\" : %i,%s"
21948 "\"full\" : %s%s"
21949 "}",
21950 eol,
21951 eol,
21952 ctx->sq_size,
21953 eol,
21954 ctx->sq_head - ctx->sq_tail,
21955 eol,
21956 ctx->sq_max_fill,
21957 eol,
21958 (ctx->sq_blocked ? "true" : "false"),
21959 eol);
21960 context_info_length += mg_str_append(&buffer, end, block);
21961#endif
21962
21963 /* Requests information */
21965 NULL,
21966 block,
21967 sizeof(block),
21968 ",%s\"requests\" : {%s"
21969 "\"total\" : %lu%s"
21970 "}",
21971 eol,
21972 eol,
21973 (unsigned long)ctx->total_requests,
21974 eol);
21975 context_info_length += mg_str_append(&buffer, end, block);
21976
21977 /* Data information */
21978 total_data_read =
21979 mg_atomic_add64((volatile int64_t *)&ctx->total_data_read, 0);
21980 total_data_written =
21981 mg_atomic_add64((volatile int64_t *)&ctx->total_data_written, 0);
21983 NULL,
21984 block,
21985 sizeof(block),
21986 ",%s\"data\" : {%s"
21987 "\"read\" : %" INT64_FMT ",%s"
21988 "\"written\" : %" INT64_FMT "%s"
21989 "}",
21990 eol,
21991 eol,
21992 total_data_read,
21993 eol,
21994 total_data_written,
21995 eol);
21996 context_info_length += mg_str_append(&buffer, end, block);
21997
21998 /* Execution time information */
21999 gmt_time_string(start_time_str,
22000 sizeof(start_time_str) - 1,
22001 &start_time);
22002 gmt_time_string(now_str, sizeof(now_str) - 1, &now);
22003
22005 NULL,
22006 block,
22007 sizeof(block),
22008 ",%s\"time\" : {%s"
22009 "\"uptime\" : %.0f,%s"
22010 "\"start\" : \"%s\",%s"
22011 "\"now\" : \"%s\"%s"
22012 "}",
22013 eol,
22014 eol,
22015 difftime(now, start_time),
22016 eol,
22017 start_time_str,
22018 eol,
22019 now_str,
22020 eol);
22021 context_info_length += mg_str_append(&buffer, end, block);
22022 }
22023
22024 /* Terminate string */
22025 if (append_eoobj) {
22026 strcat(append_eoobj, eoobj);
22027 }
22028 context_info_length += sizeof(eoobj) - 1;
22029
22030 return (int)context_info_length;
22031#else
22032 (void)ctx;
22033 if ((buffer != NULL) && (buflen > 0)) {
22034 *buffer = 0;
22035 }
22036 return 0;
22037#endif
22038}
22039
22040
22041CIVETWEB_API void
22043{
22044 /* https://github.com/civetweb/civetweb/issues/727 */
22045 if (conn != NULL) {
22046 conn->must_close = 1;
22047 }
22048}
22049
22050
22051#if defined(MG_EXPERIMENTAL_INTERFACES)
22052/* Get connection information. It can be printed or stored by the caller.
22053 * Return the size of available information. */
22054CIVETWEB_API int
22055mg_get_connection_info(const struct mg_context *ctx,
22056 int idx,
22057 char *buffer,
22058 int buflen)
22059{
22060 const struct mg_connection *conn;
22061 const struct mg_request_info *ri;
22062 char *end, *append_eoobj = NULL, block[256];
22063 size_t connection_info_length = 0;
22064 int state = 0;
22065 const char *state_str = "unknown";
22066
22067#if defined(_WIN32)
22068 static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n";
22069#else
22070 static const char eol[] = "\n", eoobj[] = "\n}\n";
22071#endif
22072
22073 if ((buffer == NULL) || (buflen < 1)) {
22074 buflen = 0;
22075 end = buffer;
22076 } else {
22077 *buffer = 0;
22078 end = buffer + buflen;
22079 }
22080 if (buflen > (int)(sizeof(eoobj) - 1)) {
22081 /* has enough space to append eoobj */
22082 append_eoobj = buffer;
22083 end -= sizeof(eoobj) - 1;
22084 }
22085
22086 if ((ctx == NULL) || (idx < 0)) {
22087 /* Parameter error */
22088 return 0;
22089 }
22090
22091 if ((unsigned)idx >= ctx->cfg_worker_threads) {
22092 /* Out of range */
22093 return 0;
22094 }
22095
22096 /* Take connection [idx]. This connection is not locked in
22097 * any way, so some other thread might use it. */
22098 conn = (ctx->worker_connections) + idx;
22099
22100 /* Initialize output string */
22101 connection_info_length += mg_str_append(&buffer, end, "{");
22102
22103 /* Init variables */
22104 ri = &(conn->request_info);
22105
22106#if defined(USE_SERVER_STATS)
22107 state = conn->conn_state;
22108
22109 /* State as string */
22110 switch (state) {
22111 case 0:
22112 state_str = "undefined";
22113 break;
22114 case 1:
22115 state_str = "not used";
22116 break;
22117 case 2:
22118 state_str = "init";
22119 break;
22120 case 3:
22121 state_str = "ready";
22122 break;
22123 case 4:
22124 state_str = "processing";
22125 break;
22126 case 5:
22127 state_str = "processed";
22128 break;
22129 case 6:
22130 state_str = "to close";
22131 break;
22132 case 7:
22133 state_str = "closing";
22134 break;
22135 case 8:
22136 state_str = "closed";
22137 break;
22138 case 9:
22139 state_str = "done";
22140 break;
22141 }
22142#endif
22143
22144 /* Connection info */
22145 if ((state >= 3) && (state < 9)) {
22147 NULL,
22148 block,
22149 sizeof(block),
22150 "%s\"connection\" : {%s"
22151 "\"remote\" : {%s"
22152 "\"protocol\" : \"%s\",%s"
22153 "\"addr\" : \"%s\",%s"
22154 "\"port\" : %u%s"
22155 "},%s"
22156 "\"handled_requests\" : %u%s"
22157 "}",
22158 eol,
22159 eol,
22160 eol,
22161 get_proto_name(conn),
22162 eol,
22163 ri->remote_addr,
22164 eol,
22165 ri->remote_port,
22166 eol,
22167 eol,
22168 conn->handled_requests,
22169 eol);
22170 connection_info_length += mg_str_append(&buffer, end, block);
22171 }
22172
22173 /* Request info */
22174 if ((state >= 4) && (state < 6)) {
22176 NULL,
22177 block,
22178 sizeof(block),
22179 "%s%s\"request_info\" : {%s"
22180 "\"method\" : \"%s\",%s"
22181 "\"uri\" : \"%s\",%s"
22182 "\"query\" : %s%s%s%s"
22183 "}",
22184 (connection_info_length > 1 ? "," : ""),
22185 eol,
22186 eol,
22187 ri->request_method,
22188 eol,
22189 ri->request_uri,
22190 eol,
22191 ri->query_string ? "\"" : "",
22192 ri->query_string ? ri->query_string : "null",
22193 ri->query_string ? "\"" : "",
22194 eol);
22195 connection_info_length += mg_str_append(&buffer, end, block);
22196 }
22197
22198 /* Execution time information */
22199 if ((state >= 2) && (state < 9)) {
22200 char start_time_str[64] = {0};
22201 char close_time_str[64] = {0};
22202 time_t start_time = conn->conn_birth_time;
22203 time_t close_time = 0;
22204 double time_diff;
22205
22206 gmt_time_string(start_time_str,
22207 sizeof(start_time_str) - 1,
22208 &start_time);
22209#if defined(USE_SERVER_STATS)
22210 close_time = conn->conn_close_time;
22211#endif
22212 if (close_time != 0) {
22213 time_diff = difftime(close_time, start_time);
22214 gmt_time_string(close_time_str,
22215 sizeof(close_time_str) - 1,
22216 &close_time);
22217 } else {
22218 time_t now = time(NULL);
22219 time_diff = difftime(now, start_time);
22220 close_time_str[0] = 0; /* or use "now" ? */
22221 }
22222
22224 NULL,
22225 block,
22226 sizeof(block),
22227 "%s%s\"time\" : {%s"
22228 "\"uptime\" : %.0f,%s"
22229 "\"start\" : \"%s\",%s"
22230 "\"closed\" : \"%s\"%s"
22231 "}",
22232 (connection_info_length > 1 ? "," : ""),
22233 eol,
22234 eol,
22235 time_diff,
22236 eol,
22237 start_time_str,
22238 eol,
22239 close_time_str,
22240 eol);
22241 connection_info_length += mg_str_append(&buffer, end, block);
22242 }
22243
22244 /* Remote user name */
22245 if ((ri->remote_user) && (state < 9)) {
22247 NULL,
22248 block,
22249 sizeof(block),
22250 "%s%s\"user\" : {%s"
22251 "\"name\" : \"%s\",%s"
22252 "}",
22253 (connection_info_length > 1 ? "," : ""),
22254 eol,
22255 eol,
22256 ri->remote_user,
22257 eol);
22258 connection_info_length += mg_str_append(&buffer, end, block);
22259 }
22260
22261 /* Data block */
22262 if (state >= 3) {
22264 NULL,
22265 block,
22266 sizeof(block),
22267 "%s%s\"data\" : {%s"
22268 "\"read\" : %" INT64_FMT ",%s"
22269 "\"written\" : %" INT64_FMT "%s"
22270 "}",
22271 (connection_info_length > 1 ? "," : ""),
22272 eol,
22273 eol,
22274 conn->consumed_content,
22275 eol,
22276 conn->num_bytes_sent,
22277 eol);
22278 connection_info_length += mg_str_append(&buffer, end, block);
22279 }
22280
22281 /* State */
22283 NULL,
22284 block,
22285 sizeof(block),
22286 "%s%s\"state\" : \"%s\"",
22287 (connection_info_length > 1 ? "," : ""),
22288 eol,
22289 state_str);
22290 connection_info_length += mg_str_append(&buffer, end, block);
22291
22292 /* Terminate string */
22293 if (append_eoobj) {
22294 strcat(append_eoobj, eoobj);
22295 }
22296 connection_info_length += sizeof(eoobj) - 1;
22297
22298 return (int)connection_info_length;
22299}
22300
22301#if 0
22302/* Get handler information. It can be printed or stored by the caller.
22303 * Return the size of available information. */
22304CIVETWEB_API int
22305mg_get_handler_info(struct mg_context *ctx,
22306 char *buffer,
22307 int buflen)
22308{
22309 int handler_info_len = 0;
22310 struct mg_handler_info *tmp_rh;
22311 mg_lock_context(ctx);
22312
22313 for (tmp_rh = ctx->dd.handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
22314
22315 if (buflen > handler_info_len+ tmp_rh->uri_len) {
22316 memcpy(buffer+handler_info_len, tmp_rh->uri, tmp_rh->uri_len);
22317 }
22318 handler_info_len += tmp_rh->uri_len;
22319
22320 switch (tmp_rh->handler_type) {
22321 case REQUEST_HANDLER:
22322 (void)tmp_rh->handler;
22323 break;
22324 case WEBSOCKET_HANDLER:
22325 (void)tmp_rh->connect_handler;
22326 (void) tmp_rh->ready_handler;
22327 (void) tmp_rh->data_handler;
22328 (void) tmp_rh->close_handler;
22329 break;
22330 case AUTH_HANDLER:
22331 (void) tmp_rh->auth_handler;
22332 break;
22333 }
22334 (void)cbdata;
22335 }
22336
22337 mg_unlock_context(ctx);
22338 return handler_info_len;
22339}
22340#endif
22341#endif
22342
22343
22344/* Initialize this library. This function does not need to be thread safe.
22345 */
22346CIVETWEB_API unsigned
22347mg_init_library(unsigned features)
22348{
22349 unsigned features_to_init = mg_check_feature(features & 0xFFu);
22350 unsigned features_inited = features_to_init;
22351
22352 if (mg_init_library_called <= 0) {
22353 /* Not initialized yet */
22354 if (0 != pthread_mutex_init(&global_lock_mutex, NULL)) {
22355 return 0;
22356 }
22357 }
22358
22360
22361 if (mg_init_library_called <= 0) {
22362 int i;
22363 size_t len;
22364
22365#if defined(_WIN32)
22366 int file_mutex_init = 1;
22367 int wsa = 1;
22368#else
22369 int mutexattr_init = 1;
22370#endif
22371 int failed = 1;
22372 int key_create = pthread_key_create(&sTlsKey, tls_dtor);
22373
22374 if (key_create == 0) {
22375#if defined(_WIN32)
22376 file_mutex_init =
22377 pthread_mutex_init(&global_log_file_lock, &pthread_mutex_attr);
22378 if (file_mutex_init == 0) {
22379 /* Start WinSock */
22380 WSADATA data;
22381 failed = wsa = WSAStartup(MAKEWORD(2, 2), &data);
22382 }
22383#else
22384 mutexattr_init = pthread_mutexattr_init(&pthread_mutex_attr);
22385 if (mutexattr_init == 0) {
22386 failed = pthread_mutexattr_settype(&pthread_mutex_attr,
22387 PTHREAD_MUTEX_RECURSIVE);
22388 }
22389#endif
22390 }
22391
22392 if (failed) {
22393#if defined(_WIN32)
22394 if (wsa == 0) {
22395 (void)WSACleanup();
22396 }
22397 if (file_mutex_init == 0) {
22398 (void)pthread_mutex_destroy(&global_log_file_lock);
22399 }
22400#else
22401 if (mutexattr_init == 0) {
22402 (void)pthread_mutexattr_destroy(&pthread_mutex_attr);
22403 }
22404#endif
22405 if (key_create == 0) {
22406 (void)pthread_key_delete(sTlsKey);
22407 }
22409 (void)pthread_mutex_destroy(&global_lock_mutex);
22410 return 0;
22411 }
22412
22413 len = 1;
22414 for (i = 0; http_methods[i].name != NULL; i++) {
22415 size_t sl = strlen(http_methods[i].name);
22416 len += sl;
22417 if (i > 0) {
22418 len += 2;
22419 }
22420 }
22421 all_methods = (char *)mg_malloc(len);
22422 if (!all_methods) {
22423 /* Must never happen */
22425 (void)pthread_mutex_destroy(&global_lock_mutex);
22426 return 0;
22427 }
22428 all_methods[0] = 0;
22429 for (i = 0; http_methods[i].name != NULL; i++) {
22430 if (i > 0) {
22431 strcat(all_methods, ", ");
22432 strcat(all_methods, http_methods[i].name);
22433 } else {
22434 strcpy(all_methods, http_methods[i].name);
22435 }
22436 }
22437 }
22438
22439#if defined(USE_LUA)
22440 lua_init_optional_libraries();
22441#endif
22442
22443#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1) \
22444 || defined(OPENSSL_API_3_0)) \
22445 && !defined(NO_SSL)
22446
22447 if (features_to_init & MG_FEATURES_SSL) {
22448 if (!mg_openssl_initialized) {
22449 char ebuf[128];
22450 if (initialize_openssl(ebuf, sizeof(ebuf))) {
22451 mg_openssl_initialized = 1;
22452 } else {
22453 (void)ebuf;
22454 DEBUG_TRACE("Initializing SSL failed: %s", ebuf);
22455 features_inited &= ~((unsigned)(MG_FEATURES_SSL));
22456 }
22457 } else {
22458 /* ssl already initialized */
22459 }
22460 }
22461
22462#endif
22463
22464 if (mg_init_library_called <= 0) {
22466 } else {
22468 }
22470
22471 return features_inited;
22472}
22473
22474
22475/* Un-initialize this library. */
22476CIVETWEB_API unsigned
22478{
22479 if (mg_init_library_called <= 0) {
22480 return 0;
22481 }
22482
22484
22486 if (mg_init_library_called == 0) {
22487#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1)) && !defined(NO_SSL)
22488 if (mg_openssl_initialized) {
22490 mg_openssl_initialized = 0;
22491 }
22492#endif
22493
22494#if defined(_WIN32)
22495 (void)WSACleanup();
22496 (void)pthread_mutex_destroy(&global_log_file_lock);
22497#else
22498 (void)pthread_mutexattr_destroy(&pthread_mutex_attr);
22499#endif
22500
22501 (void)pthread_key_delete(sTlsKey);
22502
22503#if defined(USE_LUA)
22504 lua_exit_optional_libraries();
22505#endif
22507 all_methods = NULL;
22508
22510 (void)pthread_mutex_destroy(&global_lock_mutex);
22511 return 1;
22512 }
22513
22515 return 1;
22516}
22517
22518
22519/* End of civetweb.c */
static void process_new_connection(struct mg_connection *conn)
Definition civetweb.c:19368
CIVETWEB_API int mg_start_domain(struct mg_context *ctx, const char **options)
Definition civetweb.c:21422
CIVETWEB_API void mg_lock_context(struct mg_context *ctx)
Definition civetweb.c:13018
static int is_authorized_for_put(struct mg_connection *conn)
Definition civetweb.c:9035
static int consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
Definition civetweb.c:19624
static int parse_http_request(char *buf, int len, struct mg_request_info *ri)
Definition civetweb.c:10915
#define mg_readdir(x)
Definition civetweb.c:923
static pthread_key_t sTlsKey
Definition civetweb.c:1580
static void sockaddr_to_string(char *buf, size_t len, const union usa *usa)
Definition civetweb.c:3292
CIVETWEB_API int mg_start_domain2(struct mg_context *ctx, const char **options, struct mg_error_data *error)
Definition civetweb.c:21220
static void open_auth_file(struct mg_connection *conn, const char *path, struct mg_file *filep)
Definition civetweb.c:8472
char static_assert_replacement[1]
Definition civetweb.c:125
#define IP_ADDR_STR_LEN
Definition civetweb.c:1732
CIVETWEB_API const char * mg_version(void)
Definition civetweb.c:3514
@ ENABLE_DIRECTORY_LISTING
Definition civetweb.c:2002
@ SSL_SHORT_TRUST
Definition civetweb.c:2020
@ GLOBAL_PASSWORDS_FILE
Definition civetweb.c:2004
@ ADDITIONAL_HEADER
Definition civetweb.c:2052
@ SSL_VERIFY_DEPTH
Definition civetweb.c:2016
@ ACCESS_CONTROL_ALLOW_ORIGIN
Definition civetweb.c:2041
@ SSL_PROTOCOL_VERSION
Definition civetweb.c:2019
@ SSL_DO_VERIFY_PEER
Definition civetweb.c:2012
@ SSL_CERTIFICATE
Definition civetweb.c:2008
@ RUN_AS_USER
Definition civetweb.c:1922
@ ALLOW_INDEX_SCRIPT_SUB_RES
Definition civetweb.c:2053
@ CGI2_BUFFERING
Definition civetweb.c:1975
@ SSL_CACHE_TIMEOUT
Definition civetweb.c:2013
@ CONNECTION_QUEUE_SIZE
Definition civetweb.c:1927
@ ENABLE_KEEP_ALIVE
Definition civetweb.c:1936
@ ACCESS_CONTROL_LIST
Definition civetweb.c:2006
@ CGI_INTERPRETER_ARGS
Definition civetweb.c:1962
@ SSL_CA_PATH
Definition civetweb.c:2014
@ AUTHENTICATION_DOMAIN
Definition civetweb.c:1999
@ ACCESS_CONTROL_ALLOW_HEADERS
Definition civetweb.c:2043
@ SSL_DEFAULT_VERIFY_PATHS
Definition civetweb.c:2017
@ CGI2_EXTENSIONS
Definition civetweb.c:1968
@ ENABLE_WEBDAV
Definition civetweb.c:2003
@ ACCESS_CONTROL_ALLOW_METHODS
Definition civetweb.c:2042
@ STRICT_HTTPS_MAX_AGE
Definition civetweb.c:2050
@ HIDE_FILES
Definition civetweb.c:2011
@ ERROR_LOG_FILE
Definition civetweb.c:1957
@ CGI2_INTERPRETER_ARGS
Definition civetweb.c:1971
@ STATIC_FILE_MAX_AGE
Definition civetweb.c:2046
@ LINGER_TIMEOUT
Definition civetweb.c:1926
@ URL_REWRITE_PATTERN
Definition civetweb.c:2010
@ THROTTLE
Definition civetweb.c:1935
@ SSL_CIPHER_LIST
Definition civetweb.c:2018
@ KEEP_ALIVE_TIMEOUT
Definition civetweb.c:1938
@ CGI_EXTENSIONS
Definition civetweb.c:1959
@ STATIC_FILE_CACHE_CONTROL
Definition civetweb.c:2047
@ ERROR_PAGES
Definition civetweb.c:2044
@ CGI_ENVIRONMENT
Definition civetweb.c:1960
@ PUT_DELETE_PASSWORDS_FILE
Definition civetweb.c:1997
@ ENABLE_AUTH_DOMAIN_CHECK
Definition civetweb.c:2000
@ NUM_OPTIONS
Definition civetweb.c:2055
@ MAX_REQUEST_SIZE
Definition civetweb.c:1925
@ DECODE_URL
Definition civetweb.c:1943
@ NUM_THREADS
Definition civetweb.c:1921
@ DOCUMENT_ROOT
Definition civetweb.c:1954
@ SSL_CERTIFICATE_CHAIN
Definition civetweb.c:2009
@ CGI2_ENVIRONMENT
Definition civetweb.c:1969
@ CGI_BUFFERING
Definition civetweb.c:1966
@ PROTECT_URI
Definition civetweb.c:1998
@ ACCESS_LOG_FILE
Definition civetweb.c:1956
@ REQUEST_TIMEOUT
Definition civetweb.c:1937
@ SSI_EXTENSIONS
Definition civetweb.c:2001
@ CONFIG_TCP_NODELAY
Definition civetweb.c:1923
@ SSL_CA_FILE
Definition civetweb.c:2015
@ CGI_INTERPRETER
Definition civetweb.c:1961
@ LISTEN_BACKLOG_SIZE
Definition civetweb.c:1928
@ DECODE_QUERY_STRING
Definition civetweb.c:1944
@ LISTENING_PORTS
Definition civetweb.c:1920
@ CGI2_INTERPRETER
Definition civetweb.c:1970
@ INDEX_FILES
Definition civetweb.c:2005
@ EXTRA_MIME_TYPES
Definition civetweb.c:2007
CIVETWEB_API void mg_set_websocket_handler_with_subprotocols(struct mg_context *ctx, const char *uri, struct mg_websocket_subprotocols *subprotocols, mg_websocket_connect_handler connect_handler, mg_websocket_ready_handler ready_handler, mg_websocket_data_handler data_handler, mg_websocket_close_handler close_handler, void *cbdata)
Definition civetweb.c:14508
CIVETWEB_API const char * mg_get_response_code_text(const struct mg_connection *conn, int response_code)
Definition civetweb.c:4176
static int check_authorization(struct mg_connection *conn, const char *path)
Definition civetweb.c:8918
#define vsnprintf_impl
Definition civetweb.c:897
#define HEXTOI(x)
#define mg_malloc_ctx(a, c)
Definition civetweb.c:1497
static void redirect_to_https_port(struct mg_connection *conn, int port)
Definition civetweb.c:14235
static int should_switch_to_protocol(const struct mg_connection *conn)
Definition civetweb.c:13912
static void remove_bad_file(const struct mg_connection *conn, const char *path)
Definition civetweb.c:10609
static pid_t spawn_process(struct mg_connection *conn, const char *prog, char *envblk, char *envp[], int fdin[2], int fdout[2], int fderr[2], const char *dir, int cgi_config_idx)
Definition civetweb.c:5768
static int print_dir_entry(struct mg_connection *conn, struct de *de)
Definition civetweb.c:9609
static int mg_path_suspicious(const struct mg_connection *conn, const char *path)
Definition civetweb.c:2871
const char * mime_type
Definition civetweb.c:8224
static int set_non_blocking_mode(SOCKET sock)
Definition civetweb.c:5876
static void ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
Definition civetweb.c:16697
static void mg_cry_internal_wrap(const struct mg_connection *conn, struct mg_context *ctx, const char *func, unsigned line, const char *fmt,...) PRINTF_ARGS(5
Definition civetweb.c:3481
const char * proto
Definition civetweb.c:18378
CIVETWEB_API void mg_lock_connection(struct mg_connection *conn)
Definition civetweb.c:13000
static const char * get_rel_url_at_current_server(const char *uri, const struct mg_connection *conn)
Definition civetweb.c:18465
static void mg_cry_internal_impl(const struct mg_connection *conn, const char *func, unsigned line, const char *fmt, va_list ap)
Definition civetweb.c:3382
#define LOCK_DURATION_S
Definition civetweb.c:2333
CIVETWEB_API void * mg_get_thread_pointer(const struct mg_connection *conn)
Definition civetweb.c:3209
#define MAX_WORKER_THREADS
Definition civetweb.c:465
CIVETWEB_API int mg_base64_encode(const unsigned char *src, size_t src_len, char *dst, size_t *dst_len)
Definition civetweb.c:7305
CIVETWEB_API void * mg_get_user_data(const struct mg_context *ctx)
Definition civetweb.c:3195
static void put_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:12158
CIVETWEB_API long long mg_store_body(struct mg_connection *conn, const char *path)
Definition civetweb.c:10622
CIVETWEB_API void mg_send_mime_file(struct mg_connection *conn, const char *path, const char *mime_type)
Definition civetweb.c:10517
static void do_ssi_exec(struct mg_connection *conn, char *tag)
Definition civetweb.c:12443
static void tls_dtor(void *key)
Definition civetweb.c:16321
static int is_civetweb_webdav_method(const struct mg_connection *conn)
Definition civetweb.c:7482
#define realloc
Definition civetweb.c:1541
const void * SOCK_OPT_TYPE
Definition civetweb.c:863
static int header_has_option(const char *header, const char *option)
Definition civetweb.c:3956
static int ssl_get_client_cert_info(const struct mg_connection *conn, struct mg_client_cert *client_cert)
Definition civetweb.c:16620
#define mg_cry_ctx_internal(ctx, fmt,...)
Definition civetweb.c:2586
CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len)
Definition civetweb.c:3019
#define mg_opendir(conn, x)
Definition civetweb.c:921
static FUNCTION_MAY_BE_UNUSED uint64_t mg_get_current_time_ns(void)
Definition civetweb.c:1676
static char * mg_strndup_ctx(const char *ptr, size_t len, struct mg_context *ctx)
Definition civetweb.c:3047
static int put_dir(struct mg_connection *conn, const char *path)
Definition civetweb.c:10572
#define UINT64_FMT
Definition civetweb.c:927
CIVETWEB_API struct mg_context * mg_start2(struct mg_init_data *init, struct mg_error_data *error)
Definition civetweb.c:20464
static void send_static_cache_header(struct mg_connection *conn)
Definition civetweb.c:4074
CIVETWEB_API void mg_unlock_connection(struct mg_connection *conn)
Definition civetweb.c:13009
CIVETWEB_API const char * mg_get_header(const struct mg_connection *conn, const char *name)
Definition civetweb.c:3855
#define INVALID_SOCKET
Definition civetweb.c:925
size_t ext_len
Definition civetweb.c:8223
static int print_dav_dir_entry(struct de *de, void *data)
Definition civetweb.c:12745
static void delete_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:12293
#define mg_calloc_ctx(a, b, c)
Definition civetweb.c:1498
#define mg_closedir(x)
Definition civetweb.c:922
#define free
Definition civetweb.c:1542
static int abort_cgi_process(void *data)
Definition civetweb.c:11552
static int should_keep_alive(const struct mg_connection *conn)
Definition civetweb.c:3984
static __inline void * mg_malloc(size_t a)
Definition civetweb.c:1474
CIVETWEB_API int mg_start_thread(mg_thread_func_t func, void *param)
Definition civetweb.c:5700
static int mg_fopen(const struct mg_connection *conn, const char *path, int mode, struct mg_file *filep)
Definition civetweb.c:2915
static int alloc_printf(char **out_buf, const char *fmt,...)
Definition civetweb.c:6996
static int connect_socket(struct mg_context *ctx, const char *host, int port, int use_ssl, struct mg_error_data *error, SOCKET *sock, union usa *sa)
Definition civetweb.c:9287
static void handle_static_file_request(struct mg_connection *conn, const char *path, struct mg_file *filep, const char *mime_type, const char *additional_headers)
Definition civetweb.c:10220
static void dav_move_file(struct mg_connection *conn, const char *path, int do_copy)
Definition civetweb.c:11994
static int ssl_use_pem_file(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx, const char *pem, const char *chain)
Definition civetweb.c:16940
static const char * ssl_error(void)
Definition civetweb.c:16585
CIVETWEB_API void mg_set_websocket_handler(struct mg_context *ctx, const char *uri, mg_websocket_connect_handler connect_handler, mg_websocket_ready_handler ready_handler, mg_websocket_data_handler data_handler, mg_websocket_close_handler close_handler, void *cbdata)
Definition civetweb.c:14488
static int must_hide_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:9763
static void dav_proppatch(struct mg_connection *conn, const char *path)
Definition civetweb.c:12964
static void log_access(const struct mg_connection *)
Definition civetweb.c:16063
const char * extension
Definition civetweb.c:8222
#define ERRNO
Definition civetweb.c:924
#define mg_remove(conn, x)
Definition civetweb.c:919
static int mg_poll(struct mg_pollfd *pfd, unsigned int n, int milliseconds, const stop_flag_t *stop_flag)
Definition civetweb.c:5938
CIVETWEB_API int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded)
Definition civetweb.c:7048
static void close_all_listening_sockets(struct mg_context *ctx)
Definition civetweb.c:15456
CIVETWEB_API int mg_get_var2(const char *data, size_t data_len, const char *name, char *dst, size_t dst_len, size_t occurrence)
Definition civetweb.c:7099
#define closesocket(a)
Definition civetweb.c:917
static void send_authorization_request(struct mg_connection *conn, const char *realm)
Definition civetweb.c:8974
static int get_req_headers(const struct mg_request_info *ri, const char *name, const char **output, int output_max_size)
Definition civetweb.c:3836
CIVETWEB_API void mg_unlock_context(struct mg_context *ctx)
Definition civetweb.c:13027
CIVETWEB_API int mg_base64_decode(const char *src, size_t src_len, unsigned char *dst, size_t *dst_len)
Definition civetweb.c:7383
static int check_acl(struct mg_context *phys_ctx, const union usa *sa)
Definition civetweb.c:16229
static const struct mg_option config_options[]
Definition civetweb.c:2062
static FUNCTION_MAY_BE_UNUSED unsigned long mg_current_thread_id(void)
Definition civetweb.c:1626
static const struct mg_http_method_info * get_http_method_info(const char *method)
Definition civetweb.c:10880
#define STOP_FLAG_IS_ZERO(f)
Definition civetweb.c:2322
CIVETWEB_API void mg_stop(struct mg_context *ctx)
Definition civetweb.c:20346
unsigned default_port
Definition civetweb.c:18380
static int get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
Definition civetweb.c:18737
static int parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS])
Definition civetweb.c:10725
static int set_uid_option(struct mg_context *phys_ctx)
Definition civetweb.c:16267
static int switch_domain_context(struct mg_connection *conn)
Definition civetweb.c:14174
CIVETWEB_API int mg_get_cookie(const char *cookie_header, const char *var_name, char *dst, size_t dst_size)
Definition civetweb.c:7256
static struct mg_connection * fake_connection(struct mg_connection *fc, struct mg_context *ctx)
Definition civetweb.c:3470
static void reset_per_request_attributes(struct mg_connection *conn)
Definition civetweb.c:17676
static void handle_file_based_request(struct mg_connection *conn, const char *path, struct mg_file *filep)
Definition civetweb.c:15354
static int compare_dir_entries(const void *p1, const void *p2, void *arg)
Definition civetweb.c:9716
#define FUNCTION_MAY_BE_UNUSED
Definition civetweb.c:316
static pthread_mutex_t * ssl_mutexes
Definition civetweb.c:16451
static void static void mg_set_thread_name(const char *name)
Definition civetweb.c:2780
CIVETWEB_API int mg_send_file_body(struct mg_connection *conn, const char *path)
Definition civetweb.c:10451
static int get_option_index(const char *name)
Definition civetweb.c:3159
CIVETWEB_API int mg_get_server_ports(const struct mg_context *ctx, int size, struct mg_server_port *ports)
Definition civetweb.c:3248
static char * mg_strdup(const char *str)
Definition civetweb.c:3068
static void addenv(struct cgi_environment *env, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(2
static void send_no_cache_header(struct mg_connection *conn)
Definition civetweb.c:4056
static void master_thread_run(struct mg_context *ctx)
Definition civetweb.c:20044
static const char month_names[][4]
Definition civetweb.c:1815
static __inline void * mg_realloc(void *a, size_t b)
Definition civetweb.c:1486
static void accept_new_connection(const struct socket *listener, struct mg_context *ctx)
Definition civetweb.c:19957
static volatile ptrdiff_t cryptolib_users
Definition civetweb.c:16815
static void send_file_data(struct mg_connection *conn, struct mg_file *filep, int64_t offset, int64_t len, int no_buffering)
Definition civetweb.c:10055
static int get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
Definition civetweb.c:18569
#define CRYPTO_LIB
Definition civetweb.c:911
static pthread_mutex_t global_lock_mutex
Definition civetweb.c:1089
#define CGI_ENVIRONMENT_SIZE
Definition civetweb.c:488
#define mg_get_option
Definition civetweb.c:3185
static long ssl_get_protocol(int version_id)
Definition civetweb.c:17017
static int mg_init_library_called
Definition civetweb.c:1552
#define DEBUG_ASSERT(cond)
Definition civetweb.c:260
static size_t mg_str_append(char **dst, char *end, const char *src)
Definition civetweb.c:21493
static int pull_inner(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
Definition civetweb.c:6210
#define INT64_FMT
Definition civetweb.c:926
#define MG_FILE_COMPRESSION_SIZE_LIMIT
Definition civetweb.c:478
#define USA_IN_PORT_UNSAFE(s)
Definition civetweb.c:1860
static int mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
Definition civetweb.c:6494
static int mg_send_http_error_impl(struct mg_connection *conn, int status, const char *fmt, va_list args)
Definition civetweb.c:4375
#define MG_FOPEN_MODE_READ
Definition civetweb.c:2843
#define STOP_FLAG_ASSIGN(f, v)
Definition civetweb.c:2324
CIVETWEB_API int mg_strcasecmp(const char *s1, const char *s2)
Definition civetweb.c:3034
CIVETWEB_API 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)
Definition civetweb.c:19212
static int extention_matches_script(struct mg_connection *conn, const char *filename)
Definition civetweb.c:7504
static void get_host_from_request_info(struct vec *host, const struct mg_request_info *ri)
Definition civetweb.c:14136
static pthread_mutexattr_t pthread_mutex_attr
Definition civetweb.c:1074
CIVETWEB_API int mg_send_digest_access_authentication_request(struct mg_connection *conn, const char *realm)
Definition civetweb.c:9022
static int ssl_servername_callback(SSL *ssl, int *ad, void *arg)
Definition civetweb.c:17066
static char * skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar)
Definition civetweb.c:3757
CIVETWEB_API struct mg_connection * mg_download(const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len, const char *fmt,...)
Definition civetweb.c:18874
CIVETWEB_API const struct mg_response_info * mg_get_response_info(const struct mg_connection *conn)
Definition civetweb.c:3561
static void fclose_on_exec(struct mg_file_access *filep, struct mg_connection *conn)
Definition civetweb.c:10196
static const char * get_header(const struct mg_header *hdr, int num_hdr, const char *name)
Definition civetweb.c:3820
static FUNCTION_MAY_BE_UNUSED ptrdiff_t mg_atomic_dec(volatile ptrdiff_t *addr)
Definition civetweb.c:1144
static int mg_inet_pton(int af, const char *src, void *dst, size_t dstlen, int resolve_src)
Definition civetweb.c:9246
static int init_ssl_ctx_impl(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx, const char *pem, const char *chain)
Definition civetweb.c:17239
static int forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
Definition civetweb.c:11184
static void dav_lock_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:12802
CIVETWEB_API int mg_get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int timeout)
Definition civetweb.c:18822
static int set_gpass_option(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
Definition civetweb.c:17636
CIVETWEB_API struct mg_connection * mg_connect_websocket_client_secure_extensions(const struct mg_client_options *client_options, char *error_buffer, size_t error_buffer_size, const char *path, const char *origin, const char *extensions, mg_websocket_data_handler data_func, mg_websocket_close_handler close_func, void *user_data)
Definition civetweb.c:19300
static int skip_to_end_of_word_and_terminate(char **ppw, int eol)
Definition civetweb.c:10680
CIVETWEB_API int mg_write(struct mg_connection *conn, const void *buf, size_t len)
Definition civetweb.c:6779
CIVETWEB_API unsigned mg_exit_library(void)
Definition civetweb.c:22477
static int get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
Definition civetweb.c:18645
static int set_tcp_nodelay(const struct socket *so, int nodelay_on)
Definition civetweb.c:17718
#define ARRAY_SIZE(array)
Definition civetweb.c:506
static void dav_unlock_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:12931
static int parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
Definition civetweb.c:15501
CIVETWEB_API struct mg_context * mg_get_context(const struct mg_connection *conn)
Definition civetweb.c:3188
static int get_first_ssl_listener_index(const struct mg_context *ctx)
Definition civetweb.c:14121
CIVETWEB_API int mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen)
Definition civetweb.c:21843
#define mg_mkdir(conn, path, mode)
Definition civetweb.c:918
static const struct @6 abs_uri_protocols[]
static const struct @5 builtin_mime_types[]
CIVETWEB_API int mg_modify_passwords_file(const char *fname, const char *domain, const char *user, const char *pass)
Definition civetweb.c:9220
static int hexdump2string(void *mem, int memlen, char *buf, int buflen)
Definition civetweb.c:16594
#define UTF8_PATH_MAX
Definition civetweb.c:861
static const struct mg_http_method_info http_methods[]
Definition civetweb.c:10813
static void handle_directory_request(struct mg_connection *conn, const char *dir)
Definition civetweb.c:9942
static int set_ports_option(struct mg_context *phys_ctx)
Definition civetweb.c:15736
CIVETWEB_API const char * mg_get_builtin_mime_type(const char *path)
Definition civetweb.c:8332
static int read_auth_file(struct mg_file *filep, struct read_auth_file_struct *workdata, int depth)
Definition civetweb.c:8733
static int mg_start_thread_with_id(mg_thread_func_t func, void *param, pthread_t *threadidptr)
Definition civetweb.c:5726
static void * load_tls_dll(char *ebuf, size_t ebuf_len, const char *dll_name, struct ssl_func *sw, int *feature_missing)
Definition civetweb.c:16715
static void gmt_time_string(char *buf, size_t buf_len, time_t *t)
Definition civetweb.c:3341
static const char * mg_strcasestr(const char *big_str, const char *small_str)
Definition civetweb.c:3075
static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
Definition civetweb.c:6439
static void handle_ssi_file_request(struct mg_connection *conn, const char *path, struct mg_file *filep)
Definition civetweb.c:12588
CIVETWEB_API int mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen)
Definition civetweb.c:3745
CIVETWEB_API 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)
Definition civetweb.c:19242
CIVETWEB_API void mg_set_user_connection_data(const struct mg_connection *const_conn, void *data)
Definition civetweb.c:3225
static int set_throttle(const char *spec, const union usa *rsa, const char *uri)
Definition civetweb.c:14075
static void mg_snprintf(const struct mg_connection *conn, int *truncated, char *buf, size_t buflen, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(5
static void ssl_info_callback(const SSL *ssl, int what, int ret)
Definition civetweb.c:17050
#define calloc
Definition civetweb.c:1540
CIVETWEB_API const struct mg_option * mg_get_valid_options(void)
Definition civetweb.c:2833
static void bin2str(char *to, const unsigned char *p, size_t len)
Definition civetweb.c:8389
#define MG_FOPEN_MODE_APPEND
Definition civetweb.c:2849
CIVETWEB_API const struct mg_request_info * mg_get_request_info(const struct mg_connection *conn)
Definition civetweb.c:3521
#define STOP_FLAG_IS_TWO(f)
Definition civetweb.c:2323
static const char * next_option(const char *list, struct vec *val, struct vec *eq_val)
Definition civetweb.c:3899
static const char * suggest_connection_header(const struct mg_connection *conn)
Definition civetweb.c:4046
static char * all_methods
Definition civetweb.c:10876
CIVETWEB_API void mg_set_auth_handler(struct mg_context *ctx, const char *uri, mg_authorization_handler handler, void *cbdata)
Definition civetweb.c:14538
static int alloc_vprintf(char **out_buf, char *prealloc_buf, size_t prealloc_size, const char *fmt, va_list ap)
Definition civetweb.c:6939
static time_t parse_date_string(const char *datetime)
Definition civetweb.c:8005
int SOCKET
Definition civetweb.c:928
static void do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, int include_level)
Definition civetweb.c:12358
CIVETWEB_API int mg_check_digest_access_authentication(struct mg_connection *conn, const char *realm, const char *filename)
Definition civetweb.c:8893
static void uninitialize_openssl(void)
Definition civetweb.c:17594
static int parse_match_net(const struct vec *vec, const union usa *sa, int no_strict)
Definition civetweb.c:13966
static void send_options(struct mg_connection *conn)
Definition civetweb.c:12632
static int lowercase(const char *s)
Definition civetweb.c:3012
static void release_handler_ref(struct mg_connection *conn, struct mg_handler_info *handler_info)
Definition civetweb.c:14687
static int parse_range_header(const char *header, int64_t *a, int64_t *b)
Definition civetweb.c:10169
CIVETWEB_API void * mg_get_user_connection_data(const struct mg_connection *conn)
Definition civetweb.c:3238
static int mg_stat(const struct mg_connection *conn, const char *path, struct mg_file_stat *filep)
Definition civetweb.c:5650
static int get_uri_type(const char *uri)
Definition civetweb.c:18395
#define SSL_LIB
Definition civetweb.c:908
#define DEBUG_TRACE(fmt,...)
Definition civetweb.c:242
static void get_system_name(char **sysName)
Definition civetweb.c:20389
@ CONTEXT_WS_CLIENT
Definition civetweb.c:2265
@ CONTEXT_INVALID
Definition civetweb.c:2262
@ CONTEXT_SERVER
Definition civetweb.c:2263
@ CONTEXT_HTTP_CLIENT
Definition civetweb.c:2264
CIVETWEB_API char * mg_md5(char buf[33],...)
Definition civetweb.c:8404
CIVETWEB_API int mg_get_var(const char *data, size_t data_len, const char *name, char *dst, size_t dst_len)
Definition civetweb.c:7088
static const char * header_val(const struct mg_connection *conn, const char *header)
Definition civetweb.c:16046
#define mg_cry_internal(conn, fmt,...)
Definition civetweb.c:2583
static int set_acl_option(struct mg_context *phys_ctx)
Definition civetweb.c:17662
CIVETWEB_API void mg_disable_connection_keep_alive(struct mg_connection *conn)
Definition civetweb.c:22042
static void get_mime_type(struct mg_connection *conn, const char *path, struct vec *vec)
Definition civetweb.c:8354
static int set_blocking_mode(SOCKET sock)
Definition civetweb.c:5890
#define MSG_NOSIGNAL
Definition civetweb.c:1735
CIVETWEB_API struct mg_connection * mg_connect_client_secure(const struct mg_client_options *client_options, char *error_buffer, size_t error_buffer_size)
Definition civetweb.c:18237
#define MAX_CGI_ENVIR_VARS
Definition civetweb.c:493
static char * mg_strdup_ctx(const char *str, struct mg_context *ctx)
Definition civetweb.c:3062
static int parse_auth_header(struct mg_connection *conn, char *buf, size_t buf_size, struct ah *ah)
Definition civetweb.c:8555
static int is_not_modified(const struct mg_connection *conn, const struct mg_file_stat *filestat)
Definition civetweb.c:10468
static void worker_thread_run(struct mg_connection *conn)
Definition civetweb.c:19704
static int prepare_cgi_environment(struct mg_connection *conn, const char *prog, struct cgi_environment *env, int cgi_config_idx)
Definition civetweb.c:11354
CIVETWEB_API int mg_modify_passwords_file_ha1(const char *fname, const char *domain, const char *user, const char *ha1)
Definition civetweb.c:9057
static const char * get_proto_name(const struct mg_connection *conn)
Definition civetweb.c:3574
static void * master_thread(void *thread_func_param)
Definition civetweb.c:20231
static void mg_set_handler_type(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx, const char *uri, int handler_type, int is_delete_request, mg_request_handler handler, struct mg_websocket_subprotocols *subprotocols, mg_websocket_connect_handler connect_handler, mg_websocket_ready_handler ready_handler, mg_websocket_data_handler data_handler, mg_websocket_close_handler close_handler, mg_authorization_handler auth_handler, void *cbdata)
Definition civetweb.c:14280
static int is_file_opened(const struct mg_file_access *fileacc)
Definition civetweb.c:2853
static int get_http_header_len(const char *buf, int buflen)
Definition civetweb.c:7952
static int is_valid_port(unsigned long port)
Definition civetweb.c:9239
static void handle_propfind(struct mg_connection *conn, const char *path, struct mg_file_stat *filep)
Definition civetweb.c:12759
static void interpret_uri(struct mg_connection *conn, char *filename, size_t filename_buf_len, struct mg_file_stat *filestat, int *is_found, int *is_script_resource, int *is_websocket_request, int *is_put_or_delete_request, int *is_webdav_request, int *is_template_text)
Definition civetweb.c:7622
static int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap)
Definition civetweb.c:7016
#define mg_realloc_ctx(a, b, c)
Definition civetweb.c:1499
static void send_additional_header(struct mg_connection *conn)
Definition civetweb.c:4122
static int push_all(struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int len)
Definition civetweb.c:6162
static int is_put_or_delete_method(const struct mg_connection *conn)
Definition civetweb.c:7464
static int read_message(FILE *fp, struct mg_connection *conn, char *buf, int bufsiz, int *nread)
Definition civetweb.c:11107
#define MG_FOPEN_MODE_WRITE
Definition civetweb.c:2846
static int authorize(struct mg_connection *conn, struct mg_file *filep, const char *realm)
Definition civetweb.c:8861
static void send_ssi_file(struct mg_connection *, const char *, struct mg_file *, int)
Definition civetweb.c:12482
CIVETWEB_API int mg_send_chunk(struct mg_connection *conn, const char *chunk, unsigned int chunk_len)
Definition civetweb.c:6860
@ CONNECTION_TYPE_RESPONSE
Definition civetweb.c:2462
@ CONNECTION_TYPE_INVALID
Definition civetweb.c:2460
@ CONNECTION_TYPE_REQUEST
Definition civetweb.c:2461
#define mg_static_assert(cond, txt)
Definition civetweb.c:126
#define SOCKET_TIMEOUT_QUANTUM
Definition civetweb.c:473
CIVETWEB_API struct mg_context * mg_start(const struct mg_callbacks *callbacks, void *user_data, const char **options)
Definition civetweb.c:21205
static void produce_socket(struct mg_context *ctx, const struct socket *sp)
Definition civetweb.c:19661
static void dav_mkcol(struct mg_connection *conn, const char *path)
Definition civetweb.c:11915
static void remove_dot_segments(char *inout)
Definition civetweb.c:8069
static int get_request_handler(struct mg_connection *conn, int handler_type, mg_request_handler *handler, struct mg_websocket_subprotocols **subprotocols, mg_websocket_connect_handler *connect_handler, mg_websocket_ready_handler *ready_handler, mg_websocket_data_handler *data_handler, mg_websocket_close_handler *close_handler, mg_authorization_handler *auth_handler, void **cbdata, struct mg_handler_info **handler_info)
Definition civetweb.c:14560
#define mg_pollfd
Definition civetweb.c:948
#define MG_BUF_LEN
Definition civetweb.c:498
static int alloc_vprintf2(char **buf, const char *fmt, va_list ap)
Definition civetweb.c:6906
static int extention_matches_template_text(struct mg_connection *conn, const char *filename)
Definition civetweb.c:7550
static int is_in_script_path(const struct mg_connection *conn, const char *path)
Definition civetweb.c:14645
static int should_decode_query_string(const struct mg_connection *conn)
Definition civetweb.c:4034
static double mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before)
Definition civetweb.c:3364
static void free_context(struct mg_context *ctx)
Definition civetweb.c:20249
static int sslize(struct mg_connection *conn, int(*func)(SSL *), const struct mg_client_options *client_options)
Definition civetweb.c:16455
static void legacy_init(const char **options)
Definition civetweb.c:20436
CIVETWEB_API unsigned mg_init_library(unsigned features)
Definition civetweb.c:22347
static void construct_etag(char *buf, size_t buf_len, const struct mg_file_stat *filestat)
Definition civetweb.c:10181
int volatile stop_flag_t
Definition civetweb.c:2321
CIVETWEB_API void mg_send_mime_file2(struct mg_connection *conn, const char *path, const char *mime_type, const char *additional_headers)
Definition civetweb.c:10526
static FUNCTION_MAY_BE_UNUSED ptrdiff_t mg_atomic_inc(volatile ptrdiff_t *addr)
Definition civetweb.c:1121
static int refresh_trust(struct mg_connection *conn)
Definition civetweb.c:16377
#define SHUTDOWN_WR
Definition civetweb.c:517
CIVETWEB_API int mg_send_http_error(struct mg_connection *conn, int status, const char *fmt,...)
Definition civetweb.c:4562
static void mg_vsnprintf(const struct mg_connection *conn, int *truncated, char *buf, size_t buflen, const char *fmt, va_list ap)
Definition civetweb.c:3094
static int should_decode_url(const struct mg_connection *conn)
Definition civetweb.c:4023
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)
Definition civetweb.c:3601
CIVETWEB_API int mg_send_http_redirect(struct mg_connection *conn, const char *target_url, int redirect_code)
Definition civetweb.c:4618
#define ERROR_TRY_AGAIN(err)
Definition civetweb.c:447
static void send_cors_header(struct mg_connection *conn)
Definition civetweb.c:4149
CIVETWEB_API int mg_printf(struct mg_connection *conn, const char *fmt,...)
Definition civetweb.c:7034
static unsigned char b64reverse(char letter)
Definition civetweb.c:7358
#define HTTP1_only
Definition civetweb.c:6609
CIVETWEB_API struct mg_connection * mg_connect_client(const char *host, int port, int use_ssl, char *error_buffer, size_t error_buffer_size)
Definition civetweb.c:18253
#define malloc
Definition civetweb.c:1539
#define INT64_MAX
Definition civetweb.c:513
static void mg_strlcpy(char *dst, const char *src, size_t n)
Definition civetweb.c:3002
static int remove_directory(struct mg_connection *conn, const char *dir)
Definition civetweb.c:9836
static const char * get_http_version(const struct mg_connection *conn)
Definition civetweb.c:3876
static __inline void * mg_calloc(size_t a, size_t b)
Definition civetweb.c:1480
static int check_password_digest(const char *method, const char *ha1, const char *uri, const char *nonce, const char *nc, const char *cnonce, const char *qop, const char *response)
Definition civetweb.c:8427
#define va_copy(x, y)
Definition civetweb.c:1003
static void handle_request(struct mg_connection *)
Definition civetweb.c:14704
static int parse_http_response(char *buf, int len, struct mg_response_info *ri)
Definition civetweb.c:11001
CIVETWEB_API unsigned mg_check_feature(unsigned feature)
Definition civetweb.c:21430
static void * cryptolib_dll_handle
Definition civetweb.c:16806
static const char * mg_fgets(char *buf, size_t size, struct mg_file *filep)
Definition civetweb.c:8696
#define IGNORE_UNUSED_RESULT(a)
Definition civetweb.c:291
static void discard_unread_request_data(struct mg_connection *conn)
Definition civetweb.c:6484
static int mg_fclose(struct mg_file_access *fileacc)
Definition civetweb.c:2986
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, const char *extensions, mg_websocket_data_handler data_func, mg_websocket_close_handler close_func, void *user_data)
Definition civetweb.c:18997
static void * worker_thread(void *thread_func_param)
Definition civetweb.c:19937
static struct mg_connection * mg_connect_client_impl(const struct mg_client_options *client_options, int use_ssl, struct mg_init_data *init, struct mg_error_data *error)
Definition civetweb.c:18001
@ PROTOCOL_TYPE_HTTP1
Definition civetweb.c:2466
@ PROTOCOL_TYPE_HTTP2
Definition civetweb.c:2468
@ PROTOCOL_TYPE_WEBSOCKET
Definition civetweb.c:2467
static __inline void mg_free(void *a)
Definition civetweb.c:1492
static void handle_request_stat_log(struct mg_connection *conn)
Definition civetweb.c:6562
@ AUTH_HANDLER
Definition civetweb.c:2225
@ REQUEST_HANDLER
Definition civetweb.c:2225
@ WEBSOCKET_HANDLER
Definition civetweb.c:2225
CIVETWEB_API struct mg_connection * mg_connect_websocket_client_extensions(const char *host, int port, int use_ssl, char *error_buffer, size_t error_buffer_size, const char *path, const char *origin, const char *extensions, mg_websocket_data_handler data_func, mg_websocket_close_handler close_func, void *user_data)
Definition civetweb.c:19269
static int mg_join_thread(pthread_t threadid)
Definition civetweb.c:5757
CIVETWEB_API void * mg_get_user_context_data(const struct mg_connection *conn)
Definition civetweb.c:3202
CIVETWEB_API int mg_send_http_ok(struct mg_connection *conn, const char *mime_type, long long content_length)
Definition civetweb.c:4576
static void handle_not_modified_static_file_request(struct mg_connection *conn, struct mg_file *filep)
Definition civetweb.c:10483
static int init_ssl_ctx(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
Definition civetweb.c:17485
static void close_socket_gracefully(struct mg_connection *conn)
Definition civetweb.c:17740
CIVETWEB_API void mg_close_connection(struct mg_connection *conn)
Definition civetweb.c:17938
static int is_ssl_port_used(const char *ports)
Definition civetweb.c:15679
static void init_connection(struct mg_connection *conn)
Definition civetweb.c:19329
CIVETWEB_API int mg_split_form_urlencoded(char *data, struct mg_header *form_fields, unsigned num_form_fields)
Definition civetweb.c:7157
static int print_props(struct mg_connection *conn, const char *uri, const char *name, struct mg_file_stat *filep)
Definition civetweb.c:12660
#define INITIAL_DEPTH
Definition civetweb.c:8715
static int dir_scan_callback(struct de *de, void *data)
Definition civetweb.c:9911
size_t proto_len
Definition civetweb.c:18379
static void set_close_on_exec(int fd, const struct mg_connection *conn, struct mg_context *ctx)
Definition civetweb.c:5677
static int get_month_index(const char *s)
Definition civetweb.c:7989
#define NUM_WEBDAV_LOCKS
Definition civetweb.c:2330
static void * ssllib_dll_handle
Definition civetweb.c:16805
static int mg_fgetc(struct mg_file *filep)
Definition civetweb.c:12467
CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata)
Definition civetweb.c:14466
static void handle_cgi_request(struct mg_connection *conn, const char *prog, int cgi_config_idx)
Definition civetweb.c:11587
#define mg_sleep(x)
Definition civetweb.c:920
#define PASSWORDS_FILE_NAME
Definition civetweb.c:482
CIVETWEB_API void mg_send_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:10510
static void close_connection(struct mg_connection *conn)
Definition civetweb.c:17864
static int initialize_openssl(char *ebuf, size_t ebuf_len)
Definition civetweb.c:16821
static int substitute_index_file(struct mg_connection *conn, char *path, size_t path_len, struct mg_file_stat *filestat)
Definition civetweb.c:7574
#define STRUCT_FILE_INITIALIZER
Definition civetweb.c:1891
static volatile ptrdiff_t thread_idx_max
Definition civetweb.c:1581
CIVETWEB_API int mg_get_system_info(char *buffer, int buflen)
Definition civetweb.c:21512
static FUNCTION_MAY_BE_UNUSED void mg_global_unlock(void)
Definition civetweb.c:1102
static FUNCTION_MAY_BE_UNUSED void mg_global_lock(void)
Definition civetweb.c:1094
static int scan_directory(struct mg_connection *conn, const char *dir, void *data, int(*cb)(struct de *, void *))
Definition civetweb.c:9777
static int push_inner(struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int len, double timeout)
Definition civetweb.c:6003
static uint64_t get_random(void)
Definition civetweb.c:5909
CIVETWEB_API int mg_read(struct mg_connection *conn, void *buf, size_t len)
Definition civetweb.c:6614
CIVETWEB_API int mg_url_encode(const char *src, char *dst, size_t dst_len)
Definition civetweb.c:9581
#define mg_cry
Definition civetweb.c:3510
static int is_valid_http_method(const char *method)
Definition civetweb.c:10899
static void url_decode_in_place(char *buf)
Definition civetweb.c:7080
@ MG_FEATURES_HTTP2
Definition civetweb.h:102
@ MG_FEATURES_STATS
Definition civetweb.h:95
@ MG_FEATURES_CACHE
Definition civetweb.h:91
@ MG_FEATURES_FILES
Definition civetweb.h:61
@ MG_FEATURES_CGI
Definition civetweb.h:71
@ MG_FEATURES_IPV6
Definition civetweb.h:75
@ MG_FEATURES_DEFAULT
Definition civetweb.h:57
@ MG_FEATURES_TLS
Definition civetweb.h:66
@ MG_FEATURES_X_DOMAIN_SOCKET
Definition civetweb.h:105
@ MG_FEATURES_SSL
Definition civetweb.h:67
@ MG_FEATURES_LUA
Definition civetweb.h:83
@ MG_FEATURES_WEBSOCKET
Definition civetweb.h:79
@ MG_FEATURES_SSJS
Definition civetweb.h:87
@ MG_FEATURES_COMPRESSION
Definition civetweb.h:99
@ MG_ERROR_DATA_CODE_TLS_CONNECT_ERROR
Definition civetweb.h:1794
@ MG_ERROR_DATA_CODE_INIT_LIBRARY_FAILED
Definition civetweb.h:1758
@ MG_ERROR_DATA_CODE_OS_ERROR
Definition civetweb.h:1761
@ MG_ERROR_DATA_CODE_MISSING_OPTION
Definition civetweb.h:1746
@ MG_ERROR_DATA_CODE_CONNECT_FAILED
Definition civetweb.h:1785
@ MG_ERROR_DATA_CODE_OK
Definition civetweb.h:1734
@ MG_ERROR_DATA_CODE_DUPLICATE_DOMAIN
Definition civetweb.h:1749
@ MG_ERROR_DATA_CODE_INVALID_OPTION
Definition civetweb.h:1740
@ MG_ERROR_DATA_CODE_TLS_SERVER_CERT_ERROR
Definition civetweb.h:1791
@ MG_ERROR_DATA_CODE_INIT_ACL_FAILED
Definition civetweb.h:1770
@ MG_ERROR_DATA_CODE_SCRIPT_ERROR
Definition civetweb.h:1776
@ MG_ERROR_DATA_CODE_CONNECT_TIMEOUT
Definition civetweb.h:1782
@ MG_ERROR_DATA_CODE_INIT_PORTS_FAILED
Definition civetweb.h:1764
@ MG_ERROR_DATA_CODE_INVALID_PARAM
Definition civetweb.h:1737
@ MG_ERROR_DATA_CODE_OUT_OF_MEMORY
Definition civetweb.h:1752
@ MG_ERROR_DATA_CODE_HOST_NOT_FOUND
Definition civetweb.h:1779
@ MG_ERROR_DATA_CODE_INIT_TLS_FAILED
Definition civetweb.h:1743
@ MG_ERROR_DATA_CODE_INIT_USER_FAILED
Definition civetweb.h:1767
@ MG_ERROR_DATA_CODE_SERVER_STOPPED
Definition civetweb.h:1755
@ MG_ERROR_DATA_CODE_INVALID_PASS_FILE
Definition civetweb.h:1773
@ MG_ERROR_DATA_CODE_TLS_CLIENT_CERT_ERROR
Definition civetweb.h:1788
#define MG_MAX_HEADERS
Definition civetweb.h:141
CIVETWEB_API int mg_websocket_write(struct mg_connection *conn, int opcode, const char *data, size_t data_len)
void *(* mg_thread_func_t)(void *)
Definition civetweb.h:1307
#define CIVETWEB_VERSION
Definition civetweb.h:26
#define PRINTF_FORMAT_STRING(s)
Definition civetweb.h:877
CIVETWEB_API int mg_response_header_send(struct mg_connection *conn)
int(* mg_authorization_handler)(struct mg_connection *conn, void *cbdata)
Definition civetweb.h:606
CIVETWEB_API int mg_response_header_add(struct mg_connection *conn, const char *header, const char *value, int value_len)
CIVETWEB_API int mg_websocket_client_write(struct mg_connection *conn, int opcode, const char *data, size_t data_len)
void(* mg_websocket_ready_handler)(struct mg_connection *, void *)
Definition civetweb.h:549
#define CIVETWEB_API
Definition civetweb.h:43
#define PRINTF_ARGS(x, y)
Definition civetweb.h:883
@ MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE
Definition civetweb.h:861
@ MG_WEBSOCKET_OPCODE_PONG
Definition civetweb.h:863
@ MG_WEBSOCKET_OPCODE_PING
Definition civetweb.h:862
@ MG_CONFIG_TYPE_UNKNOWN
Definition civetweb.h:694
@ MG_CONFIG_TYPE_FILE
Definition civetweb.h:697
@ MG_CONFIG_TYPE_STRING
Definition civetweb.h:696
@ MG_CONFIG_TYPE_DIRECTORY
Definition civetweb.h:698
@ MG_CONFIG_TYPE_EXT_PATTERN
Definition civetweb.h:700
@ MG_CONFIG_TYPE_STRING_MULTILINE
Definition civetweb.h:702
@ MG_CONFIG_TYPE_STRING_LIST
Definition civetweb.h:701
@ MG_CONFIG_TYPE_YES_NO_OPTIONAL
Definition civetweb.h:703
@ MG_CONFIG_TYPE_NUMBER
Definition civetweb.h:695
@ MG_CONFIG_TYPE_BOOLEAN
Definition civetweb.h:699
int(* mg_websocket_data_handler)(struct mg_connection *, int, char *, size_t, void *)
Definition civetweb.h:550
int(* mg_request_handler)(struct mg_connection *conn, void *cbdata)
Definition civetweb.h:492
CIVETWEB_API int mg_response_header_start(struct mg_connection *conn, int status)
void(* mg_websocket_close_handler)(const struct mg_connection *, void *)
Definition civetweb.h:555
int(* mg_websocket_connect_handler)(const struct mg_connection *, void *)
Definition civetweb.h:547
CIVETWEB_API int mg_response_header_add_lines(struct mg_connection *conn, const char *http1_headers)
CURL_EXTERN int void * arg
Definition curl.h:2622
guint depth
GStaticMutex mutex
union @139 unused
#define NULL
Definition gmacros.h:924
#define TRUE
Definition gmacros.h:933
#define FALSE
Definition gmacros.h:929
glong tv_sec
Definition gtypes.h:0
const char * name
Definition lsqlite3.c:2154
int value
Definition lsqlite3.c:2155
LUA_API void lua_pushstring(lua_State *L, const char *s)
LUA_API int lua_toboolean(lua_State *L, int idx)
LUA_API const char * lua_tolstring(lua_State *L, int idx, size_t *len)
LUA_API int lua_type(lua_State *L, int idx)
LUA_API void lua_rawset(lua_State *L, int idx)
static void block(LexState *ls)
static int cond(LexState *ls)
static void chunk(LexState *ls)
static void close_func(LexState *ls)
static void body(LexState *ls, expdesc *e, int needself, int line)
LUA_API void lua_close(lua_State *L)
#define LUA_VERSION_NUM
#define LUA_TSTRING
#define LUA_TBOOLEAN
#define lua_newtable(L)
#define lua_pop(L, n)
#define lua_getglobal(L, s)
#define LUA_RELEASE
#define LUA_TFUNCTION
static const char * output
static void error(LoadState *S, const char *why)
#define mask(n)
#define lua_pcall(L, n, r, f)
CURL_EXTERN CURLMcode curl_socket_t s
Definition multi.h:318
#define threadid
Definition sqlite3.c:33865
size_t fread(void *, size_t, size_t, FILE *)
size_t fwrite(const void *, size_t, size_t, FILE *)
char * nc
Definition civetweb.c:8549
char * nonce
Definition civetweb.c:8549
char * cnonce
Definition civetweb.c:8549
char * uri
Definition civetweb.c:8549
char * user
Definition civetweb.c:8546
char * plain_password
Definition civetweb.c:8548
char * qop
Definition civetweb.c:8549
char * response
Definition civetweb.c:8549
int type
Definition civetweb.c:8547
struct mg_connection * conn
Definition civetweb.c:11265
char * file_name
Definition civetweb.c:2578
struct mg_file_stat file
Definition civetweb.c:2579
size_t num_entries
Definition civetweb.c:9904
struct de * entries
Definition civetweb.c:9903
size_t arr_size
Definition civetweb.c:9905
int(* init_ssl)(void *ssl_ctx, void *user_data)
Definition civetweb.h:254
int(* log_message)(const struct mg_connection *, const char *message)
Definition civetweb.h:240
void *(* init_thread)(const struct mg_context *ctx, int thread_type)
Definition civetweb.h:393
void(* end_request)(const struct mg_connection *, int reply_status_code)
Definition civetweb.h:236
int(* init_connection)(const struct mg_connection *conn, void **conn_data)
Definition civetweb.h:417
void(* connection_close)(const struct mg_connection *)
Definition civetweb.h:320
int(* http_error)(struct mg_connection *conn, int status, const char *errmsg)
Definition civetweb.h:359
void(* exit_context)(const struct mg_context *ctx)
Definition civetweb.h:372
int(* init_ssl_domain)(const char *server_domain, void *ssl_ctx, void *user_data)
Definition civetweb.h:265
int(* external_ssl_ctx)(void **ssl_ctx, void *user_data)
Definition civetweb.h:278
void(* exit_thread)(const struct mg_context *ctx, int thread_type, void *thread_pointer)
Definition civetweb.h:400
int(* begin_request)(struct mg_connection *)
Definition civetweb.h:233
int(* log_access)(const struct mg_connection *, const char *message)
Definition civetweb.h:244
void(* init_context)(const struct mg_context *ctx)
Definition civetweb.h:367
void(* connection_closed)(const struct mg_connection *)
Definition civetweb.h:330
int(* external_ssl_ctx_domain)(const char *server_domain, void **ssl_ctx, void *user_data)
Definition civetweb.h:290
const char * issuer
Definition civetweb.h:209
const char * finger
Definition civetweb.h:211
void * peer_cert
Definition civetweb.h:207
const char * subject
Definition civetweb.h:208
const char * serial
Definition civetweb.h:210
const char * host_name
Definition civetweb.h:1488
const char * client_cert
Definition civetweb.h:1486
const char * server_cert
Definition civetweb.h:1487
const char * host
Definition civetweb.h:1484
time_t last_throttle_time
Definition civetweb.c:2563
int64_t content_len
Definition civetweb.c:2518
struct timespec req_time
Definition civetweb.c:2515
int64_t consumed_content
Definition civetweb.c:2524
char * path_info
Definition civetweb.c:2533
int64_t num_bytes_sent
Definition civetweb.c:2517
int connection_type
Definition civetweb.c:2486
pthread_mutex_t mutex
Definition civetweb.c:2565
struct socket client
Definition civetweb.c:2507
struct mg_response_info response_info
Definition civetweb.c:2495
void * tls_user_ptr
Definition civetweb.c:2571
struct mg_request_info request_info
Definition civetweb.c:2494
struct mg_context * phys_ctx
Definition civetweb.c:2497
struct mg_domain_context * dom_ctx
Definition civetweb.c:2498
int in_error_handler
Definition civetweb.c:2537
time_t conn_birth_time
Definition civetweb.c:2508
int handled_requests
Definition civetweb.c:2554
int last_throttle_bytes
Definition civetweb.c:2564
time_t start_time
Definition civetweb.c:2412
pthread_cond_t sq_empty
Definition civetweb.c:2392
struct socket * squeue
Definition civetweb.c:2387
pthread_t * worker_threadids
Definition civetweb.c:2379
volatile int sq_head
Definition civetweb.c:2389
stop_flag_t stop_flag
Definition civetweb.c:2373
void * user_data
Definition civetweb.c:2433
unsigned long starter_thread_idx
Definition civetweb.c:2380
struct mg_connection * worker_connections
Definition civetweb.c:2360
struct socket * listening_sockets
Definition civetweb.c:2356
char * systemName
Definition civetweb.c:2411
pthread_t masterthreadid
Definition civetweb.c:2376
int context_type
Definition civetweb.c:2354
pthread_mutex_t thread_mutex
Definition civetweb.c:2374
struct mg_callbacks callbacks
Definition civetweb.c:2432
struct mg_pollfd * listening_socket_fds
Definition civetweb.c:2357
struct mg_domain_context dd
Definition civetweb.c:2442
pthread_cond_t sq_full
Definition civetweb.c:2391
volatile int sq_tail
Definition civetweb.c:2390
struct twebdav_lock webdav_lock[NUM_WEBDAV_LOCKS]
Definition civetweb.c:2408
unsigned int num_listening_sockets
Definition civetweb.c:2358
unsigned int max_request_size
Definition civetweb.c:2401
unsigned int cfg_worker_threads
Definition civetweb.c:2378
pthread_mutex_t nonce_mutex
Definition civetweb.c:2427
volatile int sq_blocked
Definition civetweb.c:2393
char * config[NUM_OPTIONS]
Definition civetweb.c:2271
unsigned long nonce_count
Definition civetweb.c:2277
int64_t ssl_cert_last_mtime
Definition civetweb.c:2273
uint64_t auth_nonce_mask
Definition civetweb.c:2276
struct mg_domain_context * next
Definition civetweb.c:2285
SSL_CTX * ssl_ctx
Definition civetweb.c:2270
struct mg_handler_info * handlers
Definition civetweb.c:2272
uint64_t size
Definition civetweb.c:1871
time_t last_modified
Definition civetweb.c:1872
struct mg_file_stat stat
Definition civetweb.c:1886
struct mg_file_access access
Definition civetweb.c:1887
mg_request_handler handler
Definition civetweb.c:2237
mg_authorization_handler auth_handler
Definition civetweb.c:2251
mg_websocket_close_handler close_handler
Definition civetweb.c:2245
struct mg_handler_info * next
Definition civetweb.c:2257
unsigned int refcount
Definition civetweb.c:2238
mg_websocket_connect_handler connect_handler
Definition civetweb.c:2242
struct mg_websocket_subprotocols * subprotocols
Definition civetweb.c:2248
mg_websocket_data_handler data_handler
Definition civetweb.c:2244
mg_websocket_ready_handler ready_handler
Definition civetweb.c:2243
const char * value
Definition civetweb.h:145
const char * name
Definition civetweb.h:144
const char * name
Definition civetweb.c:10803
void * user_data
Definition civetweb.h:1800
const struct mg_callbacks * callbacks
Definition civetweb.h:1799
const char ** configuration_options
Definition civetweb.h:1801
const char * default_value
Definition civetweb.h:688
const char * name
Definition civetweb.h:686
struct mg_header http_headers[MG_MAX_HEADERS]
Definition civetweb.h:179
const char * local_uri
Definition civetweb.h:157
void * user_data
Definition civetweb.h:175
const char * local_uri_raw
Definition civetweb.h:154
const char * request_method
Definition civetweb.h:151
struct mg_client_cert * client_cert
Definition civetweb.h:182
const char * query_string
Definition civetweb.h:162
long long content_length
Definition civetweb.h:168
void * conn_data
Definition civetweb.h:176
char remote_addr[48]
Definition civetweb.h:166
const char * http_version
Definition civetweb.h:161
const char * request_uri
Definition civetweb.h:152
const char * acceptedWebSocketSubprotocol
Definition civetweb.h:184
const char * remote_user
Definition civetweb.h:164
long long content_length
Definition civetweb.h:196
const char * http_version
Definition civetweb.h:194
const char * status_text
Definition civetweb.h:193
struct mg_header http_headers[MG_MAX_HEADERS]
Definition civetweb.h:200
const char ** subprotocols
Definition civetweb.h:564
void * user_ptr
Definition civetweb.c:1590
const char * alpn_proto
Definition civetweb.c:1595
unsigned long thread_idx
Definition civetweb.c:1589
const char * f_ha1
Definition civetweb.c:8728
const char * f_domain
Definition civetweb.c:8727
const char * f_user
Definition civetweb.c:8726
const char * domain
Definition civetweb.c:8724
char buf[256+256+40]
Definition civetweb.c:8725
struct mg_connection * conn
Definition civetweb.c:8722
unsigned char is_ssl
Definition civetweb.c:1906
union usa lsa
Definition civetweb.c:1904
unsigned char ssl_redir
Definition civetweb.c:1907
SOCKET sock
Definition civetweb.c:1903
unsigned char in_use
Definition civetweb.c:1909
union usa rsa
Definition civetweb.c:1905
char user[UTF8_PATH_MAX *2]
Definition civetweb.c:2341
char token[33]
Definition civetweb.c:2339
char path[UTF8_PATH_MAX *2]
Definition civetweb.c:2340
uint64_t locktime
Definition civetweb.c:2338
size_t len
Definition civetweb.c:1866
const char * ptr
Definition civetweb.c:1865
mg_websocket_close_handler close_handler
Definition civetweb.c:18929
mg_websocket_data_handler data_handler
Definition civetweb.c:18928
struct mg_connection * conn
Definition civetweb.c:18927
struct sockaddr sa
Definition civetweb.c:1833
struct sockaddr_in sin
Definition civetweb.c:1834
#define printf