Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
civetweb.c
Go to the documentation of this file.
1/* Copyright (c) 2013-2024 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) /* Minimum API version */
55#define _WIN32_WINNT 0x0601
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(__rtems__)
187#include <rtems/version.h>
188#endif
189
190#if defined(__ZEPHYR__)
191#include <ctype.h>
192#include <fcntl.h>
193#include <netdb.h>
194#include <poll.h>
195#include <pthread.h>
196#include <stdio.h>
197#include <stdlib.h>
198#include <string.h>
199#include <sys/socket.h>
200#include <time.h>
201
202#include <zephyr/kernel.h>
203
204/* Max worker threads is the max of pthreads minus the main application thread
205 * and minus the main civetweb thread, thus -2
206 */
207#define MAX_WORKER_THREADS (CONFIG_MAX_PTHREAD_COUNT - 2)
208
209#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
210#define ZEPHYR_STACK_SIZE USE_STACK_SIZE
211#else
212#define ZEPHYR_STACK_SIZE (1024 * 16)
213#endif
214
219
221
222#endif
223
224#if !defined(CIVETWEB_HEADER_INCLUDED)
225/* Include the header file here, so the CivetWeb interface is defined for the
226 * entire implementation, including the following forward definitions. */
227#include "civetweb.h"
228#endif
229
230#if !defined(DEBUG_TRACE)
231#if defined(DEBUG)
232static void DEBUG_TRACE_FUNC(const char *func,
233 unsigned line,
234 PRINTF_FORMAT_STRING(const char *fmt),
235 ...) PRINTF_ARGS(3, 4);
236
237#define DEBUG_TRACE(fmt, ...) \
238 DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__)
239
240#define NEED_DEBUG_TRACE_FUNC
241#if !defined(DEBUG_TRACE_STREAM)
242#define DEBUG_TRACE_STREAM stderr
243#endif
244
245#else
246#define DEBUG_TRACE(fmt, ...) \
247 do { \
248 } while (0)
249#endif /* DEBUG */
250#endif /* DEBUG_TRACE */
251
252
253#if !defined(DEBUG_ASSERT)
254#if defined(DEBUG)
255#include <stdlib.h>
256#define DEBUG_ASSERT(cond) \
257 do { \
258 if (!(cond)) { \
259 DEBUG_TRACE("ASSERTION FAILED: %s", #cond); \
260 exit(2); /* Exit with error */ \
261 } \
262 } while (0)
263#else
264#define DEBUG_ASSERT(cond)
265#endif /* DEBUG */
266#endif
267
268
269#if defined(__GNUC__) && defined(GCC_INSTRUMENTATION)
272
275
276void
278{
279 if ((void *)this_fn != (void *)printf) {
280 printf("E %p %p\n", this_fn, call_site);
281 }
282}
283
284void
286{
287 if ((void *)this_fn != (void *)printf) {
288 printf("X %p %p\n", this_fn, call_site);
289 }
290}
291#endif
292
293
294#if !defined(IGNORE_UNUSED_RESULT)
295#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1))
296#endif
297
298
299#if defined(__GNUC__) || defined(__MINGW32__)
300
301/* GCC unused function attribute seems fundamentally broken.
302 * Several attempts to tell the compiler "THIS FUNCTION MAY BE USED
303 * OR UNUSED" for individual functions failed.
304 * Either the compiler creates an "unused-function" warning if a
305 * function is not marked with __attribute__((unused)).
306 * On the other hand, if the function is marked with this attribute,
307 * but is used, the compiler raises a completely idiotic
308 * "used-but-marked-unused" warning - and
309 * #pragma GCC diagnostic ignored "-Wused-but-marked-unused"
310 * raises error: unknown option after "#pragma GCC diagnostic".
311 * Disable this warning completely, until the GCC guys sober up
312 * again.
313 */
314
315#pragma GCC diagnostic ignored "-Wunused-function"
316
317#define FUNCTION_MAY_BE_UNUSED /* __attribute__((unused)) */
318
319#else
320#define FUNCTION_MAY_BE_UNUSED
321#endif
322
323
324/* Some ANSI #includes are not available on Windows CE and Zephyr */
325#if !defined(_WIN32_WCE) && !defined(__ZEPHYR__)
326#include <errno.h>
327#include <fcntl.h>
328#include <signal.h>
329#include <stdlib.h>
330#include <sys/stat.h>
331#include <sys/types.h>
332#endif /* !_WIN32_WCE */
333
334
335#if defined(__clang__)
336/* When using -Weverything, clang does not accept it's own headers
337 * in a release build configuration. Disable what is too much in
338 * -Weverything. */
339#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
340#endif
341
342#if defined(__GNUC__) || defined(__MINGW32__)
343/* Who on earth came to the conclusion, using __DATE__ should rise
344 * an "expansion of date or time macro is not reproducible"
345 * warning. That's exactly what was intended by using this macro.
346 * Just disable this nonsense warning. */
347
348/* And disabling them does not work either:
349 * #pragma clang diagnostic ignored "-Wno-error=date-time"
350 * #pragma clang diagnostic ignored "-Wdate-time"
351 * So we just have to disable ALL warnings for some lines
352 * of code.
353 * This seems to be a known GCC bug, not resolved since 2012:
354 * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431
355 */
356#endif
357
358
359#if defined(__MACH__) && defined(__APPLE__) /* Apple OSX section */
360
361#if defined(__clang__)
362#if (__clang_major__ == 3) && ((__clang_minor__ == 7) || (__clang_minor__ == 8))
363/* Avoid warnings for Xcode 7. It seems it does no longer exist in Xcode 8 */
364#pragma clang diagnostic ignored "-Wno-reserved-id-macro"
365#pragma clang diagnostic ignored "-Wno-keyword-macro"
366#endif
367#endif
368
369#ifndef CLOCK_MONOTONIC
370#define CLOCK_MONOTONIC (1)
371#endif
372#ifndef CLOCK_REALTIME
373#define CLOCK_REALTIME (2)
374#endif
375
376#include <mach/clock.h>
377#include <mach/mach.h>
378#include <mach/mach_time.h>
379#include <sys/errno.h>
380#include <sys/time.h>
381
382/* clock_gettime is not implemented on OSX prior to 10.12 */
383static int
385{
386 memset(t, 0, sizeof(*t));
387 if (clk_id == CLOCK_REALTIME) {
388 struct timeval now;
389 int rv = gettimeofday(&now, NULL);
390 if (rv) {
391 return rv;
392 }
393 t->tv_sec = now.tv_sec;
394 t->tv_nsec = now.tv_usec * 1000;
395 return 0;
396
397 } else if (clk_id == CLOCK_MONOTONIC) {
398 static uint64_t clock_start_time = 0;
400
401 uint64_t now = mach_absolute_time();
402
403 if (clock_start_time == 0) {
406
407 /* appease "unused variable" warning for release builds */
408 (void)mach_status;
409
411 }
412
413 now = (uint64_t)((double)(now - clock_start_time)
414 * (double)timebase_ifo.numer
415 / (double)timebase_ifo.denom);
416
417 t->tv_sec = now / 1000000000;
418 t->tv_nsec = now % 1000000000;
419 return 0;
420 }
421 return -1; /* EINVAL - Clock ID is unknown */
422}
423
424/* if clock_gettime is declared, then __CLOCK_AVAILABILITY will be defined */
425#if defined(__CLOCK_AVAILABILITY)
426/* If we compiled with Mac OSX 10.12 or later, then clock_gettime will be
427 * declared but it may be NULL at runtime. So we need to check before using
428 * it. */
429static int
431{
432 if (clock_gettime) {
433 return clock_gettime(clk_id, t);
434 }
435 return _civet_clock_gettime(clk_id, t);
436}
437#define clock_gettime _civet_safe_clock_gettime
438#else
439#define clock_gettime _civet_clock_gettime
440#endif
441
442#endif
443
444
445#if defined(_WIN32)
446#define ERROR_TRY_AGAIN(err) ((err) == WSAEWOULDBLOCK)
447#else
448/* Unix might return different error codes indicating to try again.
449 * For Linux EAGAIN==EWOULDBLOCK, maybe EAGAIN!=EWOULDBLOCK is history from
450 * decades ago, but better check both and let the compiler optimize it. */
451#define ERROR_TRY_AGAIN(err) \
452 (((err) == EAGAIN) || ((err) == EWOULDBLOCK) || ((err) == EINTR))
453#endif
454
455#if defined(USE_ZLIB)
456#include "zconf.h"
457#include "zlib.h"
458#endif
459
460
461/********************************************************************/
462/* CivetWeb configuration defines */
463/********************************************************************/
464
465/* Maximum number of threads that can be configured.
466 * The number of threads actually created depends on the "num_threads"
467 * configuration parameter, but this is the upper limit. */
468#if !defined(MAX_WORKER_THREADS)
469#define MAX_WORKER_THREADS (1024 * 64) /* in threads (count) */
470#endif
471
472/* Timeout interval for select/poll calls.
473 * The timeouts depend on "*_timeout_ms" configuration values, but long
474 * timeouts are split into timouts as small as SOCKET_TIMEOUT_QUANTUM.
475 * This reduces the time required to stop the server. */
476#if !defined(SOCKET_TIMEOUT_QUANTUM)
477#define SOCKET_TIMEOUT_QUANTUM (2000) /* in ms */
478#endif
479
480/* Do not try to compress files smaller than this limit. */
481#if !defined(MG_FILE_COMPRESSION_SIZE_LIMIT)
482#define MG_FILE_COMPRESSION_SIZE_LIMIT (1024) /* in bytes */
483#endif
484
485#if !defined(PASSWORDS_FILE_NAME)
486#define PASSWORDS_FILE_NAME ".htpasswd"
487#endif
488
489/* Initial buffer size for all CGI environment variables. In case there is
490 * not enough space, another block is allocated. */
491#if !defined(CGI_ENVIRONMENT_SIZE)
492#define CGI_ENVIRONMENT_SIZE (4096) /* in bytes */
493#endif
494
495/* Maximum number of environment variables. */
496#if !defined(MAX_CGI_ENVIR_VARS)
497#define MAX_CGI_ENVIR_VARS (256) /* in variables (count) */
498#endif
499
500/* General purpose buffer size. */
501#if !defined(MG_BUF_LEN) /* in bytes */
502#define MG_BUF_LEN (1024 * 8)
503#endif
504
505
506/********************************************************************/
507
508/* Helper macros */
509#if !defined(ARRAY_SIZE)
510#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
511#endif
512
513#include <stdint.h>
514
515/* Standard defines */
516#if !defined(INT64_MAX)
517#define INT64_MAX (9223372036854775807)
518#endif
519
520#define SHUTDOWN_RD (0)
521#define SHUTDOWN_WR (1)
522#define SHUTDOWN_BOTH (2)
523
525 "worker threads must be a positive number");
526
527mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8,
528 "size_t data type size check");
529
530
531#if defined(_WIN32) /* WINDOWS include block */
532#include <malloc.h> /* *alloc( */
533#include <stdlib.h> /* *alloc( */
534#include <time.h> /* struct timespec */
535#include <windows.h>
536#include <winsock2.h> /* DTL add for SO_EXCLUSIVE */
537#include <ws2tcpip.h>
538
539typedef const char *SOCK_OPT_TYPE;
540
541/* For a detailed description of these *_PATH_MAX defines, see
542 * https://github.com/civetweb/civetweb/issues/937. */
543
544/* UTF8_PATH_MAX is a char buffer size for 259 BMP characters in UTF-8 plus
545 * null termination, rounded up to the next 4 bytes boundary */
546#define UTF8_PATH_MAX (3 * 260)
547/* UTF16_PATH_MAX is the 16-bit wchar_t buffer size required for 259 BMP
548 * characters plus termination. (Note: wchar_t is 16 bit on Windows) */
549#define UTF16_PATH_MAX (260)
550
551#if !defined(_IN_PORT_T)
552#if !defined(in_port_t)
553#define in_port_t u_short
554#endif
555#endif
556
557#if defined(_WIN32_WCE)
558#error "WinCE support has ended"
559#endif
560
561#include <direct.h>
562#include <io.h>
563#include <process.h>
564
565
566#define MAKEUQUAD(lo, hi) \
567 ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32))
568#define RATE_DIFF (10000000) /* 100 nsecs */
569#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de))
570#define SYS2UNIX_TIME(lo, hi) \
571 ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF))
572
573/* Visual Studio 6 does not know __func__ or __FUNCTION__
574 * The rest of MS compilers use __FUNCTION__, not C99 __func__
575 * Also use _strtoui64 on modern M$ compilers */
576#if defined(_MSC_VER)
577#if (_MSC_VER < 1300)
578#define STRX(x) #x
579#define STR(x) STRX(x)
580#define __func__ __FILE__ ":" STR(__LINE__)
581#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x))
582#define strtoll(x, y, z) (_atoi64(x))
583#else
584#define __func__ __FUNCTION__
585#define strtoull(x, y, z) (_strtoui64(x, y, z))
586#define strtoll(x, y, z) (_strtoi64(x, y, z))
587#endif
588#endif /* _MSC_VER */
589
590
591#define ERRNO ((int)(GetLastError()))
592#define NO_SOCKLEN_T
593
594
595#if defined(_WIN64) || defined(__MINGW64__)
596#if !defined(SSL_LIB)
597
598#if defined(OPENSSL_API_3_0)
599#define SSL_LIB "libssl-3-x64.dll"
600#define CRYPTO_LIB "libcrypto-3-x64.dll"
601#endif
602
603#if defined(OPENSSL_API_1_1)
604#define SSL_LIB "libssl-1_1-x64.dll"
605#define CRYPTO_LIB "libcrypto-1_1-x64.dll"
606#endif /* OPENSSL_API_1_1 */
607
608#if defined(OPENSSL_API_1_0)
609#define SSL_LIB "ssleay64.dll"
610#define CRYPTO_LIB "libeay64.dll"
611#endif /* OPENSSL_API_1_0 */
612
613#endif
614#else /* defined(_WIN64) || defined(__MINGW64__) */
615#if !defined(SSL_LIB)
616
617#if defined(OPENSSL_API_3_0)
618#define SSL_LIB "libssl-3.dll"
619#define CRYPTO_LIB "libcrypto-3.dll"
620#endif
621
622#if defined(OPENSSL_API_1_1)
623#define SSL_LIB "libssl-1_1.dll"
624#define CRYPTO_LIB "libcrypto-1_1.dll"
625#endif /* OPENSSL_API_1_1 */
626
627#if defined(OPENSSL_API_1_0)
628#define SSL_LIB "ssleay32.dll"
629#define CRYPTO_LIB "libeay32.dll"
630#endif /* OPENSSL_API_1_0 */
631
632#endif /* SSL_LIB */
633#endif /* defined(_WIN64) || defined(__MINGW64__) */
634
635
636#define O_NONBLOCK (0)
637#if !defined(W_OK)
638#define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */
639#endif
640#define _POSIX_
641#define INT64_FMT "I64d"
642#define UINT64_FMT "I64u"
643
644#define WINCDECL __cdecl
645#define vsnprintf_impl _vsnprintf
646#define access _access
647#define mg_sleep(x) (Sleep(x))
648
649#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
650#if !defined(popen)
651#define popen(x, y) (_popen(x, y))
652#endif
653#if !defined(pclose)
654#define pclose(x) (_pclose(x))
655#endif
656#define close(x) (_close(x))
657#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y)))
658#define RTLD_LAZY (0)
659#define fseeko(x, y, z) ((_lseeki64(_fileno(x), (y), (z)) == -1) ? -1 : 0)
660#define fdopen(x, y) (_fdopen((x), (y)))
661#define write(x, y, z) (_write((x), (y), (unsigned)z))
662#define read(x, y, z) (_read((x), (y), (unsigned)z))
663#define flockfile(x) ((void)pthread_mutex_lock(&global_log_file_lock))
664#define funlockfile(x) ((void)pthread_mutex_unlock(&global_log_file_lock))
665#define sleep(x) (Sleep((x)*1000))
666#define rmdir(x) (_rmdir(x))
667#if defined(_WIN64) || !defined(__MINGW32__)
668/* Only MinGW 32 bit is missing this function */
669#define timegm(x) (_mkgmtime(x))
670#else
671time_t timegm(struct tm *tm);
672#define NEED_TIMEGM
673#endif
674
675
676#if !defined(fileno)
677#define fileno(x) (_fileno(x))
678#endif /* !fileno MINGW #defines fileno */
679
680typedef struct {
681 CRITICAL_SECTION sec; /* Immovable */
682} pthread_mutex_t;
683typedef DWORD pthread_key_t;
684typedef HANDLE pthread_t;
685typedef struct {
686 pthread_mutex_t threadIdSec;
687 struct mg_workerTLS *waiting_thread; /* The chain of threads */
689
690#if !defined(__clockid_t_defined)
691typedef DWORD clockid_t;
692#endif
693#if !defined(CLOCK_MONOTONIC)
694#define CLOCK_MONOTONIC (1)
695#endif
696#if !defined(CLOCK_REALTIME)
697#define CLOCK_REALTIME (2)
698#endif
699#if !defined(CLOCK_THREAD)
700#define CLOCK_THREAD (3)
701#endif
702#if !defined(CLOCK_PROCESS)
703#define CLOCK_PROCESS (4)
704#endif
705
706
707#if defined(_MSC_VER) && (_MSC_VER >= 1900)
708#define _TIMESPEC_DEFINED
709#endif
710#if !defined(_TIMESPEC_DEFINED)
711struct timespec {
712 time_t tv_sec; /* seconds */
713 long tv_nsec; /* nanoseconds */
714};
715#endif
716
717#if !defined(WIN_PTHREADS_TIME_H)
718#define MUST_IMPLEMENT_CLOCK_GETTIME
719#endif
720
721#if defined(MUST_IMPLEMENT_CLOCK_GETTIME)
722#define clock_gettime mg_clock_gettime
723static int
725{
726 FILETIME ft;
728 BOOL ok = FALSE;
729 double d;
730 static double perfcnt_per_sec = 0.0;
731 static BOOL initialized = FALSE;
732
733 if (!initialized) {
734 QueryPerformanceFrequency((LARGE_INTEGER *)&li);
735 perfcnt_per_sec = 1.0 / li.QuadPart;
736 initialized = TRUE;
737 }
738
739 if (tp) {
740 memset(tp, 0, sizeof(*tp));
741
742 if (clk_id == CLOCK_REALTIME) {
743
744 /* BEGIN: CLOCK_REALTIME = wall clock (date and time) */
746 li.LowPart = ft.dwLowDateTime;
747 li.HighPart = ft.dwHighDateTime;
748 li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */
749 tp->tv_sec = (time_t)(li.QuadPart / 10000000);
750 tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
751 ok = TRUE;
752 /* END: CLOCK_REALTIME */
753
754 } else if (clk_id == CLOCK_MONOTONIC) {
755
756 /* BEGIN: CLOCK_MONOTONIC = stopwatch (time differences) */
757 QueryPerformanceCounter((LARGE_INTEGER *)&li);
758 d = li.QuadPart * perfcnt_per_sec;
759 tp->tv_sec = (time_t)d;
760 d -= (double)tp->tv_sec;
761 tp->tv_nsec = (long)(d * 1.0E9);
762 ok = TRUE;
763 /* END: CLOCK_MONOTONIC */
764
765 } else if (clk_id == CLOCK_THREAD) {
766
767 /* BEGIN: CLOCK_THREAD = CPU usage of thread */
770 &t_create,
771 &t_exit,
772 &t_kernel,
773 &t_user)) {
774 li.LowPart = t_user.dwLowDateTime;
775 li.HighPart = t_user.dwHighDateTime;
776 li2.LowPart = t_kernel.dwLowDateTime;
777 li2.HighPart = t_kernel.dwHighDateTime;
778 li.QuadPart += li2.QuadPart;
779 tp->tv_sec = (time_t)(li.QuadPart / 10000000);
780 tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
781 ok = TRUE;
782 }
783 /* END: CLOCK_THREAD */
784
785 } else if (clk_id == CLOCK_PROCESS) {
786
787 /* BEGIN: CLOCK_PROCESS = CPU usage of process */
790 &t_create,
791 &t_exit,
792 &t_kernel,
793 &t_user)) {
794 li.LowPart = t_user.dwLowDateTime;
795 li.HighPart = t_user.dwHighDateTime;
796 li2.LowPart = t_kernel.dwLowDateTime;
797 li2.HighPart = t_kernel.dwHighDateTime;
798 li.QuadPart += li2.QuadPart;
799 tp->tv_sec = (time_t)(li.QuadPart / 10000000);
800 tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
801 ok = TRUE;
802 }
803 /* END: CLOCK_PROCESS */
804
805 } else {
806
807 /* BEGIN: unknown clock */
808 /* ok = FALSE; already set by init */
809 /* END: unknown clock */
810 }
811 }
812
813 return ok ? 0 : -1;
814}
815#endif
816
817
818#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */
819
820static int pthread_mutex_lock(pthread_mutex_t *);
821static int pthread_mutex_unlock(pthread_mutex_t *);
822static void path_to_unicode(const struct mg_connection *conn,
823 const char *path,
824 wchar_t *wbuf,
825 size_t wbuf_len);
826
827/* All file operations need to be rewritten to solve #246. */
828
829struct mg_file;
830
831static const char *mg_fgets(char *buf, size_t size, struct mg_file *filep);
832
833
834/* POSIX dirent interface */
835struct dirent {
836 char d_name[UTF8_PATH_MAX];
837};
838
839typedef struct DIR {
840 HANDLE handle;
842 struct dirent result;
843} DIR;
844
845#if defined(HAVE_POLL)
846#define mg_pollfd pollfd
847#else
848struct mg_pollfd {
849 SOCKET fd;
850 short events;
851 short revents;
852};
853#endif
854
855/* Mark required libraries */
856#if defined(_MSC_VER)
857#pragma comment(lib, "Ws2_32.lib")
858#endif
859
860#else /* defined(_WIN32) - WINDOWS vs UNIX include block */
861
862#include <inttypes.h>
863
864/* Linux & co. internally use UTF8 */
865#define UTF8_PATH_MAX (PATH_MAX)
866
867typedef const void *SOCK_OPT_TYPE;
868
869#if defined(ANDROID)
870typedef unsigned short int in_port_t;
871#endif
872
873#if !defined(__ZEPHYR__)
874#include <arpa/inet.h>
875#include <ctype.h>
876#include <dirent.h>
877#include <grp.h>
878#include <limits.h>
879#include <netdb.h>
880#include <netinet/in.h>
881#include <netinet/tcp.h>
882#include <poll.h>
883#include <pthread.h>
884#include <pwd.h>
885#include <stdarg.h>
886#include <stddef.h>
887#include <stdio.h>
888#include <stdlib.h>
889#include <string.h>
890#include <sys/socket.h>
891#include <sys/time.h>
892#if !defined(__rtems__)
893#include <sys/utsname.h>
894#endif
895#include <sys/wait.h>
896#include <time.h>
897#include <unistd.h>
898#if defined(USE_X_DOM_SOCKET)
899#include <sys/un.h>
900#endif
901#endif
902
903#define vsnprintf_impl vsnprintf
904
905#if !defined(NO_SSL_DL) && !defined(NO_SSL)
906#include <dlfcn.h>
907#endif
908
909#if defined(__MACH__) && defined(__APPLE__)
910
911#if defined(OPENSSL_API_3_0)
912#define SSL_LIB "libssl.3.dylib"
913#define CRYPTO_LIB "libcrypto.3.dylib"
914#endif
915
916#if defined(OPENSSL_API_1_1)
917#define SSL_LIB "libssl.1.1.dylib"
918#define CRYPTO_LIB "libcrypto.1.1.dylib"
919#endif /* OPENSSL_API_1_1 */
920
921#if defined(OPENSSL_API_1_0)
922#define SSL_LIB "libssl.1.0.dylib"
923#define CRYPTO_LIB "libcrypto.1.0.dylib"
924#endif /* OPENSSL_API_1_0 */
925
926#else
927#if !defined(SSL_LIB)
928#define SSL_LIB "libssl.so"
929#endif
930#if !defined(CRYPTO_LIB)
931#define CRYPTO_LIB "libcrypto.so"
932#endif
933#endif
934#if !defined(O_BINARY)
935#define O_BINARY (0)
936#endif /* O_BINARY */
937#define closesocket(a) (close(a))
938#define mg_mkdir(conn, path, mode) (mkdir(path, mode))
939#define mg_remove(conn, x) (remove(x))
940#define mg_sleep(x) (usleep((x)*1000))
941#define mg_opendir(conn, x) (opendir(x))
942#define mg_closedir(x) (closedir(x))
943#define mg_readdir(x) (readdir(x))
944#define ERRNO (errno)
945#define INVALID_SOCKET (-1)
946#define INT64_FMT PRId64
947#define UINT64_FMT PRIu64
948typedef int SOCKET;
949#define WINCDECL
950
951#if defined(__hpux)
952/* HPUX 11 does not have monotonic, fall back to realtime */
953#if !defined(CLOCK_MONOTONIC)
954#define CLOCK_MONOTONIC CLOCK_REALTIME
955#endif
956
957/* HPUX defines socklen_t incorrectly as size_t which is 64bit on
958 * Itanium. Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED
959 * the prototypes use int* rather than socklen_t* which matches the
960 * actual library expectation. When called with the wrong size arg
961 * accept() returns a zero client inet addr and check_acl() always
962 * fails. Since socklen_t is widely used below, just force replace
963 * their typedef with int. - DTL
964 */
965#define socklen_t int
966#endif /* hpux */
967
968#define mg_pollfd pollfd
969
970#endif /* defined(_WIN32) - WINDOWS vs UNIX include block */
971
972/* In case our C library is missing "timegm", provide an implementation */
973#if defined(NEED_TIMEGM)
974static inline int
975is_leap(int y)
976{
977 return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
978}
979
980static inline int
981count_leap(int y)
982{
983 return (y - 1969) / 4 - (y - 1901) / 100 + (y - 1601) / 400;
984}
985
986static time_t
987timegm(struct tm *tm)
988{
989 static const unsigned short ydays[] = {
990 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
991 int year = tm->tm_year + 1900;
992 int mon = tm->tm_mon;
993 int mday = tm->tm_mday - 1;
994 int hour = tm->tm_hour;
995 int min = tm->tm_min;
996 int sec = tm->tm_sec;
997
998 if (year < 1970 || mon < 0 || mon > 11 || mday < 0
999 || (mday >= ydays[mon + 1] - ydays[mon]
1000 + (mon == 1 && is_leap(year) ? 1 : 0))
1001 || hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 60)
1002 return -1;
1003
1004 time_t res = year - 1970;
1005 res *= 365;
1006 res += mday;
1007 res += ydays[mon] + (mon > 1 && is_leap(year) ? 1 : 0);
1008 res += count_leap(year);
1009
1010 res *= 24;
1011 res += hour;
1012 res *= 60;
1013 res += min;
1014 res *= 60;
1015 res += sec;
1016 return res;
1017}
1018#endif /* NEED_TIMEGM */
1019
1020
1021/* va_copy should always be a macro, C99 and C++11 - DTL */
1022#if !defined(va_copy)
1023#define va_copy(x, y) ((x) = (y))
1024#endif
1025
1026
1027#if defined(_WIN32)
1028/* Create substitutes for POSIX functions in Win32. */
1029
1030#if defined(GCC_DIAGNOSTIC)
1031/* Show no warning in case system functions are not used. */
1032#pragma GCC diagnostic push
1033#pragma GCC diagnostic ignored "-Wunused-function"
1034#endif
1035
1036
1037static pthread_mutex_t global_log_file_lock;
1038
1040static DWORD
1041pthread_self(void)
1042{
1043 return GetCurrentThreadId();
1044}
1045
1046
1048static int
1050 pthread_key_t *key,
1051 void (*_ignored)(void *) /* destructor not supported for Windows */
1052)
1053{
1054 (void)_ignored;
1055
1056 if ((key != 0)) {
1057 *key = TlsAlloc();
1058 return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1;
1059 }
1060 return -2;
1061}
1062
1063
1065static int
1067{
1068 return TlsFree(key) ? 0 : 1;
1069}
1070
1071
1073static int
1075{
1076 return TlsSetValue(key, value) ? 0 : 1;
1077}
1078
1079
1081static void *
1083{
1084 return TlsGetValue(key);
1085}
1086
1087#if defined(GCC_DIAGNOSTIC)
1088/* Enable unused function warning again */
1089#pragma GCC diagnostic pop
1090#endif
1091
1093#else
1095#endif /* _WIN32 */
1096
1097
1098#if defined(GCC_DIAGNOSTIC)
1099/* Show no warning in case system functions are not used. */
1100#pragma GCC diagnostic push
1101#pragma GCC diagnostic ignored "-Wunused-function"
1102#endif /* defined(GCC_DIAGNOSTIC) */
1103#if defined(__clang__)
1104/* Show no warning in case system functions are not used. */
1105#pragma clang diagnostic push
1106#pragma clang diagnostic ignored "-Wunused-function"
1107#endif
1108
1109static pthread_mutex_t global_lock_mutex;
1110
1111
1113static void
1118
1119
1121static void
1126
1127
1128#if defined(_WIN64)
1129mg_static_assert(SIZE_MAX == 0xFFFFFFFFFFFFFFFFu, "Mismatch for atomic types");
1130#elif defined(_WIN32)
1131mg_static_assert(SIZE_MAX == 0xFFFFFFFFu, "Mismatch for atomic types");
1132#endif
1133
1134
1135/* Atomic functions working on ptrdiff_t ("signed size_t").
1136 * Operations: Increment, Decrement, Add, Maximum.
1137 * Up to size_t, they do not an atomic "load" operation.
1138 */
1140static ptrdiff_t
1141mg_atomic_inc(volatile ptrdiff_t *addr)
1142{
1143 ptrdiff_t ret;
1144
1145#if defined(_WIN64) && !defined(NO_ATOMICS)
1147#elif defined(_WIN32) && !defined(NO_ATOMICS)
1148#ifdef __cplusplus
1149 /* For C++ the Microsoft Visual Studio compiler can not decide what
1150 * overloaded function prototpye in the SDC corresponds to "ptrdiff_t". */
1151 static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch");
1152 static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch");
1154#else
1156#endif
1157#elif defined(__GNUC__) \
1158 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1159 && !defined(NO_ATOMICS)
1161#else
1163 ret = (++(*addr));
1165#endif
1166 return ret;
1167}
1168
1169
1171static ptrdiff_t
1172mg_atomic_dec(volatile ptrdiff_t *addr)
1173{
1174 ptrdiff_t ret;
1175
1176#if defined(_WIN64) && !defined(NO_ATOMICS)
1178#elif defined(_WIN32) && !defined(NO_ATOMICS)
1179#ifdef __cplusplus
1180 /* see mg_atomic_inc */
1181 static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch");
1182 static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch");
1184#else
1186#endif
1187#elif defined(__GNUC__) \
1188 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1189 && !defined(NO_ATOMICS)
1191#else
1193 ret = (--(*addr));
1195#endif
1196 return ret;
1197}
1198
1199
1200#if defined(USE_SERVER_STATS) || defined(STOP_FLAG_NEEDS_LOCK)
1201static ptrdiff_t
1202mg_atomic_add(volatile ptrdiff_t *addr, ptrdiff_t value)
1203{
1204 ptrdiff_t ret;
1205
1206#if defined(_WIN64) && !defined(NO_ATOMICS)
1208#elif defined(_WIN32) && !defined(NO_ATOMICS)
1210#elif defined(__GNUC__) \
1211 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1212 && !defined(NO_ATOMICS)
1214#else
1216 *addr += value;
1217 ret = (*addr);
1219#endif
1220 return ret;
1221}
1222
1223
1225static ptrdiff_t
1226mg_atomic_compare_and_swap(volatile ptrdiff_t *addr,
1227 ptrdiff_t oldval,
1228 ptrdiff_t newval)
1229{
1230 ptrdiff_t ret;
1231
1232#if defined(_WIN64) && !defined(NO_ATOMICS)
1234#elif defined(_WIN32) && !defined(NO_ATOMICS)
1236#elif defined(__GNUC__) \
1237 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1238 && !defined(NO_ATOMICS)
1240#else
1242 ret = *addr;
1243 if ((ret != newval) && (ret == oldval)) {
1244 *addr = newval;
1245 }
1247#endif
1248 return ret;
1249}
1250
1251
1252static void
1253mg_atomic_max(volatile ptrdiff_t *addr, ptrdiff_t value)
1254{
1255 register ptrdiff_t tmp = *addr;
1256
1257#if defined(_WIN64) && !defined(NO_ATOMICS)
1258 while (tmp < value) {
1260 }
1261#elif defined(_WIN32) && !defined(NO_ATOMICS)
1262 while (tmp < value) {
1264 }
1265#elif defined(__GNUC__) \
1266 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1267 && !defined(NO_ATOMICS)
1268 while (tmp < value) {
1270 }
1271#else
1273 if (*addr < value) {
1274 *addr = value;
1275 }
1277#endif
1278}
1279
1280
1281static int64_t
1282mg_atomic_add64(volatile int64_t *addr, int64_t value)
1283{
1284 int64_t ret;
1285
1286#if defined(_WIN64) && !defined(NO_ATOMICS)
1288#elif defined(_WIN32) && !defined(NO_ATOMICS)
1290#elif defined(__GNUC__) \
1291 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1292 && !defined(NO_ATOMICS)
1294#else
1296 *addr += value;
1297 ret = (*addr);
1299#endif
1300 return ret;
1301}
1302#endif
1303
1304
1305#if defined(GCC_DIAGNOSTIC)
1306/* Show no warning in case system functions are not used. */
1307#pragma GCC diagnostic pop
1308#endif /* defined(GCC_DIAGNOSTIC) */
1309#if defined(__clang__)
1310/* Show no warning in case system functions are not used. */
1311#pragma clang diagnostic pop
1312#endif
1313
1314
1315#if defined(USE_SERVER_STATS)
1316
1317struct mg_memory_stat {
1318 volatile ptrdiff_t totalMemUsed;
1319 volatile ptrdiff_t maxMemUsed;
1320 volatile ptrdiff_t blockCount;
1321};
1322
1323
1324static struct mg_memory_stat *get_memory_stat(struct mg_context *ctx);
1325
1326
1327static void *
1328mg_malloc_ex(size_t size,
1329 struct mg_context *ctx,
1330 const char *file,
1331 unsigned line)
1332{
1333 void *data = malloc(size + 2 * sizeof(uintptr_t));
1334 void *memory = 0;
1335 struct mg_memory_stat *mstat = get_memory_stat(ctx);
1336
1337#if defined(MEMORY_DEBUGGING)
1338 char mallocStr[256];
1339#else
1340 (void)file;
1341 (void)line;
1342#endif
1343
1344 if (data) {
1345 uintptr_t *tmp = (uintptr_t *)data;
1346 ptrdiff_t mmem = mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)size);
1347 mg_atomic_max(&mstat->maxMemUsed, mmem);
1348 mg_atomic_inc(&mstat->blockCount);
1349 tmp[0] = size;
1350 tmp[1] = (uintptr_t)mstat;
1351 memory = (void *)&tmp[2];
1352 }
1353
1354#if defined(MEMORY_DEBUGGING)
1356 "MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n",
1357 memory,
1358 (unsigned long)size,
1359 (unsigned long)mstat->totalMemUsed,
1360 (unsigned long)mstat->blockCount,
1361 file,
1362 line);
1363 DEBUG_TRACE("%s", mallocStr);
1364#endif
1365
1366 return memory;
1367}
1368
1369
1370static void *
1371mg_calloc_ex(size_t count,
1372 size_t size,
1373 struct mg_context *ctx,
1374 const char *file,
1375 unsigned line)
1376{
1377 void *data = mg_malloc_ex(size * count, ctx, file, line);
1378
1379 if (data) {
1380 memset(data, 0, size * count);
1381 }
1382 return data;
1383}
1384
1385
1386static void
1387mg_free_ex(void *memory, const char *file, unsigned line)
1388{
1389#if defined(MEMORY_DEBUGGING)
1390 char mallocStr[256];
1391#else
1392 (void)file;
1393 (void)line;
1394#endif
1395
1396 if (memory) {
1397 void *data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t));
1398 uintptr_t size = ((uintptr_t *)data)[0];
1399 struct mg_memory_stat *mstat =
1400 (struct mg_memory_stat *)(((uintptr_t *)data)[1]);
1401 mg_atomic_add(&mstat->totalMemUsed, -(ptrdiff_t)size);
1402 mg_atomic_dec(&mstat->blockCount);
1403
1404#if defined(MEMORY_DEBUGGING)
1406 "MEM: %p %5lu free %7lu %4lu --- %s:%u\n",
1407 memory,
1408 (unsigned long)size,
1409 (unsigned long)mstat->totalMemUsed,
1410 (unsigned long)mstat->blockCount,
1411 file,
1412 line);
1413 DEBUG_TRACE("%s", mallocStr);
1414#endif
1415 free(data);
1416 }
1417}
1418
1419
1420static void *
1421mg_realloc_ex(void *memory,
1422 size_t newsize,
1423 struct mg_context *ctx,
1424 const char *file,
1425 unsigned line)
1426{
1427 void *data;
1428 void *_realloc;
1430
1431#if defined(MEMORY_DEBUGGING)
1432 char mallocStr[256];
1433#else
1434 (void)file;
1435 (void)line;
1436#endif
1437
1438 if (newsize) {
1439 if (memory) {
1440 /* Reallocate existing block */
1441 struct mg_memory_stat *mstat;
1442 data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t));
1443 oldsize = ((uintptr_t *)data)[0];
1444 mstat = (struct mg_memory_stat *)((uintptr_t *)data)[1];
1445 _realloc = realloc(data, newsize + 2 * sizeof(uintptr_t));
1446 if (_realloc) {
1447 data = _realloc;
1448 mg_atomic_add(&mstat->totalMemUsed, -(ptrdiff_t)oldsize);
1449#if defined(MEMORY_DEBUGGING)
1451 "MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n",
1452 memory,
1453 (unsigned long)oldsize,
1454 (unsigned long)mstat->totalMemUsed,
1455 (unsigned long)mstat->blockCount,
1456 file,
1457 line);
1458 DEBUG_TRACE("%s", mallocStr);
1459#endif
1460 mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)newsize);
1461
1462#if defined(MEMORY_DEBUGGING)
1464 "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n",
1465 memory,
1466 (unsigned long)newsize,
1467 (unsigned long)mstat->totalMemUsed,
1468 (unsigned long)mstat->blockCount,
1469 file,
1470 line);
1471 DEBUG_TRACE("%s", mallocStr);
1472#endif
1473 *(uintptr_t *)data = newsize;
1474 data = (void *)(((char *)data) + 2 * sizeof(uintptr_t));
1475 } else {
1476#if defined(MEMORY_DEBUGGING)
1477 DEBUG_TRACE("%s", "MEM: realloc failed\n");
1478#endif
1479 return _realloc;
1480 }
1481 } else {
1482 /* Allocate new block */
1483 data = mg_malloc_ex(newsize, ctx, file, line);
1484 }
1485 } else {
1486 /* Free existing block */
1487 data = 0;
1488 mg_free_ex(memory, file, line);
1489 }
1490
1491 return data;
1492}
1493
1494
1495#define mg_malloc(a) mg_malloc_ex(a, NULL, __FILE__, __LINE__)
1496#define mg_calloc(a, b) mg_calloc_ex(a, b, NULL, __FILE__, __LINE__)
1497#define mg_realloc(a, b) mg_realloc_ex(a, b, NULL, __FILE__, __LINE__)
1498#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__)
1499
1500#define mg_malloc_ctx(a, c) mg_malloc_ex(a, c, __FILE__, __LINE__)
1501#define mg_calloc_ctx(a, b, c) mg_calloc_ex(a, b, c, __FILE__, __LINE__)
1502#define mg_realloc_ctx(a, b, c) mg_realloc_ex(a, b, c, __FILE__, __LINE__)
1503
1504
1505#else /* USE_SERVER_STATS */
1506
1507
1508static __inline void *
1510{
1511 return malloc(a);
1512}
1513
1514static __inline void *
1515mg_calloc(size_t a, size_t b)
1516{
1517 return calloc(a, b);
1518}
1519
1520static __inline void *
1521mg_realloc(void *a, size_t b)
1522{
1523 return realloc(a, b);
1524}
1525
1526static __inline void
1528{
1529 free(a);
1530}
1531
1532#define mg_malloc_ctx(a, c) mg_malloc(a)
1533#define mg_calloc_ctx(a, b, c) mg_calloc(a, b)
1534#define mg_realloc_ctx(a, b, c) mg_realloc(a, b)
1535#define mg_free_ctx(a, c) mg_free(a)
1536
1537#endif /* USE_SERVER_STATS */
1538
1539
1540static void mg_vsnprintf(const struct mg_connection *conn,
1541 int *truncated,
1542 char *buf,
1543 size_t buflen,
1544 const char *fmt,
1545 va_list ap);
1546
1547static void mg_snprintf(const struct mg_connection *conn,
1548 int *truncated,
1549 char *buf,
1550 size_t buflen,
1551 PRINTF_FORMAT_STRING(const char *fmt),
1552 ...) PRINTF_ARGS(5, 6);
1553
1554/* This following lines are just meant as a reminder to use the mg-functions
1555 * for memory management */
1556#if defined(malloc)
1557#undef malloc
1558#endif
1559#if defined(calloc)
1560#undef calloc
1561#endif
1562#if defined(realloc)
1563#undef realloc
1564#endif
1565#if defined(free)
1566#undef free
1567#endif
1568#if defined(snprintf)
1569#undef snprintf
1570#endif
1571#if defined(vsnprintf)
1572#undef vsnprintf
1573#endif
1574#if !defined(NDEBUG)
1575#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
1576#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
1577#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
1578#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
1579#define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
1580#endif
1581#if defined(_WIN32)
1582/* vsnprintf must not be used in any system,
1583 * but this define only works well for Windows. */
1584#define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf
1585#endif
1586
1587
1588/* mg_init_library counter */
1590
1591#if !defined(NO_SSL)
1592#if defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1) \
1593 || defined(OPENSSL_API_3_0)
1594static int mg_openssl_initialized = 0;
1595#endif
1596#if !defined(OPENSSL_API_1_0) && !defined(OPENSSL_API_1_1) \
1597 && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS) \
1598 && !defined(USE_GNUTLS)
1599#error "Please define OPENSSL_API_#_# or USE_MBEDTLS or USE_GNUTLS"
1600#endif
1601#if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_1_1)
1602#error "Multiple OPENSSL_API versions defined"
1603#endif
1604#if defined(OPENSSL_API_1_1) && defined(OPENSSL_API_3_0)
1605#error "Multiple OPENSSL_API versions defined"
1606#endif
1607#if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_3_0)
1608#error "Multiple OPENSSL_API versions defined"
1609#endif
1610#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1) \
1611 || defined(OPENSSL_API_3_0)) \
1612 && (defined(USE_MBEDTLS) || defined(USE_GNUTLS))
1613#error "Multiple SSL libraries defined"
1614#endif
1615#if defined(USE_MBEDTLS) && defined(USE_GNUTLS)
1616#error "Multiple SSL libraries defined"
1617#endif
1618#endif
1619
1620
1621static pthread_key_t sTlsKey; /* Thread local storage index */
1622static volatile ptrdiff_t thread_idx_max = 0;
1623
1624#if defined(MG_LEGACY_INTERFACE)
1625#define MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE
1626#endif
1627
1630 unsigned long thread_idx;
1632#if defined(_WIN32)
1635#endif
1636 const char *alpn_proto;
1637#if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE)
1638 char txtbuf[4];
1639#endif
1640};
1641
1642
1643#if defined(GCC_DIAGNOSTIC)
1644/* Show no warning in case system functions are not used. */
1645#pragma GCC diagnostic push
1646#pragma GCC diagnostic ignored "-Wunused-function"
1647#endif /* defined(GCC_DIAGNOSTIC) */
1648#if defined(__clang__)
1649/* Show no warning in case system functions are not used. */
1650#pragma clang diagnostic push
1651#pragma clang diagnostic ignored "-Wunused-function"
1652#endif
1653
1654
1655/* Get a unique thread ID as unsigned long, independent from the data type
1656 * of thread IDs defined by the operating system API.
1657 * If two calls to mg_current_thread_id return the same value, they calls
1658 * are done from the same thread. If they return different values, they are
1659 * done from different threads. (Provided this function is used in the same
1660 * process context and threads are not repeatedly created and deleted, but
1661 * CivetWeb does not do that).
1662 * This function must match the signature required for SSL id callbacks:
1663 * CRYPTO_set_id_callback
1664 */
1666static unsigned long
1668{
1669#if defined(_WIN32)
1670 return GetCurrentThreadId();
1671#else
1672
1673#if defined(__clang__)
1674#pragma clang diagnostic push
1675#pragma clang diagnostic ignored "-Wunreachable-code"
1676 /* For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)"
1677 * or not, so one of the two conditions will be unreachable by construction.
1678 * Unfortunately the C standard does not define a way to check this at
1679 * compile time, since the #if preprocessor conditions can not use the
1680 * sizeof operator as an argument. */
1681#endif
1682
1683 if (sizeof(pthread_t) > sizeof(unsigned long)) {
1684 /* This is the problematic case for CRYPTO_set_id_callback:
1685 * The OS pthread_t can not be cast to unsigned long. */
1686 struct mg_workerTLS *tls =
1688 if (tls == NULL) {
1689 /* SSL called from an unknown thread: Create some thread index.
1690 */
1691 tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct mg_workerTLS));
1692 tls->is_master = -2; /* -2 means "3rd party thread" */
1693 tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
1695 }
1696 return tls->thread_idx;
1697 } else {
1698 /* pthread_t may be any data type, so a simple cast to unsigned long
1699 * can rise a warning/error, depending on the platform.
1700 * Here memcpy is used as an anything-to-anything cast. */
1701 unsigned long ret = 0;
1702 pthread_t t = pthread_self();
1703 memcpy(&ret, &t, sizeof(pthread_t));
1704 return ret;
1705 }
1706
1707#if defined(__clang__)
1708#pragma clang diagnostic pop
1709#endif
1710
1711#endif
1712}
1713
1714
1716static uint64_t
1718{
1719 struct timespec tsnow;
1721 return (((uint64_t)tsnow.tv_sec) * 1000000000) + (uint64_t)tsnow.tv_nsec;
1722}
1723
1724
1725#if defined(GCC_DIAGNOSTIC)
1726/* Show no warning in case system functions are not used. */
1727#pragma GCC diagnostic pop
1728#endif /* defined(GCC_DIAGNOSTIC) */
1729#if defined(__clang__)
1730/* Show no warning in case system functions are not used. */
1731#pragma clang diagnostic pop
1732#endif
1733
1734
1735#if defined(NEED_DEBUG_TRACE_FUNC)
1736static void
1737DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...)
1738{
1739 va_list args;
1740 struct timespec tsnow;
1741
1742 /* Get some operating system independent thread id */
1743 unsigned long thread_id = mg_current_thread_id();
1744
1746
1749 "*** %lu.%09lu %lu %s:%u: ",
1750 (unsigned long)tsnow.tv_sec,
1751 (unsigned long)tsnow.tv_nsec,
1752 thread_id,
1753 func,
1754 line);
1755 va_start(args, fmt);
1757 va_end(args);
1758 putc('\n', DEBUG_TRACE_STREAM);
1761}
1762#endif /* NEED_DEBUG_TRACE_FUNC */
1763
1764
1765#define MD5_STATIC static
1766#include "md5.inl"
1767
1768/* Darwin prior to 7.0 and Win32 do not have socklen_t */
1769#if defined(NO_SOCKLEN_T)
1770typedef int socklen_t;
1771#endif /* NO_SOCKLEN_T */
1772
1773#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */
1774
1775#if !defined(MSG_NOSIGNAL)
1776#define MSG_NOSIGNAL (0)
1777#endif
1778
1779
1780/* SSL: mbedTLS vs. GnuTLS vs. no-ssl vs. OpenSSL */
1781#if defined(USE_MBEDTLS)
1782/* mbedTLS */
1783#include "mod_mbedtls.inl"
1784
1785#elif defined(USE_GNUTLS)
1786/* GnuTLS */
1787#include "mod_gnutls.inl"
1788
1789#elif defined(NO_SSL)
1790/* no SSL */
1791typedef struct SSL SSL; /* dummy for SSL argument to push/pull */
1792typedef struct SSL_CTX SSL_CTX;
1793
1794#elif defined(NO_SSL_DL)
1795/* OpenSSL without dynamic loading */
1796#include <openssl/bn.h>
1797#include <openssl/conf.h>
1798#include <openssl/crypto.h>
1799#include <openssl/dh.h>
1800// remove to avoid problems with future Fedora, see https://github.com/root-project/root/commit/d75eeb8
1801// #include <openssl/engine.h>
1802#include <openssl/err.h>
1803#include <openssl/opensslv.h>
1804#include <openssl/pem.h>
1805#include <openssl/ssl.h>
1806#include <openssl/tls1.h>
1807#include <openssl/x509.h>
1808
1809#if defined(WOLFSSL_VERSION)
1810/* Additional defines for WolfSSL, see
1811 * https://github.com/civetweb/civetweb/issues/583 */
1812#include "wolfssl_extras.inl"
1813#endif
1814
1815#if defined(OPENSSL_IS_BORINGSSL)
1816/* From boringssl/src/include/openssl/mem.h:
1817 *
1818 * OpenSSL has, historically, had a complex set of malloc debugging options.
1819 * However, that was written in a time before Valgrind and ASAN. Since we now
1820 * have those tools, the OpenSSL allocation functions are simply macros around
1821 * the standard memory functions.
1822 *
1823 * #define OPENSSL_free free */
1824#define free free
1825// disable for boringssl
1826#define CONF_modules_unload(a) ((void)0)
1827#define ENGINE_cleanup() ((void)0)
1828#endif
1829
1830/* If OpenSSL headers are included, automatically select the API version */
1831#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
1832#if !defined(OPENSSL_API_3_0)
1833#define OPENSSL_API_3_0
1834#endif
1835#define OPENSSL_REMOVE_THREAD_STATE()
1836#else
1837#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
1838#if !defined(OPENSSL_API_1_1)
1839#define OPENSSL_API_1_1
1840#endif
1841#define OPENSSL_REMOVE_THREAD_STATE()
1842#else
1843#if !defined(OPENSSL_API_1_0)
1844#define OPENSSL_API_1_0
1845#endif
1846#define OPENSSL_REMOVE_THREAD_STATE() ERR_remove_thread_state(NULL)
1847#endif
1848#endif
1849
1850
1851#else
1852/* SSL loaded dynamically from DLL / shared object */
1853/* Add all prototypes here, to be independent from OpenSSL source
1854 * installation. */
1855#include "openssl_dl.inl"
1856
1857#endif /* Various SSL bindings */
1858
1859
1860#if !defined(NO_CACHING)
1861static const char month_names[][4] = {"Jan",
1862 "Feb",
1863 "Mar",
1864 "Apr",
1865 "May",
1866 "Jun",
1867 "Jul",
1868 "Aug",
1869 "Sep",
1870 "Oct",
1871 "Nov",
1872 "Dec"};
1873#endif /* !NO_CACHING */
1874
1875
1876/* Unified socket address. For IPv6 support, add IPv6 address structure in
1877 * the union u. */
1878union usa {
1879 struct sockaddr sa;
1880 struct sockaddr_in sin;
1881#if defined(USE_IPV6)
1882 struct sockaddr_in6 sin6;
1883#endif
1884#if defined(USE_X_DOM_SOCKET)
1885 struct sockaddr_un sun;
1886#endif
1887};
1888
1889#if defined(USE_X_DOM_SOCKET)
1890static unsigned short
1891USA_IN_PORT_UNSAFE(union usa *s)
1892{
1893 if (s->sa.sa_family == AF_INET)
1894 return s->sin.sin_port;
1895#if defined(USE_IPV6)
1896 if (s->sa.sa_family == AF_INET6)
1897 return s->sin6.sin6_port;
1898#endif
1899 return 0;
1900}
1901#endif
1902#if defined(USE_IPV6)
1903#define USA_IN_PORT_UNSAFE(s) \
1904 (((s)->sa.sa_family == AF_INET6) ? (s)->sin6.sin6_port : (s)->sin.sin_port)
1905#else
1906#define USA_IN_PORT_UNSAFE(s) ((s)->sin.sin_port)
1907#endif
1908
1909/* Describes a string (chunk of memory). */
1910struct vec {
1911 const char *ptr;
1912 size_t len;
1913};
1914
1916 /* File properties filled by mg_stat: */
1917 uint64_t size;
1919 int is_directory; /* Set to 1 if mg_stat is called for a directory */
1920 int is_gzipped; /* Set to 1 if the content is gzipped, in which
1921 * case we need a "Content-Eencoding: gzip" header */
1922 int location; /* 0 = nowhere, 1 = on disk, 2 = in memory */
1923};
1924
1925
1927 /* File properties filled by mg_fopen: */
1929};
1930
1935
1936
1937#define STRUCT_FILE_INITIALIZER \
1938 { \
1939 {(uint64_t)0, (time_t)0, 0, 0, 0}, \
1940 { \
1941 (FILE *)NULL \
1942 } \
1943 }
1944
1945
1946/* Describes listening socket, or socket which was accept()-ed by the master
1947 * thread and queued for future handling by the worker thread. */
1948struct socket {
1949 SOCKET sock; /* Listening socket */
1950 union usa lsa; /* Local socket address */
1951 union usa rsa; /* Remote socket address */
1952 unsigned char is_ssl; /* Is port SSL-ed */
1953 unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL
1954 * port */
1955 unsigned char
1956 is_optional; /* Shouldn't cause us to exit if we can't bind to it */
1957 unsigned char in_use; /* 0: invalid, 1: valid, 2: free */
1958};
1959
1960
1961/* Enum const for all options must be in sync with
1962 * static struct mg_option config_options[]
1963 * This is tested in the unit test (test/private.c)
1964 * "Private Config Options"
1965 */
1966enum {
1967 /* Once for each server */
1972 CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
1973 * socket option typedef TCP_NODELAY. */
1978#if defined(__linux__)
1980#endif
1981#if defined(_WIN32)
1983#endif
1988#if defined(USE_WEBSOCKET)
1991#endif
1994#if defined(USE_LUA)
1997#endif
1998#if defined(USE_HTTP2)
2000#endif
2001
2002 /* Once for each domain */
2005
2008
2013#if defined(USE_TIMERS)
2015#endif
2017
2022#if defined(USE_TIMERS)
2024#endif
2026
2027#if defined(USE_4_CGI)
2032#if defined(USE_TIMERS)
2034#endif
2036
2041#if defined(USE_TIMERS)
2043#endif
2045#endif
2046
2047 PUT_DELETE_PASSWORDS_FILE, /* must follow CGI_* */
2071
2072#if defined(USE_LUA)
2076#if defined(MG_EXPERIMENTAL_INTERFACES)
2078#endif
2079#endif
2080#if defined(USE_DUKTAPE)
2082#endif
2083
2084#if defined(USE_WEBSOCKET)
2087#endif
2088#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2090#endif
2091
2098#if !defined(NO_CACHING)
2101#endif
2102#if !defined(NO_SSL)
2104#endif
2107
2110
2111
2112/* Config option name, config types, default value.
2113 * Must be in the same order as the enum const above.
2114 */
2115static const struct mg_option config_options[] = {
2116
2117 /* Once for each server */
2118 {"listening_ports", MG_CONFIG_TYPE_STRING_LIST, "8080"},
2119 {"num_threads", MG_CONFIG_TYPE_NUMBER, "50"},
2120 {"prespawn_threads", MG_CONFIG_TYPE_NUMBER, "0"},
2121 {"run_as_user", MG_CONFIG_TYPE_STRING, NULL},
2122 {"tcp_nodelay", MG_CONFIG_TYPE_NUMBER, "0"},
2123 {"max_request_size", MG_CONFIG_TYPE_NUMBER, "16384"},
2124 {"linger_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2125 {"connection_queue", MG_CONFIG_TYPE_NUMBER, "20"},
2126 {"listen_backlog", MG_CONFIG_TYPE_NUMBER, "200"},
2127#if defined(__linux__)
2128 {"allow_sendfile_call", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2129#endif
2130#if defined(_WIN32)
2131 {"case_sensitive", MG_CONFIG_TYPE_BOOLEAN, "no"},
2132#endif
2133 {"throttle", MG_CONFIG_TYPE_STRING_LIST, NULL},
2134 {"enable_keep_alive", MG_CONFIG_TYPE_BOOLEAN, "no"},
2135 {"request_timeout_ms", MG_CONFIG_TYPE_NUMBER, "30000"},
2136 {"keep_alive_timeout_ms", MG_CONFIG_TYPE_NUMBER, "500"},
2137#if defined(USE_WEBSOCKET)
2138 {"websocket_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2139 {"enable_websocket_ping_pong", MG_CONFIG_TYPE_BOOLEAN, "no"},
2140#endif
2141 {"decode_url", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2142 {"decode_query_string", MG_CONFIG_TYPE_BOOLEAN, "no"},
2143#if defined(USE_LUA)
2144 {"lua_background_script", MG_CONFIG_TYPE_FILE, NULL},
2145 {"lua_background_script_params", MG_CONFIG_TYPE_STRING_LIST, NULL},
2146#endif
2147#if defined(USE_HTTP2)
2148 {"enable_http2", MG_CONFIG_TYPE_BOOLEAN, "no"},
2149#endif
2150
2151 /* Once for each domain */
2152 {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2153 {"fallback_document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2154
2155 {"access_log_file", MG_CONFIG_TYPE_FILE, NULL},
2156 {"error_log_file", MG_CONFIG_TYPE_FILE, NULL},
2157
2158 {"cgi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"},
2159 {"cgi_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2160 {"cgi_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2161 {"cgi_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2162#if defined(USE_TIMERS)
2163 {"cgi_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2164#endif
2165 {"cgi_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2166
2167 {"cgi2_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2168 {"cgi2_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2169 {"cgi2_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2170 {"cgi2_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2171#if defined(USE_TIMERS)
2172 {"cgi2_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2173#endif
2174 {"cgi2_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2175
2176#if defined(USE_4_CGI)
2177 {"cgi3_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2178 {"cgi3_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2179 {"cgi3_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2180 {"cgi3_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2181#if defined(USE_TIMERS)
2182 {"cgi3_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2183#endif
2184 {"cgi3_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2185
2186 {"cgi4_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2187 {"cgi4_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2188 {"cgi4_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2189 {"cgi4_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2190#if defined(USE_TIMERS)
2191 {"cgi4_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2192#endif
2193 {"cgi4_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2194
2195#endif
2196
2197 {"put_delete_auth_file", MG_CONFIG_TYPE_FILE, NULL},
2198 {"protect_uri", MG_CONFIG_TYPE_STRING_LIST, NULL},
2199 {"authentication_domain", MG_CONFIG_TYPE_STRING, "mydomain.com"},
2200 {"enable_auth_domain_check", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2201 {"ssi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"},
2202 {"enable_directory_listing", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2203 {"enable_webdav", MG_CONFIG_TYPE_BOOLEAN, "no"},
2204 {"global_auth_file", MG_CONFIG_TYPE_FILE, NULL},
2205 {"index_files",
2207#if defined(USE_LUA)
2208 "index.xhtml,index.html,index.htm,"
2209 "index.lp,index.lsp,index.lua,index.cgi,"
2210 "index.shtml,index.php"},
2211#else
2212 "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"},
2213#endif
2214 {"access_control_list", MG_CONFIG_TYPE_STRING_LIST, NULL},
2215 {"extra_mime_types", MG_CONFIG_TYPE_STRING_LIST, NULL},
2216 {"ssl_certificate", MG_CONFIG_TYPE_FILE, NULL},
2217 {"ssl_certificate_chain", MG_CONFIG_TYPE_FILE, NULL},
2218 {"url_rewrite_patterns", MG_CONFIG_TYPE_STRING_LIST, NULL},
2219 {"hide_files_patterns", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2220
2221 {"ssl_verify_peer", MG_CONFIG_TYPE_YES_NO_OPTIONAL, "no"},
2222 {"ssl_cache_timeout", MG_CONFIG_TYPE_NUMBER, "-1"},
2223
2224 {"ssl_ca_path", MG_CONFIG_TYPE_DIRECTORY, NULL},
2225 {"ssl_ca_file", MG_CONFIG_TYPE_FILE, NULL},
2226 {"ssl_verify_depth", MG_CONFIG_TYPE_NUMBER, "9"},
2227 {"ssl_default_verify_paths", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2228 {"ssl_cipher_list", MG_CONFIG_TYPE_STRING, NULL},
2229
2230 /* HTTP2 requires ALPN, and anyway TLS1.2 should be considered
2231 * as a minimum in 2020 */
2232 {"ssl_protocol_version", MG_CONFIG_TYPE_NUMBER, "4"},
2233
2234 {"ssl_short_trust", MG_CONFIG_TYPE_BOOLEAN, "no"},
2235
2236#if defined(USE_LUA)
2237 {"lua_preload_file", MG_CONFIG_TYPE_FILE, NULL},
2238 {"lua_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
2239 {"lua_server_page_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"},
2240#if defined(MG_EXPERIMENTAL_INTERFACES)
2241 {"lua_debug", MG_CONFIG_TYPE_STRING, NULL},
2242#endif
2243#endif
2244#if defined(USE_DUKTAPE)
2245 /* The support for duktape is still in alpha version state.
2246 * The name of this config option might change. */
2247 {"duktape_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"},
2248#endif
2249
2250#if defined(USE_WEBSOCKET)
2251 {"websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2252 {"fallback_websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2253#endif
2254#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2255 {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
2256#endif
2257 {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"},
2258 {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"},
2259 {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"},
2260 {"access_control_expose_headers", MG_CONFIG_TYPE_STRING, ""},
2261 {"access_control_allow_credentials", MG_CONFIG_TYPE_STRING, ""},
2262 {"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL},
2263#if !defined(NO_CACHING)
2264 {"static_file_max_age", MG_CONFIG_TYPE_NUMBER, "3600"},
2265 {"static_file_cache_control", MG_CONFIG_TYPE_STRING, NULL},
2266#endif
2267#if !defined(NO_SSL)
2268 {"strict_transport_security_max_age", MG_CONFIG_TYPE_NUMBER, NULL},
2269#endif
2270 {"additional_header", MG_CONFIG_TYPE_STRING_MULTILINE, NULL},
2271 {"allow_index_script_resource", MG_CONFIG_TYPE_BOOLEAN, "no"},
2272
2274
2275
2276/* Check if the config_options and the corresponding enum have compatible
2277 * sizes. */
2278mg_static_assert((sizeof(config_options) / sizeof(config_options[0]))
2279 == (NUM_OPTIONS + 1),
2280 "config_options and enum not sync");
2281
2282
2284
2285
2287 /* Name/Pattern of the URI. */
2288 char *uri;
2289 size_t uri_len;
2290
2291 /* handler type */
2293
2294 /* Handler for http/https or requests. */
2296 unsigned int refcount;
2298
2299 /* Handler for ws/wss (websocket) requests. */
2304
2305 /* accepted subprotocols for ws/wss requests. */
2307
2308 /* Handler for authorization requests */
2310
2311 /* User supplied argument for the handler function. */
2312 void *cbdata;
2313
2314 /* next handler in a linked list */
2316};
2317
2318
2319enum {
2325
2326
2328 SSL_CTX *ssl_ctx; /* SSL context */
2329 char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */
2330 struct mg_handler_info *handlers; /* linked list of uri handlers */
2332
2333 /* Server nonce */
2334 uint64_t auth_nonce_mask; /* Mask for all nonce values */
2335 unsigned long nonce_count; /* Used nonces, used for authentication */
2336
2337#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2338 /* linked list of shared lua websockets */
2340#endif
2341
2342 /* Linked list of domains */
2344};
2345
2346
2347/* Stop flag can be "volatile" or require a lock.
2348 * MSDN uses volatile for "Interlocked" operations, but also explicitly
2349 * states a read operation for int is always atomic. */
2350#if defined(STOP_FLAG_NEEDS_LOCK)
2351
2352typedef ptrdiff_t volatile stop_flag_t;
2353
2354static int
2356{
2358 return (sf == 0);
2359}
2360
2361static int
2363{
2365 return (sf == 2);
2366}
2367
2368static void
2370{
2371 stop_flag_t sf = 0;
2372 do {
2375 v);
2376 } while (sf != v);
2377}
2378
2379#else /* STOP_FLAG_NEEDS_LOCK */
2380
2381typedef int volatile stop_flag_t;
2382#define STOP_FLAG_IS_ZERO(f) ((*(f)) == 0)
2383#define STOP_FLAG_IS_TWO(f) ((*(f)) == 2)
2384#define STOP_FLAG_ASSIGN(f, v) ((*(f)) = (v))
2385
2386#endif /* STOP_FLAG_NEEDS_LOCK */
2387
2388
2389#if !defined(NUM_WEBDAV_LOCKS)
2390#define NUM_WEBDAV_LOCKS 10
2391#endif
2392#if !defined(LOCK_DURATION_S)
2393#define LOCK_DURATION_S 60
2394#endif
2395
2396
2398 uint64_t locktime;
2399 char token[33];
2402};
2403
2404
2406
2407 /* Part 1 - Physical context:
2408 * This holds threads, ports, timeouts, ...
2409 * set for the entire server, independent from the
2410 * addressed hostname.
2411 */
2412
2413 /* Connection related */
2414 int context_type; /* See CONTEXT_* above */
2415
2419
2420 struct mg_connection *worker_connections; /* The connection struct, pre-
2421 * allocated for each worker */
2422
2423#if defined(USE_SERVER_STATS)
2424 volatile ptrdiff_t active_connections;
2425 volatile ptrdiff_t max_active_connections;
2426 volatile ptrdiff_t total_connections;
2427 volatile ptrdiff_t total_requests;
2428 volatile int64_t total_data_read;
2429 volatile int64_t total_data_written;
2430#endif
2431
2432 /* Thread related */
2433 stop_flag_t stop_flag; /* Should we stop event loop */
2434 pthread_mutex_t thread_mutex; /* Protects client_socks or queue */
2435
2436 pthread_t masterthreadid; /* The master thread ID */
2437 unsigned int cfg_max_worker_threads; /* How many worker-threads we are
2438 allowed to create, total */
2439
2440 unsigned int spawned_worker_threads; /* How many worker-threads currently
2441 exist (modified by master thread) */
2442 unsigned int
2443 idle_worker_thread_count; /* How many worker-threads are currently
2444 sitting around with nothing to do */
2445 /* Access to this value MUST be synchronized by thread_mutex */
2446
2447 pthread_t *worker_threadids; /* The worker thread IDs */
2448 unsigned long starter_thread_idx; /* thread index which called mg_start */
2449
2450 /* Connection to thread dispatching */
2451#if defined(ALTERNATIVE_QUEUE)
2452 struct socket *client_socks;
2453 void **client_wait_events;
2454#else
2455 struct socket *squeue; /* Socket queue (sq) : accepted sockets waiting for a
2456 worker thread */
2457 volatile int sq_head; /* Head of the socket queue */
2458 volatile int sq_tail; /* Tail of the socket queue */
2459 pthread_cond_t sq_full; /* Signaled when socket is produced */
2460 pthread_cond_t sq_empty; /* Signaled when socket is consumed */
2461 volatile int sq_blocked; /* Status information: sq is full */
2462 int sq_size; /* No of elements in socket queue */
2463#if defined(USE_SERVER_STATS)
2464 int sq_max_fill;
2465#endif /* USE_SERVER_STATS */
2466#endif /* ALTERNATIVE_QUEUE */
2467
2468 /* Memory related */
2469 unsigned int max_request_size; /* The max request size */
2470
2471#if defined(USE_SERVER_STATS)
2473#endif
2474
2475 /* WebDAV lock structures */
2477
2478 /* Operating system related */
2479 char *systemName; /* What operating system is running */
2480 time_t start_time; /* Server start time, used for authentication
2481 * and for diagnstics. */
2482
2483#if defined(USE_TIMERS)
2484 struct ttimers *timers;
2485#endif
2486
2487 /* Lua specific: Background operations and shared websockets */
2488#if defined(USE_LUA)
2489 void *lua_background_state; /* lua_State (here as void *) */
2490 pthread_mutex_t lua_bg_mutex; /* Protect background state */
2491 int lua_bg_log_available; /* Use Lua background state for access log */
2492#endif
2493
2494 int user_shutdown_notification_socket; /* mg_stop() will close this
2495 socket... */
2496 int thread_shutdown_notification_socket; /* to cause poll() in all threads
2497 to return immediately */
2498
2499 /* Server nonce */
2500 pthread_mutex_t nonce_mutex; /* Protects ssl_ctx, handlers,
2501 * ssl_cert_last_mtime, nonce_count, and
2502 * next (linked list) */
2503
2504 /* Server callbacks */
2505 struct mg_callbacks callbacks; /* User-defined callback function */
2506 void *user_data; /* User-defined data */
2507
2508 /* Part 2 - Logical domain:
2509 * This holds hostname, TLS certificate, document root, ...
2510 * set for a domain hosted at the server.
2511 * There may be multiple domains hosted at one physical server.
2512 * The default domain "dd" is the first element of a list of
2513 * domains.
2514 */
2515 struct mg_domain_context dd; /* default domain */
2516};
2517
2518
2519#if defined(USE_SERVER_STATS)
2520static struct mg_memory_stat mg_common_memory = {0, 0, 0};
2521
2522static struct mg_memory_stat *
2523get_memory_stat(struct mg_context *ctx)
2524{
2525 if (ctx) {
2526 return &(ctx->ctx_memory);
2527 }
2528 return &mg_common_memory;
2529}
2530#endif
2531
2532enum {
2537
2538enum {
2543
2544
2545#if defined(USE_HTTP2)
2546#if !defined(HTTP2_DYN_TABLE_SIZE)
2547#define HTTP2_DYN_TABLE_SIZE (256)
2548#endif
2549
2550struct mg_http2_connection {
2551 uint32_t stream_id;
2552 uint32_t dyn_table_size;
2554};
2555#endif
2556
2557
2559 int connection_type; /* see CONNECTION_TYPE_* above */
2560 int protocol_type; /* see PROTOCOL_TYPE_*: 0=http/1.x, 1=ws, 2=http/2 */
2561 int request_state; /* 0: nothing sent, 1: header partially sent, 2: header
2562 fully sent */
2563#if defined(USE_HTTP2)
2565#endif
2566
2569
2572
2573#if defined(USE_SERVER_STATS)
2574 int conn_state; /* 0 = undef, numerical value may change in different
2575 * versions. For the current definition, see
2576 * mg_get_connection_info_impl */
2577#endif
2578 SSL *ssl; /* SSL descriptor */
2579 struct socket client; /* Connected client */
2580 time_t conn_birth_time; /* Time (wall clock) when connection was
2581 * established */
2582#if defined(USE_SERVER_STATS)
2583 time_t conn_close_time; /* Time (wall clock) when connection was
2584 * closed (or 0 if still open) */
2585 double processing_time; /* Processing time for one request. */
2586#endif
2587 struct timespec req_time; /* Time (since system start) when the request
2588 * was received */
2589 int64_t num_bytes_sent; /* Total bytes sent to client */
2590 int64_t content_len; /* How many bytes of content can be read
2591 * !is_chunked: Content-Length header value
2592 * or -1 (until connection closed,
2593 * not allowed for a request)
2594 * is_chunked: >= 0, appended gradually
2595 */
2596 int64_t consumed_content; /* How many bytes of content have been read */
2597 int is_chunked; /* Transfer-Encoding is chunked:
2598 * 0 = not chunked,
2599 * 1 = chunked, not yet, or some data read,
2600 * 2 = chunked, has error,
2601 * 3 = chunked, all data read except trailer,
2602 * 4 = chunked, all data read
2603 */
2604 char *buf; /* Buffer for received data */
2605 char *path_info; /* PATH_INFO part of the URL */
2606
2607 int must_close; /* 1 if connection must be closed */
2608 int accept_gzip; /* 1 if gzip encoding is accepted */
2609 int in_error_handler; /* 1 if in handler for user defined error
2610 * pages */
2611#if defined(USE_WEBSOCKET)
2612 int in_websocket_handling; /* 1 if in read_websocket */
2613#endif
2614#if defined(USE_ZLIB) && defined(USE_WEBSOCKET) \
2615 && defined(MG_EXPERIMENTAL_INTERFACES)
2616 /* Parameters for websocket data compression according to rfc7692 */
2625#endif
2626 int handled_requests; /* Number of requests handled by this connection
2627 */
2628 int buf_size; /* Buffer size */
2629 int request_len; /* Size of the request + headers in a buffer */
2630 int data_len; /* Total size of data in a buffer */
2631 int status_code; /* HTTP reply status code, e.g. 200 */
2632 int throttle; /* Throttling, bytes/sec. <= 0 means no
2633 * throttle */
2634
2635 time_t last_throttle_time; /* Last time throttled data was sent */
2636 int last_throttle_bytes; /* Bytes sent this second */
2637 pthread_mutex_t mutex; /* Used by mg_(un)lock_connection to ensure
2638 * atomic transmissions for websockets */
2639#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2640 void *lua_websocket_state; /* Lua_State for a websocket connection */
2641#endif
2642
2643 void *tls_user_ptr; /* User defined pointer in thread local storage,
2644 * for quick access */
2645};
2646
2647
2648/* Directory entry */
2649struct de {
2652};
2653
2654
2655#define mg_cry_internal(conn, fmt, ...) \
2656 mg_cry_internal_wrap(conn, NULL, __func__, __LINE__, fmt, __VA_ARGS__)
2657
2658#define mg_cry_ctx_internal(ctx, fmt, ...) \
2659 mg_cry_internal_wrap(NULL, ctx, __func__, __LINE__, fmt, __VA_ARGS__)
2660
2661static void mg_cry_internal_wrap(const struct mg_connection *conn,
2662 struct mg_context *ctx,
2663 const char *func,
2664 unsigned line,
2665 const char *fmt,
2666 ...) PRINTF_ARGS(5, 6);
2667
2668
2669#if !defined(NO_THREAD_NAME)
2670#if defined(_WIN32) && defined(_MSC_VER)
2671/* Set the thread name for debugging purposes in Visual Studio
2672 * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
2673 */
2674#pragma pack(push, 8)
2675typedef struct tagTHREADNAME_INFO {
2676 DWORD dwType; /* Must be 0x1000. */
2677 LPCSTR szName; /* Pointer to name (in user addr space). */
2678 DWORD dwThreadID; /* Thread ID (-1=caller thread). */
2679 DWORD dwFlags; /* Reserved for future use, must be zero. */
2681#pragma pack(pop)
2682
2683#elif defined(__linux__)
2684
2685#include <sys/prctl.h>
2686#include <sys/sendfile.h>
2687#if defined(ALTERNATIVE_QUEUE)
2688#include <sys/eventfd.h>
2689#endif /* ALTERNATIVE_QUEUE */
2690
2691
2692#if defined(ALTERNATIVE_QUEUE)
2693
2694static void *
2695event_create(void)
2696{
2697 int evhdl = eventfd(0, EFD_CLOEXEC);
2698 int *ret;
2699
2700 if (evhdl == -1) {
2701 /* Linux uses -1 on error, Windows NULL. */
2702 /* However, Linux does not return 0 on success either. */
2703 return 0;
2704 }
2705
2706 ret = (int *)mg_malloc(sizeof(int));
2707 if (ret) {
2708 *ret = evhdl;
2709 } else {
2710 (void)close(evhdl);
2711 }
2712
2713 return (void *)ret;
2714}
2715
2716
2717static int
2718event_wait(void *eventhdl)
2719{
2720 uint64_t u;
2721 int evhdl, s;
2722
2723 if (!eventhdl) {
2724 /* error */
2725 return 0;
2726 }
2727 evhdl = *(int *)eventhdl;
2728
2729 s = (int)read(evhdl, &u, sizeof(u));
2730 if (s != sizeof(u)) {
2731 /* error */
2732 return 0;
2733 }
2734 (void)u; /* the value is not required */
2735 return 1;
2736}
2737
2738
2739static int
2741{
2742 uint64_t u = 1;
2743 int evhdl, s;
2744
2745 if (!eventhdl) {
2746 /* error */
2747 return 0;
2748 }
2749 evhdl = *(int *)eventhdl;
2750
2751 s = (int)write(evhdl, &u, sizeof(u));
2752 if (s != sizeof(u)) {
2753 /* error */
2754 return 0;
2755 }
2756 return 1;
2757}
2758
2759
2760static void
2762{
2763 int evhdl;
2764
2765 if (!eventhdl) {
2766 /* error */
2767 return;
2768 }
2769 evhdl = *(int *)eventhdl;
2770
2771 close(evhdl);
2773}
2774
2775
2776#endif
2777
2778#endif
2779
2780
2781#if !defined(__linux__) && !defined(_WIN32) && defined(ALTERNATIVE_QUEUE)
2782
2783struct posix_event {
2784 pthread_mutex_t mutex;
2786 int signaled;
2787};
2788
2789
2790static void *
2791event_create(void)
2792{
2793 struct posix_event *ret = mg_malloc(sizeof(struct posix_event));
2794 if (ret == 0) {
2795 /* out of memory */
2796 return 0;
2797 }
2798 if (0 != pthread_mutex_init(&(ret->mutex), NULL)) {
2799 /* pthread mutex not available */
2800 mg_free(ret);
2801 return 0;
2802 }
2803 if (0 != pthread_cond_init(&(ret->cond), NULL)) {
2804 /* pthread cond not available */
2805 pthread_mutex_destroy(&(ret->mutex));
2806 mg_free(ret);
2807 return 0;
2808 }
2809 ret->signaled = 0;
2810 return (void *)ret;
2811}
2812
2813
2814static int
2815event_wait(void *eventhdl)
2816{
2817 struct posix_event *ev = (struct posix_event *)eventhdl;
2818 pthread_mutex_lock(&(ev->mutex));
2819 while (!ev->signaled) {
2820 pthread_cond_wait(&(ev->cond), &(ev->mutex));
2821 }
2822 ev->signaled = 0;
2823 pthread_mutex_unlock(&(ev->mutex));
2824 return 1;
2825}
2826
2827
2828static int
2830{
2831 struct posix_event *ev = (struct posix_event *)eventhdl;
2832 pthread_mutex_lock(&(ev->mutex));
2833 pthread_cond_signal(&(ev->cond));
2834 ev->signaled = 1;
2835 pthread_mutex_unlock(&(ev->mutex));
2836 return 1;
2837}
2838
2839
2840static void
2842{
2843 struct posix_event *ev = (struct posix_event *)eventhdl;
2844 pthread_cond_destroy(&(ev->cond));
2845 pthread_mutex_destroy(&(ev->mutex));
2846 mg_free(ev);
2847}
2848#endif
2849
2850
2851static void
2853{
2854 char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */
2855
2857 NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name);
2858
2859#if defined(_WIN32)
2860#if defined(_MSC_VER)
2861 /* Windows and Visual Studio Compiler */
2862 __try {
2864 info.dwType = 0x1000;
2865 info.szName = threadName;
2866 info.dwThreadID = ~0U;
2867 info.dwFlags = 0;
2868
2869 RaiseException(0x406D1388,
2870 0,
2871 sizeof(info) / sizeof(ULONG_PTR),
2872 (ULONG_PTR *)&info);
2874 }
2875#elif defined(__MINGW32__)
2876 /* No option known to set thread name for MinGW known */
2877#endif
2878#elif defined(_GNU_SOURCE) && defined(__GLIBC__) \
2879 && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12)))
2880 /* pthread_setname_np first appeared in glibc in version 2.12 */
2881#if defined(__MACH__) && defined(__APPLE__)
2882 /* OS X only current thread name can be changed */
2884#else
2886#endif
2887#elif defined(__linux__)
2888 /* On Linux we can use the prctl function.
2889 * When building for Linux Standard Base (LSB) use
2890 * NO_THREAD_NAME. However, thread names are a big
2891 * help for debugging, so the stadard is to set them.
2892 */
2893 (void)prctl(PR_SET_NAME, threadName, 0, 0, 0);
2894#endif
2895}
2896#else /* !defined(NO_THREAD_NAME) */
2897static void
2898mg_set_thread_name(const char *threadName)
2899{
2900}
2901#endif
2902
2903
2904CIVETWEB_API const struct mg_option *
2906{
2907 return config_options;
2908}
2909
2910
2911/* Do not open file (unused) */
2912#define MG_FOPEN_MODE_NONE (0)
2913
2914/* Open file for read only access */
2915#define MG_FOPEN_MODE_READ (1)
2916
2917/* Open file for writing, create and overwrite */
2918#define MG_FOPEN_MODE_WRITE (2)
2919
2920/* Open file for writing, create and append */
2921#define MG_FOPEN_MODE_APPEND (4)
2922
2923
2924static int
2926{
2927 if (!fileacc) {
2928 return 0;
2929 }
2930
2931 return (fileacc->fp != NULL);
2932}
2933
2934
2935#if !defined(NO_FILESYSTEMS)
2936static int mg_stat(const struct mg_connection *conn,
2937 const char *path,
2938 struct mg_file_stat *filep);
2939
2940
2941/* Reject files with special characters (for Windows) */
2942static int
2943mg_path_suspicious(const struct mg_connection *conn, const char *path)
2944{
2945 const uint8_t *c = (const uint8_t *)path;
2946 (void)conn; /* not used */
2947
2948 if ((c == NULL) || (c[0] == 0)) {
2949 /* Null pointer or empty path --> suspicious */
2950 return 1;
2951 }
2952
2953#if defined(_WIN32)
2954 while (*c) {
2955 if (*c < 32) {
2956 /* Control character */
2957 return 1;
2958 }
2959 if ((*c == '>') || (*c == '<') || (*c == '|')) {
2960 /* stdin/stdout redirection character */
2961 return 1;
2962 }
2963 if ((*c == '*') || (*c == '?')) {
2964 /* Wildcard character */
2965 return 1;
2966 }
2967 if (*c == '"') {
2968 /* Windows quotation */
2969 return 1;
2970 }
2971 c++;
2972 }
2973#endif
2974
2975 /* Nothing suspicious found */
2976 return 0;
2977}
2978
2979
2980/* mg_fopen will open a file either in memory or on the disk.
2981 * The input parameter path is a string in UTF-8 encoding.
2982 * The input parameter mode is MG_FOPEN_MODE_*
2983 * On success, fp will be set in the output struct mg_file.
2984 * All status members will also be set.
2985 * The function returns 1 on success, 0 on error. */
2986static int
2987mg_fopen(const struct mg_connection *conn,
2988 const char *path,
2989 int mode,
2990 struct mg_file *filep)
2991{
2992 int found;
2993
2994 if (!filep) {
2995 return 0;
2996 }
2997 filep->access.fp = NULL;
2998
2999 if (mg_path_suspicious(conn, path)) {
3000 return 0;
3001 }
3002
3003 /* filep is initialized in mg_stat: all fields with memset to,
3004 * some fields like size and modification date with values */
3005 found = mg_stat(conn, path, &(filep->stat));
3006
3007 if ((mode == MG_FOPEN_MODE_READ) && (!found)) {
3008 /* file does not exist and will not be created */
3009 return 0;
3010 }
3011
3012#if defined(_WIN32)
3013 {
3014 wchar_t wbuf[UTF16_PATH_MAX];
3015 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
3016 switch (mode) {
3017 case MG_FOPEN_MODE_READ:
3018 filep->access.fp = _wfopen(wbuf, L"rb");
3019 break;
3021 filep->access.fp = _wfopen(wbuf, L"wb");
3022 break;
3024 filep->access.fp = _wfopen(wbuf, L"ab");
3025 break;
3026 }
3027 }
3028#else
3029 /* Linux et al already use unicode. No need to convert. */
3030 switch (mode) {
3031 case MG_FOPEN_MODE_READ:
3032 filep->access.fp = fopen(path, "r");
3033 break;
3035 filep->access.fp = fopen(path, "w");
3036 break;
3038 filep->access.fp = fopen(path, "a");
3039 break;
3040 }
3041
3042#endif
3043 if (!found) {
3044 /* File did not exist before fopen was called.
3045 * Maybe it has been created now. Get stat info
3046 * like creation time now. */
3047 found = mg_stat(conn, path, &(filep->stat));
3048 (void)found;
3049 }
3050
3051 /* return OK if file is opened */
3052 return (filep->access.fp != NULL);
3053}
3054
3055
3056/* return 0 on success, just like fclose */
3057static int
3059{
3060 int ret = -1;
3061 if (fileacc != NULL) {
3062 if (fileacc->fp != NULL) {
3063 ret = fclose(fileacc->fp);
3064 }
3065 /* reset all members of fileacc */
3066 memset(fileacc, 0, sizeof(*fileacc));
3067 }
3068 return ret;
3069}
3070#endif /* NO_FILESYSTEMS */
3071
3072
3073static void
3074mg_strlcpy(char *dst, const char *src, size_t n)
3075{
3076 for (; *src != '\0' && n > 1; n--) {
3077 *dst++ = *src++;
3078 }
3079 *dst = '\0';
3080}
3081
3082
3083static int
3084lowercase(const char *s)
3085{
3086 return tolower((unsigned char)*s);
3087}
3088
3089
3090CIVETWEB_API int
3091mg_strncasecmp(const char *s1, const char *s2, size_t len)
3092{
3093 int diff = 0;
3094
3095 if (len > 0) {
3096 do {
3097 diff = lowercase(s1++) - lowercase(s2++);
3098 } while (diff == 0 && s1[-1] != '\0' && --len > 0);
3099 }
3100
3101 return diff;
3102}
3103
3104
3105CIVETWEB_API int
3106mg_strcasecmp(const char *s1, const char *s2)
3107{
3108 int diff;
3109
3110 do {
3111 diff = lowercase(s1++) - lowercase(s2++);
3112 } while (diff == 0 && s1[-1] != '\0');
3113
3114 return diff;
3115}
3116
3117
3118static char *
3119mg_strndup_ctx(const char *ptr, size_t len, struct mg_context *ctx)
3120{
3121 char *p;
3122 (void)ctx; /* Avoid Visual Studio warning if USE_SERVER_STATS is not
3123 * defined */
3124
3125 if ((p = (char *)mg_malloc_ctx(len + 1, ctx)) != NULL) {
3126 mg_strlcpy(p, ptr, len + 1);
3127 }
3128
3129 return p;
3130}
3131
3132
3133static char *
3134mg_strdup_ctx(const char *str, struct mg_context *ctx)
3135{
3136 return mg_strndup_ctx(str, strlen(str), ctx);
3137}
3138
3139static char *
3140mg_strdup(const char *str)
3141{
3142 return mg_strndup_ctx(str, strlen(str), NULL);
3143}
3144
3145
3146static const char *
3147mg_strcasestr(const char *big_str, const char *small_str)
3148{
3150
3151 if (big_len >= small_len) {
3152 for (i = 0; i <= (big_len - small_len); i++) {
3153 if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) {
3154 return big_str + i;
3155 }
3156 }
3157 }
3158
3159 return NULL;
3160}
3161
3162
3163/* Return null terminated string of given maximum length.
3164 * Report errors if length is exceeded. */
3165static void
3166mg_vsnprintf(const struct mg_connection *conn,
3167 int *truncated,
3168 char *buf,
3169 size_t buflen,
3170 const char *fmt,
3171 va_list ap)
3172{
3173 int n, ok;
3174
3175 if (buflen == 0) {
3176 if (truncated) {
3177 *truncated = 1;
3178 }
3179 return;
3180 }
3181
3182#if defined(__clang__)
3183#pragma clang diagnostic push
3184#pragma clang diagnostic ignored "-Wformat-nonliteral"
3185 /* Using fmt as a non-literal is intended here, since it is mostly called
3186 * indirectly by mg_snprintf */
3187#endif
3188
3189 n = (int)vsnprintf_impl(buf, buflen, fmt, ap);
3190 ok = (n >= 0) && ((size_t)n < buflen);
3191
3192#if defined(__clang__)
3193#pragma clang diagnostic pop
3194#endif
3195
3196 if (ok) {
3197 if (truncated) {
3198 *truncated = 0;
3199 }
3200 } else {
3201 if (truncated) {
3202 *truncated = 1;
3203 }
3204 mg_cry_internal(conn,
3205 "truncating vsnprintf buffer: [%.*s]",
3206 (int)((buflen > 200) ? 200 : (buflen - 1)),
3207 buf);
3208 n = (int)buflen - 1;
3209 }
3210 buf[n] = '\0';
3211}
3212
3213
3214static void
3215mg_snprintf(const struct mg_connection *conn,
3216 int *truncated,
3217 char *buf,
3218 size_t buflen,
3219 const char *fmt,
3220 ...)
3221{
3222 va_list ap;
3223
3224 va_start(ap, fmt);
3225 mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap);
3226 va_end(ap);
3227}
3228
3229
3230static int
3232{
3233 int i;
3234
3235 for (i = 0; config_options[i].name != NULL; i++) {
3236 if (strcmp(config_options[i].name, name) == 0) {
3237 return i;
3238 }
3239 }
3240 return -1;
3241}
3242
3243
3244CIVETWEB_API const char *
3245mg_get_option(const struct mg_context *ctx, const char *name)
3246{
3247 int i;
3248 if ((i = get_option_index(name)) == -1) {
3249 return NULL;
3250 } else if (!ctx || ctx->dd.config[i] == NULL) {
3251 return "";
3252 } else {
3253 return ctx->dd.config[i];
3254 }
3255}
3256
3257#define mg_get_option DO_NOT_USE_THIS_FUNCTION_INTERNALLY__access_directly
3258
3259CIVETWEB_API struct mg_context *
3261{
3262 return (conn == NULL) ? (struct mg_context *)NULL : (conn->phys_ctx);
3263}
3264
3265
3266CIVETWEB_API void *
3268{
3269 return (ctx == NULL) ? NULL : ctx->user_data;
3270}
3271
3272
3273CIVETWEB_API void *
3275{
3276 return mg_get_user_data(mg_get_context(conn));
3277}
3278
3279
3280CIVETWEB_API void *
3282{
3283 /* both methods should return the same pointer */
3284 if (conn) {
3285 /* quick access, in case conn is known */
3286 return conn->tls_user_ptr;
3287 } else {
3288 /* otherwise get pointer from thread local storage (TLS) */
3289 struct mg_workerTLS *tls =
3291 return tls->user_ptr;
3292 }
3293}
3294
3295
3296CIVETWEB_API void
3298{
3299 if (const_conn != NULL) {
3300 /* Const cast, since "const struct mg_connection *" does not mean
3301 * the connection object is not modified. Here "const" is used,
3302 * to indicate mg_read/mg_write/mg_send/.. must not be called. */
3303 struct mg_connection *conn = (struct mg_connection *)const_conn;
3304 conn->request_info.conn_data = data;
3305 }
3306}
3307
3308
3309CIVETWEB_API void *
3311{
3312 if (conn != NULL) {
3313 return conn->request_info.conn_data;
3314 }
3315 return NULL;
3316}
3317
3318
3319CIVETWEB_API int
3321 int size,
3322 struct mg_server_port *ports)
3323{
3324 int i, cnt = 0;
3325
3326 if (size <= 0) {
3327 return -1;
3328 }
3329 memset(ports, 0, sizeof(*ports) * (size_t)size);
3330 if (!ctx) {
3331 return -1;
3332 }
3333 if (!ctx->listening_sockets) {
3334 return -1;
3335 }
3336
3337 for (i = 0; (i < size) && (i < (int)ctx->num_listening_sockets); i++) {
3338
3339 ports[cnt].port =
3341 ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
3342 ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
3343 ports[cnt].is_optional = ctx->listening_sockets[i].is_optional;
3344 ports[cnt].is_bound = ctx->listening_sockets[i].sock != INVALID_SOCKET;
3345
3346 if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
3347 /* IPv4 */
3348 ports[cnt].protocol = 1;
3349 cnt++;
3350 } else if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6) {
3351 /* IPv6 */
3352 ports[cnt].protocol = 3;
3353 cnt++;
3354 }
3355 }
3356
3357 return cnt;
3358}
3359
3360
3361#if defined(USE_X_DOM_SOCKET) && !defined(UNIX_DOMAIN_SOCKET_SERVER_NAME)
3362#define UNIX_DOMAIN_SOCKET_SERVER_NAME "*"
3363#endif
3364
3365static void
3366sockaddr_to_string(char *buf, size_t len, const union usa *usa)
3367{
3368 buf[0] = '\0';
3369
3370 if (!usa) {
3371 return;
3372 }
3373
3374 if (usa->sa.sa_family == AF_INET) {
3375 getnameinfo(&usa->sa,
3376 sizeof(usa->sin),
3377 buf,
3378 (unsigned)len,
3379 NULL,
3380 0,
3382 }
3383#if defined(USE_IPV6)
3384 else if (usa->sa.sa_family == AF_INET6) {
3385 getnameinfo(&usa->sa,
3386 sizeof(usa->sin6),
3387 buf,
3388 (unsigned)len,
3389 NULL,
3390 0,
3392 }
3393#endif
3394#if defined(USE_X_DOM_SOCKET)
3395 else if (usa->sa.sa_family == AF_UNIX) {
3396 /* TODO: Define a remote address for unix domain sockets.
3397 * This code will always return "localhost", identical to http+tcp:
3398 getnameinfo(&usa->sa,
3399 sizeof(usa->sun),
3400 buf,
3401 (unsigned)len,
3402 NULL,
3403 0,
3404 NI_NUMERICHOST);
3405 */
3407 }
3408#endif
3409}
3410
3411
3412/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be
3413 * included in all responses other than 100, 101, 5xx. */
3414static void
3415gmt_time_string(char *buf, size_t buf_len, time_t *t)
3416{
3417#if !defined(REENTRANT_TIME)
3418 struct tm *tm;
3419
3420 tm = ((t != NULL) ? gmtime(t) : NULL);
3421 if (tm != NULL) {
3422#else
3423 struct tm _tm;
3424 struct tm *tm = &_tm;
3425
3426 if (t != NULL) {
3427 gmtime_r(t, tm);
3428#endif
3429 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm);
3430 } else {
3431 mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
3432 }
3433}
3434
3435
3436/* difftime for struct timespec. Return value is in seconds. */
3437static double
3438mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before)
3439{
3440 return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9
3441 + (double)(ts_now->tv_sec - ts_before->tv_sec);
3442}
3443
3444
3445#if defined(MG_EXTERNAL_FUNCTION_mg_cry_internal_impl)
3446static void mg_cry_internal_impl(const struct mg_connection *conn,
3447 const char *func,
3448 unsigned line,
3449 const char *fmt,
3450 va_list ap);
3451#include "external_mg_cry_internal_impl.inl"
3452#elif !defined(NO_FILESYSTEMS)
3453
3454/* Print error message to the opened error log stream. */
3455static void
3457 const char *func,
3458 unsigned line,
3459 const char *fmt,
3460 va_list ap)
3461{
3463 struct mg_file fi;
3464 time_t timestamp;
3465
3466 /* Unused, in the RELEASE build */
3467 (void)func;
3468 (void)line;
3469
3470#if defined(GCC_DIAGNOSTIC)
3471#pragma GCC diagnostic push
3472#pragma GCC diagnostic ignored "-Wformat-nonliteral"
3473#endif
3474
3475 IGNORE_UNUSED_RESULT(vsnprintf_impl(buf, sizeof(buf), fmt, ap));
3476
3477#if defined(GCC_DIAGNOSTIC)
3478#pragma GCC diagnostic pop
3479#endif
3480
3481 buf[sizeof(buf) - 1] = 0;
3482
3483 DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf);
3484
3485 if (!conn) {
3486 fputs(buf, stderr);
3487 return;
3488 }
3489
3490 /* Do not lock when getting the callback value, here and below.
3491 * I suppose this is fine, since function cannot disappear in the
3492 * same way string option can. */
3493 if ((conn->phys_ctx->callbacks.log_message == NULL)
3494 || (conn->phys_ctx->callbacks.log_message(conn, buf) == 0)) {
3495
3496 if (conn->dom_ctx->config[ERROR_LOG_FILE] != NULL) {
3497 if (mg_fopen(conn,
3498 conn->dom_ctx->config[ERROR_LOG_FILE],
3500 &fi)
3501 == 0) {
3502 fi.access.fp = NULL;
3503 }
3504 } else {
3505 fi.access.fp = NULL;
3506 }
3507
3508 if (fi.access.fp != NULL) {
3509 flockfile(fi.access.fp);
3510 timestamp = time(NULL);
3511
3512 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
3513 fprintf(fi.access.fp,
3514 "[%010lu] [error] [client %s] ",
3515 (unsigned long)timestamp,
3516 src_addr);
3517
3518 if (conn->request_info.request_method != NULL) {
3519 fprintf(fi.access.fp,
3520 "%s %s: ",
3521 conn->request_info.request_method,
3522 conn->request_info.request_uri
3523 ? conn->request_info.request_uri
3524 : "");
3525 }
3526
3527 fprintf(fi.access.fp, "%s", buf);
3528 fputc('\n', fi.access.fp);
3529 fflush(fi.access.fp);
3530 funlockfile(fi.access.fp);
3531 (void)mg_fclose(&fi.access); /* Ignore errors. We can't call
3532 * mg_cry here anyway ;-) */
3533 }
3534 }
3535}
3536#else
3537#error Must either enable filesystems or provide a custom mg_cry_internal_impl implementation
3538#endif /* Externally provided function */
3539
3540
3541/* Construct fake connection structure. Used for logging, if connection
3542 * is not applicable at the moment of logging. */
3543static struct mg_connection *
3545{
3546 static const struct mg_connection conn_zero = {0};
3547 *fc = conn_zero;
3548 fc->phys_ctx = ctx;
3549 fc->dom_ctx = &(ctx->dd);
3550 return fc;
3551}
3552
3553
3554static void
3556 struct mg_context *ctx,
3557 const char *func,
3558 unsigned line,
3559 const char *fmt,
3560 ...)
3561{
3562 va_list ap;
3563 va_start(ap, fmt);
3564 if (!conn && ctx) {
3565 struct mg_connection fc;
3567 } else {
3568 mg_cry_internal_impl(conn, func, line, fmt, ap);
3569 }
3570 va_end(ap);
3571}
3572
3573
3574CIVETWEB_API void
3575mg_cry(const struct mg_connection *conn, const char *fmt, ...)
3576{
3577 va_list ap;
3578 va_start(ap, fmt);
3579 mg_cry_internal_impl(conn, "user", 0, fmt, ap);
3580 va_end(ap);
3581}
3582
3583
3584#define mg_cry DO_NOT_USE_THIS_FUNCTION__USE_mg_cry_internal
3585
3586
3587CIVETWEB_API const char *
3589{
3590 return CIVETWEB_VERSION;
3591}
3592
3593
3594CIVETWEB_API const struct mg_request_info *
3596{
3597 if (!conn) {
3598 return NULL;
3599 }
3600#if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE)
3602 char txt[16];
3603 struct mg_workerTLS *tls =
3605
3606 sprintf(txt, "%03i", conn->response_info.status_code);
3607 if (strlen(txt) == 3) {
3608 memcpy(tls->txtbuf, txt, 4);
3609 } else {
3610 strcpy(tls->txtbuf, "ERR");
3611 }
3612
3613 ((struct mg_connection *)conn)->request_info.local_uri =
3614 tls->txtbuf; /* use thread safe buffer */
3615 ((struct mg_connection *)conn)->request_info.local_uri_raw =
3616 tls->txtbuf; /* use the same thread safe buffer */
3617 ((struct mg_connection *)conn)->request_info.request_uri =
3618 tls->txtbuf; /* use the same thread safe buffer */
3619
3620 ((struct mg_connection *)conn)->request_info.num_headers =
3621 conn->response_info.num_headers;
3622 memcpy(((struct mg_connection *)conn)->request_info.http_headers,
3623 conn->response_info.http_headers,
3624 sizeof(conn->response_info.http_headers));
3625 } else
3626#endif
3628 return NULL;
3629 }
3630 return &conn->request_info;
3631}
3632
3633
3634CIVETWEB_API const struct mg_response_info *
3636{
3637 if (!conn) {
3638 return NULL;
3639 }
3641 return NULL;
3642 }
3643 return &conn->response_info;
3644}
3645
3646
3647static const char *
3649{
3650#if defined(__clang__)
3651#pragma clang diagnostic push
3652#pragma clang diagnostic ignored "-Wunreachable-code"
3653 /* Depending on USE_WEBSOCKET and NO_SSL, some oft the protocols might be
3654 * not supported. Clang raises an "unreachable code" warning for parts of ?:
3655 * unreachable, but splitting into four different #ifdef clauses here is
3656 * more complicated.
3657 */
3658#endif
3659
3660 const struct mg_request_info *ri = &conn->request_info;
3661
3662 const char *proto = ((conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)
3663 ? (ri->is_ssl ? "wss" : "ws")
3664 : (ri->is_ssl ? "https" : "http"));
3665
3666 return proto;
3667
3668#if defined(__clang__)
3669#pragma clang diagnostic pop
3670#endif
3671}
3672
3673
3674static int
3676 char *buf,
3677 size_t buflen,
3678 const char *define_proto,
3679 int define_port,
3680 const char *define_uri)
3681{
3682 if ((buflen < 1) || (buf == 0) || (conn == 0)) {
3683 return -1;
3684 } else {
3685 int i, j;
3686 int truncated = 0;
3687 const struct mg_request_info *ri = &conn->request_info;
3688
3689 const char *proto =
3691 const char *uri =
3692 (define_uri != NULL)
3693 ? define_uri
3694 : ((ri->request_uri != NULL) ? ri->request_uri : ri->local_uri);
3695 int port = (define_port > 0) ? define_port : ri->server_port;
3696 int default_port = 80;
3697 char *uri_encoded;
3698 size_t uri_encoded_len;
3699
3700 if (uri == NULL) {
3701 return -1;
3702 }
3703
3704 uri_encoded_len = strlen(uri) * 3 + 1;
3706 if (uri_encoded == NULL) {
3707 return -1;
3708 }
3710
3711 /* Directory separator should be preserved. */
3712 for (i = j = 0; uri_encoded[i]; j++) {
3713 if (!strncmp(uri_encoded + i, "%2f", 3)) {
3714 uri_encoded[j] = '/';
3715 i += 3;
3716 } else {
3717 uri_encoded[j] = uri_encoded[i++];
3718 }
3719 }
3720 uri_encoded[j] = '\0';
3721
3722#if defined(USE_X_DOM_SOCKET)
3723 if (conn->client.lsa.sa.sa_family == AF_UNIX) {
3724 /* TODO: Define and document a link for UNIX domain sockets. */
3725 /* There seems to be no official standard for this.
3726 * Common uses seem to be "httpunix://", "http.unix://" or
3727 * "http+unix://" as a protocol definition string, followed by
3728 * "localhost" or "127.0.0.1" or "/tmp/unix/path" or
3729 * "%2Ftmp%2Funix%2Fpath" (url % encoded) or
3730 * "localhost:%2Ftmp%2Funix%2Fpath" (domain socket path as port) or
3731 * "" (completely skipping the server name part). In any case, the
3732 * last part is the server local path. */
3734 mg_snprintf(conn,
3735 &truncated,
3736 buf,
3737 buflen,
3738 "%s.unix://%s%s",
3739 proto,
3741 ri->local_uri);
3742 default_port = 0;
3744 return 0;
3745 }
3746#endif
3747
3748 if (define_proto) {
3749 /* If we got a protocol name, use the default port accordingly. */
3750 if ((0 == strcmp(define_proto, "https"))
3751 || (0 == strcmp(define_proto, "wss"))) {
3752 default_port = 443;
3753 }
3754 } else if (ri->is_ssl) {
3755 /* If we did not get a protocol name, use TLS as default if it is
3756 * already used. */
3757 default_port = 443;
3758 }
3759
3760 {
3761#if defined(USE_IPV6)
3762 int is_ipv6 = (conn->client.lsa.sa.sa_family == AF_INET6);
3763#endif
3765 conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK]
3766 && (!mg_strcasecmp(
3767 conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK], "yes"));
3768
3769 const char *server_domain =
3770 conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
3771
3772 char portstr[16];
3773 char server_ip[48];
3774
3775 if (port != default_port) {
3776 sprintf(portstr, ":%u", (unsigned)port);
3777 } else {
3778 portstr[0] = 0;
3779 }
3780
3782
3784 sizeof(server_ip),
3785 &conn->client.lsa);
3786
3788 }
3789
3790 mg_snprintf(conn,
3791 &truncated,
3792 buf,
3793 buflen,
3794#if defined(USE_IPV6)
3795 "%s://%s%s%s%s%s",
3796 proto,
3797 (is_ipv6 && (server_domain == server_ip)) ? "[" : "",
3799 (is_ipv6 && (server_domain == server_ip)) ? "]" : "",
3800#else
3801 "%s://%s%s%s",
3802 proto,
3804#endif
3805 portstr,
3806 uri_encoded);
3807
3809 if (truncated) {
3810 return -1;
3811 }
3812 return 0;
3813 }
3814 }
3815}
3816
3817
3818CIVETWEB_API int
3819mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen)
3820{
3821 return mg_construct_local_link(conn, buf, buflen, NULL, -1, NULL);
3822}
3823
3824
3825/* Skip the characters until one of the delimiters characters found.
3826 * 0-terminate resulting word. Skip the delimiter and following whitespaces.
3827 * Advance pointer to buffer to the next word. Return found 0-terminated
3828 * word.
3829 * Delimiters can be quoted with quotechar. */
3830static char *
3831skip_quoted(char **buf,
3832 const char *delimiters,
3833 const char *whitespace,
3834 char quotechar)
3835{
3836 char *p, *begin_word, *end_word, *end_whitespace;
3837
3838 begin_word = *buf;
3840
3841 /* Check for quotechar */
3842 if (end_word > begin_word) {
3843 p = end_word - 1;
3844 while (*p == quotechar) {
3845 /* While the delimiter is quoted, look for the next delimiter. */
3846 /* This happens, e.g., in calls from parse_auth_header,
3847 * if the user name contains a " character. */
3848
3849 /* If there is anything beyond end_word, copy it. */
3850 if (*end_word != '\0') {
3851 size_t end_off = strcspn(end_word + 1, delimiters);
3852 memmove(p, end_word, end_off + 1);
3853 p += end_off; /* p must correspond to end_word - 1 */
3854 end_word += end_off + 1;
3855 } else {
3856 *p = '\0';
3857 break;
3858 }
3859 }
3860 for (p++; p < end_word; p++) {
3861 *p = '\0';
3862 }
3863 }
3864
3865 if (*end_word == '\0') {
3866 *buf = end_word;
3867 } else {
3868
3869#if defined(GCC_DIAGNOSTIC)
3870 /* Disable spurious conversion warning for GCC */
3871#pragma GCC diagnostic push
3872#pragma GCC diagnostic ignored "-Wsign-conversion"
3873#endif /* defined(GCC_DIAGNOSTIC) */
3874
3876
3877#if defined(GCC_DIAGNOSTIC)
3878#pragma GCC diagnostic pop
3879#endif /* defined(GCC_DIAGNOSTIC) */
3880
3881 for (p = end_word; p < end_whitespace; p++) {
3882 *p = '\0';
3883 }
3884
3885 *buf = end_whitespace;
3886 }
3887
3888 return begin_word;
3889}
3890
3891
3892/* Return HTTP header value, or NULL if not found. */
3893static const char *
3894get_header(const struct mg_header *hdr, int num_hdr, const char *name)
3895{
3896 int i;
3897 for (i = 0; i < num_hdr; i++) {
3898 if (!mg_strcasecmp(name, hdr[i].name)) {
3899 return hdr[i].value;
3900 }
3901 }
3902
3903 return NULL;
3904}
3905
3906
3907/* Retrieve requested HTTP header multiple values, and return the number of
3908 * found occurrences */
3909static int
3911 const char *name,
3912 const char **output,
3913 int output_max_size)
3914{
3915 int i;
3916 int cnt = 0;
3917 if (ri) {
3918 for (i = 0; i < ri->num_headers && cnt < output_max_size; i++) {
3919 if (!mg_strcasecmp(name, ri->http_headers[i].name)) {
3920 output[cnt++] = ri->http_headers[i].value;
3921 }
3922 }
3923 }
3924 return cnt;
3925}
3926
3927
3928CIVETWEB_API const char *
3929mg_get_header(const struct mg_connection *conn, const char *name)
3930{
3931 if (!conn) {
3932 return NULL;
3933 }
3934
3936 return get_header(conn->request_info.http_headers,
3937 conn->request_info.num_headers,
3938 name);
3939 }
3941 return get_header(conn->response_info.http_headers,
3942 conn->response_info.num_headers,
3943 name);
3944 }
3945 return NULL;
3946}
3947
3948
3949static const char *
3951{
3952 if (!conn) {
3953 return NULL;
3954 }
3955
3957 return conn->request_info.http_version;
3958 }
3960 return conn->response_info.http_version;
3961 }
3962 return NULL;
3963}
3964
3965
3966/* A helper function for traversing a comma separated list of values.
3967 * It returns a list pointer shifted to the next value, or NULL if the end
3968 * of the list found.
3969 * Value is stored in val vector. If value has form "x=y", then eq_val
3970 * vector is initialized to point to the "y" part, and val vector length
3971 * is adjusted to point only to "x". */
3972static const char *
3973next_option(const char *list, struct vec *val, struct vec *eq_val)
3974{
3975 int end;
3976
3977reparse:
3978 if (val == NULL || list == NULL || *list == '\0') {
3979 /* End of the list */
3980 return NULL;
3981 }
3982
3983 /* Skip over leading LWS */
3984 while (*list == ' ' || *list == '\t')
3985 list++;
3986
3987 val->ptr = list;
3988 if ((list = strchr(val->ptr, ',')) != NULL) {
3989 /* Comma found. Store length and shift the list ptr */
3990 val->len = ((size_t)(list - val->ptr));
3991 list++;
3992 } else {
3993 /* This value is the last one */
3994 list = val->ptr + strlen(val->ptr);
3995 val->len = ((size_t)(list - val->ptr));
3996 }
3997
3998 /* Adjust length for trailing LWS */
3999 end = (int)val->len - 1;
4000 while (end >= 0 && ((val->ptr[end] == ' ') || (val->ptr[end] == '\t')))
4001 end--;
4002 val->len = (size_t)(end) + (size_t)(1);
4003
4004 if (val->len == 0) {
4005 /* Ignore any empty entries. */
4006 goto reparse;
4007 }
4008
4009 if (eq_val != NULL) {
4010 /* Value has form "x=y", adjust pointers and lengths
4011 * so that val points to "x", and eq_val points to "y". */
4012 eq_val->len = 0;
4013 eq_val->ptr = (const char *)memchr(val->ptr, '=', val->len);
4014 if (eq_val->ptr != NULL) {
4015 eq_val->ptr++; /* Skip over '=' character */
4016 eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len;
4017 val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1;
4018 }
4019 }
4020
4021 return list;
4022}
4023
4024
4025/* A helper function for checking if a comma separated list of values
4026 * contains
4027 * the given option (case insensitvely).
4028 * 'header' can be NULL, in which case false is returned. */
4029static int
4030header_has_option(const char *header, const char *option)
4031{
4032 struct vec opt_vec;
4033 struct vec eq_vec;
4034
4036 DEBUG_ASSERT(option[0] != '\0');
4037
4038 while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) {
4039 if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0)
4040 return 1;
4041 }
4042
4043 return 0;
4044}
4045
4046
4047/* Sorting function implemented in a separate file */
4048#include "sort.inl"
4049
4050/* Pattern matching has been reimplemented in a new file */
4051#include "match.inl"
4052
4053
4054/* HTTP 1.1 assumes keep alive if "Connection:" header is not set
4055 * This function must tolerate situations when connection info is not
4056 * set up, for example if request parsing failed. */
4057static int
4059{
4060 const char *http_version;
4061 const char *header;
4062
4063 /* First satisfy needs of the server */
4064 if ((conn == NULL) || conn->must_close) {
4065 /* Close, if civetweb framework needs to close */
4066 return 0;
4067 }
4068
4069 if (mg_strcasecmp(conn->dom_ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0) {
4070 /* Close, if keep alive is not enabled */
4071 return 0;
4072 }
4073
4074 /* Check explicit wish of the client */
4075 header = mg_get_header(conn, "Connection");
4076 if (header) {
4077 /* If there is a connection header from the client, obey */
4078 if (header_has_option(header, "keep-alive")) {
4079 return 1;
4080 }
4081 return 0;
4082 }
4083
4084 /* Use default of the standard */
4085 http_version = get_http_version(conn);
4086 if (http_version && (0 == strcmp(http_version, "1.1"))) {
4087 /* HTTP 1.1 default is keep alive */
4088 return 1;
4089 }
4090
4091 /* HTTP 1.0 (and earlier) default is to close the connection */
4092 return 0;
4093}
4094
4095
4096static int
4098{
4099 if (!conn || !conn->dom_ctx) {
4100 return 0;
4101 }
4102
4103 return (mg_strcasecmp(conn->dom_ctx->config[DECODE_URL], "yes") == 0);
4104}
4105
4106
4107static int
4109{
4110 if (!conn || !conn->dom_ctx) {
4111 return 0;
4112 }
4113
4114 return (mg_strcasecmp(conn->dom_ctx->config[DECODE_QUERY_STRING], "yes")
4115 == 0);
4116}
4117
4118
4119static const char *
4121{
4122 return should_keep_alive(conn) ? "keep-alive" : "close";
4123}
4124
4125
4126#include "response.inl"
4127
4128
4129static void
4131{
4132 /* Send all current and obsolete cache opt-out directives. */
4134 "Cache-Control",
4135 "no-cache, no-store, "
4136 "must-revalidate, private, max-age=0",
4137 -1);
4138 mg_response_header_add(conn, "Expires", "0", -1);
4139
4140 if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
4141 /* Obsolete, but still send it for HTTP/1.0 */
4142 mg_response_header_add(conn, "Pragma", "no-cache", -1);
4143 }
4144}
4145
4146
4147static void
4149{
4150#if !defined(NO_CACHING)
4151 int max_age;
4152 char val[64];
4153
4154 const char *cache_control =
4155 conn->dom_ctx->config[STATIC_FILE_CACHE_CONTROL];
4156
4157 /* If there is a full cache-control option configured,0 use it */
4158 if (cache_control != NULL) {
4159 mg_response_header_add(conn, "Cache-Control", cache_control, -1);
4160 return;
4161 }
4162
4163 /* Read the server config to check how long a file may be cached.
4164 * The configuration is in seconds. */
4165 max_age = atoi(conn->dom_ctx->config[STATIC_FILE_MAX_AGE]);
4166 if (max_age <= 0) {
4167 /* 0 means "do not cache". All values <0 are reserved
4168 * and may be used differently in the future. */
4169 /* If a file should not be cached, do not only send
4170 * max-age=0, but also pragmas and Expires headers. */
4172 return;
4173 }
4174
4175 /* Use "Cache-Control: max-age" instead of "Expires" header.
4176 * Reason: see https://www.mnot.net/blog/2007/05/15/expires_max-age */
4177 /* See also https://www.mnot.net/cache_docs/ */
4178 /* According to RFC 2616, Section 14.21, caching times should not exceed
4179 * one year. A year with 365 days corresponds to 31536000 seconds, a
4180 * leap
4181 * year to 31622400 seconds. For the moment, we just send whatever has
4182 * been configured, still the behavior for >1 year should be considered
4183 * as undefined. */
4185 conn, NULL, val, sizeof(val), "max-age=%lu", (unsigned long)max_age);
4186 mg_response_header_add(conn, "Cache-Control", val, -1);
4187
4188#else /* NO_CACHING */
4189
4191#endif /* !NO_CACHING */
4192}
4193
4194
4195static void
4197{
4198 const char *header = conn->dom_ctx->config[ADDITIONAL_HEADER];
4199
4200#if !defined(NO_SSL)
4201 if (conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]) {
4202 long max_age = atol(conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]);
4203 if (max_age >= 0) {
4204 char val[64];
4205 mg_snprintf(conn,
4206 NULL,
4207 val,
4208 sizeof(val),
4209 "max-age=%lu",
4210 (unsigned long)max_age);
4211 mg_response_header_add(conn, "Strict-Transport-Security", val, -1);
4212 }
4213 }
4214#endif
4215
4216 // Content-Security-Policy
4217
4218 if (header && header[0]) {
4219 mg_response_header_add_lines(conn, header);
4220 }
4221}
4222
4223
4224static void
4226{
4227 const char *origin_hdr = mg_get_header(conn, "Origin");
4228 const char *cors_orig_cfg =
4229 conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
4230 const char *cors_cred_cfg =
4232 const char *cors_hdr_cfg =
4234 const char *cors_exphdr_cfg =
4236 const char *cors_meth_cfg =
4238
4240 /* Cross-origin resource sharing (CORS), see
4241 * http://www.html5rocks.com/en/tutorials/cors/,
4242 * http://www.html5rocks.com/static/images/cors_server_flowchart.png
4243 * CORS preflight is not supported for files. */
4245 "Access-Control-Allow-Origin",
4247 -1);
4248 }
4249
4251 /* Cross-origin resource sharing (CORS), see
4252 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
4253 */
4255 "Access-Control-Allow-Credentials",
4257 -1);
4258 }
4259
4260 if (cors_hdr_cfg && *cors_hdr_cfg) {
4262 "Access-Control-Allow-Headers",
4264 -1);
4265 }
4266
4269 "Access-Control-Expose-Headers",
4271 -1);
4272 }
4273
4274 if (cors_meth_cfg && *cors_meth_cfg) {
4276 "Access-Control-Allow-Methods",
4278 -1);
4279 }
4280}
4281
4282
4283#if !defined(NO_FILESYSTEMS)
4284static void handle_file_based_request(struct mg_connection *conn,
4285 const char *path,
4286 struct mg_file *filep);
4287#endif /* NO_FILESYSTEMS */
4288
4289
4290CIVETWEB_API const char *
4292{
4293 /* See IANA HTTP status code assignment:
4294 * http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
4295 */
4296
4297 switch (response_code) {
4298 /* RFC2616 Section 10.1 - Informational 1xx */
4299 case 100:
4300 return "Continue"; /* RFC2616 Section 10.1.1 */
4301 case 101:
4302 return "Switching Protocols"; /* RFC2616 Section 10.1.2 */
4303 case 102:
4304 return "Processing"; /* RFC2518 Section 10.1 */
4305
4306 /* RFC2616 Section 10.2 - Successful 2xx */
4307 case 200:
4308 return "OK"; /* RFC2616 Section 10.2.1 */
4309 case 201:
4310 return "Created"; /* RFC2616 Section 10.2.2 */
4311 case 202:
4312 return "Accepted"; /* RFC2616 Section 10.2.3 */
4313 case 203:
4314 return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */
4315 case 204:
4316 return "No Content"; /* RFC2616 Section 10.2.5 */
4317 case 205:
4318 return "Reset Content"; /* RFC2616 Section 10.2.6 */
4319 case 206:
4320 return "Partial Content"; /* RFC2616 Section 10.2.7 */
4321 case 207:
4322 return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1
4323 */
4324 case 208:
4325 return "Already Reported"; /* RFC5842 Section 7.1 */
4326
4327 case 226:
4328 return "IM used"; /* RFC3229 Section 10.4.1 */
4329
4330 /* RFC2616 Section 10.3 - Redirection 3xx */
4331 case 300:
4332 return "Multiple Choices"; /* RFC2616 Section 10.3.1 */
4333 case 301:
4334 return "Moved Permanently"; /* RFC2616 Section 10.3.2 */
4335 case 302:
4336 return "Found"; /* RFC2616 Section 10.3.3 */
4337 case 303:
4338 return "See Other"; /* RFC2616 Section 10.3.4 */
4339 case 304:
4340 return "Not Modified"; /* RFC2616 Section 10.3.5 */
4341 case 305:
4342 return "Use Proxy"; /* RFC2616 Section 10.3.6 */
4343 case 307:
4344 return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */
4345 case 308:
4346 return "Permanent Redirect"; /* RFC7238 Section 3 */
4347
4348 /* RFC2616 Section 10.4 - Client Error 4xx */
4349 case 400:
4350 return "Bad Request"; /* RFC2616 Section 10.4.1 */
4351 case 401:
4352 return "Unauthorized"; /* RFC2616 Section 10.4.2 */
4353 case 402:
4354 return "Payment Required"; /* RFC2616 Section 10.4.3 */
4355 case 403:
4356 return "Forbidden"; /* RFC2616 Section 10.4.4 */
4357 case 404:
4358 return "Not Found"; /* RFC2616 Section 10.4.5 */
4359 case 405:
4360 return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */
4361 case 406:
4362 return "Not Acceptable"; /* RFC2616 Section 10.4.7 */
4363 case 407:
4364 return "Proxy Authentication Required"; /* RFC2616 Section 10.4.8 */
4365 case 408:
4366 return "Request Time-out"; /* RFC2616 Section 10.4.9 */
4367 case 409:
4368 return "Conflict"; /* RFC2616 Section 10.4.10 */
4369 case 410:
4370 return "Gone"; /* RFC2616 Section 10.4.11 */
4371 case 411:
4372 return "Length Required"; /* RFC2616 Section 10.4.12 */
4373 case 412:
4374 return "Precondition Failed"; /* RFC2616 Section 10.4.13 */
4375 case 413:
4376 return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */
4377 case 414:
4378 return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */
4379 case 415:
4380 return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */
4381 case 416:
4382 return "Requested range not satisfiable"; /* RFC2616 Section 10.4.17
4383 */
4384 case 417:
4385 return "Expectation Failed"; /* RFC2616 Section 10.4.18 */
4386
4387 case 421:
4388 return "Misdirected Request"; /* RFC7540 Section 9.1.2 */
4389 case 422:
4390 return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918
4391 * Section 11.2 */
4392 case 423:
4393 return "Locked"; /* RFC2518 Section 10.4, RFC4918 Section 11.3 */
4394 case 424:
4395 return "Failed Dependency"; /* RFC2518 Section 10.5, RFC4918
4396 * Section 11.4 */
4397
4398 case 426:
4399 return "Upgrade Required"; /* RFC 2817 Section 4 */
4400
4401 case 428:
4402 return "Precondition Required"; /* RFC 6585, Section 3 */
4403 case 429:
4404 return "Too Many Requests"; /* RFC 6585, Section 4 */
4405
4406 case 431:
4407 return "Request Header Fields Too Large"; /* RFC 6585, Section 5 */
4408
4409 case 451:
4410 return "Unavailable For Legal Reasons"; /* draft-tbray-http-legally-restricted-status-05,
4411 * Section 3 */
4412
4413 /* RFC2616 Section 10.5 - Server Error 5xx */
4414 case 500:
4415 return "Internal Server Error"; /* RFC2616 Section 10.5.1 */
4416 case 501:
4417 return "Not Implemented"; /* RFC2616 Section 10.5.2 */
4418 case 502:
4419 return "Bad Gateway"; /* RFC2616 Section 10.5.3 */
4420 case 503:
4421 return "Service Unavailable"; /* RFC2616 Section 10.5.4 */
4422 case 504:
4423 return "Gateway Time-out"; /* RFC2616 Section 10.5.5 */
4424 case 505:
4425 return "HTTP Version not supported"; /* RFC2616 Section 10.5.6 */
4426 case 506:
4427 return "Variant Also Negotiates"; /* RFC 2295, Section 8.1 */
4428 case 507:
4429 return "Insufficient Storage"; /* RFC2518 Section 10.6, RFC4918
4430 * Section 11.5 */
4431 case 508:
4432 return "Loop Detected"; /* RFC5842 Section 7.1 */
4433
4434 case 510:
4435 return "Not Extended"; /* RFC 2774, Section 7 */
4436 case 511:
4437 return "Network Authentication Required"; /* RFC 6585, Section 6 */
4438
4439 /* Other status codes, not shown in the IANA HTTP status code
4440 * assignment.
4441 * E.g., "de facto" standards due to common use, ... */
4442 case 418:
4443 return "I am a teapot"; /* RFC2324 Section 2.3.2 */
4444 case 419:
4445 return "Authentication Timeout"; /* common use */
4446 case 420:
4447 return "Enhance Your Calm"; /* common use */
4448 case 440:
4449 return "Login Timeout"; /* common use */
4450 case 509:
4451 return "Bandwidth Limit Exceeded"; /* common use */
4452
4453 default:
4454 /* This error code is unknown. This should not happen. */
4455 if (conn) {
4456 mg_cry_internal(conn,
4457 "Unknown HTTP response code: %u",
4459 }
4460
4461 /* Return at least a category according to RFC 2616 Section 10. */
4462 if (response_code >= 100 && response_code < 200) {
4463 /* Unknown informational status code */
4464 return "Information";
4465 }
4466 if (response_code >= 200 && response_code < 300) {
4467 /* Unknown success code */
4468 return "Success";
4469 }
4470 if (response_code >= 300 && response_code < 400) {
4471 /* Unknown redirection code */
4472 return "Redirection";
4473 }
4474 if (response_code >= 400 && response_code < 500) {
4475 /* Unknown request error code */
4476 return "Client Error";
4477 }
4478 if (response_code >= 500 && response_code < 600) {
4479 /* Unknown server error code */
4480 return "Server Error";
4481 }
4482
4483 /* Response code not even within reasonable range */
4484 return "";
4485 }
4486}
4487
4488
4489static int
4491 int status,
4492 const char *fmt,
4493 va_list args)
4494{
4495 char errmsg_buf[MG_BUF_LEN];
4496 va_list ap;
4497 int has_body;
4498
4499#if !defined(NO_FILESYSTEMS)
4500 char path_buf[UTF8_PATH_MAX];
4502 const char *error_handler = NULL;
4504 const char *error_page_file_ext, *tstr;
4505#endif /* NO_FILESYSTEMS */
4506 int handled_by_callback = 0;
4507
4508 if ((conn == NULL) || (fmt == NULL)) {
4509 return -2;
4510 }
4511
4512 /* Set status (for log) */
4513 conn->status_code = status;
4514
4515 /* Errors 1xx, 204 and 304 MUST NOT send a body */
4516 has_body = ((status > 199) && (status != 204) && (status != 304));
4517
4518 /* Prepare message in buf, if required */
4519 if (has_body
4520 || (!conn->in_error_handler
4521 && (conn->phys_ctx->callbacks.http_error != NULL))) {
4522 /* Store error message in errmsg_buf */
4523 va_copy(ap, args);
4524 mg_vsnprintf(conn, NULL, errmsg_buf, sizeof(errmsg_buf), fmt, ap);
4525 va_end(ap);
4526 /* In a debug build, print all html errors */
4527 DEBUG_TRACE("Error %i - [%s]", status, errmsg_buf);
4528 }
4529
4530 /* If there is a http_error callback, call it.
4531 * But don't do it recursively, if callback calls mg_send_http_error again.
4532 */
4533 if (!conn->in_error_handler
4534 && (conn->phys_ctx->callbacks.http_error != NULL)) {
4535 /* Mark in_error_handler to avoid recursion and call user callback. */
4536 conn->in_error_handler = 1;
4538 (conn->phys_ctx->callbacks.http_error(conn, status, errmsg_buf)
4539 == 0);
4540 conn->in_error_handler = 0;
4541 }
4542
4543 if (!handled_by_callback) {
4544 /* Check for recursion */
4545 if (conn->in_error_handler) {
4547 "Recursion when handling error %u - fall back to default",
4548 status);
4549#if !defined(NO_FILESYSTEMS)
4550 } else {
4551 /* Send user defined error pages, if defined */
4552 error_handler = conn->dom_ctx->config[ERROR_PAGES];
4553 error_page_file_ext = conn->dom_ctx->config[INDEX_FILES];
4555
4556 if (error_handler != NULL) {
4557 for (scope = 1; (scope <= 3) && !page_handler_found; scope++) {
4558 switch (scope) {
4559 case 1: /* Handler for specific error, e.g. 404 error */
4560 mg_snprintf(conn,
4561 &truncated,
4562 path_buf,
4563 sizeof(path_buf) - 32,
4564 "%serror%03u.",
4566 status);
4567 break;
4568 case 2: /* Handler for error group, e.g., 5xx error
4569 * handler
4570 * for all server errors (500-599) */
4571 mg_snprintf(conn,
4572 &truncated,
4573 path_buf,
4574 sizeof(path_buf) - 32,
4575 "%serror%01uxx.",
4577 status / 100);
4578 break;
4579 default: /* Handler for all errors */
4580 mg_snprintf(conn,
4581 &truncated,
4582 path_buf,
4583 sizeof(path_buf) - 32,
4584 "%serror.",
4586 break;
4587 }
4588
4589 /* String truncation in buf may only occur if
4590 * error_handler is too long. This string is
4591 * from the config, not from a client. */
4592 (void)truncated;
4593
4594 /* The following code is redundant, but it should avoid
4595 * false positives in static source code analyzers and
4596 * vulnerability scanners.
4597 */
4598 path_buf[sizeof(path_buf) - 32] = 0;
4599 len = (int)strlen(path_buf);
4600 if (len > (int)sizeof(path_buf) - 32) {
4601 len = (int)sizeof(path_buf) - 32;
4602 }
4603
4604 /* Start with the file extension from the configuration. */
4605 tstr = strchr(error_page_file_ext, '.');
4606
4607 while (tstr) {
4608 for (i = 1;
4609 (i < 32) && (tstr[i] != 0) && (tstr[i] != ',');
4610 i++) {
4611 /* buffer overrun is not possible here, since
4612 * (i < 32) && (len < sizeof(path_buf) - 32)
4613 * ==> (i + len) < sizeof(path_buf) */
4614 path_buf[len + i - 1] = tstr[i];
4615 }
4616 /* buffer overrun is not possible here, since
4617 * (i <= 32) && (len < sizeof(path_buf) - 32)
4618 * ==> (i + len) <= sizeof(path_buf) */
4619 path_buf[len + i - 1] = 0;
4620
4621 if (mg_stat(conn, path_buf, &error_page_file.stat)) {
4622 DEBUG_TRACE("Check error page %s - found",
4623 path_buf);
4625 break;
4626 }
4627 DEBUG_TRACE("Check error page %s - not found",
4628 path_buf);
4629
4630 /* Continue with the next file extension from the
4631 * configuration (if there is a next one). */
4632 tstr = strchr(tstr + i, '.');
4633 }
4634 }
4635 }
4636
4637 if (page_handler_found) {
4638 conn->in_error_handler = 1;
4640 conn->in_error_handler = 0;
4641 return 0;
4642 }
4643#endif /* NO_FILESYSTEMS */
4644 }
4645
4646 /* No custom error page. Send default error page. */
4647 conn->must_close = 1;
4648 mg_response_header_start(conn, status);
4651 send_cors_header(conn);
4652 if (has_body) {
4654 "Content-Type",
4655 "text/plain; charset=utf-8",
4656 -1);
4657 }
4659
4660 /* HTTP responses 1xx, 204 and 304 MUST NOT send a body */
4661 if (has_body) {
4662 /* For other errors, send a generic error message. */
4663 const char *status_text = mg_get_response_code_text(conn, status);
4664 mg_printf(conn, "Error %d: %s\n", status, status_text);
4666
4667 } else {
4668 /* No body allowed. Close the connection. */
4669 DEBUG_TRACE("Error %i", status);
4670 }
4671 }
4672 return 0;
4673}
4674
4675
4676CIVETWEB_API int
4677mg_send_http_error(struct mg_connection *conn, int status, const char *fmt, ...)
4678{
4679 va_list ap;
4680 int ret;
4681
4682 va_start(ap, fmt);
4683 ret = mg_send_http_error_impl(conn, status, fmt, ap);
4684 va_end(ap);
4685
4686 return ret;
4687}
4688
4689
4690CIVETWEB_API int
4692 const char *mime_type,
4693 long long content_length)
4694{
4695 if ((mime_type == NULL) || (*mime_type == 0)) {
4696 /* No content type defined: default to text/html */
4697 mime_type = "text/html";
4698 }
4699
4700 mg_response_header_start(conn, 200);
4703 send_cors_header(conn);
4704 mg_response_header_add(conn, "Content-Type", mime_type, -1);
4705 if (content_length < 0) {
4706 /* Size not known. Use chunked encoding (HTTP/1.x) */
4707 if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
4708 /* Only HTTP/1.x defines "chunked" encoding, HTTP/2 does not*/
4709 mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1);
4710 }
4711 } else {
4712 char len[32];
4713 int trunc = 0;
4714 mg_snprintf(conn,
4715 &trunc,
4716 len,
4717 sizeof(len),
4718 "%" UINT64_FMT,
4719 (uint64_t)content_length);
4720 if (!trunc) {
4721 /* Since 32 bytes is enough to hold any 64 bit decimal number,
4722 * !trunc is always true */
4723 mg_response_header_add(conn, "Content-Length", len, -1);
4724 }
4725 }
4727
4728 return 0;
4729}
4730
4731
4732CIVETWEB_API int
4734 const char *target_url,
4735 int redirect_code)
4736{
4737 /* Send a 30x redirect response.
4738 *
4739 * Redirect types (status codes):
4740 *
4741 * Status | Perm/Temp | Method | Version
4742 * 301 | permanent | POST->GET undefined | HTTP/1.0
4743 * 302 | temporary | POST->GET undefined | HTTP/1.0
4744 * 303 | temporary | always use GET | HTTP/1.1
4745 * 307 | temporary | always keep method | HTTP/1.1
4746 * 308 | permanent | always keep method | HTTP/1.1
4747 */
4748
4749#if defined(MG_SEND_REDIRECT_BODY)
4751 size_t content_len = 0;
4752 char content_len_text[32];
4753#endif
4754
4755 /* In case redirect_code=0, use 307. */
4756 if (redirect_code == 0) {
4757 redirect_code = 307;
4758 }
4759
4760 /* In case redirect_code is none of the above, return error. */
4761 if ((redirect_code != 301) && (redirect_code != 302)
4762 && (redirect_code != 303) && (redirect_code != 307)
4763 && (redirect_code != 308)) {
4764 /* Parameter error */
4765 return -2;
4766 }
4767
4768 /* If target_url is not defined, redirect to "/". */
4769 if ((target_url == NULL) || (*target_url == 0)) {
4770 target_url = "/";
4771 }
4772
4773#if defined(MG_SEND_REDIRECT_BODY)
4774 /* TODO: condition name? */
4775
4776 /* Prepare a response body with a hyperlink.
4777 *
4778 * According to RFC2616 (and RFC1945 before):
4779 * Unless the request method was HEAD, the entity of the
4780 * response SHOULD contain a short hypertext note with a hyperlink to
4781 * the new URI(s).
4782 *
4783 * However, this response body is not useful in M2M communication.
4784 * Probably the original reason in the RFC was, clients not supporting
4785 * a 30x HTTP redirect could still show the HTML page and let the user
4786 * press the link. Since current browsers support 30x HTTP, the additional
4787 * HTML body does not seem to make sense anymore.
4788 *
4789 * The new RFC7231 (Section 6.4) does no longer recommend it ("SHOULD"),
4790 * but it only notes:
4791 * The server's response payload usually contains a short
4792 * hypertext note with a hyperlink to the new URI(s).
4793 *
4794 * Deactivated by default. If you need the 30x body, set the define.
4795 */
4797 conn,
4798 NULL /* ignore truncation */,
4800 sizeof(redirect_body),
4801 "<html><head>%s</head><body><a href=\"%s\">%s</a></body></html>",
4803 target_url,
4804 target_url);
4805 content_len = strlen(reply);
4806 sprintf(content_len_text, "%lu", (unsigned long)content_len);
4807#endif
4808
4809 /* Send all required headers */
4811 mg_response_header_add(conn, "Location", target_url, -1);
4812 if ((redirect_code == 301) || (redirect_code == 308)) {
4813 /* Permanent redirect */
4815 } else {
4816 /* Temporary redirect */
4818 }
4820 send_cors_header(conn);
4821#if defined(MG_SEND_REDIRECT_BODY)
4822 mg_response_header_add(conn, "Content-Type", "text/html", -1);
4823 mg_response_header_add(conn, "Content-Length", content_len_text, -1);
4824#else
4825 mg_response_header_add(conn, "Content-Length", "0", 1);
4826#endif
4828
4829#if defined(MG_SEND_REDIRECT_BODY)
4830 /* Send response body */
4831 /* ... unless it is a HEAD request */
4832 if (0 != strcmp(conn->request_info.request_method, "HEAD")) {
4833 ret = mg_write(conn, redirect_body, content_len);
4834 }
4835#endif
4836
4837 return 1;
4838}
4839
4840
4841#if defined(_WIN32)
4842/* Create substitutes for POSIX functions in Win32. */
4843
4844#if defined(GCC_DIAGNOSTIC)
4845/* Show no warning in case system functions are not used. */
4846#pragma GCC diagnostic push
4847#pragma GCC diagnostic ignored "-Wunused-function"
4848#endif
4849
4850
4851static int
4852pthread_mutex_init(pthread_mutex_t *mutex, void *unused)
4853{
4854 (void)unused;
4855 /* Always initialize as PTHREAD_MUTEX_RECURSIVE */
4856 InitializeCriticalSection(&mutex->sec);
4857 return 0;
4858}
4859
4860
4861static int
4862pthread_mutex_destroy(pthread_mutex_t *mutex)
4863{
4864 DeleteCriticalSection(&mutex->sec);
4865 return 0;
4866}
4867
4868
4869static int
4870pthread_mutex_lock(pthread_mutex_t *mutex)
4871{
4872 EnterCriticalSection(&mutex->sec);
4873 return 0;
4874}
4875
4876
4877static int
4878pthread_mutex_unlock(pthread_mutex_t *mutex)
4879{
4880 LeaveCriticalSection(&mutex->sec);
4881 return 0;
4882}
4883
4884
4886static int
4887pthread_cond_init(pthread_cond_t *cv, const void *unused)
4888{
4889 (void)unused;
4890 (void)pthread_mutex_init(&cv->threadIdSec, &pthread_mutex_attr);
4891 cv->waiting_thread = NULL;
4892 return 0;
4893}
4894
4895
4897static int
4899 pthread_mutex_t *mutex,
4901{
4902 struct mg_workerTLS **ptls,
4904 int ok;
4905 uint64_t nsnow, nswaitabs;
4906 int64_t nswaitrel;
4907 DWORD mswaitrel;
4908
4909 pthread_mutex_lock(&cv->threadIdSec);
4910 /* Add this thread to cv's waiting list */
4911 ptls = &cv->waiting_thread;
4912 for (; *ptls != NULL; ptls = &(*ptls)->next_waiting_thread)
4913 ;
4914 tls->next_waiting_thread = NULL;
4915 *ptls = tls;
4916 pthread_mutex_unlock(&cv->threadIdSec);
4917
4918 if (abstime) {
4920 nswaitabs =
4921 (((uint64_t)abstime->tv_sec) * 1000000000) + abstime->tv_nsec;
4923 if (nswaitrel < 0) {
4924 nswaitrel = 0;
4925 }
4926 mswaitrel = (DWORD)(nswaitrel / 1000000);
4927 } else {
4928 mswaitrel = (DWORD)INFINITE;
4929 }
4930
4931 pthread_mutex_unlock(mutex);
4932 ok = (WAIT_OBJECT_0
4933 == WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel));
4934 if (!ok) {
4935 ok = 1;
4936 pthread_mutex_lock(&cv->threadIdSec);
4937 ptls = &cv->waiting_thread;
4938 for (; *ptls != NULL; ptls = &(*ptls)->next_waiting_thread) {
4939 if (*ptls == tls) {
4940 *ptls = tls->next_waiting_thread;
4941 ok = 0;
4942 break;
4943 }
4944 }
4945 pthread_mutex_unlock(&cv->threadIdSec);
4946 if (ok) {
4947 WaitForSingleObject(tls->pthread_cond_helper_mutex,
4948 (DWORD)INFINITE);
4949 }
4950 }
4951 /* This thread has been removed from cv's waiting list */
4952 pthread_mutex_lock(mutex);
4953
4954 return ok ? 0 : -1;
4955}
4956
4957
4959static int
4960pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex)
4961{
4962 return pthread_cond_timedwait(cv, mutex, NULL);
4963}
4964
4965
4967static int
4969{
4970 HANDLE wkup = NULL;
4971 BOOL ok = FALSE;
4972
4973 pthread_mutex_lock(&cv->threadIdSec);
4974 if (cv->waiting_thread) {
4975 wkup = cv->waiting_thread->pthread_cond_helper_mutex;
4976 cv->waiting_thread = cv->waiting_thread->next_waiting_thread;
4977
4978 ok = SetEvent(wkup);
4979 DEBUG_ASSERT(ok);
4980 }
4981 pthread_mutex_unlock(&cv->threadIdSec);
4982
4983 return ok ? 0 : 1;
4984}
4985
4986
4988static int
4990{
4991 pthread_mutex_lock(&cv->threadIdSec);
4992 while (cv->waiting_thread) {
4994 }
4995 pthread_mutex_unlock(&cv->threadIdSec);
4996
4997 return 0;
4998}
4999
5000
5002static int
5004{
5005 pthread_mutex_lock(&cv->threadIdSec);
5006 DEBUG_ASSERT(cv->waiting_thread == NULL);
5007 pthread_mutex_unlock(&cv->threadIdSec);
5008 pthread_mutex_destroy(&cv->threadIdSec);
5009
5010 return 0;
5011}
5012
5013
5014#if defined(ALTERNATIVE_QUEUE)
5016static void *
5017event_create(void)
5018{
5019 return (void *)CreateEvent(NULL, FALSE, FALSE, NULL);
5020}
5021
5022
5024static int
5025event_wait(void *eventhdl)
5026{
5027 int res = WaitForSingleObject((HANDLE)eventhdl, (DWORD)INFINITE);
5028 return (res == WAIT_OBJECT_0);
5029}
5030
5031
5033static int
5035{
5036 return (int)SetEvent((HANDLE)eventhdl);
5037}
5038
5039
5041static void
5043{
5044 CloseHandle((HANDLE)eventhdl);
5045}
5046#endif
5047
5048
5049#if defined(GCC_DIAGNOSTIC)
5050/* Enable unused function warning again */
5051#pragma GCC diagnostic pop
5052#endif
5053
5054
5055/* For Windows, change all slashes to backslashes in path names. */
5056static void
5058{
5059 int i;
5060
5061 for (i = 0; path[i] != '\0'; i++) {
5062 if (path[i] == '/') {
5063 path[i] = '\\';
5064 }
5065
5066 /* remove double backslash (check i > 0 to preserve UNC paths,
5067 * like \\server\file.txt) */
5068 if ((i > 0) && (path[i] == '\\')) {
5069 while ((path[i + 1] == '\\') || (path[i + 1] == '/')) {
5070 (void)memmove(path + i + 1, path + i + 2, strlen(path + i + 1));
5071 }
5072 }
5073 }
5074}
5075
5076
5077static int
5078mg_wcscasecmp(const wchar_t *s1, const wchar_t *s2)
5079{
5080 int diff;
5081
5082 do {
5083 diff = ((*s1 >= L'A') && (*s1 <= L'Z') ? (*s1 - L'A' + L'a') : *s1)
5084 - ((*s2 >= L'A') && (*s2 <= L'Z') ? (*s2 - L'A' + L'a') : *s2);
5085 s1++;
5086 s2++;
5087 } while ((diff == 0) && (s1[-1] != L'\0'));
5088
5089 return diff;
5090}
5091
5092
5093/* Encode 'path' which is assumed UTF-8 string, into UNICODE string.
5094 * wbuf and wbuf_len is a target buffer and its length. */
5095static void
5096path_to_unicode(const struct mg_connection *conn,
5097 const char *path,
5098 wchar_t *wbuf,
5099 size_t wbuf_len)
5100{
5101 char buf[UTF8_PATH_MAX], buf2[UTF8_PATH_MAX];
5102 wchar_t wbuf2[UTF16_PATH_MAX + 1];
5103 DWORD long_len, err;
5104 int (*fcompare)(const wchar_t *, const wchar_t *) = mg_wcscasecmp;
5105
5106 mg_strlcpy(buf, path, sizeof(buf));
5108
5109 /* Convert to Unicode and back. If doubly-converted string does not
5110 * match the original, something is fishy, reject. */
5111 memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
5112 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int)wbuf_len);
5114 CP_UTF8, 0, wbuf, (int)wbuf_len, buf2, sizeof(buf2), NULL, NULL);
5115 if (strcmp(buf, buf2) != 0) {
5116 wbuf[0] = L'\0';
5117 }
5118
5119 /* Windows file systems are not case sensitive, but you can still use
5120 * uppercase and lowercase letters (on all modern file systems).
5121 * The server can check if the URI uses the same upper/lowercase
5122 * letters an the file system, effectively making Windows servers
5123 * case sensitive (like Linux servers are). It is still not possible
5124 * to use two files with the same name in different cases on Windows
5125 * (like /a and /A) - this would be possible in Linux.
5126 * As a default, Windows is not case sensitive, but the case sensitive
5127 * file name check can be activated by an additional configuration. */
5128 if (conn) {
5129 if (conn->dom_ctx->config[CASE_SENSITIVE_FILES]
5130 && !mg_strcasecmp(conn->dom_ctx->config[CASE_SENSITIVE_FILES],
5131 "yes")) {
5132 /* Use case sensitive compare function */
5133 fcompare = wcscmp;
5134 }
5135 }
5136 (void)conn; /* conn is currently unused */
5137
5138 /* Only accept a full file path, not a Windows short (8.3) path. */
5139 memset(wbuf2, 0, ARRAY_SIZE(wbuf2) * sizeof(wchar_t));
5141 if (long_len == 0) {
5142 err = GetLastError();
5143 if (err == ERROR_FILE_NOT_FOUND) {
5144 /* File does not exist. This is not always a problem here. */
5145 return;
5146 }
5147 }
5148 if ((long_len >= ARRAY_SIZE(wbuf2)) || (fcompare(wbuf, wbuf2) != 0)) {
5149 /* Short name is used. */
5150 wbuf[0] = L'\0';
5151 }
5152}
5153
5154
5155#if !defined(NO_FILESYSTEMS)
5156/* Get file information, return 1 if file exists, 0 if not */
5157static int
5158mg_stat(const struct mg_connection *conn,
5159 const char *path,
5160 struct mg_file_stat *filep)
5161{
5162 wchar_t wbuf[UTF16_PATH_MAX];
5164 time_t creation_time;
5165 size_t len;
5166
5167 if (!filep) {
5168 return 0;
5169 }
5170 memset(filep, 0, sizeof(*filep));
5171
5172 if (mg_path_suspicious(conn, path)) {
5173 return 0;
5174 }
5175
5176 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
5177 /* Windows happily opens files with some garbage at the end of file name.
5178 * For example, fopen("a.cgi ", "r") on Windows successfully opens
5179 * "a.cgi", despite one would expect an error back. */
5180 len = strlen(path);
5181 if ((len > 0) && (path[len - 1] != ' ') && (path[len - 1] != '.')
5183 filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
5184 filep->last_modified =
5185 SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime,
5186 info.ftLastWriteTime.dwHighDateTime);
5187
5188 /* On Windows, the file creation time can be higher than the
5189 * modification time, e.g. when a file is copied.
5190 * Since the Last-Modified timestamp is used for caching
5191 * it should be based on the most recent timestamp. */
5192 creation_time = SYS2UNIX_TIME(info.ftCreationTime.dwLowDateTime,
5193 info.ftCreationTime.dwHighDateTime);
5194 if (creation_time > filep->last_modified) {
5195 filep->last_modified = creation_time;
5196 }
5197
5198 filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
5199 return 1;
5200 }
5201
5202 return 0;
5203}
5204#endif
5205
5206
5207static int
5208mg_remove(const struct mg_connection *conn, const char *path)
5209{
5210 wchar_t wbuf[UTF16_PATH_MAX];
5211 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
5212 return DeleteFileW(wbuf) ? 0 : -1;
5213}
5214
5215
5216static int
5217mg_mkdir(const struct mg_connection *conn, const char *path, int mode)
5218{
5219 wchar_t wbuf[UTF16_PATH_MAX];
5220 (void)mode;
5221 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
5222 return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
5223}
5224
5225
5226/* Create substitutes for POSIX functions in Win32. */
5227
5228#if defined(GCC_DIAGNOSTIC)
5229/* Show no warning in case system functions are not used. */
5230#pragma GCC diagnostic push
5231#pragma GCC diagnostic ignored "-Wunused-function"
5232#endif
5233
5234
5235/* Implementation of POSIX opendir/closedir/readdir for Windows. */
5237static DIR *
5238mg_opendir(const struct mg_connection *conn, const char *name)
5239{
5240 DIR *dir = NULL;
5241 wchar_t wpath[UTF16_PATH_MAX];
5242 DWORD attrs;
5243
5244 if (name == NULL) {
5246 } else if ((dir = (DIR *)mg_malloc(sizeof(*dir))) == NULL) {
5248 } else {
5251 if ((wcslen(wpath) + 2 < ARRAY_SIZE(wpath)) && (attrs != 0xFFFFFFFF)
5252 && ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0)) {
5253 (void)wcscat(wpath, L"\\*");
5254 dir->handle = FindFirstFileW(wpath, &dir->info);
5255 dir->result.d_name[0] = '\0';
5256 } else {
5257 mg_free(dir);
5258 dir = NULL;
5259 }
5260 }
5261
5262 return dir;
5263}
5264
5265
5267static int
5268mg_closedir(DIR *dir)
5269{
5270 int result = 0;
5271
5272 if (dir != NULL) {
5273 if (dir->handle != INVALID_HANDLE_VALUE)
5274 result = FindClose(dir->handle) ? 0 : -1;
5275
5276 mg_free(dir);
5277 } else {
5278 result = -1;
5280 }
5281
5282 return result;
5283}
5284
5285
5287static struct dirent *
5288mg_readdir(DIR *dir)
5289{
5290 struct dirent *result = 0;
5291
5292 if (dir) {
5293 if (dir->handle != INVALID_HANDLE_VALUE) {
5294 result = &dir->result;
5296 0,
5297 dir->info.cFileName,
5298 -1,
5299 result->d_name,
5300 sizeof(result->d_name),
5301 NULL,
5302 NULL);
5303
5304 if (!FindNextFileW(dir->handle, &dir->info)) {
5305 (void)FindClose(dir->handle);
5306 dir->handle = INVALID_HANDLE_VALUE;
5307 }
5308
5309 } else {
5311 }
5312 } else {
5314 }
5315
5316 return result;
5317}
5318
5319
5320#if !defined(HAVE_POLL)
5321#undef POLLIN
5322#undef POLLPRI
5323#undef POLLOUT
5324#undef POLLERR
5325#define POLLIN (1) /* Data ready - read will not block. */
5326#define POLLPRI (2) /* Priority data ready. */
5327#define POLLOUT (4) /* Send queue not full - write will not block. */
5328#define POLLERR (8) /* Error event */
5329
5331static int
5332poll(struct mg_pollfd *pfd, unsigned int n, int milliseconds)
5333{
5334 struct timeval tv;
5335 fd_set rset;
5336 fd_set wset;
5337 fd_set eset;
5338 unsigned int i;
5339 int result;
5340 SOCKET maxfd = 0;
5341
5342 memset(&tv, 0, sizeof(tv));
5343 tv.tv_sec = milliseconds / 1000;
5344 tv.tv_usec = (milliseconds % 1000) * 1000;
5345 FD_ZERO(&rset);
5346 FD_ZERO(&wset);
5347 FD_ZERO(&eset);
5348
5349 for (i = 0; i < n; i++) {
5350 if (pfd[i].events & (POLLIN | POLLOUT | POLLERR)) {
5351 if (pfd[i].events & POLLIN) {
5352 FD_SET(pfd[i].fd, &rset);
5353 }
5354 if (pfd[i].events & POLLOUT) {
5355 FD_SET(pfd[i].fd, &wset);
5356 }
5357 /* Check for errors for any FD in the set */
5358 FD_SET(pfd[i].fd, &eset);
5359 }
5360 pfd[i].revents = 0;
5361
5362 if (pfd[i].fd > maxfd) {
5363 maxfd = pfd[i].fd;
5364 }
5365 }
5366
5367 if ((result = select((int)maxfd + 1, &rset, &wset, &eset, &tv)) > 0) {
5368 for (i = 0; i < n; i++) {
5369 if (FD_ISSET(pfd[i].fd, &rset)) {
5370 pfd[i].revents |= POLLIN;
5371 }
5372 if (FD_ISSET(pfd[i].fd, &wset)) {
5373 pfd[i].revents |= POLLOUT;
5374 }
5375 if (FD_ISSET(pfd[i].fd, &eset)) {
5376 pfd[i].revents |= POLLERR;
5377 }
5378 }
5379 }
5380
5381 /* We should subtract the time used in select from remaining
5382 * "milliseconds", in particular if called from mg_poll with a
5383 * timeout quantum.
5384 * Unfortunately, the remaining time is not stored in "tv" in all
5385 * implementations, so the result in "tv" must be considered undefined.
5386 * See http://man7.org/linux/man-pages/man2/select.2.html */
5387
5388 return result;
5389}
5390#endif /* HAVE_POLL */
5391
5392
5393#if defined(GCC_DIAGNOSTIC)
5394/* Enable unused function warning again */
5395#pragma GCC diagnostic pop
5396#endif
5397
5398
5399static void
5401 const struct mg_connection *conn /* may be null */,
5402 struct mg_context *ctx /* may be null */)
5403{
5404 (void)conn; /* Unused. */
5405 (void)ctx;
5406
5407 (void)SetHandleInformation((HANDLE)(intptr_t)sock, HANDLE_FLAG_INHERIT, 0);
5408}
5409
5410
5411CIVETWEB_API int
5413{
5414#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5415 /* Compile-time option to control stack size, e.g.
5416 * -DUSE_STACK_SIZE=16384
5417 */
5418 return ((_beginthread((void(__cdecl *)(void *))f, USE_STACK_SIZE, p)
5419 == ((uintptr_t)(-1L)))
5420 ? -1
5421 : 0);
5422#else
5423 return (
5424 (_beginthread((void(__cdecl *)(void *))f, 0, p) == ((uintptr_t)(-1L)))
5425 ? -1
5426 : 0);
5427#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */
5428}
5429
5430
5431/* Start a thread storing the thread context. */
5432static int
5433mg_start_thread_with_id(unsigned(__stdcall *f)(void *),
5434 void *p,
5435 pthread_t *threadidptr)
5436{
5437 uintptr_t uip;
5438 HANDLE threadhandle;
5439 int result = -1;
5440
5441 uip = _beginthreadex(NULL, 0, f, p, 0, NULL);
5442 threadhandle = (HANDLE)uip;
5443 if ((uip != 0) && (threadidptr != NULL)) {
5445 result = 0;
5446 }
5447
5448 return result;
5449}
5450
5451
5452/* Wait for a thread to finish. */
5453static int
5454mg_join_thread(pthread_t threadid)
5455{
5456 int result;
5457 DWORD dwevent;
5458
5459 result = -1;
5461 if (dwevent == WAIT_FAILED) {
5462 DEBUG_TRACE("WaitForSingleObject() failed, error %d", ERRNO);
5463 } else {
5464 if (dwevent == WAIT_OBJECT_0) {
5466 result = 0;
5467 }
5468 }
5469
5470 return result;
5471}
5472
5473#if !defined(NO_SSL_DL) && !defined(NO_SSL)
5474/* If SSL is loaded dynamically, dlopen/dlclose is required. */
5475/* Create substitutes for POSIX functions in Win32. */
5476
5477#if defined(GCC_DIAGNOSTIC)
5478/* Show no warning in case system functions are not used. */
5479#pragma GCC diagnostic push
5480#pragma GCC diagnostic ignored "-Wunused-function"
5481#endif
5482
5483
5485static HANDLE
5486dlopen(const char *dll_name, int flags)
5487{
5488 wchar_t wbuf[UTF16_PATH_MAX];
5489 (void)flags;
5491 return LoadLibraryW(wbuf);
5492}
5493
5494
5496static int
5497dlclose(void *handle)
5498{
5499 int result;
5500
5501 if (FreeLibrary((HMODULE)handle) != 0) {
5502 result = 0;
5503 } else {
5504 result = -1;
5505 }
5506
5507 return result;
5508}
5509
5510
5511#if defined(GCC_DIAGNOSTIC)
5512/* Enable unused function warning again */
5513#pragma GCC diagnostic pop
5514#endif
5515
5516#endif
5517
5518
5519#if !defined(NO_CGI)
5520#define SIGKILL (0)
5521
5522
5523static int
5524kill(pid_t pid, int sig_num)
5525{
5526 (void)TerminateProcess((HANDLE)pid, (UINT)sig_num);
5527 (void)CloseHandle((HANDLE)pid);
5528 return 0;
5529}
5530
5531
5532#if !defined(WNOHANG)
5533#define WNOHANG (1)
5534#endif
5535
5536
5537static pid_t
5538waitpid(pid_t pid, int *status, int flags)
5539{
5540 DWORD timeout = INFINITE;
5541 DWORD waitres;
5542
5543 (void)status; /* Currently not used by any client here */
5544
5545 if ((flags | WNOHANG) == WNOHANG) {
5546 timeout = 0;
5547 }
5548
5549 waitres = WaitForSingleObject((HANDLE)pid, timeout);
5550 if (waitres == WAIT_OBJECT_0) {
5551 return pid;
5552 }
5553 if (waitres == WAIT_TIMEOUT) {
5554 return 0;
5555 }
5556 return (pid_t)-1;
5557}
5558
5559
5560static void
5562{
5563 char *e = s + strlen(s);
5564 while ((e > s) && isspace((unsigned char)e[-1])) {
5565 *(--e) = '\0';
5566 }
5567}
5568
5569
5570static pid_t
5571spawn_process(struct mg_connection *conn,
5572 const char *prog,
5573 char *envblk,
5574 char *envp[],
5575 int fdin[2],
5576 int fdout[2],
5577 int fderr[2],
5578 const char *dir,
5579 int cgi_config_idx)
5580{
5581 HANDLE me;
5582 char *interp;
5583 char *interp_arg = 0;
5585 int truncated;
5586 struct mg_file file = STRUCT_FILE_INITIALIZER;
5588 PROCESS_INFORMATION pi = {0};
5589
5590 (void)envp;
5591
5592 memset(&si, 0, sizeof(si));
5593 si.cb = sizeof(si);
5594
5596 si.wShowWindow = SW_HIDE;
5597
5598 me = GetCurrentProcess();
5599 DuplicateHandle(me,
5600 (HANDLE)_get_osfhandle(fdin[0]),
5601 me,
5602 &si.hStdInput,
5603 0,
5604 TRUE,
5606 DuplicateHandle(me,
5607 (HANDLE)_get_osfhandle(fdout[1]),
5608 me,
5609 &si.hStdOutput,
5610 0,
5611 TRUE,
5613 DuplicateHandle(me,
5614 (HANDLE)_get_osfhandle(fderr[1]),
5615 me,
5616 &si.hStdError,
5617 0,
5618 TRUE,
5620
5621 /* Mark handles that should not be inherited. See
5622 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx
5623 */
5626 0);
5629 0);
5632 0);
5633
5634 /* First check, if there is a CGI interpreter configured for all CGI
5635 * scripts. */
5636 interp = conn->dom_ctx->config[CGI_INTERPRETER + cgi_config_idx];
5637 if (interp != NULL) {
5638 /* If there is a configured interpreter, check for additional arguments
5639 */
5640 interp_arg =
5642 } else {
5643 /* Otherwise, the interpreter must be stated in the first line of the
5644 * CGI script file, after a #! (shebang) mark. */
5645 buf[0] = buf[1] = '\0';
5646
5647 /* Get the full script path */
5649 conn, &truncated, cmdline, sizeof(cmdline), "%s/%s", dir, prog);
5650
5651 if (truncated) {
5652 pi.hProcess = (pid_t)-1;
5653 goto spawn_cleanup;
5654 }
5655
5656 /* Open the script file, to read the first line */
5657 if (mg_fopen(conn, cmdline, MG_FOPEN_MODE_READ, &file)) {
5658
5659 /* Read the first line of the script into the buffer */
5660 mg_fgets(buf, sizeof(buf), &file);
5661 (void)mg_fclose(&file.access); /* ignore error on read only file */
5662 buf[sizeof(buf) - 1] = '\0';
5663 }
5664
5665 if ((buf[0] == '#') && (buf[1] == '!')) {
5667 } else {
5668 buf[2] = '\0';
5669 }
5670 interp = buf + 2;
5671 }
5672
5673 GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL);
5674
5675 if (interp[0] != '\0') {
5676 /* This is an interpreted script file. We must call the interpreter. */
5677 if ((interp_arg != 0) && (interp_arg[0] != 0)) {
5678 mg_snprintf(conn,
5679 &truncated,
5680 cmdline,
5681 sizeof(cmdline),
5682 "\"%s\" %s \"%s\\%s\"",
5683 interp,
5684 interp_arg,
5685 full_dir,
5686 prog);
5687 } else {
5688 mg_snprintf(conn,
5689 &truncated,
5690 cmdline,
5691 sizeof(cmdline),
5692 "\"%s\" \"%s\\%s\"",
5693 interp,
5694 full_dir,
5695 prog);
5696 }
5697 } else {
5698 /* This is (probably) a compiled program. We call it directly. */
5699 mg_snprintf(conn,
5700 &truncated,
5701 cmdline,
5702 sizeof(cmdline),
5703 "\"%s\\%s\"",
5704 full_dir,
5705 prog);
5706 }
5707
5708 if (truncated) {
5709 pi.hProcess = (pid_t)-1;
5710 goto spawn_cleanup;
5711 }
5712
5713 DEBUG_TRACE("Running [%s]", cmdline);
5714 if (CreateProcessA(NULL,
5715 cmdline,
5716 NULL,
5717 NULL,
5718 TRUE,
5720 envblk,
5721 NULL,
5722 &si,
5723 &pi)
5724 == 0) {
5726 conn, "%s: CreateProcess(%s): %ld", __func__, cmdline, (long)ERRNO);
5727 pi.hProcess = (pid_t)-1;
5728 /* goto spawn_cleanup; */
5729 }
5730
5732 (void)CloseHandle(si.hStdOutput);
5733 (void)CloseHandle(si.hStdError);
5734 (void)CloseHandle(si.hStdInput);
5735 if (pi.hThread != NULL) {
5736 (void)CloseHandle(pi.hThread);
5737 }
5738
5739 return (pid_t)pi.hProcess;
5740}
5741#endif /* !NO_CGI */
5742
5743
5744static int
5746{
5747 unsigned long non_blocking = 0;
5748 return ioctlsocket(sock, (long)FIONBIO, &non_blocking);
5749}
5750
5751
5752static int
5754{
5755 unsigned long non_blocking = 1;
5756 return ioctlsocket(sock, (long)FIONBIO, &non_blocking);
5757}
5758
5759
5760#else
5761
5762
5763#if !defined(NO_FILESYSTEMS)
5764static int
5765mg_stat(const struct mg_connection *conn,
5766 const char *path,
5767 struct mg_file_stat *filep)
5768{
5769 struct stat st;
5770 if (!filep) {
5771 return 0;
5772 }
5773 memset(filep, 0, sizeof(*filep));
5774
5775 if (mg_path_suspicious(conn, path)) {
5776 return 0;
5777 }
5778
5779 if (0 == stat(path, &st)) {
5780 filep->size = (uint64_t)(st.st_size);
5781 filep->last_modified = st.st_mtime;
5782 filep->is_directory = S_ISDIR(st.st_mode);
5783 return 1;
5784 }
5785
5786 return 0;
5787}
5788#endif /* NO_FILESYSTEMS */
5789
5790
5791static void
5793 const struct mg_connection *conn /* may be null */,
5794 struct mg_context *ctx /* may be null */)
5795{
5796#if defined(__ZEPHYR__)
5797 (void)fd;
5798 (void)conn;
5799 (void)ctx;
5800#else
5801 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
5802 if (conn || ctx) {
5803 struct mg_connection fc;
5804 mg_cry_internal((conn ? conn : fake_connection(&fc, ctx)),
5805 "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
5806 __func__,
5807 strerror(ERRNO));
5808 }
5809 }
5810#endif
5811}
5812
5813
5814CIVETWEB_API int
5816{
5817 pthread_t thread_id;
5819 int result;
5820
5821 (void)pthread_attr_init(&attr);
5823
5824#if defined(__ZEPHYR__)
5826#elif defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5827 /* Compile-time option to control stack size,
5828 * e.g. -DUSE_STACK_SIZE=16384 */
5830#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */
5831
5832 result = pthread_create(&thread_id, &attr, func, param);
5834
5835 return result;
5836}
5837
5838
5839/* Start a thread storing the thread context. */
5840static int
5842 void *param,
5843 pthread_t *threadidptr)
5844{
5845 pthread_t thread_id;
5847 int result;
5848
5849 (void)pthread_attr_init(&attr);
5850
5851#if defined(__ZEPHYR__)
5855#elif defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5856 /* Compile-time option to control stack size,
5857 * e.g. -DUSE_STACK_SIZE=16384 */
5859#endif /* defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 */
5860
5861 result = pthread_create(&thread_id, &attr, func, param);
5863 if ((result == 0) && (threadidptr != NULL)) {
5865 }
5866 return result;
5867}
5868
5869
5870/* Wait for a thread to finish. */
5871static int
5873{
5874 int result;
5875
5877 return result;
5878}
5879
5880
5881#if !defined(NO_CGI)
5882static pid_t
5884 const char *prog,
5885 char *envblk,
5886 char *envp[],
5887 int fdin[2],
5888 int fdout[2],
5889 int fderr[2],
5890 const char *dir,
5891 int cgi_config_idx)
5892{
5893 pid_t pid;
5894 const char *interp;
5895
5896 (void)envblk;
5897
5898 if ((pid = fork()) == -1) {
5899 /* Parent */
5900 mg_cry_internal(conn, "%s: fork(): %s", __func__, strerror(ERRNO));
5901 } else if (pid != 0) {
5902 /* Make sure children close parent-side descriptors.
5903 * The caller will close the child-side immediately. */
5904 set_close_on_exec(fdin[1], conn, NULL); /* stdin write */
5905 set_close_on_exec(fdout[0], conn, NULL); /* stdout read */
5906 set_close_on_exec(fderr[0], conn, NULL); /* stderr read */
5907 } else {
5908 /* Child */
5909 if (chdir(dir) != 0) {
5911 conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
5912 } else if (dup2(fdin[0], 0) == -1) {
5913 mg_cry_internal(conn,
5914 "%s: dup2(%d, 0): %s",
5915 __func__,
5916 fdin[0],
5917 strerror(ERRNO));
5918 } else if (dup2(fdout[1], 1) == -1) {
5919 mg_cry_internal(conn,
5920 "%s: dup2(%d, 1): %s",
5921 __func__,
5922 fdout[1],
5923 strerror(ERRNO));
5924 } else if (dup2(fderr[1], 2) == -1) {
5925 mg_cry_internal(conn,
5926 "%s: dup2(%d, 2): %s",
5927 __func__,
5928 fderr[1],
5929 strerror(ERRNO));
5930 } else {
5931 struct sigaction sa;
5932
5933 /* Keep stderr and stdout in two different pipes.
5934 * Stdout will be sent back to the client,
5935 * stderr should go into a server error log. */
5936 (void)close(fdin[0]);
5937 (void)close(fdout[1]);
5938 (void)close(fderr[1]);
5939
5940 /* Close write end fdin and read end fdout and fderr */
5941 (void)close(fdin[1]);
5942 (void)close(fdout[0]);
5943 (void)close(fderr[0]);
5944
5945 /* After exec, all signal handlers are restored to their default
5946 * values, with one exception of SIGCHLD. According to
5947 * POSIX.1-2001 and Linux's implementation, SIGCHLD's handler
5948 * will leave unchanged after exec if it was set to be ignored.
5949 * Restore it to default action. */
5950 memset(&sa, 0, sizeof(sa));
5951 sa.sa_handler = SIG_DFL;
5952 sigaction(SIGCHLD, &sa, NULL);
5953
5954 interp = conn->dom_ctx->config[CGI_INTERPRETER + cgi_config_idx];
5955 if (interp == NULL) {
5956 /* no interpreter configured, call the program directly */
5957 (void)execle(prog, prog, NULL, envp);
5958 mg_cry_internal(conn,
5959 "%s: execle(%s): %s",
5960 __func__,
5961 prog,
5962 strerror(ERRNO));
5963 } else {
5964 /* call the configured interpreter */
5965 const char *interp_args =
5966 conn->dom_ctx
5968
5969 if ((interp_args != NULL) && (interp_args[0] != 0)) {
5971 } else {
5972 (void)execle(interp, interp, prog, NULL, envp);
5973 }
5974 mg_cry_internal(conn,
5975 "%s: execle(%s %s): %s",
5976 __func__,
5977 interp,
5978 prog,
5979 strerror(ERRNO));
5980 }
5981 }
5983 }
5984
5985 return pid;
5986}
5987#endif /* !NO_CGI */
5988
5989
5990static int
5992{
5993 int flags = fcntl(sock, F_GETFL, 0);
5994 if (flags < 0) {
5995 return -1;
5996 }
5997
5998 if (fcntl(sock, F_SETFL, (flags | O_NONBLOCK)) < 0) {
5999 return -1;
6000 }
6001 return 0;
6002}
6003
6004static int
6006{
6007 int flags = fcntl(sock, F_GETFL, 0);
6008 if (flags < 0) {
6009 return -1;
6010 }
6011
6012 if (fcntl(sock, F_SETFL, flags & (~(int)(O_NONBLOCK))) < 0) {
6013 return -1;
6014 }
6015 return 0;
6016}
6017#endif /* _WIN32 / else */
6018
6019/* End of initial operating system specific define block. */
6020
6021
6022/* Get a random number (independent of C rand function) */
6023static uint64_t
6025{
6026 static uint64_t lfsr = 0; /* Linear feedback shift register */
6027 static uint64_t lcg = 0; /* Linear congruential generator */
6028 uint64_t now = mg_get_current_time_ns();
6029
6030 if (lfsr == 0) {
6031 /* lfsr will be only 0 if has not been initialized,
6032 * so this code is called only once. */
6035 } else {
6036 /* Get the next step of both random number generators. */
6037 lfsr = (lfsr >> 1)
6038 | ((((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1)
6039 << 63);
6040 lcg = lcg * 6364136223846793005LL + 1442695040888963407LL;
6041 }
6042
6043 /* Combining two pseudo-random number generators and a high resolution
6044 * part
6045 * of the current server time will make it hard (impossible?) to guess
6046 * the
6047 * next number. */
6048 return (lfsr ^ lcg ^ now);
6049}
6050
6051
6052static int
6054 unsigned int n,
6055 int milliseconds,
6056 const stop_flag_t *stop_flag)
6057{
6058 /* Call poll, but only for a maximum time of a few seconds.
6059 * This will allow to stop the server after some seconds, instead
6060 * of having to wait for a long socket timeout. */
6061 int ms_now = SOCKET_TIMEOUT_QUANTUM; /* Sleep quantum in ms */
6062
6063 int check_pollerr = 0;
6064 if ((n == 1) && ((pfd[0].events & POLLERR) == 0)) {
6065 /* If we wait for only one file descriptor, wait on error as well */
6066 pfd[0].events |= POLLERR;
6067 check_pollerr = 1;
6068 }
6069
6070 do {
6071 int result;
6072
6073 if (!STOP_FLAG_IS_ZERO(&*stop_flag)) {
6074 /* Shut down signal */
6075 return -2;
6076 }
6077
6078 if ((milliseconds >= 0) && (milliseconds < ms_now)) {
6080 }
6081
6082 result = poll(pfd, n, ms_now);
6083 if (result != 0) {
6084 int err = ERRNO;
6085 if ((result == 1) || (!ERROR_TRY_AGAIN(err))) {
6086 /* Poll returned either success (1) or error (-1).
6087 * Forward both to the caller. */
6088 if ((check_pollerr)
6089 && ((pfd[0].revents & (POLLIN | POLLOUT | POLLERR))
6090 == POLLERR)) {
6091 /* One and only file descriptor returned error */
6092 return -1;
6093 }
6094 return result;
6095 }
6096 }
6097
6098 /* Poll returned timeout (0). */
6099 if (milliseconds > 0) {
6101 }
6102
6103 } while (milliseconds > 0);
6104
6105 /* timeout: return 0 */
6106 return 0;
6107}
6108
6109
6110/* Write data to the IO channel - opened file descriptor, socket or SSL
6111 * descriptor.
6112 * Return value:
6113 * >=0 .. number of bytes successfully written
6114 * -1 .. timeout
6115 * -2 .. error
6116 */
6117static int
6119 FILE *fp,
6120 SOCKET sock,
6121 SSL *ssl,
6122 const char *buf,
6123 int len,
6124 double timeout)
6125{
6126 uint64_t start = 0, now = 0, timeout_ns = 0;
6127 int n, err;
6128 unsigned ms_wait = SOCKET_TIMEOUT_QUANTUM; /* Sleep quantum in ms */
6129
6130#if defined(_WIN32)
6131 typedef int len_t;
6132#else
6133 typedef size_t len_t;
6134#endif
6135
6136 if (timeout > 0) {
6138 start = now;
6139 timeout_ns = (uint64_t)(timeout * 1.0E9);
6140 }
6141
6142 if (ctx == NULL) {
6143 return -2;
6144 }
6145
6146#if defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS)
6147 if (ssl) {
6148 return -2;
6149 }
6150#endif
6151
6152 /* Try to read until it succeeds, fails, times out, or the server
6153 * shuts down. */
6154 for (;;) {
6155
6156#if defined(USE_MBEDTLS)
6157 if (ssl != NULL) {
6158 n = mbed_ssl_write(ssl, (const unsigned char *)buf, len);
6159 if (n <= 0) {
6163 n = 0;
6164 } else {
6165 fprintf(stderr, "SSL write failed, error %d\n", n);
6166 return -2;
6167 }
6168 } else {
6169 err = 0;
6170 }
6171 } else
6172#elif defined(USE_GNUTLS)
6173 if (ssl != NULL) {
6174 n = gtls_ssl_write(ssl, (const unsigned char *)buf, (size_t)len);
6175 if (n < 0) {
6177 "SSL write failed (%d): %s",
6178 n,
6180 return -2;
6181 } else {
6182 err = 0;
6183 }
6184 } else
6185#elif !defined(NO_SSL)
6186 if (ssl != NULL) {
6188 n = SSL_write(ssl, buf, len);
6189 if (n <= 0) {
6190 err = SSL_get_error(ssl, n);
6191 if ((err == SSL_ERROR_SYSCALL) && (n == -1)) {
6192 err = ERRNO;
6193 } else if ((err == SSL_ERROR_WANT_READ)
6194 || (err == SSL_ERROR_WANT_WRITE)) {
6195 n = 0;
6196 } else {
6197 DEBUG_TRACE("SSL_write() failed, error %d", err);
6199 return -2;
6200 }
6202 } else {
6203 err = 0;
6204 }
6205 } else
6206#endif
6207
6208 if (fp != NULL) {
6209 n = (int)fwrite(buf, 1, (size_t)len, fp);
6210 if (ferror(fp)) {
6211 n = -1;
6212 err = ERRNO;
6213 } else {
6214 err = 0;
6215 }
6216 } else {
6217 n = (int)send(sock, buf, (len_t)len, MSG_NOSIGNAL);
6218 err = (n < 0) ? ERRNO : 0;
6219 if (ERROR_TRY_AGAIN(err)) {
6220 err = 0;
6221 n = 0;
6222 }
6223 if (n < 0) {
6224 /* shutdown of the socket at client side */
6225 return -2;
6226 }
6227 }
6228
6229 if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
6230 return -2;
6231 }
6232
6233 if ((n > 0) || ((n == 0) && (len == 0))) {
6234 /* some data has been read, or no data was requested */
6235 return n;
6236 }
6237 if (n < 0) {
6238 /* socket error - check errno */
6239 DEBUG_TRACE("send() failed, error %d", err);
6240
6241 /* TODO (mid): error handling depending on the error code.
6242 * These codes are different between Windows and Linux.
6243 * Currently there is no problem with failing send calls,
6244 * if there is a reproducible situation, it should be
6245 * investigated in detail.
6246 */
6247 return -2;
6248 }
6249
6250 /* Only in case n=0 (timeout), repeat calling the write function */
6251
6252 /* If send failed, wait before retry */
6253 if (fp != NULL) {
6254 /* For files, just wait a fixed time.
6255 * Maybe it helps, maybe not. */
6256 mg_sleep(5);
6257 } else {
6258 /* For sockets, wait for the socket using poll */
6259 struct mg_pollfd pfd[2];
6260 int pollres;
6261 unsigned int num_sock = 1;
6262
6263 pfd[0].fd = sock;
6264 pfd[0].events = POLLOUT;
6265
6266 if (ctx->context_type == CONTEXT_SERVER) {
6268 pfd[num_sock].events = POLLIN;
6269 num_sock++;
6270 }
6271
6272 pollres = mg_poll(pfd, num_sock, (int)(ms_wait), &(ctx->stop_flag));
6273 if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
6274 return -2;
6275 }
6276 if (pollres > 0) {
6277 continue;
6278 }
6279 }
6280
6281 if (timeout > 0) {
6283 if ((now - start) > timeout_ns) {
6284 /* Timeout */
6285 break;
6286 }
6287 }
6288 }
6289
6290 (void)err; /* Avoid unused warning if NO_SSL is set and DEBUG_TRACE is not
6291 used */
6292
6293 return -1;
6294}
6295
6296
6297static int
6299 FILE *fp,
6300 SOCKET sock,
6301 SSL *ssl,
6302 const char *buf,
6303 int len)
6304{
6305 double timeout = -1.0;
6306 int n, nwritten = 0;
6307
6308 if (ctx == NULL) {
6309 return -1;
6310 }
6311
6312 if (ctx->dd.config[REQUEST_TIMEOUT]) {
6313 timeout = atoi(ctx->dd.config[REQUEST_TIMEOUT]) / 1000.0;
6314 }
6315 if (timeout <= 0.0) {
6317 / 1000.0;
6318 }
6319
6320 while ((len > 0) && STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
6321 n = push_inner(ctx, fp, sock, ssl, buf + nwritten, len, timeout);
6322 if (n < 0) {
6323 if (nwritten == 0) {
6324 nwritten = -1; /* Propagate the error */
6325 }
6326 break;
6327 } else if (n == 0) {
6328 break; /* No more data to write */
6329 } else {
6330 nwritten += n;
6331 len -= n;
6332 }
6333 }
6334
6335 return nwritten;
6336}
6337
6338
6339/* Read from IO channel - opened file descriptor, socket, or SSL descriptor.
6340 * Return value:
6341 * >=0 .. number of bytes successfully read
6342 * -1 .. timeout
6343 * -2 .. error
6344 */
6345static int
6347 struct mg_connection *conn,
6348 char *buf,
6349 int len,
6350 double timeout)
6351{
6352 int nread, err = 0;
6353
6354#if defined(_WIN32)
6355 typedef int len_t;
6356#else
6357 typedef size_t len_t;
6358#endif
6359
6360 /* We need an additional wait loop around this, because in some cases
6361 * with TLSwe may get data from the socket but not from SSL_read.
6362 * In this case we need to repeat at least once.
6363 */
6364
6365 if (fp != NULL) {
6366 /* Use read() instead of fread(), because if we're reading from the
6367 * CGI pipe, fread() may block until IO buffer is filled up. We
6368 * cannot afford to block and must pass all read bytes immediately
6369 * to the client. */
6370 nread = (int)read(fileno(fp), buf, (size_t)len);
6371
6372 err = (nread < 0) ? ERRNO : 0;
6373 if ((nread == 0) && (len > 0)) {
6374 /* Should get data, but got EOL */
6375 return -2;
6376 }
6377
6378#if defined(USE_MBEDTLS)
6379 } else if (conn->ssl != NULL) {
6380 struct mg_pollfd pfd[2];
6381 int to_read;
6382 int pollres;
6383 unsigned int num_sock = 1;
6384
6386
6387 if (to_read > 0) {
6388 /* We already know there is no more data buffered in conn->buf
6389 * but there is more available in the SSL layer. So don't poll
6390 * conn->client.sock yet. */
6391
6392 pollres = 1;
6393 if (to_read > len)
6394 to_read = len;
6395 } else {
6396 pfd[0].fd = conn->client.sock;
6397 pfd[0].events = POLLIN;
6398
6399 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
6400 pfd[num_sock].fd =
6401 conn->phys_ctx->thread_shutdown_notification_socket;
6402 pfd[num_sock].events = POLLIN;
6403 num_sock++;
6404 }
6405
6406 to_read = len;
6407
6409 num_sock,
6410 (int)(timeout * 1000.0),
6411 &(conn->phys_ctx->stop_flag));
6412
6413 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6414 return -2;
6415 }
6416 }
6417
6418 if (pollres > 0) {
6419 nread = mbed_ssl_read(conn->ssl, (unsigned char *)buf, to_read);
6420 if (nread <= 0) {
6424 nread = 0;
6425 } else {
6426 fprintf(stderr, "SSL read failed, error %d\n", nread);
6427 return -2;
6428 }
6429 } else {
6430 err = 0;
6431 }
6432
6433 } else if (pollres < 0) {
6434 /* Error */
6435 return -2;
6436 } else {
6437 /* pollres = 0 means timeout */
6438 nread = 0;
6439 }
6440
6441#elif defined(USE_GNUTLS)
6442 } else if (conn->ssl != NULL) {
6443 struct mg_pollfd pfd[2];
6444 size_t to_read;
6445 int pollres;
6446 unsigned int num_sock = 1;
6447
6449
6450 if (to_read > 0) {
6451 /* We already know there is no more data buffered in conn->buf
6452 * but there is more available in the SSL layer. So don't poll
6453 * conn->client.sock yet. */
6454
6455 pollres = 1;
6456 if (to_read > (size_t)len)
6457 to_read = (size_t)len;
6458 } else {
6459 pfd[0].fd = conn->client.sock;
6460 pfd[0].events = POLLIN;
6461
6462 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
6463 pfd[num_sock].fd =
6464 conn->phys_ctx->thread_shutdown_notification_socket;
6465 pfd[num_sock].events = POLLIN;
6466 num_sock++;
6467 }
6468
6469 to_read = (size_t)len;
6470
6472 num_sock,
6473 (int)(timeout * 1000.0),
6474 &(conn->phys_ctx->stop_flag));
6475
6476 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6477 return -2;
6478 }
6479 }
6480
6481 if (pollres > 0) {
6482 nread = gtls_ssl_read(conn->ssl, (unsigned char *)buf, to_read);
6483 if (nread < 0) {
6485 "SSL read failed (%d): %s",
6486 nread,
6488 return -2;
6489 } else {
6490 err = 0;
6491 }
6492 } else if (pollres < 0) {
6493 /* Error */
6494 return -2;
6495 } else {
6496 /* pollres = 0 means timeout */
6497 nread = 0;
6498 }
6499
6500#elif !defined(NO_SSL)
6501 } else if (conn->ssl != NULL) {
6502 int ssl_pending;
6503 struct mg_pollfd pfd[2];
6504 int pollres;
6505 unsigned int num_sock = 1;
6506
6507 if ((ssl_pending = SSL_pending(conn->ssl)) > 0) {
6508 /* We already know there is no more data buffered in conn->buf
6509 * but there is more available in the SSL layer. So don't poll
6510 * conn->client.sock yet. */
6511 if (ssl_pending > len) {
6512 ssl_pending = len;
6513 }
6514 pollres = 1;
6515 } else {
6516 pfd[0].fd = conn->client.sock;
6517 pfd[0].events = POLLIN;
6518
6519 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
6520 pfd[num_sock].fd =
6521 conn->phys_ctx->thread_shutdown_notification_socket;
6522 pfd[num_sock].events = POLLIN;
6523 num_sock++;
6524 }
6525
6527 num_sock,
6528 (int)(timeout * 1000.0),
6529 &(conn->phys_ctx->stop_flag));
6530 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6531 return -2;
6532 }
6533 }
6534 if (pollres > 0) {
6536 nread =
6537 SSL_read(conn->ssl, buf, (ssl_pending > 0) ? ssl_pending : len);
6538 if (nread <= 0) {
6539 err = SSL_get_error(conn->ssl, nread);
6540 if ((err == SSL_ERROR_SYSCALL) && (nread == -1)) {
6541 err = ERRNO;
6542 } else if ((err == SSL_ERROR_WANT_READ)
6543 || (err == SSL_ERROR_WANT_WRITE)) {
6544 nread = 0;
6545 } else {
6546 /* All errors should return -2 */
6547 DEBUG_TRACE("SSL_read() failed, error %d", err);
6549 return -2;
6550 }
6552 } else {
6553 err = 0;
6554 }
6555 } else if (pollres < 0) {
6556 /* Error */
6557 return -2;
6558 } else {
6559 /* pollres = 0 means timeout */
6560 nread = 0;
6561 }
6562#endif
6563
6564 } else {
6565 struct mg_pollfd pfd[2];
6566 int pollres;
6567 unsigned int num_sock = 1;
6568
6569 pfd[0].fd = conn->client.sock;
6570 pfd[0].events = POLLIN;
6571
6572 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
6573 pfd[num_sock].fd =
6574 conn->phys_ctx->thread_shutdown_notification_socket;
6575 pfd[num_sock].events = POLLIN;
6576 num_sock++;
6577 }
6578
6580 num_sock,
6581 (int)(timeout * 1000.0),
6582 &(conn->phys_ctx->stop_flag));
6583 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6584 return -2;
6585 }
6586 if (pollres > 0) {
6587 nread = (int)recv(conn->client.sock, buf, (len_t)len, 0);
6588 err = (nread < 0) ? ERRNO : 0;
6589 if (nread <= 0) {
6590 /* shutdown of the socket at client side */
6591 return -2;
6592 }
6593 } else if (pollres < 0) {
6594 /* error calling poll */
6595 return -2;
6596 } else {
6597 /* pollres = 0 means timeout */
6598 nread = 0;
6599 }
6600 }
6601
6602 if (conn != NULL && !STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6603 return -2;
6604 }
6605
6606 if ((nread > 0) || ((nread == 0) && (len == 0))) {
6607 /* some data has been read, or no data was requested */
6608 return nread;
6609 }
6610
6611 if (nread < 0) {
6612 /* socket error - check errno */
6613#if defined(_WIN32)
6614 if (err == WSAEWOULDBLOCK) {
6615 /* TODO (low): check if this is still required */
6616 /* standard case if called from close_socket_gracefully */
6617 return -2;
6618 } else if (err == WSAETIMEDOUT) {
6619 /* TODO (low): check if this is still required */
6620 /* timeout is handled by the while loop */
6621 return 0;
6622 } else if (err == WSAECONNABORTED) {
6623 /* See https://www.chilkatsoft.com/p/p_299.asp */
6624 return -2;
6625 } else {
6626 DEBUG_TRACE("read()/recv() failed, error %d", err);
6627 return -2;
6628 }
6629#else
6630 /* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases,
6631 * if the timeout is reached and if the socket was set to non-
6632 * blocking in close_socket_gracefully, so we can not distinguish
6633 * here. We have to wait for the timeout in both cases for now.
6634 */
6635 if (ERROR_TRY_AGAIN(err)) {
6636 /* TODO (low): check if this is still required */
6637 /* EAGAIN/EWOULDBLOCK:
6638 * standard case if called from close_socket_gracefully
6639 * => should return -1 */
6640 /* or timeout occurred
6641 * => the code must stay in the while loop */
6642
6643 /* EINTR can be generated on a socket with a timeout set even
6644 * when SA_RESTART is effective for all relevant signals
6645 * (see signal(7)).
6646 * => stay in the while loop */
6647 } else {
6648 DEBUG_TRACE("read()/recv() failed, error %d", err);
6649 return -2;
6650 }
6651#endif
6652 }
6653
6654 /* Timeout occurred, but no data available. */
6655 return -1;
6656}
6657
6658
6659static int
6660pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
6661{
6662 int n, nread = 0;
6663 double timeout = -1.0;
6664 uint64_t start_time = 0, now = 0, timeout_ns = 0;
6665
6666 if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
6667 timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
6668 }
6669 if (timeout <= 0.0) {
6671 / 1000.0;
6672 }
6673 start_time = mg_get_current_time_ns();
6674 timeout_ns = (uint64_t)(timeout * 1.0E9);
6675
6676 while ((len > 0) && STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6677 n = pull_inner(fp, conn, buf + nread, len, timeout);
6678 if (n == -2) {
6679 if (nread == 0) {
6680 nread = -1; /* Propagate the error */
6681 }
6682 break;
6683 } else if (n == -1) {
6684 /* timeout */
6685 if (timeout >= 0.0) {
6687 if ((now - start_time) <= timeout_ns) {
6688 continue;
6689 }
6690 }
6691 break;
6692 } else if (n == 0) {
6693 break; /* No more data to read */
6694 } else {
6695 nread += n;
6696 len -= n;
6697 }
6698 }
6699
6700 return nread;
6701}
6702
6703
6704static void
6706{
6707 char buf[MG_BUF_LEN];
6708
6709 while (mg_read(conn, buf, sizeof(buf)) > 0)
6710 ;
6711}
6712
6713
6714static int
6715mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
6716{
6717 int64_t content_len, n, buffered_len, nread;
6718 int64_t len64 =
6719 (int64_t)((len > INT_MAX) ? INT_MAX : len); /* since the return value is
6720 * int, we may not read more
6721 * bytes */
6722 const char *body;
6723
6724 if (conn == NULL) {
6725 return 0;
6726 }
6727
6728 /* If Content-Length is not set for a response with body data,
6729 * we do not know in advance how much data should be read. */
6730 content_len = conn->content_len;
6731 if (content_len < 0) {
6732 /* The body data is completed when the connection is closed. */
6733 content_len = INT64_MAX;
6734 }
6735
6736 nread = 0;
6737 if (conn->consumed_content < content_len) {
6738 /* Adjust number of bytes to read. */
6739 int64_t left_to_read = content_len - conn->consumed_content;
6740 if (left_to_read < len64) {
6741 /* Do not read more than the total content length of the
6742 * request.
6743 */
6745 }
6746
6747 /* Return buffered data */
6748 buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len
6749 - conn->consumed_content;
6750 if (buffered_len > 0) {
6751 if (len64 < buffered_len) {
6753 }
6754 body = conn->buf + conn->request_len + conn->consumed_content;
6755 memcpy(buf, body, (size_t)buffered_len);
6759 buf = (char *)buf + buffered_len;
6760 }
6761
6762 /* We have returned all buffered data. Read new data from the remote
6763 * socket.
6764 */
6765 if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) {
6766 conn->consumed_content += n;
6767 nread += n;
6768 } else {
6769 nread = ((nread > 0) ? nread : n);
6770 }
6771 }
6772 return (int)nread;
6773}
6774
6775
6776/* Forward declarations */
6777static void handle_request(struct mg_connection *);
6778static void log_access(const struct mg_connection *);
6779
6780
6781/* Handle request, update statistics and call access log */
6782static void
6784{
6785#if defined(USE_SERVER_STATS)
6786 struct timespec tnow;
6787 conn->conn_state = 4; /* processing */
6788#endif
6789
6790 handle_request(conn);
6791
6792
6793#if defined(USE_SERVER_STATS)
6794 conn->conn_state = 5; /* processed */
6795
6797 conn->processing_time = mg_difftimespec(&tnow, &(conn->req_time));
6798
6799 mg_atomic_add64(&(conn->phys_ctx->total_data_read), conn->consumed_content);
6800 mg_atomic_add64(&(conn->phys_ctx->total_data_written),
6801 conn->num_bytes_sent);
6802#endif
6803
6804 DEBUG_TRACE("%s", "handle_request done");
6805
6806 if (conn->phys_ctx->callbacks.end_request != NULL) {
6807 conn->phys_ctx->callbacks.end_request(conn, conn->status_code);
6808 DEBUG_TRACE("%s", "end_request callback done");
6809 }
6810 log_access(conn);
6811}
6812
6813
6814#if defined(USE_HTTP2)
6815#if defined(NO_SSL)
6816#error "HTTP2 requires ALPN, ALPN requires SSL/TLS"
6817#endif
6818#define USE_ALPN
6819#include "http2.inl"
6820/* Not supported with HTTP/2 */
6821#define HTTP1_only \
6822 { \
6823 if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) { \
6824 http2_must_use_http1(conn); \
6825 DEBUG_TRACE("%s", "must use HTTP/1.x"); \
6826 return; \
6827 } \
6828 }
6829#else
6830#define HTTP1_only
6831#endif
6832
6833
6834CIVETWEB_API int
6835mg_read(struct mg_connection *conn, void *buf, size_t len)
6836{
6837 if (len > INT_MAX) {
6838 len = INT_MAX;
6839 }
6840
6841 if (conn == NULL) {
6842 return 0;
6843 }
6844
6845 if (conn->is_chunked) {
6846 size_t all_read = 0;
6847
6848 while (len > 0) {
6849 if (conn->is_chunked >= 3) {
6850 /* No more data left to read */
6851 return 0;
6852 }
6853 if (conn->is_chunked != 1) {
6854 /* Has error */
6855 return -1;
6856 }
6857
6858 if (conn->consumed_content != conn->content_len) {
6859 /* copy from the current chunk */
6860 int read_ret = mg_read_inner(conn, (char *)buf + all_read, len);
6861
6862 if (read_ret < 1) {
6863 /* read error */
6864 conn->is_chunked = 2;
6865 return -1;
6866 }
6867
6868 all_read += (size_t)read_ret;
6869 len -= (size_t)read_ret;
6870
6871 if (conn->consumed_content == conn->content_len) {
6872 /* Add data bytes in the current chunk have been read,
6873 * so we are expecting \r\n now. */
6874 char x[2];
6875 conn->content_len += 2;
6876 if ((mg_read_inner(conn, x, 2) != 2) || (x[0] != '\r')
6877 || (x[1] != '\n')) {
6878 /* Protocol violation */
6879 conn->is_chunked = 2;
6880 return -1;
6881 }
6882 }
6883
6884 } else {
6885 /* fetch a new chunk */
6886 size_t i;
6887 char lenbuf[64];
6888 char *end = NULL;
6889 unsigned long chunkSize = 0;
6890
6891 for (i = 0; i < (sizeof(lenbuf) - 1); i++) {
6892 conn->content_len++;
6893 if (mg_read_inner(conn, lenbuf + i, 1) != 1) {
6894 lenbuf[i] = 0;
6895 }
6896 if ((i > 0) && (lenbuf[i] == ';')) {
6897 // chunk extension --> skip chars until next CR
6898 //
6899 // RFC 2616, 3.6.1 Chunked Transfer Coding
6900 // (https://www.rfc-editor.org/rfc/rfc2616#page-25)
6901 //
6902 // chunk = chunk-size [ chunk-extension ] CRLF
6903 // chunk-data CRLF
6904 // ...
6905 // chunk-extension= *( ";" chunk-ext-name [ "="
6906 // chunk-ext-val ] )
6907 do
6908 ++conn->content_len;
6909 while (mg_read_inner(conn, lenbuf + i, 1) == 1
6910 && lenbuf[i] != '\r');
6911 }
6912 if ((i > 0) && (lenbuf[i] == '\r')
6913 && (lenbuf[i - 1] != '\r')) {
6914 continue;
6915 }
6916 if ((i > 1) && (lenbuf[i] == '\n')
6917 && (lenbuf[i - 1] == '\r')) {
6918 lenbuf[i + 1] = 0;
6919 chunkSize = strtoul(lenbuf, &end, 16);
6920 if (chunkSize == 0) {
6921 /* regular end of content */
6922 conn->is_chunked = 3;
6923 }
6924 break;
6925 }
6926 if (!isxdigit((unsigned char)lenbuf[i])) {
6927 /* illegal character for chunk length */
6928 conn->is_chunked = 2;
6929 return -1;
6930 }
6931 }
6932 if ((end == NULL) || (*end != '\r')) {
6933 /* chunksize not set correctly */
6934 conn->is_chunked = 2;
6935 return -1;
6936 }
6937 if (conn->is_chunked == 3) {
6938 /* try discarding trailer for keep-alive */
6939
6940 // We found the last chunk (length 0) including the
6941 // CRLF that terminates that chunk. Now follows a possibly
6942 // empty trailer and a final CRLF.
6943 //
6944 // see RFC 2616, 3.6.1 Chunked Transfer Coding
6945 // (https://www.rfc-editor.org/rfc/rfc2616#page-25)
6946 //
6947 // Chunked-Body = *chunk
6948 // last-chunk
6949 // trailer
6950 // CRLF
6951 // ...
6952 // last-chunk = 1*("0") [ chunk-extension ] CRLF
6953 // ...
6954 // trailer = *(entity-header CRLF)
6955
6956 int crlf_count = 2; // one CRLF already determined
6957
6958 while (crlf_count < 4 && conn->is_chunked == 3) {
6959 ++conn->content_len;
6960 if (mg_read_inner(conn, lenbuf, 1) == 1) {
6961 if ((crlf_count == 0 || crlf_count == 2)) {
6962 if (lenbuf[0] == '\r')
6963 ++crlf_count;
6964 else
6965 crlf_count = 0;
6966 } else {
6967 // previous character was a CR
6968 // --> next character must be LF
6969
6970 if (lenbuf[0] == '\n')
6971 ++crlf_count;
6972 else
6973 conn->is_chunked = 2;
6974 }
6975 } else
6976 // premature end of trailer
6977 conn->is_chunked = 2;
6978 }
6979
6980 if (conn->is_chunked == 2)
6981 return -1;
6982 else
6983 conn->is_chunked = 4;
6984
6985 break;
6986 }
6987
6988 /* append a new chunk */
6989 conn->content_len += (int64_t)chunkSize;
6990 }
6991 }
6992
6993 return (int)all_read;
6994 }
6995 return mg_read_inner(conn, buf, len);
6996}
6997
6998
6999CIVETWEB_API int
7000mg_write(struct mg_connection *conn, const void *buf, size_t len)
7001{
7002 time_t now;
7003 int n, total, allowed;
7004
7005 if (conn == NULL) {
7006 return 0;
7007 }
7008 if (len > INT_MAX) {
7009 return -1;
7010 }
7011
7012 /* Mark connection as "data sent" */
7013 conn->request_state = 10;
7014#if defined(USE_HTTP2)
7015 if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
7016 http2_data_frame_head(conn, len, 0);
7017 }
7018#endif
7019
7020 if (conn->throttle > 0) {
7021 if ((now = time(NULL)) != conn->last_throttle_time) {
7022 conn->last_throttle_time = now;
7023 conn->last_throttle_bytes = 0;
7024 }
7025 allowed = conn->throttle - conn->last_throttle_bytes;
7026 if (allowed > (int)len) {
7027 allowed = (int)len;
7028 }
7029
7030 total = push_all(conn->phys_ctx,
7031 NULL,
7032 conn->client.sock,
7033 conn->ssl,
7034 (const char *)buf,
7035 allowed);
7036
7037 if (total == allowed) {
7038
7039 buf = (const char *)buf + total;
7040 conn->last_throttle_bytes += total;
7041 while ((total < (int)len)
7042 && STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
7043 allowed = (conn->throttle > ((int)len - total))
7044 ? (int)len - total
7045 : conn->throttle;
7046
7047 n = push_all(conn->phys_ctx,
7048 NULL,
7049 conn->client.sock,
7050 conn->ssl,
7051 (const char *)buf,
7052 allowed);
7053
7054 if (n != allowed) {
7055 break;
7056 }
7057 sleep(1);
7059 conn->last_throttle_time = time(NULL);
7060 buf = (const char *)buf + n;
7061 total += n;
7062 }
7063 }
7064 } else {
7065 total = push_all(conn->phys_ctx,
7066 NULL,
7067 conn->client.sock,
7068 conn->ssl,
7069 (const char *)buf,
7070 (int)len);
7071 }
7072 if (total > 0) {
7073 conn->num_bytes_sent += total;
7074 }
7075 return total;
7076}
7077
7078
7079/* Send a chunk, if "Transfer-Encoding: chunked" is used */
7080CIVETWEB_API int
7082 const char *chunk,
7083 unsigned int chunk_len)
7084{
7085 char lenbuf[16];
7086 size_t lenbuf_len;
7087 int ret;
7088 int t;
7089
7090 /* First store the length information in a text buffer. */
7091 sprintf(lenbuf, "%x\r\n", chunk_len);
7092 lenbuf_len = strlen(lenbuf);
7093
7094 /* Then send length information, chunk and terminating \r\n. */
7095 ret = mg_write(conn, lenbuf, lenbuf_len);
7096 if (ret != (int)lenbuf_len) {
7097 return -1;
7098 }
7099 t = ret;
7100
7101 ret = mg_write(conn, chunk, chunk_len);
7102 if (ret != (int)chunk_len) {
7103 return -1;
7104 }
7105 t += ret;
7106
7107 ret = mg_write(conn, "\r\n", 2);
7108 if (ret != 2) {
7109 return -1;
7110 }
7111 t += ret;
7112
7113 return t;
7114}
7115
7116
7117#if defined(GCC_DIAGNOSTIC)
7118/* This block forwards format strings to printf implementations,
7119 * so we need to disable the format-nonliteral warning. */
7120#pragma GCC diagnostic push
7121#pragma GCC diagnostic ignored "-Wformat-nonliteral"
7122#endif
7123
7124
7125/* Alternative alloc_vprintf() for non-compliant C runtimes */
7126static int
7127alloc_vprintf2(char **buf, const char *fmt, va_list ap)
7128{
7130 size_t size = MG_BUF_LEN / 4;
7131 int len = -1;
7132
7133 *buf = NULL;
7134 while (len < 0) {
7135 if (*buf) {
7136 mg_free(*buf);
7137 }
7138
7139 size *= 4;
7140 *buf = (char *)mg_malloc(size);
7141 if (!*buf) {
7142 break;
7143 }
7144
7145 va_copy(ap_copy, ap);
7146 len = vsnprintf_impl(*buf, size - 1, fmt, ap_copy);
7147 va_end(ap_copy);
7148 (*buf)[size - 1] = 0;
7149 }
7150
7151 return len;
7152}
7153
7154
7155/* Print message to buffer. If buffer is large enough to hold the message,
7156 * return buffer. If buffer is to small, allocate large enough buffer on
7157 * heap,
7158 * and return allocated buffer. */
7159static int
7161 char *prealloc_buf,
7162 size_t prealloc_size,
7163 const char *fmt,
7164 va_list ap)
7165{
7167 int len;
7168
7169 /* Windows is not standard-compliant, and vsnprintf() returns -1 if
7170 * buffer is too small. Also, older versions of msvcrt.dll do not have
7171 * _vscprintf(). However, if size is 0, vsnprintf() behaves correctly.
7172 * Therefore, we make two passes: on first pass, get required message
7173 * length.
7174 * On second pass, actually print the message. */
7175 va_copy(ap_copy, ap);
7177 va_end(ap_copy);
7178
7179 if (len < 0) {
7180 /* C runtime is not standard compliant, vsnprintf() returned -1.
7181 * Switch to alternative code path that uses incremental
7182 * allocations.
7183 */
7184 va_copy(ap_copy, ap);
7186 va_end(ap_copy);
7187
7188 } else if ((size_t)(len) >= prealloc_size) {
7189 /* The pre-allocated buffer not large enough. */
7190 /* Allocate a new buffer. */
7191 *out_buf = (char *)mg_malloc((size_t)(len) + 1);
7192 if (!*out_buf) {
7193 /* Allocation failed. Return -1 as "out of memory" error. */
7194 return -1;
7195 }
7196 /* Buffer allocation successful. Store the string there. */
7197 va_copy(ap_copy, ap);
7199 vsnprintf_impl(*out_buf, (size_t)(len) + 1, fmt, ap_copy));
7200 va_end(ap_copy);
7201
7202 } else {
7203 /* The pre-allocated buffer is large enough.
7204 * Use it to store the string and return the address. */
7205 va_copy(ap_copy, ap);
7208 va_end(ap_copy);
7210 }
7211
7212 return len;
7213}
7214
7215
7216static int
7217alloc_printf(char **out_buf, const char *fmt, ...)
7218{
7219 va_list ap;
7220 int result;
7221
7222 va_start(ap, fmt);
7224 va_end(ap);
7225
7226 return result;
7227}
7228
7229
7230#if defined(GCC_DIAGNOSTIC)
7231/* Enable format-nonliteral warning again. */
7232#pragma GCC diagnostic pop
7233#endif
7234
7235
7236static int
7237mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap)
7238{
7239 char mem[MG_BUF_LEN];
7240 char *buf = NULL;
7241 int len;
7242
7243 if ((len = alloc_vprintf(&buf, mem, sizeof(mem), fmt, ap)) > 0) {
7244 len = mg_write(conn, buf, (size_t)len);
7245 }
7246 if (buf != mem) {
7247 mg_free(buf);
7248 }
7249
7250 return len;
7251}
7252
7253
7254CIVETWEB_API int
7255mg_printf(struct mg_connection *conn, const char *fmt, ...)
7256{
7257 va_list ap;
7258 int result;
7259
7260 va_start(ap, fmt);
7261 result = mg_vprintf(conn, fmt, ap);
7262 va_end(ap);
7263
7264 return result;
7265}
7266
7267
7268CIVETWEB_API int
7269mg_url_decode(const char *src,
7270 int src_len,
7271 char *dst,
7272 int dst_len,
7274{
7275 int i, j, a, b;
7276#define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W'))
7277
7278 for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) {
7279 if ((i < src_len - 2) && (src[i] == '%')
7280 && isxdigit((unsigned char)src[i + 1])
7281 && isxdigit((unsigned char)src[i + 2])) {
7282 a = tolower((unsigned char)src[i + 1]);
7283 b = tolower((unsigned char)src[i + 2]);
7284 dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b));
7285 i += 2;
7286 } else if (is_form_url_encoded && (src[i] == '+')) {
7287 dst[j] = ' ';
7288 } else {
7289 dst[j] = src[i];
7290 }
7291 }
7292
7293 dst[j] = '\0'; /* Null-terminate the destination */
7294
7295 return (i >= src_len) ? j : -1;
7296}
7297
7298
7299/* form url decoding of an entire string */
7300static void
7302{
7303 int len = (int)strlen(buf);
7304 (void)mg_url_decode(buf, len, buf, len + 1, 1);
7305}
7306
7307
7308CIVETWEB_API int
7309mg_get_var(const char *data,
7310 size_t data_len,
7311 const char *name,
7312 char *dst,
7313 size_t dst_len)
7314{
7315 return mg_get_var2(data, data_len, name, dst, dst_len, 0);
7316}
7317
7318
7319CIVETWEB_API int
7320mg_get_var2(const char *data,
7321 size_t data_len,
7322 const char *name,
7323 char *dst,
7324 size_t dst_len,
7325 size_t occurrence)
7326{
7327 const char *p, *e, *s;
7328 size_t name_len;
7329 int len;
7330
7331 if ((dst == NULL) || (dst_len == 0)) {
7332 len = -2;
7333 } else if ((data == NULL) || (name == NULL) || (data_len == 0)) {
7334 len = -1;
7335 dst[0] = '\0';
7336 } else {
7337 name_len = strlen(name);
7338 e = data + data_len;
7339 len = -1;
7340 dst[0] = '\0';
7341
7342 /* data is "var1=val1&var2=val2...". Find variable first */
7343 for (p = data; p + name_len < e; p++) {
7344 if (((p == data) || (p[-1] == '&')) && (p[name_len] == '=')
7345 && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
7346 /* Point p to variable value */
7347 p += name_len + 1;
7348
7349 /* Point s to the end of the value */
7350 s = (const char *)memchr(p, '&', (size_t)(e - p));
7351 if (s == NULL) {
7352 s = e;
7353 }
7354 DEBUG_ASSERT(s >= p);
7355 if (s < p) {
7356 return -3;
7357 }
7358
7359 /* Decode variable into destination buffer */
7360 len = mg_url_decode(p, (int)(s - p), dst, (int)dst_len, 1);
7361
7362 /* Redirect error code from -1 to -2 (destination buffer too
7363 * small). */
7364 if (len == -1) {
7365 len = -2;
7366 }
7367 break;
7368 }
7369 }
7370 }
7371
7372 return len;
7373}
7374
7375
7376/* split a string "key1=val1&key2=val2" into key/value pairs */
7377CIVETWEB_API int
7379 struct mg_header *form_fields,
7380 unsigned num_form_fields)
7381{
7382 char *b;
7383 int i;
7384 int num = 0;
7385
7386 if (data == NULL) {
7387 /* parameter error */
7388 return -1;
7389 }
7390
7391 if ((form_fields == NULL) && (num_form_fields == 0)) {
7392 /* determine the number of expected fields */
7393 if (data[0] == 0) {
7394 return 0;
7395 }
7396 /* count number of & to return the number of key-value-pairs */
7397 num = 1;
7398 while (*data) {
7399 if (*data == '&') {
7400 num++;
7401 }
7402 data++;
7403 }
7404 return num;
7405 }
7406
7407 if ((form_fields == NULL) || ((int)num_form_fields <= 0)) {
7408 /* parameter error */
7409 return -1;
7410 }
7411
7412 for (i = 0; i < (int)num_form_fields; i++) {
7413 /* extract key-value pairs from input data */
7414 while ((*data == ' ') || (*data == '\t')) {
7415 /* skip initial spaces */
7416 data++;
7417 }
7418 if (*data == 0) {
7419 /* end of string reached */
7420 break;
7421 }
7422 form_fields[num].name = data;
7423
7424 /* find & or = */
7425 b = data;
7426 while ((*b != 0) && (*b != '&') && (*b != '=')) {
7427 b++;
7428 }
7429
7430 if (*b == 0) {
7431 /* last key without value */
7432 form_fields[num].value = NULL;
7433 } else if (*b == '&') {
7434 /* mid key without value */
7435 form_fields[num].value = NULL;
7436 } else {
7437 /* terminate string */
7438 *b = 0;
7439 /* value starts after '=' */
7440 data = b + 1;
7441 form_fields[num].value = data;
7442 }
7443
7444 /* new field is stored */
7445 num++;
7446
7447 /* find a next key */
7448 b = strchr(data, '&');
7449 if (b == 0) {
7450 /* no more data */
7451 break;
7452 } else {
7453 /* terminate value of last field at '&' */
7454 *b = 0;
7455 /* next key-value-pairs starts after '&' */
7456 data = b + 1;
7457 }
7458 }
7459
7460 /* Decode all values */
7461 for (i = 0; i < num; i++) {
7462 if (form_fields[i].name) {
7464 }
7465 if (form_fields[i].value) {
7467 }
7468 }
7469
7470 /* return number of fields found */
7471 return num;
7472}
7473
7474
7475/* HCP24: some changes to compare whole var_name */
7476CIVETWEB_API int
7478 const char *var_name,
7479 char *dst,
7480 size_t dst_size)
7481{
7482 const char *s, *p, *end;
7483 int name_len, len = -1;
7484
7485 if ((dst == NULL) || (dst_size == 0)) {
7486 return -2;
7487 }
7488
7489 dst[0] = '\0';
7490 if ((var_name == NULL) || ((s = cookie_header) == NULL)) {
7491 return -1;
7492 }
7493
7495 end = s + strlen(s);
7496 for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) {
7497 if (s[name_len] == '=') {
7498 /* HCP24: now check is it a substring or a full cookie name */
7499 if ((s == cookie_header) || (s[-1] == ' ')) {
7500 s += name_len + 1;
7501 if ((p = strchr(s, ' ')) == NULL) {
7502 p = end;
7503 }
7504 if (p[-1] == ';') {
7505 p--;
7506 }
7507 if ((*s == '"') && (p[-1] == '"') && (p > s + 1)) {
7508 s++;
7509 p--;
7510 }
7511 if ((size_t)(p - s) < dst_size) {
7512 len = (int)(p - s);
7513 mg_strlcpy(dst, s, (size_t)len + 1);
7514 } else {
7515 len = -3;
7516 }
7517 break;
7518 }
7519 }
7520 }
7521 return len;
7522}
7523
7524
7525CIVETWEB_API int
7526mg_base64_encode(const unsigned char *src,
7527 size_t src_len,
7528 char *dst,
7529 size_t *dst_len)
7530{
7531 static const char *b64 =
7532 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
7533 size_t i, j;
7534 int a, b, c;
7535
7536 if (dst_len != NULL) {
7537 /* Expected length including 0 termination: */
7538 /* IN 1 -> OUT 5, IN 2 -> OUT 5, IN 3 -> OUT 5, IN 4 -> OUT 9,
7539 * IN 5 -> OUT 9, IN 6 -> OUT 9, IN 7 -> OUT 13, etc. */
7540 size_t expected_len = ((src_len + 2) / 3) * 4 + 1;
7541 if (*dst_len < expected_len) {
7542 if (*dst_len > 0) {
7543 dst[0] = '\0';
7544 }
7546 return 0;
7547 }
7548 }
7549
7550 for (i = j = 0; i < src_len; i += 3) {
7551 a = src[i];
7552 b = ((i + 1) >= src_len) ? 0 : src[i + 1];
7553 c = ((i + 2) >= src_len) ? 0 : src[i + 2];
7554
7555 dst[j++] = b64[a >> 2];
7556 dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
7557 if (i + 1 < src_len) {
7558 dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
7559 }
7560 if (i + 2 < src_len) {
7561 dst[j++] = b64[c & 63];
7562 }
7563 }
7564 while (j % 4 != 0) {
7565 dst[j++] = '=';
7566 }
7567 dst[j++] = '\0';
7568
7569 if (dst_len != NULL) {
7570 *dst_len = (size_t)j;
7571 }
7572
7573 /* Return -1 for "OK" */
7574 return -1;
7575}
7576
7577
7578static unsigned char
7580{
7581 if ((letter >= 'A') && (letter <= 'Z')) {
7582 return (unsigned char)(letter - 'A');
7583 }
7584 if ((letter >= 'a') && (letter <= 'z')) {
7585 return (unsigned char)(letter - 'a' + 26);
7586 }
7587 if ((letter >= '0') && (letter <= '9')) {
7588 return (unsigned char)(letter - '0' + 52);
7589 }
7590 if (letter == '+') {
7591 return 62;
7592 }
7593 if (letter == '/') {
7594 return 63;
7595 }
7596 if (letter == '=') {
7597 return 255; /* normal end */
7598 }
7599 return 254; /* error */
7600}
7601
7602
7603CIVETWEB_API int
7605 size_t src_len,
7606 unsigned char *dst,
7607 size_t *dst_len)
7608{
7609 size_t i;
7610 unsigned char a, b, c, d;
7611 size_t dst_len_limit = (size_t)-1;
7612 size_t dst_len_used = 0;
7613
7614 if (dst_len != NULL) {
7616 *dst_len = 0;
7617 }
7618
7619 for (i = 0; i < src_len; i += 4) {
7620 /* Read 4 characters from BASE64 string */
7621 a = b64reverse(src[i]);
7622 if (a >= 254) {
7623 return (int)i;
7624 }
7625
7626 b = b64reverse(((i + 1) >= src_len) ? 0 : src[i + 1]);
7627 if (b >= 254) {
7628 return (int)i + 1;
7629 }
7630
7631 c = b64reverse(((i + 2) >= src_len) ? 0 : src[i + 2]);
7632 if (c == 254) {
7633 return (int)i + 2;
7634 }
7635
7636 d = b64reverse(((i + 3) >= src_len) ? 0 : src[i + 3]);
7637 if (d == 254) {
7638 return (int)i + 3;
7639 }
7640
7641 /* Add first (of 3) decoded character */
7643 dst[dst_len_used] = (unsigned char)((unsigned char)(a << 2)
7644 + (unsigned char)(b >> 4));
7645 }
7646 dst_len_used++;
7647
7648 if (c != 255) {
7650
7651 dst[dst_len_used] = (unsigned char)((unsigned char)(b << 4)
7652 + (unsigned char)(c >> 2));
7653 }
7654 dst_len_used++;
7655 if (d != 255) {
7657 dst[dst_len_used] =
7658 (unsigned char)((unsigned char)(c << 6) + d);
7659 }
7660 dst_len_used++;
7661 }
7662 }
7663 }
7664
7665 /* Add terminating zero */
7667 dst[dst_len_used] = '\0';
7668 }
7669 dst_len_used++;
7670 if (dst_len != NULL) {
7672 }
7673
7675 /* Out of memory */
7676 return 0;
7677 }
7678
7679 /* Return -1 for "OK" */
7680 return -1;
7681}
7682
7683
7684static int
7686{
7687 if (conn) {
7688 const char *s = conn->request_info.request_method;
7689 if (s != NULL) {
7690 /* PUT, DELETE, MKCOL, PATCH, LOCK, UNLOCK, PROPPATCH, MOVE, COPY */
7691 return (!strcmp(s, "PUT") || !strcmp(s, "DELETE")
7692 || !strcmp(s, "MKCOL") || !strcmp(s, "PATCH")
7693 || !strcmp(s, "LOCK") || !strcmp(s, "UNLOCK")
7694 || !strcmp(s, "PROPPATCH") || !strcmp(s, "MOVE")
7695 || !strcmp(s, "COPY"));
7696 }
7697 }
7698 return 0;
7699}
7700
7701
7702static int
7704{
7705 /* Note: Here we only have to identify the WebDav methods that need special
7706 * handling in the CivetWeb code - not all methods used in WebDav. In
7707 * particular, methods used on directories (when using Windows Explorer as
7708 * WebDav client).
7709 */
7710 if (conn) {
7711 const char *s = conn->request_info.request_method;
7712 if (s != NULL) {
7713 /* These are the civetweb builtin DAV methods */
7714 return (!strcmp(s, "PROPFIND") || !strcmp(s, "PROPPATCH")
7715 || !strcmp(s, "LOCK") || !strcmp(s, "UNLOCK")
7716 || !strcmp(s, "MOVE") || !strcmp(s, "COPY"));
7717 }
7718 }
7719 return 0;
7720}
7721
7722
7723#if !defined(NO_FILES)
7724static int
7726 struct mg_connection *conn, /* in: request (must be valid) */
7727 const char *filename /* in: filename (must be valid) */
7728)
7729{
7730#if !defined(NO_CGI)
7731 int cgi_config_idx, inc, max;
7732#endif
7733
7734#if defined(USE_LUA)
7736 filename)
7737 > 0) {
7738 return 1;
7739 }
7740#endif
7741#if defined(USE_DUKTAPE)
7743 filename)
7744 > 0) {
7745 return 1;
7746 }
7747#endif
7748#if !defined(NO_CGI)
7751 for (cgi_config_idx = 0; cgi_config_idx < max; cgi_config_idx += inc) {
7752 if ((conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx] != NULL)
7754 conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx],
7755 filename)
7756 > 0)) {
7757 return 1;
7758 }
7759 }
7760#endif
7761 /* filename and conn could be unused, if all preocessor conditions
7762 * are false (no script language supported). */
7763 (void)filename;
7764 (void)conn;
7765
7766 return 0;
7767}
7768
7769
7770static int
7772 struct mg_connection *conn, /* in: request (must be valid) */
7773 const char *filename /* in: filename (must be valid) */
7774)
7775{
7776#if defined(USE_LUA)
7778 filename)
7779 > 0) {
7780 return 1;
7781 }
7782#endif
7784 > 0) {
7785 return 1;
7786 }
7787 return 0;
7788}
7789
7790
7791/* For given directory path, substitute it to valid index file.
7792 * Return 1 if index file has been found, 0 if not found.
7793 * If the file is found, it's stats is returned in stp. */
7794static int
7796 char *path,
7797 size_t path_len,
7798 struct mg_file_stat *filestat)
7799{
7800 const char *list = conn->dom_ctx->config[INDEX_FILES];
7801 struct vec filename_vec;
7802 size_t n = strlen(path);
7803 int found = 0;
7804
7805 /* The 'path' given to us points to the directory. Remove all trailing
7806 * directory separator characters from the end of the path, and
7807 * then append single directory separator character. */
7808 while ((n > 0) && (path[n - 1] == '/')) {
7809 n--;
7810 }
7811 path[n] = '/';
7812
7813 /* Traverse index files list. For each entry, append it to the given
7814 * path and see if the file exists. If it exists, break the loop */
7815 while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
7816 /* Ignore too long entries that may overflow path buffer */
7817 if ((filename_vec.len + 1) > (path_len - (n + 1))) {
7818 continue;
7819 }
7820
7821 /* Prepare full path to the index file */
7822 mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
7823
7824 /* Does it exist? */
7825 if (mg_stat(conn, path, filestat)) {
7826 /* Yes it does, break the loop */
7827 found = 1;
7828 break;
7829 }
7830 }
7831
7832 /* If no index file exists, restore directory path */
7833 if (!found) {
7834 path[n] = '\0';
7835 }
7836
7837 return found;
7838}
7839
7840/* Same as above, except if the first try fails and a fallback-root is
7841 * configured, we'll try there also */
7842static int
7844 char *path,
7845 size_t path_len,
7846 struct mg_file_stat *filestat)
7847{
7848 int ret = substitute_index_file_aux(conn, path, path_len, filestat);
7849 if (ret == 0) {
7850 const char *root_prefix = conn->dom_ctx->config[DOCUMENT_ROOT];
7851 const char *fallback_root_prefix =
7852 conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT];
7853 if ((root_prefix) && (fallback_root_prefix)) {
7854 const size_t root_prefix_len = strlen(root_prefix);
7855 if ((strncmp(path, root_prefix, root_prefix_len) == 0)) {
7856 char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid
7857 side effects if we fail */
7858 size_t sub_path_len;
7859
7860 const size_t fallback_root_prefix_len =
7862 const char *sub_path = path + root_prefix_len;
7863 while (*sub_path == '/') {
7864 sub_path++;
7865 }
7867
7868 if (((fallback_root_prefix_len + 1 + sub_path_len + 1)
7869 < sizeof(scratch_path))) {
7870 /* The concatenations below are all safe because we
7871 * pre-verified string lengths above */
7872 char *nul;
7874 nul = strchr(scratch_path, '\0');
7875 if ((nul > scratch_path) && (*(nul - 1) != '/')) {
7876 *nul++ = '/';
7877 *nul = '\0';
7878 }
7882 sizeof(scratch_path),
7883 filestat)) {
7885 return 1;
7886 }
7887 }
7888 }
7889 }
7890 }
7891 return ret;
7892}
7893
7894#endif
7895
7896
7897static void
7898interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
7899 char *filename, /* out: filename */
7900 size_t filename_buf_len, /* in: size of filename buffer */
7901 struct mg_file_stat *filestat, /* out: file status structure */
7902 int *is_found, /* out: file found (directly) */
7903 int *is_script_resource, /* out: handled by a script? */
7904 int *is_websocket_request, /* out: websocket connection? */
7905 int *is_put_or_delete_request, /* out: put/delete a file? */
7906 int *is_webdav_request, /* out: webdav request? */
7907 int *is_template_text /* out: SSI file or LSP file? */
7908)
7909{
7910 char const *accept_encoding;
7911
7912#if !defined(NO_FILES)
7913 const char *uri = conn->request_info.local_uri;
7914 const char *roots[] = {conn->dom_ctx->config[DOCUMENT_ROOT],
7915 conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT],
7916 NULL};
7917 int fileExists = 0;
7918 const char *rewrite;
7919 struct vec a, b;
7920 ptrdiff_t match_len;
7921 char gz_path[UTF8_PATH_MAX];
7922 int truncated;
7923 int i;
7924#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
7925 char *tmp_str;
7926 size_t tmp_str_len, sep_pos;
7928#endif
7929#else
7930 (void)filename_buf_len; /* unused if NO_FILES is defined */
7931#endif
7932
7933 /* Step 1: Set all initially unknown outputs to zero */
7934 memset(filestat, 0, sizeof(*filestat));
7935 *filename = 0;
7936 *is_found = 0;
7937 *is_script_resource = 0;
7938 *is_template_text = 0;
7939
7940 /* Step 2: Classify the request method */
7941 /* Step 2a: Check if the request attempts to modify the file system */
7943 /* Step 2b: Check if the request uses WebDav method that requires special
7944 * handling */
7946
7947 /* Step 3: Check if it is a websocket request, and modify the document
7948 * root if required */
7949#if defined(USE_WEBSOCKET)
7951#if !defined(NO_FILES)
7952 if ((*is_websocket_request) && conn->dom_ctx->config[WEBSOCKET_ROOT]) {
7953 roots[0] = conn->dom_ctx->config[WEBSOCKET_ROOT];
7954 roots[1] = conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT];
7955 }
7956#endif /* !NO_FILES */
7957#else /* USE_WEBSOCKET */
7959#endif /* USE_WEBSOCKET */
7960
7961 /* Step 4: Check if gzip encoded response is allowed */
7962 conn->accept_gzip = 0;
7963 if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) {
7964 if (strstr(accept_encoding, "gzip") != NULL) {
7965 conn->accept_gzip = 1;
7966 }
7967 }
7968
7969#if !defined(NO_FILES)
7970 /* Step 5: If there is no root directory, don't look for files. */
7971 /* Note that roots[0] == NULL is a regular use case here. This occurs,
7972 * if all requests are handled by callbacks, so the WEBSOCKET_ROOT
7973 * config is not required. */
7974 if (roots[0] == NULL) {
7975 /* all file related outputs have already been set to 0, just return
7976 */
7977 return;
7978 }
7979
7980 for (i = 0; roots[i] != NULL; i++) {
7981 /* Step 6: Determine the local file path from the root path and the
7982 * request uri. */
7983 /* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift
7984 * part of the path one byte on the right. */
7985 truncated = 0;
7986 mg_snprintf(conn,
7987 &truncated,
7988 filename,
7989 filename_buf_len - 1,
7990 "%s%s",
7991 roots[i],
7992 uri);
7993
7994 if (truncated) {
7995 goto interpret_cleanup;
7996 }
7997
7998 /* Step 7: URI rewriting */
7999 rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN];
8000 while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
8001 if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
8002 mg_snprintf(conn,
8003 &truncated,
8004 filename,
8005 filename_buf_len - 1,
8006 "%.*s%s",
8007 (int)b.len,
8008 b.ptr,
8009 uri + match_len);
8010 break;
8011 }
8012 }
8013
8014 if (truncated) {
8015 goto interpret_cleanup;
8016 }
8017
8018 /* Step 8: Check if the file exists at the server */
8019 /* Local file path and name, corresponding to requested URI
8020 * is now stored in "filename" variable. */
8021 if (mg_stat(conn, filename, filestat)) {
8022 fileExists = 1;
8023 break;
8024 }
8025 }
8026
8027 if (fileExists) {
8028 int uri_len = (int)strlen(uri);
8029 int is_uri_end_slash = (uri_len > 0) && (uri[uri_len - 1] == '/');
8030
8031 /* 8.1: File exists. */
8032 *is_found = 1;
8033
8034 /* 8.2: Check if it is a script type. */
8036 /* The request addresses a CGI resource, Lua script or
8037 * server-side javascript.
8038 * The URI corresponds to the script itself (like
8039 * /path/script.cgi), and there is no additional resource
8040 * path (like /path/script.cgi/something).
8041 * Requests that modify (replace or delete) a resource, like
8042 * PUT and DELETE requests, should replace/delete the script
8043 * file.
8044 * Requests that read or write from/to a resource, like GET and
8045 * POST requests, should call the script and return the
8046 * generated response. */
8048 }
8049
8050 /* 8.3: Check for SSI and LSP files */
8052 /* Same as above, but for *.lsp and *.shtml files. */
8053 /* A "template text" is a file delivered directly to the client,
8054 * but with some text tags replaced by dynamic content.
8055 * E.g. a Server Side Include (SSI) or Lua Page/Lua Server Page
8056 * (LP, LSP) file. */
8058 }
8059
8060 /* 8.4: If the request target is a directory, there could be
8061 * a substitute file (index.html, index.cgi, ...). */
8062 /* But do not substitute a directory for a WebDav request */
8063 if (filestat->is_directory && is_uri_end_slash
8064 && (!*is_webdav_request)) {
8065 /* Use a local copy here, since substitute_index_file will
8066 * change the content of the file status */
8068 memset(&tmp_filestat, 0, sizeof(tmp_filestat));
8069
8072
8073 /* Substitute file found. Copy stat to the output, then
8074 * check if the file is a script file */
8076
8078 /* Substitute file is a script file */
8079 *is_script_resource = 1;
8080 } else if (extention_matches_template_text(conn, filename)) {
8081 /* Substitute file is a LSP or SSI file */
8082 *is_template_text = 1;
8083 } else {
8084 /* Substitute file is a regular file */
8085 *is_script_resource = 0;
8086 *is_found = (mg_stat(conn, filename, filestat) ? 1 : 0);
8087 }
8088 }
8089 /* If there is no substitute file, the server could return
8090 * a directory listing in a later step */
8091 }
8092 return;
8093 }
8094
8095 /* Step 9: Check for zipped files: */
8096 /* If we can't find the actual file, look for the file
8097 * with the same name but a .gz extension. If we find it,
8098 * use that and set the gzipped flag in the file struct
8099 * to indicate that the response need to have the content-
8100 * encoding: gzip header.
8101 * We can only do this if the browser declares support. */
8102 if (conn->accept_gzip) {
8104 conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", filename);
8105
8106 if (truncated) {
8107 goto interpret_cleanup;
8108 }
8109
8110 if (mg_stat(conn, gz_path, filestat)) {
8111 if (filestat) {
8112 filestat->is_gzipped = 1;
8113 *is_found = 1;
8114 }
8115 /* Currently gz files can not be scripts. */
8116 return;
8117 }
8118 }
8119
8120#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
8121 /* Step 10: Script resources may handle sub-resources */
8122 /* Support PATH_INFO for CGI scripts. */
8124 tmp_str =
8125 (char *)mg_malloc_ctx(tmp_str_len + UTF8_PATH_MAX + 1, conn->phys_ctx);
8126 if (!tmp_str) {
8127 /* Out of memory */
8128 goto interpret_cleanup;
8129 }
8131
8132 /* Check config, if index scripts may have sub-resources */
8135 "yes");
8136 if (*is_webdav_request) {
8137 /* TO BE DEFINED: Should scripts handle special WebDAV methods lile
8138 * PROPFIND for their subresources? */
8139 /* allow_substitute_script_subresources = 0; */
8140 }
8141
8143 while (sep_pos > 0) {
8144 sep_pos--;
8145 if (tmp_str[sep_pos] == '/') {
8146 int is_script = 0, does_exist = 0;
8147
8148 tmp_str[sep_pos] = 0;
8149 if (tmp_str[0]) {
8152 }
8153
8154 if (does_exist && is_script) {
8155 filename[sep_pos] = 0;
8156 memmove(filename + sep_pos + 2,
8157 filename + sep_pos + 1,
8158 strlen(filename + sep_pos + 1) + 1);
8159 conn->path_info = filename + sep_pos + 1;
8160 filename[sep_pos + 1] = '/';
8161 *is_script_resource = 1;
8162 *is_found = 1;
8163 break;
8164 }
8165
8169
8170 /* some intermediate directory has an index file */
8171 if (extention_matches_script(conn, tmp_str)) {
8172
8173 size_t script_name_len = strlen(tmp_str);
8174
8175 /* subres_name read before this memory locatio will be
8176 overwritten */
8177 char *subres_name = filename + sep_pos;
8179
8180 DEBUG_TRACE("Substitute script %s serving path %s",
8181 tmp_str,
8182 filename);
8183
8184 /* this index file is a script */
8186 >= filename_buf_len) {
8188 goto interpret_cleanup;
8189 }
8190
8191 conn->path_info =
8192 filename + script_name_len + 1; /* new target */
8194 conn->path_info[subres_name_len] = 0;
8196
8197 *is_script_resource = 1;
8198 *is_found = 1;
8199 break;
8200
8201 } else {
8202
8203 DEBUG_TRACE("Substitute file %s serving path %s",
8204 tmp_str,
8205 filename);
8206
8207 /* non-script files will not have sub-resources */
8208 filename[sep_pos] = 0;
8209 conn->path_info = 0;
8210 *is_script_resource = 0;
8211 *is_found = 0;
8212 break;
8213 }
8214 }
8215 }
8216
8217 tmp_str[sep_pos] = '/';
8218 }
8219 }
8220
8222
8223#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */
8224#endif /* !defined(NO_FILES) */
8225 return;
8226
8227#if !defined(NO_FILES)
8228/* Reset all outputs */
8230 memset(filestat, 0, sizeof(*filestat));
8231 *filename = 0;
8232 *is_found = 0;
8233 *is_script_resource = 0;
8236#endif /* !defined(NO_FILES) */
8237}
8238
8239
8240/* Check whether full request is buffered. Return:
8241 * -1 if request or response is malformed
8242 * 0 if request or response is not yet fully buffered
8243 * >0 actual request length, including last \r\n\r\n */
8244static int
8245get_http_header_len(const char *buf, int buflen)
8246{
8247 int i;
8248 for (i = 0; i < buflen; i++) {
8249 /* Do an unsigned comparison in some conditions below */
8250 const unsigned char c = (unsigned char)buf[i];
8251
8252 if ((c < 128) && ((char)c != '\r') && ((char)c != '\n')
8253 && !isprint(c)) {
8254 /* abort scan as soon as one malformed character is found */
8255 return -1;
8256 }
8257
8258 if (i < buflen - 1) {
8259 if ((buf[i] == '\n') && (buf[i + 1] == '\n')) {
8260 /* Two newline, no carriage return - not standard compliant,
8261 * but it should be accepted */
8262 return i + 2;
8263 }
8264 }
8265
8266 if (i < buflen - 3) {
8267 if ((buf[i] == '\r') && (buf[i + 1] == '\n') && (buf[i + 2] == '\r')
8268 && (buf[i + 3] == '\n')) {
8269 /* Two \r\n - standard compliant */
8270 return i + 4;
8271 }
8272 }
8273 }
8274
8275 return 0;
8276}
8277
8278
8279#if !defined(NO_CACHING)
8280/* Convert month to the month number. Return -1 on error, or month number */
8281static int
8282get_month_index(const char *s)
8283{
8284 size_t i;
8285
8286 for (i = 0; i < ARRAY_SIZE(month_names); i++) {
8287 if (!strcmp(s, month_names[i])) {
8288 return (int)i;
8289 }
8290 }
8291
8292 return -1;
8293}
8294
8295
8296/* Parse UTC date-time string, and return the corresponding time_t value. */
8297static time_t
8299{
8300 char month_str[32] = {0};
8301 int second, minute, hour, day, month, year;
8302 time_t result = (time_t)0;
8303 struct tm tm;
8304
8305 if ((sscanf(datetime,
8306 "%d/%3s/%d %d:%d:%d",
8307 &day,
8308 month_str,
8309 &year,
8310 &hour,
8311 &minute,
8312 &second)
8313 == 6)
8314 || (sscanf(datetime,
8315 "%d %3s %d %d:%d:%d",
8316 &day,
8317 month_str,
8318 &year,
8319 &hour,
8320 &minute,
8321 &second)
8322 == 6)
8323 || (sscanf(datetime,
8324 "%*3s, %d %3s %d %d:%d:%d",
8325 &day,
8326 month_str,
8327 &year,
8328 &hour,
8329 &minute,
8330 &second)
8331 == 6)
8332 || (sscanf(datetime,
8333 "%d-%3s-%d %d:%d:%d",
8334 &day,
8335 month_str,
8336 &year,
8337 &hour,
8338 &minute,
8339 &second)
8340 == 6)) {
8342 if ((month >= 0) && (year >= 1970)) {
8343 memset(&tm, 0, sizeof(tm));
8344 tm.tm_year = year - 1900;
8345 tm.tm_mon = month;
8346 tm.tm_mday = day;
8347 tm.tm_hour = hour;
8348 tm.tm_min = minute;
8349 tm.tm_sec = second;
8350 result = timegm(&tm);
8351 }
8352 }
8353
8354 return result;
8355}
8356#endif /* !NO_CACHING */
8357
8358
8359/* Pre-process URIs according to RFC + protect against directory disclosure
8360 * attacks by removing '..', excessive '/' and '\' characters */
8361static void
8363{
8364 /* Windows backend protection
8365 * (https://tools.ietf.org/html/rfc3986#section-7.3): Replace backslash
8366 * in URI by slash */
8367 char *out_end = inout;
8368 char *in = inout;
8369
8370 if (!in) {
8371 /* Param error. */
8372 return;
8373 }
8374
8375 while (*in) {
8376 if (*in == '\\') {
8377 *in = '/';
8378 }
8379 in++;
8380 }
8381
8382 /* Algorithm "remove_dot_segments" from
8383 * https://tools.ietf.org/html/rfc3986#section-5.2.4 */
8384 /* Step 1:
8385 * The input buffer is initialized.
8386 * The output buffer is initialized to the empty string.
8387 */
8388 in = inout;
8389
8390 /* Step 2:
8391 * While the input buffer is not empty, loop as follows:
8392 */
8393 /* Less than out_end of the inout buffer is used as output, so keep
8394 * condition: out_end <= in */
8395 while (*in) {
8396 /* Step 2a:
8397 * If the input buffer begins with a prefix of "../" or "./",
8398 * then remove that prefix from the input buffer;
8399 */
8400 if (!strncmp(in, "../", 3)) {
8401 in += 3;
8402 } else if (!strncmp(in, "./", 2)) {
8403 in += 2;
8404 }
8405 /* otherwise */
8406 /* Step 2b:
8407 * if the input buffer begins with a prefix of "/./" or "/.",
8408 * where "." is a complete path segment, then replace that
8409 * prefix with "/" in the input buffer;
8410 */
8411 else if (!strncmp(in, "/./", 3)) {
8412 in += 2;
8413 } else if (!strcmp(in, "/.")) {
8414 in[1] = 0;
8415 }
8416 /* otherwise */
8417 /* Step 2c:
8418 * if the input buffer begins with a prefix of "/../" or "/..",
8419 * where ".." is a complete path segment, then replace that
8420 * prefix with "/" in the input buffer and remove the last
8421 * segment and its preceding "/" (if any) from the output
8422 * buffer;
8423 */
8424 else if (!strncmp(in, "/../", 4)) {
8425 in += 3;
8426 if (inout != out_end) {
8427 /* remove last segment */
8428 do {
8429 out_end--;
8430 } while ((inout != out_end) && (*out_end != '/'));
8431 }
8432 } else if (!strcmp(in, "/..")) {
8433 in[1] = 0;
8434 if (inout != out_end) {
8435 /* remove last segment */
8436 do {
8437 out_end--;
8438 } while ((inout != out_end) && (*out_end != '/'));
8439 }
8440 }
8441 /* otherwise */
8442 /* Step 2d:
8443 * if the input buffer consists only of "." or "..", then remove
8444 * that from the input buffer;
8445 */
8446 else if (!strcmp(in, ".") || !strcmp(in, "..")) {
8447 *in = 0;
8448 }
8449 /* otherwise */
8450 /* Step 2e:
8451 * move the first path segment in the input buffer to the end of
8452 * the output buffer, including the initial "/" character (if
8453 * any) and any subsequent characters up to, but not including,
8454 * the next "/" character or the end of the input buffer.
8455 */
8456 else {
8457 do {
8458 *out_end = *in;
8459 out_end++;
8460 in++;
8461 } while ((*in != 0) && (*in != '/'));
8462 }
8463 }
8464
8465 /* Step 3:
8466 * Finally, the output buffer is returned as the result of
8467 * remove_dot_segments.
8468 */
8469 /* Terminate output */
8470 *out_end = 0;
8471
8472 /* For Windows, the files/folders "x" and "x." (with a dot but without
8473 * extension) are identical. Replace all "./" by "/" and remove a "." at
8474 * the end. Also replace all "//" by "/". Repeat until there is no "./"
8475 * or "//" anymore.
8476 */
8477 out_end = in = inout;
8478 while (*in) {
8479 if (*in == '.') {
8480 /* remove . at the end or preceding of / */
8481 char *in_ahead = in;
8482 do {
8483 in_ahead++;
8484 } while (*in_ahead == '.');
8485 if (*in_ahead == '/') {
8486 in = in_ahead;
8487 if ((out_end != inout) && (out_end[-1] == '/')) {
8488 /* remove generated // */
8489 out_end--;
8490 }
8491 } else if (*in_ahead == 0) {
8492 in = in_ahead;
8493 } else {
8494 do {
8495 *out_end++ = '.';
8496 in++;
8497 } while (in != in_ahead);
8498 }
8499 } else if (*in == '/') {
8500 /* replace // by / */
8501 *out_end++ = '/';
8502 do {
8503 in++;
8504 } while (*in == '/');
8505 } else {
8506 *out_end++ = *in;
8507 in++;
8508 }
8509 }
8510 *out_end = 0;
8511}
8512
8513
8514static const struct {
8515 const char *extension;
8516 size_t ext_len;
8517 const char *mime_type;
8518} builtin_mime_types[] = {
8519 /* IANA registered MIME types
8520 * (http://www.iana.org/assignments/media-types)
8521 * application types */
8522 {".bin", 4, "application/octet-stream"},
8523 {".cer", 4, "application/pkix-cert"},
8524 {".crl", 4, "application/pkix-crl"},
8525 {".crt", 4, "application/pkix-cert"},
8526 {".deb", 4, "application/octet-stream"},
8527 {".dmg", 4, "application/octet-stream"},
8528 {".dll", 4, "application/octet-stream"},
8529 {".doc", 4, "application/msword"},
8530 {".eps", 4, "application/postscript"},
8531 {".exe", 4, "application/octet-stream"},
8532 {".iso", 4, "application/octet-stream"},
8533 {".js", 3, "application/javascript"},
8534 {".json", 5, "application/json"},
8535 {".mjs", 4, "application/javascript"},
8536 {".msi", 4, "application/octet-stream"},
8537 {".pem", 4, "application/x-pem-file"},
8538 {".pdf", 4, "application/pdf"},
8539 {".ps", 3, "application/postscript"},
8540 {".rtf", 4, "application/rtf"},
8541 {".wasm", 5, "application/wasm"},
8542 {".xhtml", 6, "application/xhtml+xml"},
8543 {".xsl", 4, "application/xml"},
8544 {".xslt", 5, "application/xml"},
8545
8546 /* fonts */
8547 {".ttf", 4, "application/font-sfnt"},
8548 {".cff", 4, "application/font-sfnt"},
8549 {".otf", 4, "application/font-sfnt"},
8550 {".aat", 4, "application/font-sfnt"},
8551 {".sil", 4, "application/font-sfnt"},
8552 {".pfr", 4, "application/font-tdpfr"},
8553 {".woff", 5, "application/font-woff"},
8554 {".woff2", 6, "application/font-woff2"},
8555
8556 /* audio */
8557 {".mp3", 4, "audio/mpeg"},
8558 {".oga", 4, "audio/ogg"},
8559 {".ogg", 4, "audio/ogg"},
8560
8561 /* image */
8562 {".gif", 4, "image/gif"},
8563 {".ief", 4, "image/ief"},
8564 {".jpeg", 5, "image/jpeg"},
8565 {".jpg", 4, "image/jpeg"},
8566 {".jpm", 4, "image/jpm"},
8567 {".jpx", 4, "image/jpx"},
8568 {".png", 4, "image/png"},
8569 {".svg", 4, "image/svg+xml"},
8570 {".tif", 4, "image/tiff"},
8571 {".tiff", 5, "image/tiff"},
8572
8573 /* model */
8574 {".wrl", 4, "model/vrml"},
8575
8576 /* text */
8577 {".css", 4, "text/css"},
8578 {".csv", 4, "text/csv"},
8579 {".htm", 4, "text/html"},
8580 {".html", 5, "text/html"},
8581 {".sgm", 4, "text/sgml"},
8582 {".shtm", 5, "text/html"},
8583 {".shtml", 6, "text/html"},
8584 {".txt", 4, "text/plain"},
8585 {".xml", 4, "text/xml"},
8586
8587 /* video */
8588 {".mov", 4, "video/quicktime"},
8589 {".mp4", 4, "video/mp4"},
8590 {".mpeg", 5, "video/mpeg"},
8591 {".mpg", 4, "video/mpeg"},
8592 {".ogv", 4, "video/ogg"},
8593 {".qt", 3, "video/quicktime"},
8594
8595 /* not registered types
8596 * (http://reference.sitepoint.com/html/mime-types-full,
8597 * http://www.hansenb.pdx.edu/DMKB/dict/tutorials/mime_typ.php, ..) */
8598 {".arj", 4, "application/x-arj-compressed"},
8599 {".gz", 3, "application/x-gunzip"},
8600 {".rar", 4, "application/x-arj-compressed"},
8601 {".swf", 4, "application/x-shockwave-flash"},
8602 {".tar", 4, "application/x-tar"},
8603 {".tgz", 4, "application/x-tar-gz"},
8604 {".torrent", 8, "application/x-bittorrent"},
8605 {".ppt", 4, "application/x-mspowerpoint"},
8606 {".xls", 4, "application/x-msexcel"},
8607 {".zip", 4, "application/x-zip-compressed"},
8608 {".aac",
8609 4,
8610 "audio/aac"}, /* http://en.wikipedia.org/wiki/Advanced_Audio_Coding */
8611 {".flac", 5, "audio/flac"},
8612 {".aif", 4, "audio/x-aif"},
8613 {".m3u", 4, "audio/x-mpegurl"},
8614 {".mid", 4, "audio/x-midi"},
8615 {".ra", 3, "audio/x-pn-realaudio"},
8616 {".ram", 4, "audio/x-pn-realaudio"},
8617 {".wav", 4, "audio/x-wav"},
8618 {".bmp", 4, "image/bmp"},
8619 {".ico", 4, "image/x-icon"},
8620 {".pct", 4, "image/x-pct"},
8621 {".pict", 5, "image/pict"},
8622 {".rgb", 4, "image/x-rgb"},
8623 {".webm", 5, "video/webm"}, /* http://en.wikipedia.org/wiki/WebM */
8624 {".asf", 4, "video/x-ms-asf"},
8625 {".avi", 4, "video/x-msvideo"},
8626 {".m4v", 4, "video/x-m4v"},
8627 {NULL, 0, NULL}};
8628
8629
8630CIVETWEB_API const char *
8632{
8633 const char *ext;
8634 size_t i, path_len;
8635
8636 path_len = strlen(path);
8637
8638 for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
8639 ext = path + (path_len - builtin_mime_types[i].ext_len);
8640 if ((path_len > builtin_mime_types[i].ext_len)
8641 && (mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0)) {
8642 return builtin_mime_types[i].mime_type;
8643 }
8644 }
8645
8646 return "text/plain";
8647}
8648
8649
8650/* Look at the "path" extension and figure what mime type it has.
8651 * Store mime type in the vector. */
8652static void
8653get_mime_type(struct mg_connection *conn, const char *path, struct vec *vec)
8654{
8655 struct vec ext_vec, mime_vec;
8656 const char *list, *ext;
8657 size_t path_len;
8658
8659 path_len = strlen(path);
8660
8661 if ((conn == NULL) || (vec == NULL)) {
8662 if (vec != NULL) {
8663 memset(vec, '\0', sizeof(struct vec));
8664 }
8665 return;
8666 }
8667
8668 /* Scan user-defined mime types first, in case user wants to
8669 * override default mime types. */
8670 list = conn->dom_ctx->config[EXTRA_MIME_TYPES];
8671 while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
8672 /* ext now points to the path suffix */
8673 ext = path + path_len - ext_vec.len;
8674 if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
8675 *vec = mime_vec;
8676 return;
8677 }
8678 }
8679
8681 vec->len = strlen(vec->ptr);
8682}
8683
8684
8685/* Stringify binary data. Output buffer must be twice as big as input,
8686 * because each byte takes 2 bytes in string representation */
8687static void
8688bin2str(char *to, const unsigned char *p, size_t len)
8689{
8690 static const char *hex = "0123456789abcdef";
8691
8692 for (; len--; p++) {
8693 *to++ = hex[p[0] >> 4];
8694 *to++ = hex[p[0] & 0x0f];
8695 }
8696 *to = '\0';
8697}
8698
8699
8700/* Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
8701 */
8702CIVETWEB_API char *
8703mg_md5(char buf[33], ...)
8704{
8705 md5_byte_t hash[16];
8706 const char *p;
8707 va_list ap;
8708 md5_state_t ctx;
8709
8710 md5_init(&ctx);
8711
8712 va_start(ap, buf);
8713 while ((p = va_arg(ap, const char *)) != NULL) {
8714 md5_append(&ctx, (const md5_byte_t *)p, strlen(p));
8715 }
8716 va_end(ap);
8717
8718 md5_finish(&ctx, hash);
8719 bin2str(buf, hash, sizeof(hash));
8720 return buf;
8721}
8722
8723
8724/* Check the user's password, return 1 if OK */
8725static int
8727 const char *ha1,
8728 const char *uri,
8729 const char *nonce,
8730 const char *nc,
8731 const char *cnonce,
8732 const char *qop,
8733 const char *response)
8734{
8735 char ha2[32 + 1], expected_response[32 + 1];
8736
8737 /* Some of the parameters may be NULL */
8738 if ((method == NULL) || (nonce == NULL) || (nc == NULL) || (cnonce == NULL)
8739 || (qop == NULL) || (response == NULL)) {
8740 return 0;
8741 }
8742
8743 /* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */
8744 if (strlen(response) != 32) {
8745 return 0;
8746 }
8747
8748 mg_md5(ha2, method, ":", uri, NULL);
8750 ha1,
8751 ":",
8752 nonce,
8753 ":",
8754 nc,
8755 ":",
8756 cnonce,
8757 ":",
8758 qop,
8759 ":",
8760 ha2,
8761 NULL);
8762
8763 return mg_strcasecmp(response, expected_response) == 0;
8764}
8765
8766
8767#if !defined(NO_FILESYSTEMS)
8768/* Use the global passwords file, if specified by auth_gpass option,
8769 * or search for .htpasswd in the requested directory. */
8770static void
8772 const char *path,
8773 struct mg_file *filep)
8774{
8775 if ((conn != NULL) && (conn->dom_ctx != NULL)) {
8776 char name[UTF8_PATH_MAX];
8777 const char *p, *e,
8778 *gpass = conn->dom_ctx->config[GLOBAL_PASSWORDS_FILE];
8779 int truncated;
8780
8781 if (gpass != NULL) {
8782 /* Use global passwords file */
8783 if (!mg_fopen(conn, gpass, MG_FOPEN_MODE_READ, filep)) {
8784#if defined(DEBUG)
8785 /* Use mg_cry_internal here, since gpass has been
8786 * configured. */
8787 mg_cry_internal(conn, "fopen(%s): %s", gpass, strerror(ERRNO));
8788#endif
8789 }
8790 /* Important: using local struct mg_file to test path for
8791 * is_directory flag. If filep is used, mg_stat() makes it
8792 * appear as if auth file was opened.
8793 * TODO(mid): Check if this is still required after rewriting
8794 * mg_stat */
8795 } else if (mg_stat(conn, path, &filep->stat)
8796 && filep->stat.is_directory) {
8797 mg_snprintf(conn,
8798 &truncated,
8799 name,
8800 sizeof(name),
8801 "%s/%s",
8802 path,
8804
8805 if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
8806#if defined(DEBUG)
8807 /* Don't use mg_cry_internal here, but only a trace, since
8808 * this is a typical case. It will occur for every directory
8809 * without a password file. */
8810 DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO));
8811#endif
8812 }
8813 } else {
8814 /* Try to find .htpasswd in requested directory. */
8815 for (p = path, e = p + strlen(p) - 1; e > p; e--) {
8816 if (e[0] == '/') {
8817 break;
8818 }
8819 }
8820 mg_snprintf(conn,
8821 &truncated,
8822 name,
8823 sizeof(name),
8824 "%.*s/%s",
8825 (int)(e - p),
8826 p,
8828
8829 if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
8830#if defined(DEBUG)
8831 /* Don't use mg_cry_internal here, but only a trace, since
8832 * this is a typical case. It will occur for every directory
8833 * without a password file. */
8834 DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO));
8835#endif
8836 }
8837 }
8838 }
8839}
8840#endif /* NO_FILESYSTEMS */
8841
8842
8843/* Parsed Authorization header */
8845 char *user;
8846 int type; /* 1 = basic, 2 = digest */
8847 char *plain_password; /* Basic only */
8848 char *uri, *cnonce, *response, *qop, *nc, *nonce; /* Digest only */
8849};
8850
8851
8852/* Return 1 on success. Always initializes the auth_header structure. */
8853static int
8855 char *buf,
8856 size_t buf_size,
8857 struct auth_header *auth_header)
8858{
8859 char *name, *value, *s;
8860 const char *ah;
8861 uint64_t nonce;
8862
8863 if (!auth_header || !conn) {
8864 return 0;
8865 }
8866
8867 (void)memset(auth_header, 0, sizeof(*auth_header));
8868 ah = mg_get_header(conn, "Authorization");
8869
8870 if (ah == NULL) {
8871 /* No Authorization header at all */
8872 return 0;
8873 }
8874 if (0 == mg_strncasecmp(ah, "Basic ", 6)) {
8875 /* Basic Auth (we never asked for this, but some client may send it) */
8876 char *split;
8877 const char *userpw_b64 = ah + 6;
8879 size_t buf_len_r = buf_size;
8880 if (mg_base64_decode(
8881 userpw_b64, userpw_b64_len, (unsigned char *)buf, &buf_len_r)
8882 != -1) {
8883 return 0; /* decode error */
8884 }
8885 split = strchr(buf, ':');
8886 if (!split) {
8887 return 0; /* Format error */
8888 }
8889
8890 /* Separate string at ':' */
8891 *split = 0;
8892
8893 /* User name is before ':', Password is after ':' */
8894 auth_header->user = buf;
8895 auth_header->type = 1;
8896 auth_header->plain_password = split + 1;
8897
8898 return 1;
8899
8900 } else if (0 == mg_strncasecmp(ah, "Digest ", 7)) {
8901 /* Digest Auth ... implemented below */
8902 auth_header->type = 2;
8903
8904 } else {
8905 /* Unknown or invalid Auth method */
8906 return 0;
8907 }
8908
8909 /* Make modifiable copy of the auth header */
8910 (void)mg_strlcpy(buf, ah + 7, buf_size);
8911 s = buf;
8912
8913 /* Parse authorization header */
8914 for (;;) {
8915 /* Gobble initial spaces */
8916 while (isspace((unsigned char)*s)) {
8917 s++;
8918 }
8919 name = skip_quoted(&s, "=", " ", 0);
8920 /* Value is either quote-delimited, or ends at first comma or space.
8921 */
8922 if (s[0] == '\"') {
8923 s++;
8924 value = skip_quoted(&s, "\"", " ", '\\');
8925 if (s[0] == ',') {
8926 s++;
8927 }
8928 } else {
8929 value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF
8930 * uses spaces */
8931 }
8932 if (*name == '\0') {
8933 break;
8934 }
8935
8936 if (!strcmp(name, "username")) {
8938 } else if (!strcmp(name, "cnonce")) {
8940 } else if (!strcmp(name, "response")) {
8942 } else if (!strcmp(name, "uri")) {
8944 } else if (!strcmp(name, "qop")) {
8946 } else if (!strcmp(name, "nc")) {
8947 auth_header->nc = value;
8948 } else if (!strcmp(name, "nonce")) {
8950 }
8951 }
8952
8953#if !defined(NO_NONCE_CHECK)
8954 /* Read the nonce from the response. */
8955 if (auth_header->nonce == NULL) {
8956 return 0;
8957 }
8958 s = NULL;
8959 nonce = strtoull(auth_header->nonce, &s, 10);
8960 if ((s == NULL) || (*s != 0)) {
8961 return 0;
8962 }
8963
8964 /* Convert the nonce from the client to a number. */
8965 nonce ^= conn->dom_ctx->auth_nonce_mask;
8966
8967 /* The converted number corresponds to the time the nounce has been
8968 * created. This should not be earlier than the server start. */
8969 /* Server side nonce check is valuable in all situations but one:
8970 * if the server restarts frequently, but the client should not see
8971 * that, so the server should accept nonces from previous starts. */
8972 /* However, the reasonable default is to not accept a nonce from a
8973 * previous start, so if anyone changed the access rights between
8974 * two restarts, a new login is required. */
8975 if (nonce < (uint64_t)conn->phys_ctx->start_time) {
8976 /* nonce is from a previous start of the server and no longer valid
8977 * (replay attack?) */
8978 return 0;
8979 }
8980 /* Check if the nonce is too high, so it has not (yet) been used by the
8981 * server. */
8982 if (nonce >= ((uint64_t)conn->phys_ctx->start_time
8983 + conn->dom_ctx->nonce_count)) {
8984 return 0;
8985 }
8986#else
8987 (void)nonce;
8988#endif
8989
8990 return (auth_header->user != NULL);
8991}
8992
8993
8994static const char *
8995mg_fgets(char *buf, size_t size, struct mg_file *filep)
8996{
8997 if (!filep) {
8998 return NULL;
8999 }
9000
9001 if (filep->access.fp != NULL) {
9002 return fgets(buf, (int)size, filep->access.fp);
9003 } else {
9004 return NULL;
9005 }
9006}
9007
9008/* Define the initial recursion depth for procesesing htpasswd files that
9009 * include other htpasswd
9010 * (or even the same) files. It is not difficult to provide a file or files
9011 * s.t. they force civetweb
9012 * to infinitely recurse and then crash.
9013 */
9014#define INITIAL_DEPTH 9
9015#if INITIAL_DEPTH <= 0
9016#error Bad INITIAL_DEPTH for recursion, set to at least 1
9017#endif
9018
9019#if !defined(NO_FILESYSTEMS)
9023 const char *domain;
9024 char buf[256 + 256 + 40];
9025 const char *f_user;
9026 const char *f_domain;
9027 const char *f_ha1;
9028};
9029
9030
9031static int
9034 int depth)
9035{
9036 int is_authorized = 0;
9037 struct mg_file fp;
9038 size_t l;
9039
9040 if (!filep || !workdata || (0 == depth)) {
9041 return 0;
9042 }
9043
9044 /* Loop over passwords file */
9045 while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep) != NULL) {
9046 l = strlen(workdata->buf);
9047 while (l > 0) {
9048 if (isspace((unsigned char)workdata->buf[l - 1])
9049 || iscntrl((unsigned char)workdata->buf[l - 1])) {
9050 l--;
9051 workdata->buf[l] = 0;
9052 } else
9053 break;
9054 }
9055 if (l < 1) {
9056 continue;
9057 }
9058
9059 workdata->f_user = workdata->buf;
9060
9061 if (workdata->f_user[0] == ':') {
9062 /* user names may not contain a ':' and may not be empty,
9063 * so lines starting with ':' may be used for a special purpose
9064 */
9065 if (workdata->f_user[1] == '#') {
9066 /* :# is a comment */
9067 continue;
9068 } else if (!strncmp(workdata->f_user + 1, "include=", 8)) {
9069 if (mg_fopen(workdata->conn,
9070 workdata->f_user + 9,
9072 &fp)) {
9074 (void)mg_fclose(
9075 &fp.access); /* ignore error on read only file */
9076
9077 /* No need to continue processing files once we have a
9078 * match, since nothing will reset it back
9079 * to 0.
9080 */
9081 if (is_authorized) {
9082 return is_authorized;
9083 }
9084 } else {
9086 "%s: cannot open authorization file: %s",
9087 __func__,
9088 workdata->buf);
9089 }
9090 continue;
9091 }
9092 /* everything is invalid for the moment (might change in the
9093 * future) */
9095 "%s: syntax error in authorization file: %s",
9096 __func__,
9097 workdata->buf);
9098 continue;
9099 }
9100
9101 workdata->f_domain = strchr(workdata->f_user, ':');
9102 if (workdata->f_domain == NULL) {
9104 "%s: syntax error in authorization file: %s",
9105 __func__,
9106 workdata->buf);
9107 continue;
9108 }
9109 *(char *)(workdata->f_domain) = 0;
9110 (workdata->f_domain)++;
9111
9112 workdata->f_ha1 = strchr(workdata->f_domain, ':');
9113 if (workdata->f_ha1 == NULL) {
9115 "%s: syntax error in authorization file: %s",
9116 __func__,
9117 workdata->buf);
9118 continue;
9119 }
9120 *(char *)(workdata->f_ha1) = 0;
9121 (workdata->f_ha1)++;
9122
9123 if (!strcmp(workdata->auth_header.user, workdata->f_user)
9124 && !strcmp(workdata->domain, workdata->f_domain)) {
9125 switch (workdata->auth_header.type) {
9126 case 1: /* Basic */
9127 {
9128 char md5[33];
9129 mg_md5(md5,
9130 workdata->f_user,
9131 ":",
9132 workdata->domain,
9133 ":",
9134 workdata->auth_header.plain_password,
9135 NULL);
9136 return 0 == memcmp(workdata->f_ha1, md5, 33);
9137 }
9138 case 2: /* Digest */
9139 return check_password_digest(
9140 workdata->conn->request_info.request_method,
9141 workdata->f_ha1,
9142 workdata->auth_header.uri,
9143 workdata->auth_header.nonce,
9144 workdata->auth_header.nc,
9145 workdata->auth_header.cnonce,
9146 workdata->auth_header.qop,
9147 workdata->auth_header.response);
9148 default: /* None/Other/Unknown */
9149 return 0;
9150 }
9151 }
9152 }
9153
9154 return is_authorized;
9155}
9156
9157
9158/* Authorize against the opened passwords file. Return 1 if authorized. */
9159static int
9160authorize(struct mg_connection *conn, struct mg_file *filep, const char *realm)
9161{
9163 char buf[MG_BUF_LEN];
9164
9165 if (!conn || !conn->dom_ctx) {
9166 return 0;
9167 }
9168
9169 memset(&workdata, 0, sizeof(workdata));
9170 workdata.conn = conn;
9171
9172 if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.auth_header)) {
9173 return 0;
9174 }
9175
9176 /* CGI needs it as REMOTE_USER */
9177 conn->request_info.remote_user =
9178 mg_strdup_ctx(workdata.auth_header.user, conn->phys_ctx);
9179
9180 if (realm) {
9181 workdata.domain = realm;
9182 } else {
9183 workdata.domain = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
9184 }
9185
9187}
9188
9189
9190/* Public function to check http digest authentication header */
9191CIVETWEB_API int
9193 const char *realm,
9194 const char *filename)
9195{
9196 struct mg_file file = STRUCT_FILE_INITIALIZER;
9197 int auth;
9198
9199 if (!conn || !filename) {
9200 return -1;
9201 }
9202 if (!mg_fopen(conn, filename, MG_FOPEN_MODE_READ, &file)) {
9203 return -2;
9204 }
9205
9206 auth = authorize(conn, &file, realm);
9207
9208 mg_fclose(&file.access);
9209
9210 return auth;
9211}
9212#endif /* NO_FILESYSTEMS */
9213
9214
9215/* Return 1 if request is authorised, 0 otherwise. */
9216static int
9217check_authorization(struct mg_connection *conn, const char *path)
9218{
9219#if !defined(NO_FILESYSTEMS)
9220 char fname[UTF8_PATH_MAX];
9221 struct vec uri_vec, filename_vec;
9222 const char *list;
9223 struct mg_file file = STRUCT_FILE_INITIALIZER;
9224 int authorized = 1, truncated;
9225
9226 if (!conn || !conn->dom_ctx) {
9227 return 0;
9228 }
9229
9230 list = conn->dom_ctx->config[PROTECT_URI];
9231 while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
9232 if (!memcmp(conn->request_info.local_uri, uri_vec.ptr, uri_vec.len)) {
9233 mg_snprintf(conn,
9234 &truncated,
9235 fname,
9236 sizeof(fname),
9237 "%.*s",
9238 (int)filename_vec.len,
9239 filename_vec.ptr);
9240
9241 if (truncated
9242 || !mg_fopen(conn, fname, MG_FOPEN_MODE_READ, &file)) {
9243 mg_cry_internal(conn,
9244 "%s: cannot open %s: %s",
9245 __func__,
9246 fname,
9247 strerror(errno));
9248 }
9249 break;
9250 }
9251 }
9252
9253 if (!is_file_opened(&file.access)) {
9254 open_auth_file(conn, path, &file);
9255 }
9256
9257 if (is_file_opened(&file.access)) {
9258 authorized = authorize(conn, &file, NULL);
9259 (void)mg_fclose(&file.access); /* ignore error on read only file */
9260 }
9261
9262 return authorized;
9263#else
9264 (void)conn;
9265 (void)path;
9266 return 1;
9267#endif /* NO_FILESYSTEMS */
9268}
9269
9270
9271/* Internal function. Assumes conn is valid */
9272static void
9274{
9275 uint64_t nonce = (uint64_t)(conn->phys_ctx->start_time);
9276 int trunc = 0;
9277 char buf[128];
9278
9279 if (!realm) {
9280 realm = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
9281 }
9282
9284 nonce += conn->dom_ctx->nonce_count;
9285 ++conn->dom_ctx->nonce_count;
9287
9288 nonce ^= conn->dom_ctx->auth_nonce_mask;
9289 conn->must_close = 1;
9290
9291 /* Create 401 response */
9292 mg_response_header_start(conn, 401);
9295 mg_response_header_add(conn, "Content-Length", "0", -1);
9296
9297 /* Content for "WWW-Authenticate" header */
9298 mg_snprintf(conn,
9299 &trunc,
9300 buf,
9301 sizeof(buf),
9302 "Digest qop=\"auth\", realm=\"%s\", "
9303 "nonce=\"%" UINT64_FMT "\"",
9304 realm,
9305 nonce);
9306
9307 if (!trunc) {
9308 /* !trunc should always be true */
9309 mg_response_header_add(conn, "WWW-Authenticate", buf, -1);
9310 }
9311
9312 /* Send all headers */
9314}
9315
9316
9317/* Interface function. Parameters are provided by the user, so do
9318 * at least some basic checks.
9319 */
9320CIVETWEB_API int
9322 const char *realm)
9323{
9324 if (conn && conn->dom_ctx) {
9326 return 0;
9327 }
9328 return -1;
9329}
9330
9331
9332#if !defined(NO_FILES)
9333static int
9335{
9336 int ret = 0;
9337
9338 if (conn) {
9339 struct mg_file file = STRUCT_FILE_INITIALIZER;
9340 const char *passfile = conn->dom_ctx->config[PUT_DELETE_PASSWORDS_FILE];
9341
9342 if (passfile != NULL
9343 && mg_fopen(conn, passfile, MG_FOPEN_MODE_READ, &file)) {
9344 ret = authorize(conn, &file, NULL);
9345 (void)mg_fclose(&file.access); /* ignore error on read only file */
9346 }
9347 }
9348
9349 DEBUG_TRACE("file write authorization: %i", ret);
9350 return ret;
9351}
9352#endif
9353
9354
9355CIVETWEB_API int
9357 const char *domain,
9358 const char *user,
9359 const char *ha1)
9360{
9361 int found = 0, i, result = 1;
9362 char line[512], u[256], d[256], h[256];
9363 struct stat st = {0};
9364 FILE *fp = NULL;
9365 char *temp_file = NULL;
9366 int temp_file_offs = 0;
9367
9368 /* Regard empty password as no password - remove user record. */
9369 if ((ha1 != NULL) && (ha1[0] == '\0')) {
9370 ha1 = NULL;
9371 }
9372
9373 /* Other arguments must not be empty */
9374 if ((fname == NULL) || (domain == NULL) || (user == NULL)) {
9375 return 0;
9376 }
9377
9378 /* Using the given file format, user name and domain must not contain
9379 * the ':' character */
9380 if (strchr(user, ':') != NULL) {
9381 return 0;
9382 }
9383 if (strchr(domain, ':') != NULL) {
9384 return 0;
9385 }
9386
9387 /* Do not allow control characters like newline in user name and domain.
9388 * Do not allow excessively long names either. */
9389 for (i = 0; ((i < 255) && (user[i] != 0)); i++) {
9390 if (iscntrl((unsigned char)user[i])) {
9391 return 0;
9392 }
9393 }
9394 if (user[i]) {
9395 return 0; /* user name too long */
9396 }
9397 for (i = 0; ((i < 255) && (domain[i] != 0)); i++) {
9398 if (iscntrl((unsigned char)domain[i])) {
9399 return 0;
9400 }
9401 }
9402 if (domain[i]) {
9403 return 0; /* domain name too long */
9404 }
9405
9406 /* The maximum length of the path to the password file is limited */
9407 if (strlen(fname) >= UTF8_PATH_MAX) {
9408 return 0;
9409 }
9410
9411 /* Check if the file exists, and get file size */
9412 if (0 == stat(fname, &st)) {
9413 int temp_buf_len;
9414 if (st.st_size > 10485760) {
9415 /* Some funster provided a >10 MB text file */
9416 return 0;
9417 }
9418
9419 /* Add enough space for one more line */
9420 temp_buf_len = (int)st.st_size + 1024;
9421
9422 /* Allocate memory (instead of using a temporary file) */
9423 temp_file = (char *)mg_calloc((size_t)temp_buf_len, 1);
9424 if (!temp_file) {
9425 /* Out of memory */
9426 return 0;
9427 }
9428
9429 /* File exists. Read it into a memory buffer. */
9430 fp = fopen(fname, "r");
9431 if (fp == NULL) {
9432 /* Cannot read file. No permission? */
9434 return 0;
9435 }
9436
9437 /* Read content and store in memory */
9438 while ((fgets(line, sizeof(line), fp) != NULL)
9439 && ((temp_file_offs + 600) < temp_buf_len)) {
9440 /* file format is "user:domain:hash\n" */
9441 if (sscanf(line, "%255[^:]:%255[^:]:%255s", u, d, h) != 3) {
9442 continue;
9443 }
9444 u[255] = 0;
9445 d[255] = 0;
9446 h[255] = 0;
9447
9448 if (!strcmp(u, user) && !strcmp(d, domain)) {
9449 /* Found the user: change the password hash or drop the user
9450 */
9451 if ((ha1 != NULL) && (!found)) {
9453 "%s:%s:%s\n",
9454 user,
9455 domain,
9456 ha1);
9457 if (i < 1) {
9458 fclose(fp);
9460 return 0;
9461 }
9462 temp_file_offs += i;
9463 }
9464 found = 1;
9465 } else {
9466 /* Copy existing user, including password hash */
9467 i = sprintf(temp_file + temp_file_offs, "%s:%s:%s\n", u, d, h);
9468 if (i < 1) {
9469 fclose(fp);
9471 return 0;
9472 }
9473 temp_file_offs += i;
9474 }
9475 }
9476 fclose(fp);
9477 }
9478
9479 /* Create new file */
9480 fp = fopen(fname, "w");
9481 if (!fp) {
9483 return 0;
9484 }
9485
9486#if !defined(_WIN32)
9487 /* On Linux & co., restrict file read/write permissions to the owner */
9488 if (fchmod(fileno(fp), S_IRUSR | S_IWUSR) != 0) {
9489 result = 0;
9490 }
9491#endif
9492
9493 if ((temp_file != NULL) && (temp_file_offs > 0)) {
9494 /* Store buffered content of old file */
9495 if (fwrite(temp_file, 1, (size_t)temp_file_offs, fp)
9496 != (size_t)temp_file_offs) {
9497 result = 0;
9498 }
9499 }
9500
9501 /* If new user, just add it */
9502 if ((ha1 != NULL) && (!found)) {
9503 if (fprintf(fp, "%s:%s:%s\n", user, domain, ha1) < 6) {
9504 result = 0;
9505 }
9506 }
9507
9508 /* All data written */
9509 if (fclose(fp) != 0) {
9510 result = 0;
9511 }
9512
9514 return result;
9515}
9516
9517
9518CIVETWEB_API int
9520 const char *domain,
9521 const char *user,
9522 const char *pass)
9523{
9524 char ha1buf[33];
9525 if ((fname == NULL) || (domain == NULL) || (user == NULL)) {
9526 return 0;
9527 }
9528 if ((pass == NULL) || (pass[0] == 0)) {
9529 return mg_modify_passwords_file_ha1(fname, domain, user, NULL);
9530 }
9531
9532 mg_md5(ha1buf, user, ":", domain, ":", pass, NULL);
9533 return mg_modify_passwords_file_ha1(fname, domain, user, ha1buf);
9534}
9535
9536
9537static int
9538is_valid_port(unsigned long port)
9539{
9540 return (port <= 0xffff);
9541}
9542
9543
9544static int
9545mg_inet_pton(int af, const char *src, void *dst, size_t dstlen, int resolve_src)
9546{
9547 struct addrinfo hints, *res, *ressave;
9548 int func_ret = 0;
9549 int gai_ret;
9550
9551 memset(&hints, 0, sizeof(struct addrinfo));
9552 hints.ai_family = af;
9553 if (!resolve_src) {
9554 hints.ai_flags = AI_NUMERICHOST;
9555 }
9556
9557 gai_ret = getaddrinfo(src, NULL, &hints, &res);
9558 if (gai_ret != 0) {
9559 /* gai_strerror could be used to convert gai_ret to a string */
9560 /* POSIX return values: see
9561 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html
9562 */
9563 /* Windows return values: see
9564 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx
9565 */
9566 return 0;
9567 }
9568
9569 ressave = res;
9570
9571 while (res) {
9572 if ((dstlen >= (size_t)res->ai_addrlen)
9573 && (res->ai_addr->sa_family == af)) {
9574 memcpy(dst, res->ai_addr, res->ai_addrlen);
9575 func_ret = 1;
9576 }
9577 res = res->ai_next;
9578 }
9579
9581 return func_ret;
9582}
9583
9584
9585static int
9587 struct mg_context *ctx /* may be NULL */,
9588 const char *host,
9589 int port, /* 1..65535, or -99 for domain sockets (may be changed) */
9590 int use_ssl, /* 0 or 1 */
9591 struct mg_error_data *error,
9592 SOCKET *sock /* output: socket, must not be NULL */,
9593 union usa *sa /* output: socket address, must not be NULL */
9594)
9595{
9596 int ip_ver = 0;
9597 int conn_ret = -1;
9598 int sockerr = 0;
9599 *sock = INVALID_SOCKET;
9600 memset(sa, 0, sizeof(*sa));
9601
9602 if (host == NULL) {
9603 if (error != NULL) {
9606 NULL, /* No truncation check for ebuf */
9607 error->text,
9608 error->text_buffer_size,
9609 "%s",
9610 "NULL host");
9611 }
9612 return 0;
9613 }
9614
9615#if defined(USE_X_DOM_SOCKET)
9616 if (port == -99) {
9617 /* Unix domain socket */
9618 size_t hostlen = strlen(host);
9619 if (hostlen >= sizeof(sa->sun.sun_path)) {
9620 if (error != NULL) {
9623 NULL, /* No truncation check for ebuf */
9624 error->text,
9625 error->text_buffer_size,
9626 "%s",
9627 "host length exceeds limit");
9628 }
9629 return 0;
9630 }
9631 } else
9632#endif
9633 if ((port <= 0) || !is_valid_port((unsigned)port)) {
9634 if (error != NULL) {
9637 NULL, /* No truncation check for ebuf */
9638 error->text,
9639 error->text_buffer_size,
9640 "%s",
9641 "invalid port");
9642 }
9643 return 0;
9644 }
9645
9646#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) \
9647 && !defined(NO_SSL_DL)
9648#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
9649 if (use_ssl && (TLS_client_method == NULL)) {
9650 if (error != NULL) {
9653 NULL, /* No truncation check for ebuf */
9654 error->text,
9655 error->text_buffer_size,
9656 "%s",
9657 "SSL is not initialized");
9658 }
9659 return 0;
9660 }
9661#else
9662 if (use_ssl && (SSLv23_client_method == NULL)) {
9663 if (error != 0) {
9666 NULL, /* No truncation check for ebuf */
9667 error->text,
9668 error->text_buffer_size,
9669 "%s",
9670 "SSL is not initialized");
9671 }
9672 return 0;
9673 }
9674#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0*/
9675#else
9676 (void)use_ssl;
9677#endif /* NO SSL */
9678
9679#if defined(USE_X_DOM_SOCKET)
9680 if (port == -99) {
9681 size_t hostlen = strlen(host);
9682 /* check (hostlen < sizeof(sun.sun_path)) already passed above */
9683 ip_ver = -99;
9684 sa->sun.sun_family = AF_UNIX;
9685 memset(sa->sun.sun_path, 0, sizeof(sa->sun.sun_path));
9686 memcpy(sa->sun.sun_path, host, hostlen);
9687 } else
9688#endif
9689 if (mg_inet_pton(AF_INET, host, &sa->sin, sizeof(sa->sin), 1)) {
9690 sa->sin.sin_port = htons((uint16_t)port);
9691 ip_ver = 4;
9692#if defined(USE_IPV6)
9693 } else if (mg_inet_pton(AF_INET6, host, &sa->sin6, sizeof(sa->sin6), 1)) {
9694 sa->sin6.sin6_port = htons((uint16_t)port);
9695 ip_ver = 6;
9696 } else if (host[0] == '[') {
9697 /* While getaddrinfo on Windows will work with [::1],
9698 * getaddrinfo on Linux only works with ::1 (without []). */
9699 size_t l = strlen(host + 1);
9700 char *h = (l > 1) ? mg_strdup_ctx(host + 1, ctx) : NULL;
9701 if (h) {
9702 h[l - 1] = 0;
9703 if (mg_inet_pton(AF_INET6, h, &sa->sin6, sizeof(sa->sin6), 0)) {
9704 sa->sin6.sin6_port = htons((uint16_t)port);
9705 ip_ver = 6;
9706 }
9707 mg_free(h);
9708 }
9709#endif
9710 }
9711
9712 if (ip_ver == 0) {
9713 if (error != NULL) {
9716 NULL, /* No truncation check for ebuf */
9717 error->text,
9718 error->text_buffer_size,
9719 "%s",
9720 "host not found");
9721 }
9722 return 0;
9723 }
9724
9725 if (ip_ver == 4) {
9726 *sock = socket(PF_INET, SOCK_STREAM, 0);
9727 }
9728#if defined(USE_IPV6)
9729 else if (ip_ver == 6) {
9730 *sock = socket(PF_INET6, SOCK_STREAM, 0);
9731 }
9732#endif
9733#if defined(USE_X_DOM_SOCKET)
9734 else if (ip_ver == -99) {
9735 *sock = socket(AF_UNIX, SOCK_STREAM, 0);
9736 }
9737#endif
9738
9739 if (*sock == INVALID_SOCKET) {
9740 if (error != NULL) {
9742 error->code_sub = (unsigned)ERRNO;
9744 NULL, /* No truncation check for ebuf */
9745 error->text,
9746 error->text_buffer_size,
9747 "socket(): %s",
9748 strerror(ERRNO));
9749 }
9750 return 0;
9751 }
9752
9753 if (0 != set_non_blocking_mode(*sock)) {
9754 if (error != NULL) {
9756 error->code_sub = (unsigned)ERRNO;
9758 NULL, /* No truncation check for ebuf */
9759 error->text,
9760 error->text_buffer_size,
9761 "Cannot set socket to non-blocking: %s",
9762 strerror(ERRNO));
9763 }
9764 closesocket(*sock);
9765 *sock = INVALID_SOCKET;
9766 return 0;
9767 }
9768
9769 set_close_on_exec(*sock, NULL, ctx);
9770
9771 if (ip_ver == 4) {
9772 /* connected with IPv4 */
9773 conn_ret = connect(*sock,
9774 (struct sockaddr *)((void *)&sa->sin),
9775 sizeof(sa->sin));
9776 }
9777#if defined(USE_IPV6)
9778 else if (ip_ver == 6) {
9779 /* connected with IPv6 */
9780 conn_ret = connect(*sock,
9781 (struct sockaddr *)((void *)&sa->sin6),
9782 sizeof(sa->sin6));
9783 }
9784#endif
9785#if defined(USE_X_DOM_SOCKET)
9786 else if (ip_ver == -99) {
9787 /* connected to domain socket */
9788 conn_ret = connect(*sock,
9789 (struct sockaddr *)((void *)&sa->sun),
9790 sizeof(sa->sun));
9791 }
9792#endif
9793
9794 if (conn_ret != 0) {
9795 sockerr = ERRNO;
9796 }
9797
9798#if defined(_WIN32)
9799 if ((conn_ret != 0) && (sockerr == WSAEWOULDBLOCK)) {
9800#else
9801 if ((conn_ret != 0) && (sockerr == EINPROGRESS)) {
9802#endif
9803 /* Data for getsockopt */
9804 void *psockerr = &sockerr;
9805 int ret;
9806
9807#if defined(_WIN32)
9808 int len = (int)sizeof(sockerr);
9809#else
9810 socklen_t len = (socklen_t)sizeof(sockerr);
9811#endif
9812
9813 /* Data for poll */
9814 struct mg_pollfd pfd[2];
9815 int pollres;
9816 int ms_wait = 10000; /* 10 second timeout */
9817 stop_flag_t nonstop = 0; /* STOP_FLAG_ASSIGN(&nonstop, 0); */
9818 unsigned int num_sock = 1; /* use one or two sockets */
9819
9820 /* For a non-blocking socket, the connect sequence is:
9821 * 1) call connect (will not block)
9822 * 2) wait until the socket is ready for writing (select or poll)
9823 * 3) check connection state with getsockopt
9824 */
9825 pfd[0].fd = *sock;
9826 pfd[0].events = POLLOUT;
9827
9828 if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
9830 pfd[num_sock].events = POLLIN;
9831 num_sock++;
9832 }
9833
9834 pollres =
9835 mg_poll(pfd, num_sock, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop);
9836
9837 if (pollres != 1) {
9838 /* Not connected */
9839 if (error != NULL) {
9842 NULL, /* No truncation check for ebuf */
9843 error->text,
9844 error->text_buffer_size,
9845 "connect(%s:%d): timeout",
9846 host,
9847 port);
9848 }
9849 closesocket(*sock);
9850 *sock = INVALID_SOCKET;
9851 return 0;
9852 }
9853
9854#if defined(_WIN32)
9855 ret = getsockopt(*sock, SOL_SOCKET, SO_ERROR, (char *)psockerr, &len);
9856#else
9858#endif
9859
9860 if ((ret == 0) && (sockerr == 0)) {
9861 conn_ret = 0;
9862 }
9863 }
9864
9865 if (conn_ret != 0) {
9866 /* Not connected */
9867 if (error != NULL) {
9869 error->code_sub = (unsigned)ERRNO;
9871 NULL, /* No truncation check for ebuf */
9872 error->text,
9873 error->text_buffer_size,
9874 "connect(%s:%d): error %s",
9875 host,
9876 port,
9877 strerror(sockerr));
9878 }
9879 closesocket(*sock);
9880 *sock = INVALID_SOCKET;
9881 return 0;
9882 }
9883
9884 return 1;
9885}
9886
9887
9888CIVETWEB_API int
9889mg_url_encode(const char *src, char *dst, size_t dst_len)
9890{
9891 static const char *dont_escape = "._-$,;~()";
9892 static const char *hex = "0123456789abcdef";
9893 char *pos = dst;
9894 const char *end = dst + dst_len - 1;
9895
9896 for (; ((*src != '\0') && (pos < end)); src++, pos++) {
9897 if (isalnum((unsigned char)*src)
9898 || (strchr(dont_escape, *src) != NULL)) {
9899 *pos = *src;
9900 } else if (pos + 2 < end) {
9901 pos[0] = '%';
9902 pos[1] = hex[(unsigned char)*src >> 4];
9903 pos[2] = hex[(unsigned char)*src & 0xf];
9904 pos += 2;
9905 } else {
9906 break;
9907 }
9908 }
9909
9910 *pos = '\0';
9911 return (*src == '\0') ? (int)(pos - dst) : -1;
9912}
9913
9914/* Return 0 on success, non-zero if an error occurs. */
9915
9916static int
9917print_dir_entry(struct mg_connection *conn, struct de *de)
9918{
9919 size_t namesize, escsize, i;
9920 char *href, *esc, *p;
9921 char size[64], mod[64];
9922#if defined(REENTRANT_TIME)
9923 struct tm _tm;
9924 struct tm *tm = &_tm;
9925#else
9926 struct tm *tm;
9927#endif
9928
9929 /* Estimate worst case size for encoding and escaping */
9930 namesize = strlen(de->file_name) + 1;
9931 escsize = de->file_name[strcspn(de->file_name, "&<>")] ? namesize * 5 : 0;
9932 href = (char *)mg_malloc(namesize * 3 + escsize);
9933 if (href == NULL) {
9934 return -1;
9935 }
9937 esc = NULL;
9938 if (escsize > 0) {
9939 /* HTML escaping needed */
9940 esc = href + namesize * 3;
9941 for (i = 0, p = esc; de->file_name[i]; i++, p += strlen(p)) {
9942 mg_strlcpy(p, de->file_name + i, 2);
9943 if (*p == '&') {
9944 strcpy(p, "&amp;");
9945 } else if (*p == '<') {
9946 strcpy(p, "&lt;");
9947 } else if (*p == '>') {
9948 strcpy(p, "&gt;");
9949 }
9950 }
9951 }
9952
9953 if (de->file.is_directory) {
9954 mg_snprintf(conn,
9955 NULL, /* Buffer is big enough */
9956 size,
9957 sizeof(size),
9958 "%s",
9959 "[DIRECTORY]");
9960 } else {
9961 /* We use (signed) cast below because MSVC 6 compiler cannot
9962 * convert unsigned __int64 to double. Sigh. */
9963 if (de->file.size < 1024) {
9964 mg_snprintf(conn,
9965 NULL, /* Buffer is big enough */
9966 size,
9967 sizeof(size),
9968 "%d",
9969 (int)de->file.size);
9970 } else if (de->file.size < 0x100000) {
9971 mg_snprintf(conn,
9972 NULL, /* Buffer is big enough */
9973 size,
9974 sizeof(size),
9975 "%.1fk",
9976 (double)de->file.size / 1024.0);
9977 } else if (de->file.size < 0x40000000) {
9978 mg_snprintf(conn,
9979 NULL, /* Buffer is big enough */
9980 size,
9981 sizeof(size),
9982 "%.1fM",
9983 (double)de->file.size / 1048576);
9984 } else {
9985 mg_snprintf(conn,
9986 NULL, /* Buffer is big enough */
9987 size,
9988 sizeof(size),
9989 "%.1fG",
9990 (double)de->file.size / 1073741824);
9991 }
9992 }
9993
9994 /* Note: mg_snprintf will not cause a buffer overflow above.
9995 * So, string truncation checks are not required here. */
9996
9997#if defined(REENTRANT_TIME)
9998 localtime_r(&de->file.last_modified, tm);
9999#else
10000 tm = localtime(&de->file.last_modified);
10001#endif
10002 if (tm != NULL) {
10003 strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm);
10004 } else {
10005 mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod));
10006 }
10007 mg_printf(conn,
10008 "<tr><td><a href=\"%s%s\">%s%s</a></td>"
10009 "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
10010 href,
10011 de->file.is_directory ? "/" : "",
10012 esc ? esc : de->file_name,
10013 de->file.is_directory ? "/" : "",
10014 mod,
10015 size);
10016 mg_free(href);
10017 return 0;
10018}
10019
10020
10021/* This function is called from send_directory() and used for
10022 * sorting directory entries by size, name, or modification time. */
10023static int
10024compare_dir_entries(const void *p1, const void *p2, void *arg)
10025{
10026 const char *query_string = (const char *)(arg != NULL ? arg : "");
10027 if (p1 && p2) {
10028 const struct de *a = (const struct de *)p1, *b = (const struct de *)p2;
10029 int cmp_result = 0;
10030
10031 if ((query_string == NULL) || (query_string[0] == '\0')) {
10032 query_string = "n";
10033 }
10034
10035 /* Sort Directories vs Files */
10036 if (a->file.is_directory && !b->file.is_directory) {
10037 return -1; /* Always put directories on top */
10038 } else if (!a->file.is_directory && b->file.is_directory) {
10039 return 1; /* Always put directories on top */
10040 }
10041
10042 /* Sort by size or date */
10043 if (*query_string == 's') {
10044 cmp_result = (a->file.size == b->file.size)
10045 ? 0
10046 : ((a->file.size > b->file.size) ? 1 : -1);
10047 } else if (*query_string == 'd') {
10048 cmp_result =
10049 (a->file.last_modified == b->file.last_modified)
10050 ? 0
10051 : ((a->file.last_modified > b->file.last_modified) ? 1
10052 : -1);
10053 }
10054
10055 /* Sort by name:
10056 * if (*query_string == 'n') ...
10057 * but also sort files of same size/date by name as secondary criterion.
10058 */
10059 if (cmp_result == 0) {
10060 cmp_result = strcmp(a->file_name, b->file_name);
10061 }
10062
10063 /* For descending order, invert result */
10064 return (query_string[1] == 'd') ? -cmp_result : cmp_result;
10065 }
10066 return 0;
10067}
10068
10069
10070static int
10071must_hide_file(struct mg_connection *conn, const char *path)
10072{
10073 if (conn && conn->dom_ctx) {
10074 const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
10075 const char *pattern = conn->dom_ctx->config[HIDE_FILES];
10076 return (match_prefix_strlen(pw_pattern, path) > 0)
10077 || (match_prefix_strlen(pattern, path) > 0);
10078 }
10079 return 0;
10080}
10081
10082
10083#if !defined(NO_FILESYSTEMS)
10084static int
10086 const char *dir,
10087 void *data,
10088 int (*cb)(struct de *, void *))
10089{
10090 char path[UTF8_PATH_MAX];
10091 struct dirent *dp;
10092 DIR *dirp;
10093 struct de de;
10094 int truncated;
10095
10096 if ((dirp = mg_opendir(conn, dir)) == NULL) {
10097 return 0;
10098 } else {
10099
10100 while ((dp = mg_readdir(dirp)) != NULL) {
10101 /* Do not show current dir and hidden files */
10102 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")
10103 || must_hide_file(conn, dp->d_name)) {
10104 continue;
10105 }
10106
10108 conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name);
10109
10110 /* If we don't memset stat structure to zero, mtime will have
10111 * garbage and strftime() will segfault later on in
10112 * print_dir_entry(). memset is required only if mg_stat()
10113 * fails. For more details, see
10114 * http://code.google.com/p/mongoose/issues/detail?id=79 */
10115 memset(&de.file, 0, sizeof(de.file));
10116
10117 if (truncated) {
10118 /* If the path is not complete, skip processing. */
10119 continue;
10120 }
10121
10122 if (!mg_stat(conn, path, &de.file)) {
10123 mg_cry_internal(conn,
10124 "%s: mg_stat(%s) failed: %s",
10125 __func__,
10126 path,
10127 strerror(ERRNO));
10128 }
10129 de.file_name = dp->d_name;
10130 if (cb(&de, data)) {
10131 /* stopped */
10132 break;
10133 }
10134 }
10135 (void)mg_closedir(dirp);
10136 }
10137 return 1;
10138}
10139#endif /* NO_FILESYSTEMS */
10140
10141
10142#if !defined(NO_FILES)
10143static int
10144remove_directory(struct mg_connection *conn, const char *dir)
10145{
10146 char path[UTF8_PATH_MAX];
10147 struct dirent *dp;
10148 DIR *dirp;
10149 struct de de;
10150 int truncated;
10151 int ok = 1;
10152
10153 if ((dirp = mg_opendir(conn, dir)) == NULL) {
10154 return 0;
10155 } else {
10156
10157 while ((dp = mg_readdir(dirp)) != NULL) {
10158 /* Do not show current dir (but show hidden files as they will
10159 * also be removed) */
10160 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
10161 continue;
10162 }
10163
10165 conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name);
10166
10167 /* If we don't memset stat structure to zero, mtime will have
10168 * garbage and strftime() will segfault later on in
10169 * print_dir_entry(). memset is required only if mg_stat()
10170 * fails. For more details, see
10171 * http://code.google.com/p/mongoose/issues/detail?id=79 */
10172 memset(&de.file, 0, sizeof(de.file));
10173
10174 if (truncated) {
10175 /* Do not delete anything shorter */
10176 ok = 0;
10177 continue;
10178 }
10179
10180 if (!mg_stat(conn, path, &de.file)) {
10181 mg_cry_internal(conn,
10182 "%s: mg_stat(%s) failed: %s",
10183 __func__,
10184 path,
10185 strerror(ERRNO));
10186 ok = 0;
10187 }
10188
10189 if (de.file.is_directory) {
10190 if (remove_directory(conn, path) == 0) {
10191 ok = 0;
10192 }
10193 } else {
10194 /* This will fail file is the file is in memory */
10195 if (mg_remove(conn, path) == 0) {
10196 ok = 0;
10197 }
10198 }
10199 }
10200 (void)mg_closedir(dirp);
10201
10202 IGNORE_UNUSED_RESULT(rmdir(dir));
10203 }
10204
10205 return ok;
10206}
10207#endif
10208
10209
10211 struct de *entries;
10213 size_t arr_size;
10214};
10215
10216
10217#if !defined(NO_FILESYSTEMS)
10218static int
10220{
10221 struct dir_scan_data *dsd = (struct dir_scan_data *)data;
10222 struct de *entries = dsd->entries;
10223
10224 if ((entries == NULL) || (dsd->num_entries >= dsd->arr_size)) {
10225 /* Here "entries" is a temporary pointer and can be replaced,
10226 * "dsd->entries" is the original pointer */
10227 entries =
10228 (struct de *)mg_realloc(entries,
10229 dsd->arr_size * 2 * sizeof(entries[0]));
10230 if (entries == NULL) {
10231 /* stop scan */
10232 return 1;
10233 }
10234 dsd->entries = entries;
10235 dsd->arr_size *= 2;
10236 }
10237 entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
10238 if (entries[dsd->num_entries].file_name == NULL) {
10239 /* stop scan */
10240 return 1;
10241 }
10242 entries[dsd->num_entries].file = de->file;
10243 dsd->num_entries++;
10244
10245 return 0;
10246}
10247
10248
10249static void
10250handle_directory_request(struct mg_connection *conn, const char *dir)
10251{
10252 size_t i;
10253 int sort_direction;
10254 struct dir_scan_data data = {NULL, 0, 128};
10255 char date[64], *esc, *p;
10256 const char *title;
10257 time_t curtime = time(NULL);
10258
10259 if (!conn) {
10260 return;
10261 }
10262
10263 if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
10264 mg_send_http_error(conn,
10265 500,
10266 "Error: Cannot open directory\nopendir(%s): %s",
10267 dir,
10268 strerror(ERRNO));
10269 return;
10270 }
10271
10272 gmt_time_string(date, sizeof(date), &curtime);
10273
10274 esc = NULL;
10275 title = conn->request_info.local_uri;
10276 if (title[strcspn(title, "&<>")]) {
10277 /* HTML escaping needed */
10278 esc = (char *)mg_malloc(strlen(title) * 5 + 1);
10279 if (esc) {
10280 for (i = 0, p = esc; title[i]; i++, p += strlen(p)) {
10281 mg_strlcpy(p, title + i, 2);
10282 if (*p == '&') {
10283 strcpy(p, "&amp;");
10284 } else if (*p == '<') {
10285 strcpy(p, "&lt;");
10286 } else if (*p == '>') {
10287 strcpy(p, "&gt;");
10288 }
10289 }
10290 } else {
10291 title = "";
10292 }
10293 }
10294
10295 sort_direction = ((conn->request_info.query_string != NULL)
10296 && (conn->request_info.query_string[0] != '\0')
10297 && (conn->request_info.query_string[1] == 'd'))
10298 ? 'a'
10299 : 'd';
10300
10301 conn->must_close = 1;
10302
10303 /* Create 200 OK response */
10304 mg_response_header_start(conn, 200);
10308 "Content-Type",
10309 "text/html; charset=utf-8",
10310 -1);
10311
10312 /* Send all headers */
10314
10315 /* Body */
10316 mg_printf(conn,
10317 "<!DOCTYPE html>"
10318 "<html><head><title>Index of %s</title>"
10319 "<style>th {text-align: left;}</style></head>"
10320 "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
10321 "<tr><th><a href=\"?n%c\">Name</a></th>"
10322 "<th><a href=\"?d%c\">Modified</a></th>"
10323 "<th><a href=\"?s%c\">Size</a></th></tr>"
10324 "<tr><td colspan=\"3\"><hr></td></tr>",
10325 esc ? esc : title,
10326 esc ? esc : title,
10330 mg_free(esc);
10331
10332 /* Print first entry - link to a parent directory */
10333 mg_printf(conn,
10334 "<tr><td><a href=\"%s\">%s</a></td>"
10335 "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
10336 "..",
10337 "Parent directory",
10338 "-",
10339 "-");
10340
10341 /* Sort and print directory entries */
10342 if (data.entries != NULL) {
10343 mg_sort(data.entries,
10344 data.num_entries,
10345 sizeof(data.entries[0]),
10347 (void *)conn->request_info.query_string);
10348 for (i = 0; i < data.num_entries; i++) {
10349 print_dir_entry(conn, &data.entries[i]);
10350 mg_free(data.entries[i].file_name);
10351 }
10352 mg_free(data.entries);
10353 }
10354
10355 mg_printf(conn, "%s", "</table></pre></body></html>");
10356 conn->status_code = 200;
10357}
10358#endif /* NO_FILESYSTEMS */
10359
10360
10361/* Send len bytes from the opened file to the client. */
10362static void
10364 struct mg_file *filep,
10365 int64_t offset,
10366 int64_t len,
10367 int no_buffering)
10368{
10369 char buf[MG_BUF_LEN];
10371 int64_t size;
10372
10373 if (!filep || !conn) {
10374 return;
10375 }
10376
10377 /* Sanity check the offset */
10378 size = (filep->stat.size > INT64_MAX) ? INT64_MAX
10379 : (int64_t)(filep->stat.size);
10380 offset = (offset < 0) ? 0 : ((offset > size) ? size : offset);
10381
10382 if (len > 0 && filep->access.fp != NULL) {
10383 /* file stored on disk */
10384#if defined(__linux__)
10385 /* sendfile is only available for Linux */
10386 if ((conn->ssl == 0) && (conn->throttle == 0)
10387 && (!mg_strcasecmp(conn->dom_ctx->config[ALLOW_SENDFILE_CALL],
10388 "yes"))) {
10391 int sf_file = fileno(filep->access.fp);
10392 int loop_cnt = 0;
10393
10394 do {
10395 /* 2147479552 (0x7FFFF000) is a limit found by experiment on
10396 * 64 bit Linux (2^31 minus one memory page of 4k?). */
10397 size_t sf_tosend =
10398 (size_t)((len < 0x7FFFF000) ? len : 0x7FFFF000);
10399 sf_sent =
10400 sendfile(conn->client.sock, sf_file, &sf_offs, sf_tosend);
10401 if (sf_sent > 0) {
10402 len -= sf_sent;
10403 offset += sf_sent;
10404 } else if (loop_cnt == 0) {
10405 /* This file can not be sent using sendfile.
10406 * This might be the case for pseudo-files in the
10407 * /sys/ and /proc/ file system.
10408 * Use the regular user mode copy code instead. */
10409 break;
10410 } else if (sf_sent == 0) {
10411 /* No error, but 0 bytes sent. May be EOF? */
10412 return;
10413 }
10414 loop_cnt++;
10415
10416 } while ((len > 0) && (sf_sent >= 0));
10417
10418 if (sf_sent > 0) {
10419 return; /* OK */
10420 }
10421
10422 /* sf_sent<0 means error, thus fall back to the classic way */
10423 /* This is always the case, if sf_file is not a "normal" file,
10424 * e.g., for sending data from the output of a CGI process. */
10425 offset = (int64_t)sf_offs;
10426 }
10427#endif
10428 if ((offset > 0) && (fseeko(filep->access.fp, offset, SEEK_SET) != 0)) {
10429 mg_cry_internal(conn,
10430 "%s: fseeko() failed: %s",
10431 __func__,
10432 strerror(ERRNO));
10434 conn,
10435 500,
10436 "%s",
10437 "Error: Unable to access file at requested position.");
10438 } else {
10439 while (len > 0) {
10440 /* Calculate how much to read from the file into the buffer. */
10441 /* If no_buffering is set, we should not wait until the
10442 * CGI->Server buffer is filled, but send everything
10443 * immediately. In theory buffering could be turned off using
10444 * setbuf(filep->access.fp, NULL);
10445 * setvbuf(filep->access.fp, NULL, _IONBF, 0);
10446 * but in practice this does not work. A "Linux only" solution
10447 * may be to use select(). The only portable way is to read byte
10448 * by byte, but this is quite inefficient from a performance
10449 * point of view. */
10450 to_read = no_buffering ? 1 : sizeof(buf);
10451 if ((int64_t)to_read > len) {
10452 to_read = (int)len;
10453 }
10454
10455 /* Read from file, exit the loop on error */
10456 if ((num_read = pull_inner(filep->access.fp,
10457 NULL,
10458 buf,
10459 to_read,
10460 /* unused */ 0.0))
10461 <= 0) {
10462 break;
10463 }
10464
10465 /* Send read bytes to the client, exit the loop on error */
10466 if ((num_written = mg_write(conn, buf, (size_t)num_read))
10467 != num_read) {
10468 break;
10469 }
10470
10471 /* Both read and were successful, adjust counters */
10472 len -= num_written;
10473 }
10474 }
10475 }
10476}
10477
10478
10479static int
10480parse_range_header(const char *header, int64_t *a, int64_t *b)
10481{
10482 return sscanf(header,
10483 "bytes=%" INT64_FMT "-%" INT64_FMT,
10484 a,
10485 b); // NOLINT(cert-err34-c) 'sscanf' used to convert a string
10486 // to an integer value, but function will not report
10487 // conversion errors; consider using 'strtol' instead
10488}
10489
10490
10491static void
10492construct_etag(char *buf, size_t buf_len, const struct mg_file_stat *filestat)
10493{
10494 if ((filestat != NULL) && (buf != NULL)) {
10496 NULL, /* All calls to construct_etag use 64 byte buffer */
10497 buf,
10498 buf_len,
10499 "\"%lx.%" INT64_FMT "\"",
10500 (unsigned long)filestat->last_modified,
10501 filestat->size);
10502 }
10503}
10504
10505
10506static void
10508{
10509 if (filep != NULL && filep->fp != NULL) {
10510#if defined(_WIN32)
10511 (void)conn; /* Unused. */
10512#else
10513 if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) {
10514 mg_cry_internal(conn,
10515 "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
10516 __func__,
10517 strerror(ERRNO));
10518 }
10519#endif
10520 }
10521}
10522
10523
10524#if defined(USE_ZLIB)
10525#include "mod_zlib.inl"
10526#endif
10527
10528
10529#if !defined(NO_FILESYSTEMS)
10530static void
10532 const char *path,
10533 struct mg_file *filep,
10534 const char *mime_type,
10535 const char *additional_headers)
10536{
10537 char lm[64], etag[64];
10538 char range[128]; /* large enough, so there will be no overflow */
10539 const char *range_hdr;
10540 int64_t cl, r1, r2;
10541 struct vec mime_vec;
10542 int n, truncated;
10543 char gz_path[UTF8_PATH_MAX];
10544 const char *encoding = 0;
10545 int is_head_request;
10546
10547#if defined(USE_ZLIB)
10548 /* Compression is allowed, unless there is a reason not to use
10549 * compression. If the file is already compressed, too small or a
10550 * "range" request was made, on the fly compression is not possible. */
10552#endif
10553
10554 if ((conn == NULL) || (conn->dom_ctx == NULL) || (filep == NULL)) {
10555 return;
10556 }
10557
10558 is_head_request = !strcmp(conn->request_info.request_method, "HEAD");
10559
10560 if (mime_type == NULL) {
10561 get_mime_type(conn, path, &mime_vec);
10562 } else {
10563 mime_vec.ptr = mime_type;
10564 mime_vec.len = strlen(mime_type);
10565 }
10566 if (filep->stat.size > INT64_MAX) {
10567 mg_send_http_error(conn,
10568 500,
10569 "Error: File size is too large to send\n%" INT64_FMT,
10570 filep->stat.size);
10571 return;
10572 }
10573 cl = (int64_t)filep->stat.size;
10574 conn->status_code = 200;
10575 range[0] = '\0';
10576
10577#if defined(USE_ZLIB)
10578 /* if this file is in fact a pre-gzipped file, rewrite its filename
10579 * it's important to rewrite the filename after resolving
10580 * the mime type from it, to preserve the actual file's type */
10581 if (!conn->accept_gzip) {
10583 }
10584#endif
10585
10586 /* Check if there is a range header */
10587 range_hdr = mg_get_header(conn, "Range");
10588
10589 /* For gzipped files, add *.gz */
10590 if (filep->stat.is_gzipped) {
10591 mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path);
10592
10593 if (truncated) {
10594 mg_send_http_error(conn,
10595 500,
10596 "Error: Path of zipped file too long (%s)",
10597 path);
10598 return;
10599 }
10600
10601 path = gz_path;
10602 encoding = "gzip";
10603
10604#if defined(USE_ZLIB)
10605 /* File is already compressed. No "on the fly" compression. */
10607#endif
10608 } else if ((conn->accept_gzip) && (range_hdr == NULL)
10609 && (filep->stat.size >= MG_FILE_COMPRESSION_SIZE_LIMIT)) {
10610 struct mg_file_stat file_stat;
10611
10612 mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path);
10613
10614 if (!truncated && mg_stat(conn, gz_path, &file_stat)
10615 && !file_stat.is_directory) {
10616 file_stat.is_gzipped = 1;
10617 filep->stat = file_stat;
10618 cl = (int64_t)filep->stat.size;
10619 path = gz_path;
10620 encoding = "gzip";
10621
10622#if defined(USE_ZLIB)
10623 /* File is already compressed. No "on the fly" compression. */
10625#endif
10626 }
10627 }
10628
10629 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) {
10630 mg_send_http_error(conn,
10631 500,
10632 "Error: Cannot open file\nfopen(%s): %s",
10633 path,
10634 strerror(ERRNO));
10635 return;
10636 }
10637
10638 fclose_on_exec(&filep->access, conn);
10639
10640 /* If "Range" request was made: parse header, send only selected part
10641 * of the file. */
10642 r1 = r2 = 0;
10643 if ((range_hdr != NULL)
10644 && ((n = parse_range_header(range_hdr, &r1, &r2)) > 0) && (r1 >= 0)
10645 && (r2 >= 0)) {
10646 /* actually, range requests don't play well with a pre-gzipped
10647 * file (since the range is specified in the uncompressed space) */
10648 if (filep->stat.is_gzipped) {
10650 conn,
10651 416, /* 416 = Range Not Satisfiable */
10652 "%s",
10653 "Error: Range requests in gzipped files are not supported");
10654 (void)mg_fclose(
10655 &filep->access); /* ignore error on read only file */
10656 return;
10657 }
10658 conn->status_code = 206;
10659 cl = (n == 2) ? (((r2 > cl) ? cl : r2) - r1 + 1) : (cl - r1);
10660 mg_snprintf(conn,
10661 NULL, /* range buffer is big enough */
10662 range,
10663 sizeof(range),
10664 "bytes "
10665 "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT,
10666 r1,
10667 r1 + cl - 1,
10668 filep->stat.size);
10669
10670#if defined(USE_ZLIB)
10671 /* Do not compress ranges. */
10673#endif
10674 }
10675
10676 /* Do not compress small files. Small files do not benefit from file
10677 * compression, but there is still some overhead. */
10678#if defined(USE_ZLIB)
10679 if (filep->stat.size < MG_FILE_COMPRESSION_SIZE_LIMIT) {
10680 /* File is below the size limit. */
10682 }
10683#endif
10684
10685 /* Prepare Etag, and Last-Modified headers. */
10686 gmt_time_string(lm, sizeof(lm), &filep->stat.last_modified);
10687 construct_etag(etag, sizeof(etag), &filep->stat);
10688
10689 /* Create 2xx (200, 206) response */
10693 send_cors_header(conn);
10695 "Content-Type",
10696 mime_vec.ptr,
10697 (int)mime_vec.len);
10698 mg_response_header_add(conn, "Last-Modified", lm, -1);
10699 mg_response_header_add(conn, "Etag", etag, -1);
10700
10701#if defined(USE_ZLIB)
10702 /* On the fly compression allowed */
10704 /* For on the fly compression, we don't know the content size in
10705 * advance, so we have to use chunked encoding */
10706 encoding = "gzip";
10707 if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
10708 /* HTTP/2 is always using "chunks" (frames) */
10709 mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1);
10710 }
10711
10712 } else
10713#endif
10714 {
10715 /* Without on-the-fly compression, we know the content-length
10716 * and we can use ranges (with on-the-fly compression we cannot).
10717 * So we send these response headers only in this case. */
10718 char len[32];
10719 int trunc = 0;
10720 mg_snprintf(conn, &trunc, len, sizeof(len), "%" INT64_FMT, cl);
10721
10722 if (!trunc) {
10723 mg_response_header_add(conn, "Content-Length", len, -1);
10724 }
10725
10726 mg_response_header_add(conn, "Accept-Ranges", "bytes", -1);
10727 }
10728
10729 if (encoding) {
10730 mg_response_header_add(conn, "Content-Encoding", encoding, -1);
10731 }
10732 if (range[0] != 0) {
10733 mg_response_header_add(conn, "Content-Range", range, -1);
10734 }
10735
10736 /* The code above does not add any header starting with X- to make
10737 * sure no one of the additional_headers is included twice */
10738 if ((additional_headers != NULL) && (*additional_headers != 0)) {
10740 }
10741
10742 /* Send all headers */
10744
10745 if (!is_head_request) {
10746#if defined(USE_ZLIB)
10748 /* Compress and send */
10750 } else
10751#endif
10752 {
10753 /* Send file directly */
10754 send_file_data(conn, filep, r1, cl, 0); /* send static file */
10755 }
10756 }
10757 (void)mg_fclose(&filep->access); /* ignore error on read only file */
10758}
10759
10760
10761CIVETWEB_API int
10762mg_send_file_body(struct mg_connection *conn, const char *path)
10763{
10764 struct mg_file file = STRUCT_FILE_INITIALIZER;
10765 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, &file)) {
10766 return -1;
10767 }
10768 fclose_on_exec(&file.access, conn);
10769 send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
10770 (void)mg_fclose(&file.access); /* Ignore errors for readonly files */
10771 return 0; /* >= 0 for OK */
10772}
10773#endif /* NO_FILESYSTEMS */
10774
10775
10776#if !defined(NO_CACHING)
10777/* Return True if we should reply 304 Not Modified. */
10778static int
10780 const struct mg_file_stat *filestat)
10781{
10782 char etag[64];
10783 const char *ims = mg_get_header(conn, "If-Modified-Since");
10784 const char *inm = mg_get_header(conn, "If-None-Match");
10785 construct_etag(etag, sizeof(etag), filestat);
10786
10787 return ((inm != NULL) && !mg_strcasecmp(etag, inm))
10788 || ((ims != NULL)
10789 && (filestat->last_modified <= parse_date_string(ims)));
10790}
10791
10792
10793static void
10795 struct mg_file *filep)
10796{
10797 char lm[64], etag[64];
10798
10799 if ((conn == NULL) || (filep == NULL)) {
10800 return;
10801 }
10802
10803 gmt_time_string(lm, sizeof(lm), &filep->stat.last_modified);
10804 construct_etag(etag, sizeof(etag), &filep->stat);
10805
10806 /* Create 304 "not modified" response */
10807 mg_response_header_start(conn, 304);
10810 mg_response_header_add(conn, "Last-Modified", lm, -1);
10811 mg_response_header_add(conn, "Etag", etag, -1);
10812
10813 /* Send all headers */
10815}
10816#endif
10817
10818
10819#if !defined(NO_FILESYSTEMS)
10820CIVETWEB_API void
10821mg_send_file(struct mg_connection *conn, const char *path)
10822{
10823 mg_send_mime_file2(conn, path, NULL, NULL);
10824}
10825
10826
10827CIVETWEB_API void
10829 const char *path,
10830 const char *mime_type)
10831{
10832 mg_send_mime_file2(conn, path, mime_type, NULL);
10833}
10834
10835
10836CIVETWEB_API void
10838 const char *path,
10839 const char *mime_type,
10840 const char *additional_headers)
10841{
10842 struct mg_file file = STRUCT_FILE_INITIALIZER;
10843
10844 if (!conn) {
10845 /* No conn */
10846 return;
10847 }
10848
10849 if (mg_stat(conn, path, &file.stat)) {
10850#if !defined(NO_CACHING)
10851 if (is_not_modified(conn, &file.stat)) {
10852 /* Send 304 "Not Modified" - this must not send any body data */
10854 } else
10855#endif /* NO_CACHING */
10856 if (file.stat.is_directory) {
10858 "yes")) {
10859 handle_directory_request(conn, path);
10860 } else {
10861 mg_send_http_error(conn,
10862 403,
10863 "%s",
10864 "Error: Directory listing denied");
10865 }
10866 } else {
10868 conn, path, &file, mime_type, additional_headers);
10869 }
10870 } else {
10871 mg_send_http_error(conn, 404, "%s", "Error: File not found");
10872 }
10873}
10874
10875
10876/* For a given PUT path, create all intermediate subdirectories.
10877 * Return 0 if the path itself is a directory.
10878 * Return 1 if the path leads to a file.
10879 * Return -1 for if the path is too long.
10880 * Return -2 if path can not be created.
10881 */
10882static int
10883put_dir(struct mg_connection *conn, const char *path)
10884{
10885 char buf[UTF8_PATH_MAX];
10886 const char *s, *p;
10887 struct mg_file file = STRUCT_FILE_INITIALIZER;
10888 size_t len;
10889 int res = 1;
10890
10891 for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
10892 len = (size_t)(p - path);
10893 if (len >= sizeof(buf)) {
10894 /* path too long */
10895 res = -1;
10896 break;
10897 }
10898 memcpy(buf, path, len);
10899 buf[len] = '\0';
10900
10901 /* Try to create intermediate directory */
10902 DEBUG_TRACE("mkdir(%s)", buf);
10903 if (!mg_stat(conn, buf, &file.stat) && mg_mkdir(conn, buf, 0755) != 0) {
10904 /* path does not exist and can not be created */
10905 res = -2;
10906 break;
10907 }
10908
10909 /* Is path itself a directory? */
10910 if (p[1] == '\0') {
10911 res = 0;
10912 }
10913 }
10914
10915 return res;
10916}
10917
10918
10919static void
10920remove_bad_file(const struct mg_connection *conn, const char *path)
10921{
10922 int r = mg_remove(conn, path);
10923 if (r != 0) {
10924 mg_cry_internal(conn,
10925 "%s: Cannot remove invalid file %s",
10926 __func__,
10927 path);
10928 }
10929}
10930
10931
10932CIVETWEB_API long long
10933mg_store_body(struct mg_connection *conn, const char *path)
10934{
10935 char buf[MG_BUF_LEN];
10936 long long len = 0;
10937 int ret, n;
10938 struct mg_file fi;
10939
10940 if (conn->consumed_content != 0) {
10941 mg_cry_internal(conn, "%s: Contents already consumed", __func__);
10942 return -11;
10943 }
10944
10945 ret = put_dir(conn, path);
10946 if (ret < 0) {
10947 /* -1 for path too long,
10948 * -2 for path can not be created. */
10949 return ret;
10950 }
10951 if (ret != 1) {
10952 /* Return 0 means, path itself is a directory. */
10953 return 0;
10954 }
10955
10956 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fi) == 0) {
10957 return -12;
10958 }
10959
10960 ret = mg_read(conn, buf, sizeof(buf));
10961 while (ret > 0) {
10962 n = (int)fwrite(buf, 1, (size_t)ret, fi.access.fp);
10963 if (n != ret) {
10964 (void)mg_fclose(
10965 &fi.access); /* File is bad and will be removed anyway. */
10966 remove_bad_file(conn, path);
10967 return -13;
10968 }
10969 len += ret;
10970 ret = mg_read(conn, buf, sizeof(buf));
10971 }
10972
10973 /* File is open for writing. If fclose fails, there was probably an
10974 * error flushing the buffer to disk, so the file on disk might be
10975 * broken. Delete it and return an error to the caller. */
10976 if (mg_fclose(&fi.access) != 0) {
10977 remove_bad_file(conn, path);
10978 return -14;
10979 }
10980
10981 return len;
10982}
10983#endif /* NO_FILESYSTEMS */
10984
10985
10986/* Parse a buffer:
10987 * Forward the string pointer till the end of a word, then
10988 * terminate it and forward till the begin of the next word.
10989 */
10990static int
10992{
10993 /* Forward until a space is found - use isgraph here */
10994 /* Extended ASCII characters are also treated as word characters. */
10995 /* See http://www.cplusplus.com/reference/cctype/ */
10996 while ((unsigned char)**ppw > 127 || isgraph((unsigned char)**ppw)) {
10997 (*ppw)++;
10998 }
10999
11000 /* Check end of word */
11001 if (eol) {
11002 /* must be a end of line */
11003 if ((**ppw != '\r') && (**ppw != '\n')) {
11004 return -1;
11005 }
11006 } else {
11007 /* must be a end of a word, but not a line */
11008 if (**ppw != ' ') {
11009 return -1;
11010 }
11011 }
11012
11013 /* Terminate and forward to the next word */
11014 do {
11015 **ppw = 0;
11016 (*ppw)++;
11017 } while (isspace((unsigned char)**ppw));
11018
11019 /* Check after term */
11020 if (!eol) {
11021 /* if it's not the end of line, there must be a next word */
11022 if (!isgraph((unsigned char)**ppw)) {
11023 return -1;
11024 }
11025 }
11026
11027 /* ok */
11028 return 1;
11029}
11030
11031
11032/* Parse HTTP headers from the given buffer, advance buf pointer
11033 * to the point where parsing stopped.
11034 * All parameters must be valid pointers (not NULL).
11035 * Return <0 on error. */
11036static int
11038{
11039 int i;
11040 int num_headers = 0;
11041
11042 for (i = 0; i < (int)MG_MAX_HEADERS; i++) {
11043 char *dp = *buf;
11044
11045 /* Skip all ASCII characters (>SPACE, <127), to find a ':' */
11046 while ((*dp != ':') && (*dp >= 33) && (*dp <= 126)) {
11047 dp++;
11048 }
11049 if (dp == *buf) {
11050 /* End of headers reached. */
11051 break;
11052 }
11053
11054 /* Drop all spaces after header name before : */
11055 while (*dp == ' ') {
11056 *dp = 0;
11057 dp++;
11058 }
11059 if (*dp != ':') {
11060 /* This is not a valid field. */
11061 return -1;
11062 }
11063
11064 /* End of header key (*dp == ':') */
11065 /* Truncate here and set the key name */
11066 *dp = 0;
11067 hdr[i].name = *buf;
11068
11069 /* Skip all spaces */
11070 do {
11071 dp++;
11072 } while ((*dp == ' ') || (*dp == '\t'));
11073
11074 /* The rest of the line is the value */
11075 hdr[i].value = dp;
11076
11077 /* Find end of line */
11078 while ((*dp != 0) && (*dp != '\r') && (*dp != '\n')) {
11079 dp++;
11080 };
11081
11082 /* eliminate \r */
11083 if (*dp == '\r') {
11084 *dp = 0;
11085 dp++;
11086 if (*dp != '\n') {
11087 /* This is not a valid line. */
11088 return -1;
11089 }
11090 }
11091
11092 /* here *dp is either 0 or '\n' */
11093 /* in any case, we have found a complete header */
11094 num_headers = i + 1;
11095
11096 if (*dp) {
11097 *dp = 0;
11098 dp++;
11099 *buf = dp;
11100
11101 if ((dp[0] == '\r') || (dp[0] == '\n')) {
11102 /* We've had CRLF twice in a row
11103 * This is the end of the headers */
11104 break;
11105 }
11106 /* continue within the loop, find the next header */
11107 } else {
11108 *buf = dp;
11109 break;
11110 }
11111 }
11112 return num_headers;
11113}
11114
11115
11124
11125
11126/* https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods */
11127static const struct mg_http_method_info http_methods[] = {
11128 /* HTTP (RFC 2616) */
11129 {"GET", 0, 1, 1, 1, 1},
11130 {"POST", 1, 1, 0, 0, 0},
11131 {"PUT", 1, 0, 0, 1, 0},
11132 {"DELETE", 0, 0, 0, 1, 0},
11133 {"HEAD", 0, 0, 1, 1, 1},
11134 {"OPTIONS", 0, 0, 1, 1, 0},
11135 {"CONNECT", 1, 1, 0, 0, 0},
11136 /* TRACE method (RFC 2616) is not supported for security reasons */
11137
11138 /* PATCH method (RFC 5789) */
11139 {"PATCH", 1, 0, 0, 0, 0},
11140 /* PATCH method only allowed for CGI/Lua/LSP and callbacks. */
11141
11142 /* WEBDAV (RFC 2518) */
11143 {"PROPFIND", 0, 1, 1, 1, 0},
11144 /* http://www.webdav.org/specs/rfc4918.html, 9.1:
11145 * Some PROPFIND results MAY be cached, with care,
11146 * as there is no cache validation mechanism for
11147 * most properties. This method is both safe and
11148 * idempotent (see Section 9.1 of [RFC2616]). */
11149 {"MKCOL", 0, 0, 0, 1, 0},
11150 /* http://www.webdav.org/specs/rfc4918.html, 9.1:
11151 * When MKCOL is invoked without a request body,
11152 * the newly created collection SHOULD have no
11153 * members. A MKCOL request message may contain
11154 * a message body. The precise behavior of a MKCOL
11155 * request when the body is present is undefined,
11156 * ... ==> We do not support MKCOL with body data.
11157 * This method is idempotent, but not safe (see
11158 * Section 9.1 of [RFC2616]). Responses to this
11159 * method MUST NOT be cached. */
11160
11161 /* Methods for write access to files on WEBDAV (RFC 2518) */
11162 {"LOCK", 1, 1, 0, 0, 0},
11163 {"UNLOCK", 1, 0, 0, 0, 0},
11164 {"PROPPATCH", 1, 1, 0, 0, 0},
11165 {"COPY", 1, 0, 0, 0, 0},
11166 {"MOVE", 1, 1, 0, 0, 0},
11167
11168 /* Unsupported WEBDAV Methods: */
11169 /* + 11 methods from RFC 3253 */
11170 /* ORDERPATCH (RFC 3648) */
11171 /* ACL (RFC 3744) */
11172 /* SEARCH (RFC 5323) */
11173 /* + MicroSoft extensions
11174 * https://msdn.microsoft.com/en-us/library/aa142917.aspx */
11175
11176 /* REPORT method (RFC 3253) */
11177 {"REPORT", 1, 1, 1, 1, 1},
11178 /* REPORT method only allowed for CGI/Lua/LSP and callbacks. */
11179 /* It was defined for WEBDAV in RFC 3253, Sec. 3.6
11180 * (https://tools.ietf.org/html/rfc3253#section-3.6), but seems
11181 * to be useful for REST in case a "GET request with body" is
11182 * required. */
11183
11184 {NULL, 0, 0, 0, 0, 0}
11185 /* end of list */
11186};
11187
11188
11189/* All method names */
11190static char *all_methods = NULL; /* Built by mg_init_library */
11191
11192
11193static const struct mg_http_method_info *
11195{
11196 /* Check if the method is known to the server. The list of all known
11197 * HTTP methods can be found here at
11198 * http://www.iana.org/assignments/http-methods/http-methods.xhtml
11199 */
11200 const struct mg_http_method_info *m = http_methods;
11201
11202 while (m->name) {
11203 if (!strcmp(m->name, method)) {
11204 return m;
11205 }
11206 m++;
11207 }
11208 return NULL;
11209}
11210
11211
11212static int
11214{
11215 return (get_http_method_info(method) != NULL);
11216}
11217
11218
11219/* Parse HTTP request, fill in mg_request_info structure.
11220 * This function modifies the buffer by NUL-terminating
11221 * HTTP request components, header names and header values.
11222 * Parameters:
11223 * buf (in/out): pointer to the HTTP header to parse and split
11224 * len (in): length of HTTP header buffer
11225 * re (out): parsed header as mg_request_info
11226 * buf and ri must be valid pointers (not NULL), len>0.
11227 * Returns <0 on error. */
11228static int
11229parse_http_request(char *buf, int len, struct mg_request_info *ri)
11230{
11231 int request_length;
11232 int init_skip = 0;
11233
11234 /* Reset attributes. DO NOT TOUCH is_ssl, remote_addr,
11235 * remote_port */
11236 ri->remote_user = ri->request_method = ri->request_uri = ri->http_version =
11237 NULL;
11238 ri->num_headers = 0;
11239
11240 /* RFC says that all initial whitespaces should be ignored */
11241 /* This included all leading \r and \n (isspace) */
11242 /* See table: http://www.cplusplus.com/reference/cctype/ */
11243 while ((len > 0) && isspace((unsigned char)*buf)) {
11244 buf++;
11245 len--;
11246 init_skip++;
11247 }
11248
11249 if (len == 0) {
11250 /* Incomplete request */
11251 return 0;
11252 }
11253
11254 /* Control characters are not allowed, including zero */
11255 if (iscntrl((unsigned char)*buf)) {
11256 return -1;
11257 }
11258
11259 /* Find end of HTTP header */
11261 if (request_length <= 0) {
11262 return request_length;
11263 }
11264 buf[request_length - 1] = '\0';
11265
11266 if ((*buf == 0) || (*buf == '\r') || (*buf == '\n')) {
11267 return -1;
11268 }
11269
11270 /* The first word has to be the HTTP method */
11271 ri->request_method = buf;
11272
11273 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
11274 return -1;
11275 }
11276
11277 /* The second word is the URI */
11278 ri->request_uri = buf;
11279
11280 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
11281 return -1;
11282 }
11283
11284 /* Next would be the HTTP version */
11285 ri->http_version = buf;
11286
11287 if (skip_to_end_of_word_and_terminate(&buf, 1) <= 0) {
11288 return -1;
11289 }
11290
11291 /* Check for a valid HTTP version key */
11292 if (strncmp(ri->http_version, "HTTP/", 5) != 0) {
11293 /* Invalid request */
11294 return -1;
11295 }
11296 ri->http_version += 5;
11297
11298 /* Check for a valid http method */
11300 return -1;
11301 }
11302
11303 /* Parse all HTTP headers */
11305 if (ri->num_headers < 0) {
11306 /* Error while parsing headers */
11307 return -1;
11308 }
11309
11310 return request_length + init_skip;
11311}
11312
11313
11314static int
11315parse_http_response(char *buf, int len, struct mg_response_info *ri)
11316{
11317 int response_length;
11318 int init_skip = 0;
11319 char *tmp, *tmp2;
11320 long l;
11321
11322 /* Initialize elements. */
11323 ri->http_version = ri->status_text = NULL;
11324 ri->num_headers = ri->status_code = 0;
11325
11326 /* RFC says that all initial whitespaces should be ignored */
11327 /* This included all leading \r and \n (isspace) */
11328 /* See table: http://www.cplusplus.com/reference/cctype/ */
11329 while ((len > 0) && isspace((unsigned char)*buf)) {
11330 buf++;
11331 len--;
11332 init_skip++;
11333 }
11334
11335 if (len == 0) {
11336 /* Incomplete request */
11337 return 0;
11338 }
11339
11340 /* Control characters are not allowed, including zero */
11341 if (iscntrl((unsigned char)*buf)) {
11342 return -1;
11343 }
11344
11345 /* Find end of HTTP header */
11347 if (response_length <= 0) {
11348 return response_length;
11349 }
11350 buf[response_length - 1] = '\0';
11351
11352 if ((*buf == 0) || (*buf == '\r') || (*buf == '\n')) {
11353 return -1;
11354 }
11355
11356 /* The first word is the HTTP version */
11357 /* Check for a valid HTTP version key */
11358 if (strncmp(buf, "HTTP/", 5) != 0) {
11359 /* Invalid request */
11360 return -1;
11361 }
11362 buf += 5;
11363 if (!isgraph((unsigned char)buf[0])) {
11364 /* Invalid request */
11365 return -1;
11366 }
11367 ri->http_version = buf;
11368
11369 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
11370 return -1;
11371 }
11372
11373 /* The second word is the status as a number */
11374 tmp = buf;
11375
11376 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
11377 return -1;
11378 }
11379
11380 l = strtol(tmp, &tmp2, 10);
11381 if ((l < 100) || (l >= 1000) || ((tmp2 - tmp) != 3) || (*tmp2 != 0)) {
11382 /* Everything else but a 3 digit code is invalid */
11383 return -1;
11384 }
11385 ri->status_code = (int)l;
11386
11387 /* The rest of the line is the status text */
11388 ri->status_text = buf;
11389
11390 /* Find end of status text */
11391 /* isgraph or isspace = isprint */
11392 while (isprint((unsigned char)*buf)) {
11393 buf++;
11394 }
11395 if ((*buf != '\r') && (*buf != '\n')) {
11396 return -1;
11397 }
11398 /* Terminate string and forward buf to next line */
11399 do {
11400 *buf = 0;
11401 buf++;
11402 } while (isspace((unsigned char)*buf));
11403
11404 /* Parse all HTTP headers */
11406 if (ri->num_headers < 0) {
11407 /* Error while parsing headers */
11408 return -1;
11409 }
11410
11411 return response_length + init_skip;
11412}
11413
11414
11415/* Keep reading the input (either opened file descriptor fd, or socket sock,
11416 * or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
11417 * buffer (which marks the end of HTTP request). Buffer buf may already
11418 * have some data. The length of the data is stored in nread.
11419 * Upon every read operation, increase nread by the number of bytes read. */
11420static int
11422 struct mg_connection *conn,
11423 char *buf,
11424 int bufsiz,
11425 int *nread)
11426{
11427 int request_len, n = 0;
11429 double request_timeout;
11430
11431 if (!conn) {
11432 return 0;
11433 }
11434
11436
11437 if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
11438 /* value of request_timeout is in seconds, config in milliseconds */
11440 strtod(conn->dom_ctx->config[REQUEST_TIMEOUT], NULL) / 1000.0;
11441 } else {
11443 strtod(config_options[REQUEST_TIMEOUT].default_value, NULL)
11444 / 1000.0;
11445 }
11446 if (conn->handled_requests > 0) {
11447 if (conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT]) {
11449 strtod(conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT], NULL)
11450 / 1000.0;
11451 }
11452 }
11453
11454 request_len = get_http_header_len(buf, *nread);
11455
11456 while (request_len == 0) {
11457 /* Full request not yet received */
11458 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
11459 /* Server is to be stopped. */
11460 return -1;
11461 }
11462
11463 if (*nread >= bufsiz) {
11464 /* Request too long */
11465 return -2;
11466 }
11467
11468 n = pull_inner(
11469 fp, conn, buf + *nread, bufsiz - *nread, request_timeout);
11470 if (n == -2) {
11471 /* Receive error */
11472 return -1;
11473 }
11474
11475 /* update clock after every read request */
11477
11478 if (n > 0) {
11479 *nread += n;
11480 request_len = get_http_header_len(buf, *nread);
11481 }
11482
11483 if ((n <= 0) && (request_timeout >= 0)) {
11485 > request_timeout) {
11486 /* Timeout */
11487 return -3;
11488 }
11489 }
11490 }
11491
11492 return request_len;
11493}
11494
11495
11496#if !defined(NO_CGI) || !defined(NO_FILES)
11497static int
11498forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
11499{
11500 const char *expect;
11501 char buf[MG_BUF_LEN];
11502 int success = 0;
11503
11504 if (!conn) {
11505 return 0;
11506 }
11507
11508 expect = mg_get_header(conn, "Expect");
11509 DEBUG_ASSERT(fp != NULL);
11510 if (!fp) {
11511 mg_send_http_error(conn, 500, "%s", "Error: NULL File");
11512 return 0;
11513 }
11514
11515 if ((expect != NULL) && (mg_strcasecmp(expect, "100-continue") != 0)) {
11516 /* Client sent an "Expect: xyz" header and xyz is not 100-continue.
11517 */
11518 mg_send_http_error(conn, 417, "Error: Can not fulfill expectation");
11519 } else {
11520 if (expect != NULL) {
11521 (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
11522 conn->status_code = 100;
11523 } else {
11524 conn->status_code = 200;
11525 }
11526
11527 DEBUG_ASSERT(conn->consumed_content == 0);
11528
11529 if (conn->consumed_content != 0) {
11530 mg_send_http_error(conn, 500, "%s", "Error: Size mismatch");
11531 return 0;
11532 }
11533
11534 for (;;) {
11535 int nread = mg_read(conn, buf, sizeof(buf));
11536 if (nread <= 0) {
11537 success = (nread == 0);
11538 break;
11539 }
11540 if (push_all(conn->phys_ctx, fp, sock, ssl, buf, nread) != nread) {
11541 break;
11542 }
11543 }
11544
11545 /* Each error code path in this function must send an error */
11546 if (!success) {
11547 /* NOTE: Maybe some data has already been sent. */
11548 /* TODO (low): If some data has been sent, a correct error
11549 * reply can no longer be sent, so just close the connection */
11550 mg_send_http_error(conn, 500, "%s", "");
11551 }
11552 }
11553
11554 return success;
11555}
11556#endif
11557
11558
11559#if defined(USE_TIMERS)
11560
11561#define TIMER_API static
11562#include "timer.inl"
11563
11564#endif /* USE_TIMERS */
11565
11566
11567#if !defined(NO_CGI)
11568/* This structure helps to create an environment for the spawned CGI
11569 * program.
11570 * Environment is an array of "VARIABLE=VALUE\0" ASCII strings,
11571 * last element must be NULL.
11572 * However, on Windows there is a requirement that all these
11573 * VARIABLE=VALUE\0
11574 * strings must reside in a contiguous buffer. The end of the buffer is
11575 * marked by two '\0' characters.
11576 * We satisfy both worlds: we create an envp array (which is vars), all
11577 * entries are actually pointers inside buf. */
11580 /* Data block */
11581 char *buf; /* Environment buffer */
11582 size_t buflen; /* Space available in buf */
11583 size_t bufused; /* Space taken in buf */
11584 /* Index block */
11585 char **var; /* char **envp */
11586 size_t varlen; /* Number of variables available in var */
11587 size_t varused; /* Number of variables stored in var */
11588};
11589
11590
11591static void addenv(struct cgi_environment *env,
11592 PRINTF_FORMAT_STRING(const char *fmt),
11593 ...) PRINTF_ARGS(2, 3);
11594
11595/* Append VARIABLE=VALUE\0 string to the buffer, and add a respective
11596 * pointer into the vars array. Assumes env != NULL and fmt != NULL. */
11597static void
11599{
11600 size_t i, n, space;
11601 int truncated = 0;
11602 char *added;
11603 va_list ap;
11604
11605 if ((env->varlen - env->varused) < 2) {
11606 mg_cry_internal(env->conn,
11607 "%s: Cannot register CGI variable [%s]",
11608 __func__,
11609 fmt);
11610 return;
11611 }
11612
11613 /* Calculate how much space is left in the buffer */
11614 space = (env->buflen - env->bufused);
11615
11616 do {
11617 /* Space for "\0\0" is always needed. */
11618 if (space <= 2) {
11619 /* Allocate new buffer */
11620 n = env->buflen + CGI_ENVIRONMENT_SIZE;
11621 added = (char *)mg_realloc_ctx(env->buf, n, env->conn->phys_ctx);
11622 if (!added) {
11623 /* Out of memory */
11625 env->conn,
11626 "%s: Cannot allocate memory for CGI variable [%s]",
11627 __func__,
11628 fmt);
11629 return;
11630 }
11631 /* Retarget pointers */
11632 env->buf = added;
11633 env->buflen = n;
11634 for (i = 0, n = 0; i < env->varused; i++) {
11635 env->var[i] = added + n;
11636 n += strlen(added + n) + 1;
11637 }
11638 space = (env->buflen - env->bufused);
11639 }
11640
11641 /* Make a pointer to the free space int the buffer */
11642 added = env->buf + env->bufused;
11643
11644 /* Copy VARIABLE=VALUE\0 string into the free space */
11645 va_start(ap, fmt);
11646 mg_vsnprintf(env->conn, &truncated, added, space - 1, fmt, ap);
11647 va_end(ap);
11648
11649 /* Do not add truncated strings to the environment */
11650 if (truncated) {
11651 /* Reallocate the buffer */
11652 space = 0;
11653 }
11654 } while (truncated);
11655
11656 /* Calculate number of bytes added to the environment */
11657 n = strlen(added) + 1;
11658 env->bufused += n;
11659
11660 /* Append a pointer to the added string into the envp array */
11661 env->var[env->varused] = added;
11662 env->varused++;
11663}
11664
11665/* Return 0 on success, non-zero if an error occurs. */
11666
11667static int
11669 const char *prog,
11670 struct cgi_environment *env,
11671 int cgi_config_idx)
11672{
11673 const char *s;
11674 struct vec var_vec;
11676 int i, truncated, uri_len;
11677
11678 if ((conn == NULL) || (prog == NULL) || (env == NULL)) {
11679 return -1;
11680 }
11681
11682 env->conn = conn;
11683 env->buflen = CGI_ENVIRONMENT_SIZE;
11684 env->bufused = 0;
11685 env->buf = (char *)mg_malloc_ctx(env->buflen, conn->phys_ctx);
11686 if (env->buf == NULL) {
11687 mg_cry_internal(conn,
11688 "%s: Not enough memory for environmental buffer",
11689 __func__);
11690 return -1;
11691 }
11692 env->varlen = MAX_CGI_ENVIR_VARS;
11693 env->varused = 0;
11694 env->var =
11695 (char **)mg_malloc_ctx(env->varlen * sizeof(char *), conn->phys_ctx);
11696 if (env->var == NULL) {
11697 mg_cry_internal(conn,
11698 "%s: Not enough memory for environmental variables",
11699 __func__);
11700 mg_free(env->buf);
11701 return -1;
11702 }
11703
11704 addenv(env, "SERVER_NAME=%s", conn->dom_ctx->config[AUTHENTICATION_DOMAIN]);
11705 addenv(env, "SERVER_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
11706 addenv(env, "DOCUMENT_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
11707 if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) {
11708 addenv(env,
11709 "FALLBACK_DOCUMENT_ROOT=%s",
11710 conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]);
11711 }
11712 addenv(env, "SERVER_SOFTWARE=CivetWeb/%s", mg_version());
11713
11714 /* Prepare the environment block */
11715 addenv(env, "%s", "GATEWAY_INTERFACE=CGI/1.1");
11716 addenv(env, "%s", "SERVER_PROTOCOL=HTTP/1.1");
11717 addenv(env, "%s", "REDIRECT_STATUS=200"); /* For PHP */
11718
11719 addenv(env, "SERVER_PORT=%d", conn->request_info.server_port);
11720
11721 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
11722 addenv(env, "REMOTE_ADDR=%s", src_addr);
11723
11724 addenv(env, "REQUEST_METHOD=%s", conn->request_info.request_method);
11725 addenv(env, "REMOTE_PORT=%d", conn->request_info.remote_port);
11726
11727 addenv(env, "REQUEST_URI=%s", conn->request_info.request_uri);
11728 addenv(env, "LOCAL_URI=%s", conn->request_info.local_uri);
11729 addenv(env, "LOCAL_URI_RAW=%s", conn->request_info.local_uri_raw);
11730
11731 /* SCRIPT_NAME */
11732 uri_len = (int)strlen(conn->request_info.local_uri);
11733 if (conn->path_info == NULL) {
11734 if (conn->request_info.local_uri[uri_len - 1] != '/') {
11735 /* URI: /path_to_script/script.cgi */
11736 addenv(env, "SCRIPT_NAME=%s", conn->request_info.local_uri);
11737 } else {
11738 /* URI: /path_to_script/ ... using index.cgi */
11739 const char *index_file = strrchr(prog, '/');
11740 if (index_file) {
11741 addenv(env,
11742 "SCRIPT_NAME=%s%s",
11743 conn->request_info.local_uri,
11744 index_file + 1);
11745 }
11746 }
11747 } else {
11748 /* URI: /path_to_script/script.cgi/path_info */
11749 addenv(env,
11750 "SCRIPT_NAME=%.*s",
11751 uri_len - (int)strlen(conn->path_info),
11752 conn->request_info.local_uri);
11753 }
11754
11755 addenv(env, "SCRIPT_FILENAME=%s", prog);
11756 if (conn->path_info == NULL) {
11757 addenv(env, "PATH_TRANSLATED=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
11758 } else {
11759 addenv(env,
11760 "PATH_TRANSLATED=%s%s",
11761 conn->dom_ctx->config[DOCUMENT_ROOT],
11762 conn->path_info);
11763 }
11764
11765 addenv(env, "HTTPS=%s", (conn->ssl == NULL) ? "off" : "on");
11766
11767 if ((s = mg_get_header(conn, "Content-Type")) != NULL) {
11768 addenv(env, "CONTENT_TYPE=%s", s);
11769 }
11770 if (conn->request_info.query_string != NULL) {
11771 addenv(env, "QUERY_STRING=%s", conn->request_info.query_string);
11772 }
11773 if ((s = mg_get_header(conn, "Content-Length")) != NULL) {
11774 addenv(env, "CONTENT_LENGTH=%s", s);
11775 }
11776 if ((s = getenv("PATH")) != NULL) {
11777 addenv(env, "PATH=%s", s);
11778 }
11779 if (conn->path_info != NULL) {
11780 addenv(env, "PATH_INFO=%s", conn->path_info);
11781 }
11782
11783 if (conn->status_code > 0) {
11784 /* CGI error handler should show the status code */
11785 addenv(env, "STATUS=%d", conn->status_code);
11786 }
11787
11788#if defined(_WIN32)
11789 if ((s = getenv("COMSPEC")) != NULL) {
11790 addenv(env, "COMSPEC=%s", s);
11791 }
11792 if ((s = getenv("SYSTEMROOT")) != NULL) {
11793 addenv(env, "SYSTEMROOT=%s", s);
11794 }
11795 if ((s = getenv("SystemDrive")) != NULL) {
11796 addenv(env, "SystemDrive=%s", s);
11797 }
11798 if ((s = getenv("ProgramFiles")) != NULL) {
11799 addenv(env, "ProgramFiles=%s", s);
11800 }
11801 if ((s = getenv("ProgramFiles(x86)")) != NULL) {
11802 addenv(env, "ProgramFiles(x86)=%s", s);
11803 }
11804#else
11805 if ((s = getenv("LD_LIBRARY_PATH")) != NULL) {
11806 addenv(env, "LD_LIBRARY_PATH=%s", s);
11807 }
11808#endif /* _WIN32 */
11809
11810 if ((s = getenv("PERLLIB")) != NULL) {
11811 addenv(env, "PERLLIB=%s", s);
11812 }
11813
11814 if (conn->request_info.remote_user != NULL) {
11815 addenv(env, "REMOTE_USER=%s", conn->request_info.remote_user);
11816 addenv(env, "%s", "AUTH_TYPE=Digest");
11817 }
11818
11819 /* Add all headers as HTTP_* variables */
11820 for (i = 0; i < conn->request_info.num_headers; i++) {
11821
11822 (void)mg_snprintf(conn,
11823 &truncated,
11825 sizeof(http_var_name),
11826 "HTTP_%s",
11827 conn->request_info.http_headers[i].name);
11828
11829 if (truncated) {
11830 mg_cry_internal(conn,
11831 "%s: HTTP header variable too long [%s]",
11832 __func__,
11833 conn->request_info.http_headers[i].name);
11834 continue;
11835 }
11836
11837 /* Convert variable name into uppercase, and change - to _ */
11838 for (p = http_var_name; *p != '\0'; p++) {
11839 if (*p == '-') {
11840 *p = '_';
11841 }
11842 *p = (char)toupper((unsigned char)*p);
11843 }
11844
11845 addenv(env,
11846 "%s=%s",
11848 conn->request_info.http_headers[i].value);
11849 }
11850
11851 /* Add user-specified variables */
11852 s = conn->dom_ctx->config[CGI_ENVIRONMENT + cgi_config_idx];
11853 while ((s = next_option(s, &var_vec, NULL)) != NULL) {
11854 addenv(env, "%.*s", (int)var_vec.len, var_vec.ptr);
11855 }
11856
11857 env->var[env->varused] = NULL;
11858 env->buf[env->bufused] = '\0';
11859
11860 return 0;
11861}
11862
11863
11864/* Data for CGI process control: PID and number of references */
11866 pid_t pid;
11867 ptrdiff_t references;
11868};
11869
11870static int
11872{
11873 /* Waitpid checks for child status and won't work for a pid that does
11874 * not identify a child of the current process. Thus, if the pid is
11875 * reused, we will not affect a different process. */
11877 int status = 0;
11878 ptrdiff_t refs;
11879 pid_t ret_pid;
11880
11881 ret_pid = waitpid(proc->pid, &status, WNOHANG);
11882 if ((ret_pid != (pid_t)-1) && (status == 0)) {
11883 /* Stop child process */
11884 DEBUG_TRACE("CGI timer: Stop child process %d\n", proc->pid);
11885 kill(proc->pid, SIGABRT);
11886
11887 /* Wait until process is terminated (don't leave zombies) */
11888 while (waitpid(proc->pid, &status, 0) != (pid_t)-1) /* nop */
11889 ;
11890 } else {
11891 DEBUG_TRACE("CGI timer: Child process %d already stopped\n", proc->pid);
11892 }
11893 /* Dec reference counter */
11894 refs = mg_atomic_dec(&proc->references);
11895 if (refs == 0) {
11896 /* no more references - free data */
11897 mg_free(data);
11898 }
11899
11900 return 0;
11901}
11902
11903
11904/* Local (static) function assumes all arguments are valid. */
11905static void
11907 const char *prog,
11908 int cgi_config_idx)
11909{
11910 char *buf;
11911 size_t buflen;
11912 int headers_len, data_len, i, truncated;
11913 int fdin[2] = {-1, -1}, fdout[2] = {-1, -1}, fderr[2] = {-1, -1};
11914 const char *status, *status_text;
11915 char *pbuf, dir[UTF8_PATH_MAX], *p;
11916 struct mg_request_info ri;
11917 struct cgi_environment blk;
11918 FILE *in = NULL, *out = NULL, *err = NULL;
11920 pid_t pid = (pid_t)-1;
11921 struct process_control_data *proc = NULL;
11922 char *cfg_buffering = conn->dom_ctx->config[CGI_BUFFERING + cgi_config_idx];
11923 int no_buffering = 0;
11924
11925#if defined(USE_TIMERS)
11926 double cgi_timeout;
11927 if (conn->dom_ctx->config[CGI_TIMEOUT + cgi_config_idx]) {
11928 /* Get timeout in seconds */
11929 cgi_timeout =
11930 atof(conn->dom_ctx->config[CGI_TIMEOUT + cgi_config_idx]) * 0.001;
11931 } else {
11932 cgi_timeout =
11933 atof(config_options[REQUEST_TIMEOUT].default_value) * 0.001;
11934 }
11935#endif
11936 if (cfg_buffering != NULL) {
11937 if (!mg_strcasecmp(cfg_buffering, "no")) {
11938 no_buffering = 1;
11939 }
11940 }
11941
11942 buf = NULL;
11943 buflen = conn->phys_ctx->max_request_size;
11945 if (i != 0) {
11946 blk.buf = NULL;
11947 blk.var = NULL;
11948 goto done;
11949 }
11950
11951 /* CGI must be executed in its own directory. 'dir' must point to the
11952 * directory containing executable program, 'p' must point to the
11953 * executable program name relative to 'dir'. */
11954 (void)mg_snprintf(conn, &truncated, dir, sizeof(dir), "%s", prog);
11955
11956 if (truncated) {
11957 mg_cry_internal(conn, "Error: CGI program \"%s\": Path too long", prog);
11958 mg_send_http_error(conn, 500, "Error: %s", "CGI path too long");
11959 goto done;
11960 }
11961
11962 if ((p = strrchr(dir, '/')) != NULL) {
11963 *p++ = '\0';
11964 } else {
11965 dir[0] = '.';
11966 dir[1] = '\0';
11967 p = (char *)prog;
11968 }
11969
11970 if ((pipe(fdin) != 0) || (pipe(fdout) != 0) || (pipe(fderr) != 0)) {
11971 status = strerror(ERRNO);
11973 conn,
11974 "Error: CGI program \"%s\": Can not create CGI pipes: %s",
11975 prog,
11976 status);
11977 mg_send_http_error(conn,
11978 500,
11979 "Error: Cannot create CGI pipe: %s",
11980 status);
11981 goto done;
11982 }
11983
11984 proc = (struct process_control_data *)
11985 mg_malloc_ctx(sizeof(struct process_control_data), conn->phys_ctx);
11986 if (proc == NULL) {
11987 mg_cry_internal(conn, "Error: CGI program \"%s\": Out or memory", prog);
11988 mg_send_http_error(conn, 500, "Error: Out of memory [%s]", prog);
11989 goto done;
11990 }
11991
11992 DEBUG_TRACE("CGI: spawn %s %s\n", dir, p);
11994 conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir, cgi_config_idx);
11995
11996 if (pid == (pid_t)-1) {
11997 status = strerror(ERRNO);
11999 conn,
12000 "Error: CGI program \"%s\": Can not spawn CGI process: %s",
12001 prog,
12002 status);
12003 mg_send_http_error(conn, 500, "Error: Cannot spawn CGI process");
12004 mg_free(proc);
12005 proc = NULL;
12006 goto done;
12007 }
12008
12009 /* Store data in shared process_control_data */
12010 proc->pid = pid;
12011 proc->references = 1;
12012
12013#if defined(USE_TIMERS)
12014 if (cgi_timeout > 0.0) {
12015 proc->references = 2;
12016
12017 // Start a timer for CGI
12018 timer_add(conn->phys_ctx,
12019 cgi_timeout /* in seconds */,
12020 0.0,
12021 1,
12023 (void *)proc,
12024 NULL);
12025 }
12026#endif
12027
12028 /* Parent closes only one side of the pipes.
12029 * If we don't mark them as closed, close() attempt before
12030 * return from this function throws an exception on Windows.
12031 * Windows does not like when closed descriptor is closed again. */
12032 (void)close(fdin[0]);
12033 (void)close(fdout[1]);
12034 (void)close(fderr[1]);
12035 fdin[0] = fdout[1] = fderr[1] = -1;
12036
12037 if (((in = fdopen(fdin[1], "wb")) == NULL)
12038 || ((out = fdopen(fdout[0], "rb")) == NULL)
12039 || ((err = fdopen(fderr[0], "rb")) == NULL)) {
12040 status = strerror(ERRNO);
12041 mg_cry_internal(conn,
12042 "Error: CGI program \"%s\": Can not open fd: %s",
12043 prog,
12044 status);
12045 mg_send_http_error(conn,
12046 500,
12047 "Error: CGI can not open fd\nfdopen: %s",
12048 status);
12049 goto done;
12050 }
12051
12052 setbuf(in, NULL);
12053 setbuf(out, NULL);
12054 setbuf(err, NULL);
12055 fout.access.fp = out;
12056
12057 if ((conn->content_len != 0) || (conn->is_chunked)) {
12058 DEBUG_TRACE("CGI: send body data (%" INT64_FMT ")\n",
12059 conn->content_len);
12060
12061 /* This is a POST/PUT request, or another request with body data. */
12062 if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
12063 /* Error sending the body data */
12065 conn,
12066 "Error: CGI program \"%s\": Forward body data failed",
12067 prog);
12068 goto done;
12069 }
12070 }
12071
12072 /* Close so child gets an EOF. */
12073 fclose(in);
12074 in = NULL;
12075 fdin[1] = -1;
12076
12077 /* Now read CGI reply into a buffer. We need to set correct
12078 * status code, thus we need to see all HTTP headers first.
12079 * Do not send anything back to client, until we buffer in all
12080 * HTTP headers. */
12081 data_len = 0;
12082 buf = (char *)mg_malloc_ctx(buflen, conn->phys_ctx);
12083 if (buf == NULL) {
12084 mg_send_http_error(conn,
12085 500,
12086 "Error: Not enough memory for CGI buffer (%u bytes)",
12087 (unsigned int)buflen);
12089 conn,
12090 "Error: CGI program \"%s\": Not enough memory for buffer (%u "
12091 "bytes)",
12092 prog,
12093 (unsigned int)buflen);
12094 goto done;
12095 }
12096
12097 DEBUG_TRACE("CGI: %s", "wait for response");
12098 headers_len = read_message(out, conn, buf, (int)buflen, &data_len);
12099 DEBUG_TRACE("CGI: response: %li", (signed long)headers_len);
12100
12101 if (headers_len <= 0) {
12102
12103 /* Could not parse the CGI response. Check if some error message on
12104 * stderr. */
12105 i = pull_all(err, conn, buf, (int)buflen);
12106 if (i > 0) {
12107 /* CGI program explicitly sent an error */
12108 /* Write the error message to the internal log */
12109 mg_cry_internal(conn,
12110 "Error: CGI program \"%s\" sent error "
12111 "message: [%.*s]",
12112 prog,
12113 i,
12114 buf);
12115 /* Don't send the error message back to the client */
12116 mg_send_http_error(conn,
12117 500,
12118 "Error: CGI program \"%s\" failed.",
12119 prog);
12120 } else {
12121 /* CGI program did not explicitly send an error, but a broken
12122 * respon header */
12123 mg_cry_internal(conn,
12124 "Error: CGI program sent malformed or too big "
12125 "(>%u bytes) HTTP headers: [%.*s]",
12126 (unsigned)buflen,
12127 data_len,
12128 buf);
12129
12130 mg_send_http_error(conn,
12131 500,
12132 "Error: CGI program sent malformed or too big "
12133 "(>%u bytes) HTTP headers: [%.*s]",
12134 (unsigned)buflen,
12135 data_len,
12136 buf);
12137 }
12138
12139 /* in both cases, abort processing CGI */
12140 goto done;
12141 }
12142
12143 pbuf = buf;
12144 buf[headers_len - 1] = '\0';
12146
12147 /* Make up and send the status line */
12148 status_text = "OK";
12149 if ((status = get_header(ri.http_headers, ri.num_headers, "Status"))
12150 != NULL) {
12151 conn->status_code = atoi(status);
12152 status_text = status;
12153 while (isdigit((unsigned char)*status_text) || *status_text == ' ') {
12154 status_text++;
12155 }
12156 } else if (get_header(ri.http_headers, ri.num_headers, "Location")
12157 != NULL) {
12158 conn->status_code = 307;
12159 } else {
12160 conn->status_code = 200;
12161 }
12162
12163 if (!should_keep_alive(conn)) {
12164 conn->must_close = 1;
12165 }
12166
12167 DEBUG_TRACE("CGI: response %u %s", conn->status_code, status_text);
12168
12169 (void)mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, status_text);
12170
12171 /* Send headers */
12172 for (i = 0; i < ri.num_headers; i++) {
12173 DEBUG_TRACE("CGI header: %s: %s",
12174 ri.http_headers[i].name,
12175 ri.http_headers[i].value);
12176 mg_printf(conn,
12177 "%s: %s\r\n",
12178 ri.http_headers[i].name,
12179 ri.http_headers[i].value);
12180 }
12181 mg_write(conn, "\r\n", 2);
12182
12183 /* Send chunk of data that may have been read after the headers */
12184 mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len));
12185
12186 /* Read the rest of CGI output and send to the client */
12187 DEBUG_TRACE("CGI: %s", "forward all data");
12188 send_file_data(conn, &fout, 0, INT64_MAX, no_buffering); /* send CGI data */
12189 DEBUG_TRACE("CGI: %s", "all data sent");
12190
12191done:
12192 mg_free(blk.var);
12193 mg_free(blk.buf);
12194
12195 if (pid != (pid_t)-1) {
12196 abort_cgi_process((void *)proc);
12197 }
12198
12199 if (fdin[0] != -1) {
12200 close(fdin[0]);
12201 }
12202 if (fdout[1] != -1) {
12203 close(fdout[1]);
12204 }
12205 if (fderr[1] != -1) {
12206 close(fderr[1]);
12207 }
12208
12209 if (in != NULL) {
12210 fclose(in);
12211 } else if (fdin[1] != -1) {
12212 close(fdin[1]);
12213 }
12214
12215 if (out != NULL) {
12216 fclose(out);
12217 } else if (fdout[0] != -1) {
12218 close(fdout[0]);
12219 }
12220
12221 if (err != NULL) {
12222 fclose(err);
12223 } else if (fderr[0] != -1) {
12224 close(fderr[0]);
12225 }
12226
12227 mg_free(buf);
12228}
12229#endif /* !NO_CGI */
12230
12231
12232#if !defined(NO_FILES)
12233static void
12234dav_mkcol(struct mg_connection *conn, const char *path)
12235{
12236 int rc, body_len;
12237 struct de de;
12238
12239 if (conn == NULL) {
12240 return;
12241 }
12242
12243 /* TODO (mid): Check the mg_send_http_error situations in this function
12244 */
12245
12246 memset(&de.file, 0, sizeof(de.file));
12247 if (!mg_stat(conn, path, &de.file)) {
12248 mg_cry_internal(conn,
12249 "%s: mg_stat(%s) failed: %s",
12250 __func__,
12251 path,
12252 strerror(ERRNO));
12253 }
12254
12255 if (de.file.last_modified) {
12256 /* TODO (mid): This check does not seem to make any sense ! */
12257 /* TODO (mid): Add a webdav unit test first, before changing
12258 * anything here. */
12260 conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO));
12261 return;
12262 }
12263
12264 body_len = conn->data_len - conn->request_len;
12265 if (body_len > 0) {
12267 conn, 415, "Error: mkcol(%s): %s", path, strerror(ERRNO));
12268 return;
12269 }
12270
12271 rc = mg_mkdir(conn, path, 0755);
12272 DEBUG_TRACE("mkdir %s: %i", path, rc);
12273 if (rc == 0) {
12274 /* Create 201 "Created" response */
12275 mg_response_header_start(conn, 201);
12278 mg_response_header_add(conn, "Content-Length", "0", -1);
12279
12280 /* Send all headers - there is no body */
12282 } else {
12283 int http_status = 500;
12284 switch (errno) {
12285 case EEXIST:
12286 http_status = 405;
12287 break;
12288 case EACCES:
12289 http_status = 403;
12290 break;
12291 case ENOENT:
12292 http_status = 409;
12293 break;
12294 }
12295
12296 mg_send_http_error(conn,
12298 "Error processing %s: %s",
12299 path,
12300 strerror(ERRNO));
12301 }
12302}
12303
12304
12305/* Forward decrlaration */
12306static int get_uri_type(const char *uri);
12307static const char *
12308get_rel_url_at_current_server(const char *uri,
12309 const struct mg_connection *conn);
12310
12311
12312static void
12313dav_move_file(struct mg_connection *conn, const char *path, int do_copy)
12314{
12315 const char *overwrite_hdr;
12316 const char *destination_hdr;
12317 const char *root;
12318 int rc, dest_uri_type;
12319 int http_status = 400;
12320 int do_overwrite = 0;
12321 int destination_ok = 0;
12323 struct mg_file_stat ignored;
12324
12325 if (conn == NULL) {
12326 return;
12327 }
12328
12329 root = conn->dom_ctx->config[DOCUMENT_ROOT];
12330 overwrite_hdr = mg_get_header(conn, "Overwrite");
12331 destination_hdr = mg_get_header(conn, "Destination");
12332 if ((overwrite_hdr != NULL) && (toupper(overwrite_hdr[0]) == 'T')) {
12333 do_overwrite = 1;
12334 }
12335
12336 if ((destination_hdr == NULL) || (destination_hdr[0] == 0)) {
12337 mg_send_http_error(conn, 400, "%s", "Missing destination");
12338 return;
12339 }
12340
12341 if (root != NULL) {
12342 char *local_dest = NULL;
12344 if (dest_uri_type == 2) {
12346 } else if ((dest_uri_type == 3) || (dest_uri_type == 4)) {
12347 const char *h =
12349 if (h) {
12350 size_t len = strlen(h);
12351 local_dest = mg_malloc_ctx(len + 1, conn->phys_ctx);
12352 mg_url_decode(h, (int)len, local_dest, (int)len + 1, 0);
12353 }
12354 }
12355 if (local_dest != NULL) {
12357 if (local_dest[0] == '/') {
12358 int trunc_check = 0;
12359 mg_snprintf(conn,
12360 &trunc_check,
12361 dest_path,
12362 sizeof(dest_path),
12363 "%s/%s",
12364 root,
12365 local_dest);
12366 if (trunc_check == 0) {
12367 destination_ok = 1;
12368 }
12369 }
12371 }
12372 }
12373
12374 if (!destination_ok) {
12375 mg_send_http_error(conn, 502, "%s", "Illegal destination");
12376 return;
12377 }
12378
12379 /* Check now if this file exists */
12380 if (mg_stat(conn, dest_path, &ignored)) {
12381 /* File exists */
12382 if (do_overwrite) {
12383 /* Overwrite allowed: delete the file first */
12384 if (0 != remove(dest_path)) {
12385 /* No overwrite: return error */
12386 mg_send_http_error(conn,
12387 403,
12388 "Cannot overwrite file: %s",
12389 dest_path);
12390 return;
12391 }
12392 } else {
12393 /* No overwrite: return error */
12394 mg_send_http_error(conn,
12395 412,
12396 "Destination already exists: %s",
12397 dest_path);
12398 return;
12399 }
12400 }
12401
12402 /* Copy / Move / Rename operation. */
12403 DEBUG_TRACE("%s %s to %s", (do_copy ? "copy" : "move"), path, dest_path);
12404#if defined(_WIN32)
12405 {
12406 /* For Windows, we need to convert from UTF-8 to UTF-16 first. */
12407 wchar_t wSource[UTF16_PATH_MAX];
12408 wchar_t wDest[UTF16_PATH_MAX];
12409 BOOL ok;
12410
12413 if (do_copy) {
12415 } else {
12416 ok = MoveFileExW(wSource,
12417 wDest,
12419 }
12420 if (ok) {
12421 rc = 0;
12422 } else {
12423 DWORD lastErr = GetLastError();
12425 mg_send_http_error(conn,
12426 412,
12427 "Destination already exists: %s",
12428 dest_path);
12429 return;
12430 }
12431 rc = -1;
12432 http_status = 400;
12433 }
12434 }
12435
12436#else
12437 {
12438 /* Linux uses already UTF-8, we don't need to convert file names. */
12439
12440 if (do_copy) {
12441 /* TODO: COPY for Linux. */
12442 mg_send_http_error(conn, 403, "%s", "COPY forbidden");
12443 return;
12444 }
12445
12446 rc = rename(path, dest_path);
12447 if (rc) {
12448 switch (errno) {
12449 case EEXIST:
12450 http_status = 412;
12451 break;
12452 case EACCES:
12453 http_status = 403;
12454 break;
12455 case ENOENT:
12456 http_status = 409;
12457 break;
12458 }
12459 }
12460 }
12461#endif
12462
12463 if (rc == 0) {
12464 /* Create 204 "No Content" response */
12465 mg_response_header_start(conn, 204);
12466 mg_response_header_add(conn, "Content-Length", "0", -1);
12467
12468 /* Send all headers - there is no body */
12470 } else {
12471 mg_send_http_error(conn, http_status, "Operation failed");
12472 }
12473}
12474
12475
12476static void
12477put_file(struct mg_connection *conn, const char *path)
12478{
12479 struct mg_file file = STRUCT_FILE_INITIALIZER;
12480 const char *range;
12481 int64_t r1, r2;
12482 int rc;
12483
12484 if (conn == NULL) {
12485 return;
12486 }
12487
12488 DEBUG_TRACE("store %s", path);
12489
12490 if (mg_stat(conn, path, &file.stat)) {
12491 /* File already exists */
12492 conn->status_code = 200;
12493
12494 if (file.stat.is_directory) {
12495 /* This is an already existing directory,
12496 * so there is nothing to do for the server. */
12497 rc = 0;
12498
12499 } else {
12500 /* File exists and is not a directory. */
12501 /* Can it be replaced? */
12502
12503 /* Check if the server may write this file */
12504 if (access(path, W_OK) == 0) {
12505 /* Access granted */
12506 rc = 1;
12507 } else {
12509 conn,
12510 403,
12511 "Error: Put not possible\nReplacing %s is not allowed",
12512 path);
12513 return;
12514 }
12515 }
12516 } else {
12517 /* File should be created */
12518 conn->status_code = 201;
12519 rc = put_dir(conn, path);
12520 }
12521
12522 if (rc == 0) {
12523 /* put_dir returns 0 if path is a directory */
12524
12525 /* Create response */
12529 mg_response_header_add(conn, "Content-Length", "0", -1);
12530
12531 /* Send all headers - there is no body */
12533
12534 /* Request to create a directory has been fulfilled successfully.
12535 * No need to put a file. */
12536 return;
12537 }
12538
12539 if (rc == -1) {
12540 /* put_dir returns -1 if the path is too long */
12541 mg_send_http_error(conn,
12542 414,
12543 "Error: Path too long\nput_dir(%s): %s",
12544 path,
12545 strerror(ERRNO));
12546 return;
12547 }
12548
12549 if (rc == -2) {
12550 /* put_dir returns -2 if the directory can not be created */
12551 mg_send_http_error(conn,
12552 500,
12553 "Error: Can not create directory\nput_dir(%s): %s",
12554 path,
12555 strerror(ERRNO));
12556 return;
12557 }
12558
12559 /* A file should be created or overwritten. */
12560 /* Currently CivetWeb does not need read+write access. */
12561 if (!mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &file)
12562 || file.access.fp == NULL) {
12563 (void)mg_fclose(&file.access);
12564 mg_send_http_error(conn,
12565 500,
12566 "Error: Can not create file\nfopen(%s): %s",
12567 path,
12568 strerror(ERRNO));
12569 return;
12570 }
12571
12572 fclose_on_exec(&file.access, conn);
12573 range = mg_get_header(conn, "Content-Range");
12574 r1 = r2 = 0;
12575 if ((range != NULL) && parse_range_header(range, &r1, &r2) > 0) {
12576 conn->status_code = 206; /* Partial content */
12577 if (0 != fseeko(file.access.fp, r1, SEEK_SET)) {
12578 mg_send_http_error(conn,
12579 500,
12580 "Error: Internal error processing file %s",
12581 path);
12582 return;
12583 }
12584 }
12585
12586 if (!forward_body_data(conn, file.access.fp, INVALID_SOCKET, NULL)) {
12587 /* forward_body_data failed.
12588 * The error code has already been sent to the client,
12589 * and conn->status_code is already set. */
12590 (void)mg_fclose(&file.access);
12591 return;
12592 }
12593
12594 if (mg_fclose(&file.access) != 0) {
12595 /* fclose failed. This might have different reasons, but a likely
12596 * one is "no space on disk", http 507. */
12597 conn->status_code = 507;
12598 }
12599
12600 /* Create response (status_code has been set before) */
12604 mg_response_header_add(conn, "Content-Length", "0", -1);
12605
12606 /* Send all headers - there is no body */
12608}
12609
12610
12611static void
12612delete_file(struct mg_connection *conn, const char *path)
12613{
12614 struct de de;
12615 memset(&de.file, 0, sizeof(de.file));
12616 if (!mg_stat(conn, path, &de.file)) {
12617 /* mg_stat returns 0 if the file does not exist */
12618 mg_send_http_error(conn,
12619 404,
12620 "Error: Cannot delete file\nFile %s not found",
12621 path);
12622 return;
12623 }
12624
12625 DEBUG_TRACE("delete %s", path);
12626
12627 if (de.file.is_directory) {
12628 if (remove_directory(conn, path)) {
12629 /* Delete is successful: Return 204 without content. */
12630 mg_send_http_error(conn, 204, "%s", "");
12631 } else {
12632 /* Delete is not successful: Return 500 (Server error). */
12633 mg_send_http_error(conn, 500, "Error: Could not delete %s", path);
12634 }
12635 return;
12636 }
12637
12638 /* This is an existing file (not a directory).
12639 * Check if write permission is granted. */
12640 if (access(path, W_OK) != 0) {
12641 /* File is read only */
12643 conn,
12644 403,
12645 "Error: Delete not possible\nDeleting %s is not allowed",
12646 path);
12647 return;
12648 }
12649
12650 /* Try to delete it. */
12651 if (mg_remove(conn, path) == 0) {
12652 /* Delete was successful: Return 204 without content. */
12653 mg_response_header_start(conn, 204);
12656 mg_response_header_add(conn, "Content-Length", "0", -1);
12658
12659 } else {
12660 /* Delete not successful (file locked). */
12661 mg_send_http_error(conn,
12662 423,
12663 "Error: Cannot delete file\nremove(%s): %s",
12664 path,
12665 strerror(ERRNO));
12666 }
12667}
12668#endif /* !NO_FILES */
12669
12670
12671#if !defined(NO_FILESYSTEMS)
12672static void
12673send_ssi_file(struct mg_connection *, const char *, struct mg_file *, int);
12674
12675
12676static void
12678 const char *ssi,
12679 char *tag,
12680 int include_level)
12681{
12682 char file_name[MG_BUF_LEN], path[512], *p;
12683 struct mg_file file = STRUCT_FILE_INITIALIZER;
12684 size_t len;
12685 int truncated = 0;
12686
12687 if (conn == NULL) {
12688 return;
12689 }
12690
12691 /* sscanf() is safe here, since send_ssi_file() also uses buffer
12692 * of size MG_BUF_LEN to get the tag. So strlen(tag) is
12693 * always < MG_BUF_LEN. */
12694 if (sscanf(tag, " virtual=\"%511[^\"]\"", file_name) == 1) {
12695 /* File name is relative to the webserver root */
12696 file_name[511] = 0;
12697 (void)mg_snprintf(conn,
12698 &truncated,
12699 path,
12700 sizeof(path),
12701 "%s/%s",
12702 conn->dom_ctx->config[DOCUMENT_ROOT],
12703 file_name);
12704
12705 } else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) {
12706 /* File name is relative to the webserver working directory
12707 * or it is absolute system path */
12708 file_name[511] = 0;
12709 (void)
12710 mg_snprintf(conn, &truncated, path, sizeof(path), "%s", file_name);
12711
12712 } else if ((sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1)
12713 || (sscanf(tag, " \"%511[^\"]\"", file_name) == 1)) {
12714 /* File name is relative to the current document */
12715 file_name[511] = 0;
12716 (void)mg_snprintf(conn, &truncated, path, sizeof(path), "%s", ssi);
12717
12718 if (!truncated) {
12719 if ((p = strrchr(path, '/')) != NULL) {
12720 p[1] = '\0';
12721 }
12722 len = strlen(path);
12723 (void)mg_snprintf(conn,
12724 &truncated,
12725 path + len,
12726 sizeof(path) - len,
12727 "%s",
12728 file_name);
12729 }
12730
12731 } else {
12732 mg_cry_internal(conn, "Bad SSI #include: [%s]", tag);
12733 return;
12734 }
12735
12736 if (truncated) {
12737 mg_cry_internal(conn, "SSI #include path length overflow: [%s]", tag);
12738 return;
12739 }
12740
12741 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, &file)) {
12742 mg_cry_internal(conn,
12743 "Cannot open SSI #include: [%s]: fopen(%s): %s",
12744 tag,
12745 path,
12746 strerror(ERRNO));
12747 } else {
12748 fclose_on_exec(&file.access, conn);
12749 if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path)
12750 > 0) {
12751 send_ssi_file(conn, path, &file, include_level + 1);
12752 } else {
12753 send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
12754 }
12755 (void)mg_fclose(&file.access); /* Ignore errors for readonly files */
12756 }
12757}
12758
12759
12760#if !defined(NO_POPEN)
12761static void
12762do_ssi_exec(struct mg_connection *conn, char *tag)
12763{
12764 char cmd[1024] = "";
12765 struct mg_file file = STRUCT_FILE_INITIALIZER;
12766
12767 if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) {
12768 mg_cry_internal(conn, "Bad SSI #exec: [%s]", tag);
12769 } else {
12770 cmd[1023] = 0;
12771 if ((file.access.fp = popen(cmd, "r")) == NULL) {
12772 mg_cry_internal(conn,
12773 "Cannot SSI #exec: [%s]: %s",
12774 cmd,
12775 strerror(ERRNO));
12776 } else {
12777 send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
12778 pclose(file.access.fp);
12779 }
12780 }
12781}
12782#endif /* !NO_POPEN */
12783
12784
12785static int
12787{
12788 if (filep == NULL) {
12789 return EOF;
12790 }
12791
12792 if (filep->access.fp != NULL) {
12793 return fgetc(filep->access.fp);
12794 } else {
12795 return EOF;
12796 }
12797}
12798
12799
12800static void
12802 const char *path,
12803 struct mg_file *filep,
12804 int include_level)
12805{
12806 char buf[MG_BUF_LEN];
12807 int ch, len, in_tag, in_ssi_tag;
12808
12809 if (include_level > 10) {
12810 mg_cry_internal(conn, "SSI #include level is too deep (%s)", path);
12811 return;
12812 }
12813
12814 in_tag = in_ssi_tag = len = 0;
12815
12816 /* Read file, byte by byte, and look for SSI include tags */
12817 while ((ch = mg_fgetc(filep)) != EOF) {
12818
12819 if (in_tag) {
12820 /* We are in a tag, either SSI tag or html tag */
12821
12822 if (ch == '>') {
12823 /* Tag is closing */
12824 buf[len++] = '>';
12825
12826 if (in_ssi_tag) {
12827 /* Handle SSI tag */
12828 buf[len] = 0;
12829
12830 if ((len > 12) && !memcmp(buf + 5, "include", 7)) {
12831 do_ssi_include(conn, path, buf + 12, include_level + 1);
12832#if !defined(NO_POPEN)
12833 } else if ((len > 9) && !memcmp(buf + 5, "exec", 4)) {
12834 do_ssi_exec(conn, buf + 9);
12835#endif /* !NO_POPEN */
12836 } else {
12837 mg_cry_internal(conn,
12838 "%s: unknown SSI "
12839 "command: \"%s\"",
12840 path,
12841 buf);
12842 }
12843 len = 0;
12844 in_ssi_tag = in_tag = 0;
12845
12846 } else {
12847 /* Not an SSI tag */
12848 /* Flush buffer */
12849 (void)mg_write(conn, buf, (size_t)len);
12850 len = 0;
12851 in_tag = 0;
12852 }
12853
12854 } else {
12855 /* Tag is still open */
12856 buf[len++] = (char)(ch & 0xff);
12857
12858 if ((len == 5) && !memcmp(buf, "<!--#", 5)) {
12859 /* All SSI tags start with <!--# */
12860 in_ssi_tag = 1;
12861 }
12862
12863 if ((len + 2) > (int)sizeof(buf)) {
12864 /* Tag to long for buffer */
12865 mg_cry_internal(conn, "%s: tag is too large", path);
12866 return;
12867 }
12868 }
12869
12870 } else {
12871
12872 /* We are not in a tag yet. */
12873 if (ch == '<') {
12874 /* Tag is opening */
12875 in_tag = 1;
12876
12877 if (len > 0) {
12878 /* Flush current buffer.
12879 * Buffer is filled with "len" bytes. */
12880 (void)mg_write(conn, buf, (size_t)len);
12881 }
12882 /* Store the < */
12883 len = 1;
12884 buf[0] = '<';
12885
12886 } else {
12887 /* No Tag */
12888 /* Add data to buffer */
12889 buf[len++] = (char)(ch & 0xff);
12890 /* Flush if buffer is full */
12891 if (len == (int)sizeof(buf)) {
12892 mg_write(conn, buf, (size_t)len);
12893 len = 0;
12894 }
12895 }
12896 }
12897 }
12898
12899 /* Send the rest of buffered data */
12900 if (len > 0) {
12901 mg_write(conn, buf, (size_t)len);
12902 }
12903}
12904
12905
12906static void
12908 const char *path,
12909 struct mg_file *filep)
12910{
12911 char date[64];
12912 time_t curtime = time(NULL);
12913
12914 if ((conn == NULL) || (path == NULL) || (filep == NULL)) {
12915 return;
12916 }
12917
12918 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) {
12919 /* File exists (precondition for calling this function),
12920 * but can not be opened by the server. */
12921 mg_send_http_error(conn,
12922 500,
12923 "Error: Cannot read file\nfopen(%s): %s",
12924 path,
12925 strerror(ERRNO));
12926 } else {
12927 /* Set "must_close" for HTTP/1.x, since we do not know the
12928 * content length */
12929 conn->must_close = 1;
12930 gmt_time_string(date, sizeof(date), &curtime);
12931 fclose_on_exec(&filep->access, conn);
12932
12933 /* 200 OK response */
12934 mg_response_header_start(conn, 200);
12937 send_cors_header(conn);
12938 mg_response_header_add(conn, "Content-Type", "text/html", -1);
12940
12941 /* Header sent, now send body */
12942 send_ssi_file(conn, path, filep, 0);
12943 (void)mg_fclose(&filep->access); /* Ignore errors for readonly files */
12944 }
12945}
12946#endif /* NO_FILESYSTEMS */
12947
12948
12949#if !defined(NO_FILES)
12950static void
12952{
12953 if (!conn || !all_methods) {
12954 return;
12955 }
12956
12957 /* We do not set a "Cache-Control" header here, but leave the default.
12958 * Since browsers do not send an OPTIONS request, we can not test the
12959 * effect anyway. */
12960
12961 mg_response_header_start(conn, 200);
12962 mg_response_header_add(conn, "Content-Type", "text/html", -1);
12963
12964 if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
12965 /* Use the same as before */
12966 mg_response_header_add(conn, "Allow", all_methods, -1);
12967 mg_response_header_add(conn, "DAV", "1", -1);
12968 } else {
12969 /* TODO: Check this later for HTTP/2 */
12970 mg_response_header_add(conn, "Allow", "GET, POST", -1);
12971 }
12974}
12975
12976
12977/* Writes PROPFIND properties for a collection element */
12978static int
12980 const char *uri,
12981 const char *name,
12982 struct mg_file_stat *filep)
12983{
12984 size_t i;
12985 char mtime[64];
12986 char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
12987 char *link_concat;
12988 size_t link_concat_len;
12989
12990 if ((conn == NULL) || (uri == NULL) || (name == NULL) || (filep == NULL)) {
12991 return 0;
12992 }
12993
12994 link_concat_len = strlen(uri) + strlen(name) + 1;
12996 if (!link_concat) {
12997 return 0;
12998 }
12999 strcpy(link_concat, uri);
13001
13002 /* Get full link used in request */
13004 conn, link_buf, sizeof(link_buf), NULL, 0, link_concat);
13005
13006 /*
13007 OutputDebugStringA("print_props:\n uri: ");
13008 OutputDebugStringA(uri);
13009 OutputDebugStringA("\n name: ");
13010 OutputDebugStringA(name);
13011 OutputDebugStringA("\n link: ");
13012 OutputDebugStringA(link_buf);
13013 OutputDebugStringA("\n");
13014 */
13015
13016 gmt_time_string(mtime, sizeof(mtime), &filep->last_modified);
13017 mg_printf(conn,
13018 "<d:response>"
13019 "<d:href>%s</d:href>"
13020 "<d:propstat>"
13021 "<d:prop>"
13022 "<d:resourcetype>%s</d:resourcetype>"
13023 "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
13024 "<d:getlastmodified>%s</d:getlastmodified>"
13025 "<d:lockdiscovery>",
13026 link_buf,
13027 filep->is_directory ? "<d:collection/>" : "",
13028 filep->size,
13029 mtime);
13030
13031 for (i = 0; i < NUM_WEBDAV_LOCKS; i++) {
13032 struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
13033 if (!strcmp(dav_lock[i].path, link_buf)) {
13034 mg_printf(conn,
13035 "<d:activelock>"
13036 "<d:locktype><d:write/></d:locktype>"
13037 "<d:lockscope><d:exclusive/></d:lockscope>"
13038 "<d:depth>0</d:depth>"
13039 "<d:owner>%s</d:owner>"
13040 "<d:timeout>Second-%u</d:timeout>"
13041 "<d:locktoken>"
13042 "<d:href>%s</d:href>"
13043 "</d:locktoken>"
13044 "</d:activelock>\n",
13045 dav_lock[i].user,
13046 (unsigned)LOCK_DURATION_S,
13047 dav_lock[i].token);
13048 }
13049 }
13050
13051 mg_printf(conn,
13052 "</d:lockdiscovery>"
13053 "</d:prop>"
13054 "<d:status>HTTP/1.1 200 OK</d:status>"
13055 "</d:propstat>"
13056 "</d:response>\n");
13057
13059 return 1;
13060}
13061
13062
13063static int
13065{
13066 struct mg_connection *conn = (struct mg_connection *)data;
13067 if (!de || !conn
13068 || !print_props(
13069 conn, conn->request_info.local_uri, de->file_name, &de->file)) {
13070 /* stop scan */
13071 return 1;
13072 }
13073 return 0;
13074}
13075
13076
13077static void
13079 const char *path,
13080 struct mg_file_stat *filep)
13081{
13082 const char *depth = mg_get_header(conn, "Depth");
13083
13084 if (!conn || !path || !filep || !conn->dom_ctx) {
13085 return;
13086 }
13087
13088 /* return 207 "Multi-Status" */
13089 conn->must_close = 1;
13090 mg_response_header_start(conn, 207);
13094 "Content-Type",
13095 "application/xml; charset=utf-8",
13096 -1);
13098
13099 /* Content */
13100 mg_printf(conn,
13101 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
13102 "<d:multistatus xmlns:d='DAV:'>\n");
13103
13104 /* Print properties for the requested resource itself */
13105 print_props(conn, conn->request_info.local_uri, "", filep);
13106
13107 /* If it is a directory, print directory entries too if Depth is not 0
13108 */
13109 if (filep->is_directory
13111 "yes")
13112 && ((depth == NULL) || (strcmp(depth, "0") != 0))) {
13113 scan_directory(conn, path, conn, &print_dav_dir_entry);
13114 }
13115
13116 mg_printf(conn, "%s\n", "</d:multistatus>");
13117}
13118
13119
13120static void
13121dav_lock_file(struct mg_connection *conn, const char *path)
13122{
13123 /* internal function - therefore conn is assumed to be valid */
13124 char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
13125 uint64_t new_locktime;
13126 int lock_index = -1;
13127 int i;
13128 uint64_t LOCK_DURATION_NS =
13129 (uint64_t)(LOCK_DURATION_S) * (uint64_t)1000000000;
13130 struct twebdav_lock *dav_lock = NULL;
13131
13132 if (!path || !conn || !conn->dom_ctx || !conn->request_info.remote_user
13133 || !conn->phys_ctx) {
13134 return;
13135 }
13136
13137 dav_lock = conn->phys_ctx->webdav_lock;
13138 mg_get_request_link(conn, link_buf, sizeof(link_buf));
13139
13140 /* const char *refresh = mg_get_header(conn, "If"); */
13141 /* Link refresh should have an "If" header:
13142 * http://www.webdav.org/specs/rfc2518.html#n-example---refreshing-a-write-lock
13143 * But it seems Windows Explorer does not send them.
13144 */
13145
13148
13149 /* Find a slot for a lock */
13150 while (lock_index < 0) {
13151 /* find existing lock */
13152 for (i = 0; i < NUM_WEBDAV_LOCKS; i++) {
13153 if (!strcmp(dav_lock[i].path, link_buf)) {
13154 if (!strcmp(conn->request_info.remote_user, dav_lock[i].user)) {
13155 /* locked by the same user */
13156 dav_lock[i].locktime = new_locktime;
13157 lock_index = i;
13158 break;
13159 } else {
13160 /* already locked by someone else */
13161 if (new_locktime
13163 /* Lock expired */
13164 dav_lock[i].path[0] = 0;
13165 } else {
13166 /* Lock still valid */
13168 mg_send_http_error(conn, 423, "%s", "Already locked");
13169 return;
13170 }
13171 }
13172 }
13173 }
13174
13175 /* create new lock token */
13176 for (i = 0; i < NUM_WEBDAV_LOCKS; i++) {
13177 if (dav_lock[i].path[0] == 0) {
13178 char s[32];
13179 dav_lock[i].locktime = mg_get_current_time_ns();
13180 sprintf(s, "%" UINT64_FMT, (uint64_t)dav_lock[i].locktime);
13182 link_buf,
13183 "\x01",
13184 s,
13185 "\x01",
13186 conn->request_info.remote_user,
13187 NULL);
13189 link_buf,
13190 sizeof(dav_lock[i].path));
13192 conn->request_info.remote_user,
13193 sizeof(dav_lock[i].user));
13194 lock_index = i;
13195 break;
13196 }
13197 }
13198 if (lock_index < 0) {
13199 /* too many locks. Find oldest lock */
13200 uint64_t oldest_locktime = dav_lock[0].locktime;
13201 lock_index = 0;
13202 for (i = 1; i < NUM_WEBDAV_LOCKS; i++) {
13204 oldest_locktime = dav_lock[i].locktime;
13205 lock_index = i;
13206 }
13207 }
13208 /* invalidate oldest lock */
13209 dav_lock[lock_index].path[0] = 0;
13210 }
13211 }
13213
13214 /* return 200 "OK" */
13215 conn->must_close = 1;
13216 mg_response_header_start(conn, 200);
13220 "Content-Type",
13221 "application/xml; charset=utf-8",
13222 -1);
13223 mg_response_header_add(conn, "Lock-Token", dav_lock[lock_index].token, -1);
13225
13226 /* Content */
13227 mg_printf(conn,
13228 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
13229 "<d:prop xmlns:d=\"DAV:\">\n"
13230 " <d:lockdiscovery>\n"
13231 " <d:activelock>\n"
13232 " <d:lockscope><d:exclusive/></d:lockscope>\n"
13233 " <d:locktype><d:write/></d:locktype>\n"
13234 " <d:owner>\n"
13235 " <d:href>%s</d:href>\n"
13236 " </d:owner>\n"
13237 " <d:timeout>Second-%u</d:timeout>\n"
13238 " <d:locktoken><d:href>%s</d:href></d:locktoken>\n"
13239 " <d:lockroot>\n"
13240 " <d:href>%s</d:href>\n"
13241 " </d:lockroot>\n"
13242 " </d:activelock>\n"
13243 " </d:lockdiscovery>\n"
13244 " </d:prop>\n",
13249}
13250
13251
13252static void
13253dav_unlock_file(struct mg_connection *conn, const char *path)
13254{
13255 /* internal function - therefore conn is assumed to be valid */
13256 char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
13257 struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
13258 int lock_index;
13259
13260 if (!path || !conn->dom_ctx || !conn->request_info.remote_user) {
13261 return;
13262 }
13263
13264 mg_get_request_link(conn, link_buf, sizeof(link_buf));
13265
13267 /* find existing lock */
13270 /* Success: return 204 "No Content" */
13272 conn->must_close = 1;
13273 mg_response_header_start(conn, 204);
13275 return;
13276 }
13277 }
13279
13280 /* Error: Cannot unlock a resource that is not locked */
13281 mg_send_http_error(conn, 423, "%s", "Lock not found");
13282}
13283
13284
13285static void
13286dav_proppatch(struct mg_connection *conn, const char *path)
13287{
13288 char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
13289
13290 if (!conn || !path || !conn->dom_ctx) {
13291 return;
13292 }
13293
13294 /* return 207 "Multi-Status" */
13295 conn->must_close = 1;
13296 mg_response_header_start(conn, 207);
13300 "Content-Type",
13301 "application/xml; charset=utf-8",
13302 -1);
13304
13305 mg_get_request_link(conn, link_buf, sizeof(link_buf));
13306
13307 /* Content */
13308 mg_printf(conn,
13309 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
13310 "<d:multistatus xmlns:d='DAV:'>\n"
13311 "<d:response>\n<d:href>%s</d:href>\n",
13312 link_buf);
13313 mg_printf(conn,
13314 "<d:propstat><d:status>HTTP/1.1 403 "
13315 "Forbidden</d:status></d:propstat>\n");
13316 mg_printf(conn, "%s\n", "</d:response></d:multistatus>");
13317}
13318#endif
13319
13320
13321CIVETWEB_API void
13323{
13324 if (conn) {
13325 (void)pthread_mutex_lock(&conn->mutex);
13326 }
13327}
13328
13329
13330CIVETWEB_API void
13332{
13333 if (conn) {
13334 (void)pthread_mutex_unlock(&conn->mutex);
13335 }
13336}
13337
13338
13339CIVETWEB_API void
13341{
13342 if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
13343 (void)pthread_mutex_lock(&ctx->nonce_mutex);
13344 }
13345}
13346
13347
13348CIVETWEB_API void
13350{
13351 if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
13352 (void)pthread_mutex_unlock(&ctx->nonce_mutex);
13353 }
13354}
13355
13356
13357#if defined(USE_LUA)
13358#include "mod_lua.inl"
13359#endif /* USE_LUA */
13360
13361#if defined(USE_DUKTAPE)
13362#include "mod_duktape.inl"
13363#endif /* USE_DUKTAPE */
13364
13365#if defined(USE_WEBSOCKET)
13366
13367#if !defined(NO_SSL_DL)
13368#if !defined(OPENSSL_API_3_0)
13369#define SHA_API static
13370#include "sha1.inl"
13371#endif
13372#endif
13373
13374static int
13375send_websocket_handshake(struct mg_connection *conn, const char *websock_key)
13376{
13377 static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
13378 char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
13379 size_t dst_len = sizeof(b64_sha);
13380#if !defined(OPENSSL_API_3_0)
13382#endif
13383 int truncated;
13384
13385 /* Calculate Sec-WebSocket-Accept reply from Sec-WebSocket-Key. */
13386 mg_snprintf(conn, &truncated, buf, sizeof(buf), "%s%s", websock_key, magic);
13387 if (truncated) {
13388 conn->must_close = 1;
13389 return 0;
13390 }
13391
13392 DEBUG_TRACE("%s", "Send websocket handshake");
13393
13394#if defined(OPENSSL_API_3_0)
13395 EVP_Digest((unsigned char *)buf,
13396 (uint32_t)strlen(buf),
13397 (unsigned char *)sha,
13398 NULL,
13399 EVP_get_digestbyname("sha1"),
13400 NULL);
13401#else
13403 SHA1_Update(&sha_ctx, (unsigned char *)buf, (uint32_t)strlen(buf));
13404 SHA1_Final((unsigned char *)sha, &sha_ctx);
13405#endif
13406 mg_base64_encode((unsigned char *)sha, sizeof(sha), b64_sha, &dst_len);
13407 mg_printf(conn,
13408 "HTTP/1.1 101 Switching Protocols\r\n"
13409 "Upgrade: websocket\r\n"
13410 "Connection: Upgrade\r\n"
13411 "Sec-WebSocket-Accept: %s\r\n",
13412 b64_sha);
13413
13414#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13415 // Send negotiated compression extension parameters
13417#endif
13418
13419 if (conn->request_info.acceptedWebSocketSubprotocol) {
13420 mg_printf(conn,
13421 "Sec-WebSocket-Protocol: %s\r\n\r\n",
13422 conn->request_info.acceptedWebSocketSubprotocol);
13423 } else {
13424 mg_printf(conn, "%s", "\r\n");
13425 }
13426
13427 return 1;
13428}
13429
13430
13431#if !defined(MG_MAX_UNANSWERED_PING)
13432/* Configuration of the maximum number of websocket PINGs that might
13433 * stay unanswered before the connection is considered broken.
13434 * Note: The name of this define may still change (until it is
13435 * defined as a compile parameter in a documentation).
13436 */
13437#define MG_MAX_UNANSWERED_PING (5)
13438#endif
13439
13440
13441static void
13442read_websocket(struct mg_connection *conn,
13444 void *callback_data)
13445{
13446 /* Pointer to the beginning of the portion of the incoming websocket
13447 * message queue.
13448 * The original websocket upgrade request is never removed, so the queue
13449 * begins after it. */
13450 unsigned char *buf = (unsigned char *)conn->buf + conn->request_len;
13451 int n, error, exit_by_callback;
13452 int ret;
13453
13454 /* body_len is the length of the entire queue in bytes
13455 * len is the length of the current message
13456 * data_len is the length of the current message's data payload
13457 * header_len is the length of the current message's header */
13458 size_t i, len, mask_len = 0, header_len, body_len;
13459 uint64_t data_len = 0;
13460
13461 /* "The masking key is a 32-bit value chosen at random by the client."
13462 * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5
13463 */
13464 unsigned char mask[4];
13465
13466 /* data points to the place where the message is stored when passed to
13467 * the websocket_data callback. This is either mem on the stack, or a
13468 * dynamically allocated buffer if it is too large. */
13469 unsigned char mem[4096];
13470 unsigned char mop; /* mask flag and opcode */
13471
13472 /* Variables used for connection monitoring */
13473 double timeout = -1.0;
13474 int enable_ping_pong = 0;
13475 int ping_count = 0;
13476
13477 if (conn->dom_ctx->config[ENABLE_WEBSOCKET_PING_PONG]) {
13480 "yes");
13481 }
13482
13483 if (conn->dom_ctx->config[WEBSOCKET_TIMEOUT]) {
13484 timeout = atoi(conn->dom_ctx->config[WEBSOCKET_TIMEOUT]) / 1000.0;
13485 }
13486 if ((timeout <= 0.0) && (conn->dom_ctx->config[REQUEST_TIMEOUT])) {
13487 timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
13488 }
13489 if (timeout <= 0.0) {
13490 timeout = atof(config_options[REQUEST_TIMEOUT].default_value) / 1000.0;
13491 }
13492
13493 /* Enter data processing loop */
13494 DEBUG_TRACE("Websocket connection %s:%u start data processing loop",
13495 conn->request_info.remote_addr,
13496 conn->request_info.remote_port);
13497 conn->in_websocket_handling = 1;
13498 mg_set_thread_name("wsock");
13499
13500 /* Loop continuously, reading messages from the socket, invoking the
13501 * callback, and waiting repeatedly until an error occurs. */
13502 while (STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)
13503 && (!conn->must_close)) {
13504 header_len = 0;
13505 DEBUG_ASSERT(conn->data_len >= conn->request_len);
13506 if ((body_len = (size_t)(conn->data_len - conn->request_len)) >= 2) {
13507 len = buf[1] & 127;
13508 mask_len = (buf[1] & 128) ? 4 : 0;
13509 if ((len < 126) && (body_len >= mask_len)) {
13510 /* inline 7-bit length field */
13511 data_len = len;
13512 header_len = 2 + mask_len;
13513 } else if ((len == 126) && (body_len >= (4 + mask_len))) {
13514 /* 16-bit length field */
13515 header_len = 4 + mask_len;
13516 data_len = ((((size_t)buf[2]) << 8) + buf[3]);
13517 } else if (body_len >= (10 + mask_len)) {
13518 /* 64-bit length field */
13519 uint32_t l1, l2;
13520 memcpy(&l1, &buf[2], 4); /* Use memcpy for alignment */
13521 memcpy(&l2, &buf[6], 4);
13522 header_len = 10 + mask_len;
13523 data_len = (((uint64_t)ntohl(l1)) << 32) + ntohl(l2);
13524
13525 if (data_len > (uint64_t)0x7FFF0000ul) {
13526 /* no can do */
13528 conn,
13529 "%s",
13530 "websocket out of memory; closing connection");
13531 break;
13532 }
13533 }
13534 }
13535
13536 if ((header_len > 0) && (body_len >= header_len)) {
13537 /* Allocate space to hold websocket payload */
13538 unsigned char *data = mem;
13539
13540 if ((size_t)data_len > (size_t)sizeof(mem)) {
13541 data = (unsigned char *)mg_malloc_ctx((size_t)data_len,
13542 conn->phys_ctx);
13543 if (data == NULL) {
13544 /* Allocation failed, exit the loop and then close the
13545 * connection */
13547 conn,
13548 "%s",
13549 "websocket out of memory; closing connection");
13550 break;
13551 }
13552 }
13553
13554 /* Copy the mask before we shift the queue and destroy it */
13555 if (mask_len > 0) {
13556 memcpy(mask, buf + header_len - mask_len, sizeof(mask));
13557 } else {
13558 memset(mask, 0, sizeof(mask));
13559 }
13560
13561 /* Read frame payload from the first message in the queue into
13562 * data and advance the queue by moving the memory in place. */
13564 if (data_len + (uint64_t)header_len > (uint64_t)body_len) {
13565 mop = buf[0]; /* current mask and opcode */
13566 /* Overflow case */
13568 memcpy(data, buf + header_len, len);
13569 error = 0;
13570 while ((uint64_t)len < data_len) {
13571 n = pull_inner(NULL,
13572 conn,
13573 (char *)(data + len),
13574 (int)(data_len - len),
13575 timeout);
13576 if (n <= -2) {
13577 error = 1;
13578 break;
13579 } else if (n > 0) {
13580 len += (size_t)n;
13581 } else {
13582 /* Timeout: should retry */
13583 /* TODO: retry condition */
13584 }
13585 }
13586 if (error) {
13588 conn,
13589 "%s",
13590 "Websocket pull failed; closing connection");
13591 if (data != mem) {
13592 mg_free(data);
13593 }
13594 break;
13595 }
13596
13597 conn->data_len = conn->request_len;
13598
13599 } else {
13600
13601 mop = buf[0]; /* current mask and opcode, overwritten by
13602 * memmove() */
13603
13604 /* Length of the message being read at the front of the
13605 * queue. Cast to 31 bit is OK, since we limited
13606 * data_len before. */
13607 len = (size_t)data_len + header_len;
13608
13609 /* Copy the data payload into the data pointer for the
13610 * callback. Cast to 31 bit is OK, since we
13611 * limited data_len */
13612 memcpy(data, buf + header_len, (size_t)data_len);
13613
13614 /* Move the queue forward len bytes */
13615 memmove(buf, buf + len, body_len - len);
13616
13617 /* Mark the queue as advanced */
13618 conn->data_len -= (int)len;
13619 }
13620
13621 /* Apply mask if necessary */
13622 if (mask_len > 0) {
13623 for (i = 0; i < (size_t)data_len; i++) {
13624 data[i] ^= mask[i & 3];
13625 }
13626 }
13627
13628 exit_by_callback = 0;
13629 if (enable_ping_pong && ((mop & 0xF) == MG_WEBSOCKET_OPCODE_PONG)) {
13630 /* filter PONG messages */
13631 DEBUG_TRACE("PONG from %s:%u",
13632 conn->request_info.remote_addr,
13633 conn->request_info.remote_port);
13634 /* No unanwered PINGs left */
13635 ping_count = 0;
13636 } else if (enable_ping_pong
13637 && ((mop & 0xF) == MG_WEBSOCKET_OPCODE_PING)) {
13638 /* reply PING messages */
13639 DEBUG_TRACE("Reply PING from %s:%u",
13640 conn->request_info.remote_addr,
13641 conn->request_info.remote_port);
13642 ret = mg_websocket_write(conn,
13644 (char *)data,
13645 (size_t)data_len);
13646 if (ret <= 0) {
13647 /* Error: send failed */
13648 DEBUG_TRACE("Reply PONG failed (%i)", ret);
13649 break;
13650 }
13651
13652 } else {
13653 /* Exit the loop if callback signals to exit (server side),
13654 * or "connection close" opcode received (client side). */
13655 if (ws_data_handler != NULL) {
13656#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13657 if (mop & 0x40) {
13658 /* Inflate the data received if bit RSV1 is set. */
13659 if (!conn->websocket_deflate_initialized) {
13660 if (websocket_deflate_initialize(conn, 1) != Z_OK)
13661 exit_by_callback = 1;
13662 }
13663 if (!exit_by_callback) {
13664 size_t inflate_buf_size_old = 0;
13665 size_t inflate_buf_size =
13666 data_len
13667 * 4; // Initial guess of the inflated message
13668 // size. We double the memory when needed.
13669 Bytef *inflated = NULL;
13670 Bytef *new_mem = NULL;
13671 conn->websocket_inflate_state.avail_in =
13672 (uInt)(data_len + 4);
13673 conn->websocket_inflate_state.next_in = data;
13674 // Add trailing 0x00 0x00 0xff 0xff bytes
13675 data[data_len] = '\x00';
13676 data[data_len + 1] = '\x00';
13677 data[data_len + 2] = '\xff';
13678 data[data_len + 3] = '\xff';
13679 do {
13680 if (inflate_buf_size_old == 0) {
13681 new_mem =
13683 sizeof(Bytef));
13684 } else {
13685 inflate_buf_size *= 2;
13686 new_mem =
13689 }
13690 if (new_mem == NULL) {
13692 conn,
13693 "Out of memory: Cannot allocate "
13694 "inflate buffer of %lu bytes",
13695 (unsigned long)inflate_buf_size);
13696 exit_by_callback = 1;
13697 break;
13698 }
13699 inflated = new_mem;
13700 conn->websocket_inflate_state.avail_out =
13703 conn->websocket_inflate_state.next_out =
13705 ret = inflate(&conn->websocket_inflate_state,
13706 Z_SYNC_FLUSH);
13707 if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR
13708 || ret == Z_MEM_ERROR) {
13710 conn,
13711 "ZLIB inflate error: %i %s",
13712 ret,
13713 (conn->websocket_inflate_state.msg
13714 ? conn->websocket_inflate_state.msg
13715 : "<no error message>"));
13716 exit_by_callback = 1;
13717 break;
13718 }
13720
13721 } while (conn->websocket_inflate_state.avail_out
13722 == 0);
13724 conn->websocket_inflate_state.avail_out;
13725 if (!ws_data_handler(conn,
13726 mop,
13727 (char *)inflated,
13729 callback_data)) {
13730 exit_by_callback = 1;
13731 }
13733 }
13734 } else
13735#endif
13736 if (!ws_data_handler(conn,
13737 mop,
13738 (char *)data,
13739 (size_t)data_len,
13740 callback_data)) {
13741 exit_by_callback = 1;
13742 }
13743 }
13744 }
13745
13746 /* It a buffer has been allocated, free it again */
13747 if (data != mem) {
13748 mg_free(data);
13749 }
13750
13751 if (exit_by_callback) {
13752 DEBUG_TRACE("Callback requests to close connection from %s:%u",
13753 conn->request_info.remote_addr,
13754 conn->request_info.remote_port);
13755 break;
13756 }
13758 /* Opcode == 8, connection close */
13759 DEBUG_TRACE("Message requests to close connection from %s:%u",
13760 conn->request_info.remote_addr,
13761 conn->request_info.remote_port);
13762 break;
13763 }
13764
13765 /* Not breaking the loop, process next websocket frame. */
13766 } else {
13767 /* Read from the socket into the next available location in the
13768 * message queue. */
13769 n = pull_inner(NULL,
13770 conn,
13771 conn->buf + conn->data_len,
13772 conn->buf_size - conn->data_len,
13773 timeout);
13774 if (n <= -2) {
13775 /* Error, no bytes read */
13776 DEBUG_TRACE("PULL from %s:%u failed",
13777 conn->request_info.remote_addr,
13778 conn->request_info.remote_port);
13779 break;
13780 }
13781 if (n > 0) {
13782 conn->data_len += n;
13783 /* Reset open PING count */
13784 ping_count = 0;
13785 } else {
13786 if (STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)
13787 && (!conn->must_close)) {
13789 /* Stop sending PING */
13790 DEBUG_TRACE("Too many (%i) unanswered ping from %s:%u "
13791 "- closing connection",
13792 ping_count,
13793 conn->request_info.remote_addr,
13794 conn->request_info.remote_port);
13795 break;
13796 }
13797 if (enable_ping_pong) {
13798 /* Send Websocket PING message */
13799 DEBUG_TRACE("PING to %s:%u",
13800 conn->request_info.remote_addr,
13801 conn->request_info.remote_port);
13802 ret = mg_websocket_write(conn,
13804 NULL,
13805 0);
13806
13807 if (ret <= 0) {
13808 /* Error: send failed */
13809 DEBUG_TRACE("Send PING failed (%i)", ret);
13810 break;
13811 }
13812 ping_count++;
13813 }
13814 }
13815 /* Timeout: should retry */
13816 /* TODO: get timeout def */
13817 }
13818 }
13819 }
13820
13821 /* Leave data processing loop */
13822 mg_set_thread_name("worker");
13823 conn->in_websocket_handling = 0;
13824 DEBUG_TRACE("Websocket connection %s:%u left data processing loop",
13825 conn->request_info.remote_addr,
13826 conn->request_info.remote_port);
13827}
13828
13829
13830static int
13832 int opcode,
13833 const char *data,
13834 size_t dataLen,
13835 uint32_t masking_key)
13836{
13837 unsigned char header[14];
13838 size_t headerLen;
13839 int retval;
13840
13841#if defined(GCC_DIAGNOSTIC)
13842 /* Disable spurious conversion warning for GCC */
13843#pragma GCC diagnostic push
13844#pragma GCC diagnostic ignored "-Wconversion"
13845#endif
13846
13847 /* Note that POSIX/Winsock's send() is threadsafe
13848 * http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid
13849 * but mongoose's mg_printf/mg_write is not (because of the loop in
13850 * push(), although that is only a problem if the packet is large or
13851 * outgoing buffer is full). */
13852
13853 /* TODO: Check if this lock should be moved to user land.
13854 * Currently the server sets this lock for websockets, but
13855 * not for any other connection. It must be set for every
13856 * conn read/written by more than one thread, no matter if
13857 * it is a websocket or regular connection. */
13858 (void)mg_lock_connection(conn);
13859
13860#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13861 size_t deflated_size = 0;
13862 Bytef *deflated = 0;
13863 // Deflate websocket messages over 100kb
13864 int use_deflate = dataLen > 100 * 1024 && conn->accept_gzip;
13865
13866 if (use_deflate) {
13867 if (!conn->websocket_deflate_initialized) {
13868 if (websocket_deflate_initialize(conn, 1) != Z_OK)
13869 return 0;
13870 }
13871
13872 // Deflating the message
13873 header[0] = 0xC0u | (unsigned char)((unsigned)opcode & 0xf);
13874 conn->websocket_deflate_state.avail_in = (uInt)dataLen;
13875 conn->websocket_deflate_state.next_in = (unsigned char *)data;
13878 if (deflated == NULL) {
13880 conn,
13881 "Out of memory: Cannot allocate deflate buffer of %lu bytes",
13882 (unsigned long)deflated_size);
13884 return -1;
13885 }
13886 conn->websocket_deflate_state.avail_out = (uInt)deflated_size;
13887 conn->websocket_deflate_state.next_out = deflated;
13888 deflate(&conn->websocket_deflate_state, conn->websocket_deflate_flush);
13889 dataLen = deflated_size - conn->websocket_deflate_state.avail_out
13890 - 4; // Strip trailing 0x00 0x00 0xff 0xff bytes
13891 } else
13892#endif
13893 header[0] = 0x80u | (unsigned char)((unsigned)opcode & 0xf);
13894
13895#if defined(GCC_DIAGNOSTIC)
13896#pragma GCC diagnostic pop
13897#endif
13898
13899 /* Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 */
13900 if (dataLen < 126) {
13901 /* inline 7-bit length field */
13902 header[1] = (unsigned char)dataLen;
13903 headerLen = 2;
13904 } else if (dataLen <= 0xFFFF) {
13905 /* 16-bit length field */
13906 uint16_t len = htons((uint16_t)dataLen);
13907 header[1] = 126;
13908 memcpy(header + 2, &len, 2);
13909 headerLen = 4;
13910 } else {
13911 /* 64-bit length field */
13912 uint32_t len1 = htonl((uint32_t)((uint64_t)dataLen >> 32));
13913 uint32_t len2 = htonl((uint32_t)(dataLen & 0xFFFFFFFFu));
13914 header[1] = 127;
13915 memcpy(header + 2, &len1, 4);
13916 memcpy(header + 6, &len2, 4);
13917 headerLen = 10;
13918 }
13919
13920 if (masking_key) {
13921 /* add mask */
13922 header[1] |= 0x80;
13923 memcpy(header + headerLen, &masking_key, 4);
13924 headerLen += 4;
13925 }
13926
13927 retval = mg_write(conn, header, headerLen);
13928 if (retval != (int)headerLen) {
13929 /* Did not send complete header */
13930 retval = -1;
13931 } else {
13932 if (dataLen > 0) {
13933#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13934 if (use_deflate) {
13935 retval = mg_write(conn, deflated, dataLen);
13937 } else
13938#endif
13939 retval = mg_write(conn, data, dataLen);
13940 }
13941 /* if dataLen == 0, the header length (2) is returned */
13942 }
13943
13944 /* TODO: Remove this unlock as well, when lock is removed. */
13946
13947 return retval;
13948}
13949
13950
13951CIVETWEB_API int
13953 int opcode,
13954 const char *data,
13955 size_t dataLen)
13956{
13957 return mg_websocket_write_exec(conn, opcode, data, dataLen, 0);
13958}
13959
13960
13961static void
13962mask_data(const char *in, size_t in_len, uint32_t masking_key, char *out)
13963{
13964 size_t i = 0;
13965
13966 i = 0;
13967 if ((in_len > 3) && ((ptrdiff_t)in % 4) == 0) {
13968 /* Convert in 32 bit words, if data is 4 byte aligned */
13969 while (i < (in_len - 3)) {
13970 *(uint32_t *)(void *)(out + i) =
13971 *(uint32_t *)(void *)(in + i) ^ masking_key;
13972 i += 4;
13973 }
13974 }
13975 if (i != in_len) {
13976 /* convert 1-3 remaining bytes if ((dataLen % 4) != 0)*/
13977 while (i < in_len) {
13978 *(uint8_t *)(void *)(out + i) =
13979 *(uint8_t *)(void *)(in + i)
13980 ^ *(((uint8_t *)&masking_key) + (i % 4));
13981 i++;
13982 }
13983 }
13984}
13985
13986
13987CIVETWEB_API int
13989 int opcode,
13990 const char *data,
13991 size_t dataLen)
13992{
13993 int retval = -1;
13994 char *masked_data =
13995 (char *)mg_malloc_ctx(((dataLen + 7) / 4) * 4, conn->phys_ctx);
13996 uint32_t masking_key = 0;
13997
13998 if (masked_data == NULL) {
13999 /* Return -1 in an error case */
14000 mg_cry_internal(conn,
14001 "%s",
14002 "Cannot allocate buffer for masked websocket response: "
14003 "Out of memory");
14004 return -1;
14005 }
14006
14007 do {
14008 /* Get a masking key - but not 0 */
14009 masking_key = (uint32_t)get_random();
14010 } while (masking_key == 0);
14011
14013
14015 conn, opcode, masked_data, dataLen, masking_key);
14017
14018 return retval;
14019}
14020
14021
14022static void
14024 const char *path,
14026 struct mg_websocket_subprotocols *subprotocols,
14031 void *cbData)
14032{
14033 const char *websock_key = mg_get_header(conn, "Sec-WebSocket-Key");
14034 const char *version = mg_get_header(conn, "Sec-WebSocket-Version");
14035 ptrdiff_t lua_websock = 0;
14036
14037#if !defined(USE_LUA)
14038 (void)path;
14039#endif
14040
14041 /* Step 1: Check websocket protocol version. */
14042 /* Step 1.1: Check Sec-WebSocket-Key. */
14043 if (!websock_key) {
14044 /* The RFC standard version (https://tools.ietf.org/html/rfc6455)
14045 * requires a Sec-WebSocket-Key header.
14046 */
14047 /* It could be the hixie draft version
14048 * (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76).
14049 */
14050 const char *key1 = mg_get_header(conn, "Sec-WebSocket-Key1");
14051 const char *key2 = mg_get_header(conn, "Sec-WebSocket-Key2");
14052 char key3[8];
14053
14054 if ((key1 != NULL) && (key2 != NULL)) {
14055 /* This version uses 8 byte body data in a GET request */
14056 conn->content_len = 8;
14057 if (8 == mg_read(conn, key3, 8)) {
14058 /* This is the hixie version */
14059 mg_send_http_error(conn,
14060 426,
14061 "%s",
14062 "Protocol upgrade to RFC 6455 required");
14063 return;
14064 }
14065 }
14066 /* This is an unknown version */
14067 mg_send_http_error(conn, 400, "%s", "Malformed websocket request");
14068 return;
14069 }
14070
14071 /* Step 1.2: Check websocket protocol version. */
14072 /* The RFC version (https://tools.ietf.org/html/rfc6455) is 13. */
14073 if ((version == NULL) || (strcmp(version, "13") != 0)) {
14074 /* Reject wrong versions */
14075 mg_send_http_error(conn, 426, "%s", "Protocol upgrade required");
14076 return;
14077 }
14078
14079 /* Step 1.3: Could check for "Host", but we do not really need this
14080 * value for anything, so just ignore it. */
14081
14082 /* Step 2: If a callback is responsible, call it. */
14084 /* Step 2.1 check and select subprotocol */
14085 const char *protocols[64]; // max 64 headers
14087 "Sec-WebSocket-Protocol",
14088 protocols,
14089 64);
14090
14091 if ((nbSubprotocolHeader > 0) && subprotocols) {
14092
14093 int headerNo, idx;
14094 size_t len;
14095 const char *sep, *curSubProtocol,
14096 *acceptedWebSocketSubprotocol = NULL;
14097
14098 /* look for matching subprotocol */
14100 /* There might be multiple headers ... */
14101 const char *protocol = protocols[headerNo];
14102 curSubProtocol = protocol;
14103
14104 /* ... and in every header there might be a , separated list */
14105 while (!acceptedWebSocketSubprotocol && (*curSubProtocol)) {
14106
14107 while ((*curSubProtocol == ' ') || (*curSubProtocol == ','))
14109 sep = strchr(curSubProtocol, ',');
14110 if (sep) {
14111 len = (size_t)(sep - curSubProtocol);
14112 } else {
14114 }
14115
14116 for (idx = 0; idx < subprotocols->nb_subprotocols; idx++) {
14117 // COMPARE: curSubProtocol ==
14118 // subprotocols->subprotocols[idx]
14119 if ((strlen(subprotocols->subprotocols[idx]) == len)
14121 subprotocols->subprotocols[idx],
14122 len)
14123 == 0)) {
14124 acceptedWebSocketSubprotocol =
14125 subprotocols->subprotocols[idx];
14126 break;
14127 }
14128 }
14130 }
14131 }
14132
14133 conn->request_info.acceptedWebSocketSubprotocol =
14134 acceptedWebSocketSubprotocol;
14135 }
14136
14137#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
14139#endif
14140
14141 if ((ws_connect_handler != NULL)
14142 && (ws_connect_handler(conn, cbData) != 0)) {
14143 /* C callback has returned non-zero, do not proceed with
14144 * handshake.
14145 */
14146 /* Note that C callbacks are no longer called when Lua is
14147 * responsible, so C can no longer filter callbacks for Lua. */
14148 return;
14149 }
14150 }
14151
14152#if defined(USE_LUA)
14153 /* Step 3: No callback. Check if Lua is responsible. */
14154 else {
14155 /* Step 3.1: Check if Lua is responsible. */
14156 if (conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS]) {
14158 conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS], path);
14159 }
14160
14161 if (lua_websock > 0) {
14162 /* Step 3.2: Lua is responsible: call it. */
14163 conn->lua_websocket_state = lua_websocket_new(path, conn);
14164 if (!conn->lua_websocket_state) {
14165 /* Lua rejected the new client */
14166 return;
14167 }
14168 }
14169 }
14170#endif
14171
14172 /* Step 4: Check if there is a responsible websocket handler. */
14174 /* There is no callback, and Lua is not responsible either. */
14175 /* Reply with a 404 Not Found. We are still at a standard
14176 * HTTP request here, before the websocket handshake, so
14177 * we can still send standard HTTP error replies. */
14178 mg_send_http_error(conn, 404, "%s", "Not found");
14179 return;
14180 }
14181
14182 /* Step 5: The websocket connection has been accepted */
14184 mg_send_http_error(conn, 500, "%s", "Websocket handshake failed");
14185 return;
14186 }
14187
14188 /* Step 6: Call the ready handler */
14190 if (ws_ready_handler != NULL) {
14191 ws_ready_handler(conn, cbData);
14192 }
14193#if defined(USE_LUA)
14194 } else if (lua_websock) {
14195 if (!lua_websocket_ready(conn, conn->lua_websocket_state)) {
14196 /* the ready handler returned false */
14197 return;
14198 }
14199#endif
14200 }
14201
14202 /* Step 7: Enter the read loop */
14205#if defined(USE_LUA)
14206 } else if (lua_websock) {
14207 read_websocket(conn, lua_websocket_data, conn->lua_websocket_state);
14208#endif
14209 }
14210
14211#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
14212 /* Step 8: Close the deflate & inflate buffers */
14213 if (conn->websocket_deflate_initialized) {
14214 deflateEnd(&conn->websocket_deflate_state);
14215 inflateEnd(&conn->websocket_inflate_state);
14216 }
14217#endif
14218
14219 /* Step 9: Call the close handler */
14220 if (ws_close_handler) {
14221 ws_close_handler(conn, cbData);
14222 }
14223}
14224#endif /* !USE_WEBSOCKET */
14225
14226
14227/* Is upgrade request:
14228 * 0 = regular HTTP/1.0 or HTTP/1.1 request
14229 * 1 = upgrade to websocket
14230 * 2 = upgrade to HTTP/2
14231 * -1 = upgrade to unknown protocol
14232 */
14233static int
14235{
14236 const char *connection_headers[8];
14237 const char *upgrade_to;
14239
14240 /* A websocket protocol has the following HTTP headers:
14241 *
14242 * Connection: Upgrade
14243 * Upgrade: Websocket
14244 *
14245 * It seems some clients use multiple headers:
14246 * see https://github.com/civetweb/civetweb/issues/1083
14247 */
14249 "Connection",
14251 8);
14252 should_upgrade = 0;
14253 for (i = 0; i < connection_header_count; i++) {
14254 if (0 != mg_strcasestr(connection_headers[i], "upgrade")) {
14255 should_upgrade = 1;
14256 }
14257 }
14258 if (!should_upgrade) {
14259 return PROTOCOL_TYPE_HTTP1;
14260 }
14261
14262 upgrade_to = mg_get_header(conn, "Upgrade");
14263 if (upgrade_to == NULL) {
14264 /* "Connection: Upgrade" without "Upgrade" Header --> Error */
14265 return -1;
14266 }
14267
14268 /* Upgrade to ... */
14269 if (0 != mg_strcasestr(upgrade_to, "websocket")) {
14270 /* The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and
14271 * "Sec-WebSocket-Version" are also required.
14272 * Don't check them here, since even an unsupported websocket protocol
14273 * request still IS a websocket request (in contrast to a standard HTTP
14274 * request). It will fail later in handle_websocket_request.
14275 */
14276 return PROTOCOL_TYPE_WEBSOCKET; /* Websocket */
14277 }
14278 if (0 != mg_strcasestr(upgrade_to, "h2")) {
14279 return PROTOCOL_TYPE_HTTP2; /* Websocket */
14280 }
14281
14282 /* Upgrade to another protocol */
14283 return -1;
14284}
14285
14286
14287static int
14288parse_match_net(const struct vec *vec, const union usa *sa, int no_strict)
14289{
14290 int n;
14291 unsigned int a, b, c, d, slash;
14292
14293 if (sscanf(vec->ptr, "%u.%u.%u.%u/%u%n", &a, &b, &c, &d, &slash, &n)
14294 != 5) { // NOLINT(cert-err34-c) 'sscanf' used to convert a string to an
14295 // integer value, but function will not report conversion
14296 // errors; consider using 'strtol' instead
14297 slash = 32;
14298 if (sscanf(vec->ptr, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n)
14299 != 4) { // NOLINT(cert-err34-c) 'sscanf' used to convert a string to
14300 // an integer value, but function will not report conversion
14301 // errors; consider using 'strtol' instead
14302 n = 0;
14303 }
14304 }
14305
14306 if ((n > 0) && ((size_t)n == vec->len)) {
14307 if ((a < 256) && (b < 256) && (c < 256) && (d < 256) && (slash < 33)) {
14308 /* IPv4 format */
14309 if (sa->sa.sa_family == AF_INET) {
14310 uint32_t ip = ntohl(sa->sin.sin_addr.s_addr);
14311 uint32_t net = ((uint32_t)a << 24) | ((uint32_t)b << 16)
14312 | ((uint32_t)c << 8) | (uint32_t)d;
14313 uint32_t mask = slash ? (0xFFFFFFFFu << (32 - slash)) : 0;
14314 return (ip & mask) == net;
14315 }
14316 return 0;
14317 }
14318 }
14319#if defined(USE_IPV6)
14320 else {
14321 char ad[50];
14322 const char *p;
14323
14324 if (sscanf(vec->ptr, "[%49[^]]]/%u%n", ad, &slash, &n) != 2) {
14325 slash = 128;
14326 if (sscanf(vec->ptr, "[%49[^]]]%n", ad, &n) != 1) {
14327 n = 0;
14328 }
14329 }
14330
14331 if ((n <= 0) && no_strict) {
14332 /* no square brackets? */
14333 p = strchr(vec->ptr, '/');
14334 if (p && (p < (vec->ptr + vec->len))) {
14335 if (((size_t)(p - vec->ptr) < sizeof(ad))
14336 && (sscanf(p, "/%u%n", &slash, &n) == 1)) {
14337 n += (int)(p - vec->ptr);
14338 mg_strlcpy(ad, vec->ptr, (size_t)(p - vec->ptr) + 1);
14339 } else {
14340 n = 0;
14341 }
14342 } else if (vec->len < sizeof(ad)) {
14343 n = (int)vec->len;
14344 slash = 128;
14345 mg_strlcpy(ad, vec->ptr, vec->len + 1);
14346 }
14347 }
14348
14349 if ((n > 0) && ((size_t)n == vec->len) && (slash < 129)) {
14350 p = ad;
14351 c = 0;
14352 /* zone indexes are unsupported, at least two colons are needed */
14353 while (isxdigit((unsigned char)*p) || (*p == '.') || (*p == ':')) {
14354 if (*(p++) == ':') {
14355 c++;
14356 }
14357 }
14358 if ((*p == '\0') && (c >= 2)) {
14359 struct sockaddr_in6 sin6;
14360 unsigned int i;
14361
14362 /* for strict validation, an actual IPv6 argument is needed */
14363 if (sa->sa.sa_family != AF_INET6) {
14364 return 0;
14365 }
14366 if (mg_inet_pton(AF_INET6, ad, &sin6, sizeof(sin6), 0)) {
14367 /* IPv6 format */
14368 for (i = 0; i < 16; i++) {
14369 uint8_t ip = sa->sin6.sin6_addr.s6_addr[i];
14370 uint8_t net = sin6.sin6_addr.s6_addr[i];
14371 uint8_t mask = 0;
14372
14373 if (8 * i + 8 < slash) {
14374 mask = 0xFFu;
14375 } else if (8 * i < slash) {
14376 mask = (uint8_t)(0xFFu << (8 * i + 8 - slash));
14377 }
14378 if ((ip & mask) != net) {
14379 return 0;
14380 }
14381 }
14382 return 1;
14383 }
14384 }
14385 }
14386 }
14387#else
14388 (void)no_strict;
14389#endif
14390
14391 /* malformed */
14392 return -1;
14393}
14394
14395
14396static int
14397set_throttle(const char *spec, const union usa *rsa, const char *uri)
14398{
14399 int throttle = 0;
14400 struct vec vec, val;
14401 char mult;
14402 double v;
14403
14404 while ((spec = next_option(spec, &vec, &val)) != NULL) {
14405 mult = ',';
14406 if ((val.ptr == NULL)
14407 || (sscanf(val.ptr, "%lf%c", &v, &mult)
14408 < 1) // NOLINT(cert-err34-c) 'sscanf' used to convert a string
14409 // to an integer value, but function will not report
14410 // conversion errors; consider using 'strtol' instead
14411 || (v < 0)
14412 || ((lowercase(&mult) != 'k') && (lowercase(&mult) != 'm')
14413 && (mult != ','))) {
14414 continue;
14415 }
14416 v *= (lowercase(&mult) == 'k')
14417 ? 1024
14418 : ((lowercase(&mult) == 'm') ? 1048576 : 1);
14419 if (vec.len == 1 && vec.ptr[0] == '*') {
14420 throttle = (int)v;
14421 } else {
14422 int matched = parse_match_net(&vec, rsa, 0);
14423 if (matched >= 0) {
14424 /* a valid IP subnet */
14425 if (matched) {
14426 throttle = (int)v;
14427 }
14428 } else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
14429 throttle = (int)v;
14430 }
14431 }
14432 }
14433
14434 return throttle;
14435}
14436
14437
14438/* The mg_upload function is superseded by mg_handle_form_request. */
14439#include "handle_form.inl"
14440
14441
14442static int
14444{
14445 unsigned int i;
14446 int idx = -1;
14447 if (ctx) {
14448 for (i = 0; ((idx == -1) && (i < ctx->num_listening_sockets)); i++) {
14449 idx = ctx->listening_sockets[i].is_ssl ? ((int)(i)) : -1;
14450 }
14451 }
14452 return idx;
14453}
14454
14455
14456/* Return host (without port) */
14457static void
14458get_host_from_request_info(struct vec *host, const struct mg_request_info *ri)
14459{
14460 const char *host_header =
14461 get_header(ri->http_headers, ri->num_headers, "Host");
14462
14463 host->ptr = NULL;
14464 host->len = 0;
14465
14466 if (host_header != NULL) {
14467 const char *pos;
14468
14469 /* If the "Host" is an IPv6 address, like [::1], parse until ]
14470 * is found. */
14471 if (*host_header == '[') {
14472 pos = strchr(host_header, ']');
14473 if (!pos) {
14474 /* Malformed hostname starts with '[', but no ']' found */
14475 DEBUG_TRACE("%s", "Host name format error '[' without ']'");
14476 return;
14477 }
14478 /* terminate after ']' */
14479 host->ptr = host_header;
14480 host->len = (size_t)(pos + 1 - host_header);
14481 } else {
14482 /* Otherwise, a ':' separates hostname and port number */
14483 pos = strchr(host_header, ':');
14484 if (pos != NULL) {
14485 host->len = (size_t)(pos - host_header);
14486 } else {
14487 host->len = strlen(host_header);
14488 }
14489 host->ptr = host_header;
14490 }
14491 }
14492}
14493
14494
14495static int
14497{
14498 struct vec host;
14499
14501
14502 if (host.ptr) {
14503 if (conn->ssl) {
14504 /* This is a HTTPS connection, maybe we have a hostname
14505 * from SNI (set in ssl_servername_callback). */
14506 const char *sslhost = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
14507 if (sslhost && (conn->dom_ctx != &(conn->phys_ctx->dd))) {
14508 /* We are not using the default domain */
14509 if ((strlen(sslhost) != host.len)
14510 || mg_strncasecmp(host.ptr, sslhost, host.len)) {
14511 /* Mismatch between SNI domain and HTTP domain */
14512 DEBUG_TRACE("Host mismatch: SNI: %s, HTTPS: %.*s",
14513 sslhost,
14514 (int)host.len,
14515 host.ptr);
14516 return 0;
14517 }
14518 }
14519
14520 } else {
14521 struct mg_domain_context *dom = &(conn->phys_ctx->dd);
14522 while (dom) {
14523 const char *domName = dom->config[AUTHENTICATION_DOMAIN];
14524 size_t domNameLen = strlen(domName);
14525 if ((domNameLen == host.len)
14526 && !mg_strncasecmp(host.ptr, domName, host.len)) {
14527
14528 /* Found matching domain */
14529 DEBUG_TRACE("HTTP domain %s found",
14530 dom->config[AUTHENTICATION_DOMAIN]);
14531
14532 /* TODO: Check if this is a HTTP or HTTPS domain */
14533 conn->dom_ctx = dom;
14534 break;
14535 }
14537 dom = dom->next;
14539 }
14540 }
14541
14542 DEBUG_TRACE("HTTP%s Host: %.*s",
14543 conn->ssl ? "S" : "",
14544 (int)host.len,
14545 host.ptr);
14546
14547 } else {
14548 DEBUG_TRACE("HTTP%s Host is not set", conn->ssl ? "S" : "");
14549 return 1;
14550 }
14551
14552 return 1;
14553}
14554
14555
14556static void
14558{
14559 char target_url[MG_BUF_LEN];
14560 int truncated = 0;
14561 const char *expect_proto =
14562 (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET) ? "wss" : "https";
14563
14564 /* Use "308 Permanent Redirect" */
14565 int redirect_code = 308;
14566
14567 /* In any case, close the current connection */
14568 conn->must_close = 1;
14569
14570 /* Send host, port, uri and (if it exists) ?query_string */
14572 conn, target_url, sizeof(target_url), expect_proto, port, NULL)
14573 < 0) {
14574 truncated = 1;
14575 } else if (conn->request_info.query_string != NULL) {
14576 size_t slen1 = strlen(target_url);
14577 size_t slen2 = strlen(conn->request_info.query_string);
14578 if ((slen1 + slen2 + 2) < sizeof(target_url)) {
14579 target_url[slen1] = '?';
14580 memcpy(target_url + slen1 + 1,
14581 conn->request_info.query_string,
14582 slen2);
14583 target_url[slen1 + slen2 + 1] = 0;
14584 } else {
14585 truncated = 1;
14586 }
14587 }
14588
14589 /* Check overflow in location buffer (will not occur if MG_BUF_LEN
14590 * is used as buffer size) */
14591 if (truncated) {
14592 mg_send_http_error(conn, 500, "%s", "Redirect URL too long");
14593 return;
14594 }
14595
14596 /* Use redirect helper function */
14598}
14599
14600
14601static void
14603 struct mg_domain_context *dom_ctx,
14604 const char *uri,
14605 int handler_type,
14607 mg_request_handler handler,
14608 struct mg_websocket_subprotocols *subprotocols,
14609 mg_websocket_connect_handler connect_handler,
14610 mg_websocket_ready_handler ready_handler,
14611 mg_websocket_data_handler data_handler,
14612 mg_websocket_close_handler close_handler,
14613 mg_authorization_handler auth_handler,
14614 void *cbdata)
14615{
14616 struct mg_handler_info *tmp_rh, **lastref;
14617 size_t urilen = strlen(uri);
14618
14623 || close_handler != NULL);
14624
14626 if (handler != NULL) {
14627 return;
14628 }
14630 && (ready_handler == NULL) && (data_handler == NULL)
14631 && (close_handler == NULL)) {
14632 return;
14633 }
14634 if (auth_handler != NULL) {
14635 return;
14636 }
14637
14638 } else if (handler_type == REQUEST_HANDLER) {
14640 && data_handler == NULL && close_handler == NULL);
14643
14644 if ((connect_handler != NULL) || (ready_handler != NULL)
14645 || (data_handler != NULL) || (close_handler != NULL)) {
14646 return;
14647 }
14648 if (!is_delete_request && (handler == NULL)) {
14649 return;
14650 }
14651 if (auth_handler != NULL) {
14652 return;
14653 }
14654
14655 } else if (handler_type == AUTH_HANDLER) {
14658 && data_handler == NULL && close_handler == NULL);
14660 if (handler != NULL) {
14661 return;
14662 }
14663 if ((connect_handler != NULL) || (ready_handler != NULL)
14664 || (data_handler != NULL) || (close_handler != NULL)) {
14665 return;
14666 }
14667 if (!is_delete_request && (auth_handler == NULL)) {
14668 return;
14669 }
14670 } else {
14671 /* Unknown handler type. */
14672 return;
14673 }
14674
14675 if (!phys_ctx || !dom_ctx) {
14676 /* no context available */
14677 return;
14678 }
14679
14680 mg_lock_context(phys_ctx);
14681
14682 /* first try to find an existing handler */
14683 do {
14684 lastref = &(dom_ctx->handlers);
14685 for (tmp_rh = dom_ctx->handlers; tmp_rh != NULL;
14686 tmp_rh = tmp_rh->next) {
14687 if (tmp_rh->handler_type == handler_type
14688 && (urilen == tmp_rh->uri_len) && !strcmp(tmp_rh->uri, uri)) {
14689 if (!is_delete_request) {
14690 /* update existing handler */
14692 /* Wait for end of use before updating */
14693 if (tmp_rh->refcount) {
14694 mg_unlock_context(phys_ctx);
14695 mg_sleep(1);
14696 mg_lock_context(phys_ctx);
14697 /* tmp_rh might have been freed, search again. */
14698 break;
14699 }
14700 /* Ok, the handler is no more use -> Update it */
14701 tmp_rh->handler = handler;
14702 } else if (handler_type == WEBSOCKET_HANDLER) {
14703 tmp_rh->subprotocols = subprotocols;
14704 tmp_rh->connect_handler = connect_handler;
14705 tmp_rh->ready_handler = ready_handler;
14706 tmp_rh->data_handler = data_handler;
14707 tmp_rh->close_handler = close_handler;
14708 } else { /* AUTH_HANDLER */
14709 tmp_rh->auth_handler = auth_handler;
14710 }
14711 tmp_rh->cbdata = cbdata;
14712 } else {
14713 /* remove existing handler */
14715 /* Wait for end of use before removing */
14716 if (tmp_rh->refcount) {
14717 tmp_rh->removing = 1;
14718 mg_unlock_context(phys_ctx);
14719 mg_sleep(1);
14720 mg_lock_context(phys_ctx);
14721 /* tmp_rh might have been freed, search again. */
14722 break;
14723 }
14724 /* Ok, the handler is no more used */
14725 }
14726 *lastref = tmp_rh->next;
14727 mg_free(tmp_rh->uri);
14728 mg_free(tmp_rh);
14729 }
14730 mg_unlock_context(phys_ctx);
14731 return;
14732 }
14733 lastref = &(tmp_rh->next);
14734 }
14735 } while (tmp_rh != NULL);
14736
14737 if (is_delete_request) {
14738 /* no handler to set, this was a remove request to a non-existing
14739 * handler */
14740 mg_unlock_context(phys_ctx);
14741 return;
14742 }
14743
14744 tmp_rh =
14745 (struct mg_handler_info *)mg_calloc_ctx(1,
14746 sizeof(struct mg_handler_info),
14747 phys_ctx);
14748 if (tmp_rh == NULL) {
14749 mg_unlock_context(phys_ctx);
14750 mg_cry_ctx_internal(phys_ctx,
14751 "%s",
14752 "Cannot create new request handler struct, OOM");
14753 return;
14754 }
14755 tmp_rh->uri = mg_strdup_ctx(uri, phys_ctx);
14756 if (!tmp_rh->uri) {
14757 mg_unlock_context(phys_ctx);
14758 mg_free(tmp_rh);
14759 mg_cry_ctx_internal(phys_ctx,
14760 "%s",
14761 "Cannot create new request handler struct, OOM");
14762 return;
14763 }
14764 tmp_rh->uri_len = urilen;
14766 tmp_rh->refcount = 0;
14767 tmp_rh->removing = 0;
14768 tmp_rh->handler = handler;
14769 } else if (handler_type == WEBSOCKET_HANDLER) {
14770 tmp_rh->subprotocols = subprotocols;
14771 tmp_rh->connect_handler = connect_handler;
14772 tmp_rh->ready_handler = ready_handler;
14773 tmp_rh->data_handler = data_handler;
14774 tmp_rh->close_handler = close_handler;
14775 } else { /* AUTH_HANDLER */
14776 tmp_rh->auth_handler = auth_handler;
14777 }
14778 tmp_rh->cbdata = cbdata;
14779 tmp_rh->handler_type = handler_type;
14780 tmp_rh->next = NULL;
14781
14782 *lastref = tmp_rh;
14783 mg_unlock_context(phys_ctx);
14784}
14785
14786
14787CIVETWEB_API void
14789 const char *uri,
14791 void *cbdata)
14792{
14794 &(ctx->dd),
14795 uri,
14797 handler == NULL,
14798 handler,
14799 NULL,
14800 NULL,
14801 NULL,
14802 NULL,
14803 NULL,
14804 NULL,
14805 cbdata);
14806}
14807
14808
14809CIVETWEB_API void
14827
14828
14829CIVETWEB_API void
14857
14858
14859CIVETWEB_API void
14861 const char *uri,
14863 void *cbdata)
14864{
14866 &(ctx->dd),
14867 uri,
14869 handler == NULL,
14870 NULL,
14871 NULL,
14872 NULL,
14873 NULL,
14874 NULL,
14875 NULL,
14876 handler,
14877 cbdata);
14878}
14879
14880
14881static int
14883 int handler_type,
14891 void **cbdata,
14893{
14894 const struct mg_request_info *request_info = mg_get_request_info(conn);
14895 if (request_info) {
14896 const char *uri = request_info->local_uri;
14897 size_t urilen = strlen(uri);
14898 struct mg_handler_info *tmp_rh;
14899 int step, matched;
14900
14901 if (!conn || !conn->phys_ctx || !conn->dom_ctx) {
14902 return 0;
14903 }
14904
14906
14907 for (step = 0; step < 3; step++) {
14908 for (tmp_rh = conn->dom_ctx->handlers; tmp_rh != NULL;
14909 tmp_rh = tmp_rh->next) {
14910 if (tmp_rh->handler_type != handler_type) {
14911 continue;
14912 }
14913 if (step == 0) {
14914 /* first try for an exact match */
14915 matched = (tmp_rh->uri_len == urilen)
14916 && (strcmp(tmp_rh->uri, uri) == 0);
14917 } else if (step == 1) {
14918 /* next try for a partial match, we will accept
14919 uri/something */
14920 matched =
14921 (tmp_rh->uri_len < urilen)
14922 && (uri[tmp_rh->uri_len] == '/')
14923 && (memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0);
14924 } else {
14925 /* finally try for pattern match */
14926 matched =
14927 match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0;
14928 }
14929 if (matched) {
14931 *subprotocols = tmp_rh->subprotocols;
14932 *connect_handler = tmp_rh->connect_handler;
14933 *ready_handler = tmp_rh->ready_handler;
14934 *data_handler = tmp_rh->data_handler;
14935 *close_handler = tmp_rh->close_handler;
14936 } else if (handler_type == REQUEST_HANDLER) {
14937 if (tmp_rh->removing) {
14938 /* Treat as none found */
14939 step = 2;
14940 break;
14941 }
14942 *handler = tmp_rh->handler;
14943 /* Acquire handler and give it back */
14944 tmp_rh->refcount++;
14946 } else { /* AUTH_HANDLER */
14947 *auth_handler = tmp_rh->auth_handler;
14948 }
14949 *cbdata = tmp_rh->cbdata;
14951 return 1;
14952 }
14953 }
14954 }
14955
14957 }
14958 return 0; /* none found */
14959}
14960
14961
14962/* Check if the script file is in a path, allowed for script files.
14963 * This can be used if uploading files is possible not only for the server
14964 * admin, and the upload mechanism does not check the file extension.
14965 */
14966static int
14967is_in_script_path(const struct mg_connection *conn, const char *path)
14968{
14969 /* TODO (Feature): Add config value for allowed script path.
14970 * Default: All allowed. */
14971 (void)conn;
14972 (void)path;
14973 return 1;
14974}
14975
14976
14977#if defined(USE_WEBSOCKET) && defined(MG_EXPERIMENTAL_INTERFACES)
14978static int
14980 int bits,
14981 char *data,
14982 size_t len,
14983 void *cbdata)
14984{
14985 struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
14986 if (pcallbacks->websocket_data) {
14987 return pcallbacks->websocket_data(conn, bits, data, len);
14988 }
14989 /* No handler set - assume "OK" */
14990 return 1;
14991}
14992
14993
14994static void
14996 void *cbdata)
14997{
14998 struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
14999 if (pcallbacks->connection_close) {
15000 pcallbacks->connection_close(conn);
15001 }
15002}
15003#endif
15004
15005
15006/* Decrement recount of handler. conn must not be NULL, handler_info may be NULL
15007 */
15008static void
15011{
15012 if (handler_info != NULL) {
15013 /* Use context lock for ref counter */
15015 handler_info->refcount--;
15017 }
15018}
15019
15020
15021/* This is the heart of the Civetweb's logic.
15022 * This function is called when the request is read, parsed and validated,
15023 * and Civetweb must decide what action to take: serve a file, or
15024 * a directory, or call embedded function, etcetera. */
15025static void
15027{
15028 struct mg_request_info *ri = &conn->request_info;
15029 char path[UTF8_PATH_MAX];
15030 int uri_len, ssl_index;
15034 int i;
15035 struct mg_file file = STRUCT_FILE_INITIALIZER;
15043 void *callback_data = NULL;
15044 mg_authorization_handler auth_handler = NULL;
15045 void *auth_callback_data = NULL;
15046 int handler_type;
15047 time_t curtime = time(NULL);
15048 char date[64];
15049 char *tmp;
15050
15051 path[0] = 0;
15052
15053 /* 0. Reset internal state (required for HTTP/2 proxy) */
15054 conn->request_state = 0;
15055
15056 /* 1. get the request url */
15057 /* 1.1. split into url and query string */
15058 if ((conn->request_info.query_string = strchr(ri->request_uri, '?'))
15059 != NULL) {
15060 *((char *)conn->request_info.query_string++) = '\0';
15061 }
15062
15063 /* 1.2. do a https redirect, if required. Do not decode URIs yet. */
15064 if (!conn->client.is_ssl && conn->client.ssl_redir) {
15066 if (ssl_index >= 0) {
15067 int port = (int)ntohs(USA_IN_PORT_UNSAFE(
15068 &(conn->phys_ctx->listening_sockets[ssl_index].lsa)));
15069 redirect_to_https_port(conn, port);
15070 } else {
15071 /* A http to https forward port has been specified,
15072 * but no https port to forward to. */
15073 mg_send_http_error(conn,
15074 503,
15075 "%s",
15076 "Error: SSL forward not configured properly");
15077 mg_cry_internal(conn,
15078 "%s",
15079 "Can not redirect to SSL, no SSL port available");
15080 }
15081 return;
15082 }
15083 uri_len = (int)strlen(ri->local_uri);
15084
15085 /* 1.3. decode url (if config says so) */
15086 if (should_decode_url(conn)) {
15087 url_decode_in_place((char *)ri->local_uri);
15088 }
15089
15090 /* URL decode the query-string only if explicitly set in the configuration
15091 */
15092 if (conn->request_info.query_string) {
15093 if (should_decode_query_string(conn)) {
15094 url_decode_in_place((char *)conn->request_info.query_string);
15095 }
15096 }
15097
15098 /* 1.4. clean URIs, so a path like allowed_dir/../forbidden_file is not
15099 * possible. The fact that we cleaned the URI is stored in that the
15100 * pointer to ri->local_ur and ri->local_uri_raw are now different.
15101 * ri->local_uri_raw still points to memory allocated in
15102 * worker_thread_run(). ri->local_uri is private to the request so we
15103 * don't have to use preallocated memory here. */
15104 tmp = mg_strdup(ri->local_uri_raw);
15105 if (!tmp) {
15106 /* Out of memory. We cannot do anything reasonable here. */
15107 return;
15108 }
15110 ri->local_uri = tmp;
15111
15112 /* step 1. completed, the url is known now */
15113 DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri);
15114
15115 /* 2. if this ip has limited speed, set it for this connection */
15116 conn->throttle = set_throttle(conn->dom_ctx->config[THROTTLE],
15117 &conn->client.rsa,
15118 ri->local_uri);
15119
15120 /* 3. call a "handle everything" callback, if registered */
15121 if (conn->phys_ctx->callbacks.begin_request != NULL) {
15122 /* Note that since V1.7 the "begin_request" function is called
15123 * before an authorization check. If an authorization check is
15124 * required, use a request_handler instead. */
15125 i = conn->phys_ctx->callbacks.begin_request(conn);
15126 if (i > 0) {
15127 /* callback already processed the request. Store the
15128 return value as a status code for the access log. */
15129 conn->status_code = i;
15130 if (!conn->must_close) {
15132 }
15133 DEBUG_TRACE("%s", "begin_request handled request");
15134 return;
15135 } else if (i == 0) {
15136 /* civetweb should process the request */
15137 } else {
15138 /* unspecified - may change with the next version */
15139 DEBUG_TRACE("%s", "done (undocumented behavior)");
15140 return;
15141 }
15142 }
15143
15144 /* request not yet handled by a handler or redirect, so the request
15145 * is processed here */
15146
15147 /* 4. Check for CORS preflight requests and handle them (if configured).
15148 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
15149 */
15150 if (!strcmp(ri->request_method, "OPTIONS")) {
15151 /* Send a response to CORS preflights only if
15152 * access_control_allow_methods is not NULL and not an empty string.
15153 * In this case, scripts can still handle CORS. */
15154 const char *cors_meth_cfg =
15156 const char *cors_orig_cfg =
15157 conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
15158 const char *cors_origin =
15159 get_header(ri->http_headers, ri->num_headers, "Origin");
15160 const char *cors_acrm = get_header(ri->http_headers,
15161 ri->num_headers,
15162 "Access-Control-Request-Method");
15163
15164 /* Todo: check if cors_origin is in cors_orig_cfg.
15165 * Or, let the client check this. */
15166
15167 if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0)
15168 && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0)
15169 && (cors_origin != NULL) && (cors_acrm != NULL)) {
15170 /* This is a valid CORS preflight, and the server is configured
15171 * to handle it automatically. */
15172 const char *cors_acrh =
15174 ri->num_headers,
15175 "Access-Control-Request-Headers");
15176 const char *cors_cred_cfg =
15178 const char *cors_exphdr_cfg =
15180
15181 gmt_time_string(date, sizeof(date), &curtime);
15182 mg_printf(conn,
15183 "HTTP/1.1 200 OK\r\n"
15184 "Date: %s\r\n"
15185 "Access-Control-Allow-Origin: %s\r\n"
15186 "Access-Control-Allow-Methods: %s\r\n"
15187 "Content-Length: 0\r\n"
15188 "Connection: %s\r\n",
15189 date,
15191 ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg),
15193
15194 if (cors_cred_cfg && *cors_cred_cfg) {
15195 mg_printf(conn,
15196 "Access-Control-Allow-Credentials: %s\r\n",
15198 }
15199
15201 mg_printf(conn,
15202 "Access-Control-Expose-Headers: %s\r\n",
15204 }
15205
15206 if (cors_acrh || (cors_cred_cfg && *cors_cred_cfg)) {
15207 /* CORS request is asking for additional headers */
15208 const char *cors_hdr_cfg =
15210
15211 if ((cors_hdr_cfg != NULL) && (*cors_hdr_cfg != 0)) {
15212 /* Allow only if access_control_allow_headers is
15213 * not NULL and not an empty string. If this
15214 * configuration is set to *, allow everything.
15215 * Otherwise this configuration must be a list
15216 * of allowed HTTP header names. */
15217 mg_printf(conn,
15218 "Access-Control-Allow-Headers: %s\r\n",
15219 ((cors_hdr_cfg[0] == '*') ? cors_acrh
15220 : cors_hdr_cfg));
15221 }
15222 }
15223 mg_printf(conn, "Access-Control-Max-Age: 60\r\n");
15224 mg_printf(conn, "\r\n");
15225 DEBUG_TRACE("%s", "OPTIONS done");
15226 return;
15227 }
15228 }
15229
15230 /* 5. interpret the url to find out how the request must be handled
15231 */
15232 /* 5.1. first test, if the request targets the regular http(s)://
15233 * protocol namespace or the websocket ws(s):// protocol namespace.
15234 */
15236#if defined(USE_WEBSOCKET)
15238#else
15239 handler_type = REQUEST_HANDLER;
15240#endif /* defined(USE_WEBSOCKET) */
15241
15243 HTTP1_only;
15244 }
15245
15246 /* 5.2. check if the request will be handled by a callback */
15247 if (get_request_handler(conn,
15248 handler_type,
15250 &subprotocols,
15255 NULL,
15256 &callback_data,
15257 &handler_info)) {
15258 /* 5.2.1. A callback will handle this request. All requests
15259 * handled by a callback have to be considered as requests
15260 * to a script resource. */
15264 /* Never handle a C callback according to File WebDav rules,
15265 * even if it is a webdav method */
15266 is_webdav_request = 0; /* is_civetweb_webdav_method(conn); */
15267 } else {
15269
15270 /* 5.2.2. No callback is responsible for this request. The URI
15271 * addresses a file based resource (static content or Lua/cgi
15272 * scripts in the file system). */
15274 interpret_uri(conn,
15275 path,
15276 sizeof(path),
15277 &file.stat,
15278 &is_found,
15284 }
15285
15286 /* 5.3. A webdav request (PROPFIND/PROPPATCH/LOCK/UNLOCK) */
15287 if (is_webdav_request) {
15288 /* TODO: Do we need a config option? */
15289 const char *webdav_enable = conn->dom_ctx->config[ENABLE_WEBDAV];
15290 if (webdav_enable[0] != 'y') {
15291 mg_send_http_error(conn,
15292 405,
15293 "%s method not allowed",
15294 conn->request_info.request_method);
15295 DEBUG_TRACE("%s", "webdav rejected");
15296 return;
15297 }
15298 }
15299
15300 /* 6. authorization check */
15301 /* 6.1. a custom authorization handler is installed */
15302 if (get_request_handler(conn,
15304 NULL,
15305 NULL,
15306 NULL,
15307 NULL,
15308 NULL,
15309 NULL,
15310 &auth_handler,
15312 NULL)) {
15313 if (!auth_handler(conn, auth_callback_data)) {
15314
15315 /* Callback handler will not be used anymore. Release it */
15317 DEBUG_TRACE("%s", "auth handler rejected request");
15318 return;
15319 }
15322 HTTP1_only;
15323 /* 6.2. this request is a PUT/DELETE to a real file */
15324 /* 6.2.1. thus, the server must have real files */
15325#if defined(NO_FILES)
15326 if (1) {
15327#else
15328 if (conn->dom_ctx->config[DOCUMENT_ROOT] == NULL
15329 || conn->dom_ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL) {
15330#endif
15331 /* This code path will not be called for request handlers */
15333
15334 /* This server does not have any real files, thus the
15335 * PUT/DELETE methods are not valid. */
15336 mg_send_http_error(conn,
15337 405,
15338 "%s method not allowed",
15339 conn->request_info.request_method);
15340 DEBUG_TRACE("%s", "all file based put/delete requests rejected");
15341 return;
15342 }
15343
15344#if !defined(NO_FILES)
15345 /* 6.2.2. Check if put authorization for static files is
15346 * available.
15347 */
15348 if (!is_authorized_for_put(conn)) {
15350 DEBUG_TRACE("%s", "file write needs authorization");
15351 return;
15352 }
15353#endif
15354
15355 } else {
15356 /* 6.3. This is either a OPTIONS, GET, HEAD or POST request,
15357 * or it is a PUT or DELETE request to a resource that does not
15358 * correspond to a file. Check authorization. */
15359 if (!check_authorization(conn, path)) {
15361
15362 /* Callback handler will not be used anymore. Release it */
15364 DEBUG_TRACE("%s", "access authorization required");
15365 return;
15366 }
15367 }
15368
15369 /* request is authorized or does not need authorization */
15370
15371 /* 7. check if there are request handlers for this uri */
15373 HTTP1_only;
15374 if (!is_websocket_request) {
15375 i = callback_handler(conn, callback_data);
15376
15377 /* Callback handler will not be used anymore. Release it */
15379
15380 if (i > 0) {
15381 /* Do nothing, callback has served the request. Store
15382 * then return value as status code for the log and discard
15383 * all data from the client not used by the callback. */
15384 conn->status_code = i;
15385 if (!conn->must_close) {
15387 }
15388 } else {
15389 /* The handler did NOT handle the request. */
15390 /* Some proper reactions would be:
15391 * a) close the connections without sending anything
15392 * b) send a 404 not found
15393 * c) try if there is a file matching the URI
15394 * It would be possible to do a, b or c in the callback
15395 * implementation, and return 1 - we cannot do anything
15396 * here, that is not possible in the callback.
15397 *
15398 * TODO: What would be the best reaction here?
15399 * (Note: The reaction may change, if there is a better
15400 * idea.)
15401 */
15402
15403 /* For the moment, use option c: We look for a proper file,
15404 * but since a file request is not always a script resource,
15405 * the authorization check might be different. */
15407
15408 /* Here we are at a dead end:
15409 * According to URI matching, a callback should be
15410 * responsible for handling the request,
15411 * we called it, but the callback declared itself
15412 * not responsible.
15413 * We use a goto here, to get out of this dead end,
15414 * and continue with the default handling.
15415 * A goto here is simpler and better to understand
15416 * than some curious loop. */
15418 }
15419 } else {
15420#if defined(USE_WEBSOCKET)
15422 path,
15429 callback_data);
15430#endif
15431 }
15432 DEBUG_TRACE("%s", "websocket handling done");
15433 return;
15434 }
15435
15436 /* 8. handle websocket requests */
15437#if defined(USE_WEBSOCKET)
15439 HTTP1_only;
15440 if (is_script_resource) {
15441
15442 if (is_in_script_path(conn, path)) {
15443 /* Websocket Lua script */
15445 path,
15446 0 /* Lua Script */,
15447 NULL,
15448 NULL,
15449 NULL,
15450 NULL,
15451 NULL,
15452 conn->phys_ctx->user_data);
15453 } else {
15454 /* Script was in an illegal path */
15455 mg_send_http_error(conn, 403, "%s", "Forbidden");
15456 }
15457 } else {
15458 mg_send_http_error(conn, 404, "%s", "Not found");
15459 }
15460 DEBUG_TRACE("%s", "websocket script done");
15461 return;
15462 } else
15463#endif
15464
15465#if defined(NO_FILES)
15466 /* 9a. In case the server uses only callbacks, this uri is
15467 * unknown.
15468 * Then, all request handling ends here. */
15469 mg_send_http_error(conn, 404, "%s", "Not Found");
15470
15471#else
15472 /* 9b. This request is either for a static file or resource handled
15473 * by a script file. Thus, a DOCUMENT_ROOT must exist. */
15474 if (conn->dom_ctx->config[DOCUMENT_ROOT] == NULL) {
15475 mg_send_http_error(conn, 404, "%s", "Not Found");
15476 DEBUG_TRACE("%s", "no document root available");
15477 return;
15478 }
15479
15480 /* 10. Request is handled by a script */
15481 if (is_script_resource) {
15482 HTTP1_only;
15483 handle_file_based_request(conn, path, &file);
15484 DEBUG_TRACE("%s", "script handling done");
15485 return;
15486 }
15487
15488 /* Request was not handled by a callback or script. It will be
15489 * handled by a server internal method. */
15490
15491 /* 11. Handle put/delete/mkcol requests */
15493 HTTP1_only;
15494 /* 11.1. PUT method */
15495 if (!strcmp(ri->request_method, "PUT")) {
15496 put_file(conn, path);
15497 DEBUG_TRACE("handling %s request to %s done",
15498 ri->request_method,
15499 path);
15500 return;
15501 }
15502 /* 11.2. DELETE method */
15503 if (!strcmp(ri->request_method, "DELETE")) {
15504 delete_file(conn, path);
15505 DEBUG_TRACE("handling %s request to %s done",
15506 ri->request_method,
15507 path);
15508 return;
15509 }
15510 /* 11.3. MKCOL method */
15511 if (!strcmp(ri->request_method, "MKCOL")) {
15512 dav_mkcol(conn, path);
15513 DEBUG_TRACE("handling %s request to %s done",
15514 ri->request_method,
15515 path);
15516 return;
15517 }
15518 /* 11.4. MOVE method */
15519 if (!strcmp(ri->request_method, "MOVE")) {
15520 dav_move_file(conn, path, 0);
15521 DEBUG_TRACE("handling %s request to %s done",
15522 ri->request_method,
15523 path);
15524 return;
15525 }
15526 if (!strcmp(ri->request_method, "COPY")) {
15527 dav_move_file(conn, path, 1);
15528 DEBUG_TRACE("handling %s request to %s done",
15529 ri->request_method,
15530 path);
15531 return;
15532 }
15533 /* 11.5. LOCK method */
15534 if (!strcmp(ri->request_method, "LOCK")) {
15535 dav_lock_file(conn, path);
15536 DEBUG_TRACE("handling %s request to %s done",
15537 ri->request_method,
15538 path);
15539 return;
15540 }
15541 /* 11.6. UNLOCK method */
15542 if (!strcmp(ri->request_method, "UNLOCK")) {
15543 dav_unlock_file(conn, path);
15544 DEBUG_TRACE("handling %s request to %s done",
15545 ri->request_method,
15546 path);
15547 return;
15548 }
15549 /* 11.7. PROPPATCH method */
15550 if (!strcmp(ri->request_method, "PROPPATCH")) {
15551 dav_proppatch(conn, path);
15552 DEBUG_TRACE("handling %s request to %s done",
15553 ri->request_method,
15554 path);
15555 return;
15556 }
15557 /* 11.8. Other methods, e.g.: PATCH
15558 * This method is not supported for static resources,
15559 * only for scripts (Lua, CGI) and callbacks. */
15560 mg_send_http_error(conn,
15561 405,
15562 "%s method not allowed",
15563 conn->request_info.request_method);
15564 DEBUG_TRACE("method %s on %s is not supported",
15565 ri->request_method,
15566 path);
15567 return;
15568 }
15569
15570 /* 11. File does not exist, or it was configured that it should be
15571 * hidden */
15572 if (!is_found || (must_hide_file(conn, path))) {
15573 mg_send_http_error(conn, 404, "%s", "Not found");
15574 DEBUG_TRACE("handling %s request to %s: file not found",
15575 ri->request_method,
15576 path);
15577 return;
15578 }
15579
15580 /* 12. Directory uris should end with a slash */
15581 if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0)
15582 && (ri->local_uri[uri_len - 1] != '/')) {
15583
15584 /* Path + server root */
15585 size_t buflen = UTF8_PATH_MAX * 2 + 2;
15586 char *new_path;
15587
15588 if (ri->query_string) {
15589 buflen += strlen(ri->query_string);
15590 }
15591 new_path = (char *)mg_malloc_ctx(buflen, conn->phys_ctx);
15592 if (!new_path) {
15593 mg_send_http_error(conn, 500, "out or memory");
15594 } else {
15595 mg_get_request_link(conn, new_path, buflen - 1);
15596 strcat(new_path, "/");
15597 if (ri->query_string) {
15598 /* Append ? and query string */
15599 strcat(new_path, "?");
15601 }
15602 mg_send_http_redirect(conn, new_path, 301);
15604 }
15605 DEBUG_TRACE("%s request to %s: directory redirection sent",
15606 ri->request_method,
15607 path);
15608 return;
15609 }
15610
15611 /* 13. Handle other methods than GET/HEAD */
15612 /* 13.1. Handle PROPFIND */
15613 if (!strcmp(ri->request_method, "PROPFIND")) {
15614 handle_propfind(conn, path, &file.stat);
15615 DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15616 return;
15617 }
15618 /* 13.2. Handle OPTIONS for files */
15619 if (!strcmp(ri->request_method, "OPTIONS")) {
15620 /* This standard handler is only used for real files.
15621 * Scripts should support the OPTIONS method themselves, to allow a
15622 * maximum flexibility.
15623 * Lua and CGI scripts may fully support CORS this way (including
15624 * preflights). */
15625 send_options(conn);
15626 DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15627 return;
15628 }
15629 /* 13.3. everything but GET and HEAD (e.g. POST) */
15630 if ((0 != strcmp(ri->request_method, "GET"))
15631 && (0 != strcmp(ri->request_method, "HEAD"))) {
15632 mg_send_http_error(conn,
15633 405,
15634 "%s method not allowed",
15635 conn->request_info.request_method);
15636 DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15637 return;
15638 }
15639
15640 /* 14. directories */
15641 if (file.stat.is_directory) {
15642 /* Substitute files have already been handled above. */
15643 /* Here we can either generate and send a directory listing,
15644 * or send an "access denied" error. */
15646 "yes")) {
15647 handle_directory_request(conn, path);
15648 } else {
15649 mg_send_http_error(conn,
15650 403,
15651 "%s",
15652 "Error: Directory listing denied");
15653 }
15654 DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15655 return;
15656 }
15657
15658 /* 15. Files with search/replace patterns: LSP and SSI */
15660 HTTP1_only;
15661 handle_file_based_request(conn, path, &file);
15662 DEBUG_TRACE("handling %s request to %s done (template)",
15663 ri->request_method,
15664 path);
15665 return;
15666 }
15667
15668 /* 16. Static file - maybe cached */
15669#if !defined(NO_CACHING)
15670 if ((!conn->in_error_handler) && is_not_modified(conn, &file.stat)) {
15671 /* Send 304 "Not Modified" - this must not send any body data */
15673 DEBUG_TRACE("handling %s request to %s done (not modified)",
15674 ri->request_method,
15675 path);
15676 return;
15677 }
15678#endif /* !NO_CACHING */
15679
15680 /* 17. Static file - not cached */
15681 handle_static_file_request(conn, path, &file, NULL, NULL);
15682 DEBUG_TRACE("handling %s request to %s done (static)",
15683 ri->request_method,
15684 path);
15685
15686#endif /* !defined(NO_FILES) */
15687}
15688
15689
15690#if !defined(NO_FILESYSTEMS)
15691static void
15693 const char *path,
15694 struct mg_file *file)
15695{
15696#if !defined(NO_CGI)
15697 int cgi_config_idx, inc, max;
15698#endif
15699
15700 if (!conn || !conn->dom_ctx) {
15701 return;
15702 }
15703
15704#if defined(USE_LUA)
15706 path)
15707 > 0) {
15708 if (is_in_script_path(conn, path)) {
15709 /* Lua server page: an SSI like page containing mostly plain
15710 * html code plus some tags with server generated contents. */
15711 handle_lsp_request(conn, path, file, NULL);
15712 } else {
15713 /* Script was in an illegal path */
15714 mg_send_http_error(conn, 403, "%s", "Forbidden");
15715 }
15716 return;
15717 }
15718
15719 if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS], path)
15720 > 0) {
15721 if (is_in_script_path(conn, path)) {
15722 /* Lua in-server module script: a CGI like script used to
15723 * generate the entire reply. */
15724 mg_exec_lua_script(conn, path, NULL);
15725 } else {
15726 /* Script was in an illegal path */
15727 mg_send_http_error(conn, 403, "%s", "Forbidden");
15728 }
15729 return;
15730 }
15731#endif
15732
15733#if defined(USE_DUKTAPE)
15735 path)
15736 > 0) {
15737 if (is_in_script_path(conn, path)) {
15738 /* Call duktape to generate the page */
15739 mg_exec_duktape_script(conn, path);
15740 } else {
15741 /* Script was in an illegal path */
15742 mg_send_http_error(conn, 403, "%s", "Forbidden");
15743 }
15744 return;
15745 }
15746#endif
15747
15748#if !defined(NO_CGI)
15751 for (cgi_config_idx = 0; cgi_config_idx < max; cgi_config_idx += inc) {
15752 if (conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx] != NULL) {
15754 conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx],
15755 path)
15756 > 0) {
15757 if (is_in_script_path(conn, path)) {
15758 /* CGI scripts may support all HTTP methods */
15760 } else {
15761 /* Script was in an illegal path */
15762 mg_send_http_error(conn, 403, "%s", "Forbidden");
15763 }
15764 return;
15765 }
15766 }
15767 }
15768#endif /* !NO_CGI */
15769
15770 if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path) > 0) {
15771 if (is_in_script_path(conn, path)) {
15772 handle_ssi_file_request(conn, path, file);
15773 } else {
15774 /* Script was in an illegal path */
15775 mg_send_http_error(conn, 403, "%s", "Forbidden");
15776 }
15777 return;
15778 }
15779
15780#if !defined(NO_CACHING)
15781 if ((!conn->in_error_handler) && is_not_modified(conn, &file->stat)) {
15782 /* Send 304 "Not Modified" - this must not send any body data */
15784 return;
15785 }
15786#endif /* !NO_CACHING */
15787
15788 handle_static_file_request(conn, path, file, NULL, NULL);
15789}
15790#endif /* NO_FILESYSTEMS */
15791
15792
15793static void
15795{
15796 unsigned int i;
15797 if (!ctx) {
15798 return;
15799 }
15800
15801 for (i = 0; i < ctx->num_listening_sockets; i++) {
15802 closesocket(ctx->listening_sockets[i].sock);
15803#if defined(USE_X_DOM_SOCKET)
15804 /* For unix domain sockets, the socket name represents a file that has
15805 * to be deleted. */
15806 /* See
15807 * https://stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix
15808 */
15809 if ((ctx->listening_sockets[i].lsa.sin.sin_family == AF_UNIX)
15810 && (ctx->listening_sockets[i].sock != INVALID_SOCKET)) {
15812 remove(ctx->listening_sockets[i].lsa.sun.sun_path));
15813 }
15814#endif
15815 ctx->listening_sockets[i].sock = INVALID_SOCKET;
15816 }
15818 ctx->listening_sockets = NULL;
15821}
15822
15823
15824/* Valid listening port specification is: [ip_address:]port[s]
15825 * Examples for IPv4: 80, 443s, 127.0.0.1:3128, 192.0.2.3:8080s
15826 * Examples for IPv6: [::]:80, [::1]:80,
15827 * [2001:0db8:7654:3210:FEDC:BA98:7654:3210]:443s
15828 * see https://tools.ietf.org/html/rfc3513#section-2.2
15829 * In order to bind to both, IPv4 and IPv6, you can either add
15830 * both ports using 8080,[::]:8080, or the short form +8080.
15831 * Both forms differ in detail: 8080,[::]:8080 create two sockets,
15832 * one only accepting IPv4 the other only IPv6. +8080 creates
15833 * one socket accepting IPv4 and IPv6. Depending on the IPv6
15834 * environment, they might work differently, or might not work
15835 * at all - it must be tested what options work best in the
15836 * relevant network environment.
15837 */
15838static int
15839parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
15840{
15841 unsigned int a, b, c, d;
15842 unsigned port;
15843 unsigned long portUL;
15844 int len;
15845 const char *cb;
15846 char *endptr;
15847#if defined(USE_IPV6)
15848 char buf[100] = {0};
15849#endif
15850
15851 /* MacOS needs that. If we do not zero it, subsequent bind() will fail.
15852 * Also, all-zeroes in the socket address means binding to all addresses
15853 * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). */
15854 memset(so, 0, sizeof(*so));
15855 so->lsa.sin.sin_family = AF_INET;
15856 *ip_version = 0;
15857
15858 /* Initialize len as invalid. */
15859 port = 0;
15860 len = 0;
15861
15862 /* Test for different ways to format this string */
15863 if (sscanf(vec->ptr,
15864 "%u.%u.%u.%u:%u%n",
15865 &a,
15866 &b,
15867 &c,
15868 &d,
15869 &port,
15870 &len) // NOLINT(cert-err34-c) 'sscanf' used to convert a string
15871 // to an integer value, but function will not report
15872 // conversion errors; consider using 'strtol' instead
15873 == 5) {
15874 /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */
15875 so->lsa.sin.sin_addr.s_addr =
15876 htonl((a << 24) | (b << 16) | (c << 8) | d);
15877 so->lsa.sin.sin_port = htons((uint16_t)port);
15878 *ip_version = 4;
15879
15880#if defined(USE_IPV6)
15881 } else if (sscanf(vec->ptr, "[%49[^]]]:%u%n", buf, &port, &len) == 2
15882 && ((size_t)len <= vec->len)
15883 && mg_inet_pton(
15884 AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6), 0)) {
15885 /* IPv6 address, examples: see above */
15886 /* so->lsa.sin6.sin6_family = AF_INET6; already set by mg_inet_pton
15887 */
15888 so->lsa.sin6.sin6_port = htons((uint16_t)port);
15889 *ip_version = 6;
15890#endif
15891
15892 } else if ((vec->ptr[0] == '+')
15893 && (sscanf(vec->ptr + 1, "%u%n", &port, &len)
15894 == 1)) { // NOLINT(cert-err34-c) 'sscanf' used to convert a
15895 // string to an integer value, but function will not
15896 // report conversion errors; consider using 'strtol'
15897 // instead
15898
15899 /* Port is specified with a +, bind to IPv6 and IPv4, INADDR_ANY */
15900 /* Add 1 to len for the + character we skipped before */
15901 len++;
15902
15903#if defined(USE_IPV6)
15904 /* Set socket family to IPv6, do not use IPV6_V6ONLY */
15905 so->lsa.sin6.sin6_family = AF_INET6;
15906 so->lsa.sin6.sin6_port = htons((uint16_t)port);
15907 *ip_version = 4 + 6;
15908#else
15909 /* Bind to IPv4 only, since IPv6 is not built in. */
15910 so->lsa.sin.sin_port = htons((uint16_t)port);
15911 *ip_version = 4;
15912#endif
15913
15914 } else if (is_valid_port(portUL = strtoul(vec->ptr, &endptr, 0))
15915 && (vec->ptr != endptr)) {
15916 len = (int)(endptr - vec->ptr);
15917 port = (uint16_t)portUL;
15918 /* If only port is specified, bind to IPv4, INADDR_ANY */
15919 so->lsa.sin.sin_port = htons((uint16_t)port);
15920 *ip_version = 4;
15921
15922 } else if ((cb = strchr(vec->ptr, ':')) != NULL) {
15923 /* String could be a hostname. This check algorithm
15924 * will only work for RFC 952 compliant hostnames,
15925 * starting with a letter, containing only letters,
15926 * digits and hyphen ('-'). Newer specs may allow
15927 * more, but this is not guaranteed here, since it
15928 * may interfere with rules for port option lists. */
15929
15930 /* According to RFC 1035, hostnames are restricted to 255 characters
15931 * in total (63 between two dots). */
15932 char hostname[256];
15933 size_t hostnlen = (size_t)(cb - vec->ptr);
15934
15935 if ((hostnlen >= vec->len) || (hostnlen >= sizeof(hostname))) {
15936 /* This would be invalid in any case */
15937 *ip_version = 0;
15938 return 0;
15939 }
15940
15942
15943 if (mg_inet_pton(
15944 AF_INET, hostname, &so->lsa.sin, sizeof(so->lsa.sin), 1)) {
15945 if (sscanf(cb + 1, "%u%n", &port, &len)
15946 == 1) { // NOLINT(cert-err34-c) 'sscanf' used to convert a
15947 // string to an integer value, but function will not
15948 // report conversion errors; consider using 'strtol'
15949 // instead
15950 *ip_version = 4;
15951 so->lsa.sin.sin_port = htons((uint16_t)port);
15952 len += (int)(hostnlen + 1);
15953 } else {
15954 len = 0;
15955 }
15956#if defined(USE_IPV6)
15957 } else if (mg_inet_pton(AF_INET6,
15958 hostname,
15959 &so->lsa.sin6,
15960 sizeof(so->lsa.sin6),
15961 1)) {
15962 if (sscanf(cb + 1, "%u%n", &port, &len) == 1) {
15963 *ip_version = 6;
15964 so->lsa.sin6.sin6_port = htons((uint16_t)port);
15965 len += (int)(hostnlen + 1);
15966 } else {
15967 len = 0;
15968 }
15969#endif
15970 } else {
15971 len = 0;
15972 }
15973
15974#if defined(USE_X_DOM_SOCKET)
15975
15976 } else if (vec->ptr[0] == 'x') {
15977 /* unix (linux) domain socket */
15978 if (vec->len < sizeof(so->lsa.sun.sun_path)) {
15979 len = vec->len;
15980 so->lsa.sun.sun_family = AF_UNIX;
15981 memset(so->lsa.sun.sun_path, 0, sizeof(so->lsa.sun.sun_path));
15982 memcpy(so->lsa.sun.sun_path, (char *)vec->ptr + 1, vec->len - 1);
15983 port = 0;
15984 *ip_version = 99;
15985 } else {
15986 /* String too long */
15987 len = 0;
15988 }
15989#endif
15990
15991 } else {
15992 /* Parsing failure. */
15993 len = 0;
15994 }
15995
15996 /* sscanf and the option splitting code ensure the following condition
15997 * Make sure the port is valid and vector ends with the port, 'o', 's', or
15998 * 'r' */
15999 if ((len > 0) && (is_valid_port(port))) {
16000 int bad_suffix = 0;
16001 size_t i;
16002
16003 /* Parse any suffix character(s) after the port number */
16004 for (i = len; i < vec->len; i++) {
16005 unsigned char *opt = NULL;
16006 switch (vec->ptr[i]) {
16007 case 'o':
16008 opt = &so->is_optional;
16009 break;
16010 case 'r':
16011 opt = &so->ssl_redir;
16012 break;
16013 case 's':
16014 opt = &so->is_ssl;
16015 break;
16016 default: /* empty */
16017 break;
16018 }
16019
16020 if ((opt) && (*opt == 0))
16021 *opt = 1;
16022 else {
16023 bad_suffix = 1;
16024 break;
16025 }
16026 }
16027
16028 if ((bad_suffix == 0) && ((so->is_ssl == 0) || (so->ssl_redir == 0))) {
16029 return 1;
16030 }
16031 }
16032
16033 /* Reset ip_version to 0 if there is an error */
16034 *ip_version = 0;
16035 return 0;
16036}
16037
16038
16039/* Is there any SSL port in use? */
16040static int
16042{
16043 if (ports) {
16044 /* There are several different allowed syntax variants:
16045 * - "80" for a single port using every network interface
16046 * - "localhost:80" for a single port using only localhost
16047 * - "80,localhost:8080" for two ports, one bound to localhost
16048 * - "80,127.0.0.1:8084,[::1]:8086" for three ports, one bound
16049 * to IPv4 localhost, one to IPv6 localhost
16050 * - "+80" use port 80 for IPv4 and IPv6
16051 * - "+80r,+443s" port 80 (HTTP) is a redirect to port 443 (HTTPS),
16052 * for both: IPv4 and IPv4
16053 * - "+443s,localhost:8080" port 443 (HTTPS) for every interface,
16054 * additionally port 8080 bound to localhost connections
16055 *
16056 * If we just look for 's' anywhere in the string, "localhost:80"
16057 * will be detected as SSL (false positive).
16058 * Looking for 's' after a digit may cause false positives in
16059 * "my24service:8080".
16060 * Looking from 's' backward if there are only ':' and numbers
16061 * before will not work for "24service:8080" (non SSL, port 8080)
16062 * or "24s" (SSL, port 24).
16063 *
16064 * Remark: Initially hostnames were not allowed to start with a
16065 * digit (according to RFC 952), this was allowed later (RFC 1123,
16066 * Section 2.1).
16067 *
16068 * To get this correct, the entire string must be parsed as a whole,
16069 * reading it as a list element for element and parsing with an
16070 * algorithm equivalent to parse_port_string.
16071 *
16072 * In fact, we use local interface names here, not arbitrary
16073 * hostnames, so in most cases the only name will be "localhost".
16074 *
16075 * So, for now, we use this simple algorithm, that may still return
16076 * a false positive in bizarre cases.
16077 */
16078 int i;
16079 int portslen = (int)strlen(ports);
16080 char prevIsNumber = 0;
16081
16082 for (i = 0; i < portslen; i++) {
16083 if (prevIsNumber) {
16084 int suffixCharIdx = (ports[i] == 'o')
16085 ? (i + 1)
16086 : i; /* allow "os" and "or" suffixes */
16087 if (ports[suffixCharIdx] == 's'
16088 || ports[suffixCharIdx] == 'r') {
16089 return 1;
16090 }
16091 }
16092 if (ports[i] >= '0' && ports[i] <= '9') {
16093 prevIsNumber = 1;
16094 } else {
16095 prevIsNumber = 0;
16096 }
16097 }
16098 }
16099 return 0;
16100}
16101
16102
16103static int
16105{
16106 const char *list;
16107 int on = 1;
16108#if defined(USE_IPV6)
16109 int off = 0;
16110#endif
16111 struct vec vec;
16112 struct socket so, *ptr;
16113
16114 struct mg_pollfd *pfd;
16115 union usa usa;
16116 socklen_t len;
16117 int ip_version;
16118
16119 int portsTotal = 0;
16120 int portsOk = 0;
16121
16122 const char *opt_txt;
16123 long opt_listen_backlog;
16124
16125 if (!phys_ctx) {
16126 return 0;
16127 }
16128
16129 memset(&so, 0, sizeof(so));
16130 memset(&usa, 0, sizeof(usa));
16131 len = sizeof(usa);
16132 list = phys_ctx->dd.config[LISTENING_PORTS];
16133
16134 while ((list = next_option(list, &vec, NULL)) != NULL) {
16135
16136 portsTotal++;
16137
16138 if (!parse_port_string(&vec, &so, &ip_version)) {
16140 phys_ctx,
16141 "%.*s: invalid port spec (entry %i). Expecting list of: %s",
16142 (int)vec.len,
16143 vec.ptr,
16144 portsTotal,
16145 "[IP_ADDRESS:]PORT[s|r]");
16146 continue;
16147 }
16148
16149#if !defined(NO_SSL)
16150 if (so.is_ssl && phys_ctx->dd.ssl_ctx == NULL) {
16151
16152 mg_cry_ctx_internal(phys_ctx,
16153 "Cannot add SSL socket (entry %i)",
16154 portsTotal);
16155 continue;
16156 }
16157#endif
16158 /* Create socket. */
16159 /* For a list of protocol numbers (e.g., TCP==6) see:
16160 * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
16161 */
16162 if ((so.sock =
16163 socket(so.lsa.sa.sa_family,
16165 (ip_version == 99) ? (/* LOCAL */ 0) : (/* TCP */ 6)))
16166 == INVALID_SOCKET) {
16167
16168 mg_cry_ctx_internal(phys_ctx,
16169 "cannot create socket (entry %i)",
16170 portsTotal);
16171 if (so.is_optional) {
16172 portsOk++; /* it's okay if we couldn't create a socket,
16173 this port is optional anyway */
16174 }
16175 continue;
16176 }
16177
16178#if defined(_WIN32)
16179 /* Windows SO_REUSEADDR lets many procs binds to a
16180 * socket, SO_EXCLUSIVEADDRUSE makes the bind fail
16181 * if someone already has the socket -- DTL */
16182 /* NOTE: If SO_EXCLUSIVEADDRUSE is used,
16183 * Windows might need a few seconds before
16184 * the same port can be used again in the
16185 * same process, so a short Sleep may be
16186 * required between mg_stop and mg_start.
16187 */
16188 if (setsockopt(so.sock,
16189 SOL_SOCKET,
16191 (SOCK_OPT_TYPE)&on,
16192 sizeof(on))
16193 != 0) {
16194
16195 /* Set reuse option, but don't abort on errors. */
16197 phys_ctx,
16198 "cannot set socket option SO_EXCLUSIVEADDRUSE (entry %i)",
16199 portsTotal);
16200 }
16201#else
16202 if (setsockopt(so.sock,
16203 SOL_SOCKET,
16205 (SOCK_OPT_TYPE)&on,
16206 sizeof(on))
16207 != 0) {
16208
16209 /* Set reuse option, but don't abort on errors. */
16211 phys_ctx,
16212 "cannot set socket option SO_REUSEADDR (entry %i)",
16213 portsTotal);
16214 }
16215#endif
16216
16217#if defined(USE_X_DOM_SOCKET)
16218 if (ip_version == 99) {
16219 /* Unix domain socket */
16220 } else
16221#endif
16222
16223 if (ip_version > 4) {
16224 /* Could be 6 for IPv6 onlyor 10 (4+6) for IPv4+IPv6 */
16225#if defined(USE_IPV6)
16226 if (ip_version > 6) {
16227 if (so.lsa.sa.sa_family == AF_INET6
16228 && setsockopt(so.sock,
16231 (void *)&off,
16232 sizeof(off))
16233 != 0) {
16234
16235 /* Set IPv6 only option, but don't abort on errors. */
16236 mg_cry_ctx_internal(phys_ctx,
16237 "cannot set socket option "
16238 "IPV6_V6ONLY=off (entry %i)",
16239 portsTotal);
16240 }
16241 } else {
16242 if (so.lsa.sa.sa_family == AF_INET6
16243 && setsockopt(so.sock,
16246 (void *)&on,
16247 sizeof(on))
16248 != 0) {
16249
16250 /* Set IPv6 only option, but don't abort on errors. */
16251 mg_cry_ctx_internal(phys_ctx,
16252 "cannot set socket option "
16253 "IPV6_V6ONLY=on (entry %i)",
16254 portsTotal);
16255 }
16256 }
16257#else
16258 mg_cry_ctx_internal(phys_ctx, "%s", "IPv6 not available");
16259 closesocket(so.sock);
16260 if (so.is_optional) {
16261 portsOk++; /* it's okay if we couldn't set the socket option,
16262 this port is optional anyway */
16263 }
16264 so.sock = INVALID_SOCKET;
16265 continue;
16266#endif
16267 }
16268
16269 if (so.lsa.sa.sa_family == AF_INET) {
16270
16271 len = sizeof(so.lsa.sin);
16272 if (bind(so.sock, &so.lsa.sa, len) != 0) {
16273 mg_cry_ctx_internal(phys_ctx,
16274 "cannot bind to %.*s: %d (%s)",
16275 (int)vec.len,
16276 vec.ptr,
16277 (int)ERRNO,
16278 strerror(errno));
16279 closesocket(so.sock);
16280 so.sock = INVALID_SOCKET;
16281 if (so.is_optional) {
16282 portsOk++; /* it's okay if we couldn't bind, this port is
16283 optional anyway */
16284 }
16285 continue;
16286 }
16287 }
16288#if defined(USE_IPV6)
16289 else if (so.lsa.sa.sa_family == AF_INET6) {
16290
16291 len = sizeof(so.lsa.sin6);
16292 if (bind(so.sock, &so.lsa.sa, len) != 0) {
16293 mg_cry_ctx_internal(phys_ctx,
16294 "cannot bind to IPv6 %.*s: %d (%s)",
16295 (int)vec.len,
16296 vec.ptr,
16297 (int)ERRNO,
16298 strerror(errno));
16299 closesocket(so.sock);
16300 so.sock = INVALID_SOCKET;
16301 if (so.is_optional) {
16302 portsOk++; /* it's okay if we couldn't bind, this port is
16303 optional anyway */
16304 }
16305 continue;
16306 }
16307 }
16308#endif
16309#if defined(USE_X_DOM_SOCKET)
16310 else if (so.lsa.sa.sa_family == AF_UNIX) {
16311
16312 len = sizeof(so.lsa.sun);
16313 if (bind(so.sock, &so.lsa.sa, len) != 0) {
16314 mg_cry_ctx_internal(phys_ctx,
16315 "cannot bind to unix socket %s: %d (%s)",
16316 so.lsa.sun.sun_path,
16317 (int)ERRNO,
16318 strerror(errno));
16319 closesocket(so.sock);
16320 so.sock = INVALID_SOCKET;
16321 if (so.is_optional) {
16322 portsOk++; /* it's okay if we couldn't bind, this port is
16323 optional anyway */
16324 }
16325 continue;
16326 }
16327 }
16328#endif
16329 else {
16331 phys_ctx,
16332 "cannot bind: address family not supported (entry %i)",
16333 portsTotal);
16334 closesocket(so.sock);
16335 so.sock = INVALID_SOCKET;
16336 continue;
16337 }
16338
16339 opt_txt = phys_ctx->dd.config[LISTEN_BACKLOG_SIZE];
16342 mg_cry_ctx_internal(phys_ctx,
16343 "%s value \"%s\" is invalid",
16345 opt_txt);
16346 closesocket(so.sock);
16347 so.sock = INVALID_SOCKET;
16348 continue;
16349 }
16350
16351 if (listen(so.sock, (int)opt_listen_backlog) != 0) {
16352
16353 mg_cry_ctx_internal(phys_ctx,
16354 "cannot listen to %.*s: %d (%s)",
16355 (int)vec.len,
16356 vec.ptr,
16357 (int)ERRNO,
16358 strerror(errno));
16359 closesocket(so.sock);
16360 so.sock = INVALID_SOCKET;
16361 continue;
16362 }
16363
16364 if ((getsockname(so.sock, &(usa.sa), &len) != 0)
16365 || (usa.sa.sa_family != so.lsa.sa.sa_family)) {
16366
16367 int err = (int)ERRNO;
16368 mg_cry_ctx_internal(phys_ctx,
16369 "call to getsockname failed %.*s: %d (%s)",
16370 (int)vec.len,
16371 vec.ptr,
16372 err,
16373 strerror(errno));
16374 closesocket(so.sock);
16375 so.sock = INVALID_SOCKET;
16376 continue;
16377 }
16378
16379 /* Update lsa port in case of random free ports */
16380#if defined(USE_IPV6)
16381 if (so.lsa.sa.sa_family == AF_INET6) {
16382 so.lsa.sin6.sin6_port = usa.sin6.sin6_port;
16383 } else
16384#endif
16385 {
16386 so.lsa.sin.sin_port = usa.sin.sin_port;
16387 }
16388
16389 if ((ptr = (struct socket *)
16391 (phys_ctx->num_listening_sockets + 1)
16392 * sizeof(phys_ctx->listening_sockets[0]),
16393 phys_ctx))
16394 == NULL) {
16395
16396 mg_cry_ctx_internal(phys_ctx, "%s", "Out of memory");
16397 closesocket(so.sock);
16398 so.sock = INVALID_SOCKET;
16399 continue;
16400 }
16401
16402 /* The +2 below includes the original +1 (for the socket we're about to
16403 * add), plus another +1 for the thread_shutdown_notification_socket
16404 * that we'll also want to poll() on so that mg_stop() can return
16405 * quickly
16406 */
16407 if ((pfd = (struct mg_pollfd *)
16409 (phys_ctx->num_listening_sockets + 2)
16410 * sizeof(phys_ctx->listening_socket_fds[0]),
16411 phys_ctx))
16412 == NULL) {
16413
16414 mg_cry_ctx_internal(phys_ctx, "%s", "Out of memory");
16415 closesocket(so.sock);
16416 so.sock = INVALID_SOCKET;
16417 mg_free(ptr);
16418 continue;
16419 }
16420
16421 set_close_on_exec(so.sock, NULL, phys_ctx);
16422 phys_ctx->listening_sockets = ptr;
16423 phys_ctx->listening_sockets[phys_ctx->num_listening_sockets] = so;
16424 phys_ctx->listening_socket_fds = pfd;
16425 phys_ctx->num_listening_sockets++;
16426 portsOk++;
16427 }
16428
16429 if (portsOk != portsTotal) {
16431 portsOk = 0;
16432 }
16433
16434 return portsOk;
16435}
16436
16437
16438static const char *
16439header_val(const struct mg_connection *conn, const char *header)
16440{
16441 const char *header_value;
16442
16443 if ((header_value = mg_get_header(conn, header)) == NULL) {
16444 return "-";
16445 } else {
16446 return header_value;
16447 }
16448}
16449
16450
16451#if defined(MG_EXTERNAL_FUNCTION_log_access)
16452#include "external_log_access.inl"
16453#elif !defined(NO_FILESYSTEMS)
16454
16455static void
16456log_access(const struct mg_connection *conn)
16457{
16458 const struct mg_request_info *ri;
16459 struct mg_file fi;
16460 char date[64], src_addr[IP_ADDR_STR_LEN];
16461#if defined(REENTRANT_TIME)
16462 struct tm _tm;
16463 struct tm *tm = &_tm;
16464#else
16465 struct tm *tm;
16466#endif
16467
16468 const char *referer;
16469 const char *user_agent;
16470
16471 char log_buf[4096];
16472
16473 if (!conn || !conn->dom_ctx) {
16474 return;
16475 }
16476
16477 /* Set log message to "empty" */
16478 log_buf[0] = 0;
16479
16480#if defined(USE_LUA)
16481 if (conn->phys_ctx->lua_bg_log_available) {
16482 int ret;
16483 struct mg_context *ctx = conn->phys_ctx;
16484 lua_State *lstate = (lua_State *)ctx->lua_background_state;
16485 pthread_mutex_lock(&ctx->lua_bg_mutex);
16486 /* call "log()" in Lua */
16487 lua_getglobal(lstate, "log");
16490
16491 ret = lua_pcall(lstate, /* args */ 2, /* results */ 1, 0);
16492 if (ret == 0) {
16493 int t = lua_type(lstate, -1);
16494 if (t == LUA_TBOOLEAN) {
16495 if (lua_toboolean(lstate, -1) == 0) {
16496 /* log() returned false: do not log */
16497 pthread_mutex_unlock(&ctx->lua_bg_mutex);
16498 return;
16499 }
16500 /* log returned true: continue logging */
16501 } else if (t == LUA_TSTRING) {
16502 size_t len;
16503 const char *txt = lua_tolstring(lstate, -1, &len);
16504 if ((len == 0) || (*txt == 0)) {
16505 /* log() returned empty string: do not log */
16506 pthread_mutex_unlock(&ctx->lua_bg_mutex);
16507 return;
16508 }
16509 /* Copy test from Lua into log_buf */
16510 if (len >= sizeof(log_buf)) {
16511 len = sizeof(log_buf) - 1;
16512 }
16513 memcpy(log_buf, txt, len);
16514 log_buf[len] = 0;
16515 }
16516 } else {
16517 lua_cry(conn, ret, lstate, "lua_background_script", "log");
16518 }
16519 pthread_mutex_unlock(&ctx->lua_bg_mutex);
16520 }
16521#endif
16522
16523 if (conn->dom_ctx->config[ACCESS_LOG_FILE] != NULL) {
16524 if (mg_fopen(conn,
16525 conn->dom_ctx->config[ACCESS_LOG_FILE],
16527 &fi)
16528 == 0) {
16529 fi.access.fp = NULL;
16530 }
16531 } else {
16532 fi.access.fp = NULL;
16533 }
16534
16535 /* Log is written to a file and/or a callback. If both are not set,
16536 * executing the rest of the function is pointless. */
16537 if ((fi.access.fp == NULL)
16538 && (conn->phys_ctx->callbacks.log_access == NULL)) {
16539 return;
16540 }
16541
16542 /* If we did not get a log message from Lua, create it here. */
16543 if (!log_buf[0]) {
16544#if defined(REENTRANT_TIME)
16546#else
16547 tm = localtime(&conn->conn_birth_time);
16548#endif
16549 if (tm != NULL) {
16550 strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", tm);
16551 } else {
16552 mg_strlcpy(date, "01/Jan/1970:00:00:00 +0000", sizeof(date));
16553 }
16554
16555 ri = &conn->request_info;
16556
16557 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
16558 referer = header_val(conn, "Referer");
16559 user_agent = header_val(conn, "User-Agent");
16560
16561 mg_snprintf(conn,
16562 NULL, /* Ignore truncation in access log */
16563 log_buf,
16564 sizeof(log_buf),
16565 "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d %" INT64_FMT
16566 " %s %s",
16567 src_addr,
16568 (ri->remote_user == NULL) ? "-" : ri->remote_user,
16569 date,
16570 ri->request_method ? ri->request_method : "-",
16571 ri->request_uri ? ri->request_uri : "-",
16572 ri->query_string ? "?" : "",
16573 ri->query_string ? ri->query_string : "",
16574 ri->http_version,
16575 conn->status_code,
16576 conn->num_bytes_sent,
16577 referer,
16578 user_agent);
16579 }
16580
16581 /* Here we have a log message in log_buf. Call the callback */
16582 if (conn->phys_ctx->callbacks.log_access) {
16583 if (conn->phys_ctx->callbacks.log_access(conn, log_buf)) {
16584 /* do not log if callback returns non-zero */
16585 if (fi.access.fp) {
16586 mg_fclose(&fi.access);
16587 }
16588 return;
16589 }
16590 }
16591
16592 /* Store in file */
16593 if (fi.access.fp) {
16594 int ok = 1;
16595 flockfile(fi.access.fp);
16596 if (fprintf(fi.access.fp, "%s\n", log_buf) < 1) {
16597 ok = 0;
16598 }
16599 if (fflush(fi.access.fp) != 0) {
16600 ok = 0;
16601 }
16602 funlockfile(fi.access.fp);
16603 if (mg_fclose(&fi.access) != 0) {
16604 ok = 0;
16605 }
16606 if (!ok) {
16607 mg_cry_internal(conn,
16608 "Error writing log file %s",
16609 conn->dom_ctx->config[ACCESS_LOG_FILE]);
16610 }
16611 }
16612}
16613#else
16614#error "Either enable filesystems or provide a custom log_access implementation"
16615#endif /* Externally provided function */
16616
16617
16618/* Verify given socket address against the ACL.
16619 * Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
16620 */
16621static int
16622check_acl(struct mg_context *phys_ctx, const union usa *sa)
16623{
16624 int allowed, flag, matched;
16625 struct vec vec;
16626
16627 if (phys_ctx) {
16628 const char *list = phys_ctx->dd.config[ACCESS_CONTROL_LIST];
16629
16630 /* If any ACL is set, deny by default */
16631 allowed = (list == NULL) ? '+' : '-';
16632
16633 while ((list = next_option(list, &vec, NULL)) != NULL) {
16634 flag = vec.ptr[0];
16635 matched = -1;
16636 if ((vec.len > 0) && ((flag == '+') || (flag == '-'))) {
16637 vec.ptr++;
16638 vec.len--;
16639 matched = parse_match_net(&vec, sa, 1);
16640 }
16641 if (matched < 0) {
16642 mg_cry_ctx_internal(phys_ctx,
16643 "%s: subnet must be [+|-]IP-addr[/x]",
16644 __func__);
16645 return -1;
16646 }
16647 if (matched) {
16648 allowed = flag;
16649 }
16650 }
16651
16652 return allowed == '+';
16653 }
16654 return -1;
16655}
16656
16657
16658#if !defined(_WIN32) && !defined(__ZEPHYR__)
16659static int
16661{
16662 int success = 0;
16663
16664 if (phys_ctx) {
16665 /* We are currently running as curr_uid. */
16666 const uid_t curr_uid = getuid();
16667 /* If set, we want to run as run_as_user. */
16668 const char *run_as_user = phys_ctx->dd.config[RUN_AS_USER];
16669 const struct passwd *to_pw = NULL;
16670
16671 if ((run_as_user != NULL) && (to_pw = getpwnam(run_as_user)) == NULL) {
16672 /* run_as_user does not exist on the system. We can't proceed
16673 * further. */
16674 mg_cry_ctx_internal(phys_ctx,
16675 "%s: unknown user [%s]",
16676 __func__,
16677 run_as_user);
16678 } else if ((run_as_user == NULL) || (curr_uid == to_pw->pw_uid)) {
16679 /* There was either no request to change user, or we're already
16680 * running as run_as_user. Nothing else to do.
16681 */
16682 success = 1;
16683 } else {
16684 /* Valid change request. */
16685 if (setgid(to_pw->pw_gid) == -1) {
16686 mg_cry_ctx_internal(phys_ctx,
16687 "%s: setgid(%s): %s",
16688 __func__,
16690 strerror(errno));
16691 } else if (setgroups(0, NULL) == -1) {
16692 mg_cry_ctx_internal(phys_ctx,
16693 "%s: setgroups(): %s",
16694 __func__,
16695 strerror(errno));
16696 } else if (setuid(to_pw->pw_uid) == -1) {
16697 mg_cry_ctx_internal(phys_ctx,
16698 "%s: setuid(%s): %s",
16699 __func__,
16701 strerror(errno));
16702 } else {
16703 success = 1;
16704 }
16705 }
16706 }
16707
16708 return success;
16709}
16710#endif /* !_WIN32 */
16711
16712
16713static void
16714tls_dtor(void *key)
16715{
16716 struct mg_workerTLS *tls = (struct mg_workerTLS *)key;
16717 /* key == pthread_getspecific(sTlsKey); */
16718
16719 if (tls) {
16720 if (tls->is_master == 2) {
16721 tls->is_master = -3; /* Mark memory as dead */
16722 mg_free(tls);
16723 }
16724 }
16726}
16727
16728
16729#if defined(USE_MBEDTLS)
16730/* Check if SSL is required.
16731 * If so, set up ctx->ssl_ctx pointer. */
16732static int
16733mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
16734{
16735 if (!phys_ctx) {
16736 return 0;
16737 }
16738
16739 if (!dom_ctx) {
16740 dom_ctx = &(phys_ctx->dd);
16741 }
16742
16743 if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
16744 /* No SSL port is set. No need to setup SSL. */
16745 return 1;
16746 }
16747
16748 dom_ctx->ssl_ctx = (SSL_CTX *)mg_calloc(1, sizeof(*dom_ctx->ssl_ctx));
16749 if (dom_ctx->ssl_ctx == NULL) {
16750 fprintf(stderr, "ssl_ctx malloc failed\n");
16751 return 0;
16752 }
16753
16754 return mbed_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE])
16755 == 0
16756 ? 1
16757 : 0;
16758}
16759
16760#elif defined(USE_GNUTLS)
16761/* Check if SSL is required.
16762 * If so, set up ctx->ssl_ctx pointer. */
16763static int
16764mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
16765{
16766 if (!phys_ctx) {
16767 return 0;
16768 }
16769
16770 if (!dom_ctx) {
16771 dom_ctx = &(phys_ctx->dd);
16772 }
16773
16774 if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
16775 /* No SSL port is set. No need to setup SSL. */
16776 return 1;
16777 }
16778
16779 dom_ctx->ssl_ctx = (SSL_CTX *)mg_calloc(1, sizeof(*dom_ctx->ssl_ctx));
16780 if (dom_ctx->ssl_ctx == NULL) {
16781 fprintf(stderr, "ssl_ctx malloc failed\n");
16782 return 0;
16783 }
16784
16785 return gtls_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE])
16786 == 0
16787 ? 1
16788 : 0;
16789}
16790
16791#elif !defined(NO_SSL)
16792
16793static int ssl_use_pem_file(struct mg_context *phys_ctx,
16794 struct mg_domain_context *dom_ctx,
16795 const char *pem,
16796 const char *chain);
16797static const char *ssl_error(void);
16798
16799
16800static int
16802{
16803 struct stat cert_buf;
16804 int64_t t = 0;
16805 const char *pem;
16806 const char *chain;
16808
16809 if ((pem = conn->dom_ctx->config[SSL_CERTIFICATE]) == NULL) {
16810 /* If pem is NULL and conn->phys_ctx->callbacks.init_ssl is not,
16811 * refresh_trust still can not work. */
16812 return 0;
16813 }
16814 chain = conn->dom_ctx->config[SSL_CERTIFICATE_CHAIN];
16815 if (chain == NULL) {
16816 /* pem is not NULL here */
16817 chain = pem;
16818 }
16819 if (*chain == 0) {
16820 chain = NULL;
16821 }
16822
16823 if (stat(pem, &cert_buf) != -1) {
16824 t = (int64_t)cert_buf.st_mtime;
16825 }
16826
16828 if ((t != 0) && (conn->dom_ctx->ssl_cert_last_mtime != t)) {
16829 conn->dom_ctx->ssl_cert_last_mtime = t;
16830
16832 if (conn->dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) {
16833 if (mg_strcasecmp(conn->dom_ctx->config[SSL_DO_VERIFY_PEER], "yes")
16834 == 0) {
16836 } else if (mg_strcasecmp(conn->dom_ctx->config[SSL_DO_VERIFY_PEER],
16837 "optional")
16838 == 0) {
16840 }
16841 }
16842
16843 if (should_verify_peer) {
16844 char *ca_path = conn->dom_ctx->config[SSL_CA_PATH];
16845 char *ca_file = conn->dom_ctx->config[SSL_CA_FILE];
16846 if (SSL_CTX_load_verify_locations(conn->dom_ctx->ssl_ctx,
16847 ca_file,
16848 ca_path)
16849 != 1) {
16852 conn->phys_ctx,
16853 "SSL_CTX_load_verify_locations error: %s "
16854 "ssl_verify_peer requires setting "
16855 "either ssl_ca_path or ssl_ca_file. Is any of them "
16856 "present in "
16857 "the .conf file?",
16858 ssl_error());
16859 return 0;
16860 }
16861 }
16862
16863 if (ssl_use_pem_file(conn->phys_ctx, conn->dom_ctx, pem, chain) == 0) {
16865 return 0;
16866 }
16867 }
16869
16870 return 1;
16871}
16872
16873#if defined(OPENSSL_API_1_1)
16874#else
16875static pthread_mutex_t *ssl_mutexes;
16876#endif /* OPENSSL_API_1_1 */
16877
16878static int
16880 int (*func)(SSL *),
16881 const struct mg_client_options *client_options)
16882{
16883 int ret, err;
16884 int short_trust;
16885 unsigned timeout = 1024;
16886 unsigned i;
16887
16888 if (!conn) {
16889 return 0;
16890 }
16891
16892 short_trust =
16893 (conn->dom_ctx->config[SSL_SHORT_TRUST] != NULL)
16894 && (mg_strcasecmp(conn->dom_ctx->config[SSL_SHORT_TRUST], "yes") == 0);
16895
16896 if (short_trust) {
16897 int trust_ret = refresh_trust(conn);
16898 if (!trust_ret) {
16899 return trust_ret;
16900 }
16901 }
16902
16904 conn->ssl = SSL_new(conn->dom_ctx->ssl_ctx);
16906 if (conn->ssl == NULL) {
16907 mg_cry_internal(conn, "sslize error: %s", ssl_error());
16909 return 0;
16910 }
16911 SSL_set_app_data(conn->ssl, (char *)conn);
16912
16913 ret = SSL_set_fd(conn->ssl, conn->client.sock);
16914 if (ret != 1) {
16915 mg_cry_internal(conn, "sslize error: %s", ssl_error());
16916 SSL_free(conn->ssl);
16917 conn->ssl = NULL;
16919 return 0;
16920 }
16921
16922 if (client_options) {
16923 if (client_options->host_name) {
16924 SSL_set_tlsext_host_name(conn->ssl, client_options->host_name);
16925 }
16926 }
16927
16928 /* Reuse the request timeout for the SSL_Accept/SSL_connect timeout */
16929 if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
16930 /* NOTE: The loop below acts as a back-off, so we can end
16931 * up sleeping for more (or less) than the REQUEST_TIMEOUT. */
16932 int to = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]);
16933 if (to >= 0) {
16934 timeout = (unsigned)to;
16935 }
16936 }
16937
16938 /* SSL functions may fail and require to be called again:
16939 * see https://www.openssl.org/docs/manmaster/ssl/SSL_get_error.html
16940 * Here "func" could be SSL_connect or SSL_accept. */
16941 for (i = 0; i <= timeout; i += 50) {
16943 /* conn->dom_ctx may be changed here (see ssl_servername_callback) */
16944 ret = func(conn->ssl);
16945 if (ret != 1) {
16946 err = SSL_get_error(conn->ssl, ret);
16947 if ((err == SSL_ERROR_WANT_CONNECT)
16948 || (err == SSL_ERROR_WANT_ACCEPT)
16949 || (err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)
16950 || (err == SSL_ERROR_WANT_X509_LOOKUP)) {
16951 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
16952 /* Don't wait if the server is going to be stopped. */
16953 break;
16954 }
16955 if (err == SSL_ERROR_WANT_X509_LOOKUP) {
16956 /* Simply retry the function call. */
16957 mg_sleep(50);
16958 } else {
16959 /* Need to retry the function call "later".
16960 * See https://linux.die.net/man/3/ssl_get_error
16961 * This is typical for non-blocking sockets. */
16962 struct mg_pollfd pfd[2];
16963 int pollres;
16964 unsigned int num_sock = 1;
16965 pfd[0].fd = conn->client.sock;
16966 pfd[0].events = ((err == SSL_ERROR_WANT_CONNECT)
16967 || (err == SSL_ERROR_WANT_WRITE))
16968 ? POLLOUT
16969 : POLLIN;
16970
16971 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
16972 pfd[num_sock].fd =
16973 conn->phys_ctx->thread_shutdown_notification_socket;
16974 pfd[num_sock].events = POLLIN;
16975 num_sock++;
16976 }
16977
16979 num_sock,
16980 50,
16981 &(conn->phys_ctx->stop_flag));
16982 if (pollres < 0) {
16983 /* Break if error occurred (-1)
16984 * or server shutdown (-2) */
16985 break;
16986 }
16987 }
16988
16989 } else if (err == SSL_ERROR_SYSCALL) {
16990 /* This is an IO error. Look at errno. */
16991 mg_cry_internal(conn, "SSL syscall error %i", ERRNO);
16992 break;
16993
16994 } else {
16995 /* This is an SSL specific error, e.g. SSL_ERROR_SSL */
16996 mg_cry_internal(conn, "sslize error: %s", ssl_error());
16997 break;
16998 }
16999
17000 } else {
17001 /* success */
17002 break;
17003 }
17004 }
17006
17007 if (ret != 1) {
17008 SSL_free(conn->ssl);
17009 conn->ssl = NULL;
17011 return 0;
17012 }
17013
17014 return 1;
17015}
17016
17017
17018/* Return OpenSSL error message (from CRYPTO lib) */
17019static const char *
17021{
17022 unsigned long err;
17023 err = ERR_get_error();
17024 return ((err == 0) ? "" : ERR_error_string(err, NULL));
17025}
17026
17027
17028static int
17029hexdump2string(void *mem, int memlen, char *buf, int buflen)
17030{
17031 int i;
17032 const char hexdigit[] = "0123456789abcdef";
17033
17034 if ((memlen <= 0) || (buflen <= 0)) {
17035 return 0;
17036 }
17037 if (buflen < (3 * memlen)) {
17038 return 0;
17039 }
17040
17041 for (i = 0; i < memlen; i++) {
17042 if (i > 0) {
17043 buf[3 * i - 1] = ' ';
17044 }
17045 buf[3 * i] = hexdigit[(((uint8_t *)mem)[i] >> 4) & 0xF];
17046 buf[3 * i + 1] = hexdigit[((uint8_t *)mem)[i] & 0xF];
17047 }
17048 buf[3 * memlen - 1] = 0;
17049
17050 return 1;
17051}
17052
17053
17054static int
17056 struct mg_client_cert *client_cert)
17057{
17059 if (cert) {
17060 char str_buf[1024];
17061 unsigned char buf[256];
17062 char *str_serial = NULL;
17063 unsigned int ulen;
17064 int ilen;
17065 unsigned char *tmp_buf;
17066 unsigned char *tmp_p;
17067
17068 /* Handle to algorithm used for fingerprint */
17069 const EVP_MD *digest = EVP_get_digestbyname("sha1");
17070
17071 /* Get Subject and issuer */
17074
17075 /* Get serial number */
17077
17078 /* Translate serial number to a hex string */
17080 if (serial_bn) {
17083 }
17084 client_cert->serial =
17086
17087 /* Translate subject and issuer to a string */
17088 (void)X509_NAME_oneline(subj, str_buf, (int)sizeof(str_buf));
17089 client_cert->subject = mg_strdup_ctx(str_buf, conn->phys_ctx);
17090 (void)X509_NAME_oneline(iss, str_buf, (int)sizeof(str_buf));
17091 client_cert->issuer = mg_strdup_ctx(str_buf, conn->phys_ctx);
17092
17093 /* Calculate SHA1 fingerprint and store as a hex string */
17094 ulen = 0;
17095
17096 /* ASN1_digest is deprecated. Do the calculation manually,
17097 * using EVP_Digest. */
17098 ilen = i2d_X509(cert, NULL);
17099 tmp_buf = (ilen > 0)
17100 ? (unsigned char *)mg_malloc_ctx((unsigned)ilen + 1,
17101 conn->phys_ctx)
17102 : NULL;
17103 if (tmp_buf) {
17104 tmp_p = tmp_buf;
17105 (void)i2d_X509(cert, &tmp_p);
17106 if (!EVP_Digest(
17107 tmp_buf, (unsigned)ilen, buf, &ulen, digest, NULL)) {
17108 ulen = 0;
17109 }
17111 }
17112
17113 if (!hexdump2string(buf, (int)ulen, str_buf, (int)sizeof(str_buf))) {
17114 *str_buf = 0;
17115 }
17116 client_cert->finger = mg_strdup_ctx(str_buf, conn->phys_ctx);
17117
17118 client_cert->peer_cert = (void *)cert;
17119
17120 /* Strings returned from bn_bn2hex must be freed using OPENSSL_free,
17121 * see https://linux.die.net/man/3/bn_bn2hex */
17123 return 1;
17124 }
17125 return 0;
17126}
17127
17128
17129#if defined(OPENSSL_API_1_1)
17130#else
17131static void
17132ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
17133{
17134 (void)line;
17135 (void)file;
17136
17137 if (mode & 1) {
17138 /* 1 is CRYPTO_LOCK */
17140 } else {
17142 }
17143}
17144#endif /* OPENSSL_API_1_1 */
17145
17146
17147#if !defined(NO_SSL_DL)
17148/* Load a DLL/Shared Object with a TLS/SSL implementation. */
17149static void *
17151 size_t ebuf_len,
17152 const char *dll_name,
17153 struct ssl_func *sw,
17154 int *feature_missing)
17155{
17156 union {
17157 void *p;
17158 void (*fp)(void);
17159 } u;
17160 void *dll_handle;
17161 struct ssl_func *fp;
17162 int ok;
17163 int truncated = 0;
17164
17165 if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
17167 NULL, /* No truncation check for ebuf */
17168 ebuf,
17169 ebuf_len,
17170 "%s: cannot load %s",
17171 __func__,
17172 dll_name);
17173 return NULL;
17174 }
17175
17176 ok = 1;
17177 for (fp = sw; fp->name != NULL; fp++) {
17178#if defined(_WIN32)
17179 /* GetProcAddress() returns pointer to function */
17180 u.fp = (void (*)(void))dlsym(dll_handle, fp->name);
17181#else
17182 /* dlsym() on UNIX returns void *. ISO C forbids casts of data
17183 * pointers to function pointers. We need to use a union to make a
17184 * cast. */
17185 u.p = dlsym(dll_handle, fp->name);
17186#endif /* _WIN32 */
17187
17188 /* Set pointer (might be NULL) */
17189 fp->ptr = u.fp;
17190
17191 if (u.fp == NULL) {
17192 DEBUG_TRACE("Missing function: %s\n", fp->name);
17193 if (feature_missing) {
17195 }
17196 if (fp->required == TLS_Mandatory) {
17197 /* Mandatory function is missing */
17198 if (ok) {
17199 /* This is the first missing function.
17200 * Create a new error message. */
17202 &truncated,
17203 ebuf,
17204 ebuf_len,
17205 "%s: %s: cannot find %s",
17206 __func__,
17207 dll_name,
17208 fp->name);
17209 ok = 0;
17210 } else {
17211 /* This is yet anothermissing function.
17212 * Append existing error message. */
17213 size_t cur_len = strlen(ebuf);
17214 if (!truncated && ((ebuf_len - cur_len) > 3)) {
17216 &truncated,
17217 ebuf + cur_len,
17218 ebuf_len - cur_len - 3,
17219 ", %s",
17220 fp->name);
17221 if (truncated) {
17222 /* If truncated, add "..." */
17223 strcat(ebuf, "...");
17224 }
17225 }
17226 }
17227 }
17228 }
17229 }
17230
17231 if (!ok) {
17232 (void)dlclose(dll_handle);
17233 return NULL;
17234 }
17235
17236 return dll_handle;
17237}
17238
17239
17240static void *ssllib_dll_handle; /* Store the ssl library handle. */
17241static void *cryptolib_dll_handle; /* Store the crypto library handle. */
17242
17243#endif /* NO_SSL_DL */
17244
17245
17246#if defined(SSL_ALREADY_INITIALIZED)
17247static volatile ptrdiff_t cryptolib_users =
17248 1; /* Reference counter for crypto library. */
17249#else
17250static volatile ptrdiff_t cryptolib_users =
17251 0; /* Reference counter for crypto library. */
17252#endif
17253
17254
17255static int
17257{
17258#if !defined(OPENSSL_API_1_1) && !defined(OPENSSL_API_3_0)
17259 int i, num_locks;
17260 size_t size;
17261#endif
17262
17263 if (ebuf_len > 0) {
17264 ebuf[0] = 0;
17265 }
17266
17267#if !defined(NO_SSL_DL)
17268 if (!cryptolib_dll_handle) {
17272 if (!cryptolib_dll_handle) {
17274 NULL, /* No truncation check for ebuf */
17275 ebuf,
17276 ebuf_len,
17277 "%s: error loading library %s",
17278 __func__,
17279 CRYPTO_LIB);
17280 DEBUG_TRACE("%s", ebuf);
17281 return 0;
17282 }
17283 }
17284#endif /* NO_SSL_DL */
17285
17286 if (mg_atomic_inc(&cryptolib_users) > 1) {
17287 return 1;
17288 }
17289
17290#if !defined(OPENSSL_API_1_1) && !defined(OPENSSL_API_3_0)
17291 /* Initialize locking callbacks, needed for thread safety.
17292 * http://www.openssl.org/support/faq.html#PROG1
17293 */
17295 if (num_locks < 0) {
17296 num_locks = 0;
17297 }
17298 size = sizeof(pthread_mutex_t) * ((size_t)(num_locks));
17299
17300 /* allocate mutex array, if required */
17301 if (num_locks == 0) {
17302 /* No mutex array required */
17303 ssl_mutexes = NULL;
17304 } else {
17305 /* Mutex array required - allocate it */
17306 ssl_mutexes = (pthread_mutex_t *)mg_malloc(size);
17307
17308 /* Check OOM */
17309 if (ssl_mutexes == NULL) {
17311 NULL, /* No truncation check for ebuf */
17312 ebuf,
17313 ebuf_len,
17314 "%s: cannot allocate mutexes: %s",
17315 __func__,
17316 ssl_error());
17317 DEBUG_TRACE("%s", ebuf);
17318 return 0;
17319 }
17320
17321 /* initialize mutex array */
17322 for (i = 0; i < num_locks; i++) {
17325 NULL, /* No truncation check for ebuf */
17326 ebuf,
17327 ebuf_len,
17328 "%s: error initializing mutex %i of %i",
17329 __func__,
17330 i,
17331 num_locks);
17332 DEBUG_TRACE("%s", ebuf);
17334 return 0;
17335 }
17336 }
17337 }
17338
17341#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
17342
17343#if !defined(NO_SSL_DL)
17344 if (!ssllib_dll_handle) {
17347 if (!ssllib_dll_handle) {
17348#if !defined(OPENSSL_API_1_1)
17350#endif
17351 DEBUG_TRACE("%s", ebuf);
17352 return 0;
17353 }
17354 }
17355#endif /* NO_SSL_DL */
17356
17357#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \
17358 && !defined(NO_SSL_DL)
17359 /* Initialize SSL library */
17363 NULL);
17364#else
17365 /* Initialize SSL library */
17368#endif
17369
17370 return 1;
17371}
17372
17373
17374static int
17376 struct mg_domain_context *dom_ctx,
17377 const char *pem,
17378 const char *chain)
17379{
17380 if (SSL_CTX_use_certificate_file(dom_ctx->ssl_ctx, pem, 1) == 0) {
17381 mg_cry_ctx_internal(phys_ctx,
17382 "%s: cannot open certificate file %s: %s",
17383 __func__,
17384 pem,
17385 ssl_error());
17386 return 0;
17387 }
17388
17389 /* could use SSL_CTX_set_default_passwd_cb_userdata */
17390 if (SSL_CTX_use_PrivateKey_file(dom_ctx->ssl_ctx, pem, 1) == 0) {
17391 mg_cry_ctx_internal(phys_ctx,
17392 "%s: cannot open private key file %s: %s",
17393 __func__,
17394 pem,
17395 ssl_error());
17396 return 0;
17397 }
17398
17399 if (SSL_CTX_check_private_key(dom_ctx->ssl_ctx) == 0) {
17400 mg_cry_ctx_internal(phys_ctx,
17401 "%s: certificate and private key do not match: %s",
17402 __func__,
17403 pem);
17404 return 0;
17405 }
17406
17407 /* In contrast to OpenSSL, wolfSSL does not support certificate
17408 * chain files that contain private keys and certificates in
17409 * SSL_CTX_use_certificate_chain_file.
17410 * The CivetWeb-Server used pem-Files that contained both information.
17411 * In order to make wolfSSL work, it is split in two files.
17412 * One file that contains key and certificate used by the server and
17413 * an optional chain file for the ssl stack.
17414 */
17415 if (chain) {
17416 if (SSL_CTX_use_certificate_chain_file(dom_ctx->ssl_ctx, chain) == 0) {
17417 mg_cry_ctx_internal(phys_ctx,
17418 "%s: cannot use certificate chain file %s: %s",
17419 __func__,
17420 chain,
17421 ssl_error());
17422 return 0;
17423 }
17424 }
17425 return 1;
17426}
17427
17428
17429#if defined(OPENSSL_API_1_1)
17430static unsigned long
17432{
17433 long unsigned ret = (long unsigned)SSL_OP_ALL;
17434 if (version_id > 0)
17436 if (version_id > 1)
17438 if (version_id > 2)
17440 if (version_id > 3)
17442 if (version_id > 4)
17444#if defined(SSL_OP_NO_TLSv1_3)
17445 if (version_id > 5)
17447#endif
17448 return ret;
17449}
17450#else
17451static long
17453{
17454 unsigned long ret = (unsigned long)SSL_OP_ALL;
17455 if (version_id > 0)
17457 if (version_id > 1)
17459 if (version_id > 2)
17461 if (version_id > 3)
17463 if (version_id > 4)
17465#if defined(SSL_OP_NO_TLSv1_3)
17466 if (version_id > 5)
17468#endif
17469 return (long)ret;
17470}
17471#endif /* OPENSSL_API_1_1 */
17472
17473
17474/* SSL callback documentation:
17475 * https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_info_callback.html
17476 * https://wiki.openssl.org/index.php/Manual:SSL_CTX_set_info_callback(3)
17477 * https://linux.die.net/man/3/ssl_set_info_callback */
17478/* Note: There is no "const" for the first argument in the documentation
17479 * examples, however some (maybe most, but not all) headers of OpenSSL
17480 * versions / OpenSSL compatibility layers have it. Having a different
17481 * definition will cause a warning in C and an error in C++. Use "const SSL
17482 * *", while automatic conversion from "SSL *" works for all compilers,
17483 * but not other way around */
17484static void
17485ssl_info_callback(const SSL *ssl, int what, int ret)
17486{
17487 (void)ret;
17488
17490 SSL_get_app_data(ssl);
17491 }
17493 /* TODO: check for openSSL 1.1 */
17494 //#define SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS 0x0001
17495 // ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
17496 }
17497}
17498
17499
17500static int
17501ssl_servername_callback(SSL *ssl, int *ad, void *arg)
17502{
17503#if defined(GCC_DIAGNOSTIC)
17504#pragma GCC diagnostic push
17505#pragma GCC diagnostic ignored "-Wcast-align"
17506#endif /* defined(GCC_DIAGNOSTIC) */
17507
17508 /* We used an aligned pointer in SSL_set_app_data */
17509 struct mg_connection *conn = (struct mg_connection *)SSL_get_app_data(ssl);
17510
17511#if defined(GCC_DIAGNOSTIC)
17512#pragma GCC diagnostic pop
17513#endif /* defined(GCC_DIAGNOSTIC) */
17514
17516
17517 (void)ad;
17518 (void)arg;
17519
17520 if ((conn == NULL) || (conn->phys_ctx == NULL)) {
17521 DEBUG_ASSERT(0);
17522 return SSL_TLSEXT_ERR_NOACK;
17523 }
17524 conn->dom_ctx = &(conn->phys_ctx->dd);
17525
17526 /* Old clients (Win XP) will not support SNI. Then, there
17527 * is no server name available in the request - we can
17528 * only work with the default certificate.
17529 * Multiple HTTPS hosts on one IP+port are only possible
17530 * with a certificate containing all alternative names.
17531 */
17532 if ((servername == NULL) || (*servername == 0)) {
17533 DEBUG_TRACE("%s", "SSL connection not supporting SNI");
17535 SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx);
17537 return SSL_TLSEXT_ERR_NOACK;
17538 }
17539
17540 DEBUG_TRACE("TLS connection to host %s", servername);
17541
17542 while (conn->dom_ctx) {
17544 conn->dom_ctx->config[AUTHENTICATION_DOMAIN])) {
17545 /* Found matching domain */
17546 DEBUG_TRACE("TLS domain %s found",
17547 conn->dom_ctx->config[AUTHENTICATION_DOMAIN]);
17548 break;
17549 }
17551 conn->dom_ctx = conn->dom_ctx->next;
17553 }
17554
17555 if (conn->dom_ctx == NULL) {
17556 /* Default domain */
17557 DEBUG_TRACE("TLS default domain %s used",
17558 conn->phys_ctx->dd.config[AUTHENTICATION_DOMAIN]);
17559 conn->dom_ctx = &(conn->phys_ctx->dd);
17560 }
17562 SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx);
17564 return SSL_TLSEXT_ERR_OK;
17565}
17566
17567
17568#if defined(USE_ALPN)
17569static const char alpn_proto_list[] = "\x02h2\x08http/1.1\x08http/1.0";
17570static const char *alpn_proto_order_http1[] = {alpn_proto_list + 3,
17571 alpn_proto_list + 3 + 8,
17572 NULL};
17573#if defined(USE_HTTP2)
17574static const char *alpn_proto_order_http2[] = {alpn_proto_list,
17575 alpn_proto_list + 3,
17576 alpn_proto_list + 3 + 8,
17577 NULL};
17578#endif
17579
17580static int
17582 const unsigned char **out,
17583 unsigned char *outlen,
17584 const unsigned char *in,
17585 unsigned int inlen,
17586 void *arg)
17587{
17588 struct mg_domain_context *dom_ctx = (struct mg_domain_context *)arg;
17589 unsigned int i, j, enable_http2 = 0;
17591
17592 struct mg_workerTLS *tls =
17594
17595 (void)ssl;
17596
17597 if (tls == NULL) {
17598 /* Need to store protocol in Thread Local Storage */
17599 /* If there is no Thread Local Storage, don't use ALPN */
17600 return SSL_TLSEXT_ERR_NOACK;
17601 }
17602
17603#if defined(USE_HTTP2)
17604 enable_http2 = (0 == strcmp(dom_ctx->config[ENABLE_HTTP2], "yes"));
17605 if (enable_http2) {
17607 }
17608#endif
17609
17610 for (j = 0; alpn_proto_order[j] != NULL; j++) {
17611 /* check all accepted protocols in this order */
17612 const char *alpn_proto = alpn_proto_order[j];
17613 /* search input for matching protocol */
17614 for (i = 0; i < inlen; i++) {
17615 if (!memcmp(in + i, alpn_proto, (unsigned char)alpn_proto[0])) {
17616 *out = in + i + 1;
17617 *outlen = in[i];
17618 tls->alpn_proto = alpn_proto;
17619 return SSL_TLSEXT_ERR_OK;
17620 }
17621 }
17622 }
17623
17624 /* Nothing found */
17625 return SSL_TLSEXT_ERR_NOACK;
17626}
17627
17628
17629static int
17631 const unsigned char **data,
17632 unsigned int *len,
17633 void *arg)
17634{
17635 struct mg_domain_context *dom_ctx = (struct mg_domain_context *)arg;
17636 *data = (const unsigned char *)alpn_proto_list;
17637 *len = (unsigned int)strlen((const char *)data);
17638
17639 (void)ssl;
17640 (void)dom_ctx;
17641
17642 return SSL_TLSEXT_ERR_OK;
17643}
17644
17645
17646static int
17647init_alpn(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
17648{
17649 unsigned int alpn_len = (unsigned int)strlen((char *)alpn_proto_list);
17650 int ret = SSL_CTX_set_alpn_protos(dom_ctx->ssl_ctx,
17651 (const unsigned char *)alpn_proto_list,
17652 alpn_len);
17653 if (ret != 0) {
17654 mg_cry_ctx_internal(phys_ctx,
17655 "SSL_CTX_set_alpn_protos error: %s",
17656 ssl_error());
17657 }
17658
17661 (void *)dom_ctx);
17662
17665 (void *)dom_ctx);
17666
17667 return ret;
17668}
17669#endif
17670
17671
17672/* Setup SSL CTX as required by CivetWeb */
17673static int
17675 struct mg_domain_context *dom_ctx,
17676 const char *pem,
17677 const char *chain)
17678{
17679 int callback_ret;
17682 const char *ca_path;
17683 const char *ca_file;
17685 int verify_depth;
17686 struct timespec now_mt;
17689 int protocol_ver;
17691
17692#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \
17693 && !defined(NO_SSL_DL)
17694 if ((dom_ctx->ssl_ctx = SSL_CTX_new(TLS_server_method())) == NULL) {
17695 mg_cry_ctx_internal(phys_ctx,
17696 "SSL_CTX_new (server) error: %s",
17697 ssl_error());
17698 return 0;
17699 }
17700#else
17701 if ((dom_ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
17702 mg_cry_ctx_internal(phys_ctx,
17703 "SSL_CTX_new (server) error: %s",
17704 ssl_error());
17705 return 0;
17706 }
17707#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
17708
17709#if defined(SSL_OP_NO_TLSv1_3)
17714#else
17718#endif
17719
17720 protocol_ver = atoi(dom_ctx->config[SSL_PROTOCOL_VERSION]);
17727
17728#if defined(SSL_OP_NO_RENEGOTIATION)
17730#endif
17731
17732#if !defined(NO_SSL_DL)
17733 SSL_CTX_set_ecdh_auto(dom_ctx->ssl_ctx, 1);
17734#endif /* NO_SSL_DL */
17735
17736 /* In SSL documentation examples callback defined without const
17737 * specifier 'void (*)(SSL *, int, int)' See:
17738 * https://www.openssl.org/docs/man1.0.2/ssl/ssl.html
17739 * https://www.openssl.org/docs/man1.1.0/ssl/ssl.html
17740 * But in the source code const SSL is used:
17741 * 'void (*)(const SSL *, int, int)' See:
17742 * https://github.com/openssl/openssl/blob/1d97c8435171a7af575f73c526d79e1ef0ee5960/ssl/ssl.h#L1173
17743 * Problem about wrong documentation described, but not resolved:
17744 * https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/1147526
17745 * Wrong const cast ignored on C or can be suppressed by compiler flags.
17746 * But when compiled with modern C++ compiler, correct const should be
17747 * provided
17748 */
17750
17753
17754 /* If a callback has been specified, call it. */
17755 callback_ret = (phys_ctx->callbacks.init_ssl == NULL)
17756 ? 0
17757 : (phys_ctx->callbacks.init_ssl(dom_ctx->ssl_ctx,
17758 phys_ctx->user_data));
17759
17760 /* If callback returns 0, civetweb sets up the SSL certificate.
17761 * If it returns 1, civetweb assumes the callback already did this.
17762 * If it returns -1, initializing ssl fails. */
17763 if (callback_ret < 0) {
17764 mg_cry_ctx_internal(phys_ctx,
17765 "SSL callback returned error: %i",
17766 callback_ret);
17767 return 0;
17768 }
17769 if (callback_ret > 0) {
17770 /* Callback did everything. */
17771 return 1;
17772 }
17773
17774 /* If a domain callback has been specified, call it. */
17775 callback_ret = (phys_ctx->callbacks.init_ssl_domain == NULL)
17776 ? 0
17777 : (phys_ctx->callbacks.init_ssl_domain(
17778 dom_ctx->config[AUTHENTICATION_DOMAIN],
17779 dom_ctx->ssl_ctx,
17780 phys_ctx->user_data));
17781
17782 /* If domain callback returns 0, civetweb sets up the SSL certificate.
17783 * If it returns 1, civetweb assumes the callback already did this.
17784 * If it returns -1, initializing ssl fails. */
17785 if (callback_ret < 0) {
17786 mg_cry_ctx_internal(phys_ctx,
17787 "Domain SSL callback returned error: %i",
17788 callback_ret);
17789 return 0;
17790 }
17791 if (callback_ret > 0) {
17792 /* Domain callback did everything. */
17793 return 1;
17794 }
17795
17796 /* Use some combination of start time, domain and port as a SSL
17797 * context ID. This should be unique on the current machine. */
17800 md5_append(&md5state, (const md5_byte_t *)&now_mt, sizeof(now_mt));
17802 (const md5_byte_t *)phys_ctx->dd.config[LISTENING_PORTS],
17803 strlen(phys_ctx->dd.config[LISTENING_PORTS]));
17805 (const md5_byte_t *)dom_ctx->config[AUTHENTICATION_DOMAIN],
17807 md5_append(&md5state, (const md5_byte_t *)phys_ctx, sizeof(*phys_ctx));
17808 md5_append(&md5state, (const md5_byte_t *)dom_ctx, sizeof(*dom_ctx));
17810
17812 (unsigned char *)ssl_context_id,
17813 sizeof(ssl_context_id));
17814
17815 if (pem != NULL) {
17816 if (!ssl_use_pem_file(phys_ctx, dom_ctx, pem, chain)) {
17817 return 0;
17818 }
17819 }
17820
17821 /* Should we support client certificates? */
17822 /* Default is "no". */
17825 if (dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) {
17826 if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER], "yes") == 0) {
17827 /* Yes, they are mandatory */
17829 } else if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER],
17830 "optional")
17831 == 0) {
17832 /* Yes, they are optional */
17835 }
17836 }
17837
17839 (dom_ctx->config[SSL_DEFAULT_VERIFY_PATHS] != NULL)
17840 && (mg_strcasecmp(dom_ctx->config[SSL_DEFAULT_VERIFY_PATHS], "yes")
17841 == 0);
17842
17843 if (should_verify_peer) {
17844 ca_path = dom_ctx->config[SSL_CA_PATH];
17845 ca_file = dom_ctx->config[SSL_CA_FILE];
17847 != 1) {
17848 mg_cry_ctx_internal(phys_ctx,
17849 "SSL_CTX_load_verify_locations error: %s "
17850 "ssl_verify_peer requires setting "
17851 "either ssl_ca_path or ssl_ca_file. "
17852 "Is any of them present in the "
17853 ".conf file?",
17854 ssl_error());
17855 return 0;
17856 }
17857
17860 } else {
17861 SSL_CTX_set_verify(dom_ctx->ssl_ctx,
17864 NULL);
17865 }
17866
17868 && (SSL_CTX_set_default_verify_paths(dom_ctx->ssl_ctx) != 1)) {
17869 mg_cry_ctx_internal(phys_ctx,
17870 "SSL_CTX_set_default_verify_paths error: %s",
17871 ssl_error());
17872 return 0;
17873 }
17874
17875 if (dom_ctx->config[SSL_VERIFY_DEPTH]) {
17876 verify_depth = atoi(dom_ctx->config[SSL_VERIFY_DEPTH]);
17878 }
17879 }
17880
17881 if (dom_ctx->config[SSL_CIPHER_LIST] != NULL) {
17882 if (SSL_CTX_set_cipher_list(dom_ctx->ssl_ctx,
17883 dom_ctx->config[SSL_CIPHER_LIST])
17884 != 1) {
17885 mg_cry_ctx_internal(phys_ctx,
17886 "SSL_CTX_set_cipher_list error: %s",
17887 ssl_error());
17888 }
17889 }
17890
17891 /* SSL session caching */
17893 ? atoi(dom_ctx->config[SSL_CACHE_TIMEOUT])
17894 : 0);
17895 if (ssl_cache_timeout > 0) {
17897 /* SSL_CTX_sess_set_cache_size(dom_ctx->ssl_ctx, 10000); ... use
17898 * default */
17900 }
17901
17902#if defined(USE_ALPN)
17903 /* Initialize ALPN only of TLS library (OpenSSL version) supports ALPN */
17904#if !defined(NO_SSL_DL)
17906#endif
17907 {
17908 init_alpn(phys_ctx, dom_ctx);
17909 }
17910#endif
17911
17912 return 1;
17913}
17914
17915
17916/* Check if SSL is required.
17917 * If so, dynamically load SSL library
17918 * and set up ctx->ssl_ctx pointer. */
17919static int
17920init_ssl_ctx(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
17921{
17922 void *ssl_ctx = 0;
17923 int callback_ret;
17924 const char *pem;
17925 const char *chain;
17926 char ebuf[128];
17927
17928 if (!phys_ctx) {
17929 return 0;
17930 }
17931
17932 if (!dom_ctx) {
17933 dom_ctx = &(phys_ctx->dd);
17934 }
17935
17936 if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
17937 /* No SSL port is set. No need to setup SSL. */
17938 return 1;
17939 }
17940
17941 /* Check for external SSL_CTX */
17942 callback_ret =
17943 (phys_ctx->callbacks.external_ssl_ctx == NULL)
17944 ? 0
17945 : (phys_ctx->callbacks.external_ssl_ctx(&ssl_ctx,
17946 phys_ctx->user_data));
17947
17948 if (callback_ret < 0) {
17949 /* Callback exists and returns <0: Initializing failed. */
17950 mg_cry_ctx_internal(phys_ctx,
17951 "external_ssl_ctx callback returned error: %i",
17952 callback_ret);
17953 return 0;
17954 } else if (callback_ret > 0) {
17955 /* Callback exists and returns >0: Initializing complete,
17956 * civetweb should not modify the SSL context. */
17957 dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx;
17958 if (!initialize_openssl(ebuf, sizeof(ebuf))) {
17959 mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
17960 return 0;
17961 }
17962 return 1;
17963 }
17964 /* If the callback does not exist or return 0, civetweb must initialize
17965 * the SSL context. Handle "domain" callback next. */
17966
17967 /* Check for external domain SSL_CTX callback. */
17968 callback_ret = (phys_ctx->callbacks.external_ssl_ctx_domain == NULL)
17969 ? 0
17970 : (phys_ctx->callbacks.external_ssl_ctx_domain(
17971 dom_ctx->config[AUTHENTICATION_DOMAIN],
17972 &ssl_ctx,
17973 phys_ctx->user_data));
17974
17975 if (callback_ret < 0) {
17976 /* Callback < 0: Error. Abort init. */
17978 phys_ctx,
17979 "external_ssl_ctx_domain callback returned error: %i",
17980 callback_ret);
17981 return 0;
17982 } else if (callback_ret > 0) {
17983 /* Callback > 0: Consider init done. */
17984 dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx;
17985 if (!initialize_openssl(ebuf, sizeof(ebuf))) {
17986 mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
17987 return 0;
17988 }
17989 return 1;
17990 }
17991 /* else: external_ssl_ctx/external_ssl_ctx_domain do not exist or return
17992 * 0, CivetWeb should continue initializing SSL */
17993
17994 /* If PEM file is not specified and the init_ssl callbacks
17995 * are not specified, setup will fail. */
17996 if (((pem = dom_ctx->config[SSL_CERTIFICATE]) == NULL)
17997 && (phys_ctx->callbacks.init_ssl == NULL)
17998 && (phys_ctx->callbacks.init_ssl_domain == NULL)) {
17999 /* No certificate and no init_ssl callbacks:
18000 * Essential data to set up TLS is missing.
18001 */
18002 mg_cry_ctx_internal(phys_ctx,
18003 "Initializing SSL failed: -%s is not set",
18005 return 0;
18006 }
18007
18008 /* If a certificate chain is configured, use it. */
18009 chain = dom_ctx->config[SSL_CERTIFICATE_CHAIN];
18010 if (chain == NULL) {
18011 /* Default: certificate chain in PEM file */
18012 chain = pem;
18013 }
18014 if ((chain != NULL) && (*chain == 0)) {
18015 /* If the chain is an empty string, don't use it. */
18016 chain = NULL;
18017 }
18018
18019 if (!initialize_openssl(ebuf, sizeof(ebuf))) {
18020 mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
18021 return 0;
18022 }
18023
18024 return init_ssl_ctx_impl(phys_ctx, dom_ctx, pem, chain);
18025}
18026
18027
18028static void
18030{
18031#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
18032
18033 if (mg_atomic_dec(&cryptolib_users) == 0) {
18034
18035 /* Shutdown according to
18036 * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
18037 * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl
18038 */
18040#else
18041 int i;
18042
18043 if (mg_atomic_dec(&cryptolib_users) == 0) {
18044
18045 /* Shutdown according to
18046 * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
18047 * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl
18048 */
18054 EVP_cleanup();
18057
18058 for (i = 0; i < CRYPTO_num_locks(); i++) {
18060 }
18062 ssl_mutexes = NULL;
18063#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
18064 }
18065}
18066#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS) */
18067
18068
18069#if !defined(NO_FILESYSTEMS)
18070static int
18071set_gpass_option(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
18072{
18073 if (phys_ctx) {
18074 struct mg_file file = STRUCT_FILE_INITIALIZER;
18075 const char *path;
18076 struct mg_connection fc;
18077 if (!dom_ctx) {
18078 dom_ctx = &(phys_ctx->dd);
18079 }
18080 path = dom_ctx->config[GLOBAL_PASSWORDS_FILE];
18081 if ((path != NULL)
18082 && !mg_stat(fake_connection(&fc, phys_ctx), path, &file.stat)) {
18084 "Cannot open %s: %s",
18085 path,
18086 strerror(ERRNO));
18087 return 0;
18088 }
18089 return 1;
18090 }
18091 return 0;
18092}
18093#endif /* NO_FILESYSTEMS */
18094
18095
18096static int
18098{
18099 union usa sa;
18100 memset(&sa, 0, sizeof(sa));
18101#if defined(USE_IPV6)
18102 sa.sin6.sin6_family = AF_INET6;
18103#else
18104 sa.sin.sin_family = AF_INET;
18105#endif
18106 return check_acl(phys_ctx, &sa) != -1;
18107}
18108
18109
18110static void
18112{
18113 if (!conn) {
18114 return;
18115 }
18116
18117 conn->num_bytes_sent = conn->consumed_content = 0;
18118
18119 conn->path_info = NULL;
18120 conn->status_code = -1;
18121 conn->content_len = -1;
18122 conn->is_chunked = 0;
18123 conn->must_close = 0;
18124 conn->request_len = 0;
18125 conn->request_state = 0;
18126 conn->throttle = 0;
18127 conn->accept_gzip = 0;
18128
18129 conn->response_info.content_length = conn->request_info.content_length = -1;
18130 conn->response_info.http_version = conn->request_info.http_version = NULL;
18131 conn->response_info.num_headers = conn->request_info.num_headers = 0;
18132 conn->response_info.status_text = NULL;
18133 conn->response_info.status_code = 0;
18134
18135 conn->request_info.remote_user = NULL;
18136 conn->request_info.request_method = NULL;
18137 conn->request_info.request_uri = NULL;
18138
18139 /* Free cleaned local URI (if any) */
18140 if (conn->request_info.local_uri != conn->request_info.local_uri_raw) {
18141 mg_free((void *)conn->request_info.local_uri);
18142 conn->request_info.local_uri = NULL;
18143 }
18144 conn->request_info.local_uri = NULL;
18145
18146#if defined(USE_SERVER_STATS)
18147 conn->processing_time = 0;
18148#endif
18149}
18150
18151
18152static int
18153set_tcp_nodelay(const struct socket *so, int nodelay_on)
18154{
18155 if ((so->lsa.sa.sa_family == AF_INET)
18156 || (so->lsa.sa.sa_family == AF_INET6)) {
18157 /* Only for TCP sockets */
18158 if (setsockopt(so->sock,
18162 sizeof(nodelay_on))
18163 != 0) {
18164 /* Error */
18165 return 1;
18166 }
18167 }
18168 /* OK */
18169 return 0;
18170}
18171
18172
18173#if !defined(__ZEPHYR__)
18174static void
18176{
18177#if defined(_WIN32)
18178 char buf[MG_BUF_LEN];
18179 int n;
18180#endif
18181 struct linger linger;
18182 int error_code = 0;
18183 int linger_timeout = -2;
18184 socklen_t opt_len = sizeof(error_code);
18185
18186 if (!conn) {
18187 return;
18188 }
18189
18190 /* http://msdn.microsoft.com/en-us/library/ms739165(v=vs.85).aspx:
18191 * "Note that enabling a nonzero timeout on a nonblocking socket
18192 * is not recommended.", so set it to blocking now */
18193 set_blocking_mode(conn->client.sock);
18194
18195 /* Send FIN to the client */
18196 shutdown(conn->client.sock, SHUTDOWN_WR);
18197
18198#if defined(_WIN32)
18199 /* Read and discard pending incoming data. If we do not do that and
18200 * close
18201 * the socket, the data in the send buffer may be discarded. This
18202 * behaviour is seen on Windows, when client keeps sending data
18203 * when server decides to close the connection; then when client
18204 * does recv() it gets no data back. */
18205 do {
18206 n = pull_inner(NULL, conn, buf, sizeof(buf), /* Timeout in s: */ 1.0);
18207 } while (n > 0);
18208#endif
18209
18210 if (conn->dom_ctx->config[LINGER_TIMEOUT]) {
18211 linger_timeout = atoi(conn->dom_ctx->config[LINGER_TIMEOUT]);
18212 }
18213
18214 /* Set linger option according to configuration */
18215 if (linger_timeout >= 0) {
18216 /* Set linger option to avoid socket hanging out after close. This
18217 * prevent ephemeral port exhaust problem under high QPS. */
18218 linger.l_onoff = 1;
18219
18220#if defined(_MSC_VER)
18221#pragma warning(push)
18222#pragma warning(disable : 4244)
18223#endif
18224#if defined(GCC_DIAGNOSTIC)
18225#pragma GCC diagnostic push
18226#pragma GCC diagnostic ignored "-Wconversion"
18227#endif
18228 /* Data type of linger structure elements may differ,
18229 * so we don't know what cast we need here.
18230 * Disable type conversion warnings. */
18231
18232 linger.l_linger = (linger_timeout + 999) / 1000;
18233
18234#if defined(GCC_DIAGNOSTIC)
18235#pragma GCC diagnostic pop
18236#endif
18237#if defined(_MSC_VER)
18238#pragma warning(pop)
18239#endif
18240
18241 } else {
18242 linger.l_onoff = 0;
18243 linger.l_linger = 0;
18244 }
18245
18246 if (linger_timeout < -1) {
18247 /* Default: don't configure any linger */
18248 } else if (getsockopt(conn->client.sock,
18249 SOL_SOCKET,
18250 SO_ERROR,
18251#if defined(_WIN32) /* WinSock uses different data type here */
18252 (char *)&error_code,
18253#else
18254 &error_code,
18255#endif
18256 &opt_len)
18257 != 0) {
18258 /* Cannot determine if socket is already closed. This should
18259 * not occur and never did in a test. Log an error message
18260 * and continue. */
18261 mg_cry_internal(conn,
18262 "%s: getsockopt(SOL_SOCKET SO_ERROR) failed: %s",
18263 __func__,
18264 strerror(ERRNO));
18265#if defined(_WIN32)
18266 } else if (error_code == WSAECONNRESET) {
18267#else
18268 } else if (error_code == ECONNRESET) {
18269#endif
18270 /* Socket already closed by client/peer, close socket without linger
18271 */
18272 } else {
18273
18274 /* Set linger timeout */
18275 if (setsockopt(conn->client.sock,
18276 SOL_SOCKET,
18277 SO_LINGER,
18278 (char *)&linger,
18279 sizeof(linger))
18280 != 0) {
18282 conn,
18283 "%s: setsockopt(SOL_SOCKET SO_LINGER(%i,%i)) failed: %s",
18284 __func__,
18285 linger.l_onoff,
18286 linger.l_linger,
18287 strerror(ERRNO));
18288 }
18289 }
18290
18291 /* Now we know that our FIN is ACK-ed, safe to close */
18292 closesocket(conn->client.sock);
18293 conn->client.sock = INVALID_SOCKET;
18294}
18295#endif
18296
18297
18298static void
18300{
18301#if defined(USE_SERVER_STATS)
18302 conn->conn_state = 6; /* to close */
18303#endif
18304
18305#if defined(USE_LUA) && defined(USE_WEBSOCKET)
18306 if (conn->lua_websocket_state) {
18307 lua_websocket_close(conn, conn->lua_websocket_state);
18308 conn->lua_websocket_state = NULL;
18309 }
18310#endif
18311
18312 mg_lock_connection(conn);
18313
18314 /* Set close flag, so keep-alive loops will stop */
18315 conn->must_close = 1;
18316
18317 /* call the connection_close callback if assigned */
18318 if (conn->phys_ctx->callbacks.connection_close != NULL) {
18319 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
18320 conn->phys_ctx->callbacks.connection_close(conn);
18321 }
18322 }
18323
18324 /* Reset user data, after close callback is called.
18325 * Do not reuse it. If the user needs a destructor,
18326 * it must be done in the connection_close callback. */
18328
18329#if defined(USE_SERVER_STATS)
18330 conn->conn_state = 7; /* closing */
18331#endif
18332
18333#if defined(USE_MBEDTLS)
18334 if (conn->ssl != NULL) {
18335 mbed_ssl_close(conn->ssl);
18336 conn->ssl = NULL;
18337 }
18338#elif defined(USE_GNUTLS)
18339 if (conn->ssl != NULL) {
18340 gtls_ssl_close(conn->ssl);
18341 conn->ssl = NULL;
18342 }
18343#elif !defined(NO_SSL)
18344 if (conn->ssl != NULL) {
18345 /* Run SSL_shutdown twice to ensure completely close SSL connection
18346 */
18347 SSL_shutdown(conn->ssl);
18348 SSL_free(conn->ssl);
18350 conn->ssl = NULL;
18351 }
18352#endif
18353 if (conn->client.sock != INVALID_SOCKET) {
18354#if defined(__ZEPHYR__)
18355 closesocket(conn->client.sock);
18356#else
18358#endif
18359 conn->client.sock = INVALID_SOCKET;
18360 }
18361
18362 /* call the connection_closed callback if assigned */
18363 if (conn->phys_ctx->callbacks.connection_closed != NULL) {
18364 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
18365 conn->phys_ctx->callbacks.connection_closed(conn);
18366 }
18367 }
18368
18370
18371#if defined(USE_SERVER_STATS)
18372 conn->conn_state = 8; /* closed */
18373#endif
18374}
18375
18376
18377CIVETWEB_API void
18379{
18380 if ((conn == NULL) || (conn->phys_ctx == NULL)) {
18381 return;
18382 }
18383
18384#if defined(USE_WEBSOCKET)
18385 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
18386 if (conn->in_websocket_handling) {
18387 /* Set close flag, so the server thread can exit. */
18388 conn->must_close = 1;
18389 return;
18390 }
18391 }
18392 if (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT) {
18393
18394 unsigned int i;
18395
18396 /* client context: loops must end */
18397 STOP_FLAG_ASSIGN(&conn->phys_ctx->stop_flag, 1);
18398 conn->must_close = 1;
18399
18400 /* We need to get the client thread out of the select/recv call
18401 * here. */
18402 /* Since we use a sleep quantum of some seconds to check for recv
18403 * timeouts, we will just wait a few seconds in mg_join_thread. */
18404
18405 /* join worker thread */
18406 for (i = 0; i < conn->phys_ctx->spawned_worker_threads; i++) {
18407 mg_join_thread(conn->phys_ctx->worker_threadids[i]);
18408 }
18409 }
18410#endif /* defined(USE_WEBSOCKET) */
18411
18412 close_connection(conn);
18413
18414#if !defined(NO_SSL) && !defined(USE_MBEDTLS) \
18415 && !defined(USE_GNUTLS) // TODO: mbedTLS client
18416 if (((conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT)
18417 || (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT))
18418 && (conn->phys_ctx->dd.ssl_ctx != NULL)) {
18419 SSL_CTX_free(conn->phys_ctx->dd.ssl_ctx);
18420 }
18421#endif
18422
18423#if defined(USE_WEBSOCKET)
18424 if (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT) {
18425 mg_free(conn->phys_ctx->worker_threadids);
18426 (void)pthread_mutex_destroy(&conn->mutex);
18427 mg_free(conn);
18428 } else if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) {
18429 (void)pthread_mutex_destroy(&conn->mutex);
18430 mg_free(conn);
18431 }
18432#else
18433 if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) { /* Client */
18434 (void)pthread_mutex_destroy(&conn->mutex);
18435 mg_free(conn);
18436 }
18437#endif /* defined(USE_WEBSOCKET) */
18438}
18439
18440
18441static struct mg_connection *
18443 int use_ssl,
18444 struct mg_init_data *init,
18445 struct mg_error_data *error)
18446{
18447 struct mg_connection *conn = NULL;
18448 SOCKET sock;
18449 union usa sa;
18450 struct sockaddr *psa;
18451 socklen_t len;
18452
18453 unsigned max_req_size =
18454 (unsigned)atoi(config_options[MAX_REQUEST_SIZE].default_value);
18455
18456 /* Size of structures, aligned to 8 bytes */
18457 size_t conn_size = ((sizeof(struct mg_connection) + 7) >> 3) << 3;
18458 size_t ctx_size = ((sizeof(struct mg_context) + 7) >> 3) << 3;
18460
18461 (void)init; /* TODO: Implement required options */
18462
18463 conn = (struct mg_connection *)mg_calloc(1, alloc_size);
18464
18465 if (error != NULL) {
18466 error->code = MG_ERROR_DATA_CODE_OK;
18467 error->code_sub = 0;
18468 if (error->text_buffer_size > 0) {
18469 error->text[0] = 0;
18470 }
18471 }
18472
18473 if (conn == NULL) {
18474 if (error != NULL) {
18476 error->code_sub = (unsigned)alloc_size;
18478 NULL, /* No truncation check for ebuf */
18479 error->text,
18480 error->text_buffer_size,
18481 "calloc(): %s",
18482 strerror(ERRNO));
18483 }
18484 return NULL;
18485 }
18486
18487#if defined(GCC_DIAGNOSTIC)
18488#pragma GCC diagnostic push
18489#pragma GCC diagnostic ignored "-Wcast-align"
18490#endif /* defined(GCC_DIAGNOSTIC) */
18491 /* conn_size is aligned to 8 bytes */
18492
18493 conn->phys_ctx = (struct mg_context *)(((char *)conn) + conn_size);
18494
18495#if defined(GCC_DIAGNOSTIC)
18496#pragma GCC diagnostic pop
18497#endif /* defined(GCC_DIAGNOSTIC) */
18498
18499 conn->buf = (((char *)conn) + conn_size + ctx_size);
18500 conn->buf_size = (int)max_req_size;
18501 conn->phys_ctx->context_type = CONTEXT_HTTP_CLIENT;
18502 conn->dom_ctx = &(conn->phys_ctx->dd);
18503
18504 if (!connect_socket(conn->phys_ctx,
18505 client_options->host,
18506 client_options->port,
18507 use_ssl,
18508 error,
18509 &sock,
18510 &sa)) {
18511 /* "error" will be set by connect_socket. */
18512 /* free all memory and return NULL; */
18513 mg_free(conn);
18514 return NULL;
18515 }
18516
18517#if !defined(NO_SSL) && !defined(USE_MBEDTLS) \
18518 && !defined(USE_GNUTLS) // TODO: mbedTLS client
18519#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \
18520 && !defined(NO_SSL_DL)
18521
18522 if (use_ssl
18523 && (conn->dom_ctx->ssl_ctx = SSL_CTX_new(TLS_client_method()))
18524 == NULL) {
18525 if (error != NULL) {
18528 NULL, /* No truncation check for ebuf */
18529 error->text,
18530 error->text_buffer_size,
18531 "SSL_CTX_new error: %s",
18532 ssl_error());
18533 }
18534
18535 closesocket(sock);
18536 mg_free(conn);
18537 return NULL;
18538 }
18539
18540#else
18541
18542 if (use_ssl
18543 && (conn->dom_ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method()))
18544 == NULL) {
18545 if (error != NULL) {
18548 NULL, /* No truncation check for ebuf */
18549 error->text,
18550 error->text_buffer_size,
18551 "SSL_CTX_new error: %s",
18552 ssl_error());
18553 }
18554
18555 closesocket(sock);
18556 mg_free(conn);
18557 return NULL;
18558 }
18559
18560#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
18561#endif /* NO_SSL */
18562
18563#if defined(USE_IPV6)
18564 len = (sa.sa.sa_family == AF_INET) ? sizeof(conn->client.rsa.sin)
18565 : sizeof(conn->client.rsa.sin6);
18566 psa = (sa.sa.sa_family == AF_INET)
18567 ? (struct sockaddr *)&(conn->client.rsa.sin)
18568 : (struct sockaddr *)&(conn->client.rsa.sin6);
18569#else
18570 len = sizeof(conn->client.rsa.sin);
18571 psa = (struct sockaddr *)&(conn->client.rsa.sin);
18572#endif
18573
18574 conn->client.sock = sock;
18575 conn->client.lsa = sa;
18576
18577 if (getsockname(sock, psa, &len) != 0) {
18578 mg_cry_internal(conn,
18579 "%s: getsockname() failed: %s",
18580 __func__,
18581 strerror(ERRNO));
18582 }
18583
18584 conn->client.is_ssl = use_ssl ? 1 : 0;
18585 if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) {
18586 if (error != NULL) {
18588 error->code_sub = (unsigned)ERRNO;
18590 NULL, /* No truncation check for ebuf */
18591 error->text,
18592 error->text_buffer_size,
18593 "Can not create mutex");
18594 }
18595#if !defined(NO_SSL) && !defined(USE_MBEDTLS) \
18596 && !defined(USE_GNUTLS) // TODO: mbedTLS client
18597 SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18598#endif
18599 closesocket(sock);
18600 mg_free(conn);
18601 return NULL;
18602 }
18603
18604#if !defined(NO_SSL) && !defined(USE_MBEDTLS) \
18605 && !defined(USE_GNUTLS) // TODO: mbedTLS client
18606 if (use_ssl) {
18607 /* TODO: Check ssl_verify_peer and ssl_ca_path here.
18608 * SSL_CTX_set_verify call is needed to switch off server
18609 * certificate checking, which is off by default in OpenSSL and
18610 * on in yaSSL. */
18611 /* TODO: SSL_CTX_set_verify(conn->dom_ctx,
18612 * SSL_VERIFY_PEER, verify_ssl_server); */
18613
18614 if (client_options->client_cert) {
18615 if (!ssl_use_pem_file(conn->phys_ctx,
18616 conn->dom_ctx,
18617 client_options->client_cert,
18618 NULL)) {
18619 if (error != NULL) {
18622 NULL, /* No truncation check for ebuf */
18623 error->text,
18624 error->text_buffer_size,
18625 "Can not use SSL client certificate");
18626 }
18627
18628 SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18629 closesocket(sock);
18630 mg_free(conn);
18631 return NULL;
18632 }
18633 }
18634
18635 if (client_options->server_cert) {
18636 if (SSL_CTX_load_verify_locations(conn->dom_ctx->ssl_ctx,
18637 client_options->server_cert,
18638 NULL)
18639 != 1) {
18640 if (error != NULL) {
18643 NULL, /* No truncation check for ebuf */
18644 error->text,
18645 error->text_buffer_size,
18646 "SSL_CTX_load_verify_locations error: %s",
18647 ssl_error());
18648 }
18649 SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18650 closesocket(sock);
18651 mg_free(conn);
18652 return NULL;
18653 }
18655 } else {
18657 }
18658
18659 if (!sslize(conn, SSL_connect, client_options)) {
18660 if (error != NULL) {
18663 NULL, /* No truncation check for ebuf */
18664 error->text,
18665 error->text_buffer_size,
18666 "SSL connection error");
18667 }
18668 SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18669 closesocket(sock);
18670 mg_free(conn);
18671 return NULL;
18672 }
18673 }
18674#endif
18675
18676 return conn;
18677}
18678
18679
18682 char *error_buffer,
18683 size_t error_buffer_size)
18684{
18685 struct mg_init_data init;
18686 struct mg_error_data error;
18687
18688 memset(&init, 0, sizeof(init));
18689 memset(&error, 0, sizeof(error));
18691 error.text = error_buffer;
18692 return mg_connect_client_impl(client_options, 1, &init, &error);
18693}
18694
18695
18697mg_connect_client(const char *host,
18698 int port,
18699 int use_ssl,
18700 char *error_buffer,
18701 size_t error_buffer_size)
18702{
18703 struct mg_client_options opts;
18704 struct mg_init_data init;
18705 struct mg_error_data error;
18706
18707 memset(&init, 0, sizeof(init));
18708
18709 memset(&error, 0, sizeof(error));
18711 error.text = error_buffer;
18712
18713 memset(&opts, 0, sizeof(opts));
18714 opts.host = host;
18715 opts.port = port;
18716 if (use_ssl) {
18717 opts.host_name = host;
18718 }
18719
18720 return mg_connect_client_impl(&opts, use_ssl, &init, &error);
18721}
18722
18723
18724#if defined(MG_EXPERIMENTAL_INTERFACES)
18726mg_connect_client2(const char *host,
18727 const char *protocol,
18728 int port,
18729 const char *path,
18730 struct mg_init_data *init,
18731 struct mg_error_data *error)
18732{
18733 (void)path;
18734
18735 int is_ssl, is_ws;
18736 /* void *user_data = (init != NULL) ? init->user_data : NULL; -- TODO */
18737
18738 if (error != NULL) {
18739 error->code = MG_ERROR_DATA_CODE_OK;
18740 error->code_sub = 0;
18741 if (error->text_buffer_size > 0) {
18742 *error->text = 0;
18743 }
18744 }
18745
18746 if ((host == NULL) || (protocol == NULL)) {
18747 if (error != NULL) {
18750 NULL, /* No truncation check for error buffers */
18751 error->text,
18752 error->text_buffer_size,
18753 "%s",
18754 "Invalid parameters");
18755 }
18756 return NULL;
18757 }
18758
18759 /* check all known protocols */
18760 if (!mg_strcasecmp(protocol, "http")) {
18761 is_ssl = 0;
18762 is_ws = 0;
18763 } else if (!mg_strcasecmp(protocol, "https")) {
18764 is_ssl = 1;
18765 is_ws = 0;
18766#if defined(USE_WEBSOCKET)
18767 } else if (!mg_strcasecmp(protocol, "ws")) {
18768 is_ssl = 0;
18769 is_ws = 1;
18770 } else if (!mg_strcasecmp(protocol, "wss")) {
18771 is_ssl = 1;
18772 is_ws = 1;
18773#endif
18774 } else {
18775 if (error != NULL) {
18778 NULL, /* No truncation check for error buffers */
18779 error->text,
18780 error->text_buffer_size,
18781 "Protocol %s not supported",
18782 protocol);
18783 }
18784 return NULL;
18785 }
18786
18787 /* TODO: The current implementation here just calls the old
18788 * implementations, without using any new options. This is just a first
18789 * step to test the new interfaces. */
18790#if defined(USE_WEBSOCKET)
18791 if (is_ws) {
18792 /* TODO: implement all options */
18794 host,
18795 port,
18796 is_ssl,
18797 ((error != NULL) ? error->text : NULL),
18798 ((error != NULL) ? error->text_buffer_size : 0),
18799 (path ? path : ""),
18800 NULL /* TODO: origin */,
18803 (void *)init->callbacks);
18804 }
18805#else
18806 (void)is_ws;
18807#endif
18808
18809 /* TODO: all additional options */
18810 struct mg_client_options opts;
18811
18812 memset(&opts, 0, sizeof(opts));
18813 opts.host = host;
18814 opts.port = port;
18815
18816 return mg_connect_client_impl(&opts, is_ssl, init, error);
18817}
18818#endif
18819
18820
18821static const struct {
18822 const char *proto;
18825} abs_uri_protocols[] = {{"http://", 7, 80},
18826 {"https://", 8, 443},
18827 {"ws://", 5, 80},
18828 {"wss://", 6, 443},
18829 {NULL, 0, 0}};
18830
18831
18832/* Check if the uri is valid.
18833 * return 0 for invalid uri,
18834 * return 1 for *,
18835 * return 2 for relative uri,
18836 * return 3 for absolute uri without port,
18837 * return 4 for absolute uri with port */
18838static int
18839get_uri_type(const char *uri)
18840{
18841 int i;
18842 const char *hostend, *portbegin;
18843 char *portend;
18844 unsigned long port;
18845
18846 /* According to the HTTP standard
18847 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
18848 * URI can be an asterisk (*) or should start with slash (relative uri),
18849 * or it should start with the protocol (absolute uri). */
18850 if ((uri[0] == '*') && (uri[1] == '\0')) {
18851 /* asterisk */
18852 return 1;
18853 }
18854
18855 /* Valid URIs according to RFC 3986
18856 * (https://www.ietf.org/rfc/rfc3986.txt)
18857 * must only contain reserved characters :/?#[]@!$&'()*+,;=
18858 * and unreserved characters A-Z a-z 0-9 and -._~
18859 * and % encoded symbols.
18860 */
18861 for (i = 0; uri[i] != 0; i++) {
18862 if ((unsigned char)uri[i] < 33) {
18863 /* control characters and spaces are invalid */
18864 return 0;
18865 }
18866 /* Allow everything else here (See #894) */
18867 }
18868
18869 /* A relative uri starts with a / character */
18870 if (uri[0] == '/') {
18871 /* relative uri */
18872 return 2;
18873 }
18874
18875 /* It could be an absolute uri: */
18876 /* This function only checks if the uri is valid, not if it is
18877 * addressing the current server. So civetweb can also be used
18878 * as a proxy server. */
18879 for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
18880 if (mg_strncasecmp(uri,
18883 == 0) {
18884
18885 hostend = strchr(uri + abs_uri_protocols[i].proto_len, '/');
18886 if (!hostend) {
18887 return 0;
18888 }
18890 if (!portbegin) {
18891 return 3;
18892 }
18893
18894 port = strtoul(portbegin + 1, &portend, 10);
18895 if ((portend != hostend) || (port <= 0) || !is_valid_port(port)) {
18896 return 0;
18897 }
18898
18899 return 4;
18900 }
18901 }
18902
18903 return 0;
18904}
18905
18906
18907/* Return NULL or the relative uri at the current server */
18908static const char *
18909get_rel_url_at_current_server(const char *uri, const struct mg_connection *conn)
18910{
18911 const char *server_domain;
18912 size_t server_domain_len;
18913 size_t request_domain_len = 0;
18914 unsigned long port = 0;
18916 const char *hostbegin = NULL;
18917 const char *hostend = NULL;
18918 const char *portbegin;
18919 char *portend;
18920
18922 !mg_strcasecmp(conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK], "yes");
18923
18924 /* DNS is case insensitive, so use case insensitive string compare here
18925 */
18926 for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
18927 if (mg_strncasecmp(uri,
18930 == 0) {
18931
18932 hostbegin = uri + abs_uri_protocols[i].proto_len;
18933 hostend = strchr(hostbegin, '/');
18934 if (!hostend) {
18935 return 0;
18936 }
18937 portbegin = strchr(hostbegin, ':');
18938 if ((!portbegin) || (portbegin > hostend)) {
18939 port = abs_uri_protocols[i].default_port;
18940 request_domain_len = (size_t)(hostend - hostbegin);
18941 } else {
18942 port = strtoul(portbegin + 1, &portend, 10);
18943 if ((portend != hostend) || (port <= 0)
18944 || !is_valid_port(port)) {
18945 return 0;
18946 }
18948 }
18949 /* protocol found, port set */
18950 break;
18951 }
18952 }
18953
18954 if (!port) {
18955 /* port remains 0 if the protocol is not found */
18956 return 0;
18957 }
18958
18959 /* Check if the request is directed to a different server. */
18960 /* First check if the port is the same. */
18961 if (ntohs(USA_IN_PORT_UNSAFE(&conn->client.lsa)) != port) {
18962 /* Request is directed to a different port */
18963 return 0;
18964 }
18965
18966 /* Finally check if the server corresponds to the authentication
18967 * domain of the server (the server domain).
18968 * Allow full matches (like http://mydomain.com/path/file.ext), and
18969 * allow subdomain matches (like http://www.mydomain.com/path/file.ext),
18970 * but do not allow substrings (like
18971 * http://notmydomain.com/path/file.ext
18972 * or http://mydomain.com.fake/path/file.ext).
18973 */
18977 if ((server_domain_len == 0) || (hostbegin == NULL)) {
18978 return 0;
18979 }
18982 /* Request is directed to this server - full name match. */
18983 } else {
18985 /* Request is directed to another server: The server name
18986 * is longer than the request name.
18987 * Drop this case here to avoid overflows in the
18988 * following checks. */
18989 return 0;
18990 }
18992 /* Request is directed to another server: It could be a
18993 * substring
18994 * like notmyserver.com */
18995 return 0;
18996 }
18997 if (0
19001 /* Request is directed to another server:
19002 * The server name is different. */
19003 return 0;
19004 }
19005 }
19006 }
19007
19008 return hostend;
19009}
19010
19011
19012static int
19013get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
19014{
19015 if (ebuf_len > 0) {
19016 ebuf[0] = '\0';
19017 }
19018 *err = 0;
19019
19021
19022 if (!conn) {
19023 mg_snprintf(conn,
19024 NULL, /* No truncation check for ebuf */
19025 ebuf,
19026 ebuf_len,
19027 "%s",
19028 "Internal error");
19029 *err = 500;
19030 return 0;
19031 }
19032
19033 /* Set the time the request was received. This value should be used for
19034 * timeouts. */
19036
19037 conn->request_len =
19038 read_message(NULL, conn, conn->buf, conn->buf_size, &conn->data_len);
19039 DEBUG_ASSERT(conn->request_len < 0 || conn->data_len >= conn->request_len);
19040 if ((conn->request_len >= 0) && (conn->data_len < conn->request_len)) {
19041 mg_snprintf(conn,
19042 NULL, /* No truncation check for ebuf */
19043 ebuf,
19044 ebuf_len,
19045 "%s",
19046 "Invalid message size");
19047 *err = 500;
19048 return 0;
19049 }
19050
19051 if ((conn->request_len == 0) && (conn->data_len == conn->buf_size)) {
19052 mg_snprintf(conn,
19053 NULL, /* No truncation check for ebuf */
19054 ebuf,
19055 ebuf_len,
19056 "%s",
19057 "Message too large");
19058 *err = 413;
19059 return 0;
19060 }
19061
19062 if (conn->request_len <= 0) {
19063 if (conn->data_len > 0) {
19064 mg_snprintf(conn,
19065 NULL, /* No truncation check for ebuf */
19066 ebuf,
19067 ebuf_len,
19068 "%s",
19069 conn->request_len == -3 ? "Request timeout"
19070 : "Malformed message");
19071 *err = 400;
19072 } else {
19073 /* Server did not recv anything -> just close the connection */
19074 conn->must_close = 1;
19075 mg_snprintf(conn,
19076 NULL, /* No truncation check for ebuf */
19077 ebuf,
19078 ebuf_len,
19079 "%s",
19080 "No data received");
19081 *err = 0;
19082 }
19083 return 0;
19084 }
19085 return 1;
19086}
19087
19088
19089static int
19090get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
19091{
19092 const char *cl;
19093
19094 conn->connection_type =
19095 CONNECTION_TYPE_REQUEST; /* request (valid of not) */
19096
19097 if (!get_message(conn, ebuf, ebuf_len, err)) {
19098 return 0;
19099 }
19100
19101 if (parse_http_request(conn->buf, conn->buf_size, &conn->request_info)
19102 <= 0) {
19103 mg_snprintf(conn,
19104 NULL, /* No truncation check for ebuf */
19105 ebuf,
19106 ebuf_len,
19107 "%s",
19108 "Bad request");
19109 *err = 400;
19110 return 0;
19111 }
19112
19113 /* Message is a valid request */
19114
19115 if (!switch_domain_context(conn)) {
19116 mg_snprintf(conn,
19117 NULL, /* No truncation check for ebuf */
19118 ebuf,
19119 ebuf_len,
19120 "%s",
19121 "Bad request: Host mismatch");
19122 *err = 400;
19123 return 0;
19124 }
19125
19126#if USE_ZLIB
19127 if (((cl = get_header(conn->request_info.http_headers,
19128 conn->request_info.num_headers,
19129 "Accept-Encoding"))
19130 != NULL)
19131 && strstr(cl, "gzip")) {
19132 conn->accept_gzip = 1;
19133 }
19134#endif
19135 if (((cl = get_header(conn->request_info.http_headers,
19136 conn->request_info.num_headers,
19137 "Transfer-Encoding"))
19138 != NULL)
19139 && mg_strcasecmp(cl, "identity")) {
19140 if (mg_strcasecmp(cl, "chunked")) {
19141 mg_snprintf(conn,
19142 NULL, /* No truncation check for ebuf */
19143 ebuf,
19144 ebuf_len,
19145 "%s",
19146 "Bad request");
19147 *err = 400;
19148 return 0;
19149 }
19150 conn->is_chunked = 1;
19151 conn->content_len = 0; /* not yet read */
19152 } else if ((cl = get_header(conn->request_info.http_headers,
19153 conn->request_info.num_headers,
19154 "Content-Length"))
19155 != NULL) {
19156 /* Request has content length set */
19157 char *endptr = NULL;
19158 conn->content_len = strtoll(cl, &endptr, 10);
19159 if ((endptr == cl) || (conn->content_len < 0)) {
19160 mg_snprintf(conn,
19161 NULL, /* No truncation check for ebuf */
19162 ebuf,
19163 ebuf_len,
19164 "%s",
19165 "Bad request");
19166 *err = 411;
19167 return 0;
19168 }
19169 /* Publish the content length back to the request info. */
19170 conn->request_info.content_length = conn->content_len;
19171 } else {
19172 /* There is no exception, see RFC7230. */
19173 conn->content_len = 0;
19174 }
19175
19176 return 1;
19177}
19178
19179
19180/* conn is assumed to be valid in this internal function */
19181static int
19182get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
19183{
19184 const char *cl;
19185
19186 conn->connection_type =
19187 CONNECTION_TYPE_RESPONSE; /* response (valid or not) */
19188
19189 if (!get_message(conn, ebuf, ebuf_len, err)) {
19190 return 0;
19191 }
19192
19193 if (parse_http_response(conn->buf, conn->buf_size, &conn->response_info)
19194 <= 0) {
19195 mg_snprintf(conn,
19196 NULL, /* No truncation check for ebuf */
19197 ebuf,
19198 ebuf_len,
19199 "%s",
19200 "Bad response");
19201 *err = 400;
19202 return 0;
19203 }
19204
19205 /* Message is a valid response */
19206
19207 if (((cl = get_header(conn->response_info.http_headers,
19208 conn->response_info.num_headers,
19209 "Transfer-Encoding"))
19210 != NULL)
19211 && mg_strcasecmp(cl, "identity")) {
19212 if (mg_strcasecmp(cl, "chunked")) {
19213 mg_snprintf(conn,
19214 NULL, /* No truncation check for ebuf */
19215 ebuf,
19216 ebuf_len,
19217 "%s",
19218 "Bad request");
19219 *err = 400;
19220 return 0;
19221 }
19222 conn->is_chunked = 1;
19223 conn->content_len = 0; /* not yet read */
19224 } else if ((cl = get_header(conn->response_info.http_headers,
19225 conn->response_info.num_headers,
19226 "Content-Length"))
19227 != NULL) {
19228 char *endptr = NULL;
19229 conn->content_len = strtoll(cl, &endptr, 10);
19230 if ((endptr == cl) || (conn->content_len < 0)) {
19231 mg_snprintf(conn,
19232 NULL, /* No truncation check for ebuf */
19233 ebuf,
19234 ebuf_len,
19235 "%s",
19236 "Bad request");
19237 *err = 411;
19238 return 0;
19239 }
19240 /* Publish the content length back to the response info. */
19241 conn->response_info.content_length = conn->content_len;
19242
19243 /* TODO: check if it is still used in response_info */
19244 conn->request_info.content_length = conn->content_len;
19245
19246 /* TODO: we should also consider HEAD method */
19247 if (conn->response_info.status_code == 304) {
19248 conn->content_len = 0;
19249 }
19250 } else {
19251 /* TODO: we should also consider HEAD method */
19252 if (((conn->response_info.status_code >= 100)
19253 && (conn->response_info.status_code <= 199))
19254 || (conn->response_info.status_code == 204)
19255 || (conn->response_info.status_code == 304)) {
19256 conn->content_len = 0;
19257 } else {
19258 conn->content_len = -1; /* unknown content length */
19259 }
19260 }
19261
19262 return 1;
19263}
19264
19265
19266CIVETWEB_API int
19268 char *ebuf,
19269 size_t ebuf_len,
19270 int timeout)
19271{
19272 int err, ret;
19273 char txt[32]; /* will not overflow */
19274 char *save_timeout;
19275 char *new_timeout;
19276
19277 if (ebuf_len > 0) {
19278 ebuf[0] = '\0';
19279 }
19280
19281 if (!conn) {
19282 mg_snprintf(conn,
19283 NULL, /* No truncation check for ebuf */
19284 ebuf,
19285 ebuf_len,
19286 "%s",
19287 "Parameter error");
19288 return -1;
19289 }
19290
19291 /* Reset the previous responses */
19292 conn->data_len = 0;
19293
19294 /* Implementation of API function for HTTP clients */
19295 save_timeout = conn->dom_ctx->config[REQUEST_TIMEOUT];
19296
19297 if (timeout >= 0) {
19298 mg_snprintf(conn, NULL, txt, sizeof(txt), "%i", timeout);
19299 new_timeout = txt;
19300 } else {
19301 new_timeout = NULL;
19302 }
19303
19304 conn->dom_ctx->config[REQUEST_TIMEOUT] = new_timeout;
19305 ret = get_response(conn, ebuf, ebuf_len, &err);
19306 conn->dom_ctx->config[REQUEST_TIMEOUT] = save_timeout;
19307
19308 /* TODO: here, the URI is the http response code */
19309 conn->request_info.local_uri_raw = conn->request_info.request_uri;
19310 conn->request_info.local_uri = conn->request_info.local_uri_raw;
19311
19312 /* TODO (mid): Define proper return values - maybe return length?
19313 * For the first test use <0 for error and >0 for OK */
19314 return (ret == 0) ? -1 : +1;
19315}
19316
19317
19319mg_download(const char *host,
19320 int port,
19321 int use_ssl,
19322 char *ebuf,
19323 size_t ebuf_len,
19324 const char *fmt,
19325 ...)
19326{
19327 struct mg_connection *conn;
19328 va_list ap;
19329 int i;
19330 int reqerr;
19331
19332 if (ebuf_len > 0) {
19333 ebuf[0] = '\0';
19334 }
19335
19336 va_start(ap, fmt);
19337
19338 /* open a connection */
19339 conn = mg_connect_client(host, port, use_ssl, ebuf, ebuf_len);
19340
19341 if (conn != NULL) {
19342 i = mg_vprintf(conn, fmt, ap);
19343 if (i <= 0) {
19344 mg_snprintf(conn,
19345 NULL, /* No truncation check for ebuf */
19346 ebuf,
19347 ebuf_len,
19348 "%s",
19349 "Error sending request");
19350 } else {
19351 /* make sure the buffer is clear */
19352 conn->data_len = 0;
19353 get_response(conn, ebuf, ebuf_len, &reqerr);
19354
19355 /* TODO: here, the URI is the http response code */
19356 conn->request_info.local_uri = conn->request_info.request_uri;
19357 }
19358 }
19359
19360 /* if an error occurred, close the connection */
19361 if ((ebuf[0] != '\0') && (conn != NULL)) {
19362 mg_close_connection(conn);
19363 conn = NULL;
19364 }
19365
19366 va_end(ap);
19367 return conn;
19368}
19369
19370
19377
19378
19379#if defined(USE_WEBSOCKET)
19380#if defined(_WIN32)
19381static unsigned __stdcall websocket_client_thread(void *data)
19382#else
19383static void *
19385#endif
19386{
19389
19390 void *user_thread_ptr = NULL;
19391
19392#if !defined(_WIN32) && !defined(__ZEPHYR__)
19393 struct sigaction sa;
19394
19395 /* Ignore SIGPIPE */
19396 memset(&sa, 0, sizeof(sa));
19397 sa.sa_handler = SIG_IGN;
19398 sigaction(SIGPIPE, &sa, NULL);
19399#endif
19400
19401 mg_set_thread_name("ws-clnt");
19402
19403 if (cdata->conn->phys_ctx) {
19404 if (cdata->conn->phys_ctx->callbacks.init_thread) {
19405 /* 3 indicates a websocket client thread */
19406 /* TODO: check if conn->phys_ctx can be set */
19407 user_thread_ptr = cdata->conn->phys_ctx->callbacks.init_thread(
19408 cdata->conn->phys_ctx, 3);
19409 }
19410 }
19411
19412 read_websocket(cdata->conn, cdata->data_handler, cdata->callback_data);
19413
19414 DEBUG_TRACE("%s", "Websocket client thread exited\n");
19415
19416 if (cdata->close_handler != NULL) {
19417 cdata->close_handler(cdata->conn, cdata->callback_data);
19418 }
19419
19420 /* The websocket_client context has only this thread. If it runs out,
19421 set the stop_flag to 2 (= "stopped"). */
19422 STOP_FLAG_ASSIGN(&cdata->conn->phys_ctx->stop_flag, 2);
19423
19424 if (cdata->conn->phys_ctx->callbacks.exit_thread) {
19425 cdata->conn->phys_ctx->callbacks.exit_thread(cdata->conn->phys_ctx,
19426 3,
19428 }
19429
19430 mg_free((void *)cdata);
19431
19432#if defined(_WIN32)
19433 return 0;
19434#else
19435 return NULL;
19436#endif
19437}
19438#endif
19439
19440
19441#if defined(USE_WEBSOCKET)
19442static void
19444{
19445 uint64_t rnd;
19446 unsigned char buffer[2 * sizeof(rnd)];
19447
19448 rnd = get_random();
19449 memcpy(buffer, &rnd, sizeof(rnd));
19450 rnd = get_random();
19451 memcpy(buffer + sizeof(rnd), &rnd, sizeof(rnd));
19452
19453 size_t dst_len = 24 + 1;
19454 mg_base64_encode(buffer, sizeof(buffer), magic25, &dst_len);
19455}
19456#endif
19457
19458
19459static struct mg_connection *
19461 int use_ssl,
19462 char *error_buffer,
19463 size_t error_buffer_size,
19464 const char *path,
19465 const char *origin,
19466 const char *extensions,
19469 void *user_data)
19470{
19471 struct mg_connection *conn = NULL;
19472
19473#if defined(USE_WEBSOCKET)
19475 char magic[32];
19477
19478 const char *host = client_options->host;
19479 int i;
19480
19481 struct mg_init_data init;
19482 struct mg_error_data error;
19483
19484 memset(&init, 0, sizeof(init));
19485 memset(&error, 0, sizeof(error));
19487 error.text = error_buffer;
19488
19489#if defined(__clang__)
19490#pragma clang diagnostic push
19491#pragma clang diagnostic ignored "-Wformat-nonliteral"
19492#endif
19493
19494 /* Establish the client connection and request upgrade */
19495 conn = mg_connect_client_impl(client_options, use_ssl, &init, &error);
19496
19497 /* Connection object will be null if something goes wrong */
19498 if (conn == NULL) {
19499 /* error_buffer should be already filled ... */
19500 if (!error_buffer[0]) {
19501 /* ... if not add an error message */
19502 mg_snprintf(conn,
19503 NULL, /* No truncation check for ebuf */
19506 "Unexpected error");
19507 }
19508 return NULL;
19509 }
19510
19511 if (origin != NULL) {
19512 if (extensions != NULL) {
19513 i = mg_printf(conn,
19514 "GET %s HTTP/1.1\r\n"
19515 "Host: %s\r\n"
19516 "Upgrade: websocket\r\n"
19517 "Connection: Upgrade\r\n"
19518 "Sec-WebSocket-Key: %s\r\n"
19519 "Sec-WebSocket-Version: 13\r\n"
19520 "Sec-WebSocket-Extensions: %s\r\n"
19521 "Origin: %s\r\n"
19522 "\r\n",
19523 path,
19524 host,
19525 magic,
19526 extensions,
19527 origin);
19528 } else {
19529 i = mg_printf(conn,
19530 "GET %s HTTP/1.1\r\n"
19531 "Host: %s\r\n"
19532 "Upgrade: websocket\r\n"
19533 "Connection: Upgrade\r\n"
19534 "Sec-WebSocket-Key: %s\r\n"
19535 "Sec-WebSocket-Version: 13\r\n"
19536 "Origin: %s\r\n"
19537 "\r\n",
19538 path,
19539 host,
19540 magic,
19541 origin);
19542 }
19543 } else {
19544
19545 if (extensions != NULL) {
19546 i = mg_printf(conn,
19547 "GET %s HTTP/1.1\r\n"
19548 "Host: %s\r\n"
19549 "Upgrade: websocket\r\n"
19550 "Connection: Upgrade\r\n"
19551 "Sec-WebSocket-Key: %s\r\n"
19552 "Sec-WebSocket-Version: 13\r\n"
19553 "Sec-WebSocket-Extensions: %s\r\n"
19554 "\r\n",
19555 path,
19556 host,
19557 magic,
19558 extensions);
19559 } else {
19560 i = mg_printf(conn,
19561 "GET %s HTTP/1.1\r\n"
19562 "Host: %s\r\n"
19563 "Upgrade: websocket\r\n"
19564 "Connection: Upgrade\r\n"
19565 "Sec-WebSocket-Key: %s\r\n"
19566 "Sec-WebSocket-Version: 13\r\n"
19567 "\r\n",
19568 path,
19569 host,
19570 magic);
19571 }
19572 }
19573 if (i <= 0) {
19574 mg_snprintf(conn,
19575 NULL, /* No truncation check for ebuf */
19578 "%s",
19579 "Error sending request");
19580 mg_close_connection(conn);
19581 return NULL;
19582 }
19583
19584 conn->data_len = 0;
19585 if (!get_response(conn, error_buffer, error_buffer_size, &i)) {
19586 mg_close_connection(conn);
19587 return NULL;
19588 }
19589 conn->request_info.local_uri_raw = conn->request_info.request_uri;
19590 conn->request_info.local_uri = conn->request_info.local_uri_raw;
19591
19592#if defined(__clang__)
19593#pragma clang diagnostic pop
19594#endif
19595
19596 if (conn->response_info.status_code != 101) {
19597 /* We sent an "upgrade" request. For a correct websocket
19598 * protocol handshake, we expect a "101 Continue" response.
19599 * Otherwise it is a protocol violation. Maybe the HTTP
19600 * Server does not know websockets. */
19601 if (!*error_buffer) {
19602 /* set an error, if not yet set */
19603 mg_snprintf(conn,
19604 NULL, /* No truncation check for ebuf */
19607 "Unexpected server reply");
19608 }
19609
19610 DEBUG_TRACE("Websocket client connect error: %s\r\n", error_buffer);
19611 mg_close_connection(conn);
19612 return NULL;
19613 }
19614
19616 1, sizeof(struct websocket_client_thread_data), conn->phys_ctx);
19617 if (!thread_data) {
19618 DEBUG_TRACE("%s\r\n", "Out of memory");
19620 return NULL;
19621 }
19622
19623 thread_data->conn = conn;
19624 thread_data->data_handler = data_func;
19625 thread_data->close_handler = close_func;
19626 thread_data->callback_data = user_data;
19627
19628 conn->phys_ctx->worker_threadids =
19629 (pthread_t *)mg_calloc_ctx(1, sizeof(pthread_t), conn->phys_ctx);
19630 if (!conn->phys_ctx->worker_threadids) {
19631 DEBUG_TRACE("%s\r\n", "Out of memory");
19634 return NULL;
19635 }
19636
19637 /* Now upgrade to ws/wss client context */
19638 conn->phys_ctx->user_data = user_data;
19639 conn->phys_ctx->context_type = CONTEXT_WS_CLIENT;
19640 conn->phys_ctx->cfg_max_worker_threads = 1; /* one worker thread */
19641 conn->phys_ctx->spawned_worker_threads = 1; /* one worker thread */
19642
19643 /* Start a thread to read the websocket client connection
19644 * This thread will automatically stop when mg_disconnect is
19645 * called on the client connection */
19648 conn->phys_ctx->worker_threadids)
19649 != 0) {
19650 conn->phys_ctx->spawned_worker_threads = 0;
19653 conn = NULL;
19654 DEBUG_TRACE("%s",
19655 "Websocket client connect thread could not be started\r\n");
19656 }
19657
19658#else
19659 /* Appease "unused parameter" warnings */
19660 (void)client_options;
19661 (void)use_ssl;
19662 (void)error_buffer;
19663 (void)error_buffer_size;
19664 (void)path;
19665 (void)origin;
19666 (void)extensions;
19667 (void)user_data;
19668 (void)data_func;
19669 (void)close_func;
19670#endif
19671
19672 return conn;
19673}
19674
19675
19678 int port,
19679 int use_ssl,
19680 char *error_buffer,
19681 size_t error_buffer_size,
19682 const char *path,
19683 const char *origin,
19686 void *user_data)
19687{
19689 memset(&client_options, 0, sizeof(client_options));
19690 client_options.host = host;
19691 client_options.port = port;
19692 if (use_ssl) {
19693 client_options.host_name = host;
19694 }
19695
19697 use_ssl,
19700 path,
19701 origin,
19702 NULL,
19703 data_func,
19704 close_func,
19705 user_data);
19706}
19707
19708
19711 const struct mg_client_options *client_options,
19712 char *error_buffer,
19713 size_t error_buffer_size,
19714 const char *path,
19715 const char *origin,
19718 void *user_data)
19719{
19720 if (!client_options) {
19721 return NULL;
19722 }
19724 1,
19727 path,
19728 origin,
19729 NULL,
19730 data_func,
19731 close_func,
19732 user_data);
19733}
19734
19735
19738 int port,
19739 int use_ssl,
19740 char *error_buffer,
19741 size_t error_buffer_size,
19742 const char *path,
19743 const char *origin,
19744 const char *extensions,
19747 void *user_data)
19748{
19750 memset(&client_options, 0, sizeof(client_options));
19751 client_options.host = host;
19752 client_options.port = port;
19753
19755 use_ssl,
19758 path,
19759 origin,
19760 extensions,
19761 data_func,
19762 close_func,
19763 user_data);
19764}
19765
19766
19769 const struct mg_client_options *client_options,
19770 char *error_buffer,
19771 size_t error_buffer_size,
19772 const char *path,
19773 const char *origin,
19774 const char *extensions,
19777 void *user_data)
19778{
19779 if (!client_options) {
19780 return NULL;
19781 }
19783 1,
19786 path,
19787 origin,
19788 extensions,
19789 data_func,
19790 close_func,
19791 user_data);
19792}
19793
19794
19795/* Prepare connection data structure */
19796static void
19798{
19799 /* Is keep alive allowed by the server */
19800 int keep_alive_enabled =
19801 !mg_strcasecmp(conn->dom_ctx->config[ENABLE_KEEP_ALIVE], "yes");
19802
19803 if (!keep_alive_enabled) {
19804 conn->must_close = 1;
19805 }
19806
19807 /* Important: on new connection, reset the receiving buffer. Credit
19808 * goes to crule42. */
19809 conn->data_len = 0;
19810 conn->handled_requests = 0;
19812 conn->request_info.acceptedWebSocketSubprotocol = NULL;
19814
19815#if defined(USE_SERVER_STATS)
19816 conn->conn_state = 2; /* init */
19817#endif
19818
19819 /* call the init_connection callback if assigned */
19820 if (conn->phys_ctx->callbacks.init_connection != NULL) {
19821 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
19822 void *conn_data = NULL;
19823 conn->phys_ctx->callbacks.init_connection(conn, &conn_data);
19824 mg_set_user_connection_data(conn, conn_data);
19825 }
19826 }
19827}
19828
19829
19830/* Process a connection - may handle multiple requests
19831 * using the same connection.
19832 * Must be called with a valid connection (conn and
19833 * conn->phys_ctx must be valid).
19834 */
19835static void
19837{
19838 struct mg_request_info *ri = &conn->request_info;
19840 char ebuf[100];
19841 const char *hostend;
19842 int reqerr, uri_type;
19843
19844#if defined(USE_SERVER_STATS)
19845 ptrdiff_t mcon = mg_atomic_inc(&(conn->phys_ctx->active_connections));
19846 mg_atomic_add(&(conn->phys_ctx->total_connections), 1);
19847 mg_atomic_max(&(conn->phys_ctx->max_active_connections), mcon);
19848#endif
19849
19850 DEBUG_TRACE("Start processing connection from %s",
19851 conn->request_info.remote_addr);
19852
19853 /* Loop over multiple requests sent using the same connection
19854 * (while "keep alive"). */
19855 do {
19856 DEBUG_TRACE("calling get_request (%i times for this connection)",
19857 conn->handled_requests + 1);
19858
19859#if defined(USE_SERVER_STATS)
19860 conn->conn_state = 3; /* ready */
19861#endif
19862
19863 if (!get_request(conn, ebuf, sizeof(ebuf), &reqerr)) {
19864 /* The request sent by the client could not be understood by
19865 * the server, or it was incomplete or a timeout. Send an
19866 * error message and close the connection. */
19867 if (reqerr > 0) {
19868 DEBUG_ASSERT(ebuf[0] != '\0');
19869 mg_send_http_error(conn, reqerr, "%s", ebuf);
19870 }
19871
19872 } else if (strcmp(ri->http_version, "1.0")
19873 && strcmp(ri->http_version, "1.1")) {
19874 /* HTTP/2 is not allowed here */
19875 mg_snprintf(conn,
19876 NULL, /* No truncation check for ebuf */
19877 ebuf,
19878 sizeof(ebuf),
19879 "Bad HTTP version: [%s]",
19880 ri->http_version);
19881 mg_send_http_error(conn, 505, "%s", ebuf);
19882 }
19883
19884 if (ebuf[0] == '\0') {
19885 uri_type = get_uri_type(conn->request_info.request_uri);
19886 switch (uri_type) {
19887 case 1:
19888 /* Asterisk */
19889 conn->request_info.local_uri_raw = 0;
19890 /* TODO: Deal with '*'. */
19891 break;
19892 case 2:
19893 /* relative uri */
19894 conn->request_info.local_uri_raw =
19895 conn->request_info.request_uri;
19896 break;
19897 case 3:
19898 case 4:
19899 /* absolute uri (with/without port) */
19901 conn->request_info.request_uri, conn);
19902 if (hostend) {
19903 conn->request_info.local_uri_raw = hostend;
19904 } else {
19905 conn->request_info.local_uri_raw = NULL;
19906 }
19907 break;
19908 default:
19909 mg_snprintf(conn,
19910 NULL, /* No truncation check for ebuf */
19911 ebuf,
19912 sizeof(ebuf),
19913 "Invalid URI");
19914 mg_send_http_error(conn, 400, "%s", ebuf);
19915 conn->request_info.local_uri_raw = NULL;
19916 break;
19917 }
19918 conn->request_info.local_uri =
19919 (char *)conn->request_info.local_uri_raw;
19920 }
19921
19922 if (ebuf[0] != '\0') {
19923 conn->protocol_type = -1;
19924
19925 } else {
19926 /* HTTP/1 allows protocol upgrade */
19928
19929 if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
19930 /* This will occur, if a HTTP/1.1 request should be upgraded
19931 * to HTTP/2 - but not if HTTP/2 is negotiated using ALPN.
19932 * Since most (all?) major browsers only support HTTP/2 using
19933 * ALPN, this is hard to test and very low priority.
19934 * Deactivate it (at least for now).
19935 */
19937 }
19938 }
19939
19940 DEBUG_TRACE("http: %s, error: %s",
19941 (ri->http_version ? ri->http_version : "none"),
19942 (ebuf[0] ? ebuf : "none"));
19943
19944 if (ebuf[0] == '\0') {
19945 if (conn->request_info.local_uri) {
19946
19947 /* handle request to local server */
19949
19950 } else {
19951 /* TODO: handle non-local request (PROXY) */
19952 conn->must_close = 1;
19953 }
19954 } else {
19955 conn->must_close = 1;
19956 }
19957
19958 /* Response complete. Free header buffer */
19960
19961 if (ri->remote_user != NULL) {
19962 mg_free((void *)ri->remote_user);
19963 /* Important! When having connections with and without auth
19964 * would cause double free and then crash */
19965 ri->remote_user = NULL;
19966 }
19967
19968 /* NOTE(lsm): order is important here. should_keep_alive() call
19969 * is using parsed request, which will be invalid after
19970 * memmove's below.
19971 * Therefore, memorize should_keep_alive() result now for later
19972 * use in loop exit condition. */
19973 /* Enable it only if this request is completely discardable. */
19974 keep_alive = STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)
19975 && should_keep_alive(conn) && (conn->content_len >= 0)
19976 && (conn->request_len > 0)
19977 && ((conn->is_chunked == 4)
19978 || (!conn->is_chunked
19979 && ((conn->consumed_content == conn->content_len)
19980 || ((conn->request_len + conn->content_len)
19981 <= conn->data_len))))
19982 && (conn->protocol_type == PROTOCOL_TYPE_HTTP1);
19983
19984 if (keep_alive) {
19985 /* Discard all buffered data for this request */
19986 discard_len =
19987 ((conn->request_len + conn->content_len) < conn->data_len)
19988 ? (int)(conn->request_len + conn->content_len)
19989 : conn->data_len;
19990 conn->data_len -= discard_len;
19991
19992 if (conn->data_len > 0) {
19993 DEBUG_TRACE("discard_len = %d", discard_len);
19994 memmove(conn->buf,
19995 conn->buf + discard_len,
19996 (size_t)conn->data_len);
19997 }
19998 }
19999
20000 DEBUG_ASSERT(conn->data_len >= 0);
20001 DEBUG_ASSERT(conn->data_len <= conn->buf_size);
20002
20003 if ((conn->data_len < 0) || (conn->data_len > conn->buf_size)) {
20004 DEBUG_TRACE("internal error: data_len = %li, buf_size = %li",
20005 (long int)conn->data_len,
20006 (long int)conn->buf_size);
20007 break;
20008 }
20009 conn->handled_requests++;
20010 } while (keep_alive);
20011
20012 DEBUG_TRACE("Done processing connection from %s (%f sec)",
20013 conn->request_info.remote_addr,
20014 difftime(time(NULL), conn->conn_birth_time));
20015
20016 close_connection(conn);
20017
20018#if defined(USE_SERVER_STATS)
20019 mg_atomic_add(&(conn->phys_ctx->total_requests), conn->handled_requests);
20020 mg_atomic_dec(&(conn->phys_ctx->active_connections));
20021#endif
20022}
20023
20024static int
20026 int only_if_no_idle_threads); /* forward declaration */
20027
20028#if defined(ALTERNATIVE_QUEUE)
20029
20030static void
20031produce_socket(struct mg_context *ctx, const struct socket *sp)
20032{
20033 unsigned int i;
20034
20036 ctx, 1); /* will start a worker-thread only if there aren't currently
20037 any idle worker-threads */
20038
20039 while (!ctx->stop_flag) {
20040 for (i = 0; i < ctx->spawned_worker_threads; i++) {
20041 /* find a free worker slot and signal it */
20042 if (ctx->client_socks[i].in_use == 2) {
20043 (void)pthread_mutex_lock(&ctx->thread_mutex);
20044 if ((ctx->client_socks[i].in_use == 2) && !ctx->stop_flag) {
20045 ctx->client_socks[i] = *sp;
20046 ctx->client_socks[i].in_use = 1;
20047 /* socket has been moved to the consumer */
20049 (void)event_signal(ctx->client_wait_events[i]);
20050 return;
20051 }
20053 }
20054 }
20055 /* queue is full */
20056 mg_sleep(1);
20057 }
20058 /* must consume */
20059 set_blocking_mode(sp->sock);
20060 closesocket(sp->sock);
20061}
20062
20063
20064static int
20065consume_socket(struct mg_context *ctx,
20066 struct socket *sp,
20067 int thread_index,
20069{
20070 DEBUG_TRACE("%s", "going idle");
20071 (void)pthread_mutex_lock(&ctx->thread_mutex);
20073 == 0) { /* first call only: the master-thread pre-incremented this
20074 before he spawned us */
20076 }
20077 ctx->client_socks[thread_index].in_use = 2;
20079
20080 event_wait(ctx->client_wait_events[thread_index]);
20081
20082 (void)pthread_mutex_lock(&ctx->thread_mutex);
20083 *sp = ctx->client_socks[thread_index];
20084 if (ctx->stop_flag) {
20086 if (sp->in_use == 1) {
20087 /* must consume */
20088 set_blocking_mode(sp->sock);
20089 closesocket(sp->sock);
20090 }
20091 return 0;
20092 }
20095 if (sp->in_use == 1) {
20096 DEBUG_TRACE("grabbed socket %d, going busy", sp->sock);
20097 return 1;
20098 }
20099 /* must not reach here */
20100 DEBUG_ASSERT(0);
20101 return 0;
20102}
20103
20104#else /* ALTERNATIVE_QUEUE */
20105
20106/* Worker threads take accepted socket from the queue */
20107static int
20109 struct socket *sp,
20110 int thread_index,
20112{
20113 (void)thread_index;
20114
20115 DEBUG_TRACE("%s", "going idle");
20116 (void)pthread_mutex_lock(&ctx->thread_mutex);
20118 == 0) { /* first call only: the master-thread pre-incremented this
20119 before he spawned us */
20121 }
20122
20123 /* If the queue is empty, wait. We're idle at this point. */
20124 while ((ctx->sq_head == ctx->sq_tail)
20125 && (STOP_FLAG_IS_ZERO(&ctx->stop_flag))) {
20127 }
20128
20129 /* If we're stopping, sq_head may be equal to sq_tail. */
20130 if (ctx->sq_head > ctx->sq_tail) {
20131 /* Copy socket from the queue and increment tail */
20132 *sp = ctx->squeue[ctx->sq_tail % ctx->sq_size];
20133 ctx->sq_tail++;
20134
20135 DEBUG_TRACE("grabbed socket %d, going busy", sp ? sp->sock : -1);
20136
20137 /* Wrap pointers if needed */
20138 while (ctx->sq_tail > ctx->sq_size) {
20139 ctx->sq_tail -= ctx->sq_size;
20140 ctx->sq_head -= ctx->sq_size;
20141 }
20142 }
20143
20144 (void)pthread_cond_signal(&ctx->sq_empty);
20145
20148
20149 return STOP_FLAG_IS_ZERO(&ctx->stop_flag);
20150}
20151
20152
20153/* Master thread adds accepted socket to a queue */
20154static void
20155produce_socket(struct mg_context *ctx, const struct socket *sp)
20156{
20157 int queue_filled;
20158
20159 (void)pthread_mutex_lock(&ctx->thread_mutex);
20160
20161 queue_filled = ctx->sq_head - ctx->sq_tail;
20162
20163 /* If the queue is full, wait */
20164 while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
20165 && (queue_filled >= ctx->sq_size)) {
20166 ctx->sq_blocked = 1; /* Status information: All threads busy */
20167#if defined(USE_SERVER_STATS)
20168 if (queue_filled > ctx->sq_max_fill) {
20169 ctx->sq_max_fill = queue_filled;
20170 }
20171#endif
20172 (void)pthread_cond_wait(&ctx->sq_empty, &ctx->thread_mutex);
20173 ctx->sq_blocked = 0; /* Not blocked now */
20174 queue_filled = ctx->sq_head - ctx->sq_tail;
20175 }
20176
20177 if (queue_filled < ctx->sq_size) {
20178 /* Copy socket to the queue and increment head */
20179 ctx->squeue[ctx->sq_head % ctx->sq_size] = *sp;
20180 ctx->sq_head++;
20181 DEBUG_TRACE("queued socket %d", sp ? sp->sock : -1);
20182 }
20183
20184 queue_filled = ctx->sq_head - ctx->sq_tail;
20185#if defined(USE_SERVER_STATS)
20186 if (queue_filled > ctx->sq_max_fill) {
20187 ctx->sq_max_fill = queue_filled;
20188 }
20189#endif
20190
20191 (void)pthread_cond_signal(&ctx->sq_full);
20193
20195 ctx, 1); /* will start a worker-thread only if there aren't currently
20196 any idle worker-threads */
20197}
20198#endif /* ALTERNATIVE_QUEUE */
20199
20200
20201static void
20203{
20204 struct mg_context *ctx = conn->phys_ctx;
20205 int thread_index;
20206 struct mg_workerTLS tls;
20208
20209 mg_set_thread_name("worker");
20210
20211 tls.is_master = 0;
20212 tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
20213#if defined(_WIN32)
20214 tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
20215#endif
20216
20217 /* Initialize thread local storage before calling any callback */
20219
20220 /* Check if there is a user callback */
20221 if (ctx->callbacks.init_thread) {
20222 /* call init_thread for a worker thread (type 1), and store the
20223 * return value */
20224 tls.user_ptr = ctx->callbacks.init_thread(ctx, 1);
20225 } else {
20226 /* No callback: set user pointer to NULL */
20227 tls.user_ptr = NULL;
20228 }
20229
20230 /* Connection structure has been pre-allocated */
20231 thread_index = (int)(conn - ctx->worker_connections);
20232 if ((thread_index < 0)
20233 || ((unsigned)thread_index >= (unsigned)ctx->cfg_max_worker_threads)) {
20235 "Internal error: Invalid worker index %i",
20236 thread_index);
20237 return;
20238 }
20239
20240 /* Request buffers are not pre-allocated. They are private to the
20241 * request and do not contain any state information that might be
20242 * of interest to anyone observing a server status. */
20243 conn->buf = (char *)mg_malloc_ctx(ctx->max_request_size, conn->phys_ctx);
20244 if (conn->buf == NULL) {
20246 ctx,
20247 "Out of memory: Cannot allocate buffer for worker %i",
20248 thread_index);
20249 return;
20250 }
20251 conn->buf_size = (int)ctx->max_request_size;
20252
20253 conn->dom_ctx = &(ctx->dd); /* Use default domain and default host */
20254
20255 conn->tls_user_ptr = tls.user_ptr; /* store ptr for quick access */
20256
20257 conn->request_info.user_data = ctx->user_data;
20258 /* Allocate a mutex for this connection to allow communication both
20259 * within the request handler and from elsewhere in the application
20260 */
20261 if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) {
20262 mg_free(conn->buf);
20263 mg_cry_ctx_internal(ctx, "%s", "Cannot create mutex");
20264 return;
20265 }
20266
20267#if defined(USE_SERVER_STATS)
20268 conn->conn_state = 1; /* not consumed */
20269#endif
20270
20271 /* Call consume_socket() even when ctx->stop_flag > 0, to let it
20272 * signal sq_empty condvar to wake up the master waiting in
20273 * produce_socket() */
20274 while (consume_socket(
20277
20278 /* New connections must start with new protocol negotiation */
20279 tls.alpn_proto = NULL;
20280
20281#if defined(USE_SERVER_STATS)
20282 conn->conn_close_time = 0;
20283#endif
20284 conn->conn_birth_time = time(NULL);
20285
20286 /* Fill in IP, port info early so even if SSL setup below fails,
20287 * error handler would have the corresponding info.
20288 * Thanks to Johannes Winkelmann for the patch.
20289 */
20290 conn->request_info.remote_port =
20291 ntohs(USA_IN_PORT_UNSAFE(&conn->client.rsa));
20292
20293 conn->request_info.server_port =
20294 ntohs(USA_IN_PORT_UNSAFE(&conn->client.lsa));
20295
20296 sockaddr_to_string(conn->request_info.remote_addr,
20297 sizeof(conn->request_info.remote_addr),
20298 &conn->client.rsa);
20299
20300 DEBUG_TRACE("Incoming %sconnection from %s",
20301 (conn->client.is_ssl ? "SSL " : ""),
20302 conn->request_info.remote_addr);
20303
20304 conn->request_info.is_ssl = conn->client.is_ssl;
20305
20306 if (conn->client.is_ssl) {
20307
20308#if defined(USE_MBEDTLS)
20309 /* HTTPS connection */
20310 if (mbed_ssl_accept(&(conn->ssl),
20311 conn->dom_ctx->ssl_ctx,
20312 (int *)&(conn->client.sock),
20313 conn->phys_ctx)
20314 == 0) {
20315 /* conn->dom_ctx is set in get_request */
20316 /* process HTTPS connection */
20317 init_connection(conn);
20321 } else {
20322 /* make sure the connection is cleaned up on SSL failure */
20323 close_connection(conn);
20324 }
20325
20326#elif defined(USE_GNUTLS)
20327 /* HTTPS connection */
20328 if (gtls_ssl_accept(&(conn->ssl),
20329 conn->dom_ctx->ssl_ctx,
20330 conn->client.sock,
20331 conn->phys_ctx)
20332 == 0) {
20333 /* conn->dom_ctx is set in get_request */
20334 /* process HTTPS connection */
20335 init_connection(conn);
20339 } else {
20340 /* make sure the connection is cleaned up on SSL failure */
20341 close_connection(conn);
20342 }
20343
20344#elif !defined(NO_SSL)
20345 /* HTTPS connection */
20346 if (sslize(conn, SSL_accept, NULL)) {
20347 /* conn->dom_ctx is set in get_request */
20348
20349 /* Get SSL client certificate information (if set) */
20350 struct mg_client_cert client_cert;
20351 if (ssl_get_client_cert_info(conn, &client_cert)) {
20352 conn->request_info.client_cert = &client_cert;
20353 }
20354
20355 /* process HTTPS connection */
20356#if defined(USE_HTTP2)
20357 if ((tls.alpn_proto != NULL)
20358 && (!memcmp(tls.alpn_proto, "\x02h2", 3))) {
20359 /* process HTTPS/2 connection */
20360 init_connection(conn);
20363 conn->content_len =
20364 -1; /* content length is not predefined */
20365 conn->is_chunked = 0; /* HTTP2 is never chunked */
20367 } else
20368#endif
20369 {
20370 /* process HTTPS/1.x or WEBSOCKET-SECURE connection */
20371 init_connection(conn);
20373 /* Start with HTTP, WS will be an "upgrade" request later */
20376 }
20377
20378 /* Free client certificate info */
20379 if (conn->request_info.client_cert) {
20380 mg_free((void *)(conn->request_info.client_cert->subject));
20381 mg_free((void *)(conn->request_info.client_cert->issuer));
20382 mg_free((void *)(conn->request_info.client_cert->serial));
20383 mg_free((void *)(conn->request_info.client_cert->finger));
20384 /* Free certificate memory */
20385 X509_free(
20386 (X509 *)conn->request_info.client_cert->peer_cert);
20387 conn->request_info.client_cert->peer_cert = 0;
20388 conn->request_info.client_cert->subject = 0;
20389 conn->request_info.client_cert->issuer = 0;
20390 conn->request_info.client_cert->serial = 0;
20391 conn->request_info.client_cert->finger = 0;
20392 conn->request_info.client_cert = 0;
20393 }
20394 } else {
20395 /* make sure the connection is cleaned up on SSL failure */
20396 close_connection(conn);
20397 }
20398#endif
20399
20400 } else {
20401 /* process HTTP connection */
20402 init_connection(conn);
20404 /* Start with HTTP, WS will be an "upgrade" request later */
20407 }
20408
20409 DEBUG_TRACE("%s", "Connection closed");
20410
20411#if defined(USE_SERVER_STATS)
20412 conn->conn_close_time = time(NULL);
20413#endif
20414 }
20415
20416 /* Call exit thread user callback */
20417 if (ctx->callbacks.exit_thread) {
20418 ctx->callbacks.exit_thread(ctx, 1, tls.user_ptr);
20419 }
20420
20421 /* delete thread local storage objects */
20423#if defined(_WIN32)
20424 CloseHandle(tls.pthread_cond_helper_mutex);
20425#endif
20427
20428 /* Free the request buffer. */
20429 conn->buf_size = 0;
20430 mg_free(conn->buf);
20431 conn->buf = NULL;
20432
20433 /* Free cleaned URI (if any) */
20434 if (conn->request_info.local_uri != conn->request_info.local_uri_raw) {
20435 mg_free((void *)conn->request_info.local_uri);
20436 conn->request_info.local_uri = NULL;
20437 }
20438
20439#if defined(USE_SERVER_STATS)
20440 conn->conn_state = 9; /* done */
20441#endif
20442
20443 DEBUG_TRACE("%s", "exiting");
20444}
20445
20446
20447/* Threads have different return types on Windows and Unix. */
20448#if defined(_WIN32)
20449static unsigned __stdcall worker_thread(void *thread_func_param)
20450{
20452 return 0;
20453}
20454#else
20455static void *
20457{
20458#if !defined(__ZEPHYR__)
20459 struct sigaction sa;
20460
20461 /* Ignore SIGPIPE */
20462 memset(&sa, 0, sizeof(sa));
20463 sa.sa_handler = SIG_IGN;
20464 sigaction(SIGPIPE, &sa, NULL);
20465#endif
20466
20468 return NULL;
20469}
20470#endif /* _WIN32 */
20471
20472
20473/* This is an internal function, thus all arguments are expected to be
20474 * valid - a NULL check is not required. */
20475static void
20477{
20478 struct socket so;
20480 socklen_t len = sizeof(so.rsa);
20481#if !defined(__ZEPHYR__)
20482 int on = 1;
20483#endif
20484 memset(&so, 0, sizeof(so));
20485
20486 if ((so.sock = accept(listener->sock, &so.rsa.sa, &len))
20487 == INVALID_SOCKET) {
20488 } else if (check_acl(ctx, &so.rsa) != 1) {
20489 sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
20491 "%s: %s is not allowed to connect",
20492 __func__,
20493 src_addr);
20494 closesocket(so.sock);
20495 } else {
20496 /* Put so socket structure into the queue */
20497 DEBUG_TRACE("Accepted socket %d", (int)so.sock);
20498 set_close_on_exec(so.sock, NULL, ctx);
20499 so.is_ssl = listener->is_ssl;
20500 so.ssl_redir = listener->ssl_redir;
20501 so.is_optional = listener->is_optional;
20502 if (getsockname(so.sock, &so.lsa.sa, &len) != 0) {
20504 "%s: getsockname() failed: %s",
20505 __func__,
20506 strerror(ERRNO));
20507 }
20508
20509#if !defined(__ZEPHYR__)
20510 if ((so.lsa.sa.sa_family == AF_INET)
20511 || (so.lsa.sa.sa_family == AF_INET6)) {
20512 /* Set TCP keep-alive for TCP sockets (IPv4 and IPv6).
20513 * This is needed because if HTTP-level keep-alive
20514 * is enabled, and client resets the connection, server won't get
20515 * TCP FIN or RST and will keep the connection open forever. With
20516 * TCP keep-alive, next keep-alive handshake will figure out that
20517 * the client is down and will close the server end.
20518 * Thanks to Igor Klopov who suggested the patch. */
20519 if (setsockopt(so.sock,
20520 SOL_SOCKET,
20522 (SOCK_OPT_TYPE)&on,
20523 sizeof(on))
20524 != 0) {
20526 ctx,
20527 "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
20528 __func__,
20529 strerror(ERRNO));
20530 }
20531 }
20532#endif
20533
20534 /* Disable TCP Nagle's algorithm. Normally TCP packets are coalesced
20535 * to effectively fill up the underlying IP packet payload and
20536 * reduce the overhead of sending lots of small buffers. However
20537 * this hurts the server's throughput (ie. operations per second)
20538 * when HTTP 1.1 persistent connections are used and the responses
20539 * are relatively small (eg. less than 1400 bytes).
20540 */
20541 if ((ctx->dd.config[CONFIG_TCP_NODELAY] != NULL)
20542 && (!strcmp(ctx->dd.config[CONFIG_TCP_NODELAY], "1"))) {
20543 if (set_tcp_nodelay(&so, 1) != 0) {
20545 ctx,
20546 "%s: setsockopt(IPPROTO_TCP TCP_NODELAY) failed: %s",
20547 __func__,
20548 strerror(ERRNO));
20549 }
20550 }
20551
20552 /* The "non blocking" property should already be
20553 * inherited from the parent socket. Set it for
20554 * non-compliant socket implementations. */
20556
20557 so.in_use = 0;
20558 produce_socket(ctx, &so);
20559 }
20560}
20561
20562
20563static void
20565{
20566 struct mg_workerTLS tls;
20567 struct mg_pollfd *pfd;
20568 unsigned int i;
20569 unsigned int workerthreadcount;
20570
20571 if (!ctx || !ctx->listening_socket_fds) {
20572 return;
20573 }
20574
20575 mg_set_thread_name("master");
20576
20577 /* Increase priority of the master thread */
20578#if defined(_WIN32)
20580#elif defined(USE_MASTER_THREAD_PRIORITY)
20583 if ((min_prio >= 0) && (max_prio >= 0)
20586 struct sched_param sched_param = {0};
20587 sched_param.sched_priority = (USE_MASTER_THREAD_PRIORITY);
20589 }
20590#endif
20591
20592 /* Initialize thread local storage */
20593#if defined(_WIN32)
20594 tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
20595#endif
20596 tls.is_master = 1;
20598
20599 if (ctx->callbacks.init_thread) {
20600 /* Callback for the master thread (type 0) */
20601 tls.user_ptr = ctx->callbacks.init_thread(ctx, 0);
20602 } else {
20603 tls.user_ptr = NULL;
20604 }
20605
20606 /* Lua background script "start" event */
20607#if defined(USE_LUA)
20608 if (ctx->lua_background_state) {
20609 lua_State *lstate = (lua_State *)ctx->lua_background_state;
20610 pthread_mutex_lock(&ctx->lua_bg_mutex);
20611
20612 /* call "start()" in Lua */
20613 lua_getglobal(lstate, "start");
20614 if (lua_type(lstate, -1) == LUA_TFUNCTION) {
20615 int ret = lua_pcall(lstate, /* args */ 0, /* results */ 0, 0);
20616 if (ret != 0) {
20617 struct mg_connection fc;
20618 lua_cry(fake_connection(&fc, ctx),
20619 ret,
20620 lstate,
20621 "lua_background_script",
20622 "start");
20623 }
20624 } else {
20625 lua_pop(lstate, 1);
20626 }
20627
20628 /* determine if there is a "log()" function in Lua background script */
20629 lua_getglobal(lstate, "log");
20630 if (lua_type(lstate, -1) == LUA_TFUNCTION) {
20631 ctx->lua_bg_log_available = 1;
20632 }
20633 lua_pop(lstate, 1);
20634
20635 pthread_mutex_unlock(&ctx->lua_bg_mutex);
20636 }
20637#endif
20638
20639 /* Server starts *now* */
20640 ctx->start_time = time(NULL);
20641
20642 /* Server accept loop */
20644 while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
20645 for (i = 0; i < ctx->num_listening_sockets; i++) {
20646 pfd[i].fd = ctx->listening_sockets[i].sock;
20647 pfd[i].events = POLLIN;
20648 }
20649
20650 /* We listen on this socket just so that mg_stop() can cause mg_poll()
20651 * to return ASAP. Don't worry, we did allocate an extra slot at the end
20652 * of listening_socket_fds[] just to hold this
20653 */
20654 pfd[ctx->num_listening_sockets].fd =
20656 pfd[ctx->num_listening_sockets].events = POLLIN;
20657
20658 if (mg_poll(pfd,
20660 + 1, // +1 for the thread_shutdown_notification_socket
20662 &(ctx->stop_flag))
20663 > 0) {
20664 for (i = 0; i < ctx->num_listening_sockets; i++) {
20665 /* NOTE(lsm): on QNX, poll() returns POLLRDNORM after the
20666 * successful poll, and POLLIN is defined as
20667 * (POLLRDNORM | POLLRDBAND)
20668 * Therefore, we're checking pfd[i].revents & POLLIN, not
20669 * pfd[i].revents == POLLIN. */
20670 if (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
20671 && (pfd[i].revents & POLLIN)) {
20673 }
20674 }
20675 }
20676 }
20677
20678 /* Here stop_flag is 1 - Initiate shutdown. */
20679 DEBUG_TRACE("%s", "stopping workers");
20680
20681 /* Stop signal received: somebody called mg_stop. Quit. */
20683
20684 /* Wakeup workers that are waiting for connections to handle. */
20685#if defined(ALTERNATIVE_QUEUE)
20686 for (i = 0; i < ctx->spawned_worker_threads; i++) {
20687 event_signal(ctx->client_wait_events[i]);
20688 }
20689#else
20690 (void)pthread_mutex_lock(&ctx->thread_mutex);
20693#endif
20694
20695 /* Join all worker threads to avoid leaking threads. */
20697 for (i = 0; i < workerthreadcount; i++) {
20698 if (ctx->worker_threadids[i] != 0) {
20700 }
20701 }
20702
20703#if defined(USE_LUA)
20704 /* Free Lua state of lua background task */
20705 if (ctx->lua_background_state) {
20706 lua_State *lstate = (lua_State *)ctx->lua_background_state;
20707 ctx->lua_bg_log_available = 0;
20708
20709 /* call "stop()" in Lua */
20710 pthread_mutex_lock(&ctx->lua_bg_mutex);
20711 lua_getglobal(lstate, "stop");
20712 if (lua_type(lstate, -1) == LUA_TFUNCTION) {
20713 int ret = lua_pcall(lstate, /* args */ 0, /* results */ 0, 0);
20714 if (ret != 0) {
20715 struct mg_connection fc;
20716 lua_cry(fake_connection(&fc, ctx),
20717 ret,
20718 lstate,
20719 "lua_background_script",
20720 "stop");
20721 }
20722 }
20723 DEBUG_TRACE("Close Lua background state %p", lstate);
20725
20726 ctx->lua_background_state = 0;
20727 pthread_mutex_unlock(&ctx->lua_bg_mutex);
20728 }
20729#endif
20730
20731 DEBUG_TRACE("%s", "exiting");
20732
20733 /* call exit thread callback */
20734 if (ctx->callbacks.exit_thread) {
20735 /* Callback for the master thread (type 0) */
20736 ctx->callbacks.exit_thread(ctx, 0, tls.user_ptr);
20737 }
20738
20739#if defined(_WIN32)
20740 CloseHandle(tls.pthread_cond_helper_mutex);
20741#endif
20743
20744 /* Signal mg_stop() that we're done.
20745 * WARNING: This must be the very last thing this
20746 * thread does, as ctx becomes invalid after this line. */
20747 STOP_FLAG_ASSIGN(&ctx->stop_flag, 2);
20748}
20749
20750
20751/* Threads have different return types on Windows and Unix. */
20752#if defined(_WIN32)
20753static unsigned __stdcall master_thread(void *thread_func_param)
20754{
20756 return 0;
20757}
20758#else
20759static void *
20761{
20762#if !defined(__ZEPHYR__)
20763 struct sigaction sa;
20764
20765 /* Ignore SIGPIPE */
20766 memset(&sa, 0, sizeof(sa));
20767 sa.sa_handler = SIG_IGN;
20768 sigaction(SIGPIPE, &sa, NULL);
20769#endif
20770
20772 return NULL;
20773}
20774#endif /* _WIN32 */
20775
20776
20777static void
20779{
20780 int i;
20781 struct mg_handler_info *tmp_rh;
20782
20783 if (ctx == NULL) {
20784 return;
20785 }
20786
20787 /* Call user callback */
20788 if (ctx->callbacks.exit_context) {
20789 ctx->callbacks.exit_context(ctx);
20790 }
20791
20792 /* All threads exited, no sync is needed. Destroy thread mutex and
20793 * condvars
20794 */
20796
20797#if defined(ALTERNATIVE_QUEUE)
20798 mg_free(ctx->client_socks);
20799 if (ctx->client_wait_events != NULL) {
20800 for (i = 0; (unsigned)i < ctx->spawned_worker_threads; i++) {
20801 event_destroy(ctx->client_wait_events[i]);
20802 }
20803 mg_free(ctx->client_wait_events);
20804 }
20805#else
20806 (void)pthread_cond_destroy(&ctx->sq_empty);
20807 (void)pthread_cond_destroy(&ctx->sq_full);
20808 mg_free(ctx->squeue);
20809#endif
20810
20811 /* Destroy other context global data structures mutex */
20813
20814#if defined(USE_LUA)
20815 (void)pthread_mutex_destroy(&ctx->lua_bg_mutex);
20816#endif
20817
20818 /* Deallocate shutdown-triggering socket-pair */
20819 if (ctx->user_shutdown_notification_socket >= 0) {
20821 }
20824 }
20825
20826 /* Deallocate config parameters */
20827 for (i = 0; i < NUM_OPTIONS; i++) {
20828 if (ctx->dd.config[i] != NULL) {
20829#if defined(_MSC_VER)
20830#pragma warning(suppress : 6001)
20831#endif
20832 mg_free(ctx->dd.config[i]);
20833 }
20834 }
20835
20836 /* Deallocate request handlers */
20837 while (ctx->dd.handlers) {
20838 tmp_rh = ctx->dd.handlers;
20839 ctx->dd.handlers = tmp_rh->next;
20840 mg_free(tmp_rh->uri);
20841 mg_free(tmp_rh);
20842 }
20843
20844#if defined(USE_MBEDTLS)
20845 if (ctx->dd.ssl_ctx != NULL) {
20846 mbed_sslctx_uninit(ctx->dd.ssl_ctx);
20847 mg_free(ctx->dd.ssl_ctx);
20848 ctx->dd.ssl_ctx = NULL;
20849 }
20850
20851#elif defined(USE_GNUTLS)
20852 if (ctx->dd.ssl_ctx != NULL) {
20853 gtls_sslctx_uninit(ctx->dd.ssl_ctx);
20854 mg_free(ctx->dd.ssl_ctx);
20855 ctx->dd.ssl_ctx = NULL;
20856 }
20857
20858#elif !defined(NO_SSL)
20859 /* Deallocate SSL context */
20860 if (ctx->dd.ssl_ctx != NULL) {
20861 void *ssl_ctx = (void *)ctx->dd.ssl_ctx;
20862 int callback_ret =
20863 (ctx->callbacks.external_ssl_ctx == NULL)
20864 ? 0
20865 : (ctx->callbacks.external_ssl_ctx(&ssl_ctx, ctx->user_data));
20866
20867 if (callback_ret == 0) {
20868 SSL_CTX_free(ctx->dd.ssl_ctx);
20869 }
20870 /* else: ignore error and omit SSL_CTX_free in case
20871 * callback_ret is 1 */
20872 }
20873#endif /* !NO_SSL */
20874
20875 /* Deallocate worker thread ID array */
20877
20878 /* Deallocate worker thread ID array */
20880
20881 /* deallocate system name string */
20882 mg_free(ctx->systemName);
20883
20884 /* Deallocate context itself */
20885 mg_free(ctx);
20886}
20887
20888
20889CIVETWEB_API void
20891{
20892 pthread_t mt;
20893 if (!ctx) {
20894 return;
20895 }
20896
20897 /* We don't use a lock here. Calling mg_stop with the same ctx from
20898 * two threads is not allowed. */
20899 mt = ctx->masterthreadid;
20900 if (mt == 0) {
20901 return;
20902 }
20903
20904 ctx->masterthreadid = 0;
20905
20906 /* Set stop flag, so all threads know they have to exit. */
20907 STOP_FLAG_ASSIGN(&ctx->stop_flag, 1);
20908
20909 /* Closing this socket will cause mg_poll() in all the I/O threads to return
20910 * immediately */
20913 -1; /* to avoid calling closesocket() again in free_context() */
20914
20915 /* Join timer thread */
20916#if defined(USE_TIMERS)
20917 timers_exit(ctx);
20918#endif
20919
20920 /* Wait until everything has stopped. */
20921 while (!STOP_FLAG_IS_TWO(&ctx->stop_flag)) {
20922 (void)mg_sleep(10);
20923 }
20924
20925 /* Wait to stop master thread */
20926 mg_join_thread(mt);
20927
20928 /* Close remaining Lua states */
20929#if defined(USE_LUA)
20930 lua_ctx_exit(ctx);
20931#endif
20932
20933 /* Free memory */
20934 free_context(ctx);
20935}
20936
20937
20938static void
20940{
20941#if defined(_WIN32)
20942 char name[128];
20943 DWORD dwVersion = 0;
20944 DWORD dwMajorVersion = 0;
20945 DWORD dwMinorVersion = 0;
20946 DWORD dwBuild = 0;
20947 BOOL wowRet, isWoW = FALSE;
20948
20949#if defined(_MSC_VER)
20950#pragma warning(push)
20951 /* GetVersion was declared deprecated */
20952#pragma warning(disable : 4996)
20953#endif
20954 dwVersion = GetVersion();
20955#if defined(_MSC_VER)
20956#pragma warning(pop)
20957#endif
20958
20961 dwBuild = ((dwVersion < 0x80000000) ? (DWORD)(HIWORD(dwVersion)) : 0);
20962 (void)dwBuild;
20963
20965
20966 sprintf(name,
20967 "Windows %u.%u%s",
20968 (unsigned)dwMajorVersion,
20969 (unsigned)dwMinorVersion,
20970 (wowRet ? (isWoW ? " (WoW64)" : "") : " (?)"));
20971
20973
20974#elif defined(__rtems__)
20975 *sysName = mg_strdup("RTEMS");
20976#elif defined(__ZEPHYR__)
20977 *sysName = mg_strdup("Zephyr OS");
20978#else
20979 struct utsname name;
20980 memset(&name, 0, sizeof(name));
20981 uname(&name);
20982 *sysName = mg_strdup(name.sysname);
20983#endif
20984}
20985
20986
20987static void
20988legacy_init(const char **options)
20989{
20990 const char *ports_option = config_options[LISTENING_PORTS].default_value;
20991
20992 if (options) {
20993 const char **run_options = options;
20994 const char *optname = config_options[LISTENING_PORTS].name;
20995
20996 /* Try to find the "listening_ports" option */
20997 while (*run_options) {
20998 if (!strcmp(*run_options, optname)) {
21000 }
21001 run_options += 2;
21002 }
21003 }
21004
21006 /* Initialize with SSL support */
21008 } else {
21009 /* Initialize without SSL support */
21011 }
21012}
21013
21014/* we'll assume it's only Windows that doesn't have socketpair() available */
21015#if !defined(HAVE_SOCKETPAIR) && !defined(_WIN32)
21016#define HAVE_SOCKETPAIR 1
21017#endif
21018
21019static int
21021{
21022 int temp[2] = {-1, -1};
21023 int asock = -1;
21024
21025 /** Default to unallocated */
21026 *sockA = -1;
21027 *sockB = -1;
21028
21029#if defined(HAVE_SOCKETPAIR)
21030 int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, temp);
21031 if (ret == 0) {
21032 *sockA = temp[0];
21033 *sockB = temp[1];
21036 }
21037 (void)asock; /* not used */
21038 return ret;
21039#else
21040 /** No socketpair() call is available, so we'll have to roll our own
21041 * implementation */
21043 if (asock >= 0) {
21044 struct sockaddr_in addr;
21045 struct sockaddr *pa = (struct sockaddr *)&addr;
21046 socklen_t addrLen = sizeof(addr);
21047
21048 memset(&addr, 0, sizeof(addr));
21049 addr.sin_family = AF_INET;
21050 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
21051 addr.sin_port = 0;
21052
21053 if ((bind(asock, pa, sizeof(addr)) == 0)
21054 && (getsockname(asock, pa, &addrLen) == 0)
21055 && (listen(asock, 1) == 0)) {
21056 temp[0] = socket(PF_INET, SOCK_STREAM, 0);
21057 if ((temp[0] >= 0) && (connect(temp[0], pa, sizeof(addr)) == 0)) {
21058 temp[1] = accept(asock, pa, &addrLen);
21059 if (temp[1] >= 0) {
21061 *sockA = temp[0];
21062 *sockB = temp[1];
21065 return 0; /* success! */
21066 }
21067 }
21068 }
21069 }
21070
21071 /* Cleanup */
21072 if (asock >= 0)
21074 if (temp[0] >= 0)
21075 closesocket(temp[0]);
21076 if (temp[1] >= 0)
21077 closesocket(temp[1]);
21078 return -1; /* fail! */
21079#endif
21080}
21081
21082static int
21084{
21085 const unsigned int i = ctx->spawned_worker_threads;
21086 if (i >= ctx->cfg_max_worker_threads) {
21087 return -1; /* Oops, we hit our worker-thread limit! No more worker
21088 threads, ever! */
21089 }
21090
21091 (void)pthread_mutex_lock(&ctx->thread_mutex);
21092#if defined(ALTERNATIVE_QUEUE)
21094#else
21097 > (unsigned)(ctx->sq_head - ctx->sq_tail))) {
21098#endif
21100 return -2; /* There are idle threads available, so no need to spawn a
21101 new worker thread now */
21102 }
21103 ctx->idle_worker_thread_count++; /* we do this here to avoid a race
21104 condition while the thread is starting
21105 up */
21107
21108 ctx->worker_connections[i].phys_ctx = ctx;
21110 &ctx->worker_connections[i],
21111 &ctx->worker_threadids[i]);
21112 if (ret == 0) {
21113 ctx->spawned_worker_threads++; /* note that we've filled another slot in
21114 the table */
21115 DEBUG_TRACE("Started worker_thread #%i", ctx->spawned_worker_threads);
21116 } else {
21117 (void)pthread_mutex_lock(&ctx->thread_mutex);
21118 ctx->idle_worker_thread_count--; /* whoops, roll-back on error */
21120 }
21121 return ret;
21122}
21123
21124CIVETWEB_API struct mg_context *
21125mg_start2(struct mg_init_data *init, struct mg_error_data *error)
21126{
21127 struct mg_context *ctx;
21128 const char *name, *value, *default_value;
21130 unsigned int i;
21131 int itmp;
21132 void (*exit_callback)(const struct mg_context *ctx) = 0;
21133 const char **options =
21134 ((init != NULL) ? (init->configuration_options) : (NULL));
21135
21136 struct mg_workerTLS tls;
21137
21138 if (error != NULL) {
21139 error->code = MG_ERROR_DATA_CODE_OK;
21140 error->code_sub = 0;
21141 if (error->text_buffer_size > 0) {
21142 *error->text = 0;
21143 }
21144 }
21145
21146 if (mg_init_library_called == 0) {
21147 /* Legacy INIT, if mg_start is called without mg_init_library.
21148 * Note: This will cause a memory leak when unloading the library.
21149 */
21150 legacy_init(options);
21151 }
21152 if (mg_init_library_called == 0) {
21153 if (error != NULL) {
21156 NULL, /* No truncation check for error buffers */
21157 error->text,
21158 error->text_buffer_size,
21159 "%s",
21160 "Library uninitialized");
21161 }
21162 return NULL;
21163 }
21164
21165 /* Allocate context and initialize reasonable general case defaults. */
21166 ctx = (struct mg_context *)mg_calloc(1, sizeof(*ctx));
21167 if (ctx == NULL) {
21168 if (error != NULL) {
21170 error->code_sub = (unsigned)sizeof(*ctx);
21172 NULL, /* No truncation check for error buffers */
21173 error->text,
21174 error->text_buffer_size,
21175 "%s",
21176 "Out of memory");
21177 }
21178 return NULL;
21179 }
21180
21181 /* Random number generator will initialize at the first call */
21182 ctx->dd.auth_nonce_mask =
21183 (uint64_t)get_random() ^ (uint64_t)(ptrdiff_t)(options);
21184
21185 /* Save started thread index to reuse in other external API calls
21186 * For the sake of thread synchronization all non-civetweb threads
21187 * can be considered as single external thread */
21189 tls.is_master = -1; /* Thread calling mg_start */
21190 tls.thread_idx = ctx->starter_thread_idx;
21191#if defined(_WIN32)
21192 tls.pthread_cond_helper_mutex = NULL;
21193#endif
21195
21197#if !defined(ALTERNATIVE_QUEUE)
21198 ok &= (0 == pthread_cond_init(&ctx->sq_empty, NULL));
21199 ok &= (0 == pthread_cond_init(&ctx->sq_full, NULL));
21200 ctx->sq_blocked = 0;
21201#endif
21203#if defined(USE_LUA)
21204 ok &= (0 == pthread_mutex_init(&ctx->lua_bg_mutex, &pthread_mutex_attr));
21205#endif
21206
21207 /** mg_stop() will close the user_shutdown_notification_socket, and that
21208 * will cause poll() to return immediately in the master-thread, so that
21209 * mg_stop() can also return immediately.
21210 */
21211 ok &= (0
21214
21215 if (!ok) {
21216 unsigned error_id = (unsigned)ERRNO;
21217 const char *err_msg =
21218 "Cannot initialize thread synchronization objects";
21219 /* Fatal error - abort start. However, this situation should never
21220 * occur in practice. */
21221
21222 mg_cry_ctx_internal(ctx, "%s", err_msg);
21223 if (error != NULL) {
21225 error->code_sub = error_id;
21227 NULL, /* No truncation check for error buffers */
21228 error->text,
21229 error->text_buffer_size,
21230 "%s",
21231 err_msg);
21232 }
21233
21234 mg_free(ctx);
21236 return NULL;
21237 }
21238
21239 if ((init != NULL) && (init->callbacks != NULL)) {
21240 /* Set all callbacks except exit_context. */
21241 ctx->callbacks = *init->callbacks;
21242 exit_callback = init->callbacks->exit_context;
21243 /* The exit callback is activated once the context is successfully
21244 * created. It should not be called, if an incomplete context object
21245 * is deleted during a failed initialization. */
21246 ctx->callbacks.exit_context = 0;
21247 }
21248 ctx->user_data = ((init != NULL) ? (init->user_data) : (NULL));
21249 ctx->dd.handlers = NULL;
21250 ctx->dd.next = NULL;
21251
21252#if defined(USE_LUA)
21253 lua_ctx_init(ctx);
21254#endif
21255
21256 /* Store options */
21257 while (options && (name = *options++) != NULL) {
21258 idx = get_option_index(name);
21259 if (idx == -1) {
21260 mg_cry_ctx_internal(ctx, "Invalid option: %s", name);
21261 if (error != NULL) {
21263 error->code_sub = (unsigned)-1;
21265 NULL, /* No truncation check for error buffers */
21266 error->text,
21267 error->text_buffer_size,
21268 "Invalid configuration option: %s",
21269 name);
21270 }
21271
21272 free_context(ctx);
21274 return NULL;
21275
21276 } else if ((value = *options++) == NULL) {
21277 mg_cry_ctx_internal(ctx, "%s: option value cannot be NULL", name);
21278 if (error != NULL) {
21280 error->code_sub = (unsigned)idx;
21282 NULL, /* No truncation check for error buffers */
21283 error->text,
21284 error->text_buffer_size,
21285 "Invalid configuration option value: %s",
21286 name);
21287 }
21288
21289 free_context(ctx);
21291 return NULL;
21292 }
21293 if (ctx->dd.config[idx] != NULL) {
21294 /* A duplicate configuration option is not an error - the last
21295 * option value will be used. */
21296 mg_cry_ctx_internal(ctx, "warning: %s: duplicate option", name);
21297 mg_free(ctx->dd.config[idx]);
21298 }
21299 ctx->dd.config[idx] = mg_strdup_ctx(value, ctx);
21300 DEBUG_TRACE("[%s] -> [%s]", name, value);
21301 }
21302
21303 /* Set default value if needed */
21304 for (i = 0; config_options[i].name != NULL; i++) {
21305 default_value = config_options[i].default_value;
21306 if ((ctx->dd.config[i] == NULL) && (default_value != NULL)) {
21307 ctx->dd.config[i] = mg_strdup_ctx(default_value, ctx);
21308 }
21309 }
21310
21311 /* Request size option */
21312 itmp = atoi(ctx->dd.config[MAX_REQUEST_SIZE]);
21313 if (itmp < 1024) {
21315 "%s too small",
21317 if (error != NULL) {
21319 error->code_sub = (unsigned)MAX_REQUEST_SIZE;
21321 NULL, /* No truncation check for error buffers */
21322 error->text,
21323 error->text_buffer_size,
21324 "Invalid configuration option value: %s",
21326 }
21327
21328 free_context(ctx);
21330 return NULL;
21331 }
21332 ctx->max_request_size = (unsigned)itmp;
21333
21334 /* Queue length */
21335#if !defined(ALTERNATIVE_QUEUE)
21336 itmp = atoi(ctx->dd.config[CONNECTION_QUEUE_SIZE]);
21337 if (itmp < 1) {
21339 "%s too small",
21341 if (error != NULL) {
21345 NULL, /* No truncation check for error buffers */
21346 error->text,
21347 error->text_buffer_size,
21348 "Invalid configuration option value: %s",
21350 }
21351
21352 free_context(ctx);
21354 return NULL;
21355 }
21356 ctx->squeue =
21357 (struct socket *)mg_calloc((unsigned int)itmp, sizeof(struct socket));
21358 if (ctx->squeue == NULL) {
21360 "Out of memory: Cannot allocate %s",
21362 if (error != NULL) {
21364 error->code_sub = (unsigned)itmp * (unsigned)sizeof(struct socket);
21366 NULL, /* No truncation check for error buffers */
21367 error->text,
21368 error->text_buffer_size,
21369 "Out of memory: Cannot allocate %s",
21371 }
21372
21373 free_context(ctx);
21375 return NULL;
21376 }
21377 ctx->sq_size = itmp;
21378#endif
21379
21380 /* Worker thread count option */
21381 workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]);
21382 prespawnthreadcount = atoi(ctx->dd.config[PRESPAWN_THREADS]);
21383
21384 if ((prespawnthreadcount < 0)
21387 workerthreadcount; /* can't prespawn more than all of them! */
21388 }
21389
21391 if (workerthreadcount <= 0) {
21392 mg_cry_ctx_internal(ctx, "%s", "Invalid number of worker threads");
21393 } else {
21394 mg_cry_ctx_internal(ctx, "%s", "Too many worker threads");
21395 }
21396 if (error != NULL) {
21398 error->code_sub = NUM_THREADS;
21400 NULL, /* No truncation check for error buffers */
21401 error->text,
21402 error->text_buffer_size,
21403 "Invalid configuration option value: %s",
21405 }
21406
21407 free_context(ctx);
21409 return NULL;
21410 }
21411
21412 /* Document root */
21413#if defined(NO_FILES)
21414 if (ctx->dd.config[DOCUMENT_ROOT] != NULL) {
21415 mg_cry_ctx_internal(ctx, "%s", "Document root must not be set");
21416 if (error != NULL) {
21418 error->code_sub = (unsigned)DOCUMENT_ROOT;
21420 NULL, /* No truncation check for error buffers */
21421 error->text,
21422 error->text_buffer_size,
21423 "Invalid configuration option value: %s",
21425 }
21426
21427 free_context(ctx);
21429 return NULL;
21430 }
21431#endif
21432
21434
21435#if defined(USE_LUA)
21436 /* If a Lua background script has been configured, start it. */
21437 ctx->lua_bg_log_available = 0;
21438 if (ctx->dd.config[LUA_BACKGROUND_SCRIPT] != NULL) {
21439 char ebuf[256];
21440 struct vec opt_vec;
21441 struct vec eq_vec;
21442 const char *sparams;
21443
21444 memset(ebuf, 0, sizeof(ebuf));
21445 pthread_mutex_lock(&ctx->lua_bg_mutex);
21446
21447 /* Create a Lua state, load all standard libraries and the mg table */
21449 ctx->dd.config[LUA_BACKGROUND_SCRIPT], ctx, ebuf, sizeof(ebuf));
21450 if (!state) {
21452 "lua_background_script load error: %s",
21453 ebuf);
21454 if (error != NULL) {
21457 NULL, /* No truncation check for error buffers */
21458 error->text,
21459 error->text_buffer_size,
21460 "Error in script %s: %s",
21462 ebuf);
21463 }
21464
21465 pthread_mutex_unlock(&ctx->lua_bg_mutex);
21466
21467 free_context(ctx);
21469 return NULL;
21470 }
21471
21472 /* Add a table with parameters into mg.params */
21474 if (sparams && sparams[0]) {
21475 lua_getglobal(state, "mg");
21476 lua_pushstring(state, "params");
21477 lua_newtable(state);
21478
21479 while ((sparams = next_option(sparams, &opt_vec, &eq_vec))
21480 != NULL) {
21482 state, opt_vec.ptr, opt_vec.len, eq_vec.ptr, eq_vec.len);
21483 if (mg_strncasecmp(sparams, opt_vec.ptr, opt_vec.len) == 0)
21484 break;
21485 }
21486 lua_rawset(state, -3);
21487 lua_pop(state, 1);
21488 }
21489
21490 /* Call script */
21491 state = mg_lua_context_script_run(state,
21492 ctx->dd.config[LUA_BACKGROUND_SCRIPT],
21493 ctx,
21494 ebuf,
21495 sizeof(ebuf));
21496 if (!state) {
21498 "lua_background_script start error: %s",
21499 ebuf);
21500 if (error != NULL) {
21503 NULL, /* No truncation check for error buffers */
21504 error->text,
21505 error->text_buffer_size,
21506 "Error in script %s: %s",
21508 ebuf);
21509 }
21510 pthread_mutex_unlock(&ctx->lua_bg_mutex);
21511
21512 free_context(ctx);
21514 return NULL;
21515 }
21516
21517 /* state remains valid */
21518 ctx->lua_background_state = (void *)state;
21519 pthread_mutex_unlock(&ctx->lua_bg_mutex);
21520
21521 } else {
21522 ctx->lua_background_state = 0;
21523 }
21524#endif
21525
21526 /* Step by step initialization of ctx - depending on build options */
21527#if !defined(NO_FILESYSTEMS)
21528 if (!set_gpass_option(ctx, NULL)) {
21529 const char *err_msg = "Invalid global password file";
21530 /* Fatal error - abort start. */
21531 mg_cry_ctx_internal(ctx, "%s", err_msg);
21532
21533 if (error != NULL) {
21536 NULL, /* No truncation check for error buffers */
21537 error->text,
21538 error->text_buffer_size,
21539 "%s",
21540 err_msg);
21541 }
21542 free_context(ctx);
21544 return NULL;
21545 }
21546#endif
21547
21548#if defined(USE_MBEDTLS) || defined(USE_GNUTLS)
21549 if (!mg_sslctx_init(ctx, NULL)) {
21550 const char *err_msg = "Error initializing SSL context";
21551 /* Fatal error - abort start. */
21552 mg_cry_ctx_internal(ctx, "%s", err_msg);
21553
21554 if (error != NULL) {
21557 NULL, /* No truncation check for error buffers */
21558 error->text,
21559 error->text_buffer_size,
21560 "%s",
21561 err_msg);
21562 }
21563
21564 free_context(ctx);
21566 return NULL;
21567 }
21568
21569#elif !defined(NO_SSL)
21570 if (!init_ssl_ctx(ctx, NULL)) {
21571 const char *err_msg = "Error initializing SSL context";
21572 /* Fatal error - abort start. */
21573 mg_cry_ctx_internal(ctx, "%s", err_msg);
21574
21575 if (error != NULL) {
21578 NULL, /* No truncation check for error buffers */
21579 error->text,
21580 error->text_buffer_size,
21581 "%s",
21582 err_msg);
21583 }
21584
21585 free_context(ctx);
21587 return NULL;
21588 }
21589#endif
21590
21591 if (!set_ports_option(ctx)) {
21592 const char *err_msg = "Failed to setup server ports";
21593 /* Fatal error - abort start. */
21594 mg_cry_ctx_internal(ctx, "%s", err_msg);
21595
21596 if (error != NULL) {
21599 NULL, /* No truncation check for error buffers */
21600 error->text,
21601 error->text_buffer_size,
21602 "%s",
21603 err_msg);
21604 }
21605
21606 free_context(ctx);
21608 return NULL;
21609 }
21610
21611#if !defined(_WIN32) && !defined(__ZEPHYR__)
21612 if (!set_uid_option(ctx)) {
21613 const char *err_msg = "Failed to run as configured user";
21614 /* Fatal error - abort start. */
21615 mg_cry_ctx_internal(ctx, "%s", err_msg);
21616
21617 if (error != NULL) {
21620 NULL, /* No truncation check for error buffers */
21621 error->text,
21622 error->text_buffer_size,
21623 "%s",
21624 err_msg);
21625 }
21626
21627 free_context(ctx);
21629 return NULL;
21630 }
21631#endif
21632
21633 if (!set_acl_option(ctx)) {
21634 const char *err_msg = "Failed to setup access control list";
21635 /* Fatal error - abort start. */
21636 mg_cry_ctx_internal(ctx, "%s", err_msg);
21637
21638 if (error != NULL) {
21641 NULL, /* No truncation check for error buffers */
21642 error->text,
21643 error->text_buffer_size,
21644 "%s",
21645 err_msg);
21646 }
21647
21648 free_context(ctx);
21650 return NULL;
21651 }
21652
21653 ctx->cfg_max_worker_threads = ((unsigned int)(workerthreadcount));
21654 ctx->worker_threadids =
21655 (pthread_t *)mg_calloc_ctx(ctx->cfg_max_worker_threads,
21656 sizeof(pthread_t),
21657 ctx);
21658
21659 if (ctx->worker_threadids == NULL) {
21660 const char *err_msg = "Not enough memory for worker thread ID array";
21661 mg_cry_ctx_internal(ctx, "%s", err_msg);
21662
21663 if (error != NULL) {
21665 error->code_sub = (unsigned)ctx->cfg_max_worker_threads
21666 * (unsigned)sizeof(pthread_t);
21668 NULL, /* No truncation check for error buffers */
21669 error->text,
21670 error->text_buffer_size,
21671 "%s",
21672 err_msg);
21673 }
21674
21675 free_context(ctx);
21677 return NULL;
21678 }
21679 ctx->worker_connections =
21681 sizeof(struct mg_connection),
21682 ctx);
21683 if (ctx->worker_connections == NULL) {
21684 const char *err_msg =
21685 "Not enough memory for worker thread connection array";
21686 mg_cry_ctx_internal(ctx, "%s", err_msg);
21687
21688 if (error != NULL) {
21690 error->code_sub = (unsigned)ctx->cfg_max_worker_threads
21691 * (unsigned)sizeof(struct mg_connection);
21693 NULL, /* No truncation check for error buffers */
21694 error->text,
21695 error->text_buffer_size,
21696 "%s",
21697 err_msg);
21698 }
21699
21700 free_context(ctx);
21702 return NULL;
21703 }
21704
21705#if defined(ALTERNATIVE_QUEUE)
21706 ctx->client_wait_events =
21708 sizeof(ctx->client_wait_events[0]),
21709 ctx);
21710 if (ctx->client_wait_events == NULL) {
21711 const char *err_msg = "Not enough memory for worker event array";
21712 mg_cry_ctx_internal(ctx, "%s", err_msg);
21714
21715 if (error != NULL) {
21717 error->code_sub = (unsigned)ctx->cfg_max_worker_threads
21718 * (unsigned)sizeof(ctx->client_wait_events[0]);
21720 NULL, /* No truncation check for error buffers */
21721 error->text,
21722 error->text_buffer_size,
21723 "%s",
21724 err_msg);
21725 }
21726
21727 free_context(ctx);
21729 return NULL;
21730 }
21731
21732 ctx->client_socks =
21734 sizeof(ctx->client_socks[0]),
21735 ctx);
21736 if (ctx->client_socks == NULL) {
21737 const char *err_msg = "Not enough memory for worker socket array";
21738 mg_cry_ctx_internal(ctx, "%s", err_msg);
21739 mg_free(ctx->client_wait_events);
21741
21742 if (error != NULL) {
21744 error->code_sub = (unsigned)ctx->cfg_max_worker_threads
21745 * (unsigned)sizeof(ctx->client_socks[0]);
21747 NULL, /* No truncation check for error buffers */
21748 error->text,
21749 error->text_buffer_size,
21750 "%s",
21751 err_msg);
21752 }
21753
21754 free_context(ctx);
21756 return NULL;
21757 }
21758
21759 for (i = 0; (unsigned)i < ctx->cfg_max_worker_threads; i++) {
21760 ctx->client_wait_events[i] = event_create();
21761 if (ctx->client_wait_events[i] == 0) {
21762 const char *err_msg = "Error creating worker event %i";
21764 while (i > 0) {
21765 i--;
21766 event_destroy(ctx->client_wait_events[i]);
21767 }
21768 mg_free(ctx->client_socks);
21769 mg_free(ctx->client_wait_events);
21771
21772 if (error != NULL) {
21774 error->code_sub = (unsigned)ERRNO;
21776 NULL, /* No truncation check for error buffers */
21777 error->text,
21778 error->text_buffer_size,
21779 err_msg,
21780 i);
21781 }
21782
21783 free_context(ctx);
21785 return NULL;
21786 }
21787 }
21788#endif
21789
21790#if defined(USE_TIMERS)
21791 if (timers_init(ctx) != 0) {
21792 const char *err_msg = "Error creating timers";
21793 mg_cry_ctx_internal(ctx, "%s", err_msg);
21794
21795 if (error != NULL) {
21797 error->code_sub = (unsigned)ERRNO;
21799 NULL, /* No truncation check for error buffers */
21800 error->text,
21801 error->text_buffer_size,
21802 "%s",
21803 err_msg);
21804 }
21805
21806 free_context(ctx);
21808 return NULL;
21809 }
21810#endif
21811
21812 /* Context has been created - init user libraries */
21813 if (ctx->callbacks.init_context) {
21814 ctx->callbacks.init_context(ctx);
21815 }
21816
21817 /* From now, the context is successfully created.
21818 * When it is destroyed, the exit callback should be called. */
21819 ctx->callbacks.exit_context = exit_callback;
21820 ctx->context_type = CONTEXT_SERVER; /* server context */
21821
21822 /* Start worker threads */
21823 for (i = 0; (int)i < prespawnthreadcount; i++) {
21824 /* worker_thread sets up the other fields */
21825 if (mg_start_worker_thread(ctx, 0) != 0) {
21826 long error_no = (long)ERRNO;
21827
21828 /* thread was not created */
21829 if (ctx->spawned_worker_threads > 0) {
21830 /* If the second, third, ... thread cannot be created, set a
21831 * warning, but keep running. */
21833 "Cannot start worker thread %i: error %ld",
21834 ctx->spawned_worker_threads + 1,
21835 error_no);
21836
21837 /* If the server initialization should stop here, all
21838 * threads that have already been created must be stopped
21839 * first, before any free_context(ctx) call.
21840 */
21841
21842 } else {
21843 /* If the first worker thread cannot be created, stop
21844 * initialization and free the entire server context. */
21846 "Cannot create threads: error %ld",
21847 error_no);
21848
21849 if (error != NULL) {
21851 error->code_sub = (unsigned)error_no;
21853 NULL,
21854 NULL, /* No truncation check for error buffers */
21855 error->text,
21856 error->text_buffer_size,
21857 "Cannot create first worker thread: error %ld",
21858 error_no);
21859 }
21860
21861 free_context(ctx);
21863 return NULL;
21864 }
21865 break;
21866 }
21867 }
21868
21869 /* Start master (listening) thread */
21871
21873 return ctx;
21874}
21875
21876
21877CIVETWEB_API struct mg_context *
21879 void *user_data,
21880 const char **options)
21881{
21882 struct mg_init_data init = {0};
21883 init.callbacks = callbacks;
21884 init.user_data = user_data;
21885 init.configuration_options = options;
21886
21887 return mg_start2(&init, NULL);
21888}
21889
21890
21891/* Add an additional domain to an already running web server. */
21892CIVETWEB_API int
21894 const char **options,
21895 struct mg_error_data *error)
21896{
21897 const char *name;
21898 const char *value;
21899 const char *default_value;
21900 struct mg_domain_context *new_dom;
21901 struct mg_domain_context *dom;
21902 int idx, i;
21903
21904 if (error != NULL) {
21905 error->code = MG_ERROR_DATA_CODE_OK;
21906 error->code_sub = 0;
21907 if (error->text_buffer_size > 0) {
21908 *error->text = 0;
21909 }
21910 }
21911
21912 if ((ctx == NULL) || (options == NULL)) {
21913 if (error != NULL) {
21916 NULL, /* No truncation check for error buffers */
21917 error->text,
21918 error->text_buffer_size,
21919 "%s",
21920 "Invalid parameters");
21921 }
21922 return -1;
21923 }
21924
21925 if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
21926 if (error != NULL) {
21929 NULL, /* No truncation check for error buffers */
21930 error->text,
21931 error->text_buffer_size,
21932 "%s",
21933 "Server already stopped");
21934 }
21935 return -7;
21936 }
21937
21938 new_dom = (struct mg_domain_context *)
21939 mg_calloc_ctx(1, sizeof(struct mg_domain_context), ctx);
21940
21941 if (!new_dom) {
21942 /* Out of memory */
21943 if (error != NULL) {
21945 error->code_sub = (unsigned)sizeof(struct mg_domain_context);
21947 NULL, /* No truncation check for error buffers */
21948 error->text,
21949 error->text_buffer_size,
21950 "%s",
21951 "Out or memory");
21952 }
21953 return -6;
21954 }
21955
21956 /* Store options - TODO: unite duplicate code */
21957 while (options && (name = *options++) != NULL) {
21958 idx = get_option_index(name);
21959 if (idx == -1) {
21960 mg_cry_ctx_internal(ctx, "Invalid option: %s", name);
21961 if (error != NULL) {
21963 error->code_sub = (unsigned)-1;
21965 NULL, /* No truncation check for error buffers */
21966 error->text,
21967 error->text_buffer_size,
21968 "Invalid option: %s",
21969 name);
21970 }
21972 return -2;
21973 } else if ((value = *options++) == NULL) {
21974 mg_cry_ctx_internal(ctx, "%s: option value cannot be NULL", name);
21975 if (error != NULL) {
21977 error->code_sub = (unsigned)idx;
21979 NULL, /* No truncation check for error buffers */
21980 error->text,
21981 error->text_buffer_size,
21982 "Invalid option value: %s",
21983 name);
21984 }
21986 return -2;
21987 }
21988 if (new_dom->config[idx] != NULL) {
21989 /* Duplicate option: Later values overwrite earlier ones. */
21990 mg_cry_ctx_internal(ctx, "warning: %s: duplicate option", name);
21991 mg_free(new_dom->config[idx]);
21992 }
21993 new_dom->config[idx] = mg_strdup_ctx(value, ctx);
21994 DEBUG_TRACE("[%s] -> [%s]", name, value);
21995 }
21996
21997 /* Authentication domain is mandatory */
21998 /* TODO: Maybe use a new option hostname? */
21999 if (!new_dom->config[AUTHENTICATION_DOMAIN]) {
22000 mg_cry_ctx_internal(ctx, "%s", "authentication domain required");
22001 if (error != NULL) {
22005 NULL, /* No truncation check for error buffers */
22006 error->text,
22007 error->text_buffer_size,
22008 "Mandatory option %s missing",
22010 }
22012 return -4;
22013 }
22014
22015 /* Set default value if needed. Take the config value from
22016 * ctx as a default value. */
22017 for (i = 0; config_options[i].name != NULL; i++) {
22018 default_value = ctx->dd.config[i];
22019 if ((new_dom->config[i] == NULL) && (default_value != NULL)) {
22020 new_dom->config[i] = mg_strdup_ctx(default_value, ctx);
22021 }
22022 }
22023
22024 new_dom->handlers = NULL;
22025 new_dom->next = NULL;
22026 new_dom->nonce_count = 0;
22027 new_dom->auth_nonce_mask = get_random() ^ (get_random() << 31);
22028
22029#if defined(USE_LUA) && defined(USE_WEBSOCKET)
22030 new_dom->shared_lua_websockets = NULL;
22031#endif
22032
22033#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(USE_GNUTLS)
22034 if (!init_ssl_ctx(ctx, new_dom)) {
22035 /* Init SSL failed */
22036 if (error != NULL) {
22039 NULL, /* No truncation check for error buffers */
22040 error->text,
22041 error->text_buffer_size,
22042 "%s",
22043 "Initializing SSL context failed");
22044 }
22046 return -3;
22047 }
22048#endif
22049
22050 /* Add element to linked list. */
22051 mg_lock_context(ctx);
22052
22053 idx = 0;
22054 dom = &(ctx->dd);
22055 for (;;) {
22057 dom->config[AUTHENTICATION_DOMAIN])) {
22058 /* Domain collision */
22060 "domain %s already in use",
22062 if (error != NULL) {
22065 NULL, /* No truncation check for error buffers */
22066 error->text,
22067 error->text_buffer_size,
22068 "Domain %s specified by %s is already in use",
22071 }
22073 mg_unlock_context(ctx);
22074 return -5;
22075 }
22076
22077 /* Count number of domains */
22078 idx++;
22079
22080 if (dom->next == NULL) {
22081 dom->next = new_dom;
22082 break;
22083 }
22084 dom = dom->next;
22085 }
22086
22087 mg_unlock_context(ctx);
22088
22089 /* Return domain number */
22090 return idx;
22091}
22092
22093
22094CIVETWEB_API int
22095mg_start_domain(struct mg_context *ctx, const char **options)
22096{
22097 return mg_start_domain2(ctx, options, NULL);
22098}
22099
22100
22101/* Feature check API function */
22102CIVETWEB_API unsigned
22104{
22105 static const unsigned feature_set = 0
22106 /* Set bits for available features according to API documentation.
22107 * This bit mask is created at compile time, according to the active
22108 * preprocessor defines. It is a single const value at runtime. */
22109#if !defined(NO_FILES)
22111#endif
22112#if !defined(NO_SSL) || defined(USE_MBEDTLS) || defined(USE_GNUTLS)
22114#endif
22115#if !defined(NO_CGI)
22117#endif
22118#if defined(USE_IPV6)
22120#endif
22121#if defined(USE_WEBSOCKET)
22123#endif
22124#if defined(USE_LUA)
22126#endif
22127#if defined(USE_DUKTAPE)
22129#endif
22130#if !defined(NO_CACHING)
22132#endif
22133#if defined(USE_SERVER_STATS)
22135#endif
22136#if defined(USE_ZLIB)
22138#endif
22139#if defined(USE_HTTP2)
22141#endif
22142#if defined(USE_X_DOM_SOCKET)
22144#endif
22145
22146 /* Set some extra bits not defined in the API documentation.
22147 * These bits may change without further notice. */
22148#if defined(MG_LEGACY_INTERFACE)
22149 | 0x80000000u
22150#endif
22151#if defined(MG_EXPERIMENTAL_INTERFACES)
22152 | 0x40000000u
22153#endif
22154#if !defined(NO_RESPONSE_BUFFERING)
22155 | 0x20000000u
22156#endif
22157#if defined(MEMORY_DEBUGGING)
22158 | 0x10000000u
22159#endif
22160 ;
22161 return (feature & feature_set);
22162}
22163
22164
22165static size_t
22166mg_str_append(char **dst, char *end, const char *src)
22167{
22168 size_t len = strlen(src);
22169 if (*dst != end) {
22170 /* Append src if enough space, or close dst. */
22171 if ((size_t)(end - *dst) > len) {
22172 strcpy(*dst, src);
22173 *dst += len;
22174 } else {
22175 *dst = end;
22176 }
22177 }
22178 return len;
22179}
22180
22181
22182/* Get system information. It can be printed or stored by the caller.
22183 * Return the size of available information. */
22184CIVETWEB_API int
22185mg_get_system_info(char *buffer, int buflen)
22186{
22187 char *end, *append_eoobj = NULL, block[256];
22188 size_t system_info_length = 0;
22189
22190#if defined(_WIN32)
22191 static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n";
22192#else
22193 static const char eol[] = "\n", eoobj[] = "\n}\n";
22194#endif
22195
22196 if ((buffer == NULL) || (buflen < 1)) {
22197 buflen = 0;
22198 end = buffer;
22199 } else {
22200 *buffer = 0;
22201 end = buffer + buflen;
22202 }
22203 if (buflen > (int)(sizeof(eoobj) - 1)) {
22204 /* has enough space to append eoobj */
22205 append_eoobj = buffer;
22206 if (end) {
22207 end -= sizeof(eoobj) - 1;
22208 }
22209 }
22210
22211 system_info_length += mg_str_append(&buffer, end, "{");
22212
22213 /* Server version */
22214 {
22215 const char *version = mg_version();
22217 NULL,
22218 block,
22219 sizeof(block),
22220 "%s\"version\" : \"%s\"",
22221 eol,
22222 version);
22223 system_info_length += mg_str_append(&buffer, end, block);
22224 }
22225
22226 /* System info */
22227 {
22228#if defined(_WIN32)
22229 DWORD dwVersion = 0;
22230 DWORD dwMajorVersion = 0;
22231 DWORD dwMinorVersion = 0;
22233
22234 GetSystemInfo(&si);
22235
22236#if defined(_MSC_VER)
22237#pragma warning(push)
22238 /* GetVersion was declared deprecated */
22239#pragma warning(disable : 4996)
22240#endif
22241 dwVersion = GetVersion();
22242#if defined(_MSC_VER)
22243#pragma warning(pop)
22244#endif
22245
22248
22250 NULL,
22251 block,
22252 sizeof(block),
22253 ",%s\"os\" : \"Windows %u.%u\"",
22254 eol,
22255 (unsigned)dwMajorVersion,
22256 (unsigned)dwMinorVersion);
22257 system_info_length += mg_str_append(&buffer, end, block);
22258
22260 NULL,
22261 block,
22262 sizeof(block),
22263 ",%s\"cpu\" : \"type %u, cores %u, mask %x\"",
22264 eol,
22265 (unsigned)si.wProcessorArchitecture,
22266 (unsigned)si.dwNumberOfProcessors,
22267 (unsigned)si.dwActiveProcessorMask);
22268 system_info_length += mg_str_append(&buffer, end, block);
22269#elif defined(__rtems__)
22271 NULL,
22272 block,
22273 sizeof(block),
22274 ",%s\"os\" : \"%s %s\"",
22275 eol,
22276 "RTEMS",
22277 rtems_version());
22278 system_info_length += mg_str_append(&buffer, end, block);
22279#elif defined(__ZEPHYR__)
22281 NULL,
22282 block,
22283 sizeof(block),
22284 ",%s\"os\" : \"%s\"",
22285 eol,
22286 "Zephyr OS",
22288 system_info_length += mg_str_append(&buffer, end, block);
22289#else
22290 struct utsname name;
22291 memset(&name, 0, sizeof(name));
22292 uname(&name);
22293
22295 NULL,
22296 block,
22297 sizeof(block),
22298 ",%s\"os\" : \"%s %s (%s) - %s\"",
22299 eol,
22300 name.sysname,
22301 name.version,
22302 name.release,
22303 name.machine);
22304 system_info_length += mg_str_append(&buffer, end, block);
22305#endif
22306 }
22307
22308 /* Features */
22309 {
22311 NULL,
22312 block,
22313 sizeof(block),
22314 ",%s\"features\" : %lu"
22315 ",%s\"feature_list\" : \"Server:%s%s%s%s%s%s%s%s%s\"",
22316 eol,
22317 (unsigned long)mg_check_feature(0xFFFFFFFFu),
22318 eol,
22319 mg_check_feature(MG_FEATURES_FILES) ? " Files" : "",
22320 mg_check_feature(MG_FEATURES_SSL) ? " HTTPS" : "",
22321 mg_check_feature(MG_FEATURES_CGI) ? " CGI" : "",
22322 mg_check_feature(MG_FEATURES_IPV6) ? " IPv6" : "",
22324 : "",
22325 mg_check_feature(MG_FEATURES_LUA) ? " Lua" : "",
22326 mg_check_feature(MG_FEATURES_SSJS) ? " JavaScript" : "",
22327 mg_check_feature(MG_FEATURES_CACHE) ? " Cache" : "",
22328 mg_check_feature(MG_FEATURES_STATS) ? " Stats" : "");
22329 system_info_length += mg_str_append(&buffer, end, block);
22330
22331#if defined(USE_LUA)
22333 NULL,
22334 block,
22335 sizeof(block),
22336 ",%s\"lua_version\" : \"%u (%s)\"",
22337 eol,
22338 (unsigned)LUA_VERSION_NUM,
22339 LUA_RELEASE);
22340 system_info_length += mg_str_append(&buffer, end, block);
22341#endif
22342#if defined(USE_DUKTAPE)
22344 NULL,
22345 block,
22346 sizeof(block),
22347 ",%s\"javascript\" : \"Duktape %u.%u.%u\"",
22348 eol,
22349 (unsigned)DUK_VERSION / 10000,
22350 ((unsigned)DUK_VERSION / 100) % 100,
22351 (unsigned)DUK_VERSION % 100);
22352 system_info_length += mg_str_append(&buffer, end, block);
22353#endif
22354 }
22355
22356 /* Build identifier. If BUILD_DATE is not set, __DATE__ will be used. */
22357 {
22358#if defined(BUILD_DATE)
22359 const char *bd = BUILD_DATE;
22360#else
22361#if defined(GCC_DIAGNOSTIC)
22362#if GCC_VERSION >= 40900
22363#pragma GCC diagnostic push
22364 /* Disable idiotic compiler warning -Wdate-time, appeared in gcc5. This
22365 * does not work in some versions. If "BUILD_DATE" is defined to some
22366 * string, it is used instead of __DATE__. */
22367#pragma GCC diagnostic ignored "-Wdate-time"
22368#endif
22369#endif
22370 const char *bd = __DATE__;
22371#if defined(GCC_DIAGNOSTIC)
22372#if GCC_VERSION >= 40900
22373#pragma GCC diagnostic pop
22374#endif
22375#endif
22376#endif
22377
22379 NULL, NULL, block, sizeof(block), ",%s\"build\" : \"%s\"", eol, bd);
22380
22381 system_info_length += mg_str_append(&buffer, end, block);
22382 }
22383
22384 /* Compiler information */
22385 /* http://sourceforge.net/p/predef/wiki/Compilers/ */
22386 {
22387#if defined(_MSC_VER)
22389 NULL,
22390 block,
22391 sizeof(block),
22392 ",%s\"compiler\" : \"MSC: %u (%u)\"",
22393 eol,
22394 (unsigned)_MSC_VER,
22395 (unsigned)_MSC_FULL_VER);
22396 system_info_length += mg_str_append(&buffer, end, block);
22397#elif defined(__MINGW64__)
22399 NULL,
22400 block,
22401 sizeof(block),
22402 ",%s\"compiler\" : \"MinGW64: %u.%u\"",
22403 eol,
22404 (unsigned)__MINGW64_VERSION_MAJOR,
22405 (unsigned)__MINGW64_VERSION_MINOR);
22406 system_info_length += mg_str_append(&buffer, end, block);
22408 NULL,
22409 block,
22410 sizeof(block),
22411 ",%s\"compiler\" : \"MinGW32: %u.%u\"",
22412 eol,
22413 (unsigned)__MINGW32_MAJOR_VERSION,
22414 (unsigned)__MINGW32_MINOR_VERSION);
22415 system_info_length += mg_str_append(&buffer, end, block);
22416#elif defined(__MINGW32__)
22418 NULL,
22419 block,
22420 sizeof(block),
22421 ",%s\"compiler\" : \"MinGW32: %u.%u\"",
22422 eol,
22423 (unsigned)__MINGW32_MAJOR_VERSION,
22424 (unsigned)__MINGW32_MINOR_VERSION);
22425 system_info_length += mg_str_append(&buffer, end, block);
22426#elif defined(__clang__)
22428 NULL,
22429 block,
22430 sizeof(block),
22431 ",%s\"compiler\" : \"clang: %u.%u.%u (%s)\"",
22432 eol,
22437 system_info_length += mg_str_append(&buffer, end, block);
22438#elif defined(__GNUC__)
22440 NULL,
22441 block,
22442 sizeof(block),
22443 ",%s\"compiler\" : \"gcc: %u.%u.%u\"",
22444 eol,
22445 (unsigned)__GNUC__,
22446 (unsigned)__GNUC_MINOR__,
22447 (unsigned)__GNUC_PATCHLEVEL__);
22448 system_info_length += mg_str_append(&buffer, end, block);
22449#elif defined(__INTEL_COMPILER)
22451 NULL,
22452 block,
22453 sizeof(block),
22454 ",%s\"compiler\" : \"Intel C/C++: %u\"",
22455 eol,
22456 (unsigned)__INTEL_COMPILER);
22457 system_info_length += mg_str_append(&buffer, end, block);
22458#elif defined(__BORLANDC__)
22460 NULL,
22461 block,
22462 sizeof(block),
22463 ",%s\"compiler\" : \"Borland C: 0x%x\"",
22464 eol,
22465 (unsigned)__BORLANDC__);
22466 system_info_length += mg_str_append(&buffer, end, block);
22467#elif defined(__SUNPRO_C)
22469 NULL,
22470 block,
22471 sizeof(block),
22472 ",%s\"compiler\" : \"Solaris: 0x%x\"",
22473 eol,
22474 (unsigned)__SUNPRO_C);
22475 system_info_length += mg_str_append(&buffer, end, block);
22476#else
22478 NULL,
22479 block,
22480 sizeof(block),
22481 ",%s\"compiler\" : \"other\"",
22482 eol);
22483 system_info_length += mg_str_append(&buffer, end, block);
22484#endif
22485 }
22486
22487 /* Determine 32/64 bit data mode.
22488 * see https://en.wikipedia.org/wiki/64-bit_computing */
22489 {
22491 NULL,
22492 block,
22493 sizeof(block),
22494 ",%s\"data_model\" : \"int:%u/%u/%u/%u, float:%u/%u/%u, "
22495 "char:%u/%u, "
22496 "ptr:%u, size:%u, time:%u\"",
22497 eol,
22498 (unsigned)sizeof(short),
22499 (unsigned)sizeof(int),
22500 (unsigned)sizeof(long),
22501 (unsigned)sizeof(long long),
22502 (unsigned)sizeof(float),
22503 (unsigned)sizeof(double),
22504 (unsigned)sizeof(long double),
22505 (unsigned)sizeof(char),
22506 (unsigned)sizeof(wchar_t),
22507 (unsigned)sizeof(void *),
22508 (unsigned)sizeof(size_t),
22509 (unsigned)sizeof(time_t));
22510 system_info_length += mg_str_append(&buffer, end, block);
22511 }
22512
22513 /* Terminate string */
22514 if (append_eoobj) {
22516 }
22517 system_info_length += sizeof(eoobj) - 1;
22518
22519 return (int)system_info_length;
22520}
22521
22522
22523/* Get context information. It can be printed or stored by the caller.
22524 * Return the size of available information. */
22525CIVETWEB_API int
22526mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen)
22527{
22528#if defined(USE_SERVER_STATS)
22529 char *end, *append_eoobj = NULL, block[256];
22530 size_t context_info_length = 0;
22531
22532#if defined(_WIN32)
22533 static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n";
22534#else
22535 static const char eol[] = "\n", eoobj[] = "\n}\n";
22536#endif
22537 struct mg_memory_stat *ms = get_memory_stat((struct mg_context *)ctx);
22538
22539 if ((buffer == NULL) || (buflen < 1)) {
22540 buflen = 0;
22541 end = buffer;
22542 } else {
22543 *buffer = 0;
22544 end = buffer + buflen;
22545 }
22546 if (buflen > (int)(sizeof(eoobj) - 1)) {
22547 /* has enough space to append eoobj */
22548 append_eoobj = buffer;
22549 end -= sizeof(eoobj) - 1;
22550 }
22551
22552 context_info_length += mg_str_append(&buffer, end, "{");
22553
22554 if (ms) { /* <-- should be always true */
22555 /* Memory information */
22556 int blockCount = (int)ms->blockCount;
22557 int64_t totalMemUsed = ms->totalMemUsed;
22558 int64_t maxMemUsed = ms->maxMemUsed;
22559 if (totalMemUsed > maxMemUsed) {
22561 }
22562
22564 NULL,
22565 block,
22566 sizeof(block),
22567 "%s\"memory\" : {%s"
22568 "\"blocks\" : %i,%s"
22569 "\"used\" : %" INT64_FMT ",%s"
22570 "\"maxUsed\" : %" INT64_FMT "%s"
22571 "}",
22572 eol,
22573 eol,
22574 blockCount,
22575 eol,
22577 eol,
22578 maxMemUsed,
22579 eol);
22580 context_info_length += mg_str_append(&buffer, end, block);
22581 }
22582
22583 if (ctx) {
22584 /* Declare all variables at begin of the block, to comply
22585 * with old C standards. */
22586 char start_time_str[64] = {0};
22587 char now_str[64] = {0};
22588 time_t start_time = ctx->start_time;
22589 time_t now = time(NULL);
22591 int active_connections = (int)ctx->active_connections;
22592 int max_active_connections = (int)ctx->max_active_connections;
22593 int total_connections = (int)ctx->total_connections;
22596 }
22599 }
22600
22601 /* Connections information */
22603 NULL,
22604 block,
22605 sizeof(block),
22606 ",%s\"connections\" : {%s"
22607 "\"active\" : %i,%s"
22608 "\"maxActive\" : %i,%s"
22609 "\"total\" : %i%s"
22610 "}",
22611 eol,
22612 eol,
22614 eol,
22616 eol,
22618 eol);
22619 context_info_length += mg_str_append(&buffer, end, block);
22620
22621 /* Queue information */
22622#if !defined(ALTERNATIVE_QUEUE)
22624 NULL,
22625 block,
22626 sizeof(block),
22627 ",%s\"queue\" : {%s"
22628 "\"length\" : %i,%s"
22629 "\"filled\" : %i,%s"
22630 "\"maxFilled\" : %i,%s"
22631 "\"full\" : %s%s"
22632 "}",
22633 eol,
22634 eol,
22635 ctx->sq_size,
22636 eol,
22637 ctx->sq_head - ctx->sq_tail,
22638 eol,
22639 ctx->sq_max_fill,
22640 eol,
22641 (ctx->sq_blocked ? "true" : "false"),
22642 eol);
22643 context_info_length += mg_str_append(&buffer, end, block);
22644#endif
22645
22646 /* Requests information */
22648 NULL,
22649 block,
22650 sizeof(block),
22651 ",%s\"requests\" : {%s"
22652 "\"total\" : %lu%s"
22653 "}",
22654 eol,
22655 eol,
22656 (unsigned long)ctx->total_requests,
22657 eol);
22658 context_info_length += mg_str_append(&buffer, end, block);
22659
22660 /* Data information */
22662 mg_atomic_add64((volatile int64_t *)&ctx->total_data_read, 0);
22664 mg_atomic_add64((volatile int64_t *)&ctx->total_data_written, 0);
22666 NULL,
22667 block,
22668 sizeof(block),
22669 ",%s\"data\" : {%s"
22670 "\"read\" : %" INT64_FMT ",%s"
22671 "\"written\" : %" INT64_FMT "%s"
22672 "}",
22673 eol,
22674 eol,
22676 eol,
22678 eol);
22679 context_info_length += mg_str_append(&buffer, end, block);
22680
22681 /* Execution time information */
22683 sizeof(start_time_str) - 1,
22684 &start_time);
22685 gmt_time_string(now_str, sizeof(now_str) - 1, &now);
22686
22688 NULL,
22689 block,
22690 sizeof(block),
22691 ",%s\"time\" : {%s"
22692 "\"uptime\" : %.0f,%s"
22693 "\"start\" : \"%s\",%s"
22694 "\"now\" : \"%s\"%s"
22695 "}",
22696 eol,
22697 eol,
22698 difftime(now, start_time),
22699 eol,
22701 eol,
22702 now_str,
22703 eol);
22704 context_info_length += mg_str_append(&buffer, end, block);
22705 }
22706
22707 /* Terminate string */
22708 if (append_eoobj) {
22710 }
22711 context_info_length += sizeof(eoobj) - 1;
22712
22713 return (int)context_info_length;
22714#else
22715 (void)ctx;
22716 if ((buffer != NULL) && (buflen > 0)) {
22717 *buffer = 0;
22718 }
22719 return 0;
22720#endif
22721}
22722
22723
22724CIVETWEB_API void
22726{
22727 /* https://github.com/civetweb/civetweb/issues/727 */
22728 if (conn != NULL) {
22729 conn->must_close = 1;
22730 }
22731}
22732
22733
22734#if defined(MG_EXPERIMENTAL_INTERFACES)
22735/* Get connection information. It can be printed or stored by the caller.
22736 * Return the size of available information. */
22737CIVETWEB_API int
22738mg_get_connection_info(const struct mg_context *ctx,
22739 int idx,
22740 char *buffer,
22741 int buflen)
22742{
22743 const struct mg_connection *conn;
22744 const struct mg_request_info *ri;
22745 char *end, *append_eoobj = NULL, block[256];
22746 size_t connection_info_length = 0;
22747 int state = 0;
22748 const char *state_str = "unknown";
22749
22750#if defined(_WIN32)
22751 static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n";
22752#else
22753 static const char eol[] = "\n", eoobj[] = "\n}\n";
22754#endif
22755
22756 if ((buffer == NULL) || (buflen < 1)) {
22757 buflen = 0;
22758 end = buffer;
22759 } else {
22760 *buffer = 0;
22761 end = buffer + buflen;
22762 }
22763 if (buflen > (int)(sizeof(eoobj) - 1)) {
22764 /* has enough space to append eoobj */
22765 append_eoobj = buffer;
22766 end -= sizeof(eoobj) - 1;
22767 }
22768
22769 if ((ctx == NULL) || (idx < 0)) {
22770 /* Parameter error */
22771 return 0;
22772 }
22773
22774 if ((unsigned)idx >= ctx->cfg_max_worker_threads) {
22775 /* Out of range */
22776 return 0;
22777 }
22778
22779 /* Take connection [idx]. This connection is not locked in
22780 * any way, so some other thread might use it. */
22781 conn = (ctx->worker_connections) + idx;
22782
22783 /* Initialize output string */
22784 connection_info_length += mg_str_append(&buffer, end, "{");
22785
22786 /* Init variables */
22787 ri = &(conn->request_info);
22788
22789#if defined(USE_SERVER_STATS)
22790 state = conn->conn_state;
22791
22792 /* State as string */
22793 switch (state) {
22794 case 0:
22795 state_str = "undefined";
22796 break;
22797 case 1:
22798 state_str = "not used";
22799 break;
22800 case 2:
22801 state_str = "init";
22802 break;
22803 case 3:
22804 state_str = "ready";
22805 break;
22806 case 4:
22807 state_str = "processing";
22808 break;
22809 case 5:
22810 state_str = "processed";
22811 break;
22812 case 6:
22813 state_str = "to close";
22814 break;
22815 case 7:
22816 state_str = "closing";
22817 break;
22818 case 8:
22819 state_str = "closed";
22820 break;
22821 case 9:
22822 state_str = "done";
22823 break;
22824 }
22825#endif
22826
22827 /* Connection info */
22828 if ((state >= 3) && (state < 9)) {
22830 NULL,
22831 block,
22832 sizeof(block),
22833 "%s\"connection\" : {%s"
22834 "\"remote\" : {%s"
22835 "\"protocol\" : \"%s\",%s"
22836 "\"addr\" : \"%s\",%s"
22837 "\"port\" : %u%s"
22838 "},%s"
22839 "\"handled_requests\" : %u%s"
22840 "}",
22841 eol,
22842 eol,
22843 eol,
22844 get_proto_name(conn),
22845 eol,
22846 ri->remote_addr,
22847 eol,
22848 ri->remote_port,
22849 eol,
22850 eol,
22851 conn->handled_requests,
22852 eol);
22853 connection_info_length += mg_str_append(&buffer, end, block);
22854 }
22855
22856 /* Request info */
22857 if ((state >= 4) && (state < 6)) {
22859 NULL,
22860 block,
22861 sizeof(block),
22862 "%s%s\"request_info\" : {%s"
22863 "\"method\" : \"%s\",%s"
22864 "\"uri\" : \"%s\",%s"
22865 "\"query\" : %s%s%s%s"
22866 "}",
22867 (connection_info_length > 1 ? "," : ""),
22868 eol,
22869 eol,
22870 ri->request_method,
22871 eol,
22872 ri->request_uri,
22873 eol,
22874 ri->query_string ? "\"" : "",
22875 ri->query_string ? ri->query_string : "null",
22876 ri->query_string ? "\"" : "",
22877 eol);
22878 connection_info_length += mg_str_append(&buffer, end, block);
22879 }
22880
22881 /* Execution time information */
22882 if ((state >= 2) && (state < 9)) {
22883 char start_time_str[64] = {0};
22884 char close_time_str[64] = {0};
22885 time_t start_time = conn->conn_birth_time;
22886 time_t close_time = 0;
22887 double time_diff;
22888
22890 sizeof(start_time_str) - 1,
22891 &start_time);
22892#if defined(USE_SERVER_STATS)
22893 close_time = conn->conn_close_time;
22894#endif
22895 if (close_time != 0) {
22896 time_diff = difftime(close_time, start_time);
22898 sizeof(close_time_str) - 1,
22899 &close_time);
22900 } else {
22901 time_t now = time(NULL);
22902 time_diff = difftime(now, start_time);
22903 close_time_str[0] = 0; /* or use "now" ? */
22904 }
22905
22907 NULL,
22908 block,
22909 sizeof(block),
22910 "%s%s\"time\" : {%s"
22911 "\"uptime\" : %.0f,%s"
22912 "\"start\" : \"%s\",%s"
22913 "\"closed\" : \"%s\"%s"
22914 "}",
22915 (connection_info_length > 1 ? "," : ""),
22916 eol,
22917 eol,
22918 time_diff,
22919 eol,
22921 eol,
22923 eol);
22924 connection_info_length += mg_str_append(&buffer, end, block);
22925 }
22926
22927 /* Remote user name */
22928 if ((ri->remote_user) && (state < 9)) {
22930 NULL,
22931 block,
22932 sizeof(block),
22933 "%s%s\"user\" : {%s"
22934 "\"name\" : \"%s\",%s"
22935 "}",
22936 (connection_info_length > 1 ? "," : ""),
22937 eol,
22938 eol,
22939 ri->remote_user,
22940 eol);
22941 connection_info_length += mg_str_append(&buffer, end, block);
22942 }
22943
22944 /* Data block */
22945 if (state >= 3) {
22947 NULL,
22948 block,
22949 sizeof(block),
22950 "%s%s\"data\" : {%s"
22951 "\"read\" : %" INT64_FMT ",%s"
22952 "\"written\" : %" INT64_FMT "%s"
22953 "}",
22954 (connection_info_length > 1 ? "," : ""),
22955 eol,
22956 eol,
22957 conn->consumed_content,
22958 eol,
22959 conn->num_bytes_sent,
22960 eol);
22961 connection_info_length += mg_str_append(&buffer, end, block);
22962 }
22963
22964 /* State */
22966 NULL,
22967 block,
22968 sizeof(block),
22969 "%s%s\"state\" : \"%s\"",
22970 (connection_info_length > 1 ? "," : ""),
22971 eol,
22972 state_str);
22973 connection_info_length += mg_str_append(&buffer, end, block);
22974
22975 /* Terminate string */
22976 if (append_eoobj) {
22978 }
22979 connection_info_length += sizeof(eoobj) - 1;
22980
22981 return (int)connection_info_length;
22982}
22983
22984
22985#if 0
22986/* Get handler information. Not fully implemented. Is it required? */
22987CIVETWEB_API int
22989 char *buffer,
22990 int buflen)
22991{
22992 int handler_info_len = 0;
22993 struct mg_handler_info *tmp_rh;
22994 mg_lock_context(ctx);
22995
22996 for (tmp_rh = ctx->dd.handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
22997
22998 if (buflen > handler_info_len + tmp_rh->uri_len) {
22999 memcpy(buffer + handler_info_len, tmp_rh->uri, tmp_rh->uri_len);
23000 }
23001 handler_info_len += tmp_rh->uri_len;
23002
23003 switch (tmp_rh->handler_type) {
23004 case REQUEST_HANDLER:
23005 (void)tmp_rh->handler;
23006 break;
23007 case WEBSOCKET_HANDLER:
23008 (void)tmp_rh->connect_handler;
23009 (void)tmp_rh->ready_handler;
23010 (void)tmp_rh->data_handler;
23011 (void)tmp_rh->close_handler;
23012 break;
23013 case AUTH_HANDLER:
23014 (void)tmp_rh->auth_handler;
23015 break;
23016 }
23017 (void)cbdata;
23018 }
23019
23020 mg_unlock_context(ctx);
23021 return handler_info_len;
23022}
23023#endif
23024#endif
23025
23026
23027/* Initialize this library. This function does not need to be thread safe.
23028 */
23029CIVETWEB_API unsigned
23031{
23032 unsigned features_to_init = mg_check_feature(features & 0xFFu);
23034
23035 if (mg_init_library_called <= 0) {
23036 /* Not initialized yet */
23038 return 0;
23039 }
23040 }
23041
23043
23044 if (mg_init_library_called <= 0) {
23045 int i;
23046 size_t len;
23047
23048#if defined(_WIN32)
23049 int file_mutex_init = 1;
23050 int wsa = 1;
23051#else
23052 int mutexattr_init = 1;
23053#endif
23054 int failed = 1;
23056
23057 if (key_create == 0) {
23058#if defined(_WIN32)
23061 if (file_mutex_init == 0) {
23062 /* Start WinSock */
23063 WSADATA data;
23064 failed = wsa = WSAStartup(MAKEWORD(2, 2), &data);
23065 }
23066#else
23068 if (mutexattr_init == 0) {
23071 }
23072#endif
23073 }
23074
23075 if (failed) {
23076#if defined(_WIN32)
23077 if (wsa == 0) {
23078 (void)WSACleanup();
23079 }
23080 if (file_mutex_init == 0) {
23082 }
23083#else
23084 if (mutexattr_init == 0) {
23086 }
23087#endif
23088 if (key_create == 0) {
23090 }
23093 return 0;
23094 }
23095
23096 len = 1;
23097 for (i = 0; http_methods[i].name != NULL; i++) {
23098 size_t sl = strlen(http_methods[i].name);
23099 len += sl;
23100 if (i > 0) {
23101 len += 2;
23102 }
23103 }
23104 all_methods = (char *)mg_malloc(len);
23105 if (!all_methods) {
23106 /* Must never happen */
23109 return 0;
23110 }
23111 all_methods[0] = 0;
23112 for (i = 0; http_methods[i].name != NULL; i++) {
23113 if (i > 0) {
23114 strcat(all_methods, ", ");
23116 } else {
23118 }
23119 }
23120 }
23121
23122#if defined(USE_LUA)
23124#endif
23125
23126#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1) \
23127 || defined(OPENSSL_API_3_0)) \
23128 && !defined(NO_SSL)
23129
23132 char ebuf[128];
23133 if (initialize_openssl(ebuf, sizeof(ebuf))) {
23135 } else {
23136 (void)ebuf;
23137 DEBUG_TRACE("Initializing SSL failed: %s", ebuf);
23138 features_inited &= ~((unsigned)(MG_FEATURES_SSL));
23139 }
23140 } else {
23141 /* ssl already initialized */
23142 }
23143 }
23144
23145#endif
23146
23147 if (mg_init_library_called <= 0) {
23149 } else {
23151 }
23153
23154 return features_inited;
23155}
23156
23157
23158/* Un-initialize this library. */
23159CIVETWEB_API unsigned
23161{
23162 if (mg_init_library_called <= 0) {
23163 return 0;
23164 }
23165
23167
23169 if (mg_init_library_called == 0) {
23170#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1)) && !defined(NO_SSL)
23174 }
23175#endif
23176
23177#if defined(_WIN32)
23178 (void)WSACleanup();
23180#else
23182#endif
23183
23185
23186#if defined(USE_LUA)
23188#endif
23190 all_methods = NULL;
23191
23194 return 1;
23195 }
23196
23198 return 1;
23199}
23200
23201
23202/* End of civetweb.c */
static int esc(const char **)
Map escape sequences into their equivalent symbols.
Definition Match.cxx:438
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
#define s1(x)
Definition RSha256.hxx:91
#define h(i)
Definition RSha256.hxx:106
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
static unsigned int total
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t mask
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void on
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t UChar_t len
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t attr
Option_t Option_t TPoint TPoint const char mode
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t src
char name[80]
Definition TGX11.cxx:110
#define INVALID_HANDLE_VALUE
Definition TMapFile.cxx:84
R__EXTERN C unsigned int sleep(unsigned int seconds)
static void process_new_connection(struct mg_connection *conn)
Definition civetweb.c:19836
static int is_authorized_for_put(struct mg_connection *conn)
Definition civetweb.c:9334
static int parse_http_request(char *buf, int len, struct mg_request_info *ri)
Definition civetweb.c:11229
void mg_send_mime_file2(struct mg_connection *conn, const char *path, const char *mime_type, const char *additional_headers)
Definition civetweb.c:10837
#define mg_readdir(x)
Definition civetweb.c:943
static pthread_key_t sTlsKey
Definition civetweb.c:1621
static void sockaddr_to_string(char *buf, size_t len, const union usa *usa)
Definition civetweb.c:3366
static void open_auth_file(struct mg_connection *conn, const char *path, struct mg_file *filep)
Definition civetweb.c:8771
int mg_strncasecmp(const char *s1, const char *s2, size_t len)
Definition civetweb.c:3091
#define IP_ADDR_STR_LEN
Definition civetweb.c:1773
static int check_authorization(struct mg_connection *conn, const char *path)
Definition civetweb.c:9217
#define vsnprintf_impl
Definition civetweb.c:903
#define HEXTOI(x)
#define mg_malloc_ctx(a, c)
Definition civetweb.c:1532
static void redirect_to_https_port(struct mg_connection *conn, int port)
Definition civetweb.c:14557
static int should_switch_to_protocol(const struct mg_connection *conn)
Definition civetweb.c:14234
void mg_unlock_connection(struct mg_connection *conn)
Definition civetweb.c:13331
static void remove_bad_file(const struct mg_connection *conn, const char *path)
Definition civetweb.c:10920
const struct mg_option * mg_get_valid_options(void)
Definition civetweb.c:2905
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:5883
static int print_dir_entry(struct mg_connection *conn, struct de *de)
Definition civetweb.c:9917
static int mg_path_suspicious(const struct mg_connection *conn, const char *path)
Definition civetweb.c:2943
static int set_non_blocking_mode(SOCKET sock)
Definition civetweb.c:5991
static void ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
Definition civetweb.c:17132
const char * proto
Definition civetweb.c:18822
int mg_send_http_error(struct mg_connection *conn, int status, const char *fmt,...)
Definition civetweb.c:4677
static const char * get_rel_url_at_current_server(const char *uri, const struct mg_connection *conn)
Definition civetweb.c:18909
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:3456
#define LOCK_DURATION_S
Definition civetweb.c:2393
void mg_lock_context(struct mg_context *ctx)
Definition civetweb.c:13340
#define MAX_WORKER_THREADS
Definition civetweb.c:469
int mg_base64_decode(const char *src, size_t src_len, unsigned char *dst, size_t *dst_len)
Definition civetweb.c:7604
static void put_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:12477
static void do_ssi_exec(struct mg_connection *conn, char *tag)
Definition civetweb.c:12762
static void tls_dtor(void *key)
Definition civetweb.c:16714
static int is_civetweb_webdav_method(const struct mg_connection *conn)
Definition civetweb.c:7703
#define realloc
Definition civetweb.c:1577
const void * SOCK_OPT_TYPE
Definition civetweb.c:867
static int header_has_option(const char *header, const char *option)
Definition civetweb.c:4030
int mg_send_http_redirect(struct mg_connection *conn, const char *target_url, int redirect_code)
Definition civetweb.c:4733
int mg_get_cookie(const char *cookie_header, const char *var_name, char *dst, size_t dst_size)
Definition civetweb.c:7477
static int ssl_get_client_cert_info(const struct mg_connection *conn, struct mg_client_cert *client_cert)
Definition civetweb.c:17055
#define mg_cry_ctx_internal(ctx, fmt,...)
Definition civetweb.c:2658
@ PROTOCOL_TYPE_HTTP1
Definition civetweb.c:2539
@ PROTOCOL_TYPE_HTTP2
Definition civetweb.c:2541
@ PROTOCOL_TYPE_WEBSOCKET
Definition civetweb.c:2540
@ ENABLE_DIRECTORY_LISTING
Definition civetweb.c:2052
@ SSL_SHORT_TRUST
Definition civetweb.c:2070
@ GLOBAL_PASSWORDS_FILE
Definition civetweb.c:2054
@ ADDITIONAL_HEADER
Definition civetweb.c:2105
@ SSL_VERIFY_DEPTH
Definition civetweb.c:2066
@ ACCESS_CONTROL_ALLOW_ORIGIN
Definition civetweb.c:2092
@ ACCESS_CONTROL_EXPOSE_HEADERS
Definition civetweb.c:2095
@ SSL_PROTOCOL_VERSION
Definition civetweb.c:2069
@ SSL_DO_VERIFY_PEER
Definition civetweb.c:2062
@ SSL_CERTIFICATE
Definition civetweb.c:2058
@ RUN_AS_USER
Definition civetweb.c:1971
@ ALLOW_INDEX_SCRIPT_SUB_RES
Definition civetweb.c:2106
@ CGI2_BUFFERING
Definition civetweb.c:2025
@ SSL_CACHE_TIMEOUT
Definition civetweb.c:2063
@ CONNECTION_QUEUE_SIZE
Definition civetweb.c:1976
@ ENABLE_KEEP_ALIVE
Definition civetweb.c:1985
@ ACCESS_CONTROL_LIST
Definition civetweb.c:2056
@ CGI_INTERPRETER_ARGS
Definition civetweb.c:2012
@ SSL_CA_PATH
Definition civetweb.c:2064
@ AUTHENTICATION_DOMAIN
Definition civetweb.c:2049
@ ACCESS_CONTROL_ALLOW_HEADERS
Definition civetweb.c:2094
@ SSL_DEFAULT_VERIFY_PATHS
Definition civetweb.c:2067
@ CGI2_EXTENSIONS
Definition civetweb.c:2018
@ ENABLE_WEBDAV
Definition civetweb.c:2053
@ ACCESS_CONTROL_ALLOW_METHODS
Definition civetweb.c:2093
@ STRICT_HTTPS_MAX_AGE
Definition civetweb.c:2103
@ HIDE_FILES
Definition civetweb.c:2061
@ ERROR_LOG_FILE
Definition civetweb.c:2007
@ CGI2_INTERPRETER_ARGS
Definition civetweb.c:2021
@ STATIC_FILE_MAX_AGE
Definition civetweb.c:2099
@ LINGER_TIMEOUT
Definition civetweb.c:1975
@ URL_REWRITE_PATTERN
Definition civetweb.c:2060
@ THROTTLE
Definition civetweb.c:1984
@ SSL_CIPHER_LIST
Definition civetweb.c:2068
@ KEEP_ALIVE_TIMEOUT
Definition civetweb.c:1987
@ CGI_EXTENSIONS
Definition civetweb.c:2009
@ STATIC_FILE_CACHE_CONTROL
Definition civetweb.c:2100
@ ERROR_PAGES
Definition civetweb.c:2097
@ CGI_ENVIRONMENT
Definition civetweb.c:2010
@ PUT_DELETE_PASSWORDS_FILE
Definition civetweb.c:2047
@ ENABLE_AUTH_DOMAIN_CHECK
Definition civetweb.c:2050
@ NUM_OPTIONS
Definition civetweb.c:2108
@ MAX_REQUEST_SIZE
Definition civetweb.c:1974
@ DECODE_URL
Definition civetweb.c:1992
@ NUM_THREADS
Definition civetweb.c:1969
@ DOCUMENT_ROOT
Definition civetweb.c:2003
@ SSL_CERTIFICATE_CHAIN
Definition civetweb.c:2059
@ FALLBACK_DOCUMENT_ROOT
Definition civetweb.c:2004
@ CGI2_ENVIRONMENT
Definition civetweb.c:2019
@ PRESPAWN_THREADS
Definition civetweb.c:1970
@ CGI_BUFFERING
Definition civetweb.c:2016
@ PROTECT_URI
Definition civetweb.c:2048
@ ACCESS_LOG_FILE
Definition civetweb.c:2006
@ ACCESS_CONTROL_ALLOW_CREDENTIALS
Definition civetweb.c:2096
@ REQUEST_TIMEOUT
Definition civetweb.c:1986
@ SSI_EXTENSIONS
Definition civetweb.c:2051
@ CONFIG_TCP_NODELAY
Definition civetweb.c:1972
@ SSL_CA_FILE
Definition civetweb.c:2065
@ CGI_INTERPRETER
Definition civetweb.c:2011
@ LISTEN_BACKLOG_SIZE
Definition civetweb.c:1977
@ DECODE_QUERY_STRING
Definition civetweb.c:1993
@ LISTENING_PORTS
Definition civetweb.c:1968
@ CGI2_INTERPRETER
Definition civetweb.c:2020
@ INDEX_FILES
Definition civetweb.c:2055
@ EXTRA_MIME_TYPES
Definition civetweb.c:2057
static void mg_snprintf(const struct mg_connection *conn, int *truncated, char *buf, size_t buflen, const char *fmt,...)
Definition civetweb.c:3215
#define mg_opendir(conn, x)
Definition civetweb.c:941
static char * mg_strndup_ctx(const char *ptr, size_t len, struct mg_context *ctx)
Definition civetweb.c:3119
static int put_dir(struct mg_connection *conn, const char *path)
Definition civetweb.c:10883
#define UINT64_FMT
Definition civetweb.c:947
static void send_static_cache_header(struct mg_connection *conn)
Definition civetweb.c:4148
static int mg_socketpair(int *sockA, int *sockB)
Definition civetweb.c:21020
static ptrdiff_t mg_atomic_dec(volatile ptrdiff_t *addr)
Definition civetweb.c:1172
#define INVALID_SOCKET
Definition civetweb.c:945
static int print_dav_dir_entry(struct de *de, void *data)
Definition civetweb.c:13064
static void delete_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:12612
#define mg_calloc_ctx(a, b, c)
Definition civetweb.c:1533
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:19710
#define mg_closedir(x)
Definition civetweb.c:942
#define free
Definition civetweb.c:1578
int mg_get_server_ports(const struct mg_context *ctx, int size, struct mg_server_port *ports)
Definition civetweb.c:3320
static ptrdiff_t mg_atomic_inc(volatile ptrdiff_t *addr)
Definition civetweb.c:1141
int mg_printf(struct mg_connection *conn, const char *fmt,...)
Definition civetweb.c:7255
static int abort_cgi_process(void *data)
Definition civetweb.c:11871
static int should_keep_alive(const struct mg_connection *conn)
Definition civetweb.c:4058
static __inline void * mg_malloc(size_t a)
Definition civetweb.c:1509
static int mg_fopen(const struct mg_connection *conn, const char *path, int mode, struct mg_file *filep)
Definition civetweb.c:2987
static int alloc_printf(char **out_buf, const char *fmt,...)
Definition civetweb.c:7217
void mg_disable_connection_keep_alive(struct mg_connection *conn)
Definition civetweb.c:22725
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:9586
static void mg_global_lock(void)
Definition civetweb.c:1114
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:10531
static void dav_move_file(struct mg_connection *conn, const char *path, int do_copy)
Definition civetweb.c:12313
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:17375
static const char * ssl_error(void)
Definition civetweb.c:17020
static int must_hide_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:10071
static void dav_proppatch(struct mg_connection *conn, const char *path)
Definition civetweb.c:13286
static void log_access(const struct mg_connection *)
Definition civetweb.c:16456
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:18681
#define ERRNO
Definition civetweb.c:944
#define mg_remove(conn, x)
Definition civetweb.c:939
int mg_send_http_ok(struct mg_connection *conn, const char *mime_type, long long content_length)
Definition civetweb.c:4691
static void close_all_listening_sockets(struct mg_context *ctx)
Definition civetweb.c:15794
static const struct @141 abs_uri_protocols[]
#define closesocket(a)
Definition civetweb.c:937
void mg_send_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:10821
static void send_authorization_request(struct mg_connection *conn, const char *realm)
Definition civetweb.c:9273
static int get_req_headers(const struct mg_request_info *ri, const char *name, const char **output, int output_max_size)
Definition civetweb.c:3910
static int check_acl(struct mg_context *phys_ctx, const union usa *sa)
Definition civetweb.c:16622
static const struct mg_option config_options[]
Definition civetweb.c:2115
static const struct mg_http_method_info * get_http_method_info(const char *method)
Definition civetweb.c:11194
#define STOP_FLAG_IS_ZERO(f)
Definition civetweb.c:2382
unsigned default_port
Definition civetweb.c:18824
const char * mg_get_response_code_text(const struct mg_connection *conn, int response_code)
Definition civetweb.c:4291
static int get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
Definition civetweb.c:19182
static void mg_cry_internal_wrap(const struct mg_connection *conn, struct mg_context *ctx, const char *func, unsigned line, const char *fmt,...)
Definition civetweb.c:3555
static int set_uid_option(struct mg_context *phys_ctx)
Definition civetweb.c:16660
static int switch_domain_context(struct mg_connection *conn)
Definition civetweb.c:14496
static struct mg_connection * fake_connection(struct mg_connection *fc, struct mg_context *ctx)
Definition civetweb.c:3544
static void reset_per_request_attributes(struct mg_connection *conn)
Definition civetweb.c:18111
static void handle_file_based_request(struct mg_connection *conn, const char *path, struct mg_file *filep)
Definition civetweb.c:15692
static int compare_dir_entries(const void *p1, const void *p2, void *arg)
Definition civetweb.c:10024
#define FUNCTION_MAY_BE_UNUSED
Definition civetweb.c:320
static pthread_mutex_t * ssl_mutexes
Definition civetweb.c:16875
static int get_option_index(const char *name)
Definition civetweb.c:3231
static char * mg_strdup(const char *str)
Definition civetweb.c:3140
static void send_no_cache_header(struct mg_connection *conn)
Definition civetweb.c:4130
static void master_thread_run(struct mg_context *ctx)
Definition civetweb.c:20564
@ AUTH_HANDLER
Definition civetweb.c:2283
@ REQUEST_HANDLER
Definition civetweb.c:2283
@ WEBSOCKET_HANDLER
Definition civetweb.c:2283
static const char month_names[][4]
Definition civetweb.c:1861
const struct mg_response_info * mg_get_response_info(const struct mg_connection *conn)
Definition civetweb.c:3635
static __inline void * mg_realloc(void *a, size_t b)
Definition civetweb.c:1521
static void accept_new_connection(const struct socket *listener, struct mg_context *ctx)
Definition civetweb.c:20476
static volatile ptrdiff_t cryptolib_users
Definition civetweb.c:17250
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:10363
static int get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
Definition civetweb.c:19013
#define CRYPTO_LIB
Definition civetweb.c:931
static pthread_mutex_t global_lock_mutex
Definition civetweb.c:1109
struct mg_context * mg_start2(struct mg_init_data *init, struct mg_error_data *error)
Definition civetweb.c:21125
#define CGI_ENVIRONMENT_SIZE
Definition civetweb.c:492
static int parse_http_headers(char **buf, struct mg_header hdr[(64)])
Definition civetweb.c:11037
#define mg_get_option
Definition civetweb.c:3257
static long ssl_get_protocol(int version_id)
Definition civetweb.c:17452
void * mg_get_user_context_data(const struct mg_connection *conn)
Definition civetweb.c:3274
static int mg_init_library_called
Definition civetweb.c:1589
long long mg_store_body(struct mg_connection *conn, const char *path)
Definition civetweb.c:10933
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:19319
#define DEBUG_ASSERT(cond)
Definition civetweb.c:264
static size_t mg_str_append(char **dst, char *end, const char *src)
Definition civetweb.c:22166
static int pull_inner(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
Definition civetweb.c:6346
#define INT64_FMT
Definition civetweb.c:946
int mg_get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int timeout)
Definition civetweb.c:19267
#define MG_FILE_COMPRESSION_SIZE_LIMIT
Definition civetweb.c:482
#define USA_IN_PORT_UNSAFE(s)
Definition civetweb.c:1906
unsigned mg_check_feature(unsigned feature)
Definition civetweb.c:22103
static int mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
Definition civetweb.c:6715
static int mg_send_http_error_impl(struct mg_connection *conn, int status, const char *fmt, va_list args)
Definition civetweb.c:4490
struct mg_context * mg_start(const struct mg_callbacks *callbacks, void *user_data, const char **options)
Definition civetweb.c:21878
#define MG_FOPEN_MODE_READ
Definition civetweb.c:2915
#define STOP_FLAG_ASSIGN(f, v)
Definition civetweb.c:2384
static int extention_matches_script(struct mg_connection *conn, const char *filename)
Definition civetweb.c:7725
static void get_host_from_request_info(struct vec *host, const struct mg_request_info *ri)
Definition civetweb.c:14458
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:14810
int mg_start_domain(struct mg_context *ctx, const char **options)
Definition civetweb.c:22095
static pthread_mutexattr_t pthread_mutex_attr
Definition civetweb.c:1094
void mg_unlock_context(struct mg_context *ctx)
Definition civetweb.c:13349
static int ssl_servername_callback(SSL *ssl, int *ad, void *arg)
Definition civetweb.c:17501
int mg_modify_passwords_file(const char *fname, const char *domain, const char *user, const char *pass)
Definition civetweb.c:9519
static char * skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar)
Definition civetweb.c:3831
static void fclose_on_exec(struct mg_file_access *filep, struct mg_connection *conn)
Definition civetweb.c:10507
int mg_start_thread(mg_thread_func_t func, void *param)
Definition civetweb.c:5815
static const char * get_header(const struct mg_header *hdr, int num_hdr, const char *name)
Definition civetweb.c:3894
static int mg_inet_pton(int af, const char *src, void *dst, size_t dstlen, int resolve_src)
Definition civetweb.c:9545
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:17674
unsigned mg_init_library(unsigned features)
Definition civetweb.c:23030
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:18697
static int forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
Definition civetweb.c:11498
static void dav_lock_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:13121
static int set_gpass_option(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
Definition civetweb.c:18071
static int skip_to_end_of_word_and_terminate(char **ppw, int eol)
Definition civetweb.c:10991
static int get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
Definition civetweb.c:19090
static int set_tcp_nodelay(const struct socket *so, int nodelay_on)
Definition civetweb.c:18153
int mg_get_var(const char *data, size_t data_len, const char *name, char *dst, size_t dst_len)
Definition civetweb.c:7309
#define ARRAY_SIZE(array)
Definition civetweb.c:510
static void dav_unlock_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:13253
static int parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
Definition civetweb.c:15839
static int get_first_ssl_listener_index(const struct mg_context *ctx)
Definition civetweb.c:14443
void mg_send_mime_file(struct mg_connection *conn, const char *path, const char *mime_type)
Definition civetweb.c:10828
const char * mg_get_header(const struct mg_connection *conn, const char *name)
Definition civetweb.c:3929
#define mg_mkdir(conn, path, mode)
Definition civetweb.c:938
int mg_split_form_urlencoded(char *data, struct mg_header *form_fields, unsigned num_form_fields)
Definition civetweb.c:7378
static int hexdump2string(void *mem, int memlen, char *buf, int buflen)
Definition civetweb.c:17029
#define UTF8_PATH_MAX
Definition civetweb.c:865
static const struct mg_http_method_info http_methods[]
Definition civetweb.c:11127
static int mg_poll(struct pollfd *pfd, unsigned int n, int milliseconds, const stop_flag_t *stop_flag)
Definition civetweb.c:6053
static void handle_directory_request(struct mg_connection *conn, const char *dir)
Definition civetweb.c:10250
static int set_ports_option(struct mg_context *phys_ctx)
Definition civetweb.c:16104
static int read_auth_file(struct mg_file *filep, struct read_auth_file_struct *workdata, int depth)
Definition civetweb.c:9032
static int mg_start_thread_with_id(mg_thread_func_t func, void *param, pthread_t *threadidptr)
Definition civetweb.c:5841
unsigned mg_exit_library(void)
Definition civetweb.c:23160
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:17150
static void gmt_time_string(char *buf, size_t buf_len, time_t *t)
Definition civetweb.c:3415
int mg_send_digest_access_authentication_request(struct mg_connection *conn, const char *realm)
Definition civetweb.c:9321
static const char * mg_strcasestr(const char *big_str, const char *small_str)
Definition civetweb.c:3147
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:19677
static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
Definition civetweb.c:6660
static void handle_ssi_file_request(struct mg_connection *conn, const char *path, struct mg_file *filep)
Definition civetweb.c:12907
int mg_write(struct mg_connection *conn, const void *buf, size_t len)
Definition civetweb.c:7000
static int set_throttle(const char *spec, const union usa *rsa, const char *uri)
Definition civetweb.c:14397
static void ssl_info_callback(const SSL *ssl, int what, int ret)
Definition civetweb.c:17485
#define calloc
Definition civetweb.c:1576
static void bin2str(char *to, const unsigned char *p, size_t len)
Definition civetweb.c:8688
const struct mg_request_info * mg_get_request_info(const struct mg_connection *conn)
Definition civetweb.c:3595
static int substitute_index_file_aux(struct mg_connection *conn, char *path, size_t path_len, struct mg_file_stat *filestat)
Definition civetweb.c:7795
#define MG_FOPEN_MODE_APPEND
Definition civetweb.c:2921
char * mg_md5(char buf[33],...)
Definition civetweb.c:8703
#define STOP_FLAG_IS_TWO(f)
Definition civetweb.c:2383
static const char * next_option(const char *list, struct vec *val, struct vec *eq_val)
Definition civetweb.c:3973
static const char * suggest_connection_header(const struct mg_connection *conn)
Definition civetweb.c:4120
static char * all_methods
Definition civetweb.c:11190
static int alloc_vprintf(char **out_buf, char *prealloc_buf, size_t prealloc_size, const char *fmt, va_list ap)
Definition civetweb.c:7160
static time_t parse_date_string(const char *datetime)
Definition civetweb.c:8298
int SOCKET
Definition civetweb.c:948
static void do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, int include_level)
Definition civetweb.c:12677
static void uninitialize_openssl(void)
Definition civetweb.c:18029
int mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen)
Definition civetweb.c:3819
static int parse_match_net(const struct vec *vec, const union usa *sa, int no_strict)
Definition civetweb.c:14288
int mg_modify_passwords_file_ha1(const char *fname, const char *domain, const char *user, const char *ha1)
Definition civetweb.c:9356
static void send_options(struct mg_connection *conn)
Definition civetweb.c:12951
static int lowercase(const char *s)
Definition civetweb.c:3084
static void release_handler_ref(struct mg_connection *conn, struct mg_handler_info *handler_info)
Definition civetweb.c:15009
static int parse_range_header(const char *header, int64_t *a, int64_t *b)
Definition civetweb.c:10480
static int mg_stat(const struct mg_connection *conn, const char *path, struct mg_file_stat *filep)
Definition civetweb.c:5765
static int get_uri_type(const char *uri)
Definition civetweb.c:18839
#define SSL_LIB
Definition civetweb.c:928
#define DEBUG_TRACE(fmt,...)
Definition civetweb.c:246
static void get_system_name(char **sysName)
Definition civetweb.c:20939
int mg_url_encode(const char *src, char *dst, size_t dst_len)
Definition civetweb.c:9889
static const char * header_val(const struct mg_connection *conn, const char *header)
Definition civetweb.c:16439
#define mg_cry_internal(conn, fmt,...)
Definition civetweb.c:2655
static int set_acl_option(struct mg_context *phys_ctx)
Definition civetweb.c:18097
static void mg_set_thread_name(const char *name)
Definition civetweb.c:2852
static void get_mime_type(struct mg_connection *conn, const char *path, struct vec *vec)
Definition civetweb.c:8653
static int set_blocking_mode(SOCKET sock)
Definition civetweb.c:6005
#define MSG_NOSIGNAL
Definition civetweb.c:1776
#define MAX_CGI_ENVIR_VARS
Definition civetweb.c:497
static char * mg_strdup_ctx(const char *str, struct mg_context *ctx)
Definition civetweb.c:3134
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:19737
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded)
Definition civetweb.c:7269
void mg_set_user_connection_data(const struct mg_connection *const_conn, void *data)
Definition civetweb.c:3297
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:7320
static int is_not_modified(const struct mg_connection *conn, const struct mg_file_stat *filestat)
Definition civetweb.c:10779
static void worker_thread_run(struct mg_connection *conn)
Definition civetweb.c:20202
static int prepare_cgi_environment(struct mg_connection *conn, const char *prog, struct cgi_environment *env, int cgi_config_idx)
Definition civetweb.c:11668
static const char * get_proto_name(const struct mg_connection *conn)
Definition civetweb.c:3648
static void * master_thread(void *thread_func_param)
Definition civetweb.c:20760
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:14602
static int is_file_opened(const struct mg_file_access *fileacc)
Definition civetweb.c:2925
static int get_http_header_len(const char *buf, int buflen)
Definition civetweb.c:8245
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:19768
void mg_close_connection(struct mg_connection *conn)
Definition civetweb.c:18378
static int is_valid_port(unsigned long port)
Definition civetweb.c:9538
static void handle_propfind(struct mg_connection *conn, const char *path, struct mg_file_stat *filep)
Definition civetweb.c:13078
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:7898
static int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap)
Definition civetweb.c:7237
#define mg_realloc_ctx(a, b, c)
Definition civetweb.c:1534
static void send_additional_header(struct mg_connection *conn)
Definition civetweb.c:4196
static int push_all(struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int len)
Definition civetweb.c:6298
static int is_put_or_delete_method(const struct mg_connection *conn)
Definition civetweb.c:7685
static int read_message(FILE *fp, struct mg_connection *conn, char *buf, int bufsiz, int *nread)
Definition civetweb.c:11421
#define MG_FOPEN_MODE_WRITE
Definition civetweb.c:2918
void * mg_get_user_connection_data(const struct mg_connection *conn)
Definition civetweb.c:3310
static int authorize(struct mg_connection *conn, struct mg_file *filep, const char *realm)
Definition civetweb.c:9160
static void send_ssi_file(struct mg_connection *, const char *, struct mg_file *, int)
Definition civetweb.c:12801
#define mg_static_assert(cond, txt)
Definition civetweb.c:126
#define SOCKET_TIMEOUT_QUANTUM
Definition civetweb.c:477
static void produce_socket(struct mg_context *ctx, const struct socket *sp)
Definition civetweb.c:20155
static void dav_mkcol(struct mg_connection *conn, const char *path)
Definition civetweb.c:12234
int mg_send_chunk(struct mg_connection *conn, const char *chunk, unsigned int chunk_len)
Definition civetweb.c:7081
static void remove_dot_segments(char *inout)
Definition civetweb.c:8362
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:14882
void mg_set_auth_handler(struct mg_context *ctx, const char *uri, mg_authorization_handler handler, void *cbdata)
Definition civetweb.c:14860
int mg_start_domain2(struct mg_context *ctx, const char **options, struct mg_error_data *error)
Definition civetweb.c:21893
#define mg_pollfd
Definition civetweb.c:968
#define MG_BUF_LEN
Definition civetweb.c:502
static int alloc_vprintf2(char **buf, const char *fmt, va_list ap)
Definition civetweb.c:7127
static int extention_matches_template_text(struct mg_connection *conn, const char *filename)
Definition civetweb.c:7771
static int is_in_script_path(const struct mg_connection *conn, const char *path)
Definition civetweb.c:14967
static int should_decode_query_string(const struct mg_connection *conn)
Definition civetweb.c:4108
static double mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before)
Definition civetweb.c:3438
static void free_context(struct mg_context *ctx)
Definition civetweb.c:20778
static int sslize(struct mg_connection *conn, int(*func)(SSL *), const struct mg_client_options *client_options)
Definition civetweb.c:16879
static void legacy_init(const char **options)
Definition civetweb.c:20988
static void construct_etag(char *buf, size_t buf_len, const struct mg_file_stat *filestat)
Definition civetweb.c:10492
int mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen)
Definition civetweb.c:22526
int volatile stop_flag_t
Definition civetweb.c:2381
static int mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads)
Definition civetweb.c:21083
static int refresh_trust(struct mg_connection *conn)
Definition civetweb.c:16801
#define SHUTDOWN_WR
Definition civetweb.c:521
void * mg_get_thread_pointer(const struct mg_connection *conn)
Definition civetweb.c:3281
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:3166
static int should_decode_url(const struct mg_connection *conn)
Definition civetweb.c:4097
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:3675
static int consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index, int counter_was_preincremented)
Definition civetweb.c:20108
char static_assert_replacement[1]
Definition civetweb.c:125
#define ERROR_TRY_AGAIN(err)
Definition civetweb.c:451
static void send_cors_header(struct mg_connection *conn)
Definition civetweb.c:4225
static unsigned char b64reverse(char letter)
Definition civetweb.c:7579
static unsigned long mg_current_thread_id(void)
Definition civetweb.c:1667
#define HTTP1_only
Definition civetweb.c:6830
@ CONNECTION_TYPE_RESPONSE
Definition civetweb.c:2535
@ CONNECTION_TYPE_INVALID
Definition civetweb.c:2533
@ CONNECTION_TYPE_REQUEST
Definition civetweb.c:2534
#define malloc
Definition civetweb.c:1575
#define INT64_MAX
Definition civetweb.c:517
static void mg_strlcpy(char *dst, const char *src, size_t n)
Definition civetweb.c:3074
static int remove_directory(struct mg_connection *conn, const char *dir)
Definition civetweb.c:10144
static const char * get_http_version(const struct mg_connection *conn)
Definition civetweb.c:3950
static __inline void * mg_calloc(size_t a, size_t b)
Definition civetweb.c:1515
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:8726
#define va_copy(x, y)
Definition civetweb.c:1023
static void handle_request(struct mg_connection *)
Definition civetweb.c:15026
static int parse_http_response(char *buf, int len, struct mg_response_info *ri)
Definition civetweb.c:11315
static void * cryptolib_dll_handle
Definition civetweb.c:17241
static uint64_t mg_get_current_time_ns(void)
Definition civetweb.c:1717
const char * mg_get_builtin_mime_type(const char *path)
Definition civetweb.c:8631
static const char * mg_fgets(char *buf, size_t size, struct mg_file *filep)
Definition civetweb.c:8995
int mg_read(struct mg_connection *conn, void *buf, size_t len)
Definition civetweb.c:6835
#define IGNORE_UNUSED_RESULT(a)
Definition civetweb.c:295
static void addenv(struct cgi_environment *env, const char *fmt,...)
Definition civetweb.c:11598
static void discard_unread_request_data(struct mg_connection *conn)
Definition civetweb.c:6705
int mg_strcasecmp(const char *s1, const char *s2)
Definition civetweb.c:3106
static int mg_fclose(struct mg_file_access *fileacc)
Definition civetweb.c:3058
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:19460
void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata)
Definition civetweb.c:14788
static void * worker_thread(void *thread_func_param)
Definition civetweb.c:20456
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:18442
static __inline void mg_free(void *a)
Definition civetweb.c:1527
static void handle_request_stat_log(struct mg_connection *conn)
Definition civetweb.c:6783
int mg_check_digest_access_authentication(struct mg_connection *conn, const char *realm, const char *filename)
Definition civetweb.c:9192
static int mg_join_thread(pthread_t threadid)
Definition civetweb.c:5872
static void mg_global_unlock(void)
Definition civetweb.c:1122
int mg_get_system_info(char *buffer, int buflen)
Definition civetweb.c:22185
static void handle_not_modified_static_file_request(struct mg_connection *conn, struct mg_file *filep)
Definition civetweb.c:10794
static int init_ssl_ctx(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
Definition civetweb.c:17920
static void close_socket_gracefully(struct mg_connection *conn)
Definition civetweb.c:18175
struct mg_context * mg_get_context(const struct mg_connection *conn)
Definition civetweb.c:3260
static int is_ssl_port_used(const char *ports)
Definition civetweb.c:16041
static void init_connection(struct mg_connection *conn)
Definition civetweb.c:19797
static int print_props(struct mg_connection *conn, const char *uri, const char *name, struct mg_file_stat *filep)
Definition civetweb.c:12979
#define INITIAL_DEPTH
Definition civetweb.c:9014
static int dir_scan_callback(struct de *de, void *data)
Definition civetweb.c:10219
size_t proto_len
Definition civetweb.c:18823
int mg_base64_encode(const unsigned char *src, size_t src_len, char *dst, size_t *dst_len)
Definition civetweb.c:7526
static void set_close_on_exec(int fd, const struct mg_connection *conn, struct mg_context *ctx)
Definition civetweb.c:5792
static int get_month_index(const char *s)
Definition civetweb.c:8282
#define NUM_WEBDAV_LOCKS
Definition civetweb.c:2390
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:14830
static void * ssllib_dll_handle
Definition civetweb.c:17240
static int mg_fgetc(struct mg_file *filep)
Definition civetweb.c:12786
static const struct @140 builtin_mime_types[]
static void handle_cgi_request(struct mg_connection *conn, const char *prog, int cgi_config_idx)
Definition civetweb.c:11906
#define mg_sleep(x)
Definition civetweb.c:940
#define PASSWORDS_FILE_NAME
Definition civetweb.c:486
static void close_connection(struct mg_connection *conn)
Definition civetweb.c:18299
@ CONTEXT_WS_CLIENT
Definition civetweb.c:2323
@ CONTEXT_INVALID
Definition civetweb.c:2320
@ CONTEXT_SERVER
Definition civetweb.c:2321
@ CONTEXT_HTTP_CLIENT
Definition civetweb.c:2322
static int initialize_openssl(char *ebuf, size_t ebuf_len)
Definition civetweb.c:17256
static int substitute_index_file(struct mg_connection *conn, char *path, size_t path_len, struct mg_file_stat *filestat)
Definition civetweb.c:7843
void mg_stop(struct mg_context *ctx)
Definition civetweb.c:20890
#define STRUCT_FILE_INITIALIZER
Definition civetweb.c:1937
static volatile ptrdiff_t thread_idx_max
Definition civetweb.c:1622
void * mg_get_user_data(const struct mg_context *ctx)
Definition civetweb.c:3267
static int scan_directory(struct mg_connection *conn, const char *dir, void *data, int(*cb)(struct de *, void *))
Definition civetweb.c:10085
static int push_inner(struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int len, double timeout)
Definition civetweb.c:6118
const char * mg_version(void)
Definition civetweb.c:3588
static int parse_auth_header(struct mg_connection *conn, char *buf, size_t buf_size, struct auth_header *auth_header)
Definition civetweb.c:8854
static uint64_t get_random(void)
Definition civetweb.c:6024
void mg_lock_connection(struct mg_connection *conn)
Definition civetweb.c:13322
int mg_send_file_body(struct mg_connection *conn, const char *path)
Definition civetweb.c:10762
#define mg_cry
Definition civetweb.c:3584
static int is_valid_http_method(const char *method)
Definition civetweb.c:11213
static void url_decode_in_place(char *buf)
Definition civetweb.c:7301
int mg_websocket_client_write(struct mg_connection *conn, int opcode, const char *data, size_t data_len)
#define MG_MAX_HEADERS
Definition civetweb.h:141
@ 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
int mg_response_header_add(struct mg_connection *conn, const char *header, const char *value, int value_len)
Definition response.inl:130
void *(* mg_thread_func_t)(void *)
Definition civetweb.h:1307
int mg_response_header_send(struct mg_connection *conn)
Definition response.inl:270
int mg_websocket_write(struct mg_connection *conn, int opcode, const char *data, size_t data_len)
#define CIVETWEB_VERSION
Definition civetweb.h:26
#define PRINTF_FORMAT_STRING(s)
Definition civetweb.h:877
int(* mg_authorization_handler)(struct mg_connection *conn, void *cbdata)
Definition civetweb.h:606
@ 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
void(* mg_websocket_ready_handler)(struct mg_connection *, void *)
Definition civetweb.h:549
#define CIVETWEB_API
Definition civetweb.h:43
int mg_response_header_add_lines(struct mg_connection *conn, const char *http1_headers)
Definition response.inl:219
@ 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
#define PRINTF_ARGS(x, y)
Definition civetweb.h:883
@ 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
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
int mg_response_header_start(struct mg_connection *conn, int status)
Definition response.inl:80
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
TLine * line
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
static ptrdiff_t match_prefix_strlen(const char *pattern, const char *str)
Definition match.inl:254
static ptrdiff_t match_prefix(const char *pattern, size_t pattern_len, const char *str)
Definition match.inl:244
unsigned char md5_byte_t
Definition md5.inl:50
MD5_STATIC void md5_finish(md5_state_t *pms, md5_byte_t digest[16])
Definition md5.inl:450
MD5_STATIC void md5_init(md5_state_t *pms)
Definition md5.inl:402
MD5_STATIC void md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes)
Definition md5.inl:412
RooArgList L(Args_t &&... args)
Definition RooArgList.h:156
#define SSL_VERIFY_NONE
#define SSL_OP_CIPHER_SERVER_PREFERENCE
#define SSL_OP_NO_SSLv2
struct ssl_ctx_st SSL_CTX
#define SSL_OP_NO_TLSv1_3
#define SSL_OP_SINGLE_DH_USE
@ TLS_ALPN
@ TLS_Mandatory
#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT
#define SSL_SESS_CACHE_BOTH
#define SSL_TLSEXT_ERR_OK
#define SSL_TLSEXT_ERR_NOACK
#define OPENSSL_INIT_LOAD_CRYPTO_STRINGS
#define SSL_OP_NO_COMPRESSION
#define SSL_ERROR_SYSCALL
#define SSL_ERROR_WANT_READ
#define SSL_VERIFY_PEER
struct ssl_st SSL
#define SSL_OP_NO_TLSv1
#define SSL_ERROR_WANT_ACCEPT
#define SSL_ERROR_WANT_X509_LOOKUP
#define SSL_CB_HANDSHAKE_START
#define SSL_ERROR_WANT_CONNECT
#define SSL_OP_NO_TLSv1_2
#define SSL_OP_NO_SSLv3
#define SSL_OP_NO_RENEGOTIATION
#define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
#define TLSEXT_NAMETYPE_host_name
#define SSL_CB_HANDSHAKE_DONE
#define SSL_OP_ALL
#define OPENSSL_INIT_LOAD_SSL_STRINGS
#define SSL_OP_NO_TLSv1_1
static int tls_feature_missing[TLS_END_OF_LIST]
#define SSL_ERROR_WANT_WRITE
static void free_buffered_response_header_list(struct mg_connection *conn)
Definition response.inl:17
TCanvas * roots()
Definition roots.C:1
SHA_API void SHA1_Init(SHA_CTX *context)
Definition sha1.inl:258
SHA_API void SHA1_Update(SHA_CTX *context, const uint8_t *data, const uint32_t len)
Definition sha1.inl:271
SHA_API void SHA1_Final(unsigned char *digest, SHA_CTX *context)
Definition sha1.inl:298
#define blk(block, i)
Definition sha1.inl:124
TCanvas * slash()
Definition slash.C:1
static void mg_sort(void *data, size_t elemcount, size_t elemsize, int(*compfunc)(const void *data1, const void *data2, void *userarg), void *userarg)
Definition sort.inl:5
static const char * what
Definition stlLoader.cc:5
char * plain_password
Definition civetweb.c:8847
char * uri
Definition civetweb.c:8848
char * cnonce
Definition civetweb.c:8848
char * response
Definition civetweb.c:8848
char * user
Definition civetweb.c:8845
char * nc
Definition civetweb.c:8848
char * qop
Definition civetweb.c:8848
char * nonce
Definition civetweb.c:8848
struct mg_connection * conn
Definition civetweb.c:11579
char * file_name
Definition civetweb.c:2650
struct mg_file_stat file
Definition civetweb.c:2651
size_t num_entries
Definition civetweb.c:10212
struct de * entries
Definition civetweb.c:10211
size_t arr_size
Definition civetweb.c:10213
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
Definition civetweb.h:1484
time_t last_throttle_time
Definition civetweb.c:2635
int64_t content_len
Definition civetweb.c:2590
struct timespec req_time
Definition civetweb.c:2587
int64_t consumed_content
Definition civetweb.c:2596
char * path_info
Definition civetweb.c:2605
int64_t num_bytes_sent
Definition civetweb.c:2589
int connection_type
Definition civetweb.c:2559
pthread_mutex_t mutex
Definition civetweb.c:2637
struct socket client
Definition civetweb.c:2579
struct mg_response_info response_info
Definition civetweb.c:2568
void * tls_user_ptr
Definition civetweb.c:2643
struct mg_request_info request_info
Definition civetweb.c:2567
struct mg_context * phys_ctx
Definition civetweb.c:2570
struct mg_domain_context * dom_ctx
Definition civetweb.c:2571
int in_error_handler
Definition civetweb.c:2609
time_t conn_birth_time
Definition civetweb.c:2580
int handled_requests
Definition civetweb.c:2626
int last_throttle_bytes
Definition civetweb.c:2636
time_t start_time
Definition civetweb.c:2480
pthread_cond_t sq_empty
Definition civetweb.c:2460
int thread_shutdown_notification_socket
Definition civetweb.c:2496
struct socket * squeue
Definition civetweb.c:2455
pthread_t * worker_threadids
Definition civetweb.c:2447
unsigned int idle_worker_thread_count
Definition civetweb.c:2443
volatile int sq_head
Definition civetweb.c:2457
unsigned int spawned_worker_threads
Definition civetweb.c:2440
stop_flag_t stop_flag
Definition civetweb.c:2433
void * user_data
Definition civetweb.c:2506
unsigned long starter_thread_idx
Definition civetweb.c:2448
struct mg_connection * worker_connections
Definition civetweb.c:2420
struct socket * listening_sockets
Definition civetweb.c:2416
char * systemName
Definition civetweb.c:2479
pthread_t masterthreadid
Definition civetweb.c:2436
int context_type
Definition civetweb.c:2414
pthread_mutex_t thread_mutex
Definition civetweb.c:2434
struct mg_callbacks callbacks
Definition civetweb.c:2505
struct mg_domain_context dd
Definition civetweb.c:2515
struct pollfd * listening_socket_fds
Definition civetweb.c:2417
pthread_cond_t sq_full
Definition civetweb.c:2459
int user_shutdown_notification_socket
Definition civetweb.c:2494
unsigned int cfg_max_worker_threads
Definition civetweb.c:2437
volatile int sq_tail
Definition civetweb.c:2458
struct twebdav_lock webdav_lock[10]
Definition civetweb.c:2476
unsigned int num_listening_sockets
Definition civetweb.c:2418
unsigned int max_request_size
Definition civetweb.c:2469
pthread_mutex_t nonce_mutex
Definition civetweb.c:2500
volatile int sq_blocked
Definition civetweb.c:2461
char * config[NUM_OPTIONS]
Definition civetweb.c:2329
unsigned long nonce_count
Definition civetweb.c:2335
int64_t ssl_cert_last_mtime
Definition civetweb.c:2331
uint64_t auth_nonce_mask
Definition civetweb.c:2334
struct mg_domain_context * next
Definition civetweb.c:2343
SSL_CTX * ssl_ctx
Definition civetweb.c:2328
struct mg_handler_info * handlers
Definition civetweb.c:2330
size_t text_buffer_size
Definition civetweb.h:1727
unsigned code_sub
Definition civetweb.h:1725
unsigned code
Definition civetweb.h:1724
uint64_t size
Definition civetweb.c:1917
time_t last_modified
Definition civetweb.c:1918
struct mg_file_stat stat
Definition civetweb.c:1932
struct mg_file_access access
Definition civetweb.c:1933
mg_request_handler handler
Definition civetweb.c:2295
mg_authorization_handler auth_handler
Definition civetweb.c:2309
mg_websocket_close_handler close_handler
Definition civetweb.c:2303
struct mg_handler_info * next
Definition civetweb.c:2315
unsigned int refcount
Definition civetweb.c:2296
mg_websocket_connect_handler connect_handler
Definition civetweb.c:2300
struct mg_websocket_subprotocols * subprotocols
Definition civetweb.c:2306
mg_websocket_data_handler data_handler
Definition civetweb.c:2302
mg_websocket_ready_handler ready_handler
Definition civetweb.c:2301
const char * name
Definition civetweb.c:11117
void * user_data
Definition civetweb.h:1800
const struct mg_callbacks * callbacks
Definition civetweb.h:1799
const char ** configuration_options
Definition civetweb.h:1801
struct mg_header http_headers[(64)]
Definition civetweb.h:179
const char * local_uri
Definition civetweb.h:157
const char * local_uri_raw
Definition civetweb.h:154
const char * request_method
Definition civetweb.h:151
const char * query_string
Definition civetweb.h:162
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 * remote_user
Definition civetweb.h:164
struct mg_header http_headers[(64)]
Definition civetweb.h:200
const char * http_version
Definition civetweb.h:194
const char * status_text
Definition civetweb.h:193
const char ** subprotocols
Definition civetweb.h:564
void * user_ptr
Definition civetweb.c:1631
const char * alpn_proto
Definition civetweb.c:1636
unsigned long thread_idx
Definition civetweb.c:1630
const char * f_ha1
Definition civetweb.c:9027
const char * f_domain
Definition civetweb.c:9026
const char * f_user
Definition civetweb.c:9025
const char * domain
Definition civetweb.c:9023
char buf[256+256+40]
Definition civetweb.c:9024
struct mg_connection * conn
Definition civetweb.c:9021
unsigned char is_ssl
Definition civetweb.c:1952
union usa lsa
Definition civetweb.c:1950
unsigned char is_optional
Definition civetweb.c:1956
unsigned char ssl_redir
Definition civetweb.c:1953
SOCKET sock
Definition civetweb.c:1949
unsigned char in_use
Definition civetweb.c:1957
union usa rsa
Definition civetweb.c:1951
void(* ptr)(void)
const char * name
enum ssl_func_category required
char token[33]
Definition civetweb.c:2399
char user[(PATH_MAX) *2]
Definition civetweb.c:2401
char path[(PATH_MAX) *2]
Definition civetweb.c:2400
uint64_t locktime
Definition civetweb.c:2398
size_t len
Definition civetweb.c:1912
const char * ptr
Definition civetweb.c:1911
mg_websocket_close_handler close_handler
Definition civetweb.c:19374
mg_websocket_data_handler data_handler
Definition civetweb.c:19373
struct mg_connection * conn
Definition civetweb.c:19372
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
struct sockaddr sa
Definition civetweb.c:1879
struct sockaddr_in sin
Definition civetweb.c:1880
static void output()