Coverage Report

Created: 2023-12-08 06:16

/src/civetweb/src/civetweb.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2013-2023 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
125
char static_assert_replacement[1];
126
#define mg_static_assert(cond, txt)                                            \
127
  extern char static_assert_replacement[(cond) ? 1 : -1]
128
#endif
129
130
mg_static_assert(sizeof(int) == 4 || sizeof(int) == 8,
131
                 "int data type size check");
132
mg_static_assert(sizeof(void *) == 4 || sizeof(void *) == 8,
133
                 "pointer data type size check");
134
mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
135
136
137
/* Select queue implementation. Diagnosis features originally only implemented
138
 * for the "ALTERNATIVE_QUEUE" have been ported to the previous queue
139
 * implementation (NO_ALTERNATIVE_QUEUE) as well. The new configuration value
140
 * "CONNECTION_QUEUE_SIZE" is only available for the previous queue
141
 * implementation, since the queue length is independent from the number of
142
 * worker threads there, while the new queue is one element per worker thread.
143
 *
144
 */
145
#if defined(NO_ALTERNATIVE_QUEUE) && defined(ALTERNATIVE_QUEUE)
146
/* The queues are exclusive or - only one can be used. */
147
#error                                                                         \
148
    "Define ALTERNATIVE_QUEUE or NO_ALTERNATIVE_QUEUE (or none of them), but not both"
149
#endif
150
#if !defined(NO_ALTERNATIVE_QUEUE) && !defined(ALTERNATIVE_QUEUE)
151
/* Use a default implementation */
152
#define NO_ALTERNATIVE_QUEUE
153
#endif
154
155
#if defined(NO_FILESYSTEMS) && !defined(NO_FILES)
156
/* File system access:
157
 * NO_FILES = do not serve any files from the file system automatically.
158
 * However, with NO_FILES CivetWeb may still write log files, read access
159
 * control files, default error page files or use API functions like
160
 * mg_send_file in callbacks to send files from the server local
161
 * file system.
162
 * NO_FILES only disables the automatic mapping between URLs and local
163
 * file names.
164
 * NO_FILESYSTEM = do not access any file at all. Useful for embedded
165
 * devices without file system. Logging to files in not available
166
 * (use callbacks instead) and API functions like mg_send_file are not
167
 * available.
168
 * If NO_FILESYSTEM is set, NO_FILES must be set as well.
169
 */
170
#error "Inconsistent build flags, NO_FILESYSTEMS requires NO_FILES"
171
#endif
172
173
/* DTL -- including winsock2.h works better if lean and mean */
174
#if !defined(WIN32_LEAN_AND_MEAN)
175
#define WIN32_LEAN_AND_MEAN
176
#endif
177
178
#if defined(__SYMBIAN32__)
179
/* According to https://en.wikipedia.org/wiki/Symbian#History,
180
 * Symbian is no longer maintained since 2014-01-01.
181
 * Support for Symbian has been removed from CivetWeb
182
 */
183
#error "Symbian is no longer maintained. CivetWeb no longer supports Symbian."
184
#endif /* __SYMBIAN32__ */
185
186
#if defined(__ZEPHYR__)
187
#include <ctype.h>
188
#include <fcntl.h>
189
#include <netdb.h>
190
#include <poll.h>
191
#include <pthread.h>
192
#include <stdio.h>
193
#include <stdlib.h>
194
#include <string.h>
195
#include <sys/socket.h>
196
#include <time.h>
197
198
#include <zephyr/kernel.h>
199
200
/* Max worker threads is the max of pthreads minus the main application thread
201
 * and minus the main civetweb thread, thus -2
202
 */
203
#define MAX_WORKER_THREADS (CONFIG_MAX_PTHREAD_COUNT - 2)
204
205
#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
206
#define ZEPHYR_STACK_SIZE USE_STACK_SIZE
207
#else
208
#define ZEPHYR_STACK_SIZE (1024 * 16)
209
#endif
210
211
K_THREAD_STACK_DEFINE(civetweb_main_stack, ZEPHYR_STACK_SIZE);
212
K_THREAD_STACK_ARRAY_DEFINE(civetweb_worker_stacks,
213
                            MAX_WORKER_THREADS,
214
                            ZEPHYR_STACK_SIZE);
215
216
static int zephyr_worker_stack_index;
217
218
#endif
219
220
#if !defined(CIVETWEB_HEADER_INCLUDED)
221
/* Include the header file here, so the CivetWeb interface is defined for the
222
 * entire implementation, including the following forward definitions. */
223
#include "civetweb.h"
224
#endif
225
226
#if !defined(DEBUG_TRACE)
227
#if defined(DEBUG)
228
static void DEBUG_TRACE_FUNC(const char *func,
229
                             unsigned line,
230
                             PRINTF_FORMAT_STRING(const char *fmt),
231
                             ...) PRINTF_ARGS(3, 4);
232
233
#define DEBUG_TRACE(fmt, ...)                                                  \
234
  DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__)
235
236
#define NEED_DEBUG_TRACE_FUNC
237
#if !defined(DEBUG_TRACE_STREAM)
238
#define DEBUG_TRACE_STREAM stdout
239
#endif
240
241
#else
242
#define DEBUG_TRACE(fmt, ...)                                                  \
243
8
  do {                                                                       \
244
8
  } while (0)
245
#endif /* DEBUG */
246
#endif /* DEBUG_TRACE */
247
248
249
#if !defined(DEBUG_ASSERT)
250
#if defined(DEBUG)
251
#include <stdlib.h>
252
#define DEBUG_ASSERT(cond)                                                     \
253
  do {                                                                       \
254
    if (!(cond)) {                                                         \
255
      DEBUG_TRACE("ASSERTION FAILED: %s", #cond);                        \
256
      exit(2); /* Exit with error */                                     \
257
    }                                                                      \
258
  } while (0)
259
#else
260
#define DEBUG_ASSERT(cond)
261
#endif /* DEBUG */
262
#endif
263
264
265
#if defined(__GNUC__) && defined(GCC_INSTRUMENTATION)
266
void __cyg_profile_func_enter(void *this_fn, void *call_site)
267
    __attribute__((no_instrument_function));
268
269
void __cyg_profile_func_exit(void *this_fn, void *call_site)
270
    __attribute__((no_instrument_function));
271
272
void
273
__cyg_profile_func_enter(void *this_fn, void *call_site)
274
{
275
  if ((void *)this_fn != (void *)printf) {
276
    printf("E %p %p\n", this_fn, call_site);
277
  }
278
}
279
280
void
281
__cyg_profile_func_exit(void *this_fn, void *call_site)
282
{
283
  if ((void *)this_fn != (void *)printf) {
284
    printf("X %p %p\n", this_fn, call_site);
285
  }
286
}
287
#endif
288
289
290
#if !defined(IGNORE_UNUSED_RESULT)
291
34
#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1))
292
#endif
293
294
295
#if defined(__GNUC__) || defined(__MINGW32__)
296
297
/* GCC unused function attribute seems fundamentally broken.
298
 * Several attempts to tell the compiler "THIS FUNCTION MAY BE USED
299
 * OR UNUSED" for individual functions failed.
300
 * Either the compiler creates an "unused-function" warning if a
301
 * function is not marked with __attribute__((unused)).
302
 * On the other hand, if the function is marked with this attribute,
303
 * but is used, the compiler raises a completely idiotic
304
 * "used-but-marked-unused" warning - and
305
 *   #pragma GCC diagnostic ignored "-Wused-but-marked-unused"
306
 * raises error: unknown option after "#pragma GCC diagnostic".
307
 * Disable this warning completely, until the GCC guys sober up
308
 * again.
309
 */
310
311
#pragma GCC diagnostic ignored "-Wunused-function"
312
313
#define FUNCTION_MAY_BE_UNUSED /* __attribute__((unused)) */
314
315
#else
316
#define FUNCTION_MAY_BE_UNUSED
317
#endif
318
319
320
/* Some ANSI #includes are not available on Windows CE and Zephyr */
321
#if !defined(_WIN32_WCE) && !defined(__ZEPHYR__)
322
#include <errno.h>
323
#include <fcntl.h>
324
#include <signal.h>
325
#include <stdlib.h>
326
#include <sys/stat.h>
327
#include <sys/types.h>
328
#endif /* !_WIN32_WCE */
329
330
331
#if defined(__clang__)
332
/* When using -Weverything, clang does not accept it's own headers
333
 * in a release build configuration. Disable what is too much in
334
 * -Weverything. */
335
#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
336
#endif
337
338
#if defined(__GNUC__) || defined(__MINGW32__)
339
/* Who on earth came to the conclusion, using __DATE__ should rise
340
 * an "expansion of date or time macro is not reproducible"
341
 * warning. That's exactly what was intended by using this macro.
342
 * Just disable this nonsense warning. */
343
344
/* And disabling them does not work either:
345
 * #pragma clang diagnostic ignored "-Wno-error=date-time"
346
 * #pragma clang diagnostic ignored "-Wdate-time"
347
 * So we just have to disable ALL warnings for some lines
348
 * of code.
349
 * This seems to be a known GCC bug, not resolved since 2012:
350
 * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431
351
 */
352
#endif
353
354
355
#if defined(__MACH__) && defined(__APPLE__) /* Apple OSX section */
356
357
#if defined(__clang__)
358
#if (__clang_major__ == 3) && ((__clang_minor__ == 7) || (__clang_minor__ == 8))
359
/* Avoid warnings for Xcode 7. It seems it does no longer exist in Xcode 8 */
360
#pragma clang diagnostic ignored "-Wno-reserved-id-macro"
361
#pragma clang diagnostic ignored "-Wno-keyword-macro"
362
#endif
363
#endif
364
365
#ifndef CLOCK_MONOTONIC
366
#define CLOCK_MONOTONIC (1)
367
#endif
368
#ifndef CLOCK_REALTIME
369
#define CLOCK_REALTIME (2)
370
#endif
371
372
#include <mach/clock.h>
373
#include <mach/mach.h>
374
#include <mach/mach_time.h>
375
#include <sys/errno.h>
376
#include <sys/time.h>
377
378
/* clock_gettime is not implemented on OSX prior to 10.12 */
379
static int
380
_civet_clock_gettime(int clk_id, struct timespec *t)
381
{
382
  memset(t, 0, sizeof(*t));
383
  if (clk_id == CLOCK_REALTIME) {
384
    struct timeval now;
385
    int rv = gettimeofday(&now, NULL);
386
    if (rv) {
387
      return rv;
388
    }
389
    t->tv_sec = now.tv_sec;
390
    t->tv_nsec = now.tv_usec * 1000;
391
    return 0;
392
393
  } else if (clk_id == CLOCK_MONOTONIC) {
394
    static uint64_t clock_start_time = 0;
395
    static mach_timebase_info_data_t timebase_ifo = {0, 0};
396
397
    uint64_t now = mach_absolute_time();
398
399
    if (clock_start_time == 0) {
400
      kern_return_t mach_status = mach_timebase_info(&timebase_ifo);
401
      DEBUG_ASSERT(mach_status == KERN_SUCCESS);
402
403
      /* appease "unused variable" warning for release builds */
404
      (void)mach_status;
405
406
      clock_start_time = now;
407
    }
408
409
    now = (uint64_t)((double)(now - clock_start_time)
410
                     * (double)timebase_ifo.numer
411
                     / (double)timebase_ifo.denom);
412
413
    t->tv_sec = now / 1000000000;
414
    t->tv_nsec = now % 1000000000;
415
    return 0;
416
  }
417
  return -1; /* EINVAL - Clock ID is unknown */
418
}
419
420
/* if clock_gettime is declared, then __CLOCK_AVAILABILITY will be defined */
421
#if defined(__CLOCK_AVAILABILITY)
422
/* If we compiled with Mac OSX 10.12 or later, then clock_gettime will be
423
 * declared but it may be NULL at runtime. So we need to check before using
424
 * it. */
425
static int
426
_civet_safe_clock_gettime(int clk_id, struct timespec *t)
427
{
428
  if (clock_gettime) {
429
    return clock_gettime(clk_id, t);
430
  }
431
  return _civet_clock_gettime(clk_id, t);
432
}
433
#define clock_gettime _civet_safe_clock_gettime
434
#else
435
#define clock_gettime _civet_clock_gettime
436
#endif
437
438
#endif
439
440
441
#if defined(_WIN32)
442
#define ERROR_TRY_AGAIN(err) ((err) == WSAEWOULDBLOCK)
443
#else
444
/* Unix might return different error codes indicating to try again.
445
 * For Linux EAGAIN==EWOULDBLOCK, maybe EAGAIN!=EWOULDBLOCK is history from
446
 * decades ago, but better check both and let the compiler optimize it. */
447
#define ERROR_TRY_AGAIN(err)                                                   \
448
34
  (((err) == EAGAIN) || ((err) == EWOULDBLOCK) || ((err) == EINTR))
449
#endif
450
451
#if defined(USE_ZLIB)
452
#include "zconf.h"
453
#include "zlib.h"
454
#endif
455
456
457
/********************************************************************/
458
/* CivetWeb configuration defines */
459
/********************************************************************/
460
461
/* Maximum number of threads that can be configured.
462
 * The number of threads actually created depends on the "num_threads"
463
 * configuration parameter, but this is the upper limit. */
464
#if !defined(MAX_WORKER_THREADS)
465
2
#define MAX_WORKER_THREADS (1024 * 64) /* in threads (count) */
466
#endif
467
468
/* Timeout interval for select/poll calls.
469
 * The timeouts depend on "*_timeout_ms" configuration values, but long
470
 * timeouts are split into timouts as small as SOCKET_TIMEOUT_QUANTUM.
471
 * This reduces the time required to stop the server. */
472
#if !defined(SOCKET_TIMEOUT_QUANTUM)
473
114
#define SOCKET_TIMEOUT_QUANTUM (2000) /* in ms */
474
#endif
475
476
/* Do not try to compress files smaller than this limit. */
477
#if !defined(MG_FILE_COMPRESSION_SIZE_LIMIT)
478
0
#define MG_FILE_COMPRESSION_SIZE_LIMIT (1024) /* in bytes */
479
#endif
480
481
#if !defined(PASSWORDS_FILE_NAME)
482
0
#define PASSWORDS_FILE_NAME ".htpasswd"
483
#endif
484
485
/* Initial buffer size for all CGI environment variables. In case there is
486
 * not enough space, another block is allocated. */
487
#if !defined(CGI_ENVIRONMENT_SIZE)
488
0
#define CGI_ENVIRONMENT_SIZE (4096) /* in bytes */
489
#endif
490
491
/* Maximum number of environment variables. */
492
#if !defined(MAX_CGI_ENVIR_VARS)
493
0
#define MAX_CGI_ENVIR_VARS (256) /* in variables (count) */
494
#endif
495
496
/* General purpose buffer size. */
497
#if !defined(MG_BUF_LEN) /* in bytes */
498
0
#define MG_BUF_LEN (1024 * 8)
499
#endif
500
501
502
/********************************************************************/
503
504
/* Helper macros */
505
#if !defined(ARRAY_SIZE)
506
0
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
507
#endif
508
509
#include <stdint.h>
510
511
/* Standard defines */
512
#if !defined(INT64_MAX)
513
#define INT64_MAX (9223372036854775807)
514
#endif
515
516
#define SHUTDOWN_RD (0)
517
34
#define SHUTDOWN_WR (1)
518
#define SHUTDOWN_BOTH (2)
519
520
mg_static_assert(MAX_WORKER_THREADS >= 1,
521
                 "worker threads must be a positive number");
522
523
mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8,
524
                 "size_t data type size check");
525
526
527
#if defined(_WIN32) /* WINDOWS include block */
528
#include <malloc.h> /* *alloc( */
529
#include <stdlib.h> /* *alloc( */
530
#include <time.h>   /* struct timespec */
531
#include <windows.h>
532
#include <winsock2.h> /* DTL add for SO_EXCLUSIVE */
533
#include <ws2tcpip.h>
534
535
typedef const char *SOCK_OPT_TYPE;
536
537
/* For a detailed description of these *_PATH_MAX defines, see
538
 * https://github.com/civetweb/civetweb/issues/937. */
539
540
/* UTF8_PATH_MAX is a char buffer size for 259 BMP characters in UTF-8 plus
541
 * null termination, rounded up to the next 4 bytes boundary */
542
#define UTF8_PATH_MAX (3 * 260)
543
/* UTF16_PATH_MAX is the 16-bit wchar_t buffer size required for 259 BMP
544
 * characters plus termination. (Note: wchar_t is 16 bit on Windows) */
545
#define UTF16_PATH_MAX (260)
546
547
#if !defined(_IN_PORT_T)
548
#if !defined(in_port_t)
549
#define in_port_t u_short
550
#endif
551
#endif
552
553
#if defined(_WIN32_WCE)
554
#error "WinCE support has ended"
555
#endif
556
557
#include <direct.h>
558
#include <io.h>
559
#include <process.h>
560
561
562
#define MAKEUQUAD(lo, hi)                                                      \
563
  ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32))
564
#define RATE_DIFF (10000000) /* 100 nsecs */
565
#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de))
566
#define SYS2UNIX_TIME(lo, hi)                                                  \
567
  ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF))
568
569
/* Visual Studio 6 does not know __func__ or __FUNCTION__
570
 * The rest of MS compilers use __FUNCTION__, not C99 __func__
571
 * Also use _strtoui64 on modern M$ compilers */
572
#if defined(_MSC_VER)
573
#if (_MSC_VER < 1300)
574
#define STRX(x) #x
575
#define STR(x) STRX(x)
576
#define __func__ __FILE__ ":" STR(__LINE__)
577
#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x))
578
#define strtoll(x, y, z) (_atoi64(x))
579
#else
580
#define __func__ __FUNCTION__
581
#define strtoull(x, y, z) (_strtoui64(x, y, z))
582
#define strtoll(x, y, z) (_strtoi64(x, y, z))
583
#endif
584
#endif /* _MSC_VER */
585
586
587
#define ERRNO ((int)(GetLastError()))
588
#define NO_SOCKLEN_T
589
590
591
#if defined(_WIN64) || defined(__MINGW64__)
592
#if !defined(SSL_LIB)
593
594
#if defined(OPENSSL_API_3_0)
595
#define SSL_LIB "libssl-3-x64.dll"
596
#define CRYPTO_LIB "libcrypto-3-x64.dll"
597
#endif
598
599
#if defined(OPENSSL_API_1_1)
600
#define SSL_LIB "libssl-1_1-x64.dll"
601
#define CRYPTO_LIB "libcrypto-1_1-x64.dll"
602
#endif /* OPENSSL_API_1_1 */
603
604
#if defined(OPENSSL_API_1_0)
605
#define SSL_LIB "ssleay64.dll"
606
#define CRYPTO_LIB "libeay64.dll"
607
#endif /* OPENSSL_API_1_0 */
608
609
#endif
610
#else /* defined(_WIN64) || defined(__MINGW64__) */
611
#if !defined(SSL_LIB)
612
613
#if defined(OPENSSL_API_3_0)
614
#define SSL_LIB "libssl-3.dll"
615
#define CRYPTO_LIB "libcrypto-3.dll"
616
#endif
617
618
#if defined(OPENSSL_API_1_1)
619
#define SSL_LIB "libssl-1_1.dll"
620
#define CRYPTO_LIB "libcrypto-1_1.dll"
621
#endif /* OPENSSL_API_1_1 */
622
623
#if defined(OPENSSL_API_1_0)
624
#define SSL_LIB "ssleay32.dll"
625
#define CRYPTO_LIB "libeay32.dll"
626
#endif /* OPENSSL_API_1_0 */
627
628
#endif /* SSL_LIB */
629
#endif /* defined(_WIN64) || defined(__MINGW64__) */
630
631
632
#define O_NONBLOCK (0)
633
#if !defined(W_OK)
634
#define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */
635
#endif
636
#define _POSIX_
637
#define INT64_FMT "I64d"
638
#define UINT64_FMT "I64u"
639
640
#define WINCDECL __cdecl
641
#define vsnprintf_impl _vsnprintf
642
#define access _access
643
#define mg_sleep(x) (Sleep(x))
644
645
#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
646
#if !defined(popen)
647
#define popen(x, y) (_popen(x, y))
648
#endif
649
#if !defined(pclose)
650
#define pclose(x) (_pclose(x))
651
#endif
652
#define close(x) (_close(x))
653
#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y)))
654
#define RTLD_LAZY (0)
655
#define fseeko(x, y, z) ((_lseeki64(_fileno(x), (y), (z)) == -1) ? -1 : 0)
656
#define fdopen(x, y) (_fdopen((x), (y)))
657
#define write(x, y, z) (_write((x), (y), (unsigned)z))
658
#define read(x, y, z) (_read((x), (y), (unsigned)z))
659
#define flockfile(x) ((void)pthread_mutex_lock(&global_log_file_lock))
660
#define funlockfile(x) ((void)pthread_mutex_unlock(&global_log_file_lock))
661
#define sleep(x) (Sleep((x)*1000))
662
#define rmdir(x) (_rmdir(x))
663
#if defined(_WIN64) || !defined(__MINGW32__)
664
/* Only MinGW 32 bit is missing this function */
665
#define timegm(x) (_mkgmtime(x))
666
#else
667
time_t timegm(struct tm *tm);
668
#define NEED_TIMEGM
669
#endif
670
671
672
#if !defined(fileno)
673
#define fileno(x) (_fileno(x))
674
#endif /* !fileno MINGW #defines fileno */
675
676
typedef struct {
677
  CRITICAL_SECTION sec; /* Immovable */
678
} pthread_mutex_t;
679
typedef DWORD pthread_key_t;
680
typedef HANDLE pthread_t;
681
typedef struct {
682
  pthread_mutex_t threadIdSec;
683
  struct mg_workerTLS *waiting_thread; /* The chain of threads */
684
} pthread_cond_t;
685
686
#if !defined(__clockid_t_defined)
687
typedef DWORD clockid_t;
688
#endif
689
#if !defined(CLOCK_MONOTONIC)
690
#define CLOCK_MONOTONIC (1)
691
#endif
692
#if !defined(CLOCK_REALTIME)
693
#define CLOCK_REALTIME (2)
694
#endif
695
#if !defined(CLOCK_THREAD)
696
#define CLOCK_THREAD (3)
697
#endif
698
#if !defined(CLOCK_PROCESS)
699
#define CLOCK_PROCESS (4)
700
#endif
701
702
703
#if defined(_MSC_VER) && (_MSC_VER >= 1900)
704
#define _TIMESPEC_DEFINED
705
#endif
706
#if !defined(_TIMESPEC_DEFINED)
707
struct timespec {
708
  time_t tv_sec; /* seconds */
709
  long tv_nsec;  /* nanoseconds */
710
};
711
#endif
712
713
#if !defined(WIN_PTHREADS_TIME_H)
714
#define MUST_IMPLEMENT_CLOCK_GETTIME
715
#endif
716
717
#if defined(MUST_IMPLEMENT_CLOCK_GETTIME)
718
#define clock_gettime mg_clock_gettime
719
static int
720
clock_gettime(clockid_t clk_id, struct timespec *tp)
721
{
722
  FILETIME ft;
723
  ULARGE_INTEGER li, li2;
724
  BOOL ok = FALSE;
725
  double d;
726
  static double perfcnt_per_sec = 0.0;
727
  static BOOL initialized = FALSE;
728
729
  if (!initialized) {
730
    QueryPerformanceFrequency((LARGE_INTEGER *)&li);
731
    perfcnt_per_sec = 1.0 / li.QuadPart;
732
    initialized = TRUE;
733
  }
734
735
  if (tp) {
736
    memset(tp, 0, sizeof(*tp));
737
738
    if (clk_id == CLOCK_REALTIME) {
739
740
      /* BEGIN: CLOCK_REALTIME = wall clock (date and time) */
741
      GetSystemTimeAsFileTime(&ft);
742
      li.LowPart = ft.dwLowDateTime;
743
      li.HighPart = ft.dwHighDateTime;
744
      li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */
745
      tp->tv_sec = (time_t)(li.QuadPart / 10000000);
746
      tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
747
      ok = TRUE;
748
      /* END: CLOCK_REALTIME */
749
750
    } else if (clk_id == CLOCK_MONOTONIC) {
751
752
      /* BEGIN: CLOCK_MONOTONIC = stopwatch (time differences) */
753
      QueryPerformanceCounter((LARGE_INTEGER *)&li);
754
      d = li.QuadPart * perfcnt_per_sec;
755
      tp->tv_sec = (time_t)d;
756
      d -= (double)tp->tv_sec;
757
      tp->tv_nsec = (long)(d * 1.0E9);
758
      ok = TRUE;
759
      /* END: CLOCK_MONOTONIC */
760
761
    } else if (clk_id == CLOCK_THREAD) {
762
763
      /* BEGIN: CLOCK_THREAD = CPU usage of thread */
764
      FILETIME t_create, t_exit, t_kernel, t_user;
765
      if (GetThreadTimes(GetCurrentThread(),
766
                         &t_create,
767
                         &t_exit,
768
                         &t_kernel,
769
                         &t_user)) {
770
        li.LowPart = t_user.dwLowDateTime;
771
        li.HighPart = t_user.dwHighDateTime;
772
        li2.LowPart = t_kernel.dwLowDateTime;
773
        li2.HighPart = t_kernel.dwHighDateTime;
774
        li.QuadPart += li2.QuadPart;
775
        tp->tv_sec = (time_t)(li.QuadPart / 10000000);
776
        tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
777
        ok = TRUE;
778
      }
779
      /* END: CLOCK_THREAD */
780
781
    } else if (clk_id == CLOCK_PROCESS) {
782
783
      /* BEGIN: CLOCK_PROCESS = CPU usage of process */
784
      FILETIME t_create, t_exit, t_kernel, t_user;
785
      if (GetProcessTimes(GetCurrentProcess(),
786
                          &t_create,
787
                          &t_exit,
788
                          &t_kernel,
789
                          &t_user)) {
790
        li.LowPart = t_user.dwLowDateTime;
791
        li.HighPart = t_user.dwHighDateTime;
792
        li2.LowPart = t_kernel.dwLowDateTime;
793
        li2.HighPart = t_kernel.dwHighDateTime;
794
        li.QuadPart += li2.QuadPart;
795
        tp->tv_sec = (time_t)(li.QuadPart / 10000000);
796
        tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
797
        ok = TRUE;
798
      }
799
      /* END: CLOCK_PROCESS */
800
801
    } else {
802
803
      /* BEGIN: unknown clock */
804
      /* ok = FALSE; already set by init */
805
      /* END: unknown clock */
806
    }
807
  }
808
809
  return ok ? 0 : -1;
810
}
811
#endif
812
813
814
#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */
815
816
static int pthread_mutex_lock(pthread_mutex_t *);
817
static int pthread_mutex_unlock(pthread_mutex_t *);
818
static void path_to_unicode(const struct mg_connection *conn,
819
                            const char *path,
820
                            wchar_t *wbuf,
821
                            size_t wbuf_len);
822
823
/* All file operations need to be rewritten to solve #246. */
824
825
struct mg_file;
826
827
static const char *mg_fgets(char *buf, size_t size, struct mg_file *filep);
828
829
830
/* POSIX dirent interface */
831
struct dirent {
832
  char d_name[UTF8_PATH_MAX];
833
};
834
835
typedef struct DIR {
836
  HANDLE handle;
837
  WIN32_FIND_DATAW info;
838
  struct dirent result;
839
} DIR;
840
841
#if defined(HAVE_POLL)
842
#define mg_pollfd pollfd
843
#else
844
struct mg_pollfd {
845
  SOCKET fd;
846
  short events;
847
  short revents;
848
};
849
#endif
850
851
/* Mark required libraries */
852
#if defined(_MSC_VER)
853
#pragma comment(lib, "Ws2_32.lib")
854
#endif
855
856
#else /* defined(_WIN32) - WINDOWS vs UNIX include block */
857
858
#include <inttypes.h>
859
860
/* Linux & co. internally use UTF8 */
861
0
#define UTF8_PATH_MAX (PATH_MAX)
862
863
typedef const void *SOCK_OPT_TYPE;
864
865
#if defined(ANDROID)
866
typedef unsigned short int in_port_t;
867
#endif
868
869
#if !defined(__ZEPHYR__)
870
#include <arpa/inet.h>
871
#include <ctype.h>
872
#include <dirent.h>
873
#include <grp.h>
874
#include <limits.h>
875
#include <netdb.h>
876
#include <netinet/in.h>
877
#include <netinet/tcp.h>
878
#include <poll.h>
879
#include <pthread.h>
880
#include <pwd.h>
881
#include <stdarg.h>
882
#include <stddef.h>
883
#include <stdio.h>
884
#include <stdlib.h>
885
#include <string.h>
886
#include <sys/socket.h>
887
#include <sys/time.h>
888
#include <sys/utsname.h>
889
#include <sys/wait.h>
890
#include <time.h>
891
#include <unistd.h>
892
#if defined(USE_X_DOM_SOCKET)
893
#include <sys/un.h>
894
#endif
895
#endif
896
897
104
#define vsnprintf_impl vsnprintf
898
899
#if !defined(NO_SSL_DL) && !defined(NO_SSL)
900
#include <dlfcn.h>
901
#endif
902
903
#if defined(__MACH__) && defined(__APPLE__)
904
#define SSL_LIB "libssl.dylib"
905
#define CRYPTO_LIB "libcrypto.dylib"
906
#else
907
#if !defined(SSL_LIB)
908
0
#define SSL_LIB "libssl.so"
909
#endif
910
#if !defined(CRYPTO_LIB)
911
0
#define CRYPTO_LIB "libcrypto.so"
912
#endif
913
#endif
914
#if !defined(O_BINARY)
915
#define O_BINARY (0)
916
#endif /* O_BINARY */
917
40
#define closesocket(a) (close(a))
918
0
#define mg_mkdir(conn, path, mode) (mkdir(path, mode))
919
0
#define mg_remove(conn, x) (remove(x))
920
2
#define mg_sleep(x) (usleep((x)*1000))
921
0
#define mg_opendir(conn, x) (opendir(x))
922
0
#define mg_closedir(x) (closedir(x))
923
0
#define mg_readdir(x) (readdir(x))
924
138
#define ERRNO (errno)
925
174
#define INVALID_SOCKET (-1)
926
0
#define INT64_FMT PRId64
927
0
#define UINT64_FMT PRIu64
928
typedef int SOCKET;
929
#define WINCDECL
930
931
#if defined(__hpux)
932
/* HPUX 11 does not have monotonic, fall back to realtime */
933
#if !defined(CLOCK_MONOTONIC)
934
#define CLOCK_MONOTONIC CLOCK_REALTIME
935
#endif
936
937
/* HPUX defines socklen_t incorrectly as size_t which is 64bit on
938
 * Itanium.  Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED
939
 * the prototypes use int* rather than socklen_t* which matches the
940
 * actual library expectation.  When called with the wrong size arg
941
 * accept() returns a zero client inet addr and check_acl() always
942
 * fails.  Since socklen_t is widely used below, just force replace
943
 * their typedef with int. - DTL
944
 */
945
#define socklen_t int
946
#endif /* hpux */
947
948
#define mg_pollfd pollfd
949
950
#endif /* defined(_WIN32) - WINDOWS vs UNIX include block */
951
952
/* In case our C library is missing "timegm", provide an implementation */
953
#if defined(NEED_TIMEGM)
954
static inline int
955
is_leap(int y)
956
{
957
  return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
958
}
959
960
static inline int
961
count_leap(int y)
962
{
963
  return (y - 1969) / 4 - (y - 1901) / 100 + (y - 1601) / 400;
964
}
965
966
time_t
967
timegm(struct tm *tm)
968
{
969
  static const unsigned short ydays[] = {
970
      0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
971
  int year = tm->tm_year + 1900;
972
  int mon = tm->tm_mon;
973
  int mday = tm->tm_mday - 1;
974
  int hour = tm->tm_hour;
975
  int min = tm->tm_min;
976
  int sec = tm->tm_sec;
977
978
  if (year < 1970 || mon < 0 || mon > 11 || mday < 0
979
      || (mday >= ydays[mon + 1] - ydays[mon]
980
                      + (mon == 1 && is_leap(year) ? 1 : 0))
981
      || hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 60)
982
    return -1;
983
984
  time_t res = year - 1970;
985
  res *= 365;
986
  res += mday;
987
  res += ydays[mon] + (mon > 1 && is_leap(year) ? 1 : 0);
988
  res += count_leap(year);
989
990
  res *= 24;
991
  res += hour;
992
  res *= 60;
993
  res += min;
994
  res *= 60;
995
  res += sec;
996
  return res;
997
}
998
#endif /* NEED_TIMEGM */
999
1000
1001
/* va_copy should always be a macro, C99 and C++11 - DTL */
1002
#if !defined(va_copy)
1003
#define va_copy(x, y) ((x) = (y))
1004
#endif
1005
1006
1007
#if defined(_WIN32)
1008
/* Create substitutes for POSIX functions in Win32. */
1009
1010
#if defined(GCC_DIAGNOSTIC)
1011
/* Show no warning in case system functions are not used. */
1012
#pragma GCC diagnostic push
1013
#pragma GCC diagnostic ignored "-Wunused-function"
1014
#endif
1015
1016
1017
static pthread_mutex_t global_log_file_lock;
1018
1019
FUNCTION_MAY_BE_UNUSED
1020
static DWORD
1021
pthread_self(void)
1022
{
1023
  return GetCurrentThreadId();
1024
}
1025
1026
1027
FUNCTION_MAY_BE_UNUSED
1028
static int
1029
pthread_key_create(
1030
    pthread_key_t *key,
1031
    void (*_ignored)(void *) /* destructor not supported for Windows */
1032
)
1033
{
1034
  (void)_ignored;
1035
1036
  if ((key != 0)) {
1037
    *key = TlsAlloc();
1038
    return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1;
1039
  }
1040
  return -2;
1041
}
1042
1043
1044
FUNCTION_MAY_BE_UNUSED
1045
static int
1046
pthread_key_delete(pthread_key_t key)
1047
{
1048
  return TlsFree(key) ? 0 : 1;
1049
}
1050
1051
1052
FUNCTION_MAY_BE_UNUSED
1053
static int
1054
pthread_setspecific(pthread_key_t key, void *value)
1055
{
1056
  return TlsSetValue(key, value) ? 0 : 1;
1057
}
1058
1059
1060
FUNCTION_MAY_BE_UNUSED
1061
static void *
1062
pthread_getspecific(pthread_key_t key)
1063
{
1064
  return TlsGetValue(key);
1065
}
1066
1067
#if defined(GCC_DIAGNOSTIC)
1068
/* Enable unused function warning again */
1069
#pragma GCC diagnostic pop
1070
#endif
1071
1072
static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL;
1073
#else
1074
static pthread_mutexattr_t pthread_mutex_attr;
1075
#endif /* _WIN32 */
1076
1077
1078
#if defined(GCC_DIAGNOSTIC)
1079
/* Show no warning in case system functions are not used. */
1080
#pragma GCC diagnostic push
1081
#pragma GCC diagnostic ignored "-Wunused-function"
1082
#endif /* defined(GCC_DIAGNOSTIC) */
1083
#if defined(__clang__)
1084
/* Show no warning in case system functions are not used. */
1085
#pragma clang diagnostic push
1086
#pragma clang diagnostic ignored "-Wunused-function"
1087
#endif
1088
1089
static pthread_mutex_t global_lock_mutex;
1090
1091
1092
FUNCTION_MAY_BE_UNUSED
1093
static void
1094
mg_global_lock(void)
1095
2
{
1096
2
  (void)pthread_mutex_lock(&global_lock_mutex);
1097
2
}
1098
1099
1100
FUNCTION_MAY_BE_UNUSED
1101
static void
1102
mg_global_unlock(void)
1103
2
{
1104
2
  (void)pthread_mutex_unlock(&global_lock_mutex);
1105
2
}
1106
1107
1108
#if defined(_WIN64)
1109
mg_static_assert(SIZE_MAX == 0xFFFFFFFFFFFFFFFFu, "Mismatch for atomic types");
1110
#elif defined(_WIN32)
1111
mg_static_assert(SIZE_MAX == 0xFFFFFFFFu, "Mismatch for atomic types");
1112
#endif
1113
1114
1115
/* Atomic functions working on ptrdiff_t ("signed size_t").
1116
 * Operations: Increment, Decrement, Add, Maximum.
1117
 * Up to size_t, they do not an atomic "load" operation.
1118
 */
1119
FUNCTION_MAY_BE_UNUSED
1120
static ptrdiff_t
1121
mg_atomic_inc(volatile ptrdiff_t *addr)
1122
2
{
1123
2
  ptrdiff_t ret;
1124
1125
#if defined(_WIN64) && !defined(NO_ATOMICS)
1126
  ret = InterlockedIncrement64(addr);
1127
#elif defined(_WIN32) && !defined(NO_ATOMICS)
1128
#ifdef __cplusplus
1129
  /* For C++ the Microsoft Visual Studio compiler can not decide what
1130
   * overloaded function prototpye in the SDC corresponds to "ptrdiff_t". */
1131
  static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch");
1132
  static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch");
1133
  ret = InterlockedIncrement((LONG *)addr);
1134
#else
1135
  ret = InterlockedIncrement(addr);
1136
#endif
1137
#elif defined(__GNUC__)                                                        \
1138
    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))           \
1139
    && !defined(NO_ATOMICS)
1140
2
  ret = __sync_add_and_fetch(addr, 1);
1141
#else
1142
  mg_global_lock();
1143
  ret = (++(*addr));
1144
  mg_global_unlock();
1145
#endif
1146
2
  return ret;
1147
2
}
1148
1149
1150
FUNCTION_MAY_BE_UNUSED
1151
static ptrdiff_t
1152
mg_atomic_dec(volatile ptrdiff_t *addr)
1153
0
{
1154
0
  ptrdiff_t ret;
1155
1156
#if defined(_WIN64) && !defined(NO_ATOMICS)
1157
  ret = InterlockedDecrement64(addr);
1158
#elif defined(_WIN32) && !defined(NO_ATOMICS)
1159
#ifdef __cplusplus
1160
  /* see mg_atomic_inc */
1161
  static_assert(sizeof(ptrdiff_t) == sizeof(LONG), "Size mismatch");
1162
  static_assert(sizeof(ptrdiff_t) == sizeof(int32_t), "Size mismatch");
1163
  ret = InterlockedDecrement((LONG *)addr);
1164
#else
1165
  ret = InterlockedDecrement(addr);
1166
#endif
1167
#elif defined(__GNUC__)                                                        \
1168
    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))           \
1169
    && !defined(NO_ATOMICS)
1170
0
  ret = __sync_sub_and_fetch(addr, 1);
1171
#else
1172
  mg_global_lock();
1173
  ret = (--(*addr));
1174
  mg_global_unlock();
1175
#endif
1176
0
  return ret;
1177
0
}
1178
1179
1180
#if defined(USE_SERVER_STATS) || defined(STOP_FLAG_NEEDS_LOCK)
1181
static ptrdiff_t
1182
mg_atomic_add(volatile ptrdiff_t *addr, ptrdiff_t value)
1183
{
1184
  ptrdiff_t ret;
1185
1186
#if defined(_WIN64) && !defined(NO_ATOMICS)
1187
  ret = InterlockedAdd64(addr, value);
1188
#elif defined(_WIN32) && !defined(NO_ATOMICS)
1189
  ret = InterlockedExchangeAdd(addr, value) + value;
1190
#elif defined(__GNUC__)                                                        \
1191
    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))           \
1192
    && !defined(NO_ATOMICS)
1193
  ret = __sync_add_and_fetch(addr, value);
1194
#else
1195
  mg_global_lock();
1196
  *addr += value;
1197
  ret = (*addr);
1198
  mg_global_unlock();
1199
#endif
1200
  return ret;
1201
}
1202
1203
1204
FUNCTION_MAY_BE_UNUSED
1205
static ptrdiff_t
1206
mg_atomic_compare_and_swap(volatile ptrdiff_t *addr,
1207
                           ptrdiff_t oldval,
1208
                           ptrdiff_t newval)
1209
{
1210
  ptrdiff_t ret;
1211
1212
#if defined(_WIN64) && !defined(NO_ATOMICS)
1213
  ret = InterlockedCompareExchange64(addr, newval, oldval);
1214
#elif defined(_WIN32) && !defined(NO_ATOMICS)
1215
  ret = InterlockedCompareExchange(addr, newval, oldval);
1216
#elif defined(__GNUC__)                                                        \
1217
    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))           \
1218
    && !defined(NO_ATOMICS)
1219
  ret = __sync_val_compare_and_swap(addr, oldval, newval);
1220
#else
1221
  mg_global_lock();
1222
  ret = *addr;
1223
  if ((ret != newval) && (ret == oldval)) {
1224
    *addr = newval;
1225
  }
1226
  mg_global_unlock();
1227
#endif
1228
  return ret;
1229
}
1230
1231
1232
static void
1233
mg_atomic_max(volatile ptrdiff_t *addr, ptrdiff_t value)
1234
{
1235
  register ptrdiff_t tmp = *addr;
1236
1237
#if defined(_WIN64) && !defined(NO_ATOMICS)
1238
  while (tmp < value) {
1239
    tmp = InterlockedCompareExchange64(addr, value, tmp);
1240
  }
1241
#elif defined(_WIN32) && !defined(NO_ATOMICS)
1242
  while (tmp < value) {
1243
    tmp = InterlockedCompareExchange(addr, value, tmp);
1244
  }
1245
#elif defined(__GNUC__)                                                        \
1246
    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))           \
1247
    && !defined(NO_ATOMICS)
1248
  while (tmp < value) {
1249
    tmp = __sync_val_compare_and_swap(addr, tmp, value);
1250
  }
1251
#else
1252
  mg_global_lock();
1253
  if (*addr < value) {
1254
    *addr = value;
1255
  }
1256
  mg_global_unlock();
1257
#endif
1258
}
1259
1260
1261
static int64_t
1262
mg_atomic_add64(volatile int64_t *addr, int64_t value)
1263
{
1264
  int64_t ret;
1265
1266
#if defined(_WIN64) && !defined(NO_ATOMICS)
1267
  ret = InterlockedAdd64(addr, value);
1268
#elif defined(_WIN32) && !defined(NO_ATOMICS)
1269
  ret = InterlockedExchangeAdd64(addr, value) + value;
1270
#elif defined(__GNUC__)                                                        \
1271
    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))           \
1272
    && !defined(NO_ATOMICS)
1273
  ret = __sync_add_and_fetch(addr, value);
1274
#else
1275
  mg_global_lock();
1276
  *addr += value;
1277
  ret = (*addr);
1278
  mg_global_unlock();
1279
#endif
1280
  return ret;
1281
}
1282
#endif
1283
1284
1285
#if defined(GCC_DIAGNOSTIC)
1286
/* Show no warning in case system functions are not used. */
1287
#pragma GCC diagnostic pop
1288
#endif /* defined(GCC_DIAGNOSTIC) */
1289
#if defined(__clang__)
1290
/* Show no warning in case system functions are not used. */
1291
#pragma clang diagnostic pop
1292
#endif
1293
1294
1295
#if defined(USE_SERVER_STATS)
1296
1297
struct mg_memory_stat {
1298
  volatile ptrdiff_t totalMemUsed;
1299
  volatile ptrdiff_t maxMemUsed;
1300
  volatile ptrdiff_t blockCount;
1301
};
1302
1303
1304
static struct mg_memory_stat *get_memory_stat(struct mg_context *ctx);
1305
1306
1307
static void *
1308
mg_malloc_ex(size_t size,
1309
             struct mg_context *ctx,
1310
             const char *file,
1311
             unsigned line)
1312
{
1313
  void *data = malloc(size + 2 * sizeof(uintptr_t));
1314
  void *memory = 0;
1315
  struct mg_memory_stat *mstat = get_memory_stat(ctx);
1316
1317
#if defined(MEMORY_DEBUGGING)
1318
  char mallocStr[256];
1319
#else
1320
  (void)file;
1321
  (void)line;
1322
#endif
1323
1324
  if (data) {
1325
    uintptr_t *tmp = (uintptr_t *)data;
1326
    ptrdiff_t mmem = mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)size);
1327
    mg_atomic_max(&mstat->maxMemUsed, mmem);
1328
    mg_atomic_inc(&mstat->blockCount);
1329
    tmp[0] = size;
1330
    tmp[1] = (uintptr_t)mstat;
1331
    memory = (void *)&tmp[2];
1332
  }
1333
1334
#if defined(MEMORY_DEBUGGING)
1335
  sprintf(mallocStr,
1336
          "MEM: %p %5lu alloc   %7lu %4lu --- %s:%u\n",
1337
          memory,
1338
          (unsigned long)size,
1339
          (unsigned long)mstat->totalMemUsed,
1340
          (unsigned long)mstat->blockCount,
1341
          file,
1342
          line);
1343
  DEBUG_TRACE("%s", mallocStr);
1344
#endif
1345
1346
  return memory;
1347
}
1348
1349
1350
static void *
1351
mg_calloc_ex(size_t count,
1352
             size_t size,
1353
             struct mg_context *ctx,
1354
             const char *file,
1355
             unsigned line)
1356
{
1357
  void *data = mg_malloc_ex(size * count, ctx, file, line);
1358
1359
  if (data) {
1360
    memset(data, 0, size * count);
1361
  }
1362
  return data;
1363
}
1364
1365
1366
static void
1367
mg_free_ex(void *memory, const char *file, unsigned line)
1368
{
1369
#if defined(MEMORY_DEBUGGING)
1370
  char mallocStr[256];
1371
#else
1372
  (void)file;
1373
  (void)line;
1374
#endif
1375
1376
  if (memory) {
1377
    void *data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t));
1378
    uintptr_t size = ((uintptr_t *)data)[0];
1379
    struct mg_memory_stat *mstat =
1380
        (struct mg_memory_stat *)(((uintptr_t *)data)[1]);
1381
    mg_atomic_add(&mstat->totalMemUsed, -(ptrdiff_t)size);
1382
    mg_atomic_dec(&mstat->blockCount);
1383
1384
#if defined(MEMORY_DEBUGGING)
1385
    sprintf(mallocStr,
1386
            "MEM: %p %5lu free    %7lu %4lu --- %s:%u\n",
1387
            memory,
1388
            (unsigned long)size,
1389
            (unsigned long)mstat->totalMemUsed,
1390
            (unsigned long)mstat->blockCount,
1391
            file,
1392
            line);
1393
    DEBUG_TRACE("%s", mallocStr);
1394
#endif
1395
    free(data);
1396
  }
1397
}
1398
1399
1400
static void *
1401
mg_realloc_ex(void *memory,
1402
              size_t newsize,
1403
              struct mg_context *ctx,
1404
              const char *file,
1405
              unsigned line)
1406
{
1407
  void *data;
1408
  void *_realloc;
1409
  uintptr_t oldsize;
1410
1411
#if defined(MEMORY_DEBUGGING)
1412
  char mallocStr[256];
1413
#else
1414
  (void)file;
1415
  (void)line;
1416
#endif
1417
1418
  if (newsize) {
1419
    if (memory) {
1420
      /* Reallocate existing block */
1421
      struct mg_memory_stat *mstat;
1422
      data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t));
1423
      oldsize = ((uintptr_t *)data)[0];
1424
      mstat = (struct mg_memory_stat *)((uintptr_t *)data)[1];
1425
      _realloc = realloc(data, newsize + 2 * sizeof(uintptr_t));
1426
      if (_realloc) {
1427
        data = _realloc;
1428
        mg_atomic_add(&mstat->totalMemUsed, -(ptrdiff_t)oldsize);
1429
#if defined(MEMORY_DEBUGGING)
1430
        sprintf(mallocStr,
1431
                "MEM: %p %5lu r-free  %7lu %4lu --- %s:%u\n",
1432
                memory,
1433
                (unsigned long)oldsize,
1434
                (unsigned long)mstat->totalMemUsed,
1435
                (unsigned long)mstat->blockCount,
1436
                file,
1437
                line);
1438
        DEBUG_TRACE("%s", mallocStr);
1439
#endif
1440
        mg_atomic_add(&mstat->totalMemUsed, (ptrdiff_t)newsize);
1441
1442
#if defined(MEMORY_DEBUGGING)
1443
        sprintf(mallocStr,
1444
                "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n",
1445
                memory,
1446
                (unsigned long)newsize,
1447
                (unsigned long)mstat->totalMemUsed,
1448
                (unsigned long)mstat->blockCount,
1449
                file,
1450
                line);
1451
        DEBUG_TRACE("%s", mallocStr);
1452
#endif
1453
        *(uintptr_t *)data = newsize;
1454
        data = (void *)(((char *)data) + 2 * sizeof(uintptr_t));
1455
      } else {
1456
#if defined(MEMORY_DEBUGGING)
1457
        DEBUG_TRACE("%s", "MEM: realloc failed\n");
1458
#endif
1459
        return _realloc;
1460
      }
1461
    } else {
1462
      /* Allocate new block */
1463
      data = mg_malloc_ex(newsize, ctx, file, line);
1464
    }
1465
  } else {
1466
    /* Free existing block */
1467
    data = 0;
1468
    mg_free_ex(memory, file, line);
1469
  }
1470
1471
  return data;
1472
}
1473
1474
1475
#define mg_malloc(a) mg_malloc_ex(a, NULL, __FILE__, __LINE__)
1476
#define mg_calloc(a, b) mg_calloc_ex(a, b, NULL, __FILE__, __LINE__)
1477
#define mg_realloc(a, b) mg_realloc_ex(a, b, NULL, __FILE__, __LINE__)
1478
#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__)
1479
1480
#define mg_malloc_ctx(a, c) mg_malloc_ex(a, c, __FILE__, __LINE__)
1481
#define mg_calloc_ctx(a, b, c) mg_calloc_ex(a, b, c, __FILE__, __LINE__)
1482
#define mg_realloc_ctx(a, b, c) mg_realloc_ex(a, b, c, __FILE__, __LINE__)
1483
1484
1485
#else /* USE_SERVER_STATS */
1486
1487
1488
static __inline void *
1489
mg_malloc(size_t a)
1490
76
{
1491
76
  return malloc(a);
1492
76
}
1493
1494
static __inline void *
1495
mg_calloc(size_t a, size_t b)
1496
42
{
1497
42
  return calloc(a, b);
1498
42
}
1499
1500
static __inline void *
1501
mg_realloc(void *a, size_t b)
1502
4
{
1503
4
  return realloc(a, b);
1504
4
}
1505
1506
static __inline void
1507
mg_free(void *a)
1508
120
{
1509
120
  free(a);
1510
120
}
1511
1512
74
#define mg_malloc_ctx(a, c) mg_malloc(a)
1513
4
#define mg_calloc_ctx(a, b, c) mg_calloc(a, b)
1514
4
#define mg_realloc_ctx(a, b, c) mg_realloc(a, b)
1515
#define mg_free_ctx(a, c) mg_free(a)
1516
1517
#endif /* USE_SERVER_STATS */
1518
1519
1520
static void mg_vsnprintf(const struct mg_connection *conn,
1521
                         int *truncated,
1522
                         char *buf,
1523
                         size_t buflen,
1524
                         const char *fmt,
1525
                         va_list ap);
1526
1527
static void mg_snprintf(const struct mg_connection *conn,
1528
                        int *truncated,
1529
                        char *buf,
1530
                        size_t buflen,
1531
                        PRINTF_FORMAT_STRING(const char *fmt),
1532
                        ...) PRINTF_ARGS(5, 6);
1533
1534
/* This following lines are just meant as a reminder to use the mg-functions
1535
 * for memory management */
1536
#if defined(malloc)
1537
#undef malloc
1538
#endif
1539
#if defined(calloc)
1540
#undef calloc
1541
#endif
1542
#if defined(realloc)
1543
#undef realloc
1544
#endif
1545
#if defined(free)
1546
#undef free
1547
#endif
1548
#if defined(snprintf)
1549
#undef snprintf
1550
#endif
1551
#if defined(vsnprintf)
1552
#undef vsnprintf
1553
#endif
1554
#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
1555
#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
1556
#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
1557
#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
1558
#define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
1559
#if defined(_WIN32)
1560
/* vsnprintf must not be used in any system,
1561
 * but this define only works well for Windows. */
1562
#define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf
1563
#endif
1564
1565
1566
/* mg_init_library counter */
1567
static int mg_init_library_called = 0;
1568
1569
#if !defined(NO_SSL)
1570
#if defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1)                       \
1571
    || defined(OPENSSL_API_3_0)
1572
static int mg_openssl_initialized = 0;
1573
#endif
1574
#if !defined(OPENSSL_API_1_0) && !defined(OPENSSL_API_1_1)                     \
1575
    && !defined(OPENSSL_API_3_0) && !defined(USE_MBEDTLS)
1576
#error "Please define OPENSSL_API_#_# or USE_MBEDTLS"
1577
#endif
1578
#if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_1_1)
1579
#error "Multiple OPENSSL_API versions defined"
1580
#endif
1581
#if defined(OPENSSL_API_1_1) && defined(OPENSSL_API_3_0)
1582
#error "Multiple OPENSSL_API versions defined"
1583
#endif
1584
#if defined(OPENSSL_API_1_0) && defined(OPENSSL_API_3_0)
1585
#error "Multiple OPENSSL_API versions defined"
1586
#endif
1587
#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1)                      \
1588
     || defined(OPENSSL_API_3_0))                                              \
1589
    && defined(USE_MBEDTLS)
1590
#error "Multiple SSL libraries defined"
1591
#endif
1592
#endif
1593
1594
1595
static pthread_key_t sTlsKey; /* Thread local storage index */
1596
static volatile ptrdiff_t thread_idx_max = 0;
1597
1598
#if defined(MG_LEGACY_INTERFACE)
1599
#define MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE
1600
#endif
1601
1602
struct mg_workerTLS {
1603
  int is_master;
1604
  unsigned long thread_idx;
1605
  void *user_ptr;
1606
#if defined(_WIN32)
1607
  HANDLE pthread_cond_helper_mutex;
1608
  struct mg_workerTLS *next_waiting_thread;
1609
#endif
1610
  const char *alpn_proto;
1611
#if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE)
1612
  char txtbuf[4];
1613
#endif
1614
};
1615
1616
1617
#if defined(GCC_DIAGNOSTIC)
1618
/* Show no warning in case system functions are not used. */
1619
#pragma GCC diagnostic push
1620
#pragma GCC diagnostic ignored "-Wunused-function"
1621
#endif /* defined(GCC_DIAGNOSTIC) */
1622
#if defined(__clang__)
1623
/* Show no warning in case system functions are not used. */
1624
#pragma clang diagnostic push
1625
#pragma clang diagnostic ignored "-Wunused-function"
1626
#endif
1627
1628
1629
/* Get a unique thread ID as unsigned long, independent from the data type
1630
 * of thread IDs defined by the operating system API.
1631
 * If two calls to mg_current_thread_id  return the same value, they calls
1632
 * are done from the same thread. If they return different values, they are
1633
 * done from different threads. (Provided this function is used in the same
1634
 * process context and threads are not repeatedly created and deleted, but
1635
 * CivetWeb does not do that).
1636
 * This function must match the signature required for SSL id callbacks:
1637
 * CRYPTO_set_id_callback
1638
 */
1639
FUNCTION_MAY_BE_UNUSED
1640
static unsigned long
1641
mg_current_thread_id(void)
1642
0
{
1643
0
#if defined(_WIN32)
1644
0
  return GetCurrentThreadId();
1645
0
#else
1646
0
1647
0
#if defined(__clang__)
1648
0
#pragma clang diagnostic push
1649
0
#pragma clang diagnostic ignored "-Wunreachable-code"
1650
0
  /* For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)"
1651
0
   * or not, so one of the two conditions will be unreachable by construction.
1652
0
   * Unfortunately the C standard does not define a way to check this at
1653
0
   * compile time, since the #if preprocessor conditions can not use the
1654
0
   * sizeof operator as an argument. */
1655
0
#endif
1656
0
1657
0
  if (sizeof(pthread_t) > sizeof(unsigned long)) {
1658
0
    /* This is the problematic case for CRYPTO_set_id_callback:
1659
0
     * The OS pthread_t can not be cast to unsigned long. */
1660
0
    struct mg_workerTLS *tls =
1661
0
        (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
1662
0
    if (tls == NULL) {
1663
0
      /* SSL called from an unknown thread: Create some thread index.
1664
0
       */
1665
0
      tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct mg_workerTLS));
1666
0
      tls->is_master = -2; /* -2 means "3rd party thread" */
1667
0
      tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
1668
0
      pthread_setspecific(sTlsKey, tls);
1669
0
    }
1670
0
    return tls->thread_idx;
1671
0
  } else {
1672
0
    /* pthread_t may be any data type, so a simple cast to unsigned long
1673
0
     * can rise a warning/error, depending on the platform.
1674
0
     * Here memcpy is used as an anything-to-anything cast. */
1675
0
    unsigned long ret = 0;
1676
0
    pthread_t t = pthread_self();
1677
0
    memcpy(&ret, &t, sizeof(pthread_t));
1678
0
    return ret;
1679
0
  }
1680
0
1681
0
#if defined(__clang__)
1682
0
#pragma clang diagnostic pop
1683
0
#endif
1684
0
1685
0
#endif
1686
0
}
1687
1688
1689
FUNCTION_MAY_BE_UNUSED
1690
static uint64_t
1691
mg_get_current_time_ns(void)
1692
40
{
1693
40
  struct timespec tsnow;
1694
40
  clock_gettime(CLOCK_REALTIME, &tsnow);
1695
40
  return (((uint64_t)tsnow.tv_sec) * 1000000000) + (uint64_t)tsnow.tv_nsec;
1696
40
}
1697
1698
1699
#if defined(GCC_DIAGNOSTIC)
1700
/* Show no warning in case system functions are not used. */
1701
#pragma GCC diagnostic pop
1702
#endif /* defined(GCC_DIAGNOSTIC) */
1703
#if defined(__clang__)
1704
/* Show no warning in case system functions are not used. */
1705
#pragma clang diagnostic pop
1706
#endif
1707
1708
1709
#if defined(NEED_DEBUG_TRACE_FUNC)
1710
static void
1711
DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...)
1712
{
1713
  va_list args;
1714
  struct timespec tsnow;
1715
1716
  /* Get some operating system independent thread id */
1717
  unsigned long thread_id = mg_current_thread_id();
1718
1719
  clock_gettime(CLOCK_REALTIME, &tsnow);
1720
1721
  flockfile(DEBUG_TRACE_STREAM);
1722
  fprintf(DEBUG_TRACE_STREAM,
1723
          "*** %lu.%09lu %lu %s:%u: ",
1724
          (unsigned long)tsnow.tv_sec,
1725
          (unsigned long)tsnow.tv_nsec,
1726
          thread_id,
1727
          func,
1728
          line);
1729
  va_start(args, fmt);
1730
  vfprintf(DEBUG_TRACE_STREAM, fmt, args);
1731
  va_end(args);
1732
  putc('\n', DEBUG_TRACE_STREAM);
1733
  fflush(DEBUG_TRACE_STREAM);
1734
  funlockfile(DEBUG_TRACE_STREAM);
1735
}
1736
#endif /* NEED_DEBUG_TRACE_FUNC */
1737
1738
1739
#define MD5_STATIC static
1740
#include "md5.inl"
1741
1742
/* Darwin prior to 7.0 and Win32 do not have socklen_t */
1743
#if defined(NO_SOCKLEN_T)
1744
typedef int socklen_t;
1745
#endif /* NO_SOCKLEN_T */
1746
1747
#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */
1748
1749
#if !defined(MSG_NOSIGNAL)
1750
#define MSG_NOSIGNAL (0)
1751
#endif
1752
1753
1754
/* SSL: mbedTLS vs. no-ssl vs. OpenSSL */
1755
#if defined(USE_MBEDTLS)
1756
/* mbedTLS */
1757
#include "mod_mbedtls.inl"
1758
1759
#elif defined(NO_SSL)
1760
/* no SSL */
1761
typedef struct SSL SSL; /* dummy for SSL argument to push/pull */
1762
typedef struct SSL_CTX SSL_CTX;
1763
1764
#elif defined(NO_SSL_DL)
1765
/* OpenSSL without dynamic loading */
1766
#include <openssl/bn.h>
1767
#include <openssl/conf.h>
1768
#include <openssl/crypto.h>
1769
#include <openssl/dh.h>
1770
#include <openssl/engine.h>
1771
#include <openssl/err.h>
1772
#include <openssl/opensslv.h>
1773
#include <openssl/pem.h>
1774
#include <openssl/ssl.h>
1775
#include <openssl/tls1.h>
1776
#include <openssl/x509.h>
1777
1778
#if defined(WOLFSSL_VERSION)
1779
/* Additional defines for WolfSSL, see
1780
 * https://github.com/civetweb/civetweb/issues/583 */
1781
#include "wolfssl_extras.inl"
1782
#endif
1783
1784
#if defined(OPENSSL_IS_BORINGSSL)
1785
/* From boringssl/src/include/openssl/mem.h:
1786
 *
1787
 * OpenSSL has, historically, had a complex set of malloc debugging options.
1788
 * However, that was written in a time before Valgrind and ASAN. Since we now
1789
 * have those tools, the OpenSSL allocation functions are simply macros around
1790
 * the standard memory functions.
1791
 *
1792
 * #define OPENSSL_free free */
1793
#define free free
1794
// disable for boringssl
1795
#define CONF_modules_unload(a) ((void)0)
1796
#define ENGINE_cleanup() ((void)0)
1797
#endif
1798
1799
/* If OpenSSL headers are included, automatically select the API version */
1800
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
1801
#if !defined(OPENSSL_API_3_0)
1802
#define OPENSSL_API_3_0
1803
#endif
1804
#define OPENSSL_REMOVE_THREAD_STATE()
1805
#else
1806
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
1807
#if !defined(OPENSSL_API_1_1)
1808
#define OPENSSL_API_1_1
1809
#endif
1810
#define OPENSSL_REMOVE_THREAD_STATE()
1811
#else
1812
#if !defined(OPENSSL_API_1_0)
1813
#define OPENSSL_API_1_0
1814
#endif
1815
#define OPENSSL_REMOVE_THREAD_STATE() ERR_remove_thread_state(NULL)
1816
#endif
1817
#endif
1818
1819
1820
#else
1821
/* SSL loaded dynamically from DLL / shared object */
1822
/* Add all prototypes here, to be independent from OpenSSL source
1823
 * installation. */
1824
#include "openssl_dl.inl"
1825
1826
#endif /* Various SSL bindings */
1827
1828
1829
#if !defined(NO_CACHING)
1830
static const char month_names[][4] = {"Jan",
1831
                                      "Feb",
1832
                                      "Mar",
1833
                                      "Apr",
1834
                                      "May",
1835
                                      "Jun",
1836
                                      "Jul",
1837
                                      "Aug",
1838
                                      "Sep",
1839
                                      "Oct",
1840
                                      "Nov",
1841
                                      "Dec"};
1842
#endif /* !NO_CACHING */
1843
1844
1845
/* Unified socket address. For IPv6 support, add IPv6 address structure in
1846
 * the union u. */
1847
union usa {
1848
  struct sockaddr sa;
1849
  struct sockaddr_in sin;
1850
#if defined(USE_IPV6)
1851
  struct sockaddr_in6 sin6;
1852
#endif
1853
#if defined(USE_X_DOM_SOCKET)
1854
  struct sockaddr_un sun;
1855
#endif
1856
};
1857
1858
#if defined(USE_X_DOM_SOCKET)
1859
static unsigned short
1860
USA_IN_PORT_UNSAFE(union usa *s)
1861
{
1862
  if (s->sa.sa_family == AF_INET)
1863
    return s->sin.sin_port;
1864
#if defined(USE_IPV6)
1865
  if (s->sa.sa_family == AF_INET6)
1866
    return s->sin6.sin6_port;
1867
#endif
1868
  return 0;
1869
}
1870
#endif
1871
#if defined(USE_IPV6)
1872
#define USA_IN_PORT_UNSAFE(s)                                                  \
1873
  (((s)->sa.sa_family == AF_INET6) ? (s)->sin6.sin6_port : (s)->sin.sin_port)
1874
#else
1875
#define USA_IN_PORT_UNSAFE(s) ((s)->sin.sin_port)
1876
#endif
1877
1878
/* Describes a string (chunk of memory). */
1879
struct vec {
1880
  const char *ptr;
1881
  size_t len;
1882
};
1883
1884
struct mg_file_stat {
1885
  /* File properties filled by mg_stat: */
1886
  uint64_t size;
1887
  time_t last_modified;
1888
  int is_directory; /* Set to 1 if mg_stat is called for a directory */
1889
  int is_gzipped;   /* Set to 1 if the content is gzipped, in which
1890
                     * case we need a "Content-Eencoding: gzip" header */
1891
  int location;     /* 0 = nowhere, 1 = on disk, 2 = in memory */
1892
};
1893
1894
1895
struct mg_file_access {
1896
  /* File properties filled by mg_fopen: */
1897
  FILE *fp;
1898
};
1899
1900
struct mg_file {
1901
  struct mg_file_stat stat;
1902
  struct mg_file_access access;
1903
};
1904
1905
1906
#define STRUCT_FILE_INITIALIZER                                                \
1907
2
  {                                                                          \
1908
2
    {(uint64_t)0, (time_t)0, 0, 0, 0},                                     \
1909
2
    {                                                                      \
1910
2
      (FILE *)NULL                                                       \
1911
2
    }                                                                      \
1912
2
  }
1913
1914
1915
/* Describes listening socket, or socket which was accept()-ed by the master
1916
 * thread and queued for future handling by the worker thread. */
1917
struct socket {
1918
  SOCKET sock;             /* Listening socket */
1919
  union usa lsa;           /* Local socket address */
1920
  union usa rsa;           /* Remote socket address */
1921
  unsigned char is_ssl;    /* Is port SSL-ed */
1922
  unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL
1923
                            * port */
1924
  unsigned char
1925
      is_optional; /* Shouldn't cause us to exit if we can't bind to it */
1926
  unsigned char in_use; /* 0: invalid, 1: valid, 2: free */
1927
};
1928
1929
1930
/* Enum const for all options must be in sync with
1931
 * static struct mg_option config_options[]
1932
 * This is tested in the unit test (test/private.c)
1933
 * "Private Config Options"
1934
 */
1935
enum {
1936
  /* Once for each server */
1937
  LISTENING_PORTS,
1938
  NUM_THREADS,
1939
  PRESPAWN_THREADS,
1940
  RUN_AS_USER,
1941
  CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
1942
                       * socket option typedef TCP_NODELAY. */
1943
  MAX_REQUEST_SIZE,
1944
  LINGER_TIMEOUT,
1945
  CONNECTION_QUEUE_SIZE,
1946
  LISTEN_BACKLOG_SIZE,
1947
#if defined(__linux__)
1948
  ALLOW_SENDFILE_CALL,
1949
#endif
1950
#if defined(_WIN32)
1951
  CASE_SENSITIVE_FILES,
1952
#endif
1953
  THROTTLE,
1954
  ENABLE_KEEP_ALIVE,
1955
  REQUEST_TIMEOUT,
1956
  KEEP_ALIVE_TIMEOUT,
1957
#if defined(USE_WEBSOCKET)
1958
  WEBSOCKET_TIMEOUT,
1959
  ENABLE_WEBSOCKET_PING_PONG,
1960
#endif
1961
  DECODE_URL,
1962
  DECODE_QUERY_STRING,
1963
#if defined(USE_LUA)
1964
  LUA_BACKGROUND_SCRIPT,
1965
  LUA_BACKGROUND_SCRIPT_PARAMS,
1966
#endif
1967
#if defined(USE_HTTP2)
1968
  ENABLE_HTTP2,
1969
#endif
1970
1971
  /* Once for each domain */
1972
  DOCUMENT_ROOT,
1973
  FALLBACK_DOCUMENT_ROOT,
1974
1975
  ACCESS_LOG_FILE,
1976
  ERROR_LOG_FILE,
1977
1978
  CGI_EXTENSIONS,
1979
  CGI_ENVIRONMENT,
1980
  CGI_INTERPRETER,
1981
  CGI_INTERPRETER_ARGS,
1982
#if defined(USE_TIMERS)
1983
  CGI_TIMEOUT,
1984
#endif
1985
  CGI_BUFFERING,
1986
1987
  CGI2_EXTENSIONS,
1988
  CGI2_ENVIRONMENT,
1989
  CGI2_INTERPRETER,
1990
  CGI2_INTERPRETER_ARGS,
1991
#if defined(USE_TIMERS)
1992
  CGI2_TIMEOUT,
1993
#endif
1994
  CGI2_BUFFERING,
1995
1996
#if defined(USE_4_CGI)
1997
  CGI3_EXTENSIONS,
1998
  CGI3_ENVIRONMENT,
1999
  CGI3_INTERPRETER,
2000
  CGI3_INTERPRETER_ARGS,
2001
#if defined(USE_TIMERS)
2002
  CGI3_TIMEOUT,
2003
#endif
2004
  CGI3_BUFFERING,
2005
2006
  CGI4_EXTENSIONS,
2007
  CGI4_ENVIRONMENT,
2008
  CGI4_INTERPRETER,
2009
  CGI4_INTERPRETER_ARGS,
2010
#if defined(USE_TIMERS)
2011
  CGI4_TIMEOUT,
2012
#endif
2013
  CGI4_BUFFERING,
2014
#endif
2015
2016
  PUT_DELETE_PASSWORDS_FILE, /* must follow CGI_* */
2017
  PROTECT_URI,
2018
  AUTHENTICATION_DOMAIN,
2019
  ENABLE_AUTH_DOMAIN_CHECK,
2020
  SSI_EXTENSIONS,
2021
  ENABLE_DIRECTORY_LISTING,
2022
  ENABLE_WEBDAV,
2023
  GLOBAL_PASSWORDS_FILE,
2024
  INDEX_FILES,
2025
  ACCESS_CONTROL_LIST,
2026
  EXTRA_MIME_TYPES,
2027
  SSL_CERTIFICATE,
2028
  SSL_CERTIFICATE_CHAIN,
2029
  URL_REWRITE_PATTERN,
2030
  HIDE_FILES,
2031
  SSL_DO_VERIFY_PEER,
2032
  SSL_CACHE_TIMEOUT,
2033
  SSL_CA_PATH,
2034
  SSL_CA_FILE,
2035
  SSL_VERIFY_DEPTH,
2036
  SSL_DEFAULT_VERIFY_PATHS,
2037
  SSL_CIPHER_LIST,
2038
  SSL_PROTOCOL_VERSION,
2039
  SSL_SHORT_TRUST,
2040
2041
#if defined(USE_LUA)
2042
  LUA_PRELOAD_FILE,
2043
  LUA_SCRIPT_EXTENSIONS,
2044
  LUA_SERVER_PAGE_EXTENSIONS,
2045
#if defined(MG_EXPERIMENTAL_INTERFACES)
2046
  LUA_DEBUG_PARAMS,
2047
#endif
2048
#endif
2049
#if defined(USE_DUKTAPE)
2050
  DUKTAPE_SCRIPT_EXTENSIONS,
2051
#endif
2052
2053
#if defined(USE_WEBSOCKET)
2054
  WEBSOCKET_ROOT,
2055
  FALLBACK_WEBSOCKET_ROOT,
2056
#endif
2057
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2058
  LUA_WEBSOCKET_EXTENSIONS,
2059
#endif
2060
2061
  ACCESS_CONTROL_ALLOW_ORIGIN,
2062
  ACCESS_CONTROL_ALLOW_METHODS,
2063
  ACCESS_CONTROL_ALLOW_HEADERS,
2064
  ACCESS_CONTROL_EXPOSE_HEADERS,
2065
  ACCESS_CONTROL_ALLOW_CREDENTIALS,
2066
  ERROR_PAGES,
2067
#if !defined(NO_CACHING)
2068
  STATIC_FILE_MAX_AGE,
2069
  STATIC_FILE_CACHE_CONTROL,
2070
#endif
2071
#if !defined(NO_SSL)
2072
  STRICT_HTTPS_MAX_AGE,
2073
#endif
2074
  ADDITIONAL_HEADER,
2075
  ALLOW_INDEX_SCRIPT_SUB_RES,
2076
2077
  NUM_OPTIONS
2078
};
2079
2080
2081
/* Config option name, config types, default value.
2082
 * Must be in the same order as the enum const above.
2083
 */
2084
static const struct mg_option config_options[] = {
2085
2086
    /* Once for each server */
2087
    {"listening_ports", MG_CONFIG_TYPE_STRING_LIST, "8080"},
2088
    {"num_threads", MG_CONFIG_TYPE_NUMBER, "50"},
2089
    {"prespawn_threads", MG_CONFIG_TYPE_NUMBER, "0"},
2090
    {"run_as_user", MG_CONFIG_TYPE_STRING, NULL},
2091
    {"tcp_nodelay", MG_CONFIG_TYPE_NUMBER, "0"},
2092
    {"max_request_size", MG_CONFIG_TYPE_NUMBER, "16384"},
2093
    {"linger_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2094
    {"connection_queue", MG_CONFIG_TYPE_NUMBER, "20"},
2095
    {"listen_backlog", MG_CONFIG_TYPE_NUMBER, "200"},
2096
#if defined(__linux__)
2097
    {"allow_sendfile_call", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2098
#endif
2099
#if defined(_WIN32)
2100
    {"case_sensitive", MG_CONFIG_TYPE_BOOLEAN, "no"},
2101
#endif
2102
    {"throttle", MG_CONFIG_TYPE_STRING_LIST, NULL},
2103
    {"enable_keep_alive", MG_CONFIG_TYPE_BOOLEAN, "no"},
2104
    {"request_timeout_ms", MG_CONFIG_TYPE_NUMBER, "30000"},
2105
    {"keep_alive_timeout_ms", MG_CONFIG_TYPE_NUMBER, "500"},
2106
#if defined(USE_WEBSOCKET)
2107
    {"websocket_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2108
    {"enable_websocket_ping_pong", MG_CONFIG_TYPE_BOOLEAN, "no"},
2109
#endif
2110
    {"decode_url", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2111
    {"decode_query_string", MG_CONFIG_TYPE_BOOLEAN, "no"},
2112
#if defined(USE_LUA)
2113
    {"lua_background_script", MG_CONFIG_TYPE_FILE, NULL},
2114
    {"lua_background_script_params", MG_CONFIG_TYPE_STRING_LIST, NULL},
2115
#endif
2116
#if defined(USE_HTTP2)
2117
    {"enable_http2", MG_CONFIG_TYPE_BOOLEAN, "no"},
2118
#endif
2119
2120
    /* Once for each domain */
2121
    {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2122
    {"fallback_document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2123
2124
    {"access_log_file", MG_CONFIG_TYPE_FILE, NULL},
2125
    {"error_log_file", MG_CONFIG_TYPE_FILE, NULL},
2126
2127
    {"cgi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"},
2128
    {"cgi_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2129
    {"cgi_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2130
    {"cgi_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2131
#if defined(USE_TIMERS)
2132
    {"cgi_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2133
#endif
2134
    {"cgi_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2135
2136
    {"cgi2_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2137
    {"cgi2_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2138
    {"cgi2_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2139
    {"cgi2_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2140
#if defined(USE_TIMERS)
2141
    {"cgi2_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2142
#endif
2143
    {"cgi2_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2144
2145
#if defined(USE_4_CGI)
2146
    {"cgi3_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2147
    {"cgi3_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2148
    {"cgi3_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2149
    {"cgi3_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2150
#if defined(USE_TIMERS)
2151
    {"cgi3_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2152
#endif
2153
    {"cgi3_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2154
2155
    {"cgi4_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2156
    {"cgi4_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2157
    {"cgi4_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2158
    {"cgi4_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2159
#if defined(USE_TIMERS)
2160
    {"cgi4_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2161
#endif
2162
    {"cgi4_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2163
2164
#endif
2165
2166
    {"put_delete_auth_file", MG_CONFIG_TYPE_FILE, NULL},
2167
    {"protect_uri", MG_CONFIG_TYPE_STRING_LIST, NULL},
2168
    {"authentication_domain", MG_CONFIG_TYPE_STRING, "mydomain.com"},
2169
    {"enable_auth_domain_check", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2170
    {"ssi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"},
2171
    {"enable_directory_listing", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2172
    {"enable_webdav", MG_CONFIG_TYPE_BOOLEAN, "no"},
2173
    {"global_auth_file", MG_CONFIG_TYPE_FILE, NULL},
2174
    {"index_files",
2175
     MG_CONFIG_TYPE_STRING_LIST,
2176
#if defined(USE_LUA)
2177
     "index.xhtml,index.html,index.htm,"
2178
     "index.lp,index.lsp,index.lua,index.cgi,"
2179
     "index.shtml,index.php"},
2180
#else
2181
     "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"},
2182
#endif
2183
    {"access_control_list", MG_CONFIG_TYPE_STRING_LIST, NULL},
2184
    {"extra_mime_types", MG_CONFIG_TYPE_STRING_LIST, NULL},
2185
    {"ssl_certificate", MG_CONFIG_TYPE_FILE, NULL},
2186
    {"ssl_certificate_chain", MG_CONFIG_TYPE_FILE, NULL},
2187
    {"url_rewrite_patterns", MG_CONFIG_TYPE_STRING_LIST, NULL},
2188
    {"hide_files_patterns", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2189
2190
    {"ssl_verify_peer", MG_CONFIG_TYPE_YES_NO_OPTIONAL, "no"},
2191
    {"ssl_cache_timeout", MG_CONFIG_TYPE_NUMBER, "-1"},
2192
2193
    {"ssl_ca_path", MG_CONFIG_TYPE_DIRECTORY, NULL},
2194
    {"ssl_ca_file", MG_CONFIG_TYPE_FILE, NULL},
2195
    {"ssl_verify_depth", MG_CONFIG_TYPE_NUMBER, "9"},
2196
    {"ssl_default_verify_paths", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2197
    {"ssl_cipher_list", MG_CONFIG_TYPE_STRING, NULL},
2198
2199
    /* HTTP2 requires ALPN, and anyway TLS1.2 should be considered
2200
     * as a minimum in 2020 */
2201
    {"ssl_protocol_version", MG_CONFIG_TYPE_NUMBER, "4"},
2202
2203
    {"ssl_short_trust", MG_CONFIG_TYPE_BOOLEAN, "no"},
2204
2205
#if defined(USE_LUA)
2206
    {"lua_preload_file", MG_CONFIG_TYPE_FILE, NULL},
2207
    {"lua_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
2208
    {"lua_server_page_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"},
2209
#if defined(MG_EXPERIMENTAL_INTERFACES)
2210
    {"lua_debug", MG_CONFIG_TYPE_STRING, NULL},
2211
#endif
2212
#endif
2213
#if defined(USE_DUKTAPE)
2214
    /* The support for duktape is still in alpha version state.
2215
     * The name of this config option might change. */
2216
    {"duktape_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"},
2217
#endif
2218
2219
#if defined(USE_WEBSOCKET)
2220
    {"websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2221
    {"fallback_websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2222
#endif
2223
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2224
    {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
2225
#endif
2226
    {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"},
2227
    {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"},
2228
    {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"},
2229
    {"access_control_expose_headers", MG_CONFIG_TYPE_STRING, ""},
2230
    {"access_control_allow_credentials", MG_CONFIG_TYPE_STRING, ""},
2231
    {"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL},
2232
#if !defined(NO_CACHING)
2233
    {"static_file_max_age", MG_CONFIG_TYPE_NUMBER, "3600"},
2234
    {"static_file_cache_control", MG_CONFIG_TYPE_STRING, NULL},
2235
#endif
2236
#if !defined(NO_SSL)
2237
    {"strict_transport_security_max_age", MG_CONFIG_TYPE_NUMBER, NULL},
2238
#endif
2239
    {"additional_header", MG_CONFIG_TYPE_STRING_MULTILINE, NULL},
2240
    {"allow_index_script_resource", MG_CONFIG_TYPE_BOOLEAN, "no"},
2241
2242
    {NULL, MG_CONFIG_TYPE_UNKNOWN, NULL}};
2243
2244
2245
/* Check if the config_options and the corresponding enum have compatible
2246
 * sizes. */
2247
mg_static_assert((sizeof(config_options) / sizeof(config_options[0]))
2248
                     == (NUM_OPTIONS + 1),
2249
                 "config_options and enum not sync");
2250
2251
2252
enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER };
2253
2254
2255
struct mg_handler_info {
2256
  /* Name/Pattern of the URI. */
2257
  char *uri;
2258
  size_t uri_len;
2259
2260
  /* handler type */
2261
  int handler_type;
2262
2263
  /* Handler for http/https or requests. */
2264
  mg_request_handler handler;
2265
  unsigned int refcount;
2266
  int removing;
2267
2268
  /* Handler for ws/wss (websocket) requests. */
2269
  mg_websocket_connect_handler connect_handler;
2270
  mg_websocket_ready_handler ready_handler;
2271
  mg_websocket_data_handler data_handler;
2272
  mg_websocket_close_handler close_handler;
2273
2274
  /* accepted subprotocols for ws/wss requests. */
2275
  struct mg_websocket_subprotocols *subprotocols;
2276
2277
  /* Handler for authorization requests */
2278
  mg_authorization_handler auth_handler;
2279
2280
  /* User supplied argument for the handler function. */
2281
  void *cbdata;
2282
2283
  /* next handler in a linked list */
2284
  struct mg_handler_info *next;
2285
};
2286
2287
2288
enum {
2289
  CONTEXT_INVALID,
2290
  CONTEXT_SERVER,
2291
  CONTEXT_HTTP_CLIENT,
2292
  CONTEXT_WS_CLIENT
2293
};
2294
2295
2296
struct mg_domain_context {
2297
  SSL_CTX *ssl_ctx;                 /* SSL context */
2298
  char *config[NUM_OPTIONS];        /* Civetweb configuration parameters */
2299
  struct mg_handler_info *handlers; /* linked list of uri handlers */
2300
  int64_t ssl_cert_last_mtime;
2301
2302
  /* Server nonce */
2303
  uint64_t auth_nonce_mask;  /* Mask for all nonce values */
2304
  unsigned long nonce_count; /* Used nonces, used for authentication */
2305
2306
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2307
  /* linked list of shared lua websockets */
2308
  struct mg_shared_lua_websocket_list *shared_lua_websockets;
2309
#endif
2310
2311
  /* Linked list of domains */
2312
  struct mg_domain_context *next;
2313
};
2314
2315
2316
/* Stop flag can be "volatile" or require a lock.
2317
 * MSDN uses volatile for "Interlocked" operations, but also explicitly
2318
 * states a read operation for int is always atomic. */
2319
#if defined(STOP_FLAG_NEEDS_LOCK)
2320
2321
typedef ptrdiff_t volatile stop_flag_t;
2322
2323
static int
2324
STOP_FLAG_IS_ZERO(const stop_flag_t *f)
2325
{
2326
  stop_flag_t sf = mg_atomic_add((stop_flag_t *)f, 0);
2327
  return (sf == 0);
2328
}
2329
2330
static int
2331
STOP_FLAG_IS_TWO(stop_flag_t *f)
2332
{
2333
  stop_flag_t sf = mg_atomic_add(f, 0);
2334
  return (sf == 2);
2335
}
2336
2337
static void
2338
STOP_FLAG_ASSIGN(stop_flag_t *f, stop_flag_t v)
2339
{
2340
  stop_flag_t sf = 0;
2341
  do {
2342
    sf = mg_atomic_compare_and_swap(f, *f, v);
2343
  } while (sf != v);
2344
}
2345
2346
#else /* STOP_FLAG_NEEDS_LOCK */
2347
2348
typedef int volatile stop_flag_t;
2349
222
#define STOP_FLAG_IS_ZERO(f) ((*(f)) == 0)
2350
4
#define STOP_FLAG_IS_TWO(f) ((*(f)) == 2)
2351
4
#define STOP_FLAG_ASSIGN(f, v) ((*(f)) = (v))
2352
2353
#endif /* STOP_FLAG_NEEDS_LOCK */
2354
2355
2356
#if !defined(NUM_WEBDAV_LOCKS)
2357
0
#define NUM_WEBDAV_LOCKS 10
2358
#endif
2359
#if !defined(LOCK_DURATION_S)
2360
0
#define LOCK_DURATION_S 60
2361
#endif
2362
2363
2364
struct twebdav_lock {
2365
  uint64_t locktime;
2366
  char token[33];
2367
  char path[UTF8_PATH_MAX * 2];
2368
  char user[UTF8_PATH_MAX * 2];
2369
};
2370
2371
2372
struct mg_context {
2373
2374
  /* Part 1 - Physical context:
2375
   * This holds threads, ports, timeouts, ...
2376
   * set for the entire server, independent from the
2377
   * addressed hostname.
2378
   */
2379
2380
  /* Connection related */
2381
  int context_type; /* See CONTEXT_* above */
2382
2383
  struct socket *listening_sockets;
2384
  struct mg_pollfd *listening_socket_fds;
2385
  unsigned int num_listening_sockets;
2386
2387
  struct mg_connection *worker_connections; /* The connection struct, pre-
2388
                                             * allocated for each worker */
2389
2390
#if defined(USE_SERVER_STATS)
2391
  volatile ptrdiff_t active_connections;
2392
  volatile ptrdiff_t max_active_connections;
2393
  volatile ptrdiff_t total_connections;
2394
  volatile ptrdiff_t total_requests;
2395
  volatile int64_t total_data_read;
2396
  volatile int64_t total_data_written;
2397
#endif
2398
2399
  /* Thread related */
2400
  stop_flag_t stop_flag;        /* Should we stop event loop */
2401
  pthread_mutex_t thread_mutex; /* Protects client_socks or queue */
2402
2403
  pthread_t masterthreadid;            /* The master thread ID */
2404
  unsigned int cfg_max_worker_threads; /* How many worker-threads we are
2405
                                          allowed to create, total */
2406
2407
  unsigned int spawned_worker_threads; /* How many worker-threads currently
2408
                                          exist (modified by master thread) */
2409
  unsigned int
2410
      idle_worker_thread_count; /* How many worker-threads are currently
2411
                                   sitting around with nothing to do */
2412
  /* Access to this value MUST be synchronized by thread_mutex */
2413
2414
  pthread_t *worker_threadids;      /* The worker thread IDs */
2415
  unsigned long starter_thread_idx; /* thread index which called mg_start */
2416
2417
  /* Connection to thread dispatching */
2418
#if defined(ALTERNATIVE_QUEUE)
2419
  struct socket *client_socks;
2420
  void **client_wait_events;
2421
#else
2422
  struct socket *squeue; /* Socket queue (sq) : accepted sockets waiting for a
2423
                         worker thread */
2424
  volatile int sq_head;  /* Head of the socket queue */
2425
  volatile int sq_tail;  /* Tail of the socket queue */
2426
  pthread_cond_t sq_full;  /* Signaled when socket is produced */
2427
  pthread_cond_t sq_empty; /* Signaled when socket is consumed */
2428
  volatile int sq_blocked; /* Status information: sq is full */
2429
  int sq_size;             /* No of elements in socket queue */
2430
#if defined(USE_SERVER_STATS)
2431
  int sq_max_fill;
2432
#endif /* USE_SERVER_STATS */
2433
#endif /* ALTERNATIVE_QUEUE */
2434
2435
  /* Memory related */
2436
  unsigned int max_request_size; /* The max request size */
2437
2438
#if defined(USE_SERVER_STATS)
2439
  struct mg_memory_stat ctx_memory;
2440
#endif
2441
2442
  /* WebDAV lock structures */
2443
  struct twebdav_lock webdav_lock[NUM_WEBDAV_LOCKS];
2444
2445
  /* Operating system related */
2446
  char *systemName;  /* What operating system is running */
2447
  time_t start_time; /* Server start time, used for authentication
2448
                      * and for diagnstics. */
2449
2450
#if defined(USE_TIMERS)
2451
  struct ttimers *timers;
2452
#endif
2453
2454
  /* Lua specific: Background operations and shared websockets */
2455
#if defined(USE_LUA)
2456
  void *lua_background_state;   /* lua_State (here as void *) */
2457
  pthread_mutex_t lua_bg_mutex; /* Protect background state */
2458
  int lua_bg_log_available;     /* Use Lua background state for access log */
2459
#endif
2460
2461
  int user_shutdown_notification_socket;   /* mg_stop() will close this
2462
                                              socket... */
2463
  int thread_shutdown_notification_socket; /* to cause poll() in all threads
2464
                                              to return immediately */
2465
2466
  /* Server nonce */
2467
  pthread_mutex_t nonce_mutex; /* Protects ssl_ctx, handlers,
2468
                                * ssl_cert_last_mtime, nonce_count, and
2469
                                * next (linked list) */
2470
2471
  /* Server callbacks */
2472
  struct mg_callbacks callbacks; /* User-defined callback function */
2473
  void *user_data;               /* User-defined data */
2474
2475
  /* Part 2 - Logical domain:
2476
   * This holds hostname, TLS certificate, document root, ...
2477
   * set for a domain hosted at the server.
2478
   * There may be multiple domains hosted at one physical server.
2479
   * The default domain "dd" is the first element of a list of
2480
   * domains.
2481
   */
2482
  struct mg_domain_context dd; /* default domain */
2483
};
2484
2485
2486
#if defined(USE_SERVER_STATS)
2487
static struct mg_memory_stat mg_common_memory = {0, 0, 0};
2488
2489
static struct mg_memory_stat *
2490
get_memory_stat(struct mg_context *ctx)
2491
{
2492
  if (ctx) {
2493
    return &(ctx->ctx_memory);
2494
  }
2495
  return &mg_common_memory;
2496
}
2497
#endif
2498
2499
enum {
2500
  CONNECTION_TYPE_INVALID = 0,
2501
  CONNECTION_TYPE_REQUEST = 1,
2502
  CONNECTION_TYPE_RESPONSE = 2
2503
};
2504
2505
enum {
2506
  PROTOCOL_TYPE_HTTP1 = 0,
2507
  PROTOCOL_TYPE_WEBSOCKET = 1,
2508
  PROTOCOL_TYPE_HTTP2 = 2
2509
};
2510
2511
2512
#if defined(USE_HTTP2)
2513
#if !defined(HTTP2_DYN_TABLE_SIZE)
2514
#define HTTP2_DYN_TABLE_SIZE (256)
2515
#endif
2516
2517
struct mg_http2_connection {
2518
  uint32_t stream_id;
2519
  uint32_t dyn_table_size;
2520
  struct mg_header dyn_table[HTTP2_DYN_TABLE_SIZE];
2521
};
2522
#endif
2523
2524
2525
struct mg_connection {
2526
  int connection_type; /* see CONNECTION_TYPE_* above */
2527
  int protocol_type;   /* see PROTOCOL_TYPE_*: 0=http/1.x, 1=ws, 2=http/2 */
2528
  int request_state;   /* 0: nothing sent, 1: header partially sent, 2: header
2529
                       fully sent */
2530
#if defined(USE_HTTP2)
2531
  struct mg_http2_connection http2;
2532
#endif
2533
2534
  struct mg_request_info request_info;
2535
  struct mg_response_info response_info;
2536
2537
  struct mg_context *phys_ctx;
2538
  struct mg_domain_context *dom_ctx;
2539
2540
#if defined(USE_SERVER_STATS)
2541
  int conn_state; /* 0 = undef, numerical value may change in different
2542
                   * versions. For the current definition, see
2543
                   * mg_get_connection_info_impl */
2544
#endif
2545
2546
  SSL *ssl;               /* SSL descriptor */
2547
  struct socket client;   /* Connected client */
2548
  time_t conn_birth_time; /* Time (wall clock) when connection was
2549
                           * established */
2550
#if defined(USE_SERVER_STATS)
2551
  time_t conn_close_time; /* Time (wall clock) when connection was
2552
                           * closed (or 0 if still open) */
2553
  double processing_time; /* Processing time for one request. */
2554
#endif
2555
  struct timespec req_time; /* Time (since system start) when the request
2556
                             * was received */
2557
  int64_t num_bytes_sent;   /* Total bytes sent to client */
2558
  int64_t content_len;      /* How many bytes of content can be read
2559
                             * !is_chunked: Content-Length header value
2560
                             *              or -1 (until connection closed,
2561
                             *                     not allowed for a request)
2562
                             * is_chunked: >= 0, appended gradually
2563
                             */
2564
  int64_t consumed_content; /* How many bytes of content have been read */
2565
  int is_chunked;           /* Transfer-Encoding is chunked:
2566
                             * 0 = not chunked,
2567
                             * 1 = chunked, not yet, or some data read,
2568
                             * 2 = chunked, has error,
2569
                             * 3 = chunked, all data read except trailer,
2570
                             * 4 = chunked, all data read
2571
                             */
2572
  char *buf;                /* Buffer for received data */
2573
  char *path_info;          /* PATH_INFO part of the URL */
2574
2575
  int must_close;       /* 1 if connection must be closed */
2576
  int accept_gzip;      /* 1 if gzip encoding is accepted */
2577
  int in_error_handler; /* 1 if in handler for user defined error
2578
                         * pages */
2579
#if defined(USE_WEBSOCKET)
2580
  int in_websocket_handling; /* 1 if in read_websocket */
2581
#endif
2582
#if defined(USE_ZLIB) && defined(USE_WEBSOCKET)                                \
2583
    && defined(MG_EXPERIMENTAL_INTERFACES)
2584
  /* Parameters for websocket data compression according to rfc7692 */
2585
  int websocket_deflate_server_max_windows_bits;
2586
  int websocket_deflate_client_max_windows_bits;
2587
  int websocket_deflate_server_no_context_takeover;
2588
  int websocket_deflate_client_no_context_takeover;
2589
  int websocket_deflate_initialized;
2590
  int websocket_deflate_flush;
2591
  z_stream websocket_deflate_state;
2592
  z_stream websocket_inflate_state;
2593
#endif
2594
  int handled_requests; /* Number of requests handled by this connection
2595
                         */
2596
  int buf_size;         /* Buffer size */
2597
  int request_len;      /* Size of the request + headers in a buffer */
2598
  int data_len;         /* Total size of data in a buffer */
2599
  int status_code;      /* HTTP reply status code, e.g. 200 */
2600
  int throttle;         /* Throttling, bytes/sec. <= 0 means no
2601
                         * throttle */
2602
2603
  time_t last_throttle_time; /* Last time throttled data was sent */
2604
  int last_throttle_bytes;   /* Bytes sent this second */
2605
  pthread_mutex_t mutex;     /* Used by mg_(un)lock_connection to ensure
2606
                              * atomic transmissions for websockets */
2607
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2608
  void *lua_websocket_state; /* Lua_State for a websocket connection */
2609
#endif
2610
2611
  void *tls_user_ptr; /* User defined pointer in thread local storage,
2612
                       * for quick access */
2613
};
2614
2615
2616
/* Directory entry */
2617
struct de {
2618
  char *file_name;
2619
  struct mg_file_stat file;
2620
};
2621
2622
2623
#define mg_cry_internal(conn, fmt, ...)                                        \
2624
0
  mg_cry_internal_wrap(conn, NULL, __func__, __LINE__, fmt, __VA_ARGS__)
2625
2626
#define mg_cry_ctx_internal(ctx, fmt, ...)                                     \
2627
0
  mg_cry_internal_wrap(NULL, ctx, __func__, __LINE__, fmt, __VA_ARGS__)
2628
2629
static void mg_cry_internal_wrap(const struct mg_connection *conn,
2630
                                 struct mg_context *ctx,
2631
                                 const char *func,
2632
                                 unsigned line,
2633
                                 const char *fmt,
2634
                                 ...) PRINTF_ARGS(5, 6);
2635
2636
2637
#if !defined(NO_THREAD_NAME)
2638
#if defined(_WIN32) && defined(_MSC_VER)
2639
/* Set the thread name for debugging purposes in Visual Studio
2640
 * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
2641
 */
2642
#pragma pack(push, 8)
2643
typedef struct tagTHREADNAME_INFO {
2644
  DWORD dwType;     /* Must be 0x1000. */
2645
  LPCSTR szName;    /* Pointer to name (in user addr space). */
2646
  DWORD dwThreadID; /* Thread ID (-1=caller thread). */
2647
  DWORD dwFlags;    /* Reserved for future use, must be zero. */
2648
} THREADNAME_INFO;
2649
#pragma pack(pop)
2650
2651
#elif defined(__linux__)
2652
2653
#include <sys/prctl.h>
2654
#include <sys/sendfile.h>
2655
#if defined(ALTERNATIVE_QUEUE)
2656
#include <sys/eventfd.h>
2657
#endif /* ALTERNATIVE_QUEUE */
2658
2659
2660
#if defined(ALTERNATIVE_QUEUE)
2661
2662
static void *
2663
event_create(void)
2664
{
2665
  int evhdl = eventfd(0, EFD_CLOEXEC);
2666
  int *ret;
2667
2668
  if (evhdl == -1) {
2669
    /* Linux uses -1 on error, Windows NULL. */
2670
    /* However, Linux does not return 0 on success either. */
2671
    return 0;
2672
  }
2673
2674
  ret = (int *)mg_malloc(sizeof(int));
2675
  if (ret) {
2676
    *ret = evhdl;
2677
  } else {
2678
    (void)close(evhdl);
2679
  }
2680
2681
  return (void *)ret;
2682
}
2683
2684
2685
static int
2686
event_wait(void *eventhdl)
2687
{
2688
  uint64_t u;
2689
  int evhdl, s;
2690
2691
  if (!eventhdl) {
2692
    /* error */
2693
    return 0;
2694
  }
2695
  evhdl = *(int *)eventhdl;
2696
2697
  s = (int)read(evhdl, &u, sizeof(u));
2698
  if (s != sizeof(u)) {
2699
    /* error */
2700
    return 0;
2701
  }
2702
  (void)u; /* the value is not required */
2703
  return 1;
2704
}
2705
2706
2707
static int
2708
event_signal(void *eventhdl)
2709
{
2710
  uint64_t u = 1;
2711
  int evhdl, s;
2712
2713
  if (!eventhdl) {
2714
    /* error */
2715
    return 0;
2716
  }
2717
  evhdl = *(int *)eventhdl;
2718
2719
  s = (int)write(evhdl, &u, sizeof(u));
2720
  if (s != sizeof(u)) {
2721
    /* error */
2722
    return 0;
2723
  }
2724
  return 1;
2725
}
2726
2727
2728
static void
2729
event_destroy(void *eventhdl)
2730
{
2731
  int evhdl;
2732
2733
  if (!eventhdl) {
2734
    /* error */
2735
    return;
2736
  }
2737
  evhdl = *(int *)eventhdl;
2738
2739
  close(evhdl);
2740
  mg_free(eventhdl);
2741
}
2742
2743
2744
#endif
2745
2746
#endif
2747
2748
2749
#if !defined(__linux__) && !defined(_WIN32) && defined(ALTERNATIVE_QUEUE)
2750
2751
struct posix_event {
2752
  pthread_mutex_t mutex;
2753
  pthread_cond_t cond;
2754
  int signaled;
2755
};
2756
2757
2758
static void *
2759
event_create(void)
2760
{
2761
  struct posix_event *ret = mg_malloc(sizeof(struct posix_event));
2762
  if (ret == 0) {
2763
    /* out of memory */
2764
    return 0;
2765
  }
2766
  if (0 != pthread_mutex_init(&(ret->mutex), NULL)) {
2767
    /* pthread mutex not available */
2768
    mg_free(ret);
2769
    return 0;
2770
  }
2771
  if (0 != pthread_cond_init(&(ret->cond), NULL)) {
2772
    /* pthread cond not available */
2773
    pthread_mutex_destroy(&(ret->mutex));
2774
    mg_free(ret);
2775
    return 0;
2776
  }
2777
  ret->signaled = 0;
2778
  return (void *)ret;
2779
}
2780
2781
2782
static int
2783
event_wait(void *eventhdl)
2784
{
2785
  struct posix_event *ev = (struct posix_event *)eventhdl;
2786
  pthread_mutex_lock(&(ev->mutex));
2787
  while (!ev->signaled) {
2788
    pthread_cond_wait(&(ev->cond), &(ev->mutex));
2789
  }
2790
  ev->signaled = 0;
2791
  pthread_mutex_unlock(&(ev->mutex));
2792
  return 1;
2793
}
2794
2795
2796
static int
2797
event_signal(void *eventhdl)
2798
{
2799
  struct posix_event *ev = (struct posix_event *)eventhdl;
2800
  pthread_mutex_lock(&(ev->mutex));
2801
  pthread_cond_signal(&(ev->cond));
2802
  ev->signaled = 1;
2803
  pthread_mutex_unlock(&(ev->mutex));
2804
  return 1;
2805
}
2806
2807
2808
static void
2809
event_destroy(void *eventhdl)
2810
{
2811
  struct posix_event *ev = (struct posix_event *)eventhdl;
2812
  pthread_cond_destroy(&(ev->cond));
2813
  pthread_mutex_destroy(&(ev->mutex));
2814
  mg_free(ev);
2815
}
2816
#endif
2817
2818
2819
static void
2820
mg_set_thread_name(const char *name)
2821
2
{
2822
2
  char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */
2823
2824
2
  mg_snprintf(
2825
2
      NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name);
2826
2827
#if defined(_WIN32)
2828
#if defined(_MSC_VER)
2829
  /* Windows and Visual Studio Compiler */
2830
  __try {
2831
    THREADNAME_INFO info;
2832
    info.dwType = 0x1000;
2833
    info.szName = threadName;
2834
    info.dwThreadID = ~0U;
2835
    info.dwFlags = 0;
2836
2837
    RaiseException(0x406D1388,
2838
                   0,
2839
                   sizeof(info) / sizeof(ULONG_PTR),
2840
                   (ULONG_PTR *)&info);
2841
  } __except (EXCEPTION_EXECUTE_HANDLER) {
2842
  }
2843
#elif defined(__MINGW32__)
2844
  /* No option known to set thread name for MinGW known */
2845
#endif
2846
#elif defined(_GNU_SOURCE) && defined(__GLIBC__)                               \
2847
    && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12)))
2848
  /* pthread_setname_np first appeared in glibc in version 2.12 */
2849
#if defined(__MACH__) && defined(__APPLE__)
2850
  /* OS X only current thread name can be changed */
2851
  (void)pthread_setname_np(threadName);
2852
#else
2853
2
  (void)pthread_setname_np(pthread_self(), threadName);
2854
2
#endif
2855
#elif defined(__linux__)
2856
  /* On Linux we can use the prctl function.
2857
   * When building for Linux Standard Base (LSB) use
2858
   * NO_THREAD_NAME. However, thread names are a big
2859
   * help for debugging, so the stadard is to set them.
2860
   */
2861
  (void)prctl(PR_SET_NAME, threadName, 0, 0, 0);
2862
#endif
2863
2
}
2864
#else /* !defined(NO_THREAD_NAME) */
2865
static void
2866
mg_set_thread_name(const char *threadName)
2867
{
2868
}
2869
#endif
2870
2871
2872
CIVETWEB_API const struct mg_option *
2873
mg_get_valid_options(void)
2874
0
{
2875
0
  return config_options;
2876
0
}
2877
2878
2879
/* Do not open file (unused) */
2880
#define MG_FOPEN_MODE_NONE (0)
2881
2882
/* Open file for read only access */
2883
0
#define MG_FOPEN_MODE_READ (1)
2884
2885
/* Open file for writing, create and overwrite */
2886
0
#define MG_FOPEN_MODE_WRITE (2)
2887
2888
/* Open file for writing, create and append */
2889
0
#define MG_FOPEN_MODE_APPEND (4)
2890
2891
2892
static int
2893
is_file_opened(const struct mg_file_access *fileacc)
2894
0
{
2895
0
  if (!fileacc) {
2896
0
    return 0;
2897
0
  }
2898
2899
0
  return (fileacc->fp != NULL);
2900
0
}
2901
2902
2903
#if !defined(NO_FILESYSTEMS)
2904
static int mg_stat(const struct mg_connection *conn,
2905
                   const char *path,
2906
                   struct mg_file_stat *filep);
2907
2908
2909
/* Reject files with special characters (for Windows) */
2910
static int
2911
mg_path_suspicious(const struct mg_connection *conn, const char *path)
2912
0
{
2913
0
  const uint8_t *c = (const uint8_t *)path;
2914
0
  (void)conn; /* not used */
2915
2916
0
  if ((c == NULL) || (c[0] == 0)) {
2917
    /* Null pointer or empty path --> suspicious */
2918
0
    return 1;
2919
0
  }
2920
2921
#if defined(_WIN32)
2922
  while (*c) {
2923
    if (*c < 32) {
2924
      /* Control character */
2925
      return 1;
2926
    }
2927
    if ((*c == '>') || (*c == '<') || (*c == '|')) {
2928
      /* stdin/stdout redirection character */
2929
      return 1;
2930
    }
2931
    if ((*c == '*') || (*c == '?')) {
2932
      /* Wildcard character */
2933
      return 1;
2934
    }
2935
    if (*c == '"') {
2936
      /* Windows quotation */
2937
      return 1;
2938
    }
2939
    c++;
2940
  }
2941
#endif
2942
2943
  /* Nothing suspicious found */
2944
0
  return 0;
2945
0
}
2946
2947
2948
/* mg_fopen will open a file either in memory or on the disk.
2949
 * The input parameter path is a string in UTF-8 encoding.
2950
 * The input parameter mode is MG_FOPEN_MODE_*
2951
 * On success, fp will be set in the output struct mg_file.
2952
 * All status members will also be set.
2953
 * The function returns 1 on success, 0 on error. */
2954
static int
2955
mg_fopen(const struct mg_connection *conn,
2956
         const char *path,
2957
         int mode,
2958
         struct mg_file *filep)
2959
0
{
2960
0
  int found;
2961
2962
0
  if (!filep) {
2963
0
    return 0;
2964
0
  }
2965
0
  filep->access.fp = NULL;
2966
2967
0
  if (mg_path_suspicious(conn, path)) {
2968
0
    return 0;
2969
0
  }
2970
2971
  /* filep is initialized in mg_stat: all fields with memset to,
2972
   * some fields like size and modification date with values */
2973
0
  found = mg_stat(conn, path, &(filep->stat));
2974
2975
0
  if ((mode == MG_FOPEN_MODE_READ) && (!found)) {
2976
    /* file does not exist and will not be created */
2977
0
    return 0;
2978
0
  }
2979
2980
#if defined(_WIN32)
2981
  {
2982
    wchar_t wbuf[UTF16_PATH_MAX];
2983
    path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
2984
    switch (mode) {
2985
    case MG_FOPEN_MODE_READ:
2986
      filep->access.fp = _wfopen(wbuf, L"rb");
2987
      break;
2988
    case MG_FOPEN_MODE_WRITE:
2989
      filep->access.fp = _wfopen(wbuf, L"wb");
2990
      break;
2991
    case MG_FOPEN_MODE_APPEND:
2992
      filep->access.fp = _wfopen(wbuf, L"ab");
2993
      break;
2994
    }
2995
  }
2996
#else
2997
  /* Linux et al already use unicode. No need to convert. */
2998
0
  switch (mode) {
2999
0
  case MG_FOPEN_MODE_READ:
3000
0
    filep->access.fp = fopen(path, "r");
3001
0
    break;
3002
0
  case MG_FOPEN_MODE_WRITE:
3003
0
    filep->access.fp = fopen(path, "w");
3004
0
    break;
3005
0
  case MG_FOPEN_MODE_APPEND:
3006
0
    filep->access.fp = fopen(path, "a");
3007
0
    break;
3008
0
  }
3009
3010
0
#endif
3011
0
  if (!found) {
3012
    /* File did not exist before fopen was called.
3013
     * Maybe it has been created now. Get stat info
3014
     * like creation time now. */
3015
0
    found = mg_stat(conn, path, &(filep->stat));
3016
0
    (void)found;
3017
0
  }
3018
3019
  /* return OK if file is opened */
3020
0
  return (filep->access.fp != NULL);
3021
0
}
3022
3023
3024
/* return 0 on success, just like fclose */
3025
static int
3026
mg_fclose(struct mg_file_access *fileacc)
3027
0
{
3028
0
  int ret = -1;
3029
0
  if (fileacc != NULL) {
3030
0
    if (fileacc->fp != NULL) {
3031
0
      ret = fclose(fileacc->fp);
3032
0
    }
3033
    /* reset all members of fileacc */
3034
0
    memset(fileacc, 0, sizeof(*fileacc));
3035
0
  }
3036
0
  return ret;
3037
0
}
3038
#endif /* NO_FILESYSTEMS */
3039
3040
3041
static void
3042
mg_strlcpy(char *dst, const char *src, size_t n)
3043
74
{
3044
482
  for (; *src != '\0' && n > 1; n--) {
3045
408
    *dst++ = *src++;
3046
408
  }
3047
74
  *dst = '\0';
3048
74
}
3049
3050
3051
static int
3052
lowercase(const char *s)
3053
0
{
3054
0
  return tolower((unsigned char)*s);
3055
0
}
3056
3057
3058
CIVETWEB_API int
3059
mg_strncasecmp(const char *s1, const char *s2, size_t len)
3060
0
{
3061
0
  int diff = 0;
3062
3063
0
  if (len > 0) {
3064
0
    do {
3065
0
      diff = lowercase(s1++) - lowercase(s2++);
3066
0
    } while (diff == 0 && s1[-1] != '\0' && --len > 0);
3067
0
  }
3068
3069
0
  return diff;
3070
0
}
3071
3072
3073
CIVETWEB_API int
3074
mg_strcasecmp(const char *s1, const char *s2)
3075
0
{
3076
0
  int diff;
3077
3078
0
  do {
3079
0
    diff = lowercase(s1++) - lowercase(s2++);
3080
0
  } while (diff == 0 && s1[-1] != '\0');
3081
3082
0
  return diff;
3083
0
}
3084
3085
3086
static char *
3087
mg_strndup_ctx(const char *ptr, size_t len, struct mg_context *ctx)
3088
74
{
3089
74
  char *p;
3090
74
  (void)ctx; /* Avoid Visual Studio warning if USE_SERVER_STATS is not
3091
              * defined */
3092
3093
74
  if ((p = (char *)mg_malloc_ctx(len + 1, ctx)) != NULL) {
3094
74
    mg_strlcpy(p, ptr, len + 1);
3095
74
  }
3096
3097
74
  return p;
3098
74
}
3099
3100
3101
static char *
3102
mg_strdup_ctx(const char *str, struct mg_context *ctx)
3103
72
{
3104
72
  return mg_strndup_ctx(str, strlen(str), ctx);
3105
72
}
3106
3107
static char *
3108
mg_strdup(const char *str)
3109
2
{
3110
2
  return mg_strndup_ctx(str, strlen(str), NULL);
3111
2
}
3112
3113
3114
static const char *
3115
mg_strcasestr(const char *big_str, const char *small_str)
3116
0
{
3117
0
  size_t i, big_len = strlen(big_str), small_len = strlen(small_str);
3118
3119
0
  if (big_len >= small_len) {
3120
0
    for (i = 0; i <= (big_len - small_len); i++) {
3121
0
      if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) {
3122
0
        return big_str + i;
3123
0
      }
3124
0
    }
3125
0
  }
3126
3127
0
  return NULL;
3128
0
}
3129
3130
3131
/* Return null terminated string of given maximum length.
3132
 * Report errors if length is exceeded. */
3133
static void
3134
mg_vsnprintf(const struct mg_connection *conn,
3135
             int *truncated,
3136
             char *buf,
3137
             size_t buflen,
3138
             const char *fmt,
3139
             va_list ap)
3140
70
{
3141
70
  int n, ok;
3142
3143
70
  if (buflen == 0) {
3144
0
    if (truncated) {
3145
0
      *truncated = 1;
3146
0
    }
3147
0
    return;
3148
0
  }
3149
3150
70
#if defined(__clang__)
3151
70
#pragma clang diagnostic push
3152
70
#pragma clang diagnostic ignored "-Wformat-nonliteral"
3153
  /* Using fmt as a non-literal is intended here, since it is mostly called
3154
   * indirectly by mg_snprintf */
3155
70
#endif
3156
3157
70
  n = (int)vsnprintf_impl(buf, buflen, fmt, ap);
3158
70
  ok = (n >= 0) && ((size_t)n < buflen);
3159
3160
70
#if defined(__clang__)
3161
70
#pragma clang diagnostic pop
3162
70
#endif
3163
3164
70
  if (ok) {
3165
70
    if (truncated) {
3166
0
      *truncated = 0;
3167
0
    }
3168
70
  } else {
3169
0
    if (truncated) {
3170
0
      *truncated = 1;
3171
0
    }
3172
0
    mg_cry_internal(conn,
3173
0
                    "truncating vsnprintf buffer: [%.*s]",
3174
0
                    (int)((buflen > 200) ? 200 : (buflen - 1)),
3175
0
                    buf);
3176
0
    n = (int)buflen - 1;
3177
0
  }
3178
70
  buf[n] = '\0';
3179
70
}
3180
3181
3182
static void
3183
mg_snprintf(const struct mg_connection *conn,
3184
            int *truncated,
3185
            char *buf,
3186
            size_t buflen,
3187
            const char *fmt,
3188
            ...)
3189
70
{
3190
70
  va_list ap;
3191
3192
70
  va_start(ap, fmt);
3193
70
  mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap);
3194
70
  va_end(ap);
3195
70
}
3196
3197
3198
static int
3199
get_option_index(const char *name)
3200
4
{
3201
4
  int i;
3202
3203
36
  for (i = 0; config_options[i].name != NULL; i++) {
3204
36
    if (strcmp(config_options[i].name, name) == 0) {
3205
4
      return i;
3206
4
    }
3207
36
  }
3208
0
  return -1;
3209
4
}
3210
3211
3212
CIVETWEB_API const char *
3213
mg_get_option(const struct mg_context *ctx, const char *name)
3214
0
{
3215
0
  int i;
3216
0
  if ((i = get_option_index(name)) == -1) {
3217
0
    return NULL;
3218
0
  } else if (!ctx || ctx->dd.config[i] == NULL) {
3219
0
    return "";
3220
0
  } else {
3221
0
    return ctx->dd.config[i];
3222
0
  }
3223
0
}
3224
3225
#define mg_get_option DO_NOT_USE_THIS_FUNCTION_INTERNALLY__access_directly
3226
3227
CIVETWEB_API struct mg_context *
3228
mg_get_context(const struct mg_connection *conn)
3229
0
{
3230
0
  return (conn == NULL) ? (struct mg_context *)NULL : (conn->phys_ctx);
3231
0
}
3232
3233
3234
CIVETWEB_API void *
3235
mg_get_user_data(const struct mg_context *ctx)
3236
0
{
3237
0
  return (ctx == NULL) ? NULL : ctx->user_data;
3238
0
}
3239
3240
3241
CIVETWEB_API void *
3242
mg_get_user_context_data(const struct mg_connection *conn)
3243
0
{
3244
0
  return mg_get_user_data(mg_get_context(conn));
3245
0
}
3246
3247
3248
CIVETWEB_API void *
3249
mg_get_thread_pointer(const struct mg_connection *conn)
3250
0
{
3251
  /* both methods should return the same pointer */
3252
0
  if (conn) {
3253
    /* quick access, in case conn is known */
3254
0
    return conn->tls_user_ptr;
3255
0
  } else {
3256
    /* otherwise get pointer from thread local storage (TLS) */
3257
0
    struct mg_workerTLS *tls =
3258
0
        (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
3259
0
    return tls->user_ptr;
3260
0
  }
3261
0
}
3262
3263
3264
CIVETWEB_API void
3265
mg_set_user_connection_data(const struct mg_connection *const_conn, void *data)
3266
34
{
3267
34
  if (const_conn != NULL) {
3268
    /* Const cast, since "const struct mg_connection *" does not mean
3269
     * the connection object is not modified. Here "const" is used,
3270
     * to indicate mg_read/mg_write/mg_send/.. must not be called. */
3271
34
    struct mg_connection *conn = (struct mg_connection *)const_conn;
3272
34
    conn->request_info.conn_data = data;
3273
34
  }
3274
34
}
3275
3276
3277
CIVETWEB_API void *
3278
mg_get_user_connection_data(const struct mg_connection *conn)
3279
0
{
3280
0
  if (conn != NULL) {
3281
0
    return conn->request_info.conn_data;
3282
0
  }
3283
0
  return NULL;
3284
0
}
3285
3286
3287
CIVETWEB_API int
3288
mg_get_server_ports(const struct mg_context *ctx,
3289
                    int size,
3290
                    struct mg_server_port *ports)
3291
2
{
3292
2
  int i, cnt = 0;
3293
3294
2
  if (size <= 0) {
3295
0
    return -1;
3296
0
  }
3297
2
  memset(ports, 0, sizeof(*ports) * (size_t)size);
3298
2
  if (!ctx) {
3299
0
    return -1;
3300
0
  }
3301
2
  if (!ctx->listening_sockets) {
3302
0
    return -1;
3303
0
  }
3304
3305
4
  for (i = 0; (i < size) && (i < (int)ctx->num_listening_sockets); i++) {
3306
3307
2
    ports[cnt].port =
3308
2
        ntohs(USA_IN_PORT_UNSAFE(&(ctx->listening_sockets[i].lsa)));
3309
2
    ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
3310
2
    ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
3311
3312
2
    if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
3313
      /* IPv4 */
3314
2
      ports[cnt].protocol = 1;
3315
2
      cnt++;
3316
2
    } else if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6) {
3317
      /* IPv6 */
3318
0
      ports[cnt].protocol = 3;
3319
0
      cnt++;
3320
0
    }
3321
2
  }
3322
3323
2
  return cnt;
3324
2
}
3325
3326
3327
#if defined(USE_X_DOM_SOCKET) && !defined(UNIX_DOMAIN_SOCKET_SERVER_NAME)
3328
#define UNIX_DOMAIN_SOCKET_SERVER_NAME "*"
3329
#endif
3330
3331
static void
3332
sockaddr_to_string(char *buf, size_t len, const union usa *usa)
3333
0
{
3334
0
  buf[0] = '\0';
3335
3336
0
  if (!usa) {
3337
0
    return;
3338
0
  }
3339
3340
0
  if (usa->sa.sa_family == AF_INET) {
3341
0
    getnameinfo(&usa->sa,
3342
0
                sizeof(usa->sin),
3343
0
                buf,
3344
0
                (unsigned)len,
3345
0
                NULL,
3346
0
                0,
3347
0
                NI_NUMERICHOST);
3348
0
  }
3349
#if defined(USE_IPV6)
3350
  else if (usa->sa.sa_family == AF_INET6) {
3351
    getnameinfo(&usa->sa,
3352
                sizeof(usa->sin6),
3353
                buf,
3354
                (unsigned)len,
3355
                NULL,
3356
                0,
3357
                NI_NUMERICHOST);
3358
  }
3359
#endif
3360
#if defined(USE_X_DOM_SOCKET)
3361
  else if (usa->sa.sa_family == AF_UNIX) {
3362
    /* TODO: Define a remote address for unix domain sockets.
3363
    * This code will always return "localhost", identical to http+tcp:
3364
    getnameinfo(&usa->sa,
3365
    sizeof(usa->sun),
3366
    buf,
3367
    (unsigned)len,
3368
    NULL,
3369
    0,
3370
    NI_NUMERICHOST);
3371
    */
3372
    mg_strlcpy(buf, UNIX_DOMAIN_SOCKET_SERVER_NAME, len);
3373
  }
3374
#endif
3375
0
}
3376
3377
3378
/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be
3379
 * included in all responses other than 100, 101, 5xx. */
3380
static void
3381
gmt_time_string(char *buf, size_t buf_len, time_t *t)
3382
0
{
3383
0
#if !defined(REENTRANT_TIME)
3384
0
  struct tm *tm;
3385
3386
0
  tm = ((t != NULL) ? gmtime(t) : NULL);
3387
0
  if (tm != NULL) {
3388
#else
3389
  struct tm _tm;
3390
  struct tm *tm = &_tm;
3391
3392
  if (t != NULL) {
3393
    gmtime_r(t, tm);
3394
#endif
3395
0
    strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm);
3396
0
  } else {
3397
0
    mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
3398
0
  }
3399
0
}
3400
3401
3402
/* difftime for struct timespec. Return value is in seconds. */
3403
static double
3404
mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before)
3405
0
{
3406
0
  return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9
3407
0
         + (double)(ts_now->tv_sec - ts_before->tv_sec);
3408
0
}
3409
3410
3411
#if defined(MG_EXTERNAL_FUNCTION_mg_cry_internal_impl)
3412
static void mg_cry_internal_impl(const struct mg_connection *conn,
3413
                                 const char *func,
3414
                                 unsigned line,
3415
                                 const char *fmt,
3416
                                 va_list ap);
3417
#include "external_mg_cry_internal_impl.inl"
3418
#elif !defined(NO_FILESYSTEMS)
3419
3420
/* Print error message to the opened error log stream. */
3421
static void
3422
mg_cry_internal_impl(const struct mg_connection *conn,
3423
                     const char *func,
3424
                     unsigned line,
3425
                     const char *fmt,
3426
                     va_list ap)
3427
0
{
3428
0
  char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
3429
0
  struct mg_file fi;
3430
0
  time_t timestamp;
3431
3432
  /* Unused, in the RELEASE build */
3433
0
  (void)func;
3434
0
  (void)line;
3435
3436
#if defined(GCC_DIAGNOSTIC)
3437
#pragma GCC diagnostic push
3438
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
3439
#endif
3440
3441
0
  IGNORE_UNUSED_RESULT(vsnprintf_impl(buf, sizeof(buf), fmt, ap));
3442
3443
#if defined(GCC_DIAGNOSTIC)
3444
#pragma GCC diagnostic pop
3445
#endif
3446
3447
0
  buf[sizeof(buf) - 1] = 0;
3448
3449
0
  DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf);
3450
3451
0
  if (!conn) {
3452
0
    puts(buf);
3453
0
    return;
3454
0
  }
3455
3456
  /* Do not lock when getting the callback value, here and below.
3457
   * I suppose this is fine, since function cannot disappear in the
3458
   * same way string option can. */
3459
0
  if ((conn->phys_ctx->callbacks.log_message == NULL)
3460
0
      || (conn->phys_ctx->callbacks.log_message(conn, buf) == 0)) {
3461
3462
0
    if (conn->dom_ctx->config[ERROR_LOG_FILE] != NULL) {
3463
0
      if (mg_fopen(conn,
3464
0
                   conn->dom_ctx->config[ERROR_LOG_FILE],
3465
0
                   MG_FOPEN_MODE_APPEND,
3466
0
                   &fi)
3467
0
          == 0) {
3468
0
        fi.access.fp = NULL;
3469
0
      }
3470
0
    } else {
3471
0
      fi.access.fp = NULL;
3472
0
    }
3473
3474
0
    if (fi.access.fp != NULL) {
3475
0
      flockfile(fi.access.fp);
3476
0
      timestamp = time(NULL);
3477
3478
0
      sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
3479
0
      fprintf(fi.access.fp,
3480
0
              "[%010lu] [error] [client %s] ",
3481
0
              (unsigned long)timestamp,
3482
0
              src_addr);
3483
3484
0
      if (conn->request_info.request_method != NULL) {
3485
0
        fprintf(fi.access.fp,
3486
0
                "%s %s: ",
3487
0
                conn->request_info.request_method,
3488
0
                conn->request_info.request_uri
3489
0
                    ? conn->request_info.request_uri
3490
0
                    : "");
3491
0
      }
3492
3493
0
      fprintf(fi.access.fp, "%s", buf);
3494
0
      fputc('\n', fi.access.fp);
3495
0
      fflush(fi.access.fp);
3496
0
      funlockfile(fi.access.fp);
3497
0
      (void)mg_fclose(&fi.access); /* Ignore errors. We can't call
3498
                                    * mg_cry here anyway ;-) */
3499
0
    }
3500
0
  }
3501
0
}
3502
#else
3503
#error Must either enable filesystems or provide a custom mg_cry_internal_impl implementation
3504
#endif /* Externally provided function */
3505
3506
3507
/* Construct fake connection structure. Used for logging, if connection
3508
 * is not applicable at the moment of logging. */
3509
static struct mg_connection *
3510
fake_connection(struct mg_connection *fc, struct mg_context *ctx)
3511
0
{
3512
0
  static const struct mg_connection conn_zero = {0};
3513
0
  *fc = conn_zero;
3514
0
  fc->phys_ctx = ctx;
3515
0
  fc->dom_ctx = &(ctx->dd);
3516
0
  return fc;
3517
0
}
3518
3519
3520
static void
3521
mg_cry_internal_wrap(const struct mg_connection *conn,
3522
                     struct mg_context *ctx,
3523
                     const char *func,
3524
                     unsigned line,
3525
                     const char *fmt,
3526
                     ...)
3527
0
{
3528
0
  va_list ap;
3529
0
  va_start(ap, fmt);
3530
0
  if (!conn && ctx) {
3531
0
    struct mg_connection fc;
3532
0
    mg_cry_internal_impl(fake_connection(&fc, ctx), func, line, fmt, ap);
3533
0
  } else {
3534
0
    mg_cry_internal_impl(conn, func, line, fmt, ap);
3535
0
  }
3536
0
  va_end(ap);
3537
0
}
3538
3539
3540
CIVETWEB_API void
3541
mg_cry(const struct mg_connection *conn, const char *fmt, ...)
3542
0
{
3543
0
  va_list ap;
3544
0
  va_start(ap, fmt);
3545
0
  mg_cry_internal_impl(conn, "user", 0, fmt, ap);
3546
0
  va_end(ap);
3547
0
}
3548
3549
3550
#define mg_cry DO_NOT_USE_THIS_FUNCTION__USE_mg_cry_internal
3551
3552
3553
CIVETWEB_API const char *
3554
mg_version(void)
3555
0
{
3556
0
  return CIVETWEB_VERSION;
3557
0
}
3558
3559
3560
CIVETWEB_API const struct mg_request_info *
3561
mg_get_request_info(const struct mg_connection *conn)
3562
0
{
3563
0
  if (!conn) {
3564
0
    return NULL;
3565
0
  }
3566
#if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE)
3567
  if (conn->connection_type == CONNECTION_TYPE_RESPONSE) {
3568
    char txt[16];
3569
    struct mg_workerTLS *tls =
3570
        (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
3571
3572
    sprintf(txt, "%03i", conn->response_info.status_code);
3573
    if (strlen(txt) == 3) {
3574
      memcpy(tls->txtbuf, txt, 4);
3575
    } else {
3576
      strcpy(tls->txtbuf, "ERR");
3577
    }
3578
3579
    ((struct mg_connection *)conn)->request_info.local_uri =
3580
        tls->txtbuf; /* use thread safe buffer */
3581
    ((struct mg_connection *)conn)->request_info.local_uri_raw =
3582
        tls->txtbuf; /* use the same thread safe buffer */
3583
    ((struct mg_connection *)conn)->request_info.request_uri =
3584
        tls->txtbuf; /* use  the same thread safe buffer */
3585
3586
    ((struct mg_connection *)conn)->request_info.num_headers =
3587
        conn->response_info.num_headers;
3588
    memcpy(((struct mg_connection *)conn)->request_info.http_headers,
3589
           conn->response_info.http_headers,
3590
           sizeof(conn->response_info.http_headers));
3591
  } else
3592
#endif
3593
0
      if (conn->connection_type != CONNECTION_TYPE_REQUEST) {
3594
0
    return NULL;
3595
0
  }
3596
0
  return &conn->request_info;
3597
0
}
3598
3599
3600
CIVETWEB_API const struct mg_response_info *
3601
mg_get_response_info(const struct mg_connection *conn)
3602
34
{
3603
34
  if (!conn) {
3604
0
    return NULL;
3605
0
  }
3606
34
  if (conn->connection_type != CONNECTION_TYPE_RESPONSE) {
3607
0
    return NULL;
3608
0
  }
3609
34
  return &conn->response_info;
3610
34
}
3611
3612
3613
static const char *
3614
get_proto_name(const struct mg_connection *conn)
3615
0
{
3616
0
#if defined(__clang__)
3617
0
#pragma clang diagnostic push
3618
0
#pragma clang diagnostic ignored "-Wunreachable-code"
3619
  /* Depending on USE_WEBSOCKET and NO_SSL, some oft the protocols might be
3620
   * not supported. Clang raises an "unreachable code" warning for parts of ?:
3621
   * unreachable, but splitting into four different #ifdef clauses here is
3622
   * more complicated.
3623
   */
3624
0
#endif
3625
3626
0
  const struct mg_request_info *ri = &conn->request_info;
3627
3628
0
  const char *proto = ((conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)
3629
0
                           ? (ri->is_ssl ? "wss" : "ws")
3630
0
                           : (ri->is_ssl ? "https" : "http"));
3631
3632
0
  return proto;
3633
3634
0
#if defined(__clang__)
3635
0
#pragma clang diagnostic pop
3636
0
#endif
3637
0
}
3638
3639
3640
static int
3641
mg_construct_local_link(const struct mg_connection *conn,
3642
                        char *buf,
3643
                        size_t buflen,
3644
                        const char *define_proto,
3645
                        int define_port,
3646
                        const char *define_uri)
3647
0
{
3648
0
  if ((buflen < 1) || (buf == 0) || (conn == 0)) {
3649
0
    return -1;
3650
0
  } else {
3651
0
    int i, j;
3652
0
    int truncated = 0;
3653
0
    const struct mg_request_info *ri = &conn->request_info;
3654
3655
0
    const char *proto =
3656
0
        (define_proto != NULL) ? define_proto : get_proto_name(conn);
3657
0
    const char *uri =
3658
0
        (define_uri != NULL)
3659
0
            ? define_uri
3660
0
            : ((ri->request_uri != NULL) ? ri->request_uri : ri->local_uri);
3661
0
    int port = (define_port > 0) ? define_port : ri->server_port;
3662
0
    int default_port = 80;
3663
0
    char *uri_encoded;
3664
0
    size_t uri_encoded_len;
3665
3666
0
    if (uri == NULL) {
3667
0
      return -1;
3668
0
    }
3669
3670
0
    uri_encoded_len = strlen(uri) * 3 + 1;
3671
0
    uri_encoded = (char *)mg_malloc_ctx(uri_encoded_len, conn->phys_ctx);
3672
0
    if (uri_encoded == NULL) {
3673
0
      return -1;
3674
0
    }
3675
0
    mg_url_encode(uri, uri_encoded, uri_encoded_len);
3676
3677
    /* Directory separator should be preserved. */
3678
0
    for (i = j = 0; uri_encoded[i]; j++) {
3679
0
      if (!strncmp(uri_encoded + i, "%2f", 3)) {
3680
0
        uri_encoded[j] = '/';
3681
0
        i += 3;
3682
0
      } else {
3683
0
        uri_encoded[j] = uri_encoded[i++];
3684
0
      }
3685
0
    }
3686
0
    uri_encoded[j] = '\0';
3687
3688
#if defined(USE_X_DOM_SOCKET)
3689
    if (conn->client.lsa.sa.sa_family == AF_UNIX) {
3690
      /* TODO: Define and document a link for UNIX domain sockets. */
3691
      /* There seems to be no official standard for this.
3692
       * Common uses seem to be "httpunix://", "http.unix://" or
3693
       * "http+unix://" as a protocol definition string, followed by
3694
       * "localhost" or "127.0.0.1" or "/tmp/unix/path" or
3695
       * "%2Ftmp%2Funix%2Fpath" (url % encoded) or
3696
       * "localhost:%2Ftmp%2Funix%2Fpath" (domain socket path as port) or
3697
       * "" (completely skipping the server name part). In any case, the
3698
       * last part is the server local path. */
3699
      const char *server_name = UNIX_DOMAIN_SOCKET_SERVER_NAME;
3700
      mg_snprintf(conn,
3701
                  &truncated,
3702
                  buf,
3703
                  buflen,
3704
                  "%s.unix://%s%s",
3705
                  proto,
3706
                  server_name,
3707
                  ri->local_uri);
3708
      default_port = 0;
3709
      mg_free(uri_encoded);
3710
      return 0;
3711
    }
3712
#endif
3713
3714
0
    if (define_proto) {
3715
      /* If we got a protocol name, use the default port accordingly. */
3716
0
      if ((0 == strcmp(define_proto, "https"))
3717
0
          || (0 == strcmp(define_proto, "wss"))) {
3718
0
        default_port = 443;
3719
0
      }
3720
0
    } else if (ri->is_ssl) {
3721
      /* If we did not get a protocol name, use TLS as default if it is
3722
       * already used. */
3723
0
      default_port = 443;
3724
0
    }
3725
3726
0
    {
3727
#if defined(USE_IPV6)
3728
      int is_ipv6 = (conn->client.lsa.sa.sa_family == AF_INET6);
3729
#endif
3730
0
      int auth_domain_check_enabled =
3731
0
          conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK]
3732
0
          && (!mg_strcasecmp(
3733
0
                 conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK], "yes"));
3734
3735
0
      const char *server_domain =
3736
0
          conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
3737
3738
0
      char portstr[16];
3739
0
      char server_ip[48];
3740
3741
0
      if (port != default_port) {
3742
0
        sprintf(portstr, ":%u", (unsigned)port);
3743
0
      } else {
3744
0
        portstr[0] = 0;
3745
0
      }
3746
3747
0
      if (!auth_domain_check_enabled || !server_domain) {
3748
3749
0
        sockaddr_to_string(server_ip,
3750
0
                           sizeof(server_ip),
3751
0
                           &conn->client.lsa);
3752
3753
0
        server_domain = server_ip;
3754
0
      }
3755
3756
0
      mg_snprintf(conn,
3757
0
                  &truncated,
3758
0
                  buf,
3759
0
                  buflen,
3760
#if defined(USE_IPV6)
3761
                  "%s://%s%s%s%s%s",
3762
                  proto,
3763
                  (is_ipv6 && (server_domain == server_ip)) ? "[" : "",
3764
                  server_domain,
3765
                  (is_ipv6 && (server_domain == server_ip)) ? "]" : "",
3766
#else
3767
0
                  "%s://%s%s%s",
3768
0
                  proto,
3769
0
                  server_domain,
3770
0
#endif
3771
0
                  portstr,
3772
0
                  uri_encoded);
3773
3774
0
      mg_free(uri_encoded);
3775
0
      if (truncated) {
3776
0
        return -1;
3777
0
      }
3778
0
      return 0;
3779
0
    }
3780
0
  }
3781
0
}
3782
3783
3784
CIVETWEB_API int
3785
mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen)
3786
0
{
3787
0
  return mg_construct_local_link(conn, buf, buflen, NULL, -1, NULL);
3788
0
}
3789
3790
3791
/* Skip the characters until one of the delimiters characters found.
3792
 * 0-terminate resulting word. Skip the delimiter and following whitespaces.
3793
 * Advance pointer to buffer to the next word. Return found 0-terminated
3794
 * word.
3795
 * Delimiters can be quoted with quotechar. */
3796
static char *
3797
skip_quoted(char **buf,
3798
            const char *delimiters,
3799
            const char *whitespace,
3800
            char quotechar)
3801
0
{
3802
0
  char *p, *begin_word, *end_word, *end_whitespace;
3803
3804
0
  begin_word = *buf;
3805
0
  end_word = begin_word + strcspn(begin_word, delimiters);
3806
3807
  /* Check for quotechar */
3808
0
  if (end_word > begin_word) {
3809
0
    p = end_word - 1;
3810
0
    while (*p == quotechar) {
3811
      /* While the delimiter is quoted, look for the next delimiter. */
3812
      /* This happens, e.g., in calls from parse_auth_header,
3813
       * if the user name contains a " character. */
3814
3815
      /* If there is anything beyond end_word, copy it. */
3816
0
      if (*end_word != '\0') {
3817
0
        size_t end_off = strcspn(end_word + 1, delimiters);
3818
0
        memmove(p, end_word, end_off + 1);
3819
0
        p += end_off; /* p must correspond to end_word - 1 */
3820
0
        end_word += end_off + 1;
3821
0
      } else {
3822
0
        *p = '\0';
3823
0
        break;
3824
0
      }
3825
0
    }
3826
0
    for (p++; p < end_word; p++) {
3827
0
      *p = '\0';
3828
0
    }
3829
0
  }
3830
3831
0
  if (*end_word == '\0') {
3832
0
    *buf = end_word;
3833
0
  } else {
3834
3835
#if defined(GCC_DIAGNOSTIC)
3836
    /* Disable spurious conversion warning for GCC */
3837
#pragma GCC diagnostic push
3838
#pragma GCC diagnostic ignored "-Wsign-conversion"
3839
#endif /* defined(GCC_DIAGNOSTIC) */
3840
3841
0
    end_whitespace = end_word + strspn(&end_word[1], whitespace) + 1;
3842
3843
#if defined(GCC_DIAGNOSTIC)
3844
#pragma GCC diagnostic pop
3845
#endif /* defined(GCC_DIAGNOSTIC) */
3846
3847
0
    for (p = end_word; p < end_whitespace; p++) {
3848
0
      *p = '\0';
3849
0
    }
3850
3851
0
    *buf = end_whitespace;
3852
0
  }
3853
3854
0
  return begin_word;
3855
0
}
3856
3857
3858
/* Return HTTP header value, or NULL if not found. */
3859
static const char *
3860
get_header(const struct mg_header *hdr, int num_hdr, const char *name)
3861
0
{
3862
0
  int i;
3863
0
  for (i = 0; i < num_hdr; i++) {
3864
0
    if (!mg_strcasecmp(name, hdr[i].name)) {
3865
0
      return hdr[i].value;
3866
0
    }
3867
0
  }
3868
3869
0
  return NULL;
3870
0
}
3871
3872
3873
/* Retrieve requested HTTP header multiple values, and return the number of
3874
 * found occurrences */
3875
static int
3876
get_req_headers(const struct mg_request_info *ri,
3877
                const char *name,
3878
                const char **output,
3879
                int output_max_size)
3880
0
{
3881
0
  int i;
3882
0
  int cnt = 0;
3883
0
  if (ri) {
3884
0
    for (i = 0; i < ri->num_headers && cnt < output_max_size; i++) {
3885
0
      if (!mg_strcasecmp(name, ri->http_headers[i].name)) {
3886
0
        output[cnt++] = ri->http_headers[i].value;
3887
0
      }
3888
0
    }
3889
0
  }
3890
0
  return cnt;
3891
0
}
3892
3893
3894
CIVETWEB_API const char *
3895
mg_get_header(const struct mg_connection *conn, const char *name)
3896
0
{
3897
0
  if (!conn) {
3898
0
    return NULL;
3899
0
  }
3900
3901
0
  if (conn->connection_type == CONNECTION_TYPE_REQUEST) {
3902
0
    return get_header(conn->request_info.http_headers,
3903
0
                      conn->request_info.num_headers,
3904
0
                      name);
3905
0
  }
3906
0
  if (conn->connection_type == CONNECTION_TYPE_RESPONSE) {
3907
0
    return get_header(conn->response_info.http_headers,
3908
0
                      conn->response_info.num_headers,
3909
0
                      name);
3910
0
  }
3911
0
  return NULL;
3912
0
}
3913
3914
3915
static const char *
3916
get_http_version(const struct mg_connection *conn)
3917
0
{
3918
0
  if (!conn) {
3919
0
    return NULL;
3920
0
  }
3921
3922
0
  if (conn->connection_type == CONNECTION_TYPE_REQUEST) {
3923
0
    return conn->request_info.http_version;
3924
0
  }
3925
0
  if (conn->connection_type == CONNECTION_TYPE_RESPONSE) {
3926
0
    return conn->response_info.http_version;
3927
0
  }
3928
0
  return NULL;
3929
0
}
3930
3931
3932
/* A helper function for traversing a comma separated list of values.
3933
 * It returns a list pointer shifted to the next value, or NULL if the end
3934
 * of the list found.
3935
 * Value is stored in val vector. If value has form "x=y", then eq_val
3936
 * vector is initialized to point to the "y" part, and val vector length
3937
 * is adjusted to point only to "x". */
3938
static const char *
3939
next_option(const char *list, struct vec *val, struct vec *eq_val)
3940
6
{
3941
6
  int end;
3942
3943
6
reparse:
3944
6
  if (val == NULL || list == NULL || *list == '\0') {
3945
    /* End of the list */
3946
4
    return NULL;
3947
4
  }
3948
3949
  /* Skip over leading LWS */
3950
2
  while (*list == ' ' || *list == '\t')
3951
0
    list++;
3952
3953
2
  val->ptr = list;
3954
2
  if ((list = strchr(val->ptr, ',')) != NULL) {
3955
    /* Comma found. Store length and shift the list ptr */
3956
0
    val->len = ((size_t)(list - val->ptr));
3957
0
    list++;
3958
2
  } else {
3959
    /* This value is the last one */
3960
2
    list = val->ptr + strlen(val->ptr);
3961
2
    val->len = ((size_t)(list - val->ptr));
3962
2
  }
3963
3964
  /* Adjust length for trailing LWS */
3965
2
  end = (int)val->len - 1;
3966
2
  while (end >= 0 && ((val->ptr[end] == ' ') || (val->ptr[end] == '\t')))
3967
0
    end--;
3968
2
  val->len = (size_t)(end) + (size_t)(1);
3969
3970
2
  if (val->len == 0) {
3971
    /* Ignore any empty entries. */
3972
0
    goto reparse;
3973
0
  }
3974
3975
2
  if (eq_val != NULL) {
3976
    /* Value has form "x=y", adjust pointers and lengths
3977
     * so that val points to "x", and eq_val points to "y". */
3978
0
    eq_val->len = 0;
3979
0
    eq_val->ptr = (const char *)memchr(val->ptr, '=', val->len);
3980
0
    if (eq_val->ptr != NULL) {
3981
0
      eq_val->ptr++; /* Skip over '=' character */
3982
0
      eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len;
3983
0
      val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1;
3984
0
    }
3985
0
  }
3986
3987
2
  return list;
3988
2
}
3989
3990
3991
/* A helper function for checking if a comma separated list of values
3992
 * contains
3993
 * the given option (case insensitvely).
3994
 * 'header' can be NULL, in which case false is returned. */
3995
static int
3996
header_has_option(const char *header, const char *option)
3997
0
{
3998
0
  struct vec opt_vec;
3999
0
  struct vec eq_vec;
4000
4001
0
  DEBUG_ASSERT(option != NULL);
4002
0
  DEBUG_ASSERT(option[0] != '\0');
4003
4004
0
  while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) {
4005
0
    if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0)
4006
0
      return 1;
4007
0
  }
4008
4009
0
  return 0;
4010
0
}
4011
4012
4013
/* Sorting function implemented in a separate file */
4014
#include "sort.inl"
4015
4016
/* Pattern matching has been reimplemented in a new file */
4017
#include "match.inl"
4018
4019
4020
/* HTTP 1.1 assumes keep alive if "Connection:" header is not set
4021
 * This function must tolerate situations when connection info is not
4022
 * set up, for example if request parsing failed. */
4023
static int
4024
should_keep_alive(const struct mg_connection *conn)
4025
0
{
4026
0
  const char *http_version;
4027
0
  const char *header;
4028
4029
  /* First satisfy needs of the server */
4030
0
  if ((conn == NULL) || conn->must_close) {
4031
    /* Close, if civetweb framework needs to close */
4032
0
    return 0;
4033
0
  }
4034
4035
0
  if (mg_strcasecmp(conn->dom_ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0) {
4036
    /* Close, if keep alive is not enabled */
4037
0
    return 0;
4038
0
  }
4039
4040
  /* Check explicit wish of the client */
4041
0
  header = mg_get_header(conn, "Connection");
4042
0
  if (header) {
4043
    /* If there is a connection header from the client, obey */
4044
0
    if (header_has_option(header, "keep-alive")) {
4045
0
      return 1;
4046
0
    }
4047
0
    return 0;
4048
0
  }
4049
4050
  /* Use default of the standard */
4051
0
  http_version = get_http_version(conn);
4052
0
  if (http_version && (0 == strcmp(http_version, "1.1"))) {
4053
    /* HTTP 1.1 default is keep alive */
4054
0
    return 1;
4055
0
  }
4056
4057
  /* HTTP 1.0 (and earlier) default is to close the connection */
4058
0
  return 0;
4059
0
}
4060
4061
4062
static int
4063
should_decode_url(const struct mg_connection *conn)
4064
0
{
4065
0
  if (!conn || !conn->dom_ctx) {
4066
0
    return 0;
4067
0
  }
4068
4069
0
  return (mg_strcasecmp(conn->dom_ctx->config[DECODE_URL], "yes") == 0);
4070
0
}
4071
4072
4073
static int
4074
should_decode_query_string(const struct mg_connection *conn)
4075
0
{
4076
0
  if (!conn || !conn->dom_ctx) {
4077
0
    return 0;
4078
0
  }
4079
4080
0
  return (mg_strcasecmp(conn->dom_ctx->config[DECODE_QUERY_STRING], "yes")
4081
0
          == 0);
4082
0
}
4083
4084
4085
static const char *
4086
suggest_connection_header(const struct mg_connection *conn)
4087
0
{
4088
0
  return should_keep_alive(conn) ? "keep-alive" : "close";
4089
0
}
4090
4091
4092
#include "response.inl"
4093
4094
4095
static void
4096
send_no_cache_header(struct mg_connection *conn)
4097
0
{
4098
  /* Send all current and obsolete cache opt-out directives. */
4099
0
  mg_response_header_add(conn,
4100
0
                         "Cache-Control",
4101
0
                         "no-cache, no-store, "
4102
0
                         "must-revalidate, private, max-age=0",
4103
0
                         -1);
4104
0
  mg_response_header_add(conn, "Expires", "0", -1);
4105
4106
0
  if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
4107
    /* Obsolete, but still send it for HTTP/1.0 */
4108
0
    mg_response_header_add(conn, "Pragma", "no-cache", -1);
4109
0
  }
4110
0
}
4111
4112
4113
static void
4114
send_static_cache_header(struct mg_connection *conn)
4115
0
{
4116
0
#if !defined(NO_CACHING)
4117
0
  int max_age;
4118
0
  char val[64];
4119
4120
0
  const char *cache_control =
4121
0
      conn->dom_ctx->config[STATIC_FILE_CACHE_CONTROL];
4122
4123
  /* If there is a full cache-control option configured,0 use it */
4124
0
  if (cache_control != NULL) {
4125
0
    mg_response_header_add(conn, "Cache-Control", cache_control, -1);
4126
0
    return;
4127
0
  }
4128
4129
  /* Read the server config to check how long a file may be cached.
4130
   * The configuration is in seconds. */
4131
0
  max_age = atoi(conn->dom_ctx->config[STATIC_FILE_MAX_AGE]);
4132
0
  if (max_age <= 0) {
4133
    /* 0 means "do not cache". All values <0 are reserved
4134
     * and may be used differently in the future. */
4135
    /* If a file should not be cached, do not only send
4136
     * max-age=0, but also pragmas and Expires headers. */
4137
0
    send_no_cache_header(conn);
4138
0
    return;
4139
0
  }
4140
4141
  /* Use "Cache-Control: max-age" instead of "Expires" header.
4142
   * Reason: see https://www.mnot.net/blog/2007/05/15/expires_max-age */
4143
  /* See also https://www.mnot.net/cache_docs/ */
4144
  /* According to RFC 2616, Section 14.21, caching times should not exceed
4145
   * one year. A year with 365 days corresponds to 31536000 seconds, a
4146
   * leap
4147
   * year to 31622400 seconds. For the moment, we just send whatever has
4148
   * been configured, still the behavior for >1 year should be considered
4149
   * as undefined. */
4150
0
  mg_snprintf(
4151
0
      conn, NULL, val, sizeof(val), "max-age=%lu", (unsigned long)max_age);
4152
0
  mg_response_header_add(conn, "Cache-Control", val, -1);
4153
4154
#else  /* NO_CACHING */
4155
4156
  send_no_cache_header(conn);
4157
#endif /* !NO_CACHING */
4158
0
}
4159
4160
4161
static void
4162
send_additional_header(struct mg_connection *conn)
4163
0
{
4164
0
  const char *header = conn->dom_ctx->config[ADDITIONAL_HEADER];
4165
4166
0
#if !defined(NO_SSL)
4167
0
  if (conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]) {
4168
0
    long max_age = atol(conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]);
4169
0
    if (max_age >= 0) {
4170
0
      char val[64];
4171
0
      mg_snprintf(conn,
4172
0
                  NULL,
4173
0
                  val,
4174
0
                  sizeof(val),
4175
0
                  "max-age=%lu",
4176
0
                  (unsigned long)max_age);
4177
0
      mg_response_header_add(conn, "Strict-Transport-Security", val, -1);
4178
0
    }
4179
0
  }
4180
0
#endif
4181
4182
  // Content-Security-Policy
4183
4184
0
  if (header && header[0]) {
4185
0
    mg_response_header_add_lines(conn, header);
4186
0
  }
4187
0
}
4188
4189
4190
static void
4191
send_cors_header(struct mg_connection *conn)
4192
0
{
4193
0
  const char *origin_hdr = mg_get_header(conn, "Origin");
4194
0
  const char *cors_orig_cfg =
4195
0
      conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
4196
0
  const char *cors_cred_cfg =
4197
0
      conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
4198
0
  const char *cors_hdr_cfg =
4199
0
      conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
4200
0
  const char *cors_exphdr_cfg =
4201
0
      conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
4202
0
  const char *cors_meth_cfg =
4203
0
      conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
4204
4205
0
  if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) {
4206
    /* Cross-origin resource sharing (CORS), see
4207
     * http://www.html5rocks.com/en/tutorials/cors/,
4208
     * http://www.html5rocks.com/static/images/cors_server_flowchart.png
4209
     * CORS preflight is not supported for files. */
4210
0
    mg_response_header_add(conn,
4211
0
                           "Access-Control-Allow-Origin",
4212
0
                           cors_orig_cfg,
4213
0
                           -1);
4214
0
  }
4215
4216
0
  if (cors_cred_cfg && *cors_cred_cfg && origin_hdr && *origin_hdr) {
4217
    /* Cross-origin resource sharing (CORS), see
4218
     * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
4219
     */
4220
0
    mg_response_header_add(conn,
4221
0
                           "Access-Control-Allow-Credentials",
4222
0
                           cors_cred_cfg,
4223
0
                           -1);
4224
0
  }
4225
4226
0
  if (cors_hdr_cfg && *cors_hdr_cfg) {
4227
0
    mg_response_header_add(conn,
4228
0
                           "Access-Control-Allow-Headers",
4229
0
                           cors_hdr_cfg,
4230
0
                           -1);
4231
0
  }
4232
4233
0
  if (cors_exphdr_cfg && *cors_exphdr_cfg) {
4234
0
    mg_response_header_add(conn,
4235
0
                           "Access-Control-Expose-Headers",
4236
0
                           cors_exphdr_cfg,
4237
0
                           -1);
4238
0
  }
4239
4240
0
  if (cors_meth_cfg && *cors_meth_cfg) {
4241
0
    mg_response_header_add(conn,
4242
0
                           "Access-Control-Allow-Methods",
4243
0
                           cors_meth_cfg,
4244
0
                           -1);
4245
0
  }
4246
0
}
4247
4248
4249
#if !defined(NO_FILESYSTEMS)
4250
static void handle_file_based_request(struct mg_connection *conn,
4251
                                      const char *path,
4252
                                      struct mg_file *filep);
4253
#endif /* NO_FILESYSTEMS */
4254
4255
4256
CIVETWEB_API const char *
4257
mg_get_response_code_text(const struct mg_connection *conn, int response_code)
4258
0
{
4259
  /* See IANA HTTP status code assignment:
4260
   * http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
4261
   */
4262
4263
0
  switch (response_code) {
4264
    /* RFC2616 Section 10.1 - Informational 1xx */
4265
0
  case 100:
4266
0
    return "Continue"; /* RFC2616 Section 10.1.1 */
4267
0
  case 101:
4268
0
    return "Switching Protocols"; /* RFC2616 Section 10.1.2 */
4269
0
  case 102:
4270
0
    return "Processing"; /* RFC2518 Section 10.1 */
4271
4272
    /* RFC2616 Section 10.2 - Successful 2xx */
4273
0
  case 200:
4274
0
    return "OK"; /* RFC2616 Section 10.2.1 */
4275
0
  case 201:
4276
0
    return "Created"; /* RFC2616 Section 10.2.2 */
4277
0
  case 202:
4278
0
    return "Accepted"; /* RFC2616 Section 10.2.3 */
4279
0
  case 203:
4280
0
    return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */
4281
0
  case 204:
4282
0
    return "No Content"; /* RFC2616 Section 10.2.5 */
4283
0
  case 205:
4284
0
    return "Reset Content"; /* RFC2616 Section 10.2.6 */
4285
0
  case 206:
4286
0
    return "Partial Content"; /* RFC2616 Section 10.2.7 */
4287
0
  case 207:
4288
0
    return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1
4289
                            */
4290
0
  case 208:
4291
0
    return "Already Reported"; /* RFC5842 Section 7.1 */
4292
4293
0
  case 226:
4294
0
    return "IM used"; /* RFC3229 Section 10.4.1 */
4295
4296
    /* RFC2616 Section 10.3 - Redirection 3xx */
4297
0
  case 300:
4298
0
    return "Multiple Choices"; /* RFC2616 Section 10.3.1 */
4299
0
  case 301:
4300
0
    return "Moved Permanently"; /* RFC2616 Section 10.3.2 */
4301
0
  case 302:
4302
0
    return "Found"; /* RFC2616 Section 10.3.3 */
4303
0
  case 303:
4304
0
    return "See Other"; /* RFC2616 Section 10.3.4 */
4305
0
  case 304:
4306
0
    return "Not Modified"; /* RFC2616 Section 10.3.5 */
4307
0
  case 305:
4308
0
    return "Use Proxy"; /* RFC2616 Section 10.3.6 */
4309
0
  case 307:
4310
0
    return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */
4311
0
  case 308:
4312
0
    return "Permanent Redirect"; /* RFC7238 Section 3 */
4313
4314
    /* RFC2616 Section 10.4 - Client Error 4xx */
4315
0
  case 400:
4316
0
    return "Bad Request"; /* RFC2616 Section 10.4.1 */
4317
0
  case 401:
4318
0
    return "Unauthorized"; /* RFC2616 Section 10.4.2 */
4319
0
  case 402:
4320
0
    return "Payment Required"; /* RFC2616 Section 10.4.3 */
4321
0
  case 403:
4322
0
    return "Forbidden"; /* RFC2616 Section 10.4.4 */
4323
0
  case 404:
4324
0
    return "Not Found"; /* RFC2616 Section 10.4.5 */
4325
0
  case 405:
4326
0
    return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */
4327
0
  case 406:
4328
0
    return "Not Acceptable"; /* RFC2616 Section 10.4.7 */
4329
0
  case 407:
4330
0
    return "Proxy Authentication Required"; /* RFC2616 Section 10.4.8 */
4331
0
  case 408:
4332
0
    return "Request Time-out"; /* RFC2616 Section 10.4.9 */
4333
0
  case 409:
4334
0
    return "Conflict"; /* RFC2616 Section 10.4.10 */
4335
0
  case 410:
4336
0
    return "Gone"; /* RFC2616 Section 10.4.11 */
4337
0
  case 411:
4338
0
    return "Length Required"; /* RFC2616 Section 10.4.12 */
4339
0
  case 412:
4340
0
    return "Precondition Failed"; /* RFC2616 Section 10.4.13 */
4341
0
  case 413:
4342
0
    return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */
4343
0
  case 414:
4344
0
    return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */
4345
0
  case 415:
4346
0
    return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */
4347
0
  case 416:
4348
0
    return "Requested range not satisfiable"; /* RFC2616 Section 10.4.17
4349
                                               */
4350
0
  case 417:
4351
0
    return "Expectation Failed"; /* RFC2616 Section 10.4.18 */
4352
4353
0
  case 421:
4354
0
    return "Misdirected Request"; /* RFC7540 Section 9.1.2 */
4355
0
  case 422:
4356
0
    return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918
4357
                                     * Section 11.2 */
4358
0
  case 423:
4359
0
    return "Locked"; /* RFC2518 Section 10.4, RFC4918 Section 11.3 */
4360
0
  case 424:
4361
0
    return "Failed Dependency"; /* RFC2518 Section 10.5, RFC4918
4362
                                 * Section 11.4 */
4363
4364
0
  case 426:
4365
0
    return "Upgrade Required"; /* RFC 2817 Section 4 */
4366
4367
0
  case 428:
4368
0
    return "Precondition Required"; /* RFC 6585, Section 3 */
4369
0
  case 429:
4370
0
    return "Too Many Requests"; /* RFC 6585, Section 4 */
4371
4372
0
  case 431:
4373
0
    return "Request Header Fields Too Large"; /* RFC 6585, Section 5 */
4374
4375
0
  case 451:
4376
0
    return "Unavailable For Legal Reasons"; /* draft-tbray-http-legally-restricted-status-05,
4377
                                             * Section 3 */
4378
4379
    /* RFC2616 Section 10.5 - Server Error 5xx */
4380
0
  case 500:
4381
0
    return "Internal Server Error"; /* RFC2616 Section 10.5.1 */
4382
0
  case 501:
4383
0
    return "Not Implemented"; /* RFC2616 Section 10.5.2 */
4384
0
  case 502:
4385
0
    return "Bad Gateway"; /* RFC2616 Section 10.5.3 */
4386
0
  case 503:
4387
0
    return "Service Unavailable"; /* RFC2616 Section 10.5.4 */
4388
0
  case 504:
4389
0
    return "Gateway Time-out"; /* RFC2616 Section 10.5.5 */
4390
0
  case 505:
4391
0
    return "HTTP Version not supported"; /* RFC2616 Section 10.5.6 */
4392
0
  case 506:
4393
0
    return "Variant Also Negotiates"; /* RFC 2295, Section 8.1 */
4394
0
  case 507:
4395
0
    return "Insufficient Storage"; /* RFC2518 Section 10.6, RFC4918
4396
                                    * Section 11.5 */
4397
0
  case 508:
4398
0
    return "Loop Detected"; /* RFC5842 Section 7.1 */
4399
4400
0
  case 510:
4401
0
    return "Not Extended"; /* RFC 2774, Section 7 */
4402
0
  case 511:
4403
0
    return "Network Authentication Required"; /* RFC 6585, Section 6 */
4404
4405
    /* Other status codes, not shown in the IANA HTTP status code
4406
     * assignment.
4407
     * E.g., "de facto" standards due to common use, ... */
4408
0
  case 418:
4409
0
    return "I am a teapot"; /* RFC2324 Section 2.3.2 */
4410
0
  case 419:
4411
0
    return "Authentication Timeout"; /* common use */
4412
0
  case 420:
4413
0
    return "Enhance Your Calm"; /* common use */
4414
0
  case 440:
4415
0
    return "Login Timeout"; /* common use */
4416
0
  case 509:
4417
0
    return "Bandwidth Limit Exceeded"; /* common use */
4418
4419
0
  default:
4420
    /* This error code is unknown. This should not happen. */
4421
0
    if (conn) {
4422
0
      mg_cry_internal(conn,
4423
0
                      "Unknown HTTP response code: %u",
4424
0
                      response_code);
4425
0
    }
4426
4427
    /* Return at least a category according to RFC 2616 Section 10. */
4428
0
    if (response_code >= 100 && response_code < 200) {
4429
      /* Unknown informational status code */
4430
0
      return "Information";
4431
0
    }
4432
0
    if (response_code >= 200 && response_code < 300) {
4433
      /* Unknown success code */
4434
0
      return "Success";
4435
0
    }
4436
0
    if (response_code >= 300 && response_code < 400) {
4437
      /* Unknown redirection code */
4438
0
      return "Redirection";
4439
0
    }
4440
0
    if (response_code >= 400 && response_code < 500) {
4441
      /* Unknown request error code */
4442
0
      return "Client Error";
4443
0
    }
4444
0
    if (response_code >= 500 && response_code < 600) {
4445
      /* Unknown server error code */
4446
0
      return "Server Error";
4447
0
    }
4448
4449
    /* Response code not even within reasonable range */
4450
0
    return "";
4451
0
  }
4452
0
}
4453
4454
4455
static int
4456
mg_send_http_error_impl(struct mg_connection *conn,
4457
                        int status,
4458
                        const char *fmt,
4459
                        va_list args)
4460
0
{
4461
0
  char errmsg_buf[MG_BUF_LEN];
4462
0
  va_list ap;
4463
0
  int has_body;
4464
4465
0
#if !defined(NO_FILESYSTEMS)
4466
0
  char path_buf[UTF8_PATH_MAX];
4467
0
  int len, i, page_handler_found, scope, truncated;
4468
0
  const char *error_handler = NULL;
4469
0
  struct mg_file error_page_file = STRUCT_FILE_INITIALIZER;
4470
0
  const char *error_page_file_ext, *tstr;
4471
0
#endif /* NO_FILESYSTEMS */
4472
0
  int handled_by_callback = 0;
4473
4474
0
  if ((conn == NULL) || (fmt == NULL)) {
4475
0
    return -2;
4476
0
  }
4477
4478
  /* Set status (for log) */
4479
0
  conn->status_code = status;
4480
4481
  /* Errors 1xx, 204 and 304 MUST NOT send a body */
4482
0
  has_body = ((status > 199) && (status != 204) && (status != 304));
4483
4484
  /* Prepare message in buf, if required */
4485
0
  if (has_body
4486
0
      || (!conn->in_error_handler
4487
0
          && (conn->phys_ctx->callbacks.http_error != NULL))) {
4488
    /* Store error message in errmsg_buf */
4489
0
    va_copy(ap, args);
4490
0
    mg_vsnprintf(conn, NULL, errmsg_buf, sizeof(errmsg_buf), fmt, ap);
4491
0
    va_end(ap);
4492
    /* In a debug build, print all html errors */
4493
0
    DEBUG_TRACE("Error %i - [%s]", status, errmsg_buf);
4494
0
  }
4495
4496
  /* If there is a http_error callback, call it.
4497
   * But don't do it recursively, if callback calls mg_send_http_error again.
4498
   */
4499
0
  if (!conn->in_error_handler
4500
0
      && (conn->phys_ctx->callbacks.http_error != NULL)) {
4501
    /* Mark in_error_handler to avoid recursion and call user callback. */
4502
0
    conn->in_error_handler = 1;
4503
0
    handled_by_callback =
4504
0
        (conn->phys_ctx->callbacks.http_error(conn, status, errmsg_buf)
4505
0
         == 0);
4506
0
    conn->in_error_handler = 0;
4507
0
  }
4508
4509
0
  if (!handled_by_callback) {
4510
    /* Check for recursion */
4511
0
    if (conn->in_error_handler) {
4512
0
      DEBUG_TRACE(
4513
0
          "Recursion when handling error %u - fall back to default",
4514
0
          status);
4515
0
#if !defined(NO_FILESYSTEMS)
4516
0
    } else {
4517
      /* Send user defined error pages, if defined */
4518
0
      error_handler = conn->dom_ctx->config[ERROR_PAGES];
4519
0
      error_page_file_ext = conn->dom_ctx->config[INDEX_FILES];
4520
0
      page_handler_found = 0;
4521
4522
0
      if (error_handler != NULL) {
4523
0
        for (scope = 1; (scope <= 3) && !page_handler_found; scope++) {
4524
0
          switch (scope) {
4525
0
          case 1: /* Handler for specific error, e.g. 404 error */
4526
0
            mg_snprintf(conn,
4527
0
                        &truncated,
4528
0
                        path_buf,
4529
0
                        sizeof(path_buf) - 32,
4530
0
                        "%serror%03u.",
4531
0
                        error_handler,
4532
0
                        status);
4533
0
            break;
4534
0
          case 2: /* Handler for error group, e.g., 5xx error
4535
                   * handler
4536
                   * for all server errors (500-599) */
4537
0
            mg_snprintf(conn,
4538
0
                        &truncated,
4539
0
                        path_buf,
4540
0
                        sizeof(path_buf) - 32,
4541
0
                        "%serror%01uxx.",
4542
0
                        error_handler,
4543
0
                        status / 100);
4544
0
            break;
4545
0
          default: /* Handler for all errors */
4546
0
            mg_snprintf(conn,
4547
0
                        &truncated,
4548
0
                        path_buf,
4549
0
                        sizeof(path_buf) - 32,
4550
0
                        "%serror.",
4551
0
                        error_handler);
4552
0
            break;
4553
0
          }
4554
4555
          /* String truncation in buf may only occur if
4556
           * error_handler is too long. This string is
4557
           * from the config, not from a client. */
4558
0
          (void)truncated;
4559
4560
          /* The following code is redundant, but it should avoid
4561
           * false positives in static source code analyzers and
4562
           * vulnerability scanners.
4563
           */
4564
0
          path_buf[sizeof(path_buf) - 32] = 0;
4565
0
          len = (int)strlen(path_buf);
4566
0
          if (len > (int)sizeof(path_buf) - 32) {
4567
0
            len = (int)sizeof(path_buf) - 32;
4568
0
          }
4569
4570
          /* Start with the file extension from the configuration. */
4571
0
          tstr = strchr(error_page_file_ext, '.');
4572
4573
0
          while (tstr) {
4574
0
            for (i = 1;
4575
0
                 (i < 32) && (tstr[i] != 0) && (tstr[i] != ',');
4576
0
                 i++) {
4577
              /* buffer overrun is not possible here, since
4578
               * (i < 32) && (len < sizeof(path_buf) - 32)
4579
               * ==> (i + len) < sizeof(path_buf) */
4580
0
              path_buf[len + i - 1] = tstr[i];
4581
0
            }
4582
            /* buffer overrun is not possible here, since
4583
             * (i <= 32) && (len < sizeof(path_buf) - 32)
4584
             * ==> (i + len) <= sizeof(path_buf) */
4585
0
            path_buf[len + i - 1] = 0;
4586
4587
0
            if (mg_stat(conn, path_buf, &error_page_file.stat)) {
4588
0
              DEBUG_TRACE("Check error page %s - found",
4589
0
                          path_buf);
4590
0
              page_handler_found = 1;
4591
0
              break;
4592
0
            }
4593
0
            DEBUG_TRACE("Check error page %s - not found",
4594
0
                        path_buf);
4595
4596
            /* Continue with the next file extension from the
4597
             * configuration (if there is a next one). */
4598
0
            tstr = strchr(tstr + i, '.');
4599
0
          }
4600
0
        }
4601
0
      }
4602
4603
0
      if (page_handler_found) {
4604
0
        conn->in_error_handler = 1;
4605
0
        handle_file_based_request(conn, path_buf, &error_page_file);
4606
0
        conn->in_error_handler = 0;
4607
0
        return 0;
4608
0
      }
4609
0
#endif /* NO_FILESYSTEMS */
4610
0
    }
4611
4612
    /* No custom error page. Send default error page. */
4613
0
    conn->must_close = 1;
4614
0
    mg_response_header_start(conn, status);
4615
0
    send_no_cache_header(conn);
4616
0
    send_additional_header(conn);
4617
0
    send_cors_header(conn);
4618
0
    if (has_body) {
4619
0
      mg_response_header_add(conn,
4620
0
                             "Content-Type",
4621
0
                             "text/plain; charset=utf-8",
4622
0
                             -1);
4623
0
    }
4624
0
    mg_response_header_send(conn);
4625
4626
    /* HTTP responses 1xx, 204 and 304 MUST NOT send a body */
4627
0
    if (has_body) {
4628
      /* For other errors, send a generic error message. */
4629
0
      const char *status_text = mg_get_response_code_text(conn, status);
4630
0
      mg_printf(conn, "Error %d: %s\n", status, status_text);
4631
0
      mg_write(conn, errmsg_buf, strlen(errmsg_buf));
4632
4633
0
    } else {
4634
      /* No body allowed. Close the connection. */
4635
0
      DEBUG_TRACE("Error %i", status);
4636
0
    }
4637
0
  }
4638
0
  return 0;
4639
0
}
4640
4641
4642
CIVETWEB_API int
4643
mg_send_http_error(struct mg_connection *conn, int status, const char *fmt, ...)
4644
0
{
4645
0
  va_list ap;
4646
0
  int ret;
4647
4648
0
  va_start(ap, fmt);
4649
0
  ret = mg_send_http_error_impl(conn, status, fmt, ap);
4650
0
  va_end(ap);
4651
4652
0
  return ret;
4653
0
}
4654
4655
4656
CIVETWEB_API int
4657
mg_send_http_ok(struct mg_connection *conn,
4658
                const char *mime_type,
4659
                long long content_length)
4660
0
{
4661
0
  if ((mime_type == NULL) || (*mime_type == 0)) {
4662
    /* No content type defined: default to text/html */
4663
0
    mime_type = "text/html";
4664
0
  }
4665
4666
0
  mg_response_header_start(conn, 200);
4667
0
  send_no_cache_header(conn);
4668
0
  send_additional_header(conn);
4669
0
  send_cors_header(conn);
4670
0
  mg_response_header_add(conn, "Content-Type", mime_type, -1);
4671
0
  if (content_length < 0) {
4672
    /* Size not known. Use chunked encoding (HTTP/1.x) */
4673
0
    if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
4674
      /* Only HTTP/1.x defines "chunked" encoding, HTTP/2 does not*/
4675
0
      mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1);
4676
0
    }
4677
0
  } else {
4678
0
    char len[32];
4679
0
    int trunc = 0;
4680
0
    mg_snprintf(conn,
4681
0
                &trunc,
4682
0
                len,
4683
0
                sizeof(len),
4684
0
                "%" UINT64_FMT,
4685
0
                (uint64_t)content_length);
4686
0
    if (!trunc) {
4687
      /* Since 32 bytes is enough to hold any 64 bit decimal number,
4688
       * !trunc is always true */
4689
0
      mg_response_header_add(conn, "Content-Length", len, -1);
4690
0
    }
4691
0
  }
4692
0
  mg_response_header_send(conn);
4693
4694
0
  return 0;
4695
0
}
4696
4697
4698
CIVETWEB_API int
4699
mg_send_http_redirect(struct mg_connection *conn,
4700
                      const char *target_url,
4701
                      int redirect_code)
4702
0
{
4703
  /* Send a 30x redirect response.
4704
   *
4705
   * Redirect types (status codes):
4706
   *
4707
   * Status | Perm/Temp | Method              | Version
4708
   *   301  | permanent | POST->GET undefined | HTTP/1.0
4709
   *   302  | temporary | POST->GET undefined | HTTP/1.0
4710
   *   303  | temporary | always use GET      | HTTP/1.1
4711
   *   307  | temporary | always keep method  | HTTP/1.1
4712
   *   308  | permanent | always keep method  | HTTP/1.1
4713
   */
4714
4715
#if defined(MG_SEND_REDIRECT_BODY)
4716
  char redirect_body[MG_BUF_LEN];
4717
  size_t content_len = 0;
4718
  char content_len_text[32];
4719
#endif
4720
4721
  /* In case redirect_code=0, use 307. */
4722
0
  if (redirect_code == 0) {
4723
0
    redirect_code = 307;
4724
0
  }
4725
4726
  /* In case redirect_code is none of the above, return error. */
4727
0
  if ((redirect_code != 301) && (redirect_code != 302)
4728
0
      && (redirect_code != 303) && (redirect_code != 307)
4729
0
      && (redirect_code != 308)) {
4730
    /* Parameter error */
4731
0
    return -2;
4732
0
  }
4733
4734
  /* If target_url is not defined, redirect to "/". */
4735
0
  if ((target_url == NULL) || (*target_url == 0)) {
4736
0
    target_url = "/";
4737
0
  }
4738
4739
#if defined(MG_SEND_REDIRECT_BODY)
4740
  /* TODO: condition name? */
4741
4742
  /* Prepare a response body with a hyperlink.
4743
   *
4744
   * According to RFC2616 (and RFC1945 before):
4745
   * Unless the request method was HEAD, the entity of the
4746
   * response SHOULD contain a short hypertext note with a hyperlink to
4747
   * the new URI(s).
4748
   *
4749
   * However, this response body is not useful in M2M communication.
4750
   * Probably the original reason in the RFC was, clients not supporting
4751
   * a 30x HTTP redirect could still show the HTML page and let the user
4752
   * press the link. Since current browsers support 30x HTTP, the additional
4753
   * HTML body does not seem to make sense anymore.
4754
   *
4755
   * The new RFC7231 (Section 6.4) does no longer recommend it ("SHOULD"),
4756
   * but it only notes:
4757
   * The server's response payload usually contains a short
4758
   * hypertext note with a hyperlink to the new URI(s).
4759
   *
4760
   * Deactivated by default. If you need the 30x body, set the define.
4761
   */
4762
  mg_snprintf(
4763
      conn,
4764
      NULL /* ignore truncation */,
4765
      redirect_body,
4766
      sizeof(redirect_body),
4767
      "<html><head>%s</head><body><a href=\"%s\">%s</a></body></html>",
4768
      redirect_text,
4769
      target_url,
4770
      target_url);
4771
  content_len = strlen(reply);
4772
  sprintf(content_len_text, "%lu", (unsigned long)content_len);
4773
#endif
4774
4775
  /* Send all required headers */
4776
0
  mg_response_header_start(conn, redirect_code);
4777
0
  mg_response_header_add(conn, "Location", target_url, -1);
4778
0
  if ((redirect_code == 301) || (redirect_code == 308)) {
4779
    /* Permanent redirect */
4780
0
    send_static_cache_header(conn);
4781
0
  } else {
4782
    /* Temporary redirect */
4783
0
    send_no_cache_header(conn);
4784
0
  }
4785
0
  send_additional_header(conn);
4786
0
  send_cors_header(conn);
4787
#if defined(MG_SEND_REDIRECT_BODY)
4788
  mg_response_header_add(conn, "Content-Type", "text/html", -1);
4789
  mg_response_header_add(conn, "Content-Length", content_len_text, -1);
4790
#else
4791
0
  mg_response_header_add(conn, "Content-Length", "0", 1);
4792
0
#endif
4793
0
  mg_response_header_send(conn);
4794
4795
#if defined(MG_SEND_REDIRECT_BODY)
4796
  /* Send response body */
4797
  /* ... unless it is a HEAD request */
4798
  if (0 != strcmp(conn->request_info.request_method, "HEAD")) {
4799
    ret = mg_write(conn, redirect_body, content_len);
4800
  }
4801
#endif
4802
4803
0
  return 1;
4804
0
}
4805
4806
4807
#if defined(_WIN32)
4808
/* Create substitutes for POSIX functions in Win32. */
4809
4810
#if defined(GCC_DIAGNOSTIC)
4811
/* Show no warning in case system functions are not used. */
4812
#pragma GCC diagnostic push
4813
#pragma GCC diagnostic ignored "-Wunused-function"
4814
#endif
4815
4816
4817
static int
4818
pthread_mutex_init(pthread_mutex_t *mutex, void *unused)
4819
{
4820
  (void)unused;
4821
  /* Always initialize as PTHREAD_MUTEX_RECURSIVE */
4822
  InitializeCriticalSection(&mutex->sec);
4823
  return 0;
4824
}
4825
4826
4827
static int
4828
pthread_mutex_destroy(pthread_mutex_t *mutex)
4829
{
4830
  DeleteCriticalSection(&mutex->sec);
4831
  return 0;
4832
}
4833
4834
4835
static int
4836
pthread_mutex_lock(pthread_mutex_t *mutex)
4837
{
4838
  EnterCriticalSection(&mutex->sec);
4839
  return 0;
4840
}
4841
4842
4843
static int
4844
pthread_mutex_unlock(pthread_mutex_t *mutex)
4845
{
4846
  LeaveCriticalSection(&mutex->sec);
4847
  return 0;
4848
}
4849
4850
4851
FUNCTION_MAY_BE_UNUSED
4852
static int
4853
pthread_cond_init(pthread_cond_t *cv, const void *unused)
4854
{
4855
  (void)unused;
4856
  (void)pthread_mutex_init(&cv->threadIdSec, &pthread_mutex_attr);
4857
  cv->waiting_thread = NULL;
4858
  return 0;
4859
}
4860
4861
4862
FUNCTION_MAY_BE_UNUSED
4863
static int
4864
pthread_cond_timedwait(pthread_cond_t *cv,
4865
                       pthread_mutex_t *mutex,
4866
                       FUNCTION_MAY_BE_UNUSED const struct timespec *abstime)
4867
{
4868
  struct mg_workerTLS **ptls,
4869
      *tls = (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
4870
  int ok;
4871
  uint64_t nsnow, nswaitabs;
4872
  int64_t nswaitrel;
4873
  DWORD mswaitrel;
4874
4875
  pthread_mutex_lock(&cv->threadIdSec);
4876
  /* Add this thread to cv's waiting list */
4877
  ptls = &cv->waiting_thread;
4878
  for (; *ptls != NULL; ptls = &(*ptls)->next_waiting_thread)
4879
    ;
4880
  tls->next_waiting_thread = NULL;
4881
  *ptls = tls;
4882
  pthread_mutex_unlock(&cv->threadIdSec);
4883
4884
  if (abstime) {
4885
    nsnow = mg_get_current_time_ns();
4886
    nswaitabs =
4887
        (((uint64_t)abstime->tv_sec) * 1000000000) + abstime->tv_nsec;
4888
    nswaitrel = nswaitabs - nsnow;
4889
    if (nswaitrel < 0) {
4890
      nswaitrel = 0;
4891
    }
4892
    mswaitrel = (DWORD)(nswaitrel / 1000000);
4893
  } else {
4894
    mswaitrel = (DWORD)INFINITE;
4895
  }
4896
4897
  pthread_mutex_unlock(mutex);
4898
  ok = (WAIT_OBJECT_0
4899
        == WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel));
4900
  if (!ok) {
4901
    ok = 1;
4902
    pthread_mutex_lock(&cv->threadIdSec);
4903
    ptls = &cv->waiting_thread;
4904
    for (; *ptls != NULL; ptls = &(*ptls)->next_waiting_thread) {
4905
      if (*ptls == tls) {
4906
        *ptls = tls->next_waiting_thread;
4907
        ok = 0;
4908
        break;
4909
      }
4910
    }
4911
    pthread_mutex_unlock(&cv->threadIdSec);
4912
    if (ok) {
4913
      WaitForSingleObject(tls->pthread_cond_helper_mutex,
4914
                          (DWORD)INFINITE);
4915
    }
4916
  }
4917
  /* This thread has been removed from cv's waiting list */
4918
  pthread_mutex_lock(mutex);
4919
4920
  return ok ? 0 : -1;
4921
}
4922
4923
4924
FUNCTION_MAY_BE_UNUSED
4925
static int
4926
pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex)
4927
{
4928
  return pthread_cond_timedwait(cv, mutex, NULL);
4929
}
4930
4931
4932
FUNCTION_MAY_BE_UNUSED
4933
static int
4934
pthread_cond_signal(pthread_cond_t *cv)
4935
{
4936
  HANDLE wkup = NULL;
4937
  BOOL ok = FALSE;
4938
4939
  pthread_mutex_lock(&cv->threadIdSec);
4940
  if (cv->waiting_thread) {
4941
    wkup = cv->waiting_thread->pthread_cond_helper_mutex;
4942
    cv->waiting_thread = cv->waiting_thread->next_waiting_thread;
4943
4944
    ok = SetEvent(wkup);
4945
    DEBUG_ASSERT(ok);
4946
  }
4947
  pthread_mutex_unlock(&cv->threadIdSec);
4948
4949
  return ok ? 0 : 1;
4950
}
4951
4952
4953
FUNCTION_MAY_BE_UNUSED
4954
static int
4955
pthread_cond_broadcast(pthread_cond_t *cv)
4956
{
4957
  pthread_mutex_lock(&cv->threadIdSec);
4958
  while (cv->waiting_thread) {
4959
    pthread_cond_signal(cv);
4960
  }
4961
  pthread_mutex_unlock(&cv->threadIdSec);
4962
4963
  return 0;
4964
}
4965
4966
4967
FUNCTION_MAY_BE_UNUSED
4968
static int
4969
pthread_cond_destroy(pthread_cond_t *cv)
4970
{
4971
  pthread_mutex_lock(&cv->threadIdSec);
4972
  DEBUG_ASSERT(cv->waiting_thread == NULL);
4973
  pthread_mutex_unlock(&cv->threadIdSec);
4974
  pthread_mutex_destroy(&cv->threadIdSec);
4975
4976
  return 0;
4977
}
4978
4979
4980
#if defined(ALTERNATIVE_QUEUE)
4981
FUNCTION_MAY_BE_UNUSED
4982
static void *
4983
event_create(void)
4984
{
4985
  return (void *)CreateEvent(NULL, FALSE, FALSE, NULL);
4986
}
4987
4988
4989
FUNCTION_MAY_BE_UNUSED
4990
static int
4991
event_wait(void *eventhdl)
4992
{
4993
  int res = WaitForSingleObject((HANDLE)eventhdl, (DWORD)INFINITE);
4994
  return (res == WAIT_OBJECT_0);
4995
}
4996
4997
4998
FUNCTION_MAY_BE_UNUSED
4999
static int
5000
event_signal(void *eventhdl)
5001
{
5002
  return (int)SetEvent((HANDLE)eventhdl);
5003
}
5004
5005
5006
FUNCTION_MAY_BE_UNUSED
5007
static void
5008
event_destroy(void *eventhdl)
5009
{
5010
  CloseHandle((HANDLE)eventhdl);
5011
}
5012
#endif
5013
5014
5015
#if defined(GCC_DIAGNOSTIC)
5016
/* Enable unused function warning again */
5017
#pragma GCC diagnostic pop
5018
#endif
5019
5020
5021
/* For Windows, change all slashes to backslashes in path names. */
5022
static void
5023
change_slashes_to_backslashes(char *path)
5024
{
5025
  int i;
5026
5027
  for (i = 0; path[i] != '\0'; i++) {
5028
    if (path[i] == '/') {
5029
      path[i] = '\\';
5030
    }
5031
5032
    /* remove double backslash (check i > 0 to preserve UNC paths,
5033
     * like \\server\file.txt) */
5034
    if ((i > 0) && (path[i] == '\\')) {
5035
      while ((path[i + 1] == '\\') || (path[i + 1] == '/')) {
5036
        (void)memmove(path + i + 1, path + i + 2, strlen(path + i + 1));
5037
      }
5038
    }
5039
  }
5040
}
5041
5042
5043
static int
5044
mg_wcscasecmp(const wchar_t *s1, const wchar_t *s2)
5045
{
5046
  int diff;
5047
5048
  do {
5049
    diff = ((*s1 >= L'A') && (*s1 <= L'Z') ? (*s1 - L'A' + L'a') : *s1)
5050
           - ((*s2 >= L'A') && (*s2 <= L'Z') ? (*s2 - L'A' + L'a') : *s2);
5051
    s1++;
5052
    s2++;
5053
  } while ((diff == 0) && (s1[-1] != L'\0'));
5054
5055
  return diff;
5056
}
5057
5058
5059
/* Encode 'path' which is assumed UTF-8 string, into UNICODE string.
5060
 * wbuf and wbuf_len is a target buffer and its length. */
5061
static void
5062
path_to_unicode(const struct mg_connection *conn,
5063
                const char *path,
5064
                wchar_t *wbuf,
5065
                size_t wbuf_len)
5066
{
5067
  char buf[UTF8_PATH_MAX], buf2[UTF8_PATH_MAX];
5068
  wchar_t wbuf2[UTF16_PATH_MAX + 1];
5069
  DWORD long_len, err;
5070
  int (*fcompare)(const wchar_t *, const wchar_t *) = mg_wcscasecmp;
5071
5072
  mg_strlcpy(buf, path, sizeof(buf));
5073
  change_slashes_to_backslashes(buf);
5074
5075
  /* Convert to Unicode and back. If doubly-converted string does not
5076
   * match the original, something is fishy, reject. */
5077
  memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
5078
  MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int)wbuf_len);
5079
  WideCharToMultiByte(
5080
      CP_UTF8, 0, wbuf, (int)wbuf_len, buf2, sizeof(buf2), NULL, NULL);
5081
  if (strcmp(buf, buf2) != 0) {
5082
    wbuf[0] = L'\0';
5083
  }
5084
5085
  /* Windows file systems are not case sensitive, but you can still use
5086
   * uppercase and lowercase letters (on all modern file systems).
5087
   * The server can check if the URI uses the same upper/lowercase
5088
   * letters an the file system, effectively making Windows servers
5089
   * case sensitive (like Linux servers are). It is still not possible
5090
   * to use two files with the same name in different cases on Windows
5091
   * (like /a and /A) - this would be possible in Linux.
5092
   * As a default, Windows is not case sensitive, but the case sensitive
5093
   * file name check can be activated by an additional configuration. */
5094
  if (conn) {
5095
    if (conn->dom_ctx->config[CASE_SENSITIVE_FILES]
5096
        && !mg_strcasecmp(conn->dom_ctx->config[CASE_SENSITIVE_FILES],
5097
                          "yes")) {
5098
      /* Use case sensitive compare function */
5099
      fcompare = wcscmp;
5100
    }
5101
  }
5102
  (void)conn; /* conn is currently unused */
5103
5104
  /* Only accept a full file path, not a Windows short (8.3) path. */
5105
  memset(wbuf2, 0, ARRAY_SIZE(wbuf2) * sizeof(wchar_t));
5106
  long_len = GetLongPathNameW(wbuf, wbuf2, ARRAY_SIZE(wbuf2) - 1);
5107
  if (long_len == 0) {
5108
    err = GetLastError();
5109
    if (err == ERROR_FILE_NOT_FOUND) {
5110
      /* File does not exist. This is not always a problem here. */
5111
      return;
5112
    }
5113
  }
5114
  if ((long_len >= ARRAY_SIZE(wbuf2)) || (fcompare(wbuf, wbuf2) != 0)) {
5115
    /* Short name is used. */
5116
    wbuf[0] = L'\0';
5117
  }
5118
}
5119
5120
5121
#if !defined(NO_FILESYSTEMS)
5122
/* Get file information, return 1 if file exists, 0 if not */
5123
static int
5124
mg_stat(const struct mg_connection *conn,
5125
        const char *path,
5126
        struct mg_file_stat *filep)
5127
{
5128
  wchar_t wbuf[UTF16_PATH_MAX];
5129
  WIN32_FILE_ATTRIBUTE_DATA info;
5130
  time_t creation_time;
5131
  size_t len;
5132
5133
  if (!filep) {
5134
    return 0;
5135
  }
5136
  memset(filep, 0, sizeof(*filep));
5137
5138
  if (mg_path_suspicious(conn, path)) {
5139
    return 0;
5140
  }
5141
5142
  path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
5143
  /* Windows happily opens files with some garbage at the end of file name.
5144
   * For example, fopen("a.cgi    ", "r") on Windows successfully opens
5145
   * "a.cgi", despite one would expect an error back. */
5146
  len = strlen(path);
5147
  if ((len > 0) && (path[len - 1] != ' ') && (path[len - 1] != '.')
5148
      && (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0)) {
5149
    filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
5150
    filep->last_modified =
5151
        SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime,
5152
                      info.ftLastWriteTime.dwHighDateTime);
5153
5154
    /* On Windows, the file creation time can be higher than the
5155
     * modification time, e.g. when a file is copied.
5156
     * Since the Last-Modified timestamp is used for caching
5157
     * it should be based on the most recent timestamp. */
5158
    creation_time = SYS2UNIX_TIME(info.ftCreationTime.dwLowDateTime,
5159
                                  info.ftCreationTime.dwHighDateTime);
5160
    if (creation_time > filep->last_modified) {
5161
      filep->last_modified = creation_time;
5162
    }
5163
5164
    filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
5165
    return 1;
5166
  }
5167
5168
  return 0;
5169
}
5170
#endif
5171
5172
5173
static int
5174
mg_remove(const struct mg_connection *conn, const char *path)
5175
{
5176
  wchar_t wbuf[UTF16_PATH_MAX];
5177
  path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
5178
  return DeleteFileW(wbuf) ? 0 : -1;
5179
}
5180
5181
5182
static int
5183
mg_mkdir(const struct mg_connection *conn, const char *path, int mode)
5184
{
5185
  wchar_t wbuf[UTF16_PATH_MAX];
5186
  (void)mode;
5187
  path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
5188
  return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
5189
}
5190
5191
5192
/* Create substitutes for POSIX functions in Win32. */
5193
5194
#if defined(GCC_DIAGNOSTIC)
5195
/* Show no warning in case system functions are not used. */
5196
#pragma GCC diagnostic push
5197
#pragma GCC diagnostic ignored "-Wunused-function"
5198
#endif
5199
5200
5201
/* Implementation of POSIX opendir/closedir/readdir for Windows. */
5202
FUNCTION_MAY_BE_UNUSED
5203
static DIR *
5204
mg_opendir(const struct mg_connection *conn, const char *name)
5205
{
5206
  DIR *dir = NULL;
5207
  wchar_t wpath[UTF16_PATH_MAX];
5208
  DWORD attrs;
5209
5210
  if (name == NULL) {
5211
    SetLastError(ERROR_BAD_ARGUMENTS);
5212
  } else if ((dir = (DIR *)mg_malloc(sizeof(*dir))) == NULL) {
5213
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5214
  } else {
5215
    path_to_unicode(conn, name, wpath, ARRAY_SIZE(wpath));
5216
    attrs = GetFileAttributesW(wpath);
5217
    if ((wcslen(wpath) + 2 < ARRAY_SIZE(wpath)) && (attrs != 0xFFFFFFFF)
5218
        && ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0)) {
5219
      (void)wcscat(wpath, L"\\*");
5220
      dir->handle = FindFirstFileW(wpath, &dir->info);
5221
      dir->result.d_name[0] = '\0';
5222
    } else {
5223
      mg_free(dir);
5224
      dir = NULL;
5225
    }
5226
  }
5227
5228
  return dir;
5229
}
5230
5231
5232
FUNCTION_MAY_BE_UNUSED
5233
static int
5234
mg_closedir(DIR *dir)
5235
{
5236
  int result = 0;
5237
5238
  if (dir != NULL) {
5239
    if (dir->handle != INVALID_HANDLE_VALUE)
5240
      result = FindClose(dir->handle) ? 0 : -1;
5241
5242
    mg_free(dir);
5243
  } else {
5244
    result = -1;
5245
    SetLastError(ERROR_BAD_ARGUMENTS);
5246
  }
5247
5248
  return result;
5249
}
5250
5251
5252
FUNCTION_MAY_BE_UNUSED
5253
static struct dirent *
5254
mg_readdir(DIR *dir)
5255
{
5256
  struct dirent *result = 0;
5257
5258
  if (dir) {
5259
    if (dir->handle != INVALID_HANDLE_VALUE) {
5260
      result = &dir->result;
5261
      (void)WideCharToMultiByte(CP_UTF8,
5262
                                0,
5263
                                dir->info.cFileName,
5264
                                -1,
5265
                                result->d_name,
5266
                                sizeof(result->d_name),
5267
                                NULL,
5268
                                NULL);
5269
5270
      if (!FindNextFileW(dir->handle, &dir->info)) {
5271
        (void)FindClose(dir->handle);
5272
        dir->handle = INVALID_HANDLE_VALUE;
5273
      }
5274
5275
    } else {
5276
      SetLastError(ERROR_FILE_NOT_FOUND);
5277
    }
5278
  } else {
5279
    SetLastError(ERROR_BAD_ARGUMENTS);
5280
  }
5281
5282
  return result;
5283
}
5284
5285
5286
#if !defined(HAVE_POLL)
5287
#undef POLLIN
5288
#undef POLLPRI
5289
#undef POLLOUT
5290
#undef POLLERR
5291
#define POLLIN (1)  /* Data ready - read will not block. */
5292
#define POLLPRI (2) /* Priority data ready. */
5293
#define POLLOUT (4) /* Send queue not full - write will not block. */
5294
#define POLLERR (8) /* Error event */
5295
5296
FUNCTION_MAY_BE_UNUSED
5297
static int
5298
poll(struct mg_pollfd *pfd, unsigned int n, int milliseconds)
5299
{
5300
  struct timeval tv;
5301
  fd_set rset;
5302
  fd_set wset;
5303
  fd_set eset;
5304
  unsigned int i;
5305
  int result;
5306
  SOCKET maxfd = 0;
5307
5308
  memset(&tv, 0, sizeof(tv));
5309
  tv.tv_sec = milliseconds / 1000;
5310
  tv.tv_usec = (milliseconds % 1000) * 1000;
5311
  FD_ZERO(&rset);
5312
  FD_ZERO(&wset);
5313
  FD_ZERO(&eset);
5314
5315
  for (i = 0; i < n; i++) {
5316
    if (pfd[i].events & (POLLIN | POLLOUT | POLLERR)) {
5317
      if (pfd[i].events & POLLIN) {
5318
        FD_SET(pfd[i].fd, &rset);
5319
      }
5320
      if (pfd[i].events & POLLOUT) {
5321
        FD_SET(pfd[i].fd, &wset);
5322
      }
5323
      /* Check for errors for any FD in the set */
5324
      FD_SET(pfd[i].fd, &eset);
5325
    }
5326
    pfd[i].revents = 0;
5327
5328
    if (pfd[i].fd > maxfd) {
5329
      maxfd = pfd[i].fd;
5330
    }
5331
  }
5332
5333
  if ((result = select((int)maxfd + 1, &rset, &wset, &eset, &tv)) > 0) {
5334
    for (i = 0; i < n; i++) {
5335
      if (FD_ISSET(pfd[i].fd, &rset)) {
5336
        pfd[i].revents |= POLLIN;
5337
      }
5338
      if (FD_ISSET(pfd[i].fd, &wset)) {
5339
        pfd[i].revents |= POLLOUT;
5340
      }
5341
      if (FD_ISSET(pfd[i].fd, &eset)) {
5342
        pfd[i].revents |= POLLERR;
5343
      }
5344
    }
5345
  }
5346
5347
  /* We should subtract the time used in select from remaining
5348
   * "milliseconds", in particular if called from mg_poll with a
5349
   * timeout quantum.
5350
   * Unfortunately, the remaining time is not stored in "tv" in all
5351
   * implementations, so the result in "tv" must be considered undefined.
5352
   * See http://man7.org/linux/man-pages/man2/select.2.html */
5353
5354
  return result;
5355
}
5356
#endif /* HAVE_POLL */
5357
5358
5359
#if defined(GCC_DIAGNOSTIC)
5360
/* Enable unused function warning again */
5361
#pragma GCC diagnostic pop
5362
#endif
5363
5364
5365
static void
5366
set_close_on_exec(SOCKET sock,
5367
                  const struct mg_connection *conn /* may be null */,
5368
                  struct mg_context *ctx /* may be null */)
5369
{
5370
  (void)conn; /* Unused. */
5371
  (void)ctx;
5372
5373
  (void)SetHandleInformation((HANDLE)(intptr_t)sock, HANDLE_FLAG_INHERIT, 0);
5374
}
5375
5376
5377
CIVETWEB_API int
5378
mg_start_thread(mg_thread_func_t f, void *p)
5379
{
5380
#if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5381
  /* Compile-time option to control stack size, e.g.
5382
   * -DUSE_STACK_SIZE=16384
5383
   */
5384
  return ((_beginthread((void(__cdecl *)(void *))f, USE_STACK_SIZE, p)
5385
           == ((uintptr_t)(-1L)))
5386
              ? -1
5387
              : 0);
5388
#else
5389
  return (
5390
      (_beginthread((void(__cdecl *)(void *))f, 0, p) == ((uintptr_t)(-1L)))
5391
          ? -1
5392
          : 0);
5393
#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */
5394
}
5395
5396
5397
/* Start a thread storing the thread context. */
5398
static int
5399
mg_start_thread_with_id(unsigned(__stdcall *f)(void *),
5400
                        void *p,
5401
                        pthread_t *threadidptr)
5402
{
5403
  uintptr_t uip;
5404
  HANDLE threadhandle;
5405
  int result = -1;
5406
5407
  uip = _beginthreadex(NULL, 0, f, p, 0, NULL);
5408
  threadhandle = (HANDLE)uip;
5409
  if ((uip != 0) && (threadidptr != NULL)) {
5410
    *threadidptr = threadhandle;
5411
    result = 0;
5412
  }
5413
5414
  return result;
5415
}
5416
5417
5418
/* Wait for a thread to finish. */
5419
static int
5420
mg_join_thread(pthread_t threadid)
5421
{
5422
  int result;
5423
  DWORD dwevent;
5424
5425
  result = -1;
5426
  dwevent = WaitForSingleObject(threadid, (DWORD)INFINITE);
5427
  if (dwevent == WAIT_FAILED) {
5428
    DEBUG_TRACE("WaitForSingleObject() failed, error %d", ERRNO);
5429
  } else {
5430
    if (dwevent == WAIT_OBJECT_0) {
5431
      CloseHandle(threadid);
5432
      result = 0;
5433
    }
5434
  }
5435
5436
  return result;
5437
}
5438
5439
#if !defined(NO_SSL_DL) && !defined(NO_SSL)
5440
/* If SSL is loaded dynamically, dlopen/dlclose is required. */
5441
/* Create substitutes for POSIX functions in Win32. */
5442
5443
#if defined(GCC_DIAGNOSTIC)
5444
/* Show no warning in case system functions are not used. */
5445
#pragma GCC diagnostic push
5446
#pragma GCC diagnostic ignored "-Wunused-function"
5447
#endif
5448
5449
5450
FUNCTION_MAY_BE_UNUSED
5451
static HANDLE
5452
dlopen(const char *dll_name, int flags)
5453
{
5454
  wchar_t wbuf[UTF16_PATH_MAX];
5455
  (void)flags;
5456
  path_to_unicode(NULL, dll_name, wbuf, ARRAY_SIZE(wbuf));
5457
  return LoadLibraryW(wbuf);
5458
}
5459
5460
5461
FUNCTION_MAY_BE_UNUSED
5462
static int
5463
dlclose(void *handle)
5464
{
5465
  int result;
5466
5467
  if (FreeLibrary((HMODULE)handle) != 0) {
5468
    result = 0;
5469
  } else {
5470
    result = -1;
5471
  }
5472
5473
  return result;
5474
}
5475
5476
5477
#if defined(GCC_DIAGNOSTIC)
5478
/* Enable unused function warning again */
5479
#pragma GCC diagnostic pop
5480
#endif
5481
5482
#endif
5483
5484
5485
#if !defined(NO_CGI)
5486
#define SIGKILL (0)
5487
5488
5489
static int
5490
kill(pid_t pid, int sig_num)
5491
{
5492
  (void)TerminateProcess((HANDLE)pid, (UINT)sig_num);
5493
  (void)CloseHandle((HANDLE)pid);
5494
  return 0;
5495
}
5496
5497
5498
#if !defined(WNOHANG)
5499
#define WNOHANG (1)
5500
#endif
5501
5502
5503
static pid_t
5504
waitpid(pid_t pid, int *status, int flags)
5505
{
5506
  DWORD timeout = INFINITE;
5507
  DWORD waitres;
5508
5509
  (void)status; /* Currently not used by any client here */
5510
5511
  if ((flags | WNOHANG) == WNOHANG) {
5512
    timeout = 0;
5513
  }
5514
5515
  waitres = WaitForSingleObject((HANDLE)pid, timeout);
5516
  if (waitres == WAIT_OBJECT_0) {
5517
    return pid;
5518
  }
5519
  if (waitres == WAIT_TIMEOUT) {
5520
    return 0;
5521
  }
5522
  return (pid_t)-1;
5523
}
5524
5525
5526
static void
5527
trim_trailing_whitespaces(char *s)
5528
{
5529
  char *e = s + strlen(s);
5530
  while ((e > s) && isspace((unsigned char)e[-1])) {
5531
    *(--e) = '\0';
5532
  }
5533
}
5534
5535
5536
static pid_t
5537
spawn_process(struct mg_connection *conn,
5538
              const char *prog,
5539
              char *envblk,
5540
              char *envp[],
5541
              int fdin[2],
5542
              int fdout[2],
5543
              int fderr[2],
5544
              const char *dir,
5545
              int cgi_config_idx)
5546
{
5547
  HANDLE me;
5548
  char *interp;
5549
  char *interp_arg = 0;
5550
  char full_dir[UTF8_PATH_MAX], cmdline[UTF8_PATH_MAX], buf[UTF8_PATH_MAX];
5551
  int truncated;
5552
  struct mg_file file = STRUCT_FILE_INITIALIZER;
5553
  STARTUPINFOA si;
5554
  PROCESS_INFORMATION pi = {0};
5555
5556
  (void)envp;
5557
5558
  memset(&si, 0, sizeof(si));
5559
  si.cb = sizeof(si);
5560
5561
  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
5562
  si.wShowWindow = SW_HIDE;
5563
5564
  me = GetCurrentProcess();
5565
  DuplicateHandle(me,
5566
                  (HANDLE)_get_osfhandle(fdin[0]),
5567
                  me,
5568
                  &si.hStdInput,
5569
                  0,
5570
                  TRUE,
5571
                  DUPLICATE_SAME_ACCESS);
5572
  DuplicateHandle(me,
5573
                  (HANDLE)_get_osfhandle(fdout[1]),
5574
                  me,
5575
                  &si.hStdOutput,
5576
                  0,
5577
                  TRUE,
5578
                  DUPLICATE_SAME_ACCESS);
5579
  DuplicateHandle(me,
5580
                  (HANDLE)_get_osfhandle(fderr[1]),
5581
                  me,
5582
                  &si.hStdError,
5583
                  0,
5584
                  TRUE,
5585
                  DUPLICATE_SAME_ACCESS);
5586
5587
  /* Mark handles that should not be inherited. See
5588
   * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx
5589
   */
5590
  SetHandleInformation((HANDLE)_get_osfhandle(fdin[1]),
5591
                       HANDLE_FLAG_INHERIT,
5592
                       0);
5593
  SetHandleInformation((HANDLE)_get_osfhandle(fdout[0]),
5594
                       HANDLE_FLAG_INHERIT,
5595
                       0);
5596
  SetHandleInformation((HANDLE)_get_osfhandle(fderr[0]),
5597
                       HANDLE_FLAG_INHERIT,
5598
                       0);
5599
5600
  /* First check, if there is a CGI interpreter configured for all CGI
5601
   * scripts. */
5602
  interp = conn->dom_ctx->config[CGI_INTERPRETER + cgi_config_idx];
5603
  if (interp != NULL) {
5604
    /* If there is a configured interpreter, check for additional arguments
5605
     */
5606
    interp_arg =
5607
        conn->dom_ctx->config[CGI_INTERPRETER_ARGS + cgi_config_idx];
5608
  } else {
5609
    /* Otherwise, the interpreter must be stated in the first line of the
5610
     * CGI script file, after a #! (shebang) mark. */
5611
    buf[0] = buf[1] = '\0';
5612
5613
    /* Get the full script path */
5614
    mg_snprintf(
5615
        conn, &truncated, cmdline, sizeof(cmdline), "%s/%s", dir, prog);
5616
5617
    if (truncated) {
5618
      pi.hProcess = (pid_t)-1;
5619
      goto spawn_cleanup;
5620
    }
5621
5622
    /* Open the script file, to read the first line */
5623
    if (mg_fopen(conn, cmdline, MG_FOPEN_MODE_READ, &file)) {
5624
5625
      /* Read the first line of the script into the buffer */
5626
      mg_fgets(buf, sizeof(buf), &file);
5627
      (void)mg_fclose(&file.access); /* ignore error on read only file */
5628
      buf[sizeof(buf) - 1] = '\0';
5629
    }
5630
5631
    if ((buf[0] == '#') && (buf[1] == '!')) {
5632
      trim_trailing_whitespaces(buf + 2);
5633
    } else {
5634
      buf[2] = '\0';
5635
    }
5636
    interp = buf + 2;
5637
  }
5638
5639
  GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL);
5640
5641
  if (interp[0] != '\0') {
5642
    /* This is an interpreted script file. We must call the interpreter. */
5643
    if ((interp_arg != 0) && (interp_arg[0] != 0)) {
5644
      mg_snprintf(conn,
5645
                  &truncated,
5646
                  cmdline,
5647
                  sizeof(cmdline),
5648
                  "\"%s\" %s \"%s\\%s\"",
5649
                  interp,
5650
                  interp_arg,
5651
                  full_dir,
5652
                  prog);
5653
    } else {
5654
      mg_snprintf(conn,
5655
                  &truncated,
5656
                  cmdline,
5657
                  sizeof(cmdline),
5658
                  "\"%s\" \"%s\\%s\"",
5659
                  interp,
5660
                  full_dir,
5661
                  prog);
5662
    }
5663
  } else {
5664
    /* This is (probably) a compiled program. We call it directly. */
5665
    mg_snprintf(conn,
5666
                &truncated,
5667
                cmdline,
5668
                sizeof(cmdline),
5669
                "\"%s\\%s\"",
5670
                full_dir,
5671
                prog);
5672
  }
5673
5674
  if (truncated) {
5675
    pi.hProcess = (pid_t)-1;
5676
    goto spawn_cleanup;
5677
  }
5678
5679
  DEBUG_TRACE("Running [%s]", cmdline);
5680
  if (CreateProcessA(NULL,
5681
                     cmdline,
5682
                     NULL,
5683
                     NULL,
5684
                     TRUE,
5685
                     CREATE_NEW_PROCESS_GROUP,
5686
                     envblk,
5687
                     NULL,
5688
                     &si,
5689
                     &pi)
5690
      == 0) {
5691
    mg_cry_internal(
5692
        conn, "%s: CreateProcess(%s): %ld", __func__, cmdline, (long)ERRNO);
5693
    pi.hProcess = (pid_t)-1;
5694
    /* goto spawn_cleanup; */
5695
  }
5696
5697
spawn_cleanup:
5698
  (void)CloseHandle(si.hStdOutput);
5699
  (void)CloseHandle(si.hStdError);
5700
  (void)CloseHandle(si.hStdInput);
5701
  if (pi.hThread != NULL) {
5702
    (void)CloseHandle(pi.hThread);
5703
  }
5704
5705
  return (pid_t)pi.hProcess;
5706
}
5707
#endif /* !NO_CGI */
5708
5709
5710
static int
5711
set_blocking_mode(SOCKET sock)
5712
{
5713
  unsigned long non_blocking = 0;
5714
  return ioctlsocket(sock, (long)FIONBIO, &non_blocking);
5715
}
5716
5717
5718
static int
5719
set_non_blocking_mode(SOCKET sock)
5720
{
5721
  unsigned long non_blocking = 1;
5722
  return ioctlsocket(sock, (long)FIONBIO, &non_blocking);
5723
}
5724
5725
5726
#else
5727
5728
5729
#if !defined(NO_FILESYSTEMS)
5730
static int
5731
mg_stat(const struct mg_connection *conn,
5732
        const char *path,
5733
        struct mg_file_stat *filep)
5734
0
{
5735
0
  struct stat st;
5736
0
  if (!filep) {
5737
0
    return 0;
5738
0
  }
5739
0
  memset(filep, 0, sizeof(*filep));
5740
5741
0
  if (mg_path_suspicious(conn, path)) {
5742
0
    return 0;
5743
0
  }
5744
5745
0
  if (0 == stat(path, &st)) {
5746
0
    filep->size = (uint64_t)(st.st_size);
5747
0
    filep->last_modified = st.st_mtime;
5748
0
    filep->is_directory = S_ISDIR(st.st_mode);
5749
0
    return 1;
5750
0
  }
5751
5752
0
  return 0;
5753
0
}
5754
#endif /* NO_FILESYSTEMS */
5755
5756
5757
static void
5758
set_close_on_exec(int fd,
5759
                  const struct mg_connection *conn /* may be null */,
5760
                  struct mg_context *ctx /* may be null */)
5761
40
{
5762
#if defined(__ZEPHYR__)
5763
  (void)fd;
5764
  (void)conn;
5765
  (void)ctx;
5766
#else
5767
40
  if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
5768
0
    if (conn || ctx) {
5769
0
      struct mg_connection fc;
5770
0
      mg_cry_internal((conn ? conn : fake_connection(&fc, ctx)),
5771
0
                      "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
5772
0
                      __func__,
5773
0
                      strerror(ERRNO));
5774
0
    }
5775
0
  }
5776
40
#endif
5777
40
}
5778
5779
5780
CIVETWEB_API int
5781
mg_start_thread(mg_thread_func_t func, void *param)
5782
0
{
5783
0
  pthread_t thread_id;
5784
0
  pthread_attr_t attr;
5785
0
  int result;
5786
5787
0
  (void)pthread_attr_init(&attr);
5788
0
  (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5789
5790
#if defined(__ZEPHYR__)
5791
  pthread_attr_setstack(&attr, &civetweb_main_stack, ZEPHYR_STACK_SIZE);
5792
#elif defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5793
  /* Compile-time option to control stack size,
5794
   * e.g. -DUSE_STACK_SIZE=16384 */
5795
0
  (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
5796
0
#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */
5797
5798
0
  result = pthread_create(&thread_id, &attr, func, param);
5799
0
  pthread_attr_destroy(&attr);
5800
5801
0
  return result;
5802
0
}
5803
5804
5805
/* Start a thread storing the thread context. */
5806
static int
5807
mg_start_thread_with_id(mg_thread_func_t func,
5808
                        void *param,
5809
                        pthread_t *threadidptr)
5810
2
{
5811
2
  pthread_t thread_id;
5812
2
  pthread_attr_t attr;
5813
2
  int result;
5814
5815
2
  (void)pthread_attr_init(&attr);
5816
5817
#if defined(__ZEPHYR__)
5818
  pthread_attr_setstack(&attr,
5819
                        &civetweb_worker_stacks[zephyr_worker_stack_index++],
5820
                        ZEPHYR_STACK_SIZE);
5821
#elif defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5822
  /* Compile-time option to control stack size,
5823
   * e.g. -DUSE_STACK_SIZE=16384 */
5824
2
  (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
5825
2
#endif /* defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 */
5826
5827
2
  result = pthread_create(&thread_id, &attr, func, param);
5828
2
  pthread_attr_destroy(&attr);
5829
2
  if ((result == 0) && (threadidptr != NULL)) {
5830
2
    *threadidptr = thread_id;
5831
2
  }
5832
2
  return result;
5833
2
}
5834
5835
5836
/* Wait for a thread to finish. */
5837
static int
5838
mg_join_thread(pthread_t threadid)
5839
2
{
5840
2
  int result;
5841
5842
2
  result = pthread_join(threadid, NULL);
5843
2
  return result;
5844
2
}
5845
5846
5847
#if !defined(NO_CGI)
5848
static pid_t
5849
spawn_process(struct mg_connection *conn,
5850
              const char *prog,
5851
              char *envblk,
5852
              char *envp[],
5853
              int fdin[2],
5854
              int fdout[2],
5855
              int fderr[2],
5856
              const char *dir,
5857
              int cgi_config_idx)
5858
0
{
5859
0
  pid_t pid;
5860
0
  const char *interp;
5861
5862
0
  (void)envblk;
5863
5864
0
  if ((pid = fork()) == -1) {
5865
    /* Parent */
5866
0
    mg_cry_internal(conn, "%s: fork(): %s", __func__, strerror(ERRNO));
5867
0
  } else if (pid != 0) {
5868
    /* Make sure children close parent-side descriptors.
5869
     * The caller will close the child-side immediately. */
5870
0
    set_close_on_exec(fdin[1], conn, NULL);  /* stdin write */
5871
0
    set_close_on_exec(fdout[0], conn, NULL); /* stdout read */
5872
0
    set_close_on_exec(fderr[0], conn, NULL); /* stderr read */
5873
0
  } else {
5874
    /* Child */
5875
0
    if (chdir(dir) != 0) {
5876
0
      mg_cry_internal(
5877
0
          conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
5878
0
    } else if (dup2(fdin[0], 0) == -1) {
5879
0
      mg_cry_internal(conn,
5880
0
                      "%s: dup2(%d, 0): %s",
5881
0
                      __func__,
5882
0
                      fdin[0],
5883
0
                      strerror(ERRNO));
5884
0
    } else if (dup2(fdout[1], 1) == -1) {
5885
0
      mg_cry_internal(conn,
5886
0
                      "%s: dup2(%d, 1): %s",
5887
0
                      __func__,
5888
0
                      fdout[1],
5889
0
                      strerror(ERRNO));
5890
0
    } else if (dup2(fderr[1], 2) == -1) {
5891
0
      mg_cry_internal(conn,
5892
0
                      "%s: dup2(%d, 2): %s",
5893
0
                      __func__,
5894
0
                      fderr[1],
5895
0
                      strerror(ERRNO));
5896
0
    } else {
5897
0
      struct sigaction sa;
5898
5899
      /* Keep stderr and stdout in two different pipes.
5900
       * Stdout will be sent back to the client,
5901
       * stderr should go into a server error log. */
5902
0
      (void)close(fdin[0]);
5903
0
      (void)close(fdout[1]);
5904
0
      (void)close(fderr[1]);
5905
5906
      /* Close write end fdin and read end fdout and fderr */
5907
0
      (void)close(fdin[1]);
5908
0
      (void)close(fdout[0]);
5909
0
      (void)close(fderr[0]);
5910
5911
      /* After exec, all signal handlers are restored to their default
5912
       * values, with one exception of SIGCHLD. According to
5913
       * POSIX.1-2001 and Linux's implementation, SIGCHLD's handler
5914
       * will leave unchanged after exec if it was set to be ignored.
5915
       * Restore it to default action. */
5916
0
      memset(&sa, 0, sizeof(sa));
5917
0
      sa.sa_handler = SIG_DFL;
5918
0
      sigaction(SIGCHLD, &sa, NULL);
5919
5920
0
      interp = conn->dom_ctx->config[CGI_INTERPRETER + cgi_config_idx];
5921
0
      if (interp == NULL) {
5922
        /* no interpreter configured, call the program directly */
5923
0
        (void)execle(prog, prog, NULL, envp);
5924
0
        mg_cry_internal(conn,
5925
0
                        "%s: execle(%s): %s",
5926
0
                        __func__,
5927
0
                        prog,
5928
0
                        strerror(ERRNO));
5929
0
      } else {
5930
        /* call the configured interpreter */
5931
0
        const char *interp_args =
5932
0
            conn->dom_ctx
5933
0
                ->config[CGI_INTERPRETER_ARGS + cgi_config_idx];
5934
5935
0
        if ((interp_args != NULL) && (interp_args[0] != 0)) {
5936
0
          (void)execle(interp, interp, interp_args, prog, NULL, envp);
5937
0
        } else {
5938
0
          (void)execle(interp, interp, prog, NULL, envp);
5939
0
        }
5940
0
        mg_cry_internal(conn,
5941
0
                        "%s: execle(%s %s): %s",
5942
0
                        __func__,
5943
0
                        interp,
5944
0
                        prog,
5945
0
                        strerror(ERRNO));
5946
0
      }
5947
0
    }
5948
0
    exit(EXIT_FAILURE);
5949
0
  }
5950
5951
0
  return pid;
5952
0
}
5953
#endif /* !NO_CGI */
5954
5955
5956
static int
5957
set_non_blocking_mode(SOCKET sock)
5958
34
{
5959
34
  int flags = fcntl(sock, F_GETFL, 0);
5960
34
  if (flags < 0) {
5961
0
    return -1;
5962
0
  }
5963
5964
34
  if (fcntl(sock, F_SETFL, (flags | O_NONBLOCK)) < 0) {
5965
0
    return -1;
5966
0
  }
5967
34
  return 0;
5968
34
}
5969
5970
static int
5971
set_blocking_mode(SOCKET sock)
5972
34
{
5973
34
  int flags = fcntl(sock, F_GETFL, 0);
5974
34
  if (flags < 0) {
5975
0
    return -1;
5976
0
  }
5977
5978
34
  if (fcntl(sock, F_SETFL, flags & (~(int)(O_NONBLOCK))) < 0) {
5979
0
    return -1;
5980
0
  }
5981
34
  return 0;
5982
34
}
5983
#endif /* _WIN32 / else */
5984
5985
/* End of initial operating system specific define block. */
5986
5987
5988
/* Get a random number (independent of C rand function) */
5989
static uint64_t
5990
get_random(void)
5991
2
{
5992
2
  static uint64_t lfsr = 0; /* Linear feedback shift register */
5993
2
  static uint64_t lcg = 0;  /* Linear congruential generator */
5994
2
  uint64_t now = mg_get_current_time_ns();
5995
5996
2
  if (lfsr == 0) {
5997
    /* lfsr will be only 0 if has not been initialized,
5998
     * so this code is called only once. */
5999
2
    lfsr = mg_get_current_time_ns();
6000
2
    lcg = mg_get_current_time_ns();
6001
2
  } else {
6002
    /* Get the next step of both random number generators. */
6003
0
    lfsr = (lfsr >> 1)
6004
0
           | ((((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1)
6005
0
              << 63);
6006
0
    lcg = lcg * 6364136223846793005LL + 1442695040888963407LL;
6007
0
  }
6008
6009
  /* Combining two pseudo-random number generators and a high resolution
6010
   * part
6011
   * of the current server time will make it hard (impossible?) to guess
6012
   * the
6013
   * next number. */
6014
2
  return (lfsr ^ lcg ^ now);
6015
2
}
6016
6017
6018
static int
6019
mg_poll(struct mg_pollfd *pfd,
6020
        unsigned int n,
6021
        int milliseconds,
6022
        const stop_flag_t *stop_flag)
6023
74
{
6024
  /* Call poll, but only for a maximum time of a few seconds.
6025
   * This will allow to stop the server after some seconds, instead
6026
   * of having to wait for a long socket timeout. */
6027
74
  int ms_now = SOCKET_TIMEOUT_QUANTUM; /* Sleep quantum in ms */
6028
6029
74
  int check_pollerr = 0;
6030
74
  if ((n == 1) && ((pfd[0].events & POLLERR) == 0)) {
6031
    /* If we wait for only one file descriptor, wait on error as well */
6032
34
    pfd[0].events |= POLLERR;
6033
34
    check_pollerr = 1;
6034
34
  }
6035
6036
74
  do {
6037
74
    int result;
6038
6039
74
    if (!STOP_FLAG_IS_ZERO(&*stop_flag)) {
6040
      /* Shut down signal */
6041
0
      return -2;
6042
0
    }
6043
6044
74
    if ((milliseconds >= 0) && (milliseconds < ms_now)) {
6045
34
      ms_now = milliseconds;
6046
34
    }
6047
6048
74
    result = poll(pfd, n, ms_now);
6049
74
    if (result != 0) {
6050
70
      int err = ERRNO;
6051
70
      if ((result == 1) || (!ERROR_TRY_AGAIN(err))) {
6052
        /* Poll returned either success (1) or error (-1).
6053
         * Forward both to the caller. */
6054
70
        if ((check_pollerr)
6055
70
            && ((pfd[0].revents & (POLLIN | POLLOUT | POLLERR))
6056
34
                == POLLERR)) {
6057
          /* One and only file descriptor returned error */
6058
0
          return -1;
6059
0
        }
6060
70
        return result;
6061
70
      }
6062
70
    }
6063
6064
    /* Poll returned timeout (0). */
6065
4
    if (milliseconds > 0) {
6066
4
      milliseconds -= ms_now;
6067
4
    }
6068
6069
4
  } while (milliseconds > 0);
6070
6071
  /* timeout: return 0 */
6072
4
  return 0;
6073
74
}
6074
6075
6076
/* Write data to the IO channel - opened file descriptor, socket or SSL
6077
 * descriptor.
6078
 * Return value:
6079
 *  >=0 .. number of bytes successfully written
6080
 *   -1 .. timeout
6081
 *   -2 .. error
6082
 */
6083
static int
6084
push_inner(struct mg_context *ctx,
6085
           FILE *fp,
6086
           SOCKET sock,
6087
           SSL *ssl,
6088
           const char *buf,
6089
           int len,
6090
           double timeout)
6091
34
{
6092
34
  uint64_t start = 0, now = 0, timeout_ns = 0;
6093
34
  int n, err;
6094
34
  unsigned ms_wait = SOCKET_TIMEOUT_QUANTUM; /* Sleep quantum in ms */
6095
6096
#if defined(_WIN32)
6097
  typedef int len_t;
6098
#else
6099
34
  typedef size_t len_t;
6100
34
#endif
6101
6102
34
  if (timeout > 0) {
6103
34
    now = mg_get_current_time_ns();
6104
34
    start = now;
6105
34
    timeout_ns = (uint64_t)(timeout * 1.0E9);
6106
34
  }
6107
6108
34
  if (ctx == NULL) {
6109
0
    return -2;
6110
0
  }
6111
6112
#if defined(NO_SSL) && !defined(USE_MBEDTLS)
6113
  if (ssl) {
6114
    return -2;
6115
  }
6116
#endif
6117
6118
  /* Try to read until it succeeds, fails, times out, or the server
6119
   * shuts down. */
6120
34
  for (;;) {
6121
6122
#if defined(USE_MBEDTLS)
6123
    if (ssl != NULL) {
6124
      n = mbed_ssl_write(ssl, (const unsigned char *)buf, len);
6125
      if (n <= 0) {
6126
        if ((n == MBEDTLS_ERR_SSL_WANT_READ)
6127
            || (n == MBEDTLS_ERR_SSL_WANT_WRITE)
6128
            || n == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) {
6129
          n = 0;
6130
        } else {
6131
          fprintf(stderr, "SSL write failed, error %d\n", n);
6132
          return -2;
6133
        }
6134
      } else {
6135
        err = 0;
6136
      }
6137
    } else
6138
#elif !defined(NO_SSL)
6139
34
    if (ssl != NULL) {
6140
0
      ERR_clear_error();
6141
0
      n = SSL_write(ssl, buf, len);
6142
0
      if (n <= 0) {
6143
0
        err = SSL_get_error(ssl, n);
6144
0
        if ((err == SSL_ERROR_SYSCALL) && (n == -1)) {
6145
0
          err = ERRNO;
6146
0
        } else if ((err == SSL_ERROR_WANT_READ)
6147
0
                   || (err == SSL_ERROR_WANT_WRITE)) {
6148
0
          n = 0;
6149
0
        } else {
6150
0
          DEBUG_TRACE("SSL_write() failed, error %d", err);
6151
0
          ERR_clear_error();
6152
0
          return -2;
6153
0
        }
6154
0
        ERR_clear_error();
6155
0
      } else {
6156
0
        err = 0;
6157
0
      }
6158
0
    } else
6159
34
#endif
6160
6161
34
        if (fp != NULL) {
6162
0
      n = (int)fwrite(buf, 1, (size_t)len, fp);
6163
0
      if (ferror(fp)) {
6164
0
        n = -1;
6165
0
        err = ERRNO;
6166
0
      } else {
6167
0
        err = 0;
6168
0
      }
6169
34
    } else {
6170
34
      n = (int)send(sock, buf, (len_t)len, MSG_NOSIGNAL);
6171
34
      err = (n < 0) ? ERRNO : 0;
6172
34
      if (ERROR_TRY_AGAIN(err)) {
6173
0
        err = 0;
6174
0
        n = 0;
6175
0
      }
6176
34
      if (n < 0) {
6177
        /* shutdown of the socket at client side */
6178
0
        return -2;
6179
0
      }
6180
34
    }
6181
6182
34
    if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
6183
0
      return -2;
6184
0
    }
6185
6186
34
    if ((n > 0) || ((n == 0) && (len == 0))) {
6187
      /* some data has been read, or no data was requested */
6188
34
      return n;
6189
34
    }
6190
0
    if (n < 0) {
6191
      /* socket error - check errno */
6192
0
      DEBUG_TRACE("send() failed, error %d", err);
6193
6194
      /* TODO (mid): error handling depending on the error code.
6195
       * These codes are different between Windows and Linux.
6196
       * Currently there is no problem with failing send calls,
6197
       * if there is a reproducible situation, it should be
6198
       * investigated in detail.
6199
       */
6200
0
      return -2;
6201
0
    }
6202
6203
    /* Only in case n=0 (timeout), repeat calling the write function */
6204
6205
    /* If send failed, wait before retry */
6206
0
    if (fp != NULL) {
6207
      /* For files, just wait a fixed time.
6208
       * Maybe it helps, maybe not. */
6209
0
      mg_sleep(5);
6210
0
    } else {
6211
      /* For sockets, wait for the socket using poll */
6212
0
      struct mg_pollfd pfd[2];
6213
0
      int pollres;
6214
6215
0
      pfd[0].fd = sock;
6216
0
      pfd[0].events = POLLOUT;
6217
6218
0
      pfd[1].fd = ctx->thread_shutdown_notification_socket;
6219
0
      pfd[1].events = POLLIN;
6220
0
      pollres = mg_poll(pfd, 2, (int)(ms_wait), &(ctx->stop_flag));
6221
0
      if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
6222
0
        return -2;
6223
0
      }
6224
0
      if (pollres > 0) {
6225
0
        continue;
6226
0
      }
6227
0
    }
6228
6229
0
    if (timeout > 0) {
6230
0
      now = mg_get_current_time_ns();
6231
0
      if ((now - start) > timeout_ns) {
6232
        /* Timeout */
6233
0
        break;
6234
0
      }
6235
0
    }
6236
0
  }
6237
6238
0
  (void)err; /* Avoid unused warning if NO_SSL is set and DEBUG_TRACE is not
6239
             used */
6240
6241
0
  return -1;
6242
34
}
6243
6244
6245
static int
6246
push_all(struct mg_context *ctx,
6247
         FILE *fp,
6248
         SOCKET sock,
6249
         SSL *ssl,
6250
         const char *buf,
6251
         int len)
6252
34
{
6253
34
  double timeout = -1.0;
6254
34
  int n, nwritten = 0;
6255
6256
34
  if (ctx == NULL) {
6257
0
    return -1;
6258
0
  }
6259
6260
34
  if (ctx->dd.config[REQUEST_TIMEOUT]) {
6261
0
    timeout = atoi(ctx->dd.config[REQUEST_TIMEOUT]) / 1000.0;
6262
0
  }
6263
34
  if (timeout <= 0.0) {
6264
34
    timeout = strtod(config_options[REQUEST_TIMEOUT].default_value, NULL)
6265
34
              / 1000.0;
6266
34
  }
6267
6268
68
  while ((len > 0) && STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
6269
34
    n = push_inner(ctx, fp, sock, ssl, buf + nwritten, len, timeout);
6270
34
    if (n < 0) {
6271
0
      if (nwritten == 0) {
6272
0
        nwritten = -1; /* Propagate the error */
6273
0
      }
6274
0
      break;
6275
34
    } else if (n == 0) {
6276
0
      break; /* No more data to write */
6277
34
    } else {
6278
34
      nwritten += n;
6279
34
      len -= n;
6280
34
    }
6281
34
  }
6282
6283
34
  return nwritten;
6284
34
}
6285
6286
6287
/* Read from IO channel - opened file descriptor, socket, or SSL descriptor.
6288
 * Return value:
6289
 *  >=0 .. number of bytes successfully read
6290
 *   -1 .. timeout
6291
 *   -2 .. error
6292
 */
6293
static int
6294
pull_inner(FILE *fp,
6295
           struct mg_connection *conn,
6296
           char *buf,
6297
           int len,
6298
           double timeout)
6299
34
{
6300
34
  int nread, err = 0;
6301
6302
#if defined(_WIN32)
6303
  typedef int len_t;
6304
#else
6305
34
  typedef size_t len_t;
6306
34
#endif
6307
6308
  /* We need an additional wait loop around this, because in some cases
6309
   * with TLSwe may get data from the socket but not from SSL_read.
6310
   * In this case we need to repeat at least once.
6311
   */
6312
6313
34
  if (fp != NULL) {
6314
    /* Use read() instead of fread(), because if we're reading from the
6315
     * CGI pipe, fread() may block until IO buffer is filled up. We
6316
     * cannot afford to block and must pass all read bytes immediately
6317
     * to the client. */
6318
0
    nread = (int)read(fileno(fp), buf, (size_t)len);
6319
6320
0
    err = (nread < 0) ? ERRNO : 0;
6321
0
    if ((nread == 0) && (len > 0)) {
6322
      /* Should get data, but got EOL */
6323
0
      return -2;
6324
0
    }
6325
6326
#if defined(USE_MBEDTLS)
6327
  } else if (conn->ssl != NULL) {
6328
    struct mg_pollfd pfd[2];
6329
    int to_read;
6330
    int pollres;
6331
6332
    to_read = mbedtls_ssl_get_bytes_avail(conn->ssl);
6333
6334
    if (to_read > 0) {
6335
      /* We already know there is no more data buffered in conn->buf
6336
       * but there is more available in the SSL layer. So don't poll
6337
       * conn->client.sock yet. */
6338
6339
      pollres = 1;
6340
      if (to_read > len)
6341
        to_read = len;
6342
    } else {
6343
      pfd[0].fd = conn->client.sock;
6344
      pfd[0].events = POLLIN;
6345
6346
      pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket;
6347
      pfd[1].events = POLLIN;
6348
6349
      to_read = len;
6350
6351
      pollres = mg_poll(pfd,
6352
                        2,
6353
                        (int)(timeout * 1000.0),
6354
                        &(conn->phys_ctx->stop_flag));
6355
6356
      if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6357
        return -2;
6358
      }
6359
    }
6360
6361
    if (pollres > 0) {
6362
      nread = mbed_ssl_read(conn->ssl, (unsigned char *)buf, to_read);
6363
      if (nread <= 0) {
6364
        if ((nread == MBEDTLS_ERR_SSL_WANT_READ)
6365
            || (nread == MBEDTLS_ERR_SSL_WANT_WRITE)
6366
            || nread == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) {
6367
          nread = 0;
6368
        } else {
6369
          fprintf(stderr, "SSL read failed, error %d\n", nread);
6370
          return -2;
6371
        }
6372
      } else {
6373
        err = 0;
6374
      }
6375
6376
    } else if (pollres < 0) {
6377
      /* Error */
6378
      return -2;
6379
    } else {
6380
      /* pollres = 0 means timeout */
6381
      nread = 0;
6382
    }
6383
6384
#elif !defined(NO_SSL)
6385
34
  } else if (conn->ssl != NULL) {
6386
0
    int ssl_pending;
6387
0
    struct mg_pollfd pfd[2];
6388
0
    int pollres;
6389
6390
0
    if ((ssl_pending = SSL_pending(conn->ssl)) > 0) {
6391
      /* We already know there is no more data buffered in conn->buf
6392
       * but there is more available in the SSL layer. So don't poll
6393
       * conn->client.sock yet. */
6394
0
      if (ssl_pending > len) {
6395
0
        ssl_pending = len;
6396
0
      }
6397
0
      pollres = 1;
6398
0
    } else {
6399
0
      pfd[0].fd = conn->client.sock;
6400
0
      pfd[0].events = POLLIN;
6401
0
      pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket;
6402
0
      pfd[1].events = POLLIN;
6403
6404
0
      pollres = mg_poll(pfd,
6405
0
                        2,
6406
0
                        (int)(timeout * 1000.0),
6407
0
                        &(conn->phys_ctx->stop_flag));
6408
0
      if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6409
0
        return -2;
6410
0
      }
6411
0
    }
6412
0
    if (pollres > 0) {
6413
0
      ERR_clear_error();
6414
0
      nread =
6415
0
          SSL_read(conn->ssl, buf, (ssl_pending > 0) ? ssl_pending : len);
6416
0
      if (nread <= 0) {
6417
0
        err = SSL_get_error(conn->ssl, nread);
6418
0
        if ((err == SSL_ERROR_SYSCALL) && (nread == -1)) {
6419
0
          err = ERRNO;
6420
0
        } else if ((err == SSL_ERROR_WANT_READ)
6421
0
                   || (err == SSL_ERROR_WANT_WRITE)) {
6422
0
          nread = 0;
6423
0
        } else {
6424
          /* All errors should return -2 */
6425
0
          DEBUG_TRACE("SSL_read() failed, error %d", err);
6426
0
          ERR_clear_error();
6427
0
          return -2;
6428
0
        }
6429
0
        ERR_clear_error();
6430
0
      } else {
6431
0
        err = 0;
6432
0
      }
6433
0
    } else if (pollres < 0) {
6434
      /* Error */
6435
0
      return -2;
6436
0
    } else {
6437
      /* pollres = 0 means timeout */
6438
0
      nread = 0;
6439
0
    }
6440
0
#endif
6441
6442
34
  } else {
6443
34
    struct mg_pollfd pfd[2];
6444
34
    int pollres;
6445
6446
34
    pfd[0].fd = conn->client.sock;
6447
34
    pfd[0].events = POLLIN;
6448
6449
34
    pfd[1].fd = conn->phys_ctx->thread_shutdown_notification_socket;
6450
34
    pfd[1].events = POLLIN;
6451
6452
34
    pollres = mg_poll(pfd,
6453
34
                      2,
6454
34
                      (int)(timeout * 1000.0),
6455
34
                      &(conn->phys_ctx->stop_flag));
6456
34
    if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6457
0
      return -2;
6458
0
    }
6459
34
    if (pollres > 0) {
6460
34
      nread = (int)recv(conn->client.sock, buf, (len_t)len, 0);
6461
34
      err = (nread < 0) ? ERRNO : 0;
6462
34
      if (nread <= 0) {
6463
        /* shutdown of the socket at client side */
6464
34
        return -2;
6465
34
      }
6466
34
    } else if (pollres < 0) {
6467
      /* error calling poll */
6468
0
      return -2;
6469
0
    } else {
6470
      /* pollres = 0 means timeout */
6471
0
      nread = 0;
6472
0
    }
6473
34
  }
6474
6475
0
  if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6476
0
    return -2;
6477
0
  }
6478
6479
0
  if ((nread > 0) || ((nread == 0) && (len == 0))) {
6480
    /* some data has been read, or no data was requested */
6481
0
    return nread;
6482
0
  }
6483
6484
0
  if (nread < 0) {
6485
    /* socket error - check errno */
6486
#if defined(_WIN32)
6487
    if (err == WSAEWOULDBLOCK) {
6488
      /* TODO (low): check if this is still required */
6489
      /* standard case if called from close_socket_gracefully */
6490
      return -2;
6491
    } else if (err == WSAETIMEDOUT) {
6492
      /* TODO (low): check if this is still required */
6493
      /* timeout is handled by the while loop  */
6494
      return 0;
6495
    } else if (err == WSAECONNABORTED) {
6496
      /* See https://www.chilkatsoft.com/p/p_299.asp */
6497
      return -2;
6498
    } else {
6499
      DEBUG_TRACE("recv() failed, error %d", err);
6500
      return -2;
6501
    }
6502
#else
6503
    /* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases,
6504
     * if the timeout is reached and if the socket was set to non-
6505
     * blocking in close_socket_gracefully, so we can not distinguish
6506
     * here. We have to wait for the timeout in both cases for now.
6507
     */
6508
0
    if (ERROR_TRY_AGAIN(err)) {
6509
      /* TODO (low): check if this is still required */
6510
      /* EAGAIN/EWOULDBLOCK:
6511
       * standard case if called from close_socket_gracefully
6512
       * => should return -1 */
6513
      /* or timeout occurred
6514
       * => the code must stay in the while loop */
6515
6516
      /* EINTR can be generated on a socket with a timeout set even
6517
       * when SA_RESTART is effective for all relevant signals
6518
       * (see signal(7)).
6519
       * => stay in the while loop */
6520
0
    } else {
6521
0
      DEBUG_TRACE("recv() failed, error %d", err);
6522
0
      return -2;
6523
0
    }
6524
0
#endif
6525
0
  }
6526
6527
  /* Timeout occurred, but no data available. */
6528
0
  return -1;
6529
0
}
6530
6531
6532
static int
6533
pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
6534
0
{
6535
0
  int n, nread = 0;
6536
0
  double timeout = -1.0;
6537
0
  uint64_t start_time = 0, now = 0, timeout_ns = 0;
6538
6539
0
  if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
6540
0
    timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
6541
0
  }
6542
0
  if (timeout <= 0.0) {
6543
0
    timeout = strtod(config_options[REQUEST_TIMEOUT].default_value, NULL)
6544
0
              / 1000.0;
6545
0
  }
6546
0
  start_time = mg_get_current_time_ns();
6547
0
  timeout_ns = (uint64_t)(timeout * 1.0E9);
6548
6549
0
  while ((len > 0) && STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6550
0
    n = pull_inner(fp, conn, buf + nread, len, timeout);
6551
0
    if (n == -2) {
6552
0
      if (nread == 0) {
6553
0
        nread = -1; /* Propagate the error */
6554
0
      }
6555
0
      break;
6556
0
    } else if (n == -1) {
6557
      /* timeout */
6558
0
      if (timeout >= 0.0) {
6559
0
        now = mg_get_current_time_ns();
6560
0
        if ((now - start_time) <= timeout_ns) {
6561
0
          continue;
6562
0
        }
6563
0
      }
6564
0
      break;
6565
0
    } else if (n == 0) {
6566
0
      break; /* No more data to read */
6567
0
    } else {
6568
0
      nread += n;
6569
0
      len -= n;
6570
0
    }
6571
0
  }
6572
6573
0
  return nread;
6574
0
}
6575
6576
6577
static void
6578
discard_unread_request_data(struct mg_connection *conn)
6579
0
{
6580
0
  char buf[MG_BUF_LEN];
6581
6582
0
  while (mg_read(conn, buf, sizeof(buf)) > 0)
6583
0
    ;
6584
0
}
6585
6586
6587
static int
6588
mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
6589
0
{
6590
0
  int64_t content_len, n, buffered_len, nread;
6591
0
  int64_t len64 =
6592
0
      (int64_t)((len > INT_MAX) ? INT_MAX : len); /* since the return value is
6593
                                                   * int, we may not read more
6594
                                                   * bytes */
6595
0
  const char *body;
6596
6597
0
  if (conn == NULL) {
6598
0
    return 0;
6599
0
  }
6600
6601
  /* If Content-Length is not set for a response with body data,
6602
   * we do not know in advance how much data should be read. */
6603
0
  content_len = conn->content_len;
6604
0
  if (content_len < 0) {
6605
    /* The body data is completed when the connection is closed. */
6606
0
    content_len = INT64_MAX;
6607
0
  }
6608
6609
0
  nread = 0;
6610
0
  if (conn->consumed_content < content_len) {
6611
    /* Adjust number of bytes to read. */
6612
0
    int64_t left_to_read = content_len - conn->consumed_content;
6613
0
    if (left_to_read < len64) {
6614
      /* Do not read more than the total content length of the
6615
       * request.
6616
       */
6617
0
      len64 = left_to_read;
6618
0
    }
6619
6620
    /* Return buffered data */
6621
0
    buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len
6622
0
                   - conn->consumed_content;
6623
0
    if (buffered_len > 0) {
6624
0
      if (len64 < buffered_len) {
6625
0
        buffered_len = len64;
6626
0
      }
6627
0
      body = conn->buf + conn->request_len + conn->consumed_content;
6628
0
      memcpy(buf, body, (size_t)buffered_len);
6629
0
      len64 -= buffered_len;
6630
0
      conn->consumed_content += buffered_len;
6631
0
      nread += buffered_len;
6632
0
      buf = (char *)buf + buffered_len;
6633
0
    }
6634
6635
    /* We have returned all buffered data. Read new data from the remote
6636
     * socket.
6637
     */
6638
0
    if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) {
6639
0
      conn->consumed_content += n;
6640
0
      nread += n;
6641
0
    } else {
6642
0
      nread = ((nread > 0) ? nread : n);
6643
0
    }
6644
0
  }
6645
0
  return (int)nread;
6646
0
}
6647
6648
6649
/* Forward declarations */
6650
static void handle_request(struct mg_connection *);
6651
static void log_access(const struct mg_connection *);
6652
6653
6654
/* Handle request, update statistics and call access log */
6655
static void
6656
handle_request_stat_log(struct mg_connection *conn)
6657
0
{
6658
#if defined(USE_SERVER_STATS)
6659
  struct timespec tnow;
6660
  conn->conn_state = 4; /* processing */
6661
#endif
6662
6663
0
  handle_request(conn);
6664
6665
6666
#if defined(USE_SERVER_STATS)
6667
  conn->conn_state = 5; /* processed */
6668
6669
  clock_gettime(CLOCK_MONOTONIC, &tnow);
6670
  conn->processing_time = mg_difftimespec(&tnow, &(conn->req_time));
6671
6672
  mg_atomic_add64(&(conn->phys_ctx->total_data_read), conn->consumed_content);
6673
  mg_atomic_add64(&(conn->phys_ctx->total_data_written),
6674
                  conn->num_bytes_sent);
6675
#endif
6676
6677
0
  DEBUG_TRACE("%s", "handle_request done");
6678
6679
0
  if (conn->phys_ctx->callbacks.end_request != NULL) {
6680
0
    conn->phys_ctx->callbacks.end_request(conn, conn->status_code);
6681
0
    DEBUG_TRACE("%s", "end_request callback done");
6682
0
  }
6683
0
  log_access(conn);
6684
0
}
6685
6686
6687
#if defined(USE_HTTP2)
6688
#if defined(NO_SSL)
6689
#error "HTTP2 requires ALPN, ALPN requires SSL/TLS"
6690
#endif
6691
#define USE_ALPN
6692
#include "http2.inl"
6693
/* Not supported with HTTP/2 */
6694
#define HTTP1_only                                                             \
6695
  {                                                                          \
6696
    if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {                      \
6697
      http2_must_use_http1(conn);                                        \
6698
      DEBUG_TRACE("%s", "must use HTTP/1.x");                            \
6699
      return;                                                            \
6700
    }                                                                      \
6701
  }
6702
#else
6703
#define HTTP1_only
6704
#endif
6705
6706
6707
CIVETWEB_API int
6708
mg_read(struct mg_connection *conn, void *buf, size_t len)
6709
0
{
6710
0
  if (len > INT_MAX) {
6711
0
    len = INT_MAX;
6712
0
  }
6713
6714
0
  if (conn == NULL) {
6715
0
    return 0;
6716
0
  }
6717
6718
0
  if (conn->is_chunked) {
6719
0
    size_t all_read = 0;
6720
6721
0
    while (len > 0) {
6722
0
      if (conn->is_chunked >= 3) {
6723
        /* No more data left to read */
6724
0
        return 0;
6725
0
      }
6726
0
      if (conn->is_chunked != 1) {
6727
        /* Has error */
6728
0
        return -1;
6729
0
      }
6730
6731
0
      if (conn->consumed_content != conn->content_len) {
6732
        /* copy from the current chunk */
6733
0
        int read_ret = mg_read_inner(conn, (char *)buf + all_read, len);
6734
6735
0
        if (read_ret < 1) {
6736
          /* read error */
6737
0
          conn->is_chunked = 2;
6738
0
          return -1;
6739
0
        }
6740
6741
0
        all_read += (size_t)read_ret;
6742
0
        len -= (size_t)read_ret;
6743
6744
0
        if (conn->consumed_content == conn->content_len) {
6745
          /* Add data bytes in the current chunk have been read,
6746
           * so we are expecting \r\n now. */
6747
0
          char x[2];
6748
0
          conn->content_len += 2;
6749
0
          if ((mg_read_inner(conn, x, 2) != 2) || (x[0] != '\r')
6750
0
              || (x[1] != '\n')) {
6751
            /* Protocol violation */
6752
0
            conn->is_chunked = 2;
6753
0
            return -1;
6754
0
          }
6755
0
        }
6756
6757
0
      } else {
6758
        /* fetch a new chunk */
6759
0
        size_t i;
6760
0
        char lenbuf[64];
6761
0
        char *end = NULL;
6762
0
        unsigned long chunkSize = 0;
6763
6764
0
        for (i = 0; i < (sizeof(lenbuf) - 1); i++) {
6765
0
          conn->content_len++;
6766
0
          if (mg_read_inner(conn, lenbuf + i, 1) != 1) {
6767
0
            lenbuf[i] = 0;
6768
0
          }
6769
0
          if ((i > 0) && (lenbuf[i] == ';')) {
6770
            // chunk extension --> skip chars until next CR
6771
            //
6772
            // RFC 2616, 3.6.1 Chunked Transfer Coding
6773
            // (https://www.rfc-editor.org/rfc/rfc2616#page-25)
6774
            //
6775
            // chunk          = chunk-size [ chunk-extension ] CRLF
6776
            //                  chunk-data CRLF
6777
            // ...
6778
            // chunk-extension= *( ";" chunk-ext-name [ "="
6779
            // chunk-ext-val ] )
6780
0
            do
6781
0
              ++conn->content_len;
6782
0
            while (mg_read_inner(conn, lenbuf + i, 1) == 1
6783
0
                   && lenbuf[i] != '\r');
6784
0
          }
6785
0
          if ((i > 0) && (lenbuf[i] == '\r')
6786
0
              && (lenbuf[i - 1] != '\r')) {
6787
0
            continue;
6788
0
          }
6789
0
          if ((i > 1) && (lenbuf[i] == '\n')
6790
0
              && (lenbuf[i - 1] == '\r')) {
6791
0
            lenbuf[i + 1] = 0;
6792
0
            chunkSize = strtoul(lenbuf, &end, 16);
6793
0
            if (chunkSize == 0) {
6794
              /* regular end of content */
6795
0
              conn->is_chunked = 3;
6796
0
            }
6797
0
            break;
6798
0
          }
6799
0
          if (!isxdigit((unsigned char)lenbuf[i])) {
6800
            /* illegal character for chunk length */
6801
0
            conn->is_chunked = 2;
6802
0
            return -1;
6803
0
          }
6804
0
        }
6805
0
        if ((end == NULL) || (*end != '\r')) {
6806
          /* chunksize not set correctly */
6807
0
          conn->is_chunked = 2;
6808
0
          return -1;
6809
0
        }
6810
0
        if (conn->is_chunked == 3) {
6811
          /* try discarding trailer for keep-alive */
6812
6813
          // We found the last chunk (length 0) including the
6814
          // CRLF that terminates that chunk. Now follows a possibly
6815
          // empty trailer and a final CRLF.
6816
          //
6817
          // see RFC 2616, 3.6.1 Chunked Transfer Coding
6818
          // (https://www.rfc-editor.org/rfc/rfc2616#page-25)
6819
          //
6820
          // Chunked-Body   = *chunk
6821
          //                  last-chunk
6822
          //                  trailer
6823
          //                  CRLF
6824
          // ...
6825
          // last-chunk     = 1*("0") [ chunk-extension ] CRLF
6826
          // ...
6827
          // trailer        = *(entity-header CRLF)
6828
6829
0
          int crlf_count = 2; // one CRLF already determined
6830
6831
0
          while (crlf_count < 4 && conn->is_chunked == 3) {
6832
0
            ++conn->content_len;
6833
0
            if (mg_read_inner(conn, lenbuf, 1) == 1) {
6834
0
              if ((crlf_count == 0 || crlf_count == 2)) {
6835
0
                if (lenbuf[0] == '\r')
6836
0
                  ++crlf_count;
6837
0
                else
6838
0
                  crlf_count = 0;
6839
0
              } else {
6840
                // previous character was a CR
6841
                // --> next character must be LF
6842
6843
0
                if (lenbuf[0] == '\n')
6844
0
                  ++crlf_count;
6845
0
                else
6846
0
                  conn->is_chunked = 2;
6847
0
              }
6848
0
            } else
6849
              // premature end of trailer
6850
0
              conn->is_chunked = 2;
6851
0
          }
6852
6853
0
          if (conn->is_chunked == 2)
6854
0
            return -1;
6855
0
          else
6856
0
            conn->is_chunked = 4;
6857
6858
0
          break;
6859
0
        }
6860
6861
        /* append a new chunk */
6862
0
        conn->content_len += (int64_t)chunkSize;
6863
0
      }
6864
0
    }
6865
6866
0
    return (int)all_read;
6867
0
  }
6868
0
  return mg_read_inner(conn, buf, len);
6869
0
}
6870
6871
6872
CIVETWEB_API int
6873
mg_write(struct mg_connection *conn, const void *buf, size_t len)
6874
34
{
6875
34
  time_t now;
6876
34
  int n, total, allowed;
6877
6878
34
  if (conn == NULL) {
6879
0
    return 0;
6880
0
  }
6881
34
  if (len > INT_MAX) {
6882
0
    return -1;
6883
0
  }
6884
6885
  /* Mark connection as "data sent" */
6886
34
  conn->request_state = 10;
6887
#if defined(USE_HTTP2)
6888
  if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
6889
    http2_data_frame_head(conn, len, 0);
6890
  }
6891
#endif
6892
6893
34
  if (conn->throttle > 0) {
6894
0
    if ((now = time(NULL)) != conn->last_throttle_time) {
6895
0
      conn->last_throttle_time = now;
6896
0
      conn->last_throttle_bytes = 0;
6897
0
    }
6898
0
    allowed = conn->throttle - conn->last_throttle_bytes;
6899
0
    if (allowed > (int)len) {
6900
0
      allowed = (int)len;
6901
0
    }
6902
6903
0
    total = push_all(conn->phys_ctx,
6904
0
                     NULL,
6905
0
                     conn->client.sock,
6906
0
                     conn->ssl,
6907
0
                     (const char *)buf,
6908
0
                     allowed);
6909
6910
0
    if (total == allowed) {
6911
6912
0
      buf = (const char *)buf + total;
6913
0
      conn->last_throttle_bytes += total;
6914
0
      while ((total < (int)len)
6915
0
             && STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6916
0
        allowed = (conn->throttle > ((int)len - total))
6917
0
                      ? (int)len - total
6918
0
                      : conn->throttle;
6919
6920
0
        n = push_all(conn->phys_ctx,
6921
0
                     NULL,
6922
0
                     conn->client.sock,
6923
0
                     conn->ssl,
6924
0
                     (const char *)buf,
6925
0
                     allowed);
6926
6927
0
        if (n != allowed) {
6928
0
          break;
6929
0
        }
6930
0
        sleep(1);
6931
0
        conn->last_throttle_bytes = allowed;
6932
0
        conn->last_throttle_time = time(NULL);
6933
0
        buf = (const char *)buf + n;
6934
0
        total += n;
6935
0
      }
6936
0
    }
6937
34
  } else {
6938
34
    total = push_all(conn->phys_ctx,
6939
34
                     NULL,
6940
34
                     conn->client.sock,
6941
34
                     conn->ssl,
6942
34
                     (const char *)buf,
6943
34
                     (int)len);
6944
34
  }
6945
34
  if (total > 0) {
6946
34
    conn->num_bytes_sent += total;
6947
34
  }
6948
34
  return total;
6949
34
}
6950
6951
6952
/* Send a chunk, if "Transfer-Encoding: chunked" is used */
6953
CIVETWEB_API int
6954
mg_send_chunk(struct mg_connection *conn,
6955
              const char *chunk,
6956
              unsigned int chunk_len)
6957
0
{
6958
0
  char lenbuf[16];
6959
0
  size_t lenbuf_len;
6960
0
  int ret;
6961
0
  int t;
6962
6963
  /* First store the length information in a text buffer. */
6964
0
  sprintf(lenbuf, "%x\r\n", chunk_len);
6965
0
  lenbuf_len = strlen(lenbuf);
6966
6967
  /* Then send length information, chunk and terminating \r\n. */
6968
0
  ret = mg_write(conn, lenbuf, lenbuf_len);
6969
0
  if (ret != (int)lenbuf_len) {
6970
0
    return -1;
6971
0
  }
6972
0
  t = ret;
6973
6974
0
  ret = mg_write(conn, chunk, chunk_len);
6975
0
  if (ret != (int)chunk_len) {
6976
0
    return -1;
6977
0
  }
6978
0
  t += ret;
6979
6980
0
  ret = mg_write(conn, "\r\n", 2);
6981
0
  if (ret != 2) {
6982
0
    return -1;
6983
0
  }
6984
0
  t += ret;
6985
6986
0
  return t;
6987
0
}
6988
6989
6990
#if defined(GCC_DIAGNOSTIC)
6991
/* This block forwards format strings to printf implementations,
6992
 * so we need to disable the format-nonliteral warning. */
6993
#pragma GCC diagnostic push
6994
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
6995
#endif
6996
6997
6998
/* Alternative alloc_vprintf() for non-compliant C runtimes */
6999
static int
7000
alloc_vprintf2(char **buf, const char *fmt, va_list ap)
7001
0
{
7002
0
  va_list ap_copy;
7003
0
  size_t size = MG_BUF_LEN / 4;
7004
0
  int len = -1;
7005
7006
0
  *buf = NULL;
7007
0
  while (len < 0) {
7008
0
    if (*buf) {
7009
0
      mg_free(*buf);
7010
0
    }
7011
7012
0
    size *= 4;
7013
0
    *buf = (char *)mg_malloc(size);
7014
0
    if (!*buf) {
7015
0
      break;
7016
0
    }
7017
7018
0
    va_copy(ap_copy, ap);
7019
0
    len = vsnprintf_impl(*buf, size - 1, fmt, ap_copy);
7020
0
    va_end(ap_copy);
7021
0
    (*buf)[size - 1] = 0;
7022
0
  }
7023
7024
0
  return len;
7025
0
}
7026
7027
7028
/* Print message to buffer. If buffer is large enough to hold the message,
7029
 * return buffer. If buffer is to small, allocate large enough buffer on
7030
 * heap,
7031
 * and return allocated buffer. */
7032
static int
7033
alloc_vprintf(char **out_buf,
7034
              char *prealloc_buf,
7035
              size_t prealloc_size,
7036
              const char *fmt,
7037
              va_list ap)
7038
34
{
7039
34
  va_list ap_copy;
7040
34
  int len;
7041
7042
  /* Windows is not standard-compliant, and vsnprintf() returns -1 if
7043
   * buffer is too small. Also, older versions of msvcrt.dll do not have
7044
   * _vscprintf().  However, if size is 0, vsnprintf() behaves correctly.
7045
   * Therefore, we make two passes: on first pass, get required message
7046
   * length.
7047
   * On second pass, actually print the message. */
7048
34
  va_copy(ap_copy, ap);
7049
34
  len = vsnprintf_impl(NULL, 0, fmt, ap_copy);
7050
34
  va_end(ap_copy);
7051
7052
34
  if (len < 0) {
7053
    /* C runtime is not standard compliant, vsnprintf() returned -1.
7054
     * Switch to alternative code path that uses incremental
7055
     * allocations.
7056
     */
7057
0
    va_copy(ap_copy, ap);
7058
0
    len = alloc_vprintf2(out_buf, fmt, ap_copy);
7059
0
    va_end(ap_copy);
7060
7061
34
  } else if ((size_t)(len) >= prealloc_size) {
7062
    /* The pre-allocated buffer not large enough. */
7063
    /* Allocate a new buffer. */
7064
0
    *out_buf = (char *)mg_malloc((size_t)(len) + 1);
7065
0
    if (!*out_buf) {
7066
      /* Allocation failed. Return -1 as "out of memory" error. */
7067
0
      return -1;
7068
0
    }
7069
    /* Buffer allocation successful. Store the string there. */
7070
0
    va_copy(ap_copy, ap);
7071
0
    IGNORE_UNUSED_RESULT(
7072
0
        vsnprintf_impl(*out_buf, (size_t)(len) + 1, fmt, ap_copy));
7073
0
    va_end(ap_copy);
7074
7075
34
  } else {
7076
    /* The pre-allocated buffer is large enough.
7077
     * Use it to store the string and return the address. */
7078
34
    va_copy(ap_copy, ap);
7079
34
    IGNORE_UNUSED_RESULT(
7080
34
        vsnprintf_impl(prealloc_buf, prealloc_size, fmt, ap_copy));
7081
34
    va_end(ap_copy);
7082
34
    *out_buf = prealloc_buf;
7083
34
  }
7084
7085
34
  return len;
7086
34
}
7087
7088
7089
static int
7090
alloc_printf(char **out_buf, const char *fmt, ...)
7091
0
{
7092
0
  va_list ap;
7093
0
  int result;
7094
0
7095
0
  va_start(ap, fmt);
7096
0
  result = alloc_vprintf(out_buf, NULL, 0, fmt, ap);
7097
0
  va_end(ap);
7098
0
7099
0
  return result;
7100
0
}
7101
7102
7103
#if defined(GCC_DIAGNOSTIC)
7104
/* Enable format-nonliteral warning again. */
7105
#pragma GCC diagnostic pop
7106
#endif
7107
7108
7109
static int
7110
mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap)
7111
34
{
7112
34
  char mem[MG_BUF_LEN];
7113
34
  char *buf = NULL;
7114
34
  int len;
7115
7116
34
  if ((len = alloc_vprintf(&buf, mem, sizeof(mem), fmt, ap)) > 0) {
7117
34
    len = mg_write(conn, buf, (size_t)len);
7118
34
  }
7119
34
  if (buf != mem) {
7120
0
    mg_free(buf);
7121
0
  }
7122
7123
34
  return len;
7124
34
}
7125
7126
7127
CIVETWEB_API int
7128
mg_printf(struct mg_connection *conn, const char *fmt, ...)
7129
34
{
7130
34
  va_list ap;
7131
34
  int result;
7132
7133
34
  va_start(ap, fmt);
7134
34
  result = mg_vprintf(conn, fmt, ap);
7135
34
  va_end(ap);
7136
7137
34
  return result;
7138
34
}
7139
7140
7141
CIVETWEB_API int
7142
mg_url_decode(const char *src,
7143
              int src_len,
7144
              char *dst,
7145
              int dst_len,
7146
              int is_form_url_encoded)
7147
0
{
7148
0
  int i, j, a, b;
7149
0
#define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W'))
7150
7151
0
  for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) {
7152
0
    if ((i < src_len - 2) && (src[i] == '%')
7153
0
        && isxdigit((unsigned char)src[i + 1])
7154
0
        && isxdigit((unsigned char)src[i + 2])) {
7155
0
      a = tolower((unsigned char)src[i + 1]);
7156
0
      b = tolower((unsigned char)src[i + 2]);
7157
0
      dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b));
7158
0
      i += 2;
7159
0
    } else if (is_form_url_encoded && (src[i] == '+')) {
7160
0
      dst[j] = ' ';
7161
0
    } else {
7162
0
      dst[j] = src[i];
7163
0
    }
7164
0
  }
7165
7166
0
  dst[j] = '\0'; /* Null-terminate the destination */
7167
7168
0
  return (i >= src_len) ? j : -1;
7169
0
}
7170
7171
7172
/* form url decoding of an entire string */
7173
static void
7174
url_decode_in_place(char *buf)
7175
0
{
7176
0
  int len = (int)strlen(buf);
7177
0
  (void)mg_url_decode(buf, len, buf, len + 1, 1);
7178
0
}
7179
7180
7181
CIVETWEB_API int
7182
mg_get_var(const char *data,
7183
           size_t data_len,
7184
           const char *name,
7185
           char *dst,
7186
           size_t dst_len)
7187
0
{
7188
0
  return mg_get_var2(data, data_len, name, dst, dst_len, 0);
7189
0
}
7190
7191
7192
CIVETWEB_API int
7193
mg_get_var2(const char *data,
7194
            size_t data_len,
7195
            const char *name,
7196
            char *dst,
7197
            size_t dst_len,
7198
            size_t occurrence)
7199
0
{
7200
0
  const char *p, *e, *s;
7201
0
  size_t name_len;
7202
0
  int len;
7203
7204
0
  if ((dst == NULL) || (dst_len == 0)) {
7205
0
    len = -2;
7206
0
  } else if ((data == NULL) || (name == NULL) || (data_len == 0)) {
7207
0
    len = -1;
7208
0
    dst[0] = '\0';
7209
0
  } else {
7210
0
    name_len = strlen(name);
7211
0
    e = data + data_len;
7212
0
    len = -1;
7213
0
    dst[0] = '\0';
7214
7215
    /* data is "var1=val1&var2=val2...". Find variable first */
7216
0
    for (p = data; p + name_len < e; p++) {
7217
0
      if (((p == data) || (p[-1] == '&')) && (p[name_len] == '=')
7218
0
          && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
7219
        /* Point p to variable value */
7220
0
        p += name_len + 1;
7221
7222
        /* Point s to the end of the value */
7223
0
        s = (const char *)memchr(p, '&', (size_t)(e - p));
7224
0
        if (s == NULL) {
7225
0
          s = e;
7226
0
        }
7227
0
        DEBUG_ASSERT(s >= p);
7228
0
        if (s < p) {
7229
0
          return -3;
7230
0
        }
7231
7232
        /* Decode variable into destination buffer */
7233
0
        len = mg_url_decode(p, (int)(s - p), dst, (int)dst_len, 1);
7234
7235
        /* Redirect error code from -1 to -2 (destination buffer too
7236
         * small). */
7237
0
        if (len == -1) {
7238
0
          len = -2;
7239
0
        }
7240
0
        break;
7241
0
      }
7242
0
    }
7243
0
  }
7244
7245
0
  return len;
7246
0
}
7247
7248
7249
/* split a string "key1=val1&key2=val2" into key/value pairs */
7250
CIVETWEB_API int
7251
mg_split_form_urlencoded(char *data,
7252
                         struct mg_header *form_fields,
7253
                         unsigned num_form_fields)
7254
0
{
7255
0
  char *b;
7256
0
  int i;
7257
0
  int num = 0;
7258
7259
0
  if (data == NULL) {
7260
    /* parameter error */
7261
0
    return -1;
7262
0
  }
7263
7264
0
  if ((form_fields == NULL) && (num_form_fields == 0)) {
7265
    /* determine the number of expected fields */
7266
0
    if (data[0] == 0) {
7267
0
      return 0;
7268
0
    }
7269
    /* count number of & to return the number of key-value-pairs */
7270
0
    num = 1;
7271
0
    while (*data) {
7272
0
      if (*data == '&') {
7273
0
        num++;
7274
0
      }
7275
0
      data++;
7276
0
    }
7277
0
    return num;
7278
0
  }
7279
7280
0
  if ((form_fields == NULL) || ((int)num_form_fields <= 0)) {
7281
    /* parameter error */
7282
0
    return -1;
7283
0
  }
7284
7285
0
  for (i = 0; i < (int)num_form_fields; i++) {
7286
    /* extract key-value pairs from input data */
7287
0
    while ((*data == ' ') || (*data == '\t')) {
7288
      /* skip initial spaces */
7289
0
      data++;
7290
0
    }
7291
0
    if (*data == 0) {
7292
      /* end of string reached */
7293
0
      break;
7294
0
    }
7295
0
    form_fields[num].name = data;
7296
7297
    /* find & or = */
7298
0
    b = data;
7299
0
    while ((*b != 0) && (*b != '&') && (*b != '=')) {
7300
0
      b++;
7301
0
    }
7302
7303
0
    if (*b == 0) {
7304
      /* last key without value */
7305
0
      form_fields[num].value = NULL;
7306
0
    } else if (*b == '&') {
7307
      /* mid key without value */
7308
0
      form_fields[num].value = NULL;
7309
0
    } else {
7310
      /* terminate string */
7311
0
      *b = 0;
7312
      /* value starts after '=' */
7313
0
      data = b + 1;
7314
0
      form_fields[num].value = data;
7315
0
    }
7316
7317
    /* new field is stored */
7318
0
    num++;
7319
7320
    /* find a next key */
7321
0
    b = strchr(data, '&');
7322
0
    if (b == 0) {
7323
      /* no more data */
7324
0
      break;
7325
0
    } else {
7326
      /* terminate value of last field at '&' */
7327
0
      *b = 0;
7328
      /* next key-value-pairs starts after '&' */
7329
0
      data = b + 1;
7330
0
    }
7331
0
  }
7332
7333
  /* Decode all values */
7334
0
  for (i = 0; i < num; i++) {
7335
0
    if (form_fields[i].name) {
7336
0
      url_decode_in_place((char *)form_fields[i].name);
7337
0
    }
7338
0
    if (form_fields[i].value) {
7339
0
      url_decode_in_place((char *)form_fields[i].value);
7340
0
    }
7341
0
  }
7342
7343
  /* return number of fields found */
7344
0
  return num;
7345
0
}
7346
7347
7348
/* HCP24: some changes to compare whole var_name */
7349
CIVETWEB_API int
7350
mg_get_cookie(const char *cookie_header,
7351
              const char *var_name,
7352
              char *dst,
7353
              size_t dst_size)
7354
0
{
7355
0
  const char *s, *p, *end;
7356
0
  int name_len, len = -1;
7357
7358
0
  if ((dst == NULL) || (dst_size == 0)) {
7359
0
    return -2;
7360
0
  }
7361
7362
0
  dst[0] = '\0';
7363
0
  if ((var_name == NULL) || ((s = cookie_header) == NULL)) {
7364
0
    return -1;
7365
0
  }
7366
7367
0
  name_len = (int)strlen(var_name);
7368
0
  end = s + strlen(s);
7369
0
  for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) {
7370
0
    if (s[name_len] == '=') {
7371
      /* HCP24: now check is it a substring or a full cookie name */
7372
0
      if ((s == cookie_header) || (s[-1] == ' ')) {
7373
0
        s += name_len + 1;
7374
0
        if ((p = strchr(s, ' ')) == NULL) {
7375
0
          p = end;
7376
0
        }
7377
0
        if (p[-1] == ';') {
7378
0
          p--;
7379
0
        }
7380
0
        if ((*s == '"') && (p[-1] == '"') && (p > s + 1)) {
7381
0
          s++;
7382
0
          p--;
7383
0
        }
7384
0
        if ((size_t)(p - s) < dst_size) {
7385
0
          len = (int)(p - s);
7386
0
          mg_strlcpy(dst, s, (size_t)len + 1);
7387
0
        } else {
7388
0
          len = -3;
7389
0
        }
7390
0
        break;
7391
0
      }
7392
0
    }
7393
0
  }
7394
0
  return len;
7395
0
}
7396
7397
7398
CIVETWEB_API int
7399
mg_base64_encode(const unsigned char *src,
7400
                 size_t src_len,
7401
                 char *dst,
7402
                 size_t *dst_len)
7403
0
{
7404
0
  static const char *b64 =
7405
0
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
7406
0
  size_t i, j;
7407
0
  int a, b, c;
7408
7409
0
  if (dst_len != NULL) {
7410
    /* Expected length including 0 termination: */
7411
    /* IN 1 -> OUT 5, IN 2 -> OUT 5, IN 3 -> OUT 5, IN 4 -> OUT 9,
7412
     * IN 5 -> OUT 9, IN 6 -> OUT 9, IN 7 -> OUT 13, etc. */
7413
0
    size_t expected_len = ((src_len + 2) / 3) * 4 + 1;
7414
0
    if (*dst_len < expected_len) {
7415
0
      if (*dst_len > 0) {
7416
0
        dst[0] = '\0';
7417
0
      }
7418
0
      *dst_len = expected_len;
7419
0
      return 0;
7420
0
    }
7421
0
  }
7422
7423
0
  for (i = j = 0; i < src_len; i += 3) {
7424
0
    a = src[i];
7425
0
    b = ((i + 1) >= src_len) ? 0 : src[i + 1];
7426
0
    c = ((i + 2) >= src_len) ? 0 : src[i + 2];
7427
7428
0
    dst[j++] = b64[a >> 2];
7429
0
    dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
7430
0
    if (i + 1 < src_len) {
7431
0
      dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
7432
0
    }
7433
0
    if (i + 2 < src_len) {
7434
0
      dst[j++] = b64[c & 63];
7435
0
    }
7436
0
  }
7437
0
  while (j % 4 != 0) {
7438
0
    dst[j++] = '=';
7439
0
  }
7440
0
  dst[j++] = '\0';
7441
7442
0
  if (dst_len != NULL) {
7443
0
    *dst_len = (size_t)j;
7444
0
  }
7445
7446
  /* Return -1 for "OK" */
7447
0
  return -1;
7448
0
}
7449
7450
7451
static unsigned char
7452
b64reverse(char letter)
7453
0
{
7454
0
  if ((letter >= 'A') && (letter <= 'Z')) {
7455
0
    return (unsigned char)(letter - 'A');
7456
0
  }
7457
0
  if ((letter >= 'a') && (letter <= 'z')) {
7458
0
    return (unsigned char)(letter - 'a' + 26);
7459
0
  }
7460
0
  if ((letter >= '0') && (letter <= '9')) {
7461
0
    return (unsigned char)(letter - '0' + 52);
7462
0
  }
7463
0
  if (letter == '+') {
7464
0
    return 62;
7465
0
  }
7466
0
  if (letter == '/') {
7467
0
    return 63;
7468
0
  }
7469
0
  if (letter == '=') {
7470
0
    return 255; /* normal end */
7471
0
  }
7472
0
  return 254; /* error */
7473
0
}
7474
7475
7476
CIVETWEB_API int
7477
mg_base64_decode(const char *src,
7478
                 size_t src_len,
7479
                 unsigned char *dst,
7480
                 size_t *dst_len)
7481
0
{
7482
0
  size_t i;
7483
0
  unsigned char a, b, c, d;
7484
0
  size_t dst_len_limit = (size_t)-1;
7485
0
  size_t dst_len_used = 0;
7486
7487
0
  if (dst_len != NULL) {
7488
0
    dst_len_limit = *dst_len;
7489
0
    *dst_len = 0;
7490
0
  }
7491
7492
0
  for (i = 0; i < src_len; i += 4) {
7493
    /* Read 4 characters from BASE64 string */
7494
0
    a = b64reverse(src[i]);
7495
0
    if (a >= 254) {
7496
0
      return (int)i;
7497
0
    }
7498
7499
0
    b = b64reverse(((i + 1) >= src_len) ? 0 : src[i + 1]);
7500
0
    if (b >= 254) {
7501
0
      return (int)i + 1;
7502
0
    }
7503
7504
0
    c = b64reverse(((i + 2) >= src_len) ? 0 : src[i + 2]);
7505
0
    if (c == 254) {
7506
0
      return (int)i + 2;
7507
0
    }
7508
7509
0
    d = b64reverse(((i + 3) >= src_len) ? 0 : src[i + 3]);
7510
0
    if (d == 254) {
7511
0
      return (int)i + 3;
7512
0
    }
7513
7514
    /* Add first (of 3) decoded character */
7515
0
    if (dst_len_used < dst_len_limit) {
7516
0
      dst[dst_len_used] = (unsigned char)((unsigned char)(a << 2)
7517
0
                                          + (unsigned char)(b >> 4));
7518
0
    }
7519
0
    dst_len_used++;
7520
7521
0
    if (c != 255) {
7522
0
      if (dst_len_used < dst_len_limit) {
7523
7524
0
        dst[dst_len_used] = (unsigned char)((unsigned char)(b << 4)
7525
0
                                            + (unsigned char)(c >> 2));
7526
0
      }
7527
0
      dst_len_used++;
7528
0
      if (d != 255) {
7529
0
        if (dst_len_used < dst_len_limit) {
7530
0
          dst[dst_len_used] =
7531
0
              (unsigned char)((unsigned char)(c << 6) + d);
7532
0
        }
7533
0
        dst_len_used++;
7534
0
      }
7535
0
    }
7536
0
  }
7537
7538
  /* Add terminating zero */
7539
0
  if (dst_len_used < dst_len_limit) {
7540
0
    dst[dst_len_used] = '\0';
7541
0
  }
7542
0
  dst_len_used++;
7543
0
  if (dst_len != NULL) {
7544
0
    *dst_len = dst_len_used;
7545
0
  }
7546
7547
0
  if (dst_len_used > dst_len_limit) {
7548
    /* Out of memory */
7549
0
    return 0;
7550
0
  }
7551
7552
  /* Return -1 for "OK" */
7553
0
  return -1;
7554
0
}
7555
7556
7557
static int
7558
is_put_or_delete_method(const struct mg_connection *conn)
7559
0
{
7560
0
  if (conn) {
7561
0
    const char *s = conn->request_info.request_method;
7562
0
    if (s != NULL) {
7563
      /* PUT, DELETE, MKCOL, PATCH, LOCK, UNLOCK, PROPPATCH, MOVE, COPY */
7564
0
      return (!strcmp(s, "PUT") || !strcmp(s, "DELETE")
7565
0
              || !strcmp(s, "MKCOL") || !strcmp(s, "PATCH")
7566
0
              || !strcmp(s, "LOCK") || !strcmp(s, "UNLOCK")
7567
0
              || !strcmp(s, "PROPPATCH") || !strcmp(s, "MOVE")
7568
0
              || !strcmp(s, "COPY"));
7569
0
    }
7570
0
  }
7571
0
  return 0;
7572
0
}
7573
7574
7575
static int
7576
is_civetweb_webdav_method(const struct mg_connection *conn)
7577
0
{
7578
  /* Note: Here we only have to identify the WebDav methods that need special
7579
   * handling in the CivetWeb code - not all methods used in WebDav. In
7580
   * particular, methods used on directories (when using Windows Explorer as
7581
   * WebDav client).
7582
   */
7583
0
  if (conn) {
7584
0
    const char *s = conn->request_info.request_method;
7585
0
    if (s != NULL) {
7586
      /* These are the civetweb builtin DAV methods */
7587
0
      return (!strcmp(s, "PROPFIND") || !strcmp(s, "PROPPATCH")
7588
0
              || !strcmp(s, "LOCK") || !strcmp(s, "UNLOCK")
7589
0
              || !strcmp(s, "MOVE") || !strcmp(s, "COPY"));
7590
0
    }
7591
0
  }
7592
0
  return 0;
7593
0
}
7594
7595
7596
#if !defined(NO_FILES)
7597
static int
7598
extention_matches_script(
7599
    struct mg_connection *conn, /* in: request (must be valid) */
7600
    const char *filename        /* in: filename  (must be valid) */
7601
)
7602
0
{
7603
0
#if !defined(NO_CGI)
7604
0
  int cgi_config_idx, inc, max;
7605
0
#endif
7606
7607
#if defined(USE_LUA)
7608
  if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS],
7609
                          filename)
7610
      > 0) {
7611
    return 1;
7612
  }
7613
#endif
7614
#if defined(USE_DUKTAPE)
7615
  if (match_prefix_strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
7616
                          filename)
7617
      > 0) {
7618
    return 1;
7619
  }
7620
#endif
7621
0
#if !defined(NO_CGI)
7622
0
  inc = CGI2_EXTENSIONS - CGI_EXTENSIONS;
7623
0
  max = PUT_DELETE_PASSWORDS_FILE - CGI_EXTENSIONS;
7624
0
  for (cgi_config_idx = 0; cgi_config_idx < max; cgi_config_idx += inc) {
7625
0
    if ((conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx] != NULL)
7626
0
        && (match_prefix_strlen(
7627
0
                conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx],
7628
0
                filename)
7629
0
            > 0)) {
7630
0
      return 1;
7631
0
    }
7632
0
  }
7633
0
#endif
7634
  /* filename and conn could be unused, if all preocessor conditions
7635
   * are false (no script language supported). */
7636
0
  (void)filename;
7637
0
  (void)conn;
7638
7639
0
  return 0;
7640
0
}
7641
7642
7643
static int
7644
extention_matches_template_text(
7645
    struct mg_connection *conn, /* in: request (must be valid) */
7646
    const char *filename        /* in: filename  (must be valid) */
7647
)
7648
0
{
7649
#if defined(USE_LUA)
7650
  if (match_prefix_strlen(conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
7651
                          filename)
7652
      > 0) {
7653
    return 1;
7654
  }
7655
#endif
7656
0
  if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], filename)
7657
0
      > 0) {
7658
0
    return 1;
7659
0
  }
7660
0
  return 0;
7661
0
}
7662
7663
7664
/* For given directory path, substitute it to valid index file.
7665
 * Return 1 if index file has been found, 0 if not found.
7666
 * If the file is found, it's stats is returned in stp. */
7667
static int
7668
substitute_index_file_aux(struct mg_connection *conn,
7669
                          char *path,
7670
                          size_t path_len,
7671
                          struct mg_file_stat *filestat)
7672
0
{
7673
0
  const char *list = conn->dom_ctx->config[INDEX_FILES];
7674
0
  struct vec filename_vec;
7675
0
  size_t n = strlen(path);
7676
0
  int found = 0;
7677
7678
  /* The 'path' given to us points to the directory. Remove all trailing
7679
   * directory separator characters from the end of the path, and
7680
   * then append single directory separator character. */
7681
0
  while ((n > 0) && (path[n - 1] == '/')) {
7682
0
    n--;
7683
0
  }
7684
0
  path[n] = '/';
7685
7686
  /* Traverse index files list. For each entry, append it to the given
7687
   * path and see if the file exists. If it exists, break the loop */
7688
0
  while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
7689
    /* Ignore too long entries that may overflow path buffer */
7690
0
    if ((filename_vec.len + 1) > (path_len - (n + 1))) {
7691
0
      continue;
7692
0
    }
7693
7694
    /* Prepare full path to the index file */
7695
0
    mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
7696
7697
    /* Does it exist? */
7698
0
    if (mg_stat(conn, path, filestat)) {
7699
      /* Yes it does, break the loop */
7700
0
      found = 1;
7701
0
      break;
7702
0
    }
7703
0
  }
7704
7705
  /* If no index file exists, restore directory path */
7706
0
  if (!found) {
7707
0
    path[n] = '\0';
7708
0
  }
7709
7710
0
  return found;
7711
0
}
7712
7713
/* Same as above, except if the first try fails and a fallback-root is
7714
 * configured, we'll try there also */
7715
static int
7716
substitute_index_file(struct mg_connection *conn,
7717
                      char *path,
7718
                      size_t path_len,
7719
                      struct mg_file_stat *filestat)
7720
0
{
7721
0
  int ret = substitute_index_file_aux(conn, path, path_len, filestat);
7722
0
  if (ret == 0) {
7723
0
    const char *root_prefix = conn->dom_ctx->config[DOCUMENT_ROOT];
7724
0
    const char *fallback_root_prefix =
7725
0
        conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT];
7726
0
    if ((root_prefix) && (fallback_root_prefix)) {
7727
0
      const size_t root_prefix_len = strlen(root_prefix);
7728
0
      if ((strncmp(path, root_prefix, root_prefix_len) == 0)) {
7729
0
        char scratch_path[UTF8_PATH_MAX]; /* separate storage, to avoid
7730
                                          side effects if we fail */
7731
0
        size_t sub_path_len;
7732
7733
0
        const size_t fallback_root_prefix_len =
7734
0
            strlen(fallback_root_prefix);
7735
0
        const char *sub_path = path + root_prefix_len;
7736
0
        while (*sub_path == '/') {
7737
0
          sub_path++;
7738
0
        }
7739
0
        sub_path_len = strlen(sub_path);
7740
7741
0
        if (((fallback_root_prefix_len + 1 + sub_path_len + 1)
7742
0
             < sizeof(scratch_path))) {
7743
          /* The concatenations below are all safe because we
7744
           * pre-verified string lengths above */
7745
0
          char *nul;
7746
0
          strcpy(scratch_path, fallback_root_prefix);
7747
0
          nul = strchr(scratch_path, '\0');
7748
0
          if ((nul > scratch_path) && (*(nul - 1) != '/')) {
7749
0
            *nul++ = '/';
7750
0
            *nul = '\0';
7751
0
          }
7752
0
          strcat(scratch_path, sub_path);
7753
0
          if (substitute_index_file_aux(conn,
7754
0
                                        scratch_path,
7755
0
                                        sizeof(scratch_path),
7756
0
                                        filestat)) {
7757
0
            mg_strlcpy(path, scratch_path, path_len);
7758
0
            return 1;
7759
0
          }
7760
0
        }
7761
0
      }
7762
0
    }
7763
0
  }
7764
0
  return ret;
7765
0
}
7766
7767
#endif
7768
7769
7770
static void
7771
interpret_uri(struct mg_connection *conn, /* in/out: request (must be valid) */
7772
              char *filename,             /* out: filename */
7773
              size_t filename_buf_len,    /* in: size of filename buffer */
7774
              struct mg_file_stat *filestat, /* out: file status structure */
7775
              int *is_found,                 /* out: file found (directly) */
7776
              int *is_script_resource,       /* out: handled by a script? */
7777
              int *is_websocket_request,     /* out: websocket connection? */
7778
              int *is_put_or_delete_request, /* out: put/delete a file? */
7779
              int *is_webdav_request,        /* out: webdav request? */
7780
              int *is_template_text          /* out: SSI file or LSP file? */
7781
)
7782
0
{
7783
0
  char const *accept_encoding;
7784
7785
0
#if !defined(NO_FILES)
7786
0
  const char *uri = conn->request_info.local_uri;
7787
0
  const char *roots[] = {conn->dom_ctx->config[DOCUMENT_ROOT],
7788
0
                         conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT],
7789
0
                         NULL};
7790
0
  int fileExists = 0;
7791
0
  const char *rewrite;
7792
0
  struct vec a, b;
7793
0
  ptrdiff_t match_len;
7794
0
  char gz_path[UTF8_PATH_MAX];
7795
0
  int truncated;
7796
0
  int i;
7797
0
#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
7798
0
  char *tmp_str;
7799
0
  size_t tmp_str_len, sep_pos;
7800
0
  int allow_substitute_script_subresources;
7801
0
#endif
7802
#else
7803
  (void)filename_buf_len; /* unused if NO_FILES is defined */
7804
#endif
7805
7806
  /* Step 1: Set all initially unknown outputs to zero */
7807
0
  memset(filestat, 0, sizeof(*filestat));
7808
0
  *filename = 0;
7809
0
  *is_found = 0;
7810
0
  *is_script_resource = 0;
7811
0
  *is_template_text = 0;
7812
7813
  /* Step 2: Classify the request method */
7814
  /* Step 2a: Check if the request attempts to modify the file system */
7815
0
  *is_put_or_delete_request = is_put_or_delete_method(conn);
7816
  /* Step 2b: Check if the request uses WebDav method that requires special
7817
   * handling */
7818
0
  *is_webdav_request = is_civetweb_webdav_method(conn);
7819
7820
  /* Step 3: Check if it is a websocket request, and modify the document
7821
   * root if required */
7822
#if defined(USE_WEBSOCKET)
7823
  *is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET);
7824
#if !defined(NO_FILES)
7825
  if ((*is_websocket_request) && conn->dom_ctx->config[WEBSOCKET_ROOT]) {
7826
    roots[0] = conn->dom_ctx->config[WEBSOCKET_ROOT];
7827
    roots[1] = conn->dom_ctx->config[FALLBACK_WEBSOCKET_ROOT];
7828
  }
7829
#endif /* !NO_FILES */
7830
#else  /* USE_WEBSOCKET */
7831
0
  *is_websocket_request = 0;
7832
0
#endif /* USE_WEBSOCKET */
7833
7834
  /* Step 4: Check if gzip encoded response is allowed */
7835
0
  conn->accept_gzip = 0;
7836
0
  if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) {
7837
0
    if (strstr(accept_encoding, "gzip") != NULL) {
7838
0
      conn->accept_gzip = 1;
7839
0
    }
7840
0
  }
7841
7842
0
#if !defined(NO_FILES)
7843
  /* Step 5: If there is no root directory, don't look for files. */
7844
  /* Note that roots[0] == NULL is a regular use case here. This occurs,
7845
   * if all requests are handled by callbacks, so the WEBSOCKET_ROOT
7846
   * config is not required. */
7847
0
  if (roots[0] == NULL) {
7848
    /* all file related outputs have already been set to 0, just return
7849
     */
7850
0
    return;
7851
0
  }
7852
7853
0
  for (i = 0; roots[i] != NULL; i++) {
7854
    /* Step 6: Determine the local file path from the root path and the
7855
     * request uri. */
7856
    /* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift
7857
     * part of the path one byte on the right. */
7858
0
    truncated = 0;
7859
0
    mg_snprintf(conn,
7860
0
                &truncated,
7861
0
                filename,
7862
0
                filename_buf_len - 1,
7863
0
                "%s%s",
7864
0
                roots[i],
7865
0
                uri);
7866
7867
0
    if (truncated) {
7868
0
      goto interpret_cleanup;
7869
0
    }
7870
7871
    /* Step 7: URI rewriting */
7872
0
    rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN];
7873
0
    while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
7874
0
      if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
7875
0
        mg_snprintf(conn,
7876
0
                    &truncated,
7877
0
                    filename,
7878
0
                    filename_buf_len - 1,
7879
0
                    "%.*s%s",
7880
0
                    (int)b.len,
7881
0
                    b.ptr,
7882
0
                    uri + match_len);
7883
0
        break;
7884
0
      }
7885
0
    }
7886
7887
0
    if (truncated) {
7888
0
      goto interpret_cleanup;
7889
0
    }
7890
7891
    /* Step 8: Check if the file exists at the server */
7892
    /* Local file path and name, corresponding to requested URI
7893
     * is now stored in "filename" variable. */
7894
0
    if (mg_stat(conn, filename, filestat)) {
7895
0
      fileExists = 1;
7896
0
      break;
7897
0
    }
7898
0
  }
7899
7900
0
  if (fileExists) {
7901
0
    int uri_len = (int)strlen(uri);
7902
0
    int is_uri_end_slash = (uri_len > 0) && (uri[uri_len - 1] == '/');
7903
7904
    /* 8.1: File exists. */
7905
0
    *is_found = 1;
7906
7907
    /* 8.2: Check if it is a script type. */
7908
0
    if (extention_matches_script(conn, filename)) {
7909
      /* The request addresses a CGI resource, Lua script or
7910
       * server-side javascript.
7911
       * The URI corresponds to the script itself (like
7912
       * /path/script.cgi), and there is no additional resource
7913
       * path (like /path/script.cgi/something).
7914
       * Requests that modify (replace or delete) a resource, like
7915
       * PUT and DELETE requests, should replace/delete the script
7916
       * file.
7917
       * Requests that read or write from/to a resource, like GET and
7918
       * POST requests, should call the script and return the
7919
       * generated response. */
7920
0
      *is_script_resource = (!*is_put_or_delete_request);
7921
0
    }
7922
7923
    /* 8.3: Check for SSI and LSP files */
7924
0
    if (extention_matches_template_text(conn, filename)) {
7925
      /* Same as above, but for *.lsp and *.shtml files. */
7926
      /* A "template text" is a file delivered directly to the client,
7927
       * but with some text tags replaced by dynamic content.
7928
       * E.g. a Server Side Include (SSI) or Lua Page/Lua Server Page
7929
       * (LP, LSP) file. */
7930
0
      *is_template_text = (!*is_put_or_delete_request);
7931
0
    }
7932
7933
    /* 8.4: If the request target is a directory, there could be
7934
     * a substitute file (index.html, index.cgi, ...). */
7935
    /* But do not substitute a directory for a WebDav request */
7936
0
    if (filestat->is_directory && is_uri_end_slash
7937
0
        && (!*is_webdav_request)) {
7938
      /* Use a local copy here, since substitute_index_file will
7939
       * change the content of the file status */
7940
0
      struct mg_file_stat tmp_filestat;
7941
0
      memset(&tmp_filestat, 0, sizeof(tmp_filestat));
7942
7943
0
      if (substitute_index_file(
7944
0
              conn, filename, filename_buf_len, &tmp_filestat)) {
7945
7946
        /* Substitute file found. Copy stat to the output, then
7947
         * check if the file is a script file */
7948
0
        *filestat = tmp_filestat;
7949
7950
0
        if (extention_matches_script(conn, filename)) {
7951
          /* Substitute file is a script file */
7952
0
          *is_script_resource = 1;
7953
0
        } else if (extention_matches_template_text(conn, filename)) {
7954
          /* Substitute file is a LSP or SSI file */
7955
0
          *is_template_text = 1;
7956
0
        } else {
7957
          /* Substitute file is a regular file */
7958
0
          *is_script_resource = 0;
7959
0
          *is_found = (mg_stat(conn, filename, filestat) ? 1 : 0);
7960
0
        }
7961
0
      }
7962
      /* If there is no substitute file, the server could return
7963
       * a directory listing in a later step */
7964
0
    }
7965
0
    return;
7966
0
  }
7967
7968
  /* Step 9: Check for zipped files: */
7969
  /* If we can't find the actual file, look for the file
7970
   * with the same name but a .gz extension. If we find it,
7971
   * use that and set the gzipped flag in the file struct
7972
   * to indicate that the response need to have the content-
7973
   * encoding: gzip header.
7974
   * We can only do this if the browser declares support. */
7975
0
  if (conn->accept_gzip) {
7976
0
    mg_snprintf(
7977
0
        conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", filename);
7978
7979
0
    if (truncated) {
7980
0
      goto interpret_cleanup;
7981
0
    }
7982
7983
0
    if (mg_stat(conn, gz_path, filestat)) {
7984
0
      if (filestat) {
7985
0
        filestat->is_gzipped = 1;
7986
0
        *is_found = 1;
7987
0
      }
7988
      /* Currently gz files can not be scripts. */
7989
0
      return;
7990
0
    }
7991
0
  }
7992
7993
0
#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
7994
  /* Step 10: Script resources may handle sub-resources */
7995
  /* Support PATH_INFO for CGI scripts. */
7996
0
  tmp_str_len = strlen(filename);
7997
0
  tmp_str =
7998
0
      (char *)mg_malloc_ctx(tmp_str_len + UTF8_PATH_MAX + 1, conn->phys_ctx);
7999
0
  if (!tmp_str) {
8000
    /* Out of memory */
8001
0
    goto interpret_cleanup;
8002
0
  }
8003
0
  memcpy(tmp_str, filename, tmp_str_len + 1);
8004
8005
  /* Check config, if index scripts may have sub-resources */
8006
0
  allow_substitute_script_subresources =
8007
0
      !mg_strcasecmp(conn->dom_ctx->config[ALLOW_INDEX_SCRIPT_SUB_RES],
8008
0
                     "yes");
8009
0
  if (*is_webdav_request) {
8010
    /* TO BE DEFINED: Should scripts handle special WebDAV methods lile
8011
     * PROPFIND for their subresources? */
8012
    /* allow_substitute_script_subresources = 0; */
8013
0
  }
8014
8015
0
  sep_pos = tmp_str_len;
8016
0
  while (sep_pos > 0) {
8017
0
    sep_pos--;
8018
0
    if (tmp_str[sep_pos] == '/') {
8019
0
      int is_script = 0, does_exist = 0;
8020
8021
0
      tmp_str[sep_pos] = 0;
8022
0
      if (tmp_str[0]) {
8023
0
        is_script = extention_matches_script(conn, tmp_str);
8024
0
        does_exist = mg_stat(conn, tmp_str, filestat);
8025
0
      }
8026
8027
0
      if (does_exist && is_script) {
8028
0
        filename[sep_pos] = 0;
8029
0
        memmove(filename + sep_pos + 2,
8030
0
                filename + sep_pos + 1,
8031
0
                strlen(filename + sep_pos + 1) + 1);
8032
0
        conn->path_info = filename + sep_pos + 1;
8033
0
        filename[sep_pos + 1] = '/';
8034
0
        *is_script_resource = 1;
8035
0
        *is_found = 1;
8036
0
        break;
8037
0
      }
8038
8039
0
      if (allow_substitute_script_subresources) {
8040
0
        if (substitute_index_file(
8041
0
                conn, tmp_str, tmp_str_len + UTF8_PATH_MAX, filestat)) {
8042
8043
          /* some intermediate directory has an index file */
8044
0
          if (extention_matches_script(conn, tmp_str)) {
8045
8046
0
            size_t script_name_len = strlen(tmp_str);
8047
8048
            /* subres_name read before this memory locatio will be
8049
            overwritten */
8050
0
            char *subres_name = filename + sep_pos;
8051
0
            size_t subres_name_len = strlen(subres_name);
8052
8053
0
            DEBUG_TRACE("Substitute script %s serving path %s",
8054
0
                        tmp_str,
8055
0
                        filename);
8056
8057
            /* this index file is a script */
8058
0
            if ((script_name_len + subres_name_len + 2)
8059
0
                >= filename_buf_len) {
8060
0
              mg_free(tmp_str);
8061
0
              goto interpret_cleanup;
8062
0
            }
8063
8064
0
            conn->path_info =
8065
0
                filename + script_name_len + 1; /* new target */
8066
0
            memmove(conn->path_info, subres_name, subres_name_len);
8067
0
            conn->path_info[subres_name_len] = 0;
8068
0
            memcpy(filename, tmp_str, script_name_len + 1);
8069
8070
0
            *is_script_resource = 1;
8071
0
            *is_found = 1;
8072
0
            break;
8073
8074
0
          } else {
8075
8076
0
            DEBUG_TRACE("Substitute file %s serving path %s",
8077
0
                        tmp_str,
8078
0
                        filename);
8079
8080
            /* non-script files will not have sub-resources */
8081
0
            filename[sep_pos] = 0;
8082
0
            conn->path_info = 0;
8083
0
            *is_script_resource = 0;
8084
0
            *is_found = 0;
8085
0
            break;
8086
0
          }
8087
0
        }
8088
0
      }
8089
8090
0
      tmp_str[sep_pos] = '/';
8091
0
    }
8092
0
  }
8093
8094
0
  mg_free(tmp_str);
8095
8096
0
#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */
8097
0
#endif /* !defined(NO_FILES) */
8098
0
  return;
8099
8100
0
#if !defined(NO_FILES)
8101
/* Reset all outputs */
8102
0
interpret_cleanup:
8103
0
  memset(filestat, 0, sizeof(*filestat));
8104
0
  *filename = 0;
8105
0
  *is_found = 0;
8106
0
  *is_script_resource = 0;
8107
0
  *is_websocket_request = 0;
8108
0
  *is_put_or_delete_request = 0;
8109
0
#endif /* !defined(NO_FILES) */
8110
0
}
8111
8112
8113
/* Check whether full request is buffered. Return:
8114
 * -1  if request or response is malformed
8115
 *  0  if request or response is not yet fully buffered
8116
 * >0  actual request length, including last \r\n\r\n */
8117
static int
8118
get_http_header_len(const char *buf, int buflen)
8119
34
{
8120
34
  int i;
8121
34
  for (i = 0; i < buflen; i++) {
8122
    /* Do an unsigned comparison in some conditions below */
8123
0
    const unsigned char c = (unsigned char)buf[i];
8124
8125
0
    if ((c < 128) && ((char)c != '\r') && ((char)c != '\n')
8126
0
        && !isprint(c)) {
8127
      /* abort scan as soon as one malformed character is found */
8128
0
      return -1;
8129
0
    }
8130
8131
0
    if (i < buflen - 1) {
8132
0
      if ((buf[i] == '\n') && (buf[i + 1] == '\n')) {
8133
        /* Two newline, no carriage return - not standard compliant,
8134
         * but it should be accepted */
8135
0
        return i + 2;
8136
0
      }
8137
0
    }
8138
8139
0
    if (i < buflen - 3) {
8140
0
      if ((buf[i] == '\r') && (buf[i + 1] == '\n') && (buf[i + 2] == '\r')
8141
0
          && (buf[i + 3] == '\n')) {
8142
        /* Two \r\n - standard compliant */
8143
0
        return i + 4;
8144
0
      }
8145
0
    }
8146
0
  }
8147
8148
34
  return 0;
8149
34
}
8150
8151
8152
#if !defined(NO_CACHING)
8153
/* Convert month to the month number. Return -1 on error, or month number */
8154
static int
8155
get_month_index(const char *s)
8156
0
{
8157
0
  size_t i;
8158
8159
0
  for (i = 0; i < ARRAY_SIZE(month_names); i++) {
8160
0
    if (!strcmp(s, month_names[i])) {
8161
0
      return (int)i;
8162
0
    }
8163
0
  }
8164
8165
0
  return -1;
8166
0
}
8167
8168
8169
/* Parse UTC date-time string, and return the corresponding time_t value. */
8170
static time_t
8171
parse_date_string(const char *datetime)
8172
0
{
8173
0
  char month_str[32] = {0};
8174
0
  int second, minute, hour, day, month, year;
8175
0
  time_t result = (time_t)0;
8176
0
  struct tm tm;
8177
8178
0
  if ((sscanf(datetime,
8179
0
              "%d/%3s/%d %d:%d:%d",
8180
0
              &day,
8181
0
              month_str,
8182
0
              &year,
8183
0
              &hour,
8184
0
              &minute,
8185
0
              &second)
8186
0
       == 6)
8187
0
      || (sscanf(datetime,
8188
0
                 "%d %3s %d %d:%d:%d",
8189
0
                 &day,
8190
0
                 month_str,
8191
0
                 &year,
8192
0
                 &hour,
8193
0
                 &minute,
8194
0
                 &second)
8195
0
          == 6)
8196
0
      || (sscanf(datetime,
8197
0
                 "%*3s, %d %3s %d %d:%d:%d",
8198
0
                 &day,
8199
0
                 month_str,
8200
0
                 &year,
8201
0
                 &hour,
8202
0
                 &minute,
8203
0
                 &second)
8204
0
          == 6)
8205
0
      || (sscanf(datetime,
8206
0
                 "%d-%3s-%d %d:%d:%d",
8207
0
                 &day,
8208
0
                 month_str,
8209
0
                 &year,
8210
0
                 &hour,
8211
0
                 &minute,
8212
0
                 &second)
8213
0
          == 6)) {
8214
0
    month = get_month_index(month_str);
8215
0
    if ((month >= 0) && (year >= 1970)) {
8216
0
      memset(&tm, 0, sizeof(tm));
8217
0
      tm.tm_year = year - 1900;
8218
0
      tm.tm_mon = month;
8219
0
      tm.tm_mday = day;
8220
0
      tm.tm_hour = hour;
8221
0
      tm.tm_min = minute;
8222
0
      tm.tm_sec = second;
8223
0
      result = timegm(&tm);
8224
0
    }
8225
0
  }
8226
8227
0
  return result;
8228
0
}
8229
#endif /* !NO_CACHING */
8230
8231
8232
/* Pre-process URIs according to RFC + protect against directory disclosure
8233
 * attacks by removing '..', excessive '/' and '\' characters */
8234
static void
8235
remove_dot_segments(char *inout)
8236
0
{
8237
  /* Windows backend protection
8238
   * (https://tools.ietf.org/html/rfc3986#section-7.3): Replace backslash
8239
   * in URI by slash */
8240
0
  char *out_end = inout;
8241
0
  char *in = inout;
8242
8243
0
  if (!in) {
8244
    /* Param error. */
8245
0
    return;
8246
0
  }
8247
8248
0
  while (*in) {
8249
0
    if (*in == '\\') {
8250
0
      *in = '/';
8251
0
    }
8252
0
    in++;
8253
0
  }
8254
8255
  /* Algorithm "remove_dot_segments" from
8256
   * https://tools.ietf.org/html/rfc3986#section-5.2.4 */
8257
  /* Step 1:
8258
   * The input buffer is initialized.
8259
   * The output buffer is initialized to the empty string.
8260
   */
8261
0
  in = inout;
8262
8263
  /* Step 2:
8264
   * While the input buffer is not empty, loop as follows:
8265
   */
8266
  /* Less than out_end of the inout buffer is used as output, so keep
8267
   * condition: out_end <= in */
8268
0
  while (*in) {
8269
    /* Step 2a:
8270
     * If the input buffer begins with a prefix of "../" or "./",
8271
     * then remove that prefix from the input buffer;
8272
     */
8273
0
    if (!strncmp(in, "../", 3)) {
8274
0
      in += 3;
8275
0
    } else if (!strncmp(in, "./", 2)) {
8276
0
      in += 2;
8277
0
    }
8278
    /* otherwise */
8279
    /* Step 2b:
8280
     * if the input buffer begins with a prefix of "/./" or "/.",
8281
     * where "." is a complete path segment, then replace that
8282
     * prefix with "/" in the input buffer;
8283
     */
8284
0
    else if (!strncmp(in, "/./", 3)) {
8285
0
      in += 2;
8286
0
    } else if (!strcmp(in, "/.")) {
8287
0
      in[1] = 0;
8288
0
    }
8289
    /* otherwise */
8290
    /* Step 2c:
8291
     * if the input buffer begins with a prefix of "/../" or "/..",
8292
     * where ".." is a complete path segment, then replace that
8293
     * prefix with "/" in the input buffer and remove the last
8294
     * segment and its preceding "/" (if any) from the output
8295
     * buffer;
8296
     */
8297
0
    else if (!strncmp(in, "/../", 4)) {
8298
0
      in += 3;
8299
0
      if (inout != out_end) {
8300
        /* remove last segment */
8301
0
        do {
8302
0
          out_end--;
8303
0
        } while ((inout != out_end) && (*out_end != '/'));
8304
0
      }
8305
0
    } else if (!strcmp(in, "/..")) {
8306
0
      in[1] = 0;
8307
0
      if (inout != out_end) {
8308
        /* remove last segment */
8309
0
        do {
8310
0
          out_end--;
8311
0
        } while ((inout != out_end) && (*out_end != '/'));
8312
0
      }
8313
0
    }
8314
    /* otherwise */
8315
    /* Step 2d:
8316
     * if the input buffer consists only of "." or "..", then remove
8317
     * that from the input buffer;
8318
     */
8319
0
    else if (!strcmp(in, ".") || !strcmp(in, "..")) {
8320
0
      *in = 0;
8321
0
    }
8322
    /* otherwise */
8323
    /* Step 2e:
8324
     * move the first path segment in the input buffer to the end of
8325
     * the output buffer, including the initial "/" character (if
8326
     * any) and any subsequent characters up to, but not including,
8327
     * the next "/" character or the end of the input buffer.
8328
     */
8329
0
    else {
8330
0
      do {
8331
0
        *out_end = *in;
8332
0
        out_end++;
8333
0
        in++;
8334
0
      } while ((*in != 0) && (*in != '/'));
8335
0
    }
8336
0
  }
8337
8338
  /* Step 3:
8339
   * Finally, the output buffer is returned as the result of
8340
   * remove_dot_segments.
8341
   */
8342
  /* Terminate output */
8343
0
  *out_end = 0;
8344
8345
  /* For Windows, the files/folders "x" and "x." (with a dot but without
8346
   * extension) are identical. Replace all "./" by "/" and remove a "." at
8347
   * the end. Also replace all "//" by "/". Repeat until there is no "./"
8348
   * or "//" anymore.
8349
   */
8350
0
  out_end = in = inout;
8351
0
  while (*in) {
8352
0
    if (*in == '.') {
8353
      /* remove . at the end or preceding of / */
8354
0
      char *in_ahead = in;
8355
0
      do {
8356
0
        in_ahead++;
8357
0
      } while (*in_ahead == '.');
8358
0
      if (*in_ahead == '/') {
8359
0
        in = in_ahead;
8360
0
        if ((out_end != inout) && (out_end[-1] == '/')) {
8361
          /* remove generated // */
8362
0
          out_end--;
8363
0
        }
8364
0
      } else if (*in_ahead == 0) {
8365
0
        in = in_ahead;
8366
0
      } else {
8367
0
        do {
8368
0
          *out_end++ = '.';
8369
0
          in++;
8370
0
        } while (in != in_ahead);
8371
0
      }
8372
0
    } else if (*in == '/') {
8373
      /* replace // by / */
8374
0
      *out_end++ = '/';
8375
0
      do {
8376
0
        in++;
8377
0
      } while (*in == '/');
8378
0
    } else {
8379
0
      *out_end++ = *in;
8380
0
      in++;
8381
0
    }
8382
0
  }
8383
0
  *out_end = 0;
8384
0
}
8385
8386
8387
static const struct {
8388
  const char *extension;
8389
  size_t ext_len;
8390
  const char *mime_type;
8391
} builtin_mime_types[] = {
8392
    /* IANA registered MIME types
8393
     * (http://www.iana.org/assignments/media-types)
8394
     * application types */
8395
    {".bin", 4, "application/octet-stream"},
8396
    {".deb", 4, "application/octet-stream"},
8397
    {".dmg", 4, "application/octet-stream"},
8398
    {".dll", 4, "application/octet-stream"},
8399
    {".doc", 4, "application/msword"},
8400
    {".eps", 4, "application/postscript"},
8401
    {".exe", 4, "application/octet-stream"},
8402
    {".iso", 4, "application/octet-stream"},
8403
    {".js", 3, "application/javascript"},
8404
    {".json", 5, "application/json"},
8405
    {".msi", 4, "application/octet-stream"},
8406
    {".pdf", 4, "application/pdf"},
8407
    {".ps", 3, "application/postscript"},
8408
    {".rtf", 4, "application/rtf"},
8409
    {".wasm", 5, "application/wasm"},
8410
    {".xhtml", 6, "application/xhtml+xml"},
8411
    {".xsl", 4, "application/xml"},
8412
    {".xslt", 5, "application/xml"},
8413
8414
    /* fonts */
8415
    {".ttf", 4, "application/font-sfnt"},
8416
    {".cff", 4, "application/font-sfnt"},
8417
    {".otf", 4, "application/font-sfnt"},
8418
    {".aat", 4, "application/font-sfnt"},
8419
    {".sil", 4, "application/font-sfnt"},
8420
    {".pfr", 4, "application/font-tdpfr"},
8421
    {".woff", 5, "application/font-woff"},
8422
    {".woff2", 6, "application/font-woff2"},
8423
8424
    /* audio */
8425
    {".mp3", 4, "audio/mpeg"},
8426
    {".oga", 4, "audio/ogg"},
8427
    {".ogg", 4, "audio/ogg"},
8428
8429
    /* image */
8430
    {".gif", 4, "image/gif"},
8431
    {".ief", 4, "image/ief"},
8432
    {".jpeg", 5, "image/jpeg"},
8433
    {".jpg", 4, "image/jpeg"},
8434
    {".jpm", 4, "image/jpm"},
8435
    {".jpx", 4, "image/jpx"},
8436
    {".png", 4, "image/png"},
8437
    {".svg", 4, "image/svg+xml"},
8438
    {".tif", 4, "image/tiff"},
8439
    {".tiff", 5, "image/tiff"},
8440
8441
    /* model */
8442
    {".wrl", 4, "model/vrml"},
8443
8444
    /* text */
8445
    {".css", 4, "text/css"},
8446
    {".csv", 4, "text/csv"},
8447
    {".htm", 4, "text/html"},
8448
    {".html", 5, "text/html"},
8449
    {".sgm", 4, "text/sgml"},
8450
    {".shtm", 5, "text/html"},
8451
    {".shtml", 6, "text/html"},
8452
    {".txt", 4, "text/plain"},
8453
    {".xml", 4, "text/xml"},
8454
8455
    /* video */
8456
    {".mov", 4, "video/quicktime"},
8457
    {".mp4", 4, "video/mp4"},
8458
    {".mpeg", 5, "video/mpeg"},
8459
    {".mpg", 4, "video/mpeg"},
8460
    {".ogv", 4, "video/ogg"},
8461
    {".qt", 3, "video/quicktime"},
8462
8463
    /* not registered types
8464
     * (http://reference.sitepoint.com/html/mime-types-full,
8465
     * http://www.hansenb.pdx.edu/DMKB/dict/tutorials/mime_typ.php, ..) */
8466
    {".arj", 4, "application/x-arj-compressed"},
8467
    {".gz", 3, "application/x-gunzip"},
8468
    {".rar", 4, "application/x-arj-compressed"},
8469
    {".swf", 4, "application/x-shockwave-flash"},
8470
    {".tar", 4, "application/x-tar"},
8471
    {".tgz", 4, "application/x-tar-gz"},
8472
    {".torrent", 8, "application/x-bittorrent"},
8473
    {".ppt", 4, "application/x-mspowerpoint"},
8474
    {".xls", 4, "application/x-msexcel"},
8475
    {".zip", 4, "application/x-zip-compressed"},
8476
    {".aac",
8477
     4,
8478
     "audio/aac"}, /* http://en.wikipedia.org/wiki/Advanced_Audio_Coding */
8479
    {".flac", 5, "audio/flac"},
8480
    {".aif", 4, "audio/x-aif"},
8481
    {".m3u", 4, "audio/x-mpegurl"},
8482
    {".mid", 4, "audio/x-midi"},
8483
    {".ra", 3, "audio/x-pn-realaudio"},
8484
    {".ram", 4, "audio/x-pn-realaudio"},
8485
    {".wav", 4, "audio/x-wav"},
8486
    {".bmp", 4, "image/bmp"},
8487
    {".ico", 4, "image/x-icon"},
8488
    {".pct", 4, "image/x-pct"},
8489
    {".pict", 5, "image/pict"},
8490
    {".rgb", 4, "image/x-rgb"},
8491
    {".webm", 5, "video/webm"}, /* http://en.wikipedia.org/wiki/WebM */
8492
    {".asf", 4, "video/x-ms-asf"},
8493
    {".avi", 4, "video/x-msvideo"},
8494
    {".m4v", 4, "video/x-m4v"},
8495
    {NULL, 0, NULL}};
8496
8497
8498
CIVETWEB_API const char *
8499
mg_get_builtin_mime_type(const char *path)
8500
0
{
8501
0
  const char *ext;
8502
0
  size_t i, path_len;
8503
8504
0
  path_len = strlen(path);
8505
8506
0
  for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
8507
0
    ext = path + (path_len - builtin_mime_types[i].ext_len);
8508
0
    if ((path_len > builtin_mime_types[i].ext_len)
8509
0
        && (mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0)) {
8510
0
      return builtin_mime_types[i].mime_type;
8511
0
    }
8512
0
  }
8513
8514
0
  return "text/plain";
8515
0
}
8516
8517
8518
/* Look at the "path" extension and figure what mime type it has.
8519
 * Store mime type in the vector. */
8520
static void
8521
get_mime_type(struct mg_connection *conn, const char *path, struct vec *vec)
8522
0
{
8523
0
  struct vec ext_vec, mime_vec;
8524
0
  const char *list, *ext;
8525
0
  size_t path_len;
8526
8527
0
  path_len = strlen(path);
8528
8529
0
  if ((conn == NULL) || (vec == NULL)) {
8530
0
    if (vec != NULL) {
8531
0
      memset(vec, '\0', sizeof(struct vec));
8532
0
    }
8533
0
    return;
8534
0
  }
8535
8536
  /* Scan user-defined mime types first, in case user wants to
8537
   * override default mime types. */
8538
0
  list = conn->dom_ctx->config[EXTRA_MIME_TYPES];
8539
0
  while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
8540
    /* ext now points to the path suffix */
8541
0
    ext = path + path_len - ext_vec.len;
8542
0
    if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
8543
0
      *vec = mime_vec;
8544
0
      return;
8545
0
    }
8546
0
  }
8547
8548
0
  vec->ptr = mg_get_builtin_mime_type(path);
8549
0
  vec->len = strlen(vec->ptr);
8550
0
}
8551
8552
8553
/* Stringify binary data. Output buffer must be twice as big as input,
8554
 * because each byte takes 2 bytes in string representation */
8555
static void
8556
bin2str(char *to, const unsigned char *p, size_t len)
8557
0
{
8558
0
  static const char *hex = "0123456789abcdef";
8559
8560
0
  for (; len--; p++) {
8561
0
    *to++ = hex[p[0] >> 4];
8562
0
    *to++ = hex[p[0] & 0x0f];
8563
0
  }
8564
0
  *to = '\0';
8565
0
}
8566
8567
8568
/* Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
8569
 */
8570
CIVETWEB_API char *
8571
mg_md5(char buf[33], ...)
8572
0
{
8573
0
  md5_byte_t hash[16];
8574
0
  const char *p;
8575
0
  va_list ap;
8576
0
  md5_state_t ctx;
8577
8578
0
  md5_init(&ctx);
8579
8580
0
  va_start(ap, buf);
8581
0
  while ((p = va_arg(ap, const char *)) != NULL) {
8582
0
    md5_append(&ctx, (const md5_byte_t *)p, strlen(p));
8583
0
  }
8584
0
  va_end(ap);
8585
8586
0
  md5_finish(&ctx, hash);
8587
0
  bin2str(buf, hash, sizeof(hash));
8588
0
  return buf;
8589
0
}
8590
8591
8592
/* Check the user's password, return 1 if OK */
8593
static int
8594
check_password_digest(const char *method,
8595
                      const char *ha1,
8596
                      const char *uri,
8597
                      const char *nonce,
8598
                      const char *nc,
8599
                      const char *cnonce,
8600
                      const char *qop,
8601
                      const char *response)
8602
0
{
8603
0
  char ha2[32 + 1], expected_response[32 + 1];
8604
8605
  /* Some of the parameters may be NULL */
8606
0
  if ((method == NULL) || (nonce == NULL) || (nc == NULL) || (cnonce == NULL)
8607
0
      || (qop == NULL) || (response == NULL)) {
8608
0
    return 0;
8609
0
  }
8610
8611
  /* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */
8612
0
  if (strlen(response) != 32) {
8613
0
    return 0;
8614
0
  }
8615
8616
0
  mg_md5(ha2, method, ":", uri, NULL);
8617
0
  mg_md5(expected_response,
8618
0
         ha1,
8619
0
         ":",
8620
0
         nonce,
8621
0
         ":",
8622
0
         nc,
8623
0
         ":",
8624
0
         cnonce,
8625
0
         ":",
8626
0
         qop,
8627
0
         ":",
8628
0
         ha2,
8629
0
         NULL);
8630
8631
0
  return mg_strcasecmp(response, expected_response) == 0;
8632
0
}
8633
8634
8635
#if !defined(NO_FILESYSTEMS)
8636
/* Use the global passwords file, if specified by auth_gpass option,
8637
 * or search for .htpasswd in the requested directory. */
8638
static void
8639
open_auth_file(struct mg_connection *conn,
8640
               const char *path,
8641
               struct mg_file *filep)
8642
0
{
8643
0
  if ((conn != NULL) && (conn->dom_ctx != NULL)) {
8644
0
    char name[UTF8_PATH_MAX];
8645
0
    const char *p, *e,
8646
0
        *gpass = conn->dom_ctx->config[GLOBAL_PASSWORDS_FILE];
8647
0
    int truncated;
8648
8649
0
    if (gpass != NULL) {
8650
      /* Use global passwords file */
8651
0
      if (!mg_fopen(conn, gpass, MG_FOPEN_MODE_READ, filep)) {
8652
#if defined(DEBUG)
8653
        /* Use mg_cry_internal here, since gpass has been
8654
         * configured. */
8655
        mg_cry_internal(conn, "fopen(%s): %s", gpass, strerror(ERRNO));
8656
#endif
8657
0
      }
8658
      /* Important: using local struct mg_file to test path for
8659
       * is_directory flag. If filep is used, mg_stat() makes it
8660
       * appear as if auth file was opened.
8661
       * TODO(mid): Check if this is still required after rewriting
8662
       * mg_stat */
8663
0
    } else if (mg_stat(conn, path, &filep->stat)
8664
0
               && filep->stat.is_directory) {
8665
0
      mg_snprintf(conn,
8666
0
                  &truncated,
8667
0
                  name,
8668
0
                  sizeof(name),
8669
0
                  "%s/%s",
8670
0
                  path,
8671
0
                  PASSWORDS_FILE_NAME);
8672
8673
0
      if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
8674
#if defined(DEBUG)
8675
        /* Don't use mg_cry_internal here, but only a trace, since
8676
         * this is a typical case. It will occur for every directory
8677
         * without a password file. */
8678
        DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO));
8679
#endif
8680
0
      }
8681
0
    } else {
8682
      /* Try to find .htpasswd in requested directory. */
8683
0
      for (p = path, e = p + strlen(p) - 1; e > p; e--) {
8684
0
        if (e[0] == '/') {
8685
0
          break;
8686
0
        }
8687
0
      }
8688
0
      mg_snprintf(conn,
8689
0
                  &truncated,
8690
0
                  name,
8691
0
                  sizeof(name),
8692
0
                  "%.*s/%s",
8693
0
                  (int)(e - p),
8694
0
                  p,
8695
0
                  PASSWORDS_FILE_NAME);
8696
8697
0
      if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
8698
#if defined(DEBUG)
8699
        /* Don't use mg_cry_internal here, but only a trace, since
8700
         * this is a typical case. It will occur for every directory
8701
         * without a password file. */
8702
        DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO));
8703
#endif
8704
0
      }
8705
0
    }
8706
0
  }
8707
0
}
8708
#endif /* NO_FILESYSTEMS */
8709
8710
8711
/* Parsed Authorization header */
8712
struct ah {
8713
  char *user;
8714
  int type;             /* 1 = basic, 2 = digest */
8715
  char *plain_password; /* Basic only */
8716
  char *uri, *cnonce, *response, *qop, *nc, *nonce; /* Digest only */
8717
};
8718
8719
8720
/* Return 1 on success. Always initializes the ah structure. */
8721
static int
8722
parse_auth_header(struct mg_connection *conn,
8723
                  char *buf,
8724
                  size_t buf_size,
8725
                  struct ah *ah)
8726
0
{
8727
0
  char *name, *value, *s;
8728
0
  const char *auth_header;
8729
0
  uint64_t nonce;
8730
8731
0
  if (!ah || !conn) {
8732
0
    return 0;
8733
0
  }
8734
8735
0
  (void)memset(ah, 0, sizeof(*ah));
8736
0
  auth_header = mg_get_header(conn, "Authorization");
8737
8738
0
  if (auth_header == NULL) {
8739
    /* No Authorization header at all */
8740
0
    return 0;
8741
0
  }
8742
0
  if (0 == mg_strncasecmp(auth_header, "Basic ", 6)) {
8743
    /* Basic Auth (we never asked for this, but some client may send it) */
8744
0
    char *split;
8745
0
    const char *userpw_b64 = auth_header + 6;
8746
0
    size_t userpw_b64_len = strlen(userpw_b64);
8747
0
    size_t buf_len_r = buf_size;
8748
0
    if (mg_base64_decode(
8749
0
            userpw_b64, userpw_b64_len, (unsigned char *)buf, &buf_len_r)
8750
0
        != -1) {
8751
0
      return 0; /* decode error */
8752
0
    }
8753
0
    split = strchr(buf, ':');
8754
0
    if (!split) {
8755
0
      return 0; /* Format error */
8756
0
    }
8757
8758
    /* Separate string at ':' */
8759
0
    *split = 0;
8760
8761
    /* User name is before ':', Password is after ':'  */
8762
0
    ah->user = buf;
8763
0
    ah->type = 1;
8764
0
    ah->plain_password = split + 1;
8765
8766
0
    return 1;
8767
8768
0
  } else if (0 == mg_strncasecmp(auth_header, "Digest ", 7)) {
8769
    /* Digest Auth ... implemented below */
8770
0
    ah->type = 2;
8771
8772
0
  } else {
8773
    /* Unknown or invalid Auth method */
8774
0
    return 0;
8775
0
  }
8776
8777
  /* Make modifiable copy of the auth header */
8778
0
  (void)mg_strlcpy(buf, auth_header + 7, buf_size);
8779
0
  s = buf;
8780
8781
  /* Parse authorization header */
8782
0
  for (;;) {
8783
    /* Gobble initial spaces */
8784
0
    while (isspace((unsigned char)*s)) {
8785
0
      s++;
8786
0
    }
8787
0
    name = skip_quoted(&s, "=", " ", 0);
8788
    /* Value is either quote-delimited, or ends at first comma or space.
8789
     */
8790
0
    if (s[0] == '\"') {
8791
0
      s++;
8792
0
      value = skip_quoted(&s, "\"", " ", '\\');
8793
0
      if (s[0] == ',') {
8794
0
        s++;
8795
0
      }
8796
0
    } else {
8797
0
      value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF
8798
                                              * uses spaces */
8799
0
    }
8800
0
    if (*name == '\0') {
8801
0
      break;
8802
0
    }
8803
8804
0
    if (!strcmp(name, "username")) {
8805
0
      ah->user = value;
8806
0
    } else if (!strcmp(name, "cnonce")) {
8807
0
      ah->cnonce = value;
8808
0
    } else if (!strcmp(name, "response")) {
8809
0
      ah->response = value;
8810
0
    } else if (!strcmp(name, "uri")) {
8811
0
      ah->uri = value;
8812
0
    } else if (!strcmp(name, "qop")) {
8813
0
      ah->qop = value;
8814
0
    } else if (!strcmp(name, "nc")) {
8815
0
      ah->nc = value;
8816
0
    } else if (!strcmp(name, "nonce")) {
8817
0
      ah->nonce = value;
8818
0
    }
8819
0
  }
8820
8821
0
#if !defined(NO_NONCE_CHECK)
8822
  /* Read the nonce from the response. */
8823
0
  if (ah->nonce == NULL) {
8824
0
    return 0;
8825
0
  }
8826
0
  s = NULL;
8827
0
  nonce = strtoull(ah->nonce, &s, 10);
8828
0
  if ((s == NULL) || (*s != 0)) {
8829
0
    return 0;
8830
0
  }
8831
8832
  /* Convert the nonce from the client to a number. */
8833
0
  nonce ^= conn->dom_ctx->auth_nonce_mask;
8834
8835
  /* The converted number corresponds to the time the nounce has been
8836
   * created. This should not be earlier than the server start. */
8837
  /* Server side nonce check is valuable in all situations but one:
8838
   * if the server restarts frequently, but the client should not see
8839
   * that, so the server should accept nonces from previous starts. */
8840
  /* However, the reasonable default is to not accept a nonce from a
8841
   * previous start, so if anyone changed the access rights between
8842
   * two restarts, a new login is required. */
8843
0
  if (nonce < (uint64_t)conn->phys_ctx->start_time) {
8844
    /* nonce is from a previous start of the server and no longer valid
8845
     * (replay attack?) */
8846
0
    return 0;
8847
0
  }
8848
  /* Check if the nonce is too high, so it has not (yet) been used by the
8849
   * server. */
8850
0
  if (nonce >= ((uint64_t)conn->phys_ctx->start_time
8851
0
                + conn->dom_ctx->nonce_count)) {
8852
0
    return 0;
8853
0
  }
8854
#else
8855
  (void)nonce;
8856
#endif
8857
8858
0
  return (ah->user != NULL);
8859
0
}
8860
8861
8862
static const char *
8863
mg_fgets(char *buf, size_t size, struct mg_file *filep)
8864
0
{
8865
0
  if (!filep) {
8866
0
    return NULL;
8867
0
  }
8868
8869
0
  if (filep->access.fp != NULL) {
8870
0
    return fgets(buf, (int)size, filep->access.fp);
8871
0
  } else {
8872
0
    return NULL;
8873
0
  }
8874
0
}
8875
8876
/* Define the initial recursion depth for procesesing htpasswd files that
8877
 * include other htpasswd
8878
 * (or even the same) files.  It is not difficult to provide a file or files
8879
 * s.t. they force civetweb
8880
 * to infinitely recurse and then crash.
8881
 */
8882
0
#define INITIAL_DEPTH 9
8883
#if INITIAL_DEPTH <= 0
8884
#error Bad INITIAL_DEPTH for recursion, set to at least 1
8885
#endif
8886
8887
#if !defined(NO_FILESYSTEMS)
8888
struct read_auth_file_struct {
8889
  struct mg_connection *conn;
8890
  struct ah ah;
8891
  const char *domain;
8892
  char buf[256 + 256 + 40];
8893
  const char *f_user;
8894
  const char *f_domain;
8895
  const char *f_ha1;
8896
};
8897
8898
8899
static int
8900
read_auth_file(struct mg_file *filep,
8901
               struct read_auth_file_struct *workdata,
8902
               int depth)
8903
0
{
8904
0
  int is_authorized = 0;
8905
0
  struct mg_file fp;
8906
0
  size_t l;
8907
8908
0
  if (!filep || !workdata || (0 == depth)) {
8909
0
    return 0;
8910
0
  }
8911
8912
  /* Loop over passwords file */
8913
0
  while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep) != NULL) {
8914
0
    l = strlen(workdata->buf);
8915
0
    while (l > 0) {
8916
0
      if (isspace((unsigned char)workdata->buf[l - 1])
8917
0
          || iscntrl((unsigned char)workdata->buf[l - 1])) {
8918
0
        l--;
8919
0
        workdata->buf[l] = 0;
8920
0
      } else
8921
0
        break;
8922
0
    }
8923
0
    if (l < 1) {
8924
0
      continue;
8925
0
    }
8926
8927
0
    workdata->f_user = workdata->buf;
8928
8929
0
    if (workdata->f_user[0] == ':') {
8930
      /* user names may not contain a ':' and may not be empty,
8931
       * so lines starting with ':' may be used for a special purpose
8932
       */
8933
0
      if (workdata->f_user[1] == '#') {
8934
        /* :# is a comment */
8935
0
        continue;
8936
0
      } else if (!strncmp(workdata->f_user + 1, "include=", 8)) {
8937
0
        if (mg_fopen(workdata->conn,
8938
0
                     workdata->f_user + 9,
8939
0
                     MG_FOPEN_MODE_READ,
8940
0
                     &fp)) {
8941
0
          is_authorized = read_auth_file(&fp, workdata, depth - 1);
8942
0
          (void)mg_fclose(
8943
0
              &fp.access); /* ignore error on read only file */
8944
8945
          /* No need to continue processing files once we have a
8946
           * match, since nothing will reset it back
8947
           * to 0.
8948
           */
8949
0
          if (is_authorized) {
8950
0
            return is_authorized;
8951
0
          }
8952
0
        } else {
8953
0
          mg_cry_internal(workdata->conn,
8954
0
                          "%s: cannot open authorization file: %s",
8955
0
                          __func__,
8956
0
                          workdata->buf);
8957
0
        }
8958
0
        continue;
8959
0
      }
8960
      /* everything is invalid for the moment (might change in the
8961
       * future) */
8962
0
      mg_cry_internal(workdata->conn,
8963
0
                      "%s: syntax error in authorization file: %s",
8964
0
                      __func__,
8965
0
                      workdata->buf);
8966
0
      continue;
8967
0
    }
8968
8969
0
    workdata->f_domain = strchr(workdata->f_user, ':');
8970
0
    if (workdata->f_domain == NULL) {
8971
0
      mg_cry_internal(workdata->conn,
8972
0
                      "%s: syntax error in authorization file: %s",
8973
0
                      __func__,
8974
0
                      workdata->buf);
8975
0
      continue;
8976
0
    }
8977
0
    *(char *)(workdata->f_domain) = 0;
8978
0
    (workdata->f_domain)++;
8979
8980
0
    workdata->f_ha1 = strchr(workdata->f_domain, ':');
8981
0
    if (workdata->f_ha1 == NULL) {
8982
0
      mg_cry_internal(workdata->conn,
8983
0
                      "%s: syntax error in authorization file: %s",
8984
0
                      __func__,
8985
0
                      workdata->buf);
8986
0
      continue;
8987
0
    }
8988
0
    *(char *)(workdata->f_ha1) = 0;
8989
0
    (workdata->f_ha1)++;
8990
8991
0
    if (!strcmp(workdata->ah.user, workdata->f_user)
8992
0
        && !strcmp(workdata->domain, workdata->f_domain)) {
8993
0
      switch (workdata->ah.type) {
8994
0
      case 1: /* Basic */
8995
0
      {
8996
0
        char md5[33];
8997
0
        mg_md5(md5,
8998
0
               workdata->f_user,
8999
0
               ":",
9000
0
               workdata->domain,
9001
0
               ":",
9002
0
               workdata->ah.plain_password,
9003
0
               NULL);
9004
0
        return 0 == memcmp(workdata->f_ha1, md5, 33);
9005
0
      }
9006
0
      case 2: /* Digest */
9007
0
        return check_password_digest(
9008
0
            workdata->conn->request_info.request_method,
9009
0
            workdata->f_ha1,
9010
0
            workdata->ah.uri,
9011
0
            workdata->ah.nonce,
9012
0
            workdata->ah.nc,
9013
0
            workdata->ah.cnonce,
9014
0
            workdata->ah.qop,
9015
0
            workdata->ah.response);
9016
0
      default: /* None/Other/Unknown */
9017
0
        return 0;
9018
0
      }
9019
0
    }
9020
0
  }
9021
9022
0
  return is_authorized;
9023
0
}
9024
9025
9026
/* Authorize against the opened passwords file. Return 1 if authorized. */
9027
static int
9028
authorize(struct mg_connection *conn, struct mg_file *filep, const char *realm)
9029
0
{
9030
0
  struct read_auth_file_struct workdata;
9031
0
  char buf[MG_BUF_LEN];
9032
9033
0
  if (!conn || !conn->dom_ctx) {
9034
0
    return 0;
9035
0
  }
9036
9037
0
  memset(&workdata, 0, sizeof(workdata));
9038
0
  workdata.conn = conn;
9039
9040
0
  if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) {
9041
0
    return 0;
9042
0
  }
9043
9044
  /* CGI needs it as REMOTE_USER */
9045
0
  conn->request_info.remote_user =
9046
0
      mg_strdup_ctx(workdata.ah.user, conn->phys_ctx);
9047
9048
0
  if (realm) {
9049
0
    workdata.domain = realm;
9050
0
  } else {
9051
0
    workdata.domain = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
9052
0
  }
9053
9054
0
  return read_auth_file(filep, &workdata, INITIAL_DEPTH);
9055
0
}
9056
9057
9058
/* Public function to check http digest authentication header */
9059
CIVETWEB_API int
9060
mg_check_digest_access_authentication(struct mg_connection *conn,
9061
                                      const char *realm,
9062
                                      const char *filename)
9063
0
{
9064
0
  struct mg_file file = STRUCT_FILE_INITIALIZER;
9065
0
  int auth;
9066
9067
0
  if (!conn || !filename) {
9068
0
    return -1;
9069
0
  }
9070
0
  if (!mg_fopen(conn, filename, MG_FOPEN_MODE_READ, &file)) {
9071
0
    return -2;
9072
0
  }
9073
9074
0
  auth = authorize(conn, &file, realm);
9075
9076
0
  mg_fclose(&file.access);
9077
9078
0
  return auth;
9079
0
}
9080
#endif /* NO_FILESYSTEMS */
9081
9082
9083
/* Return 1 if request is authorised, 0 otherwise. */
9084
static int
9085
check_authorization(struct mg_connection *conn, const char *path)
9086
0
{
9087
0
#if !defined(NO_FILESYSTEMS)
9088
0
  char fname[UTF8_PATH_MAX];
9089
0
  struct vec uri_vec, filename_vec;
9090
0
  const char *list;
9091
0
  struct mg_file file = STRUCT_FILE_INITIALIZER;
9092
0
  int authorized = 1, truncated;
9093
9094
0
  if (!conn || !conn->dom_ctx) {
9095
0
    return 0;
9096
0
  }
9097
9098
0
  list = conn->dom_ctx->config[PROTECT_URI];
9099
0
  while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
9100
0
    if (!memcmp(conn->request_info.local_uri, uri_vec.ptr, uri_vec.len)) {
9101
0
      mg_snprintf(conn,
9102
0
                  &truncated,
9103
0
                  fname,
9104
0
                  sizeof(fname),
9105
0
                  "%.*s",
9106
0
                  (int)filename_vec.len,
9107
0
                  filename_vec.ptr);
9108
9109
0
      if (truncated
9110
0
          || !mg_fopen(conn, fname, MG_FOPEN_MODE_READ, &file)) {
9111
0
        mg_cry_internal(conn,
9112
0
                        "%s: cannot open %s: %s",
9113
0
                        __func__,
9114
0
                        fname,
9115
0
                        strerror(errno));
9116
0
      }
9117
0
      break;
9118
0
    }
9119
0
  }
9120
9121
0
  if (!is_file_opened(&file.access)) {
9122
0
    open_auth_file(conn, path, &file);
9123
0
  }
9124
9125
0
  if (is_file_opened(&file.access)) {
9126
0
    authorized = authorize(conn, &file, NULL);
9127
0
    (void)mg_fclose(&file.access); /* ignore error on read only file */
9128
0
  }
9129
9130
0
  return authorized;
9131
#else
9132
  (void)conn;
9133
  (void)path;
9134
  return 1;
9135
#endif /* NO_FILESYSTEMS */
9136
0
}
9137
9138
9139
/* Internal function. Assumes conn is valid */
9140
static void
9141
send_authorization_request(struct mg_connection *conn, const char *realm)
9142
0
{
9143
0
  uint64_t nonce = (uint64_t)(conn->phys_ctx->start_time);
9144
0
  int trunc = 0;
9145
0
  char buf[128];
9146
9147
0
  if (!realm) {
9148
0
    realm = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
9149
0
  }
9150
9151
0
  mg_lock_context(conn->phys_ctx);
9152
0
  nonce += conn->dom_ctx->nonce_count;
9153
0
  ++conn->dom_ctx->nonce_count;
9154
0
  mg_unlock_context(conn->phys_ctx);
9155
9156
0
  nonce ^= conn->dom_ctx->auth_nonce_mask;
9157
0
  conn->must_close = 1;
9158
9159
  /* Create 401 response */
9160
0
  mg_response_header_start(conn, 401);
9161
0
  send_no_cache_header(conn);
9162
0
  send_additional_header(conn);
9163
0
  mg_response_header_add(conn, "Content-Length", "0", -1);
9164
9165
  /* Content for "WWW-Authenticate" header */
9166
0
  mg_snprintf(conn,
9167
0
              &trunc,
9168
0
              buf,
9169
0
              sizeof(buf),
9170
0
              "Digest qop=\"auth\", realm=\"%s\", "
9171
0
              "nonce=\"%" UINT64_FMT "\"",
9172
0
              realm,
9173
0
              nonce);
9174
9175
0
  if (!trunc) {
9176
    /* !trunc should always be true */
9177
0
    mg_response_header_add(conn, "WWW-Authenticate", buf, -1);
9178
0
  }
9179
9180
  /* Send all headers */
9181
0
  mg_response_header_send(conn);
9182
0
}
9183
9184
9185
/* Interface function. Parameters are provided by the user, so do
9186
 * at least some basic checks.
9187
 */
9188
CIVETWEB_API int
9189
mg_send_digest_access_authentication_request(struct mg_connection *conn,
9190
                                             const char *realm)
9191
0
{
9192
0
  if (conn && conn->dom_ctx) {
9193
0
    send_authorization_request(conn, realm);
9194
0
    return 0;
9195
0
  }
9196
0
  return -1;
9197
0
}
9198
9199
9200
#if !defined(NO_FILES)
9201
static int
9202
is_authorized_for_put(struct mg_connection *conn)
9203
0
{
9204
0
  int ret = 0;
9205
9206
0
  if (conn) {
9207
0
    struct mg_file file = STRUCT_FILE_INITIALIZER;
9208
0
    const char *passfile = conn->dom_ctx->config[PUT_DELETE_PASSWORDS_FILE];
9209
9210
0
    if (passfile != NULL
9211
0
        && mg_fopen(conn, passfile, MG_FOPEN_MODE_READ, &file)) {
9212
0
      ret = authorize(conn, &file, NULL);
9213
0
      (void)mg_fclose(&file.access); /* ignore error on read only file */
9214
0
    }
9215
0
  }
9216
9217
0
  DEBUG_TRACE("file write authorization: %i", ret);
9218
0
  return ret;
9219
0
}
9220
#endif
9221
9222
9223
CIVETWEB_API int
9224
mg_modify_passwords_file_ha1(const char *fname,
9225
                             const char *domain,
9226
                             const char *user,
9227
                             const char *ha1)
9228
0
{
9229
0
  int found = 0, i, result = 1;
9230
0
  char line[512], u[256], d[256], h[256];
9231
0
  struct stat st = {0};
9232
0
  FILE *fp = NULL;
9233
0
  char *temp_file = NULL;
9234
0
  int temp_file_offs = 0;
9235
9236
  /* Regard empty password as no password - remove user record. */
9237
0
  if ((ha1 != NULL) && (ha1[0] == '\0')) {
9238
0
    ha1 = NULL;
9239
0
  }
9240
9241
  /* Other arguments must not be empty */
9242
0
  if ((fname == NULL) || (domain == NULL) || (user == NULL)) {
9243
0
    return 0;
9244
0
  }
9245
9246
  /* Using the given file format, user name and domain must not contain
9247
   * the ':' character */
9248
0
  if (strchr(user, ':') != NULL) {
9249
0
    return 0;
9250
0
  }
9251
0
  if (strchr(domain, ':') != NULL) {
9252
0
    return 0;
9253
0
  }
9254
9255
  /* Do not allow control characters like newline in user name and domain.
9256
   * Do not allow excessively long names either. */
9257
0
  for (i = 0; ((i < 255) && (user[i] != 0)); i++) {
9258
0
    if (iscntrl((unsigned char)user[i])) {
9259
0
      return 0;
9260
0
    }
9261
0
  }
9262
0
  if (user[i]) {
9263
0
    return 0; /* user name too long */
9264
0
  }
9265
0
  for (i = 0; ((i < 255) && (domain[i] != 0)); i++) {
9266
0
    if (iscntrl((unsigned char)domain[i])) {
9267
0
      return 0;
9268
0
    }
9269
0
  }
9270
0
  if (domain[i]) {
9271
0
    return 0; /* domain name too long */
9272
0
  }
9273
9274
  /* The maximum length of the path to the password file is limited */
9275
0
  if (strlen(fname) >= UTF8_PATH_MAX) {
9276
0
    return 0;
9277
0
  }
9278
9279
  /* Check if the file exists, and get file size */
9280
0
  if (0 == stat(fname, &st)) {
9281
0
    int temp_buf_len;
9282
0
    if (st.st_size > 10485760) {
9283
      /* Some funster provided a >10 MB text file */
9284
0
      return 0;
9285
0
    }
9286
9287
    /* Add enough space for one more line */
9288
0
    temp_buf_len = (int)st.st_size + 1024;
9289
9290
    /* Allocate memory (instead of using a temporary file) */
9291
0
    temp_file = (char *)mg_calloc((size_t)temp_buf_len, 1);
9292
0
    if (!temp_file) {
9293
      /* Out of memory */
9294
0
      return 0;
9295
0
    }
9296
9297
    /* File exists. Read it into a memory buffer. */
9298
0
    fp = fopen(fname, "r");
9299
0
    if (fp == NULL) {
9300
      /* Cannot read file. No permission? */
9301
0
      mg_free(temp_file);
9302
0
      return 0;
9303
0
    }
9304
9305
    /* Read content and store in memory */
9306
0
    while ((fgets(line, sizeof(line), fp) != NULL)
9307
0
           && ((temp_file_offs + 600) < temp_buf_len)) {
9308
      /* file format is "user:domain:hash\n" */
9309
0
      if (sscanf(line, "%255[^:]:%255[^:]:%255s", u, d, h) != 3) {
9310
0
        continue;
9311
0
      }
9312
0
      u[255] = 0;
9313
0
      d[255] = 0;
9314
0
      h[255] = 0;
9315
9316
0
      if (!strcmp(u, user) && !strcmp(d, domain)) {
9317
        /* Found the user: change the password hash or drop the user
9318
         */
9319
0
        if ((ha1 != NULL) && (!found)) {
9320
0
          i = sprintf(temp_file + temp_file_offs,
9321
0
                      "%s:%s:%s\n",
9322
0
                      user,
9323
0
                      domain,
9324
0
                      ha1);
9325
0
          if (i < 1) {
9326
0
            fclose(fp);
9327
0
            mg_free(temp_file);
9328
0
            return 0;
9329
0
          }
9330
0
          temp_file_offs += i;
9331
0
        }
9332
0
        found = 1;
9333
0
      } else {
9334
        /* Copy existing user, including password hash */
9335
0
        i = sprintf(temp_file + temp_file_offs, "%s:%s:%s\n", u, d, h);
9336
0
        if (i < 1) {
9337
0
          fclose(fp);
9338
0
          mg_free(temp_file);
9339
0
          return 0;
9340
0
        }
9341
0
        temp_file_offs += i;
9342
0
      }
9343
0
    }
9344
0
    fclose(fp);
9345
0
  }
9346
9347
  /* Create new file */
9348
0
  fp = fopen(fname, "w");
9349
0
  if (!fp) {
9350
0
    mg_free(temp_file);
9351
0
    return 0;
9352
0
  }
9353
9354
0
#if !defined(_WIN32)
9355
  /* On Linux & co., restrict file read/write permissions to the owner */
9356
0
  if (fchmod(fileno(fp), S_IRUSR | S_IWUSR) != 0) {
9357
0
    result = 0;
9358
0
  }
9359
0
#endif
9360
9361
0
  if ((temp_file != NULL) && (temp_file_offs > 0)) {
9362
    /* Store buffered content of old file */
9363
0
    if (fwrite(temp_file, 1, (size_t)temp_file_offs, fp)
9364
0
        != (size_t)temp_file_offs) {
9365
0
      result = 0;
9366
0
    }
9367
0
  }
9368
9369
  /* If new user, just add it */
9370
0
  if ((ha1 != NULL) && (!found)) {
9371
0
    if (fprintf(fp, "%s:%s:%s\n", user, domain, ha1) < 6) {
9372
0
      result = 0;
9373
0
    }
9374
0
  }
9375
9376
  /* All data written */
9377
0
  if (fclose(fp) != 0) {
9378
0
    result = 0;
9379
0
  }
9380
9381
0
  mg_free(temp_file);
9382
0
  return result;
9383
0
}
9384
9385
9386
CIVETWEB_API int
9387
mg_modify_passwords_file(const char *fname,
9388
                         const char *domain,
9389
                         const char *user,
9390
                         const char *pass)
9391
0
{
9392
0
  char ha1buf[33];
9393
0
  if ((fname == NULL) || (domain == NULL) || (user == NULL)) {
9394
0
    return 0;
9395
0
  }
9396
0
  if ((pass == NULL) || (pass[0] == 0)) {
9397
0
    return mg_modify_passwords_file_ha1(fname, domain, user, NULL);
9398
0
  }
9399
9400
0
  mg_md5(ha1buf, user, ":", domain, ":", pass, NULL);
9401
0
  return mg_modify_passwords_file_ha1(fname, domain, user, ha1buf);
9402
0
}
9403
9404
9405
static int
9406
is_valid_port(unsigned long port)
9407
38
{
9408
38
  return (port <= 0xffff);
9409
38
}
9410
9411
9412
static int
9413
mg_inet_pton(int af, const char *src, void *dst, size_t dstlen, int resolve_src)
9414
34
{
9415
34
  struct addrinfo hints, *res, *ressave;
9416
34
  int func_ret = 0;
9417
34
  int gai_ret;
9418
9419
34
  memset(&hints, 0, sizeof(struct addrinfo));
9420
34
  hints.ai_family = af;
9421
34
  if (!resolve_src) {
9422
0
    hints.ai_flags = AI_NUMERICHOST;
9423
0
  }
9424
9425
34
  gai_ret = getaddrinfo(src, NULL, &hints, &res);
9426
34
  if (gai_ret != 0) {
9427
    /* gai_strerror could be used to convert gai_ret to a string */
9428
    /* POSIX return values: see
9429
     * http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html
9430
     */
9431
    /* Windows return values: see
9432
     * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx
9433
     */
9434
0
    return 0;
9435
0
  }
9436
9437
34
  ressave = res;
9438
9439
136
  while (res) {
9440
102
    if ((dstlen >= (size_t)res->ai_addrlen)
9441
102
        && (res->ai_addr->sa_family == af)) {
9442
102
      memcpy(dst, res->ai_addr, res->ai_addrlen);
9443
102
      func_ret = 1;
9444
102
    }
9445
102
    res = res->ai_next;
9446
102
  }
9447
9448
34
  freeaddrinfo(ressave);
9449
34
  return func_ret;
9450
34
}
9451
9452
9453
static int
9454
connect_socket(
9455
    struct mg_context *ctx /* may be NULL */,
9456
    const char *host,
9457
    int port,    /* 1..65535, or -99 for domain sockets (may be changed) */
9458
    int use_ssl, /* 0 or 1 */
9459
    struct mg_error_data *error,
9460
    SOCKET *sock /* output: socket, must not be NULL */,
9461
    union usa *sa /* output: socket address, must not be NULL  */
9462
)
9463
34
{
9464
34
  int ip_ver = 0;
9465
34
  int conn_ret = -1;
9466
34
  int sockerr = 0;
9467
34
  *sock = INVALID_SOCKET;
9468
34
  memset(sa, 0, sizeof(*sa));
9469
9470
34
  if (host == NULL) {
9471
0
    if (error != NULL) {
9472
0
      error->code = MG_ERROR_DATA_CODE_INVALID_PARAM;
9473
0
      mg_snprintf(NULL,
9474
0
                  NULL, /* No truncation check for ebuf */
9475
0
                  error->text,
9476
0
                  error->text_buffer_size,
9477
0
                  "%s",
9478
0
                  "NULL host");
9479
0
    }
9480
0
    return 0;
9481
0
  }
9482
9483
#if defined(USE_X_DOM_SOCKET)
9484
  if (port == -99) {
9485
    /* Unix domain socket */
9486
    size_t hostlen = strlen(host);
9487
    if (hostlen >= sizeof(sa->sun.sun_path)) {
9488
      if (error != NULL) {
9489
        error->code = MG_ERROR_DATA_CODE_INVALID_PARAM;
9490
        mg_snprintf(NULL,
9491
                    NULL, /* No truncation check for ebuf */
9492
                    error->text,
9493
                    error->text_buffer_size,
9494
                    "%s",
9495
                    "host length exceeds limit");
9496
      }
9497
      return 0;
9498
    }
9499
  } else
9500
#endif
9501
34
      if ((port <= 0) || !is_valid_port((unsigned)port)) {
9502
0
    if (error != NULL) {
9503
0
      error->code = MG_ERROR_DATA_CODE_INVALID_PARAM;
9504
0
      mg_snprintf(NULL,
9505
0
                  NULL, /* No truncation check for ebuf */
9506
0
                  error->text,
9507
0
                  error->text_buffer_size,
9508
0
                  "%s",
9509
0
                  "invalid port");
9510
0
    }
9511
0
    return 0;
9512
0
  }
9513
9514
34
#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(NO_SSL_DL)
9515
34
#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
9516
34
  if (use_ssl && (TLS_client_method == NULL)) {
9517
0
    if (error != NULL) {
9518
0
      error->code = MG_ERROR_DATA_CODE_INIT_LIBRARY_FAILED;
9519
0
      mg_snprintf(NULL,
9520
0
                  NULL, /* No truncation check for ebuf */
9521
0
                  error->text,
9522
0
                  error->text_buffer_size,
9523
0
                  "%s",
9524
0
                  "SSL is not initialized");
9525
0
    }
9526
0
    return 0;
9527
0
  }
9528
#else
9529
  if (use_ssl && (SSLv23_client_method == NULL)) {
9530
    if (error != 0) {
9531
      error->code = MG_ERROR_DATA_CODE_INIT_LIBRARY_FAILED;
9532
      mg_snprintf(NULL,
9533
                  NULL, /* No truncation check for ebuf */
9534
                  error->text,
9535
                  error->text_buffer_size,
9536
                  "%s",
9537
                  "SSL is not initialized");
9538
    }
9539
    return 0;
9540
  }
9541
#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0*/
9542
#else
9543
  (void)use_ssl;
9544
#endif /* NO SSL */
9545
9546
#if defined(USE_X_DOM_SOCKET)
9547
  if (port == -99) {
9548
    size_t hostlen = strlen(host);
9549
    /* check (hostlen < sizeof(sun.sun_path)) already passed above */
9550
    ip_ver = -99;
9551
    sa->sun.sun_family = AF_UNIX;
9552
    memset(sa->sun.sun_path, 0, sizeof(sa->sun.sun_path));
9553
    memcpy(sa->sun.sun_path, host, hostlen);
9554
  } else
9555
#endif
9556
34
      if (mg_inet_pton(AF_INET, host, &sa->sin, sizeof(sa->sin), 1)) {
9557
34
    sa->sin.sin_port = htons((uint16_t)port);
9558
34
    ip_ver = 4;
9559
#if defined(USE_IPV6)
9560
  } else if (mg_inet_pton(AF_INET6, host, &sa->sin6, sizeof(sa->sin6), 1)) {
9561
    sa->sin6.sin6_port = htons((uint16_t)port);
9562
    ip_ver = 6;
9563
  } else if (host[0] == '[') {
9564
    /* While getaddrinfo on Windows will work with [::1],
9565
     * getaddrinfo on Linux only works with ::1 (without []). */
9566
    size_t l = strlen(host + 1);
9567
    char *h = (l > 1) ? mg_strdup_ctx(host + 1, ctx) : NULL;
9568
    if (h) {
9569
      h[l - 1] = 0;
9570
      if (mg_inet_pton(AF_INET6, h, &sa->sin6, sizeof(sa->sin6), 0)) {
9571
        sa->sin6.sin6_port = htons((uint16_t)port);
9572
        ip_ver = 6;
9573
      }
9574
      mg_free(h);
9575
    }
9576
#endif
9577
34
  }
9578
9579
34
  if (ip_ver == 0) {
9580
0
    if (error != NULL) {
9581
0
      error->code = MG_ERROR_DATA_CODE_HOST_NOT_FOUND;
9582
0
      mg_snprintf(NULL,
9583
0
                  NULL, /* No truncation check for ebuf */
9584
0
                  error->text,
9585
0
                  error->text_buffer_size,
9586
0
                  "%s",
9587
0
                  "host not found");
9588
0
    }
9589
0
    return 0;
9590
0
  }
9591
9592
34
  if (ip_ver == 4) {
9593
34
    *sock = socket(PF_INET, SOCK_STREAM, 0);
9594
34
  }
9595
#if defined(USE_IPV6)
9596
  else if (ip_ver == 6) {
9597
    *sock = socket(PF_INET6, SOCK_STREAM, 0);
9598
  }
9599
#endif
9600
#if defined(USE_X_DOM_SOCKET)
9601
  else if (ip_ver == -99) {
9602
    *sock = socket(AF_UNIX, SOCK_STREAM, 0);
9603
  }
9604
#endif
9605
9606
34
  if (*sock == INVALID_SOCKET) {
9607
0
    if (error != NULL) {
9608
0
      error->code = MG_ERROR_DATA_CODE_OS_ERROR;
9609
0
      error->code_sub = (unsigned)ERRNO;
9610
0
      mg_snprintf(NULL,
9611
0
                  NULL, /* No truncation check for ebuf */
9612
0
                  error->text,
9613
0
                  error->text_buffer_size,
9614
0
                  "socket(): %s",
9615
0
                  strerror(ERRNO));
9616
0
    }
9617
0
    return 0;
9618
0
  }
9619
9620
34
  if (0 != set_non_blocking_mode(*sock)) {
9621
0
    if (error != NULL) {
9622
0
      error->code = MG_ERROR_DATA_CODE_OS_ERROR;
9623
0
      error->code_sub = (unsigned)ERRNO;
9624
0
      mg_snprintf(NULL,
9625
0
                  NULL, /* No truncation check for ebuf */
9626
0
                  error->text,
9627
0
                  error->text_buffer_size,
9628
0
                  "Cannot set socket to non-blocking: %s",
9629
0
                  strerror(ERRNO));
9630
0
    }
9631
0
    closesocket(*sock);
9632
0
    *sock = INVALID_SOCKET;
9633
0
    return 0;
9634
0
  }
9635
9636
34
  set_close_on_exec(*sock, NULL, ctx);
9637
9638
34
  if (ip_ver == 4) {
9639
    /* connected with IPv4 */
9640
34
    conn_ret = connect(*sock,
9641
34
                       (struct sockaddr *)((void *)&sa->sin),
9642
34
                       sizeof(sa->sin));
9643
34
  }
9644
#if defined(USE_IPV6)
9645
  else if (ip_ver == 6) {
9646
    /* connected with IPv6 */
9647
    conn_ret = connect(*sock,
9648
                       (struct sockaddr *)((void *)&sa->sin6),
9649
                       sizeof(sa->sin6));
9650
  }
9651
#endif
9652
#if defined(USE_X_DOM_SOCKET)
9653
  else if (ip_ver == -99) {
9654
    /* connected to domain socket */
9655
    conn_ret = connect(*sock,
9656
                       (struct sockaddr *)((void *)&sa->sun),
9657
                       sizeof(sa->sun));
9658
  }
9659
#endif
9660
9661
34
  if (conn_ret != 0) {
9662
34
    sockerr = ERRNO;
9663
34
  }
9664
9665
#if defined(_WIN32)
9666
  if ((conn_ret != 0) && (sockerr == WSAEWOULDBLOCK)) {
9667
#else
9668
34
  if ((conn_ret != 0) && (sockerr == EINPROGRESS)) {
9669
34
#endif
9670
    /* Data for getsockopt */
9671
34
    void *psockerr = &sockerr;
9672
34
    int ret;
9673
9674
#if defined(_WIN32)
9675
    int len = (int)sizeof(sockerr);
9676
#else
9677
34
    socklen_t len = (socklen_t)sizeof(sockerr);
9678
34
#endif
9679
9680
    /* Data for poll */
9681
34
    struct mg_pollfd pfd[2];
9682
34
    int pollres;
9683
34
    int ms_wait = 10000;       /* 10 second timeout */
9684
34
    stop_flag_t nonstop = 0;   /* STOP_FLAG_ASSIGN(&nonstop, 0); */
9685
34
    unsigned int num_sock = 1; /* use one or two sockets */
9686
9687
    /* For a non-blocking socket, the connect sequence is:
9688
     * 1) call connect (will not block)
9689
     * 2) wait until the socket is ready for writing (select or poll)
9690
     * 3) check connection state with getsockopt
9691
     */
9692
34
    pfd[0].fd = *sock;
9693
34
    pfd[0].events = POLLOUT;
9694
9695
34
    if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
9696
0
      pfd[num_sock].fd = ctx->thread_shutdown_notification_socket;
9697
0
      pfd[num_sock].events = POLLIN;
9698
0
      num_sock++;
9699
0
    }
9700
9701
34
    pollres =
9702
34
        mg_poll(pfd, num_sock, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop);
9703
9704
34
    if (pollres != 1) {
9705
      /* Not connected */
9706
0
      if (error != NULL) {
9707
0
        error->code = MG_ERROR_DATA_CODE_CONNECT_TIMEOUT;
9708
0
        mg_snprintf(NULL,
9709
0
                    NULL, /* No truncation check for ebuf */
9710
0
                    error->text,
9711
0
                    error->text_buffer_size,
9712
0
                    "connect(%s:%d): timeout",
9713
0
                    host,
9714
0
                    port);
9715
0
      }
9716
0
      closesocket(*sock);
9717
0
      *sock = INVALID_SOCKET;
9718
0
      return 0;
9719
0
    }
9720
9721
#if defined(_WIN32)
9722
    ret = getsockopt(*sock, SOL_SOCKET, SO_ERROR, (char *)psockerr, &len);
9723
#else
9724
34
    ret = getsockopt(*sock, SOL_SOCKET, SO_ERROR, psockerr, &len);
9725
34
#endif
9726
9727
34
    if ((ret == 0) && (sockerr == 0)) {
9728
34
      conn_ret = 0;
9729
34
    }
9730
34
  }
9731
9732
34
  if (conn_ret != 0) {
9733
    /* Not connected */
9734
0
    if (error != NULL) {
9735
0
      error->code = MG_ERROR_DATA_CODE_CONNECT_FAILED;
9736
0
      error->code_sub = (unsigned)ERRNO;
9737
0
      mg_snprintf(NULL,
9738
0
                  NULL, /* No truncation check for ebuf */
9739
0
                  error->text,
9740
0
                  error->text_buffer_size,
9741
0
                  "connect(%s:%d): error %s",
9742
0
                  host,
9743
0
                  port,
9744
0
                  strerror(sockerr));
9745
0
    }
9746
0
    closesocket(*sock);
9747
0
    *sock = INVALID_SOCKET;
9748
0
    return 0;
9749
0
  }
9750
9751
34
  return 1;
9752
34
}
9753
9754
9755
CIVETWEB_API int
9756
mg_url_encode(const char *src, char *dst, size_t dst_len)
9757
0
{
9758
0
  static const char *dont_escape = "._-$,;~()";
9759
0
  static const char *hex = "0123456789abcdef";
9760
0
  char *pos = dst;
9761
0
  const char *end = dst + dst_len - 1;
9762
9763
0
  for (; ((*src != '\0') && (pos < end)); src++, pos++) {
9764
0
    if (isalnum((unsigned char)*src)
9765
0
        || (strchr(dont_escape, *src) != NULL)) {
9766
0
      *pos = *src;
9767
0
    } else if (pos + 2 < end) {
9768
0
      pos[0] = '%';
9769
0
      pos[1] = hex[(unsigned char)*src >> 4];
9770
0
      pos[2] = hex[(unsigned char)*src & 0xf];
9771
0
      pos += 2;
9772
0
    } else {
9773
0
      break;
9774
0
    }
9775
0
  }
9776
9777
0
  *pos = '\0';
9778
0
  return (*src == '\0') ? (int)(pos - dst) : -1;
9779
0
}
9780
9781
/* Return 0 on success, non-zero if an error occurs. */
9782
9783
static int
9784
print_dir_entry(struct mg_connection *conn, struct de *de)
9785
0
{
9786
0
  size_t namesize, escsize, i;
9787
0
  char *href, *esc, *p;
9788
0
  char size[64], mod[64];
9789
#if defined(REENTRANT_TIME)
9790
  struct tm _tm;
9791
  struct tm *tm = &_tm;
9792
#else
9793
0
  struct tm *tm;
9794
0
#endif
9795
9796
  /* Estimate worst case size for encoding and escaping */
9797
0
  namesize = strlen(de->file_name) + 1;
9798
0
  escsize = de->file_name[strcspn(de->file_name, "&<>")] ? namesize * 5 : 0;
9799
0
  href = (char *)mg_malloc(namesize * 3 + escsize);
9800
0
  if (href == NULL) {
9801
0
    return -1;
9802
0
  }
9803
0
  mg_url_encode(de->file_name, href, namesize * 3);
9804
0
  esc = NULL;
9805
0
  if (escsize > 0) {
9806
    /* HTML escaping needed */
9807
0
    esc = href + namesize * 3;
9808
0
    for (i = 0, p = esc; de->file_name[i]; i++, p += strlen(p)) {
9809
0
      mg_strlcpy(p, de->file_name + i, 2);
9810
0
      if (*p == '&') {
9811
0
        strcpy(p, "&amp;");
9812
0
      } else if (*p == '<') {
9813
0
        strcpy(p, "&lt;");
9814
0
      } else if (*p == '>') {
9815
0
        strcpy(p, "&gt;");
9816
0
      }
9817
0
    }
9818
0
  }
9819
9820
0
  if (de->file.is_directory) {
9821
0
    mg_snprintf(conn,
9822
0
                NULL, /* Buffer is big enough */
9823
0
                size,
9824
0
                sizeof(size),
9825
0
                "%s",
9826
0
                "[DIRECTORY]");
9827
0
  } else {
9828
    /* We use (signed) cast below because MSVC 6 compiler cannot
9829
     * convert unsigned __int64 to double. Sigh. */
9830
0
    if (de->file.size < 1024) {
9831
0
      mg_snprintf(conn,
9832
0
                  NULL, /* Buffer is big enough */
9833
0
                  size,
9834
0
                  sizeof(size),
9835
0
                  "%d",
9836
0
                  (int)de->file.size);
9837
0
    } else if (de->file.size < 0x100000) {
9838
0
      mg_snprintf(conn,
9839
0
                  NULL, /* Buffer is big enough */
9840
0
                  size,
9841
0
                  sizeof(size),
9842
0
                  "%.1fk",
9843
0
                  (double)de->file.size / 1024.0);
9844
0
    } else if (de->file.size < 0x40000000) {
9845
0
      mg_snprintf(conn,
9846
0
                  NULL, /* Buffer is big enough */
9847
0
                  size,
9848
0
                  sizeof(size),
9849
0
                  "%.1fM",
9850
0
                  (double)de->file.size / 1048576);
9851
0
    } else {
9852
0
      mg_snprintf(conn,
9853
0
                  NULL, /* Buffer is big enough */
9854
0
                  size,
9855
0
                  sizeof(size),
9856
0
                  "%.1fG",
9857
0
                  (double)de->file.size / 1073741824);
9858
0
    }
9859
0
  }
9860
9861
  /* Note: mg_snprintf will not cause a buffer overflow above.
9862
   * So, string truncation checks are not required here. */
9863
9864
#if defined(REENTRANT_TIME)
9865
  localtime_r(&de->file.last_modified, tm);
9866
#else
9867
0
  tm = localtime(&de->file.last_modified);
9868
0
#endif
9869
0
  if (tm != NULL) {
9870
0
    strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm);
9871
0
  } else {
9872
0
    mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod));
9873
0
  }
9874
0
  mg_printf(conn,
9875
0
            "<tr><td><a href=\"%s%s\">%s%s</a></td>"
9876
0
            "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
9877
0
            href,
9878
0
            de->file.is_directory ? "/" : "",
9879
0
            esc ? esc : de->file_name,
9880
0
            de->file.is_directory ? "/" : "",
9881
0
            mod,
9882
0
            size);
9883
0
  mg_free(href);
9884
0
  return 0;
9885
0
}
9886
9887
9888
/* This function is called from send_directory() and used for
9889
 * sorting directory entries by size, name, or modification time. */
9890
static int
9891
compare_dir_entries(const void *p1, const void *p2, void *arg)
9892
0
{
9893
0
  const char *query_string = (const char *)(arg != NULL ? arg : "");
9894
0
  if (p1 && p2) {
9895
0
    const struct de *a = (const struct de *)p1, *b = (const struct de *)p2;
9896
0
    int cmp_result = 0;
9897
9898
0
    if ((query_string == NULL) || (query_string[0] == '\0')) {
9899
0
      query_string = "n";
9900
0
    }
9901
9902
    /* Sort Directories vs Files */
9903
0
    if (a->file.is_directory && !b->file.is_directory) {
9904
0
      return -1; /* Always put directories on top */
9905
0
    } else if (!a->file.is_directory && b->file.is_directory) {
9906
0
      return 1; /* Always put directories on top */
9907
0
    }
9908
9909
    /* Sort by size or date */
9910
0
    if (*query_string == 's') {
9911
0
      cmp_result = (a->file.size == b->file.size)
9912
0
                       ? 0
9913
0
                       : ((a->file.size > b->file.size) ? 1 : -1);
9914
0
    } else if (*query_string == 'd') {
9915
0
      cmp_result =
9916
0
          (a->file.last_modified == b->file.last_modified)
9917
0
              ? 0
9918
0
              : ((a->file.last_modified > b->file.last_modified) ? 1
9919
0
                                                                 : -1);
9920
0
    }
9921
9922
    /* Sort by name:
9923
     * if (*query_string == 'n')  ...
9924
     * but also sort files of same size/date by name as secondary criterion.
9925
     */
9926
0
    if (cmp_result == 0) {
9927
0
      cmp_result = strcmp(a->file_name, b->file_name);
9928
0
    }
9929
9930
    /* For descending order, invert result */
9931
0
    return (query_string[1] == 'd') ? -cmp_result : cmp_result;
9932
0
  }
9933
0
  return 0;
9934
0
}
9935
9936
9937
static int
9938
must_hide_file(struct mg_connection *conn, const char *path)
9939
0
{
9940
0
  if (conn && conn->dom_ctx) {
9941
0
    const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
9942
0
    const char *pattern = conn->dom_ctx->config[HIDE_FILES];
9943
0
    return (match_prefix_strlen(pw_pattern, path) > 0)
9944
0
           || (match_prefix_strlen(pattern, path) > 0);
9945
0
  }
9946
0
  return 0;
9947
0
}
9948
9949
9950
#if !defined(NO_FILESYSTEMS)
9951
static int
9952
scan_directory(struct mg_connection *conn,
9953
               const char *dir,
9954
               void *data,
9955
               int (*cb)(struct de *, void *))
9956
0
{
9957
0
  char path[UTF8_PATH_MAX];
9958
0
  struct dirent *dp;
9959
0
  DIR *dirp;
9960
0
  struct de de;
9961
0
  int truncated;
9962
9963
0
  if ((dirp = mg_opendir(conn, dir)) == NULL) {
9964
0
    return 0;
9965
0
  } else {
9966
9967
0
    while ((dp = mg_readdir(dirp)) != NULL) {
9968
      /* Do not show current dir and hidden files */
9969
0
      if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")
9970
0
          || must_hide_file(conn, dp->d_name)) {
9971
0
        continue;
9972
0
      }
9973
9974
0
      mg_snprintf(
9975
0
          conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name);
9976
9977
      /* If we don't memset stat structure to zero, mtime will have
9978
       * garbage and strftime() will segfault later on in
9979
       * print_dir_entry(). memset is required only if mg_stat()
9980
       * fails. For more details, see
9981
       * http://code.google.com/p/mongoose/issues/detail?id=79 */
9982
0
      memset(&de.file, 0, sizeof(de.file));
9983
9984
0
      if (truncated) {
9985
        /* If the path is not complete, skip processing. */
9986
0
        continue;
9987
0
      }
9988
9989
0
      if (!mg_stat(conn, path, &de.file)) {
9990
0
        mg_cry_internal(conn,
9991
0
                        "%s: mg_stat(%s) failed: %s",
9992
0
                        __func__,
9993
0
                        path,
9994
0
                        strerror(ERRNO));
9995
0
      }
9996
0
      de.file_name = dp->d_name;
9997
0
      if (cb(&de, data)) {
9998
        /* stopped */
9999
0
        break;
10000
0
      }
10001
0
    }
10002
0
    (void)mg_closedir(dirp);
10003
0
  }
10004
0
  return 1;
10005
0
}
10006
#endif /* NO_FILESYSTEMS */
10007
10008
10009
#if !defined(NO_FILES)
10010
static int
10011
remove_directory(struct mg_connection *conn, const char *dir)
10012
0
{
10013
0
  char path[UTF8_PATH_MAX];
10014
0
  struct dirent *dp;
10015
0
  DIR *dirp;
10016
0
  struct de de;
10017
0
  int truncated;
10018
0
  int ok = 1;
10019
10020
0
  if ((dirp = mg_opendir(conn, dir)) == NULL) {
10021
0
    return 0;
10022
0
  } else {
10023
10024
0
    while ((dp = mg_readdir(dirp)) != NULL) {
10025
      /* Do not show current dir (but show hidden files as they will
10026
       * also be removed) */
10027
0
      if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
10028
0
        continue;
10029
0
      }
10030
10031
0
      mg_snprintf(
10032
0
          conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name);
10033
10034
      /* If we don't memset stat structure to zero, mtime will have
10035
       * garbage and strftime() will segfault later on in
10036
       * print_dir_entry(). memset is required only if mg_stat()
10037
       * fails. For more details, see
10038
       * http://code.google.com/p/mongoose/issues/detail?id=79 */
10039
0
      memset(&de.file, 0, sizeof(de.file));
10040
10041
0
      if (truncated) {
10042
        /* Do not delete anything shorter */
10043
0
        ok = 0;
10044
0
        continue;
10045
0
      }
10046
10047
0
      if (!mg_stat(conn, path, &de.file)) {
10048
0
        mg_cry_internal(conn,
10049
0
                        "%s: mg_stat(%s) failed: %s",
10050
0
                        __func__,
10051
0
                        path,
10052
0
                        strerror(ERRNO));
10053
0
        ok = 0;
10054
0
      }
10055
10056
0
      if (de.file.is_directory) {
10057
0
        if (remove_directory(conn, path) == 0) {
10058
0
          ok = 0;
10059
0
        }
10060
0
      } else {
10061
        /* This will fail file is the file is in memory */
10062
0
        if (mg_remove(conn, path) == 0) {
10063
0
          ok = 0;
10064
0
        }
10065
0
      }
10066
0
    }
10067
0
    (void)mg_closedir(dirp);
10068
10069
0
    IGNORE_UNUSED_RESULT(rmdir(dir));
10070
0
  }
10071
10072
0
  return ok;
10073
0
}
10074
#endif
10075
10076
10077
struct dir_scan_data {
10078
  struct de *entries;
10079
  size_t num_entries;
10080
  size_t arr_size;
10081
};
10082
10083
10084
#if !defined(NO_FILESYSTEMS)
10085
static int
10086
dir_scan_callback(struct de *de, void *data)
10087
0
{
10088
0
  struct dir_scan_data *dsd = (struct dir_scan_data *)data;
10089
0
  struct de *entries = dsd->entries;
10090
10091
0
  if ((entries == NULL) || (dsd->num_entries >= dsd->arr_size)) {
10092
    /* Here "entries" is a temporary pointer and can be replaced,
10093
     * "dsd->entries" is the original pointer */
10094
0
    entries =
10095
0
        (struct de *)mg_realloc(entries,
10096
0
                                dsd->arr_size * 2 * sizeof(entries[0]));
10097
0
    if (entries == NULL) {
10098
      /* stop scan */
10099
0
      return 1;
10100
0
    }
10101
0
    dsd->entries = entries;
10102
0
    dsd->arr_size *= 2;
10103
0
  }
10104
0
  entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
10105
0
  if (entries[dsd->num_entries].file_name == NULL) {
10106
    /* stop scan */
10107
0
    return 1;
10108
0
  }
10109
0
  entries[dsd->num_entries].file = de->file;
10110
0
  dsd->num_entries++;
10111
10112
0
  return 0;
10113
0
}
10114
10115
10116
static void
10117
handle_directory_request(struct mg_connection *conn, const char *dir)
10118
0
{
10119
0
  size_t i;
10120
0
  int sort_direction;
10121
0
  struct dir_scan_data data = {NULL, 0, 128};
10122
0
  char date[64], *esc, *p;
10123
0
  const char *title;
10124
0
  time_t curtime = time(NULL);
10125
10126
0
  if (!conn) {
10127
0
    return;
10128
0
  }
10129
10130
0
  if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
10131
0
    mg_send_http_error(conn,
10132
0
                       500,
10133
0
                       "Error: Cannot open directory\nopendir(%s): %s",
10134
0
                       dir,
10135
0
                       strerror(ERRNO));
10136
0
    return;
10137
0
  }
10138
10139
0
  gmt_time_string(date, sizeof(date), &curtime);
10140
10141
0
  esc = NULL;
10142
0
  title = conn->request_info.local_uri;
10143
0
  if (title[strcspn(title, "&<>")]) {
10144
    /* HTML escaping needed */
10145
0
    esc = (char *)mg_malloc(strlen(title) * 5 + 1);
10146
0
    if (esc) {
10147
0
      for (i = 0, p = esc; title[i]; i++, p += strlen(p)) {
10148
0
        mg_strlcpy(p, title + i, 2);
10149
0
        if (*p == '&') {
10150
0
          strcpy(p, "&amp;");
10151
0
        } else if (*p == '<') {
10152
0
          strcpy(p, "&lt;");
10153
0
        } else if (*p == '>') {
10154
0
          strcpy(p, "&gt;");
10155
0
        }
10156
0
      }
10157
0
    } else {
10158
0
      title = "";
10159
0
    }
10160
0
  }
10161
10162
0
  sort_direction = ((conn->request_info.query_string != NULL)
10163
0
                    && (conn->request_info.query_string[0] != '\0')
10164
0
                    && (conn->request_info.query_string[1] == 'd'))
10165
0
                       ? 'a'
10166
0
                       : 'd';
10167
10168
0
  conn->must_close = 1;
10169
10170
  /* Create 200 OK response */
10171
0
  mg_response_header_start(conn, 200);
10172
0
  send_static_cache_header(conn);
10173
0
  send_additional_header(conn);
10174
0
  mg_response_header_add(conn,
10175
0
                         "Content-Type",
10176
0
                         "text/html; charset=utf-8",
10177
0
                         -1);
10178
10179
  /* Send all headers */
10180
0
  mg_response_header_send(conn);
10181
10182
  /* Body */
10183
0
  mg_printf(conn,
10184
0
            "<!DOCTYPE html>"
10185
0
            "<html><head><title>Index of %s</title>"
10186
0
            "<style>th {text-align: left;}</style></head>"
10187
0
            "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
10188
0
            "<tr><th><a href=\"?n%c\">Name</a></th>"
10189
0
            "<th><a href=\"?d%c\">Modified</a></th>"
10190
0
            "<th><a href=\"?s%c\">Size</a></th></tr>"
10191
0
            "<tr><td colspan=\"3\"><hr></td></tr>",
10192
0
            esc ? esc : title,
10193
0
            esc ? esc : title,
10194
0
            sort_direction,
10195
0
            sort_direction,
10196
0
            sort_direction);
10197
0
  mg_free(esc);
10198
10199
  /* Print first entry - link to a parent directory */
10200
0
  mg_printf(conn,
10201
0
            "<tr><td><a href=\"%s\">%s</a></td>"
10202
0
            "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
10203
0
            "..",
10204
0
            "Parent directory",
10205
0
            "-",
10206
0
            "-");
10207
10208
  /* Sort and print directory entries */
10209
0
  if (data.entries != NULL) {
10210
0
    mg_sort(data.entries,
10211
0
            data.num_entries,
10212
0
            sizeof(data.entries[0]),
10213
0
            compare_dir_entries,
10214
0
            (void *)conn->request_info.query_string);
10215
0
    for (i = 0; i < data.num_entries; i++) {
10216
0
      print_dir_entry(conn, &data.entries[i]);
10217
0
      mg_free(data.entries[i].file_name);
10218
0
    }
10219
0
    mg_free(data.entries);
10220
0
  }
10221
10222
0
  mg_printf(conn, "%s", "</table></pre></body></html>");
10223
0
  conn->status_code = 200;
10224
0
}
10225
#endif /* NO_FILESYSTEMS */
10226
10227
10228
/* Send len bytes from the opened file to the client. */
10229
static void
10230
send_file_data(struct mg_connection *conn,
10231
               struct mg_file *filep,
10232
               int64_t offset,
10233
               int64_t len,
10234
               int no_buffering)
10235
0
{
10236
0
  char buf[MG_BUF_LEN];
10237
0
  int to_read, num_read, num_written;
10238
0
  int64_t size;
10239
10240
0
  if (!filep || !conn) {
10241
0
    return;
10242
0
  }
10243
10244
  /* Sanity check the offset */
10245
0
  size = (filep->stat.size > INT64_MAX) ? INT64_MAX
10246
0
                                        : (int64_t)(filep->stat.size);
10247
0
  offset = (offset < 0) ? 0 : ((offset > size) ? size : offset);
10248
10249
0
  if (len > 0 && filep->access.fp != NULL) {
10250
    /* file stored on disk */
10251
0
#if defined(__linux__)
10252
    /* sendfile is only available for Linux */
10253
0
    if ((conn->ssl == 0) && (conn->throttle == 0)
10254
0
        && (!mg_strcasecmp(conn->dom_ctx->config[ALLOW_SENDFILE_CALL],
10255
0
                           "yes"))) {
10256
0
      off_t sf_offs = (off_t)offset;
10257
0
      ssize_t sf_sent;
10258
0
      int sf_file = fileno(filep->access.fp);
10259
0
      int loop_cnt = 0;
10260
10261
0
      do {
10262
        /* 2147479552 (0x7FFFF000) is a limit found by experiment on
10263
         * 64 bit Linux (2^31 minus one memory page of 4k?). */
10264
0
        size_t sf_tosend =
10265
0
            (size_t)((len < 0x7FFFF000) ? len : 0x7FFFF000);
10266
0
        sf_sent =
10267
0
            sendfile(conn->client.sock, sf_file, &sf_offs, sf_tosend);
10268
0
        if (sf_sent > 0) {
10269
0
          len -= sf_sent;
10270
0
          offset += sf_sent;
10271
0
        } else if (loop_cnt == 0) {
10272
          /* This file can not be sent using sendfile.
10273
           * This might be the case for pseudo-files in the
10274
           * /sys/ and /proc/ file system.
10275
           * Use the regular user mode copy code instead. */
10276
0
          break;
10277
0
        } else if (sf_sent == 0) {
10278
          /* No error, but 0 bytes sent. May be EOF? */
10279
0
          return;
10280
0
        }
10281
0
        loop_cnt++;
10282
10283
0
      } while ((len > 0) && (sf_sent >= 0));
10284
10285
0
      if (sf_sent > 0) {
10286
0
        return; /* OK */
10287
0
      }
10288
10289
      /* sf_sent<0 means error, thus fall back to the classic way */
10290
      /* This is always the case, if sf_file is not a "normal" file,
10291
       * e.g., for sending data from the output of a CGI process. */
10292
0
      offset = (int64_t)sf_offs;
10293
0
    }
10294
0
#endif
10295
0
    if ((offset > 0) && (fseeko(filep->access.fp, offset, SEEK_SET) != 0)) {
10296
0
      mg_cry_internal(conn,
10297
0
                      "%s: fseeko() failed: %s",
10298
0
                      __func__,
10299
0
                      strerror(ERRNO));
10300
0
      mg_send_http_error(
10301
0
          conn,
10302
0
          500,
10303
0
          "%s",
10304
0
          "Error: Unable to access file at requested position.");
10305
0
    } else {
10306
0
      while (len > 0) {
10307
        /* Calculate how much to read from the file into the buffer. */
10308
        /* If no_buffering is set, we should not wait until the
10309
         * CGI->Server buffer is filled, but send everything
10310
         * immediately. In theory buffering could be turned off using
10311
         * setbuf(filep->access.fp, NULL);
10312
         * setvbuf(filep->access.fp, NULL, _IONBF, 0);
10313
         * but in practice this does not work. A "Linux only" solution
10314
         * may be to use select(). The only portable way is to read byte
10315
         * by byte, but this is quite inefficient from a performance
10316
         * point of view. */
10317
0
        to_read = no_buffering ? 1 : sizeof(buf);
10318
0
        if ((int64_t)to_read > len) {
10319
0
          to_read = (int)len;
10320
0
        }
10321
10322
        /* Read from file, exit the loop on error */
10323
0
        if ((num_read =
10324
0
                 (int)fread(buf, 1, (size_t)to_read, filep->access.fp))
10325
0
            <= 0) {
10326
0
          break;
10327
0
        }
10328
10329
        /* Send read bytes to the client, exit the loop on error */
10330
0
        if ((num_written = mg_write(conn, buf, (size_t)num_read))
10331
0
            != num_read) {
10332
0
          break;
10333
0
        }
10334
10335
        /* Both read and were successful, adjust counters */
10336
0
        len -= num_written;
10337
0
      }
10338
0
    }
10339
0
  }
10340
0
}
10341
10342
10343
static int
10344
parse_range_header(const char *header, int64_t *a, int64_t *b)
10345
0
{
10346
0
  return sscanf(header,
10347
0
                "bytes=%" INT64_FMT "-%" INT64_FMT,
10348
0
                a,
10349
0
                b); // NOLINT(cert-err34-c) 'sscanf' used to convert a string
10350
                    // to an integer value, but function will not report
10351
                    // conversion errors; consider using 'strtol' instead
10352
0
}
10353
10354
10355
static void
10356
construct_etag(char *buf, size_t buf_len, const struct mg_file_stat *filestat)
10357
0
{
10358
0
  if ((filestat != NULL) && (buf != NULL)) {
10359
0
    mg_snprintf(NULL,
10360
0
                NULL, /* All calls to construct_etag use 64 byte buffer */
10361
0
                buf,
10362
0
                buf_len,
10363
0
                "\"%lx.%" INT64_FMT "\"",
10364
0
                (unsigned long)filestat->last_modified,
10365
0
                filestat->size);
10366
0
  }
10367
0
}
10368
10369
10370
static void
10371
fclose_on_exec(struct mg_file_access *filep, struct mg_connection *conn)
10372
0
{
10373
0
  if (filep != NULL && filep->fp != NULL) {
10374
#if defined(_WIN32)
10375
    (void)conn; /* Unused. */
10376
#else
10377
0
    if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) {
10378
0
      mg_cry_internal(conn,
10379
0
                      "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
10380
0
                      __func__,
10381
0
                      strerror(ERRNO));
10382
0
    }
10383
0
#endif
10384
0
  }
10385
0
}
10386
10387
10388
#if defined(USE_ZLIB)
10389
#include "mod_zlib.inl"
10390
#endif
10391
10392
10393
#if !defined(NO_FILESYSTEMS)
10394
static void
10395
handle_static_file_request(struct mg_connection *conn,
10396
                           const char *path,
10397
                           struct mg_file *filep,
10398
                           const char *mime_type,
10399
                           const char *additional_headers)
10400
0
{
10401
0
  char lm[64], etag[64];
10402
0
  char range[128]; /* large enough, so there will be no overflow */
10403
0
  const char *range_hdr;
10404
0
  int64_t cl, r1, r2;
10405
0
  struct vec mime_vec;
10406
0
  int n, truncated;
10407
0
  char gz_path[UTF8_PATH_MAX];
10408
0
  const char *encoding = 0;
10409
0
  int is_head_request;
10410
10411
#if defined(USE_ZLIB)
10412
  /* Compression is allowed, unless there is a reason not to use
10413
   * compression. If the file is already compressed, too small or a
10414
   * "range" request was made, on the fly compression is not possible. */
10415
  int allow_on_the_fly_compression = 1;
10416
#endif
10417
10418
0
  if ((conn == NULL) || (conn->dom_ctx == NULL) || (filep == NULL)) {
10419
0
    return;
10420
0
  }
10421
10422
0
  is_head_request = !strcmp(conn->request_info.request_method, "HEAD");
10423
10424
0
  if (mime_type == NULL) {
10425
0
    get_mime_type(conn, path, &mime_vec);
10426
0
  } else {
10427
0
    mime_vec.ptr = mime_type;
10428
0
    mime_vec.len = strlen(mime_type);
10429
0
  }
10430
0
  if (filep->stat.size > INT64_MAX) {
10431
0
    mg_send_http_error(conn,
10432
0
                       500,
10433
0
                       "Error: File size is too large to send\n%" INT64_FMT,
10434
0
                       filep->stat.size);
10435
0
    return;
10436
0
  }
10437
0
  cl = (int64_t)filep->stat.size;
10438
0
  conn->status_code = 200;
10439
0
  range[0] = '\0';
10440
10441
#if defined(USE_ZLIB)
10442
  /* if this file is in fact a pre-gzipped file, rewrite its filename
10443
   * it's important to rewrite the filename after resolving
10444
   * the mime type from it, to preserve the actual file's type */
10445
  if (!conn->accept_gzip) {
10446
    allow_on_the_fly_compression = 0;
10447
  }
10448
#endif
10449
10450
  /* Check if there is a range header */
10451
0
  range_hdr = mg_get_header(conn, "Range");
10452
10453
  /* For gzipped files, add *.gz */
10454
0
  if (filep->stat.is_gzipped) {
10455
0
    mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path);
10456
10457
0
    if (truncated) {
10458
0
      mg_send_http_error(conn,
10459
0
                         500,
10460
0
                         "Error: Path of zipped file too long (%s)",
10461
0
                         path);
10462
0
      return;
10463
0
    }
10464
10465
0
    path = gz_path;
10466
0
    encoding = "gzip";
10467
10468
#if defined(USE_ZLIB)
10469
    /* File is already compressed. No "on the fly" compression. */
10470
    allow_on_the_fly_compression = 0;
10471
#endif
10472
0
  } else if ((conn->accept_gzip) && (range_hdr == NULL)
10473
0
             && (filep->stat.size >= MG_FILE_COMPRESSION_SIZE_LIMIT)) {
10474
0
    struct mg_file_stat file_stat;
10475
10476
0
    mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path);
10477
10478
0
    if (!truncated && mg_stat(conn, gz_path, &file_stat)
10479
0
        && !file_stat.is_directory) {
10480
0
      file_stat.is_gzipped = 1;
10481
0
      filep->stat = file_stat;
10482
0
      cl = (int64_t)filep->stat.size;
10483
0
      path = gz_path;
10484
0
      encoding = "gzip";
10485
10486
#if defined(USE_ZLIB)
10487
      /* File is already compressed. No "on the fly" compression. */
10488
      allow_on_the_fly_compression = 0;
10489
#endif
10490
0
    }
10491
0
  }
10492
10493
0
  if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) {
10494
0
    mg_send_http_error(conn,
10495
0
                       500,
10496
0
                       "Error: Cannot open file\nfopen(%s): %s",
10497
0
                       path,
10498
0
                       strerror(ERRNO));
10499
0
    return;
10500
0
  }
10501
10502
0
  fclose_on_exec(&filep->access, conn);
10503
10504
  /* If "Range" request was made: parse header, send only selected part
10505
   * of the file. */
10506
0
  r1 = r2 = 0;
10507
0
  if ((range_hdr != NULL)
10508
0
      && ((n = parse_range_header(range_hdr, &r1, &r2)) > 0) && (r1 >= 0)
10509
0
      && (r2 >= 0)) {
10510
    /* actually, range requests don't play well with a pre-gzipped
10511
     * file (since the range is specified in the uncompressed space) */
10512
0
    if (filep->stat.is_gzipped) {
10513
0
      mg_send_http_error(
10514
0
          conn,
10515
0
          416, /* 416 = Range Not Satisfiable */
10516
0
          "%s",
10517
0
          "Error: Range requests in gzipped files are not supported");
10518
0
      (void)mg_fclose(
10519
0
          &filep->access); /* ignore error on read only file */
10520
0
      return;
10521
0
    }
10522
0
    conn->status_code = 206;
10523
0
    cl = (n == 2) ? (((r2 > cl) ? cl : r2) - r1 + 1) : (cl - r1);
10524
0
    mg_snprintf(conn,
10525
0
                NULL, /* range buffer is big enough */
10526
0
                range,
10527
0
                sizeof(range),
10528
0
                "bytes "
10529
0
                "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT,
10530
0
                r1,
10531
0
                r1 + cl - 1,
10532
0
                filep->stat.size);
10533
10534
#if defined(USE_ZLIB)
10535
    /* Do not compress ranges. */
10536
    allow_on_the_fly_compression = 0;
10537
#endif
10538
0
  }
10539
10540
  /* Do not compress small files. Small files do not benefit from file
10541
   * compression, but there is still some overhead. */
10542
#if defined(USE_ZLIB)
10543
  if (filep->stat.size < MG_FILE_COMPRESSION_SIZE_LIMIT) {
10544
    /* File is below the size limit. */
10545
    allow_on_the_fly_compression = 0;
10546
  }
10547
#endif
10548
10549
  /* Prepare Etag, and Last-Modified headers. */
10550
0
  gmt_time_string(lm, sizeof(lm), &filep->stat.last_modified);
10551
0
  construct_etag(etag, sizeof(etag), &filep->stat);
10552
10553
  /* Create 2xx (200, 206) response */
10554
0
  mg_response_header_start(conn, conn->status_code);
10555
0
  send_static_cache_header(conn);
10556
0
  send_additional_header(conn);
10557
0
  send_cors_header(conn);
10558
0
  mg_response_header_add(conn,
10559
0
                         "Content-Type",
10560
0
                         mime_vec.ptr,
10561
0
                         (int)mime_vec.len);
10562
0
  mg_response_header_add(conn, "Last-Modified", lm, -1);
10563
0
  mg_response_header_add(conn, "Etag", etag, -1);
10564
10565
#if defined(USE_ZLIB)
10566
  /* On the fly compression allowed */
10567
  if (allow_on_the_fly_compression) {
10568
    /* For on the fly compression, we don't know the content size in
10569
     * advance, so we have to use chunked encoding */
10570
    encoding = "gzip";
10571
    if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
10572
      /* HTTP/2 is always using "chunks" (frames) */
10573
      mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1);
10574
    }
10575
10576
  } else
10577
#endif
10578
0
  {
10579
    /* Without on-the-fly compression, we know the content-length
10580
     * and we can use ranges (with on-the-fly compression we cannot).
10581
     * So we send these response headers only in this case. */
10582
0
    char len[32];
10583
0
    int trunc = 0;
10584
0
    mg_snprintf(conn, &trunc, len, sizeof(len), "%" INT64_FMT, cl);
10585
10586
0
    if (!trunc) {
10587
0
      mg_response_header_add(conn, "Content-Length", len, -1);
10588
0
    }
10589
10590
0
    mg_response_header_add(conn, "Accept-Ranges", "bytes", -1);
10591
0
  }
10592
10593
0
  if (encoding) {
10594
0
    mg_response_header_add(conn, "Content-Encoding", encoding, -1);
10595
0
  }
10596
0
  if (range[0] != 0) {
10597
0
    mg_response_header_add(conn, "Content-Range", range, -1);
10598
0
  }
10599
10600
  /* The code above does not add any header starting with X- to make
10601
   * sure no one of the additional_headers is included twice */
10602
0
  if ((additional_headers != NULL) && (*additional_headers != 0)) {
10603
0
    mg_response_header_add_lines(conn, additional_headers);
10604
0
  }
10605
10606
  /* Send all headers */
10607
0
  mg_response_header_send(conn);
10608
10609
0
  if (!is_head_request) {
10610
#if defined(USE_ZLIB)
10611
    if (allow_on_the_fly_compression) {
10612
      /* Compress and send */
10613
      send_compressed_data(conn, filep);
10614
    } else
10615
#endif
10616
0
    {
10617
      /* Send file directly */
10618
0
      send_file_data(conn, filep, r1, cl, 0); /* send static file */
10619
0
    }
10620
0
  }
10621
0
  (void)mg_fclose(&filep->access); /* ignore error on read only file */
10622
0
}
10623
10624
10625
CIVETWEB_API int
10626
mg_send_file_body(struct mg_connection *conn, const char *path)
10627
0
{
10628
0
  struct mg_file file = STRUCT_FILE_INITIALIZER;
10629
0
  if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, &file)) {
10630
0
    return -1;
10631
0
  }
10632
0
  fclose_on_exec(&file.access, conn);
10633
0
  send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
10634
0
  (void)mg_fclose(&file.access); /* Ignore errors for readonly files */
10635
0
  return 0;                      /* >= 0 for OK */
10636
0
}
10637
#endif /* NO_FILESYSTEMS */
10638
10639
10640
#if !defined(NO_CACHING)
10641
/* Return True if we should reply 304 Not Modified. */
10642
static int
10643
is_not_modified(const struct mg_connection *conn,
10644
                const struct mg_file_stat *filestat)
10645
0
{
10646
0
  char etag[64];
10647
0
  const char *ims = mg_get_header(conn, "If-Modified-Since");
10648
0
  const char *inm = mg_get_header(conn, "If-None-Match");
10649
0
  construct_etag(etag, sizeof(etag), filestat);
10650
10651
0
  return ((inm != NULL) && !mg_strcasecmp(etag, inm))
10652
0
         || ((ims != NULL)
10653
0
             && (filestat->last_modified <= parse_date_string(ims)));
10654
0
}
10655
10656
10657
static void
10658
handle_not_modified_static_file_request(struct mg_connection *conn,
10659
                                        struct mg_file *filep)
10660
0
{
10661
0
  char lm[64], etag[64];
10662
10663
0
  if ((conn == NULL) || (filep == NULL)) {
10664
0
    return;
10665
0
  }
10666
10667
0
  gmt_time_string(lm, sizeof(lm), &filep->stat.last_modified);
10668
0
  construct_etag(etag, sizeof(etag), &filep->stat);
10669
10670
  /* Create 304 "not modified" response */
10671
0
  mg_response_header_start(conn, 304);
10672
0
  send_static_cache_header(conn);
10673
0
  send_additional_header(conn);
10674
0
  mg_response_header_add(conn, "Last-Modified", lm, -1);
10675
0
  mg_response_header_add(conn, "Etag", etag, -1);
10676
10677
  /* Send all headers */
10678
0
  mg_response_header_send(conn);
10679
0
}
10680
#endif
10681
10682
10683
#if !defined(NO_FILESYSTEMS)
10684
CIVETWEB_API void
10685
mg_send_file(struct mg_connection *conn, const char *path)
10686
0
{
10687
0
  mg_send_mime_file2(conn, path, NULL, NULL);
10688
0
}
10689
10690
10691
CIVETWEB_API void
10692
mg_send_mime_file(struct mg_connection *conn,
10693
                  const char *path,
10694
                  const char *mime_type)
10695
0
{
10696
0
  mg_send_mime_file2(conn, path, mime_type, NULL);
10697
0
}
10698
10699
10700
CIVETWEB_API void
10701
mg_send_mime_file2(struct mg_connection *conn,
10702
                   const char *path,
10703
                   const char *mime_type,
10704
                   const char *additional_headers)
10705
0
{
10706
0
  struct mg_file file = STRUCT_FILE_INITIALIZER;
10707
10708
0
  if (!conn) {
10709
    /* No conn */
10710
0
    return;
10711
0
  }
10712
10713
0
  if (mg_stat(conn, path, &file.stat)) {
10714
0
#if !defined(NO_CACHING)
10715
0
    if (is_not_modified(conn, &file.stat)) {
10716
      /* Send 304 "Not Modified" - this must not send any body data */
10717
0
      handle_not_modified_static_file_request(conn, &file);
10718
0
    } else
10719
0
#endif /* NO_CACHING */
10720
0
        if (file.stat.is_directory) {
10721
0
      if (!mg_strcasecmp(conn->dom_ctx->config[ENABLE_DIRECTORY_LISTING],
10722
0
                         "yes")) {
10723
0
        handle_directory_request(conn, path);
10724
0
      } else {
10725
0
        mg_send_http_error(conn,
10726
0
                           403,
10727
0
                           "%s",
10728
0
                           "Error: Directory listing denied");
10729
0
      }
10730
0
    } else {
10731
0
      handle_static_file_request(
10732
0
          conn, path, &file, mime_type, additional_headers);
10733
0
    }
10734
0
  } else {
10735
0
    mg_send_http_error(conn, 404, "%s", "Error: File not found");
10736
0
  }
10737
0
}
10738
10739
10740
/* For a given PUT path, create all intermediate subdirectories.
10741
 * Return  0  if the path itself is a directory.
10742
 * Return  1  if the path leads to a file.
10743
 * Return -1  for if the path is too long.
10744
 * Return -2  if path can not be created.
10745
 */
10746
static int
10747
put_dir(struct mg_connection *conn, const char *path)
10748
0
{
10749
0
  char buf[UTF8_PATH_MAX];
10750
0
  const char *s, *p;
10751
0
  struct mg_file file = STRUCT_FILE_INITIALIZER;
10752
0
  size_t len;
10753
0
  int res = 1;
10754
10755
0
  for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
10756
0
    len = (size_t)(p - path);
10757
0
    if (len >= sizeof(buf)) {
10758
      /* path too long */
10759
0
      res = -1;
10760
0
      break;
10761
0
    }
10762
0
    memcpy(buf, path, len);
10763
0
    buf[len] = '\0';
10764
10765
    /* Try to create intermediate directory */
10766
0
    DEBUG_TRACE("mkdir(%s)", buf);
10767
0
    if (!mg_stat(conn, buf, &file.stat) && mg_mkdir(conn, buf, 0755) != 0) {
10768
      /* path does not exist and can not be created */
10769
0
      res = -2;
10770
0
      break;
10771
0
    }
10772
10773
    /* Is path itself a directory? */
10774
0
    if (p[1] == '\0') {
10775
0
      res = 0;
10776
0
    }
10777
0
  }
10778
10779
0
  return res;
10780
0
}
10781
10782
10783
static void
10784
remove_bad_file(const struct mg_connection *conn, const char *path)
10785
0
{
10786
0
  int r = mg_remove(conn, path);
10787
0
  if (r != 0) {
10788
0
    mg_cry_internal(conn,
10789
0
                    "%s: Cannot remove invalid file %s",
10790
0
                    __func__,
10791
0
                    path);
10792
0
  }
10793
0
}
10794
10795
10796
CIVETWEB_API long long
10797
mg_store_body(struct mg_connection *conn, const char *path)
10798
0
{
10799
0
  char buf[MG_BUF_LEN];
10800
0
  long long len = 0;
10801
0
  int ret, n;
10802
0
  struct mg_file fi;
10803
10804
0
  if (conn->consumed_content != 0) {
10805
0
    mg_cry_internal(conn, "%s: Contents already consumed", __func__);
10806
0
    return -11;
10807
0
  }
10808
10809
0
  ret = put_dir(conn, path);
10810
0
  if (ret < 0) {
10811
    /* -1 for path too long,
10812
     * -2 for path can not be created. */
10813
0
    return ret;
10814
0
  }
10815
0
  if (ret != 1) {
10816
    /* Return 0 means, path itself is a directory. */
10817
0
    return 0;
10818
0
  }
10819
10820
0
  if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fi) == 0) {
10821
0
    return -12;
10822
0
  }
10823
10824
0
  ret = mg_read(conn, buf, sizeof(buf));
10825
0
  while (ret > 0) {
10826
0
    n = (int)fwrite(buf, 1, (size_t)ret, fi.access.fp);
10827
0
    if (n != ret) {
10828
0
      (void)mg_fclose(
10829
0
          &fi.access); /* File is bad and will be removed anyway. */
10830
0
      remove_bad_file(conn, path);
10831
0
      return -13;
10832
0
    }
10833
0
    len += ret;
10834
0
    ret = mg_read(conn, buf, sizeof(buf));
10835
0
  }
10836
10837
  /* File is open for writing. If fclose fails, there was probably an
10838
   * error flushing the buffer to disk, so the file on disk might be
10839
   * broken. Delete it and return an error to the caller. */
10840
0
  if (mg_fclose(&fi.access) != 0) {
10841
0
    remove_bad_file(conn, path);
10842
0
    return -14;
10843
0
  }
10844
10845
0
  return len;
10846
0
}
10847
#endif /* NO_FILESYSTEMS */
10848
10849
10850
/* Parse a buffer:
10851
 * Forward the string pointer till the end of a word, then
10852
 * terminate it and forward till the begin of the next word.
10853
 */
10854
static int
10855
skip_to_end_of_word_and_terminate(char **ppw, int eol)
10856
0
{
10857
  /* Forward until a space is found - use isgraph here */
10858
  /* Extended ASCII characters are also treated as word characters. */
10859
  /* See http://www.cplusplus.com/reference/cctype/ */
10860
0
  while ((unsigned char)**ppw > 127 || isgraph((unsigned char)**ppw)) {
10861
0
    (*ppw)++;
10862
0
  }
10863
10864
  /* Check end of word */
10865
0
  if (eol) {
10866
    /* must be a end of line */
10867
0
    if ((**ppw != '\r') && (**ppw != '\n')) {
10868
0
      return -1;
10869
0
    }
10870
0
  } else {
10871
    /* must be a end of a word, but not a line */
10872
0
    if (**ppw != ' ') {
10873
0
      return -1;
10874
0
    }
10875
0
  }
10876
10877
  /* Terminate and forward to the next word */
10878
0
  do {
10879
0
    **ppw = 0;
10880
0
    (*ppw)++;
10881
0
  } while (isspace((unsigned char)**ppw));
10882
10883
  /* Check after term */
10884
0
  if (!eol) {
10885
    /* if it's not the end of line, there must be a next word */
10886
0
    if (!isgraph((unsigned char)**ppw)) {
10887
0
      return -1;
10888
0
    }
10889
0
  }
10890
10891
  /* ok */
10892
0
  return 1;
10893
0
}
10894
10895
10896
/* Parse HTTP headers from the given buffer, advance buf pointer
10897
 * to the point where parsing stopped.
10898
 * All parameters must be valid pointers (not NULL).
10899
 * Return <0 on error. */
10900
static int
10901
parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS])
10902
0
{
10903
0
  int i;
10904
0
  int num_headers = 0;
10905
10906
0
  for (i = 0; i < (int)MG_MAX_HEADERS; i++) {
10907
0
    char *dp = *buf;
10908
10909
    /* Skip all ASCII characters (>SPACE, <127), to find a ':' */
10910
0
    while ((*dp != ':') && (*dp >= 33) && (*dp <= 126)) {
10911
0
      dp++;
10912
0
    }
10913
0
    if (dp == *buf) {
10914
      /* End of headers reached. */
10915
0
      break;
10916
0
    }
10917
10918
    /* Drop all spaces after header name before : */
10919
0
    while (*dp == ' ') {
10920
0
      *dp = 0;
10921
0
      dp++;
10922
0
    }
10923
0
    if (*dp != ':') {
10924
      /* This is not a valid field. */
10925
0
      return -1;
10926
0
    }
10927
10928
    /* End of header key (*dp == ':') */
10929
    /* Truncate here and set the key name */
10930
0
    *dp = 0;
10931
0
    hdr[i].name = *buf;
10932
10933
    /* Skip all spaces */
10934
0
    do {
10935
0
      dp++;
10936
0
    } while ((*dp == ' ') || (*dp == '\t'));
10937
10938
    /* The rest of the line is the value */
10939
0
    hdr[i].value = dp;
10940
10941
    /* Find end of line */
10942
0
    while ((*dp != 0) && (*dp != '\r') && (*dp != '\n')) {
10943
0
      dp++;
10944
0
    };
10945
10946
    /* eliminate \r */
10947
0
    if (*dp == '\r') {
10948
0
      *dp = 0;
10949
0
      dp++;
10950
0
      if (*dp != '\n') {
10951
        /* This is not a valid line. */
10952
0
        return -1;
10953
0
      }
10954
0
    }
10955
10956
    /* here *dp is either 0 or '\n' */
10957
    /* in any case, we have a new header */
10958
0
    num_headers = i + 1;
10959
10960
0
    if (*dp) {
10961
0
      *dp = 0;
10962
0
      dp++;
10963
0
      *buf = dp;
10964
10965
0
      if ((dp[0] == '\r') || (dp[0] == '\n')) {
10966
        /* This is the end of the header */
10967
0
        break;
10968
0
      }
10969
0
    } else {
10970
0
      *buf = dp;
10971
0
      break;
10972
0
    }
10973
0
  }
10974
0
  return num_headers;
10975
0
}
10976
10977
10978
struct mg_http_method_info {
10979
  const char *name;
10980
  int request_has_body;
10981
  int response_has_body;
10982
  int is_safe;
10983
  int is_idempotent;
10984
  int is_cacheable;
10985
};
10986
10987
10988
/* https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods */
10989
static const struct mg_http_method_info http_methods[] = {
10990
    /* HTTP (RFC 2616) */
10991
    {"GET", 0, 1, 1, 1, 1},
10992
    {"POST", 1, 1, 0, 0, 0},
10993
    {"PUT", 1, 0, 0, 1, 0},
10994
    {"DELETE", 0, 0, 0, 1, 0},
10995
    {"HEAD", 0, 0, 1, 1, 1},
10996
    {"OPTIONS", 0, 0, 1, 1, 0},
10997
    {"CONNECT", 1, 1, 0, 0, 0},
10998
    /* TRACE method (RFC 2616) is not supported for security reasons */
10999
11000
    /* PATCH method (RFC 5789) */
11001
    {"PATCH", 1, 0, 0, 0, 0},
11002
    /* PATCH method only allowed for CGI/Lua/LSP and callbacks. */
11003
11004
    /* WEBDAV (RFC 2518) */
11005
    {"PROPFIND", 0, 1, 1, 1, 0},
11006
    /* http://www.webdav.org/specs/rfc4918.html, 9.1:
11007
     * Some PROPFIND results MAY be cached, with care,
11008
     * as there is no cache validation mechanism for
11009
     * most properties. This method is both safe and
11010
     * idempotent (see Section 9.1 of [RFC2616]). */
11011
    {"MKCOL", 0, 0, 0, 1, 0},
11012
    /* http://www.webdav.org/specs/rfc4918.html, 9.1:
11013
     * When MKCOL is invoked without a request body,
11014
     * the newly created collection SHOULD have no
11015
     * members. A MKCOL request message may contain
11016
     * a message body. The precise behavior of a MKCOL
11017
     * request when the body is present is undefined,
11018
     * ... ==> We do not support MKCOL with body data.
11019
     * This method is idempotent, but not safe (see
11020
     * Section 9.1 of [RFC2616]). Responses to this
11021
     * method MUST NOT be cached. */
11022
11023
    /* Methods for write access to files on WEBDAV (RFC 2518) */
11024
    {"LOCK", 1, 1, 0, 0, 0},
11025
    {"UNLOCK", 1, 0, 0, 0, 0},
11026
    {"PROPPATCH", 1, 1, 0, 0, 0},
11027
    {"COPY", 1, 0, 0, 0, 0},
11028
    {"MOVE", 1, 1, 0, 0, 0},
11029
11030
    /* Unsupported WEBDAV Methods: */
11031
    /* + 11 methods from RFC 3253 */
11032
    /* ORDERPATCH (RFC 3648) */
11033
    /* ACL (RFC 3744) */
11034
    /* SEARCH (RFC 5323) */
11035
    /* + MicroSoft extensions
11036
     * https://msdn.microsoft.com/en-us/library/aa142917.aspx */
11037
11038
    /* REPORT method (RFC 3253) */
11039
    {"REPORT", 1, 1, 1, 1, 1},
11040
    /* REPORT method only allowed for CGI/Lua/LSP and callbacks. */
11041
    /* It was defined for WEBDAV in RFC 3253, Sec. 3.6
11042
     * (https://tools.ietf.org/html/rfc3253#section-3.6), but seems
11043
     * to be useful for REST in case a "GET request with body" is
11044
     * required. */
11045
11046
    {NULL, 0, 0, 0, 0, 0}
11047
    /* end of list */
11048
};
11049
11050
11051
/* All method names */
11052
static char *all_methods = NULL; /* Built by mg_init_library */
11053
11054
11055
static const struct mg_http_method_info *
11056
get_http_method_info(const char *method)
11057
0
{
11058
  /* Check if the method is known to the server. The list of all known
11059
   * HTTP methods can be found here at
11060
   * http://www.iana.org/assignments/http-methods/http-methods.xhtml
11061
   */
11062
0
  const struct mg_http_method_info *m = http_methods;
11063
11064
0
  while (m->name) {
11065
0
    if (!strcmp(m->name, method)) {
11066
0
      return m;
11067
0
    }
11068
0
    m++;
11069
0
  }
11070
0
  return NULL;
11071
0
}
11072
11073
11074
static int
11075
is_valid_http_method(const char *method)
11076
0
{
11077
0
  return (get_http_method_info(method) != NULL);
11078
0
}
11079
11080
11081
/* Parse HTTP request, fill in mg_request_info structure.
11082
 * This function modifies the buffer by NUL-terminating
11083
 * HTTP request components, header names and header values.
11084
 * Parameters:
11085
 *   buf (in/out): pointer to the HTTP header to parse and split
11086
 *   len (in): length of HTTP header buffer
11087
 *   re (out): parsed header as mg_request_info
11088
 * buf and ri must be valid pointers (not NULL), len>0.
11089
 * Returns <0 on error. */
11090
static int
11091
parse_http_request(char *buf, int len, struct mg_request_info *ri)
11092
0
{
11093
0
  int request_length;
11094
0
  int init_skip = 0;
11095
11096
  /* Reset attributes. DO NOT TOUCH is_ssl, remote_addr,
11097
   * remote_port */
11098
0
  ri->remote_user = ri->request_method = ri->request_uri = ri->http_version =
11099
0
      NULL;
11100
0
  ri->num_headers = 0;
11101
11102
  /* RFC says that all initial whitespaces should be ignored */
11103
  /* This included all leading \r and \n (isspace) */
11104
  /* See table: http://www.cplusplus.com/reference/cctype/ */
11105
0
  while ((len > 0) && isspace((unsigned char)*buf)) {
11106
0
    buf++;
11107
0
    len--;
11108
0
    init_skip++;
11109
0
  }
11110
11111
0
  if (len == 0) {
11112
    /* Incomplete request */
11113
0
    return 0;
11114
0
  }
11115
11116
  /* Control characters are not allowed, including zero */
11117
0
  if (iscntrl((unsigned char)*buf)) {
11118
0
    return -1;
11119
0
  }
11120
11121
  /* Find end of HTTP header */
11122
0
  request_length = get_http_header_len(buf, len);
11123
0
  if (request_length <= 0) {
11124
0
    return request_length;
11125
0
  }
11126
0
  buf[request_length - 1] = '\0';
11127
11128
0
  if ((*buf == 0) || (*buf == '\r') || (*buf == '\n')) {
11129
0
    return -1;
11130
0
  }
11131
11132
  /* The first word has to be the HTTP method */
11133
0
  ri->request_method = buf;
11134
11135
0
  if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
11136
0
    return -1;
11137
0
  }
11138
11139
  /* The second word is the URI */
11140
0
  ri->request_uri = buf;
11141
11142
0
  if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
11143
0
    return -1;
11144
0
  }
11145
11146
  /* Next would be the HTTP version */
11147
0
  ri->http_version = buf;
11148
11149
0
  if (skip_to_end_of_word_and_terminate(&buf, 1) <= 0) {
11150
0
    return -1;
11151
0
  }
11152
11153
  /* Check for a valid HTTP version key */
11154
0
  if (strncmp(ri->http_version, "HTTP/", 5) != 0) {
11155
    /* Invalid request */
11156
0
    return -1;
11157
0
  }
11158
0
  ri->http_version += 5;
11159
11160
  /* Check for a valid http method */
11161
0
  if (!is_valid_http_method(ri->request_method)) {
11162
0
    return -1;
11163
0
  }
11164
11165
  /* Parse all HTTP headers */
11166
0
  ri->num_headers = parse_http_headers(&buf, ri->http_headers);
11167
0
  if (ri->num_headers < 0) {
11168
    /* Error while parsing headers */
11169
0
    return -1;
11170
0
  }
11171
11172
0
  return request_length + init_skip;
11173
0
}
11174
11175
11176
static int
11177
parse_http_response(char *buf, int len, struct mg_response_info *ri)
11178
0
{
11179
0
  int response_length;
11180
0
  int init_skip = 0;
11181
0
  char *tmp, *tmp2;
11182
0
  long l;
11183
11184
  /* Initialize elements. */
11185
0
  ri->http_version = ri->status_text = NULL;
11186
0
  ri->num_headers = ri->status_code = 0;
11187
11188
  /* RFC says that all initial whitespaces should be ignored */
11189
  /* This included all leading \r and \n (isspace) */
11190
  /* See table: http://www.cplusplus.com/reference/cctype/ */
11191
0
  while ((len > 0) && isspace((unsigned char)*buf)) {
11192
0
    buf++;
11193
0
    len--;
11194
0
    init_skip++;
11195
0
  }
11196
11197
0
  if (len == 0) {
11198
    /* Incomplete request */
11199
0
    return 0;
11200
0
  }
11201
11202
  /* Control characters are not allowed, including zero */
11203
0
  if (iscntrl((unsigned char)*buf)) {
11204
0
    return -1;
11205
0
  }
11206
11207
  /* Find end of HTTP header */
11208
0
  response_length = get_http_header_len(buf, len);
11209
0
  if (response_length <= 0) {
11210
0
    return response_length;
11211
0
  }
11212
0
  buf[response_length - 1] = '\0';
11213
11214
0
  if ((*buf == 0) || (*buf == '\r') || (*buf == '\n')) {
11215
0
    return -1;
11216
0
  }
11217
11218
  /* The first word is the HTTP version */
11219
  /* Check for a valid HTTP version key */
11220
0
  if (strncmp(buf, "HTTP/", 5) != 0) {
11221
    /* Invalid request */
11222
0
    return -1;
11223
0
  }
11224
0
  buf += 5;
11225
0
  if (!isgraph((unsigned char)buf[0])) {
11226
    /* Invalid request */
11227
0
    return -1;
11228
0
  }
11229
0
  ri->http_version = buf;
11230
11231
0
  if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
11232
0
    return -1;
11233
0
  }
11234
11235
  /* The second word is the status as a number */
11236
0
  tmp = buf;
11237
11238
0
  if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
11239
0
    return -1;
11240
0
  }
11241
11242
0
  l = strtol(tmp, &tmp2, 10);
11243
0
  if ((l < 100) || (l >= 1000) || ((tmp2 - tmp) != 3) || (*tmp2 != 0)) {
11244
    /* Everything else but a 3 digit code is invalid */
11245
0
    return -1;
11246
0
  }
11247
0
  ri->status_code = (int)l;
11248
11249
  /* The rest of the line is the status text */
11250
0
  ri->status_text = buf;
11251
11252
  /* Find end of status text */
11253
  /* isgraph or isspace = isprint */
11254
0
  while (isprint((unsigned char)*buf)) {
11255
0
    buf++;
11256
0
  }
11257
0
  if ((*buf != '\r') && (*buf != '\n')) {
11258
0
    return -1;
11259
0
  }
11260
  /* Terminate string and forward buf to next line */
11261
0
  do {
11262
0
    *buf = 0;
11263
0
    buf++;
11264
0
  } while (isspace((unsigned char)*buf));
11265
11266
  /* Parse all HTTP headers */
11267
0
  ri->num_headers = parse_http_headers(&buf, ri->http_headers);
11268
0
  if (ri->num_headers < 0) {
11269
    /* Error while parsing headers */
11270
0
    return -1;
11271
0
  }
11272
11273
0
  return response_length + init_skip;
11274
0
}
11275
11276
11277
/* Keep reading the input (either opened file descriptor fd, or socket sock,
11278
 * or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
11279
 * buffer (which marks the end of HTTP request). Buffer buf may already
11280
 * have some data. The length of the data is stored in nread.
11281
 * Upon every read operation, increase nread by the number of bytes read. */
11282
static int
11283
read_message(FILE *fp,
11284
             struct mg_connection *conn,
11285
             char *buf,
11286
             int bufsiz,
11287
             int *nread)
11288
34
{
11289
34
  int request_len, n = 0;
11290
34
  struct timespec last_action_time;
11291
34
  double request_timeout;
11292
11293
34
  if (!conn) {
11294
0
    return 0;
11295
0
  }
11296
11297
34
  memset(&last_action_time, 0, sizeof(last_action_time));
11298
11299
34
  if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
11300
    /* value of request_timeout is in seconds, config in milliseconds */
11301
34
    request_timeout =
11302
34
        strtod(conn->dom_ctx->config[REQUEST_TIMEOUT], NULL) / 1000.0;
11303
34
  } else {
11304
0
    request_timeout =
11305
0
        strtod(config_options[REQUEST_TIMEOUT].default_value, NULL)
11306
0
        / 1000.0;
11307
0
  }
11308
34
  if (conn->handled_requests > 0) {
11309
0
    if (conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT]) {
11310
0
      request_timeout =
11311
0
          strtod(conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT], NULL)
11312
0
          / 1000.0;
11313
0
    }
11314
0
  }
11315
11316
34
  request_len = get_http_header_len(buf, *nread);
11317
11318
34
  while (request_len == 0) {
11319
    /* Full request not yet received */
11320
34
    if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
11321
      /* Server is to be stopped. */
11322
0
      return -1;
11323
0
    }
11324
11325
34
    if (*nread >= bufsiz) {
11326
      /* Request too long */
11327
0
      return -2;
11328
0
    }
11329
11330
34
    n = pull_inner(
11331
34
        fp, conn, buf + *nread, bufsiz - *nread, request_timeout);
11332
34
    if (n == -2) {
11333
      /* Receive error */
11334
34
      return -1;
11335
34
    }
11336
11337
    /* update clock after every read request */
11338
0
    clock_gettime(CLOCK_MONOTONIC, &last_action_time);
11339
11340
0
    if (n > 0) {
11341
0
      *nread += n;
11342
0
      request_len = get_http_header_len(buf, *nread);
11343
0
    }
11344
11345
0
    if ((n <= 0) && (request_timeout >= 0)) {
11346
0
      if (mg_difftimespec(&last_action_time, &(conn->req_time))
11347
0
          > request_timeout) {
11348
        /* Timeout */
11349
0
        return -3;
11350
0
      }
11351
0
    }
11352
0
  }
11353
11354
0
  return request_len;
11355
34
}
11356
11357
11358
#if !defined(NO_CGI) || !defined(NO_FILES)
11359
static int
11360
forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
11361
0
{
11362
0
  const char *expect;
11363
0
  char buf[MG_BUF_LEN];
11364
0
  int success = 0;
11365
11366
0
  if (!conn) {
11367
0
    return 0;
11368
0
  }
11369
11370
0
  expect = mg_get_header(conn, "Expect");
11371
0
  DEBUG_ASSERT(fp != NULL);
11372
0
  if (!fp) {
11373
0
    mg_send_http_error(conn, 500, "%s", "Error: NULL File");
11374
0
    return 0;
11375
0
  }
11376
11377
0
  if ((expect != NULL) && (mg_strcasecmp(expect, "100-continue") != 0)) {
11378
    /* Client sent an "Expect: xyz" header and xyz is not 100-continue.
11379
     */
11380
0
    mg_send_http_error(conn, 417, "Error: Can not fulfill expectation");
11381
0
  } else {
11382
0
    if (expect != NULL) {
11383
0
      (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
11384
0
      conn->status_code = 100;
11385
0
    } else {
11386
0
      conn->status_code = 200;
11387
0
    }
11388
11389
0
    DEBUG_ASSERT(conn->consumed_content == 0);
11390
11391
0
    if (conn->consumed_content != 0) {
11392
0
      mg_send_http_error(conn, 500, "%s", "Error: Size mismatch");
11393
0
      return 0;
11394
0
    }
11395
11396
0
    for (;;) {
11397
0
      int nread = mg_read(conn, buf, sizeof(buf));
11398
0
      if (nread <= 0) {
11399
0
        success = (nread == 0);
11400
0
        break;
11401
0
      }
11402
0
      if (push_all(conn->phys_ctx, fp, sock, ssl, buf, nread) != nread) {
11403
0
        break;
11404
0
      }
11405
0
    }
11406
11407
    /* Each error code path in this function must send an error */
11408
0
    if (!success) {
11409
      /* NOTE: Maybe some data has already been sent. */
11410
      /* TODO (low): If some data has been sent, a correct error
11411
       * reply can no longer be sent, so just close the connection */
11412
0
      mg_send_http_error(conn, 500, "%s", "");
11413
0
    }
11414
0
  }
11415
11416
0
  return success;
11417
0
}
11418
#endif
11419
11420
11421
#if defined(USE_TIMERS)
11422
11423
#define TIMER_API static
11424
#include "timer.inl"
11425
11426
#endif /* USE_TIMERS */
11427
11428
11429
#if !defined(NO_CGI)
11430
/* This structure helps to create an environment for the spawned CGI
11431
 * program.
11432
 * Environment is an array of "VARIABLE=VALUE\0" ASCII strings,
11433
 * last element must be NULL.
11434
 * However, on Windows there is a requirement that all these
11435
 * VARIABLE=VALUE\0
11436
 * strings must reside in a contiguous buffer. The end of the buffer is
11437
 * marked by two '\0' characters.
11438
 * We satisfy both worlds: we create an envp array (which is vars), all
11439
 * entries are actually pointers inside buf. */
11440
struct cgi_environment {
11441
  struct mg_connection *conn;
11442
  /* Data block */
11443
  char *buf;      /* Environment buffer */
11444
  size_t buflen;  /* Space available in buf */
11445
  size_t bufused; /* Space taken in buf */
11446
  /* Index block */
11447
  char **var;     /* char **envp */
11448
  size_t varlen;  /* Number of variables available in var */
11449
  size_t varused; /* Number of variables stored in var */
11450
};
11451
11452
11453
static void addenv(struct cgi_environment *env,
11454
                   PRINTF_FORMAT_STRING(const char *fmt),
11455
                   ...) PRINTF_ARGS(2, 3);
11456
11457
/* Append VARIABLE=VALUE\0 string to the buffer, and add a respective
11458
 * pointer into the vars array. Assumes env != NULL and fmt != NULL. */
11459
static void
11460
addenv(struct cgi_environment *env, const char *fmt, ...)
11461
0
{
11462
0
  size_t i, n, space;
11463
0
  int truncated = 0;
11464
0
  char *added;
11465
0
  va_list ap;
11466
11467
0
  if ((env->varlen - env->varused) < 2) {
11468
0
    mg_cry_internal(env->conn,
11469
0
                    "%s: Cannot register CGI variable [%s]",
11470
0
                    __func__,
11471
0
                    fmt);
11472
0
    return;
11473
0
  }
11474
11475
  /* Calculate how much space is left in the buffer */
11476
0
  space = (env->buflen - env->bufused);
11477
11478
0
  do {
11479
    /* Space for "\0\0" is always needed. */
11480
0
    if (space <= 2) {
11481
      /* Allocate new buffer */
11482
0
      n = env->buflen + CGI_ENVIRONMENT_SIZE;
11483
0
      added = (char *)mg_realloc_ctx(env->buf, n, env->conn->phys_ctx);
11484
0
      if (!added) {
11485
        /* Out of memory */
11486
0
        mg_cry_internal(
11487
0
            env->conn,
11488
0
            "%s: Cannot allocate memory for CGI variable [%s]",
11489
0
            __func__,
11490
0
            fmt);
11491
0
        return;
11492
0
      }
11493
      /* Retarget pointers */
11494
0
      env->buf = added;
11495
0
      env->buflen = n;
11496
0
      for (i = 0, n = 0; i < env->varused; i++) {
11497
0
        env->var[i] = added + n;
11498
0
        n += strlen(added + n) + 1;
11499
0
      }
11500
0
      space = (env->buflen - env->bufused);
11501
0
    }
11502
11503
    /* Make a pointer to the free space int the buffer */
11504
0
    added = env->buf + env->bufused;
11505
11506
    /* Copy VARIABLE=VALUE\0 string into the free space */
11507
0
    va_start(ap, fmt);
11508
0
    mg_vsnprintf(env->conn, &truncated, added, space - 1, fmt, ap);
11509
0
    va_end(ap);
11510
11511
    /* Do not add truncated strings to the environment */
11512
0
    if (truncated) {
11513
      /* Reallocate the buffer */
11514
0
      space = 0;
11515
0
    }
11516
0
  } while (truncated);
11517
11518
  /* Calculate number of bytes added to the environment */
11519
0
  n = strlen(added) + 1;
11520
0
  env->bufused += n;
11521
11522
  /* Append a pointer to the added string into the envp array */
11523
0
  env->var[env->varused] = added;
11524
0
  env->varused++;
11525
0
}
11526
11527
/* Return 0 on success, non-zero if an error occurs. */
11528
11529
static int
11530
prepare_cgi_environment(struct mg_connection *conn,
11531
                        const char *prog,
11532
                        struct cgi_environment *env,
11533
                        int cgi_config_idx)
11534
0
{
11535
0
  const char *s;
11536
0
  struct vec var_vec;
11537
0
  char *p, src_addr[IP_ADDR_STR_LEN], http_var_name[128];
11538
0
  int i, truncated, uri_len;
11539
11540
0
  if ((conn == NULL) || (prog == NULL) || (env == NULL)) {
11541
0
    return -1;
11542
0
  }
11543
11544
0
  env->conn = conn;
11545
0
  env->buflen = CGI_ENVIRONMENT_SIZE;
11546
0
  env->bufused = 0;
11547
0
  env->buf = (char *)mg_malloc_ctx(env->buflen, conn->phys_ctx);
11548
0
  if (env->buf == NULL) {
11549
0
    mg_cry_internal(conn,
11550
0
                    "%s: Not enough memory for environmental buffer",
11551
0
                    __func__);
11552
0
    return -1;
11553
0
  }
11554
0
  env->varlen = MAX_CGI_ENVIR_VARS;
11555
0
  env->varused = 0;
11556
0
  env->var =
11557
0
      (char **)mg_malloc_ctx(env->varlen * sizeof(char *), conn->phys_ctx);
11558
0
  if (env->var == NULL) {
11559
0
    mg_cry_internal(conn,
11560
0
                    "%s: Not enough memory for environmental variables",
11561
0
                    __func__);
11562
0
    mg_free(env->buf);
11563
0
    return -1;
11564
0
  }
11565
11566
0
  addenv(env, "SERVER_NAME=%s", conn->dom_ctx->config[AUTHENTICATION_DOMAIN]);
11567
0
  addenv(env, "SERVER_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
11568
0
  addenv(env, "DOCUMENT_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
11569
0
  if (conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]) {
11570
0
    addenv(env,
11571
0
           "FALLBACK_DOCUMENT_ROOT=%s",
11572
0
           conn->dom_ctx->config[FALLBACK_DOCUMENT_ROOT]);
11573
0
  }
11574
0
  addenv(env, "SERVER_SOFTWARE=CivetWeb/%s", mg_version());
11575
11576
  /* Prepare the environment block */
11577
0
  addenv(env, "%s", "GATEWAY_INTERFACE=CGI/1.1");
11578
0
  addenv(env, "%s", "SERVER_PROTOCOL=HTTP/1.1");
11579
0
  addenv(env, "%s", "REDIRECT_STATUS=200"); /* For PHP */
11580
11581
0
  addenv(env, "SERVER_PORT=%d", conn->request_info.server_port);
11582
11583
0
  sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
11584
0
  addenv(env, "REMOTE_ADDR=%s", src_addr);
11585
11586
0
  addenv(env, "REQUEST_METHOD=%s", conn->request_info.request_method);
11587
0
  addenv(env, "REMOTE_PORT=%d", conn->request_info.remote_port);
11588
11589
0
  addenv(env, "REQUEST_URI=%s", conn->request_info.request_uri);
11590
0
  addenv(env, "LOCAL_URI=%s", conn->request_info.local_uri);
11591
0
  addenv(env, "LOCAL_URI_RAW=%s", conn->request_info.local_uri_raw);
11592
11593
  /* SCRIPT_NAME */
11594
0
  uri_len = (int)strlen(conn->request_info.local_uri);
11595
0
  if (conn->path_info == NULL) {
11596
0
    if (conn->request_info.local_uri[uri_len - 1] != '/') {
11597
      /* URI: /path_to_script/script.cgi */
11598
0
      addenv(env, "SCRIPT_NAME=%s", conn->request_info.local_uri);
11599
0
    } else {
11600
      /* URI: /path_to_script/ ... using index.cgi */
11601
0
      const char *index_file = strrchr(prog, '/');
11602
0
      if (index_file) {
11603
0
        addenv(env,
11604
0
               "SCRIPT_NAME=%s%s",
11605
0
               conn->request_info.local_uri,
11606
0
               index_file + 1);
11607
0
      }
11608
0
    }
11609
0
  } else {
11610
    /* URI: /path_to_script/script.cgi/path_info */
11611
0
    addenv(env,
11612
0
           "SCRIPT_NAME=%.*s",
11613
0
           uri_len - (int)strlen(conn->path_info),
11614
0
           conn->request_info.local_uri);
11615
0
  }
11616
11617
0
  addenv(env, "SCRIPT_FILENAME=%s", prog);
11618
0
  if (conn->path_info == NULL) {
11619
0
    addenv(env, "PATH_TRANSLATED=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
11620
0
  } else {
11621
0
    addenv(env,
11622
0
           "PATH_TRANSLATED=%s%s",
11623
0
           conn->dom_ctx->config[DOCUMENT_ROOT],
11624
0
           conn->path_info);
11625
0
  }
11626
11627
0
  addenv(env, "HTTPS=%s", (conn->ssl == NULL) ? "off" : "on");
11628
11629
0
  if ((s = mg_get_header(conn, "Content-Type")) != NULL) {
11630
0
    addenv(env, "CONTENT_TYPE=%s", s);
11631
0
  }
11632
0
  if (conn->request_info.query_string != NULL) {
11633
0
    addenv(env, "QUERY_STRING=%s", conn->request_info.query_string);
11634
0
  }
11635
0
  if ((s = mg_get_header(conn, "Content-Length")) != NULL) {
11636
0
    addenv(env, "CONTENT_LENGTH=%s", s);
11637
0
  }
11638
0
  if ((s = getenv("PATH")) != NULL) {
11639
0
    addenv(env, "PATH=%s", s);
11640
0
  }
11641
0
  if (conn->path_info != NULL) {
11642
0
    addenv(env, "PATH_INFO=%s", conn->path_info);
11643
0
  }
11644
11645
0
  if (conn->status_code > 0) {
11646
    /* CGI error handler should show the status code */
11647
0
    addenv(env, "STATUS=%d", conn->status_code);
11648
0
  }
11649
11650
#if defined(_WIN32)
11651
  if ((s = getenv("COMSPEC")) != NULL) {
11652
    addenv(env, "COMSPEC=%s", s);
11653
  }
11654
  if ((s = getenv("SYSTEMROOT")) != NULL) {
11655
    addenv(env, "SYSTEMROOT=%s", s);
11656
  }
11657
  if ((s = getenv("SystemDrive")) != NULL) {
11658
    addenv(env, "SystemDrive=%s", s);
11659
  }
11660
  if ((s = getenv("ProgramFiles")) != NULL) {
11661
    addenv(env, "ProgramFiles=%s", s);
11662
  }
11663
  if ((s = getenv("ProgramFiles(x86)")) != NULL) {
11664
    addenv(env, "ProgramFiles(x86)=%s", s);
11665
  }
11666
#else
11667
0
  if ((s = getenv("LD_LIBRARY_PATH")) != NULL) {
11668
0
    addenv(env, "LD_LIBRARY_PATH=%s", s);
11669
0
  }
11670
0
#endif /* _WIN32 */
11671
11672
0
  if ((s = getenv("PERLLIB")) != NULL) {
11673
0
    addenv(env, "PERLLIB=%s", s);
11674
0
  }
11675
11676
0
  if (conn->request_info.remote_user != NULL) {
11677
0
    addenv(env, "REMOTE_USER=%s", conn->request_info.remote_user);
11678
0
    addenv(env, "%s", "AUTH_TYPE=Digest");
11679
0
  }
11680
11681
  /* Add all headers as HTTP_* variables */
11682
0
  for (i = 0; i < conn->request_info.num_headers; i++) {
11683
11684
0
    (void)mg_snprintf(conn,
11685
0
                      &truncated,
11686
0
                      http_var_name,
11687
0
                      sizeof(http_var_name),
11688
0
                      "HTTP_%s",
11689
0
                      conn->request_info.http_headers[i].name);
11690
11691
0
    if (truncated) {
11692
0
      mg_cry_internal(conn,
11693
0
                      "%s: HTTP header variable too long [%s]",
11694
0
                      __func__,
11695
0
                      conn->request_info.http_headers[i].name);
11696
0
      continue;
11697
0
    }
11698
11699
    /* Convert variable name into uppercase, and change - to _ */
11700
0
    for (p = http_var_name; *p != '\0'; p++) {
11701
0
      if (*p == '-') {
11702
0
        *p = '_';
11703
0
      }
11704
0
      *p = (char)toupper((unsigned char)*p);
11705
0
    }
11706
11707
0
    addenv(env,
11708
0
           "%s=%s",
11709
0
           http_var_name,
11710
0
           conn->request_info.http_headers[i].value);
11711
0
  }
11712
11713
  /* Add user-specified variables */
11714
0
  s = conn->dom_ctx->config[CGI_ENVIRONMENT + cgi_config_idx];
11715
0
  while ((s = next_option(s, &var_vec, NULL)) != NULL) {
11716
0
    addenv(env, "%.*s", (int)var_vec.len, var_vec.ptr);
11717
0
  }
11718
11719
0
  env->var[env->varused] = NULL;
11720
0
  env->buf[env->bufused] = '\0';
11721
11722
0
  return 0;
11723
0
}
11724
11725
11726
/* Data for CGI process control: PID and number of references */
11727
struct process_control_data {
11728
  pid_t pid;
11729
  ptrdiff_t references;
11730
};
11731
11732
static int
11733
abort_cgi_process(void *data)
11734
0
{
11735
  /* Waitpid checks for child status and won't work for a pid that does
11736
   * not identify a child of the current process. Thus, if the pid is
11737
   * reused, we will not affect a different process. */
11738
0
  struct process_control_data *proc = (struct process_control_data *)data;
11739
0
  int status = 0;
11740
0
  ptrdiff_t refs;
11741
0
  pid_t ret_pid;
11742
11743
0
  ret_pid = waitpid(proc->pid, &status, WNOHANG);
11744
0
  if ((ret_pid != (pid_t)-1) && (status == 0)) {
11745
    /* Stop child process */
11746
0
    DEBUG_TRACE("CGI timer: Stop child process %d\n", proc->pid);
11747
0
    kill(proc->pid, SIGABRT);
11748
11749
    /* Wait until process is terminated (don't leave zombies) */
11750
0
    while (waitpid(proc->pid, &status, 0) != (pid_t)-1) /* nop */
11751
0
      ;
11752
0
  } else {
11753
0
    DEBUG_TRACE("CGI timer: Child process %d already stopped\n", proc->pid);
11754
0
  }
11755
  /* Dec reference counter */
11756
0
  refs = mg_atomic_dec(&proc->references);
11757
0
  if (refs == 0) {
11758
    /* no more references - free data */
11759
0
    mg_free(data);
11760
0
  }
11761
11762
0
  return 0;
11763
0
}
11764
11765
11766
/* Local (static) function assumes all arguments are valid. */
11767
static void
11768
handle_cgi_request(struct mg_connection *conn,
11769
                   const char *prog,
11770
                   int cgi_config_idx)
11771
0
{
11772
0
  char *buf;
11773
0
  size_t buflen;
11774
0
  int headers_len, data_len, i, truncated;
11775
0
  int fdin[2] = {-1, -1}, fdout[2] = {-1, -1}, fderr[2] = {-1, -1};
11776
0
  const char *status, *status_text;
11777
0
  char *pbuf, dir[UTF8_PATH_MAX], *p;
11778
0
  struct mg_request_info ri;
11779
0
  struct cgi_environment blk;
11780
0
  FILE *in = NULL, *out = NULL, *err = NULL;
11781
0
  struct mg_file fout = STRUCT_FILE_INITIALIZER;
11782
0
  pid_t pid = (pid_t)-1;
11783
0
  struct process_control_data *proc = NULL;
11784
0
  char *cfg_buffering = conn->dom_ctx->config[CGI_BUFFERING + cgi_config_idx];
11785
0
  int no_buffering = 0;
11786
11787
#if defined(USE_TIMERS)
11788
  double cgi_timeout;
11789
  if (conn->dom_ctx->config[CGI_TIMEOUT + cgi_config_idx]) {
11790
    /* Get timeout in seconds */
11791
    cgi_timeout =
11792
        atof(conn->dom_ctx->config[CGI_TIMEOUT + cgi_config_idx]) * 0.001;
11793
  } else {
11794
    cgi_timeout =
11795
        atof(config_options[REQUEST_TIMEOUT].default_value) * 0.001;
11796
  }
11797
#endif
11798
0
  if (cfg_buffering != NULL) {
11799
0
    if (!mg_strcasecmp(cfg_buffering, "no")) {
11800
0
      no_buffering = 1;
11801
0
    }
11802
0
  }
11803
11804
0
  buf = NULL;
11805
0
  buflen = conn->phys_ctx->max_request_size;
11806
0
  i = prepare_cgi_environment(conn, prog, &blk, cgi_config_idx);
11807
0
  if (i != 0) {
11808
0
    blk.buf = NULL;
11809
0
    blk.var = NULL;
11810
0
    goto done;
11811
0
  }
11812
11813
  /* CGI must be executed in its own directory. 'dir' must point to the
11814
   * directory containing executable program, 'p' must point to the
11815
   * executable program name relative to 'dir'. */
11816
0
  (void)mg_snprintf(conn, &truncated, dir, sizeof(dir), "%s", prog);
11817
11818
0
  if (truncated) {
11819
0
    mg_cry_internal(conn, "Error: CGI program \"%s\": Path too long", prog);
11820
0
    mg_send_http_error(conn, 500, "Error: %s", "CGI path too long");
11821
0
    goto done;
11822
0
  }
11823
11824
0
  if ((p = strrchr(dir, '/')) != NULL) {
11825
0
    *p++ = '\0';
11826
0
  } else {
11827
0
    dir[0] = '.';
11828
0
    dir[1] = '\0';
11829
0
    p = (char *)prog;
11830
0
  }
11831
11832
0
  if ((pipe(fdin) != 0) || (pipe(fdout) != 0) || (pipe(fderr) != 0)) {
11833
0
    status = strerror(ERRNO);
11834
0
    mg_cry_internal(
11835
0
        conn,
11836
0
        "Error: CGI program \"%s\": Can not create CGI pipes: %s",
11837
0
        prog,
11838
0
        status);
11839
0
    mg_send_http_error(conn,
11840
0
                       500,
11841
0
                       "Error: Cannot create CGI pipe: %s",
11842
0
                       status);
11843
0
    goto done;
11844
0
  }
11845
11846
0
  proc = (struct process_control_data *)
11847
0
      mg_malloc_ctx(sizeof(struct process_control_data), conn->phys_ctx);
11848
0
  if (proc == NULL) {
11849
0
    mg_cry_internal(conn, "Error: CGI program \"%s\": Out or memory", prog);
11850
0
    mg_send_http_error(conn, 500, "Error: Out of memory [%s]", prog);
11851
0
    goto done;
11852
0
  }
11853
11854
0
  DEBUG_TRACE("CGI: spawn %s %s\n", dir, p);
11855
0
  pid = spawn_process(
11856
0
      conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir, cgi_config_idx);
11857
11858
0
  if (pid == (pid_t)-1) {
11859
0
    status = strerror(ERRNO);
11860
0
    mg_cry_internal(
11861
0
        conn,
11862
0
        "Error: CGI program \"%s\": Can not spawn CGI process: %s",
11863
0
        prog,
11864
0
        status);
11865
0
    mg_send_http_error(conn, 500, "Error: Cannot spawn CGI process");
11866
0
    mg_free(proc);
11867
0
    proc = NULL;
11868
0
    goto done;
11869
0
  }
11870
11871
  /* Store data in shared process_control_data */
11872
0
  proc->pid = pid;
11873
0
  proc->references = 1;
11874
11875
#if defined(USE_TIMERS)
11876
  if (cgi_timeout > 0.0) {
11877
    proc->references = 2;
11878
11879
    // Start a timer for CGI
11880
    timer_add(conn->phys_ctx,
11881
              cgi_timeout /* in seconds */,
11882
              0.0,
11883
              1,
11884
              abort_cgi_process,
11885
              (void *)proc,
11886
              NULL);
11887
  }
11888
#endif
11889
11890
  /* Parent closes only one side of the pipes.
11891
   * If we don't mark them as closed, close() attempt before
11892
   * return from this function throws an exception on Windows.
11893
   * Windows does not like when closed descriptor is closed again. */
11894
0
  (void)close(fdin[0]);
11895
0
  (void)close(fdout[1]);
11896
0
  (void)close(fderr[1]);
11897
0
  fdin[0] = fdout[1] = fderr[1] = -1;
11898
11899
0
  if (((in = fdopen(fdin[1], "wb")) == NULL)
11900
0
      || ((out = fdopen(fdout[0], "rb")) == NULL)
11901
0
      || ((err = fdopen(fderr[0], "rb")) == NULL)) {
11902
0
    status = strerror(ERRNO);
11903
0
    mg_cry_internal(conn,
11904
0
                    "Error: CGI program \"%s\": Can not open fd: %s",
11905
0
                    prog,
11906
0
                    status);
11907
0
    mg_send_http_error(conn,
11908
0
                       500,
11909
0
                       "Error: CGI can not open fd\nfdopen: %s",
11910
0
                       status);
11911
0
    goto done;
11912
0
  }
11913
11914
0
  setbuf(in, NULL);
11915
0
  setbuf(out, NULL);
11916
0
  setbuf(err, NULL);
11917
0
  fout.access.fp = out;
11918
11919
0
  if ((conn->content_len != 0) || (conn->is_chunked)) {
11920
0
    DEBUG_TRACE("CGI: send body data (%" INT64_FMT ")\n",
11921
0
                conn->content_len);
11922
11923
    /* This is a POST/PUT request, or another request with body data. */
11924
0
    if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
11925
      /* Error sending the body data */
11926
0
      mg_cry_internal(
11927
0
          conn,
11928
0
          "Error: CGI program \"%s\": Forward body data failed",
11929
0
          prog);
11930
0
      goto done;
11931
0
    }
11932
0
  }
11933
11934
  /* Close so child gets an EOF. */
11935
0
  fclose(in);
11936
0
  in = NULL;
11937
0
  fdin[1] = -1;
11938
11939
  /* Now read CGI reply into a buffer. We need to set correct
11940
   * status code, thus we need to see all HTTP headers first.
11941
   * Do not send anything back to client, until we buffer in all
11942
   * HTTP headers. */
11943
0
  data_len = 0;
11944
0
  buf = (char *)mg_malloc_ctx(buflen, conn->phys_ctx);
11945
0
  if (buf == NULL) {
11946
0
    mg_send_http_error(conn,
11947
0
                       500,
11948
0
                       "Error: Not enough memory for CGI buffer (%u bytes)",
11949
0
                       (unsigned int)buflen);
11950
0
    mg_cry_internal(
11951
0
        conn,
11952
0
        "Error: CGI program \"%s\": Not enough memory for buffer (%u "
11953
0
        "bytes)",
11954
0
        prog,
11955
0
        (unsigned int)buflen);
11956
0
    goto done;
11957
0
  }
11958
11959
0
  DEBUG_TRACE("CGI: %s", "wait for response");
11960
0
  headers_len = read_message(out, conn, buf, (int)buflen, &data_len);
11961
0
  DEBUG_TRACE("CGI: response: %li", (signed long)headers_len);
11962
11963
0
  if (headers_len <= 0) {
11964
11965
    /* Could not parse the CGI response. Check if some error message on
11966
     * stderr. */
11967
0
    i = pull_all(err, conn, buf, (int)buflen);
11968
0
    if (i > 0) {
11969
      /* CGI program explicitly sent an error */
11970
      /* Write the error message to the internal log */
11971
0
      mg_cry_internal(conn,
11972
0
                      "Error: CGI program \"%s\" sent error "
11973
0
                      "message: [%.*s]",
11974
0
                      prog,
11975
0
                      i,
11976
0
                      buf);
11977
      /* Don't send the error message back to the client */
11978
0
      mg_send_http_error(conn,
11979
0
                         500,
11980
0
                         "Error: CGI program \"%s\" failed.",
11981
0
                         prog);
11982
0
    } else {
11983
      /* CGI program did not explicitly send an error, but a broken
11984
       * respon header */
11985
0
      mg_cry_internal(conn,
11986
0
                      "Error: CGI program sent malformed or too big "
11987
0
                      "(>%u bytes) HTTP headers: [%.*s]",
11988
0
                      (unsigned)buflen,
11989
0
                      data_len,
11990
0
                      buf);
11991
11992
0
      mg_send_http_error(conn,
11993
0
                         500,
11994
0
                         "Error: CGI program sent malformed or too big "
11995
0
                         "(>%u bytes) HTTP headers: [%.*s]",
11996
0
                         (unsigned)buflen,
11997
0
                         data_len,
11998
0
                         buf);
11999
0
    }
12000
12001
    /* in both cases, abort processing CGI */
12002
0
    goto done;
12003
0
  }
12004
12005
0
  pbuf = buf;
12006
0
  buf[headers_len - 1] = '\0';
12007
0
  ri.num_headers = parse_http_headers(&pbuf, ri.http_headers);
12008
12009
  /* Make up and send the status line */
12010
0
  status_text = "OK";
12011
0
  if ((status = get_header(ri.http_headers, ri.num_headers, "Status"))
12012
0
      != NULL) {
12013
0
    conn->status_code = atoi(status);
12014
0
    status_text = status;
12015
0
    while (isdigit((unsigned char)*status_text) || *status_text == ' ') {
12016
0
      status_text++;
12017
0
    }
12018
0
  } else if (get_header(ri.http_headers, ri.num_headers, "Location")
12019
0
             != NULL) {
12020
0
    conn->status_code = 307;
12021
0
  } else {
12022
0
    conn->status_code = 200;
12023
0
  }
12024
12025
0
  if (!should_keep_alive(conn)) {
12026
0
    conn->must_close = 1;
12027
0
  }
12028
12029
0
  DEBUG_TRACE("CGI: response %u %s", conn->status_code, status_text);
12030
12031
0
  (void)mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, status_text);
12032
12033
  /* Send headers */
12034
0
  for (i = 0; i < ri.num_headers; i++) {
12035
0
    DEBUG_TRACE("CGI header: %s: %s",
12036
0
                ri.http_headers[i].name,
12037
0
                ri.http_headers[i].value);
12038
0
    mg_printf(conn,
12039
0
              "%s: %s\r\n",
12040
0
              ri.http_headers[i].name,
12041
0
              ri.http_headers[i].value);
12042
0
  }
12043
0
  mg_write(conn, "\r\n", 2);
12044
12045
  /* Send chunk of data that may have been read after the headers */
12046
0
  mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len));
12047
12048
  /* Read the rest of CGI output and send to the client */
12049
0
  DEBUG_TRACE("CGI: %s", "forward all data");
12050
0
  send_file_data(conn, &fout, 0, INT64_MAX, no_buffering); /* send CGI data */
12051
0
  DEBUG_TRACE("CGI: %s", "all data sent");
12052
12053
0
done:
12054
0
  mg_free(blk.var);
12055
0
  mg_free(blk.buf);
12056
12057
0
  if (pid != (pid_t)-1) {
12058
0
    abort_cgi_process((void *)proc);
12059
0
  }
12060
12061
0
  if (fdin[0] != -1) {
12062
0
    close(fdin[0]);
12063
0
  }
12064
0
  if (fdout[1] != -1) {
12065
0
    close(fdout[1]);
12066
0
  }
12067
0
  if (fderr[1] != -1) {
12068
0
    close(fderr[1]);
12069
0
  }
12070
12071
0
  if (in != NULL) {
12072
0
    fclose(in);
12073
0
  } else if (fdin[1] != -1) {
12074
0
    close(fdin[1]);
12075
0
  }
12076
12077
0
  if (out != NULL) {
12078
0
    fclose(out);
12079
0
  } else if (fdout[0] != -1) {
12080
0
    close(fdout[0]);
12081
0
  }
12082
12083
0
  if (err != NULL) {
12084
0
    fclose(err);
12085
0
  } else if (fderr[0] != -1) {
12086
0
    close(fderr[0]);
12087
0
  }
12088
12089
0
  mg_free(buf);
12090
0
}
12091
#endif /* !NO_CGI */
12092
12093
12094
#if !defined(NO_FILES)
12095
static void
12096
dav_mkcol(struct mg_connection *conn, const char *path)
12097
0
{
12098
0
  int rc, body_len;
12099
0
  struct de de;
12100
12101
0
  if (conn == NULL) {
12102
0
    return;
12103
0
  }
12104
12105
  /* TODO (mid): Check the mg_send_http_error situations in this function
12106
   */
12107
12108
0
  memset(&de.file, 0, sizeof(de.file));
12109
0
  if (!mg_stat(conn, path, &de.file)) {
12110
0
    mg_cry_internal(conn,
12111
0
                    "%s: mg_stat(%s) failed: %s",
12112
0
                    __func__,
12113
0
                    path,
12114
0
                    strerror(ERRNO));
12115
0
  }
12116
12117
0
  if (de.file.last_modified) {
12118
    /* TODO (mid): This check does not seem to make any sense ! */
12119
    /* TODO (mid): Add a webdav unit test first, before changing
12120
     * anything here. */
12121
0
    mg_send_http_error(
12122
0
        conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO));
12123
0
    return;
12124
0
  }
12125
12126
0
  body_len = conn->data_len - conn->request_len;
12127
0
  if (body_len > 0) {
12128
0
    mg_send_http_error(
12129
0
        conn, 415, "Error: mkcol(%s): %s", path, strerror(ERRNO));
12130
0
    return;
12131
0
  }
12132
12133
0
  rc = mg_mkdir(conn, path, 0755);
12134
0
  DEBUG_TRACE("mkdir %s: %i", path, rc);
12135
0
  if (rc == 0) {
12136
    /* Create 201 "Created" response */
12137
0
    mg_response_header_start(conn, 201);
12138
0
    send_static_cache_header(conn);
12139
0
    send_additional_header(conn);
12140
0
    mg_response_header_add(conn, "Content-Length", "0", -1);
12141
12142
    /* Send all headers - there is no body */
12143
0
    mg_response_header_send(conn);
12144
0
  } else {
12145
0
    int http_status = 500;
12146
0
    switch (errno) {
12147
0
    case EEXIST:
12148
0
      http_status = 405;
12149
0
      break;
12150
0
    case EACCES:
12151
0
      http_status = 403;
12152
0
      break;
12153
0
    case ENOENT:
12154
0
      http_status = 409;
12155
0
      break;
12156
0
    }
12157
12158
0
    mg_send_http_error(conn,
12159
0
                       http_status,
12160
0
                       "Error processing %s: %s",
12161
0
                       path,
12162
0
                       strerror(ERRNO));
12163
0
  }
12164
0
}
12165
12166
12167
/* Forward decrlaration */
12168
static int get_uri_type(const char *uri);
12169
static const char *
12170
get_rel_url_at_current_server(const char *uri,
12171
                              const struct mg_connection *conn);
12172
12173
12174
static void
12175
dav_move_file(struct mg_connection *conn, const char *path, int do_copy)
12176
0
{
12177
0
  const char *overwrite_hdr;
12178
0
  const char *destination_hdr;
12179
0
  const char *root;
12180
0
  int rc, dest_uri_type;
12181
0
  int http_status = 400;
12182
0
  int do_overwrite = 0;
12183
0
  int destination_ok = 0;
12184
0
  char dest_path[UTF8_PATH_MAX];
12185
0
  struct mg_file_stat ignored;
12186
12187
0
  if (conn == NULL) {
12188
0
    return;
12189
0
  }
12190
12191
0
  root = conn->dom_ctx->config[DOCUMENT_ROOT];
12192
0
  overwrite_hdr = mg_get_header(conn, "Overwrite");
12193
0
  destination_hdr = mg_get_header(conn, "Destination");
12194
0
  if ((overwrite_hdr != NULL) && (toupper(overwrite_hdr[0]) == 'T')) {
12195
0
    do_overwrite = 1;
12196
0
  }
12197
12198
0
  if ((destination_hdr == NULL) || (destination_hdr[0] == 0)) {
12199
0
    mg_send_http_error(conn, 400, "%s", "Missing destination");
12200
0
    return;
12201
0
  }
12202
12203
0
  if (root != NULL) {
12204
0
    char *local_dest = NULL;
12205
0
    dest_uri_type = get_uri_type(destination_hdr);
12206
0
    if (dest_uri_type == 2) {
12207
0
      local_dest = mg_strdup_ctx(destination_hdr, conn->phys_ctx);
12208
0
    } else if ((dest_uri_type == 3) || (dest_uri_type == 4)) {
12209
0
      const char *h =
12210
0
          get_rel_url_at_current_server(destination_hdr, conn);
12211
0
      if (h) {
12212
0
        size_t len = strlen(h);
12213
0
        local_dest = mg_malloc_ctx(len + 1, conn->phys_ctx);
12214
0
        mg_url_decode(h, (int)len, local_dest, (int)len + 1, 0);
12215
0
      }
12216
0
    }
12217
0
    if (local_dest != NULL) {
12218
0
      remove_dot_segments(local_dest);
12219
0
      if (local_dest[0] == '/') {
12220
0
        int trunc_check = 0;
12221
0
        mg_snprintf(conn,
12222
0
                    &trunc_check,
12223
0
                    dest_path,
12224
0
                    sizeof(dest_path),
12225
0
                    "%s/%s",
12226
0
                    root,
12227
0
                    local_dest);
12228
0
        if (trunc_check == 0) {
12229
0
          destination_ok = 1;
12230
0
        }
12231
0
      }
12232
0
      mg_free(local_dest);
12233
0
    }
12234
0
  }
12235
12236
0
  if (!destination_ok) {
12237
0
    mg_send_http_error(conn, 502, "%s", "Illegal destination");
12238
0
    return;
12239
0
  }
12240
12241
  /* Check now if this file exists */
12242
0
  if (mg_stat(conn, dest_path, &ignored)) {
12243
    /* File exists */
12244
0
    if (do_overwrite) {
12245
      /* Overwrite allowed: delete the file first */
12246
0
      if (0 != remove(dest_path)) {
12247
        /* No overwrite: return error */
12248
0
        mg_send_http_error(conn,
12249
0
                           403,
12250
0
                           "Cannot overwrite file: %s",
12251
0
                           dest_path);
12252
0
        return;
12253
0
      }
12254
0
    } else {
12255
      /* No overwrite: return error */
12256
0
      mg_send_http_error(conn,
12257
0
                         412,
12258
0
                         "Destination already exists: %s",
12259
0
                         dest_path);
12260
0
      return;
12261
0
    }
12262
0
  }
12263
12264
  /* Copy / Move / Rename operation. */
12265
0
  DEBUG_TRACE("%s %s to %s", (do_copy ? "copy" : "move"), path, dest_path);
12266
#if defined(_WIN32)
12267
  {
12268
    /* For Windows, we need to convert from UTF-8 to UTF-16 first. */
12269
    wchar_t wSource[UTF16_PATH_MAX];
12270
    wchar_t wDest[UTF16_PATH_MAX];
12271
    BOOL ok;
12272
12273
    path_to_unicode(conn, path, wSource, ARRAY_SIZE(wSource));
12274
    path_to_unicode(conn, dest_path, wDest, ARRAY_SIZE(wDest));
12275
    if (do_copy) {
12276
      ok = CopyFileW(wSource, wDest, do_overwrite ? FALSE : TRUE);
12277
    } else {
12278
      ok = MoveFileExW(wSource,
12279
                       wDest,
12280
                       do_overwrite ? MOVEFILE_REPLACE_EXISTING : 0);
12281
    }
12282
    if (ok) {
12283
      rc = 0;
12284
    } else {
12285
      DWORD lastErr = GetLastError();
12286
      if (lastErr == ERROR_ALREADY_EXISTS) {
12287
        mg_send_http_error(conn,
12288
                           412,
12289
                           "Destination already exists: %s",
12290
                           dest_path);
12291
        return;
12292
      }
12293
      rc = -1;
12294
      http_status = 400;
12295
    }
12296
  }
12297
12298
#else
12299
0
  {
12300
    /* Linux uses already UTF-8, we don't need to convert file names. */
12301
12302
0
    if (do_copy) {
12303
      /* TODO: COPY for Linux. */
12304
0
      mg_send_http_error(conn, 403, "%s", "COPY forbidden");
12305
0
      return;
12306
0
    }
12307
12308
0
    rc = rename(path, dest_path);
12309
0
    if (rc) {
12310
0
      switch (errno) {
12311
0
      case EEXIST:
12312
0
        http_status = 412;
12313
0
        break;
12314
0
      case EACCES:
12315
0
        http_status = 403;
12316
0
        break;
12317
0
      case ENOENT:
12318
0
        http_status = 409;
12319
0
        break;
12320
0
      }
12321
0
    }
12322
0
  }
12323
0
#endif
12324
12325
0
  if (rc == 0) {
12326
    /* Create 204 "No Content" response */
12327
0
    mg_response_header_start(conn, 204);
12328
0
    mg_response_header_add(conn, "Content-Length", "0", -1);
12329
12330
    /* Send all headers - there is no body */
12331
0
    mg_response_header_send(conn);
12332
0
  } else {
12333
0
    mg_send_http_error(conn, http_status, "Operation failed");
12334
0
  }
12335
0
}
12336
12337
12338
static void
12339
put_file(struct mg_connection *conn, const char *path)
12340
0
{
12341
0
  struct mg_file file = STRUCT_FILE_INITIALIZER;
12342
0
  const char *range;
12343
0
  int64_t r1, r2;
12344
0
  int rc;
12345
12346
0
  if (conn == NULL) {
12347
0
    return;
12348
0
  }
12349
12350
0
  DEBUG_TRACE("store %s", path);
12351
12352
0
  if (mg_stat(conn, path, &file.stat)) {
12353
    /* File already exists */
12354
0
    conn->status_code = 200;
12355
12356
0
    if (file.stat.is_directory) {
12357
      /* This is an already existing directory,
12358
       * so there is nothing to do for the server. */
12359
0
      rc = 0;
12360
12361
0
    } else {
12362
      /* File exists and is not a directory. */
12363
      /* Can it be replaced? */
12364
12365
      /* Check if the server may write this file */
12366
0
      if (access(path, W_OK) == 0) {
12367
        /* Access granted */
12368
0
        rc = 1;
12369
0
      } else {
12370
0
        mg_send_http_error(
12371
0
            conn,
12372
0
            403,
12373
0
            "Error: Put not possible\nReplacing %s is not allowed",
12374
0
            path);
12375
0
        return;
12376
0
      }
12377
0
    }
12378
0
  } else {
12379
    /* File should be created */
12380
0
    conn->status_code = 201;
12381
0
    rc = put_dir(conn, path);
12382
0
  }
12383
12384
0
  if (rc == 0) {
12385
    /* put_dir returns 0 if path is a directory */
12386
12387
    /* Create response */
12388
0
    mg_response_header_start(conn, conn->status_code);
12389
0
    send_no_cache_header(conn);
12390
0
    send_additional_header(conn);
12391
0
    mg_response_header_add(conn, "Content-Length", "0", -1);
12392
12393
    /* Send all headers - there is no body */
12394
0
    mg_response_header_send(conn);
12395
12396
    /* Request to create a directory has been fulfilled successfully.
12397
     * No need to put a file. */
12398
0
    return;
12399
0
  }
12400
12401
0
  if (rc == -1) {
12402
    /* put_dir returns -1 if the path is too long */
12403
0
    mg_send_http_error(conn,
12404
0
                       414,
12405
0
                       "Error: Path too long\nput_dir(%s): %s",
12406
0
                       path,
12407
0
                       strerror(ERRNO));
12408
0
    return;
12409
0
  }
12410
12411
0
  if (rc == -2) {
12412
    /* put_dir returns -2 if the directory can not be created */
12413
0
    mg_send_http_error(conn,
12414
0
                       500,
12415
0
                       "Error: Can not create directory\nput_dir(%s): %s",
12416
0
                       path,
12417
0
                       strerror(ERRNO));
12418
0
    return;
12419
0
  }
12420
12421
  /* A file should be created or overwritten. */
12422
  /* Currently CivetWeb does not need read+write access. */
12423
0
  if (!mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &file)
12424
0
      || file.access.fp == NULL) {
12425
0
    (void)mg_fclose(&file.access);
12426
0
    mg_send_http_error(conn,
12427
0
                       500,
12428
0
                       "Error: Can not create file\nfopen(%s): %s",
12429
0
                       path,
12430
0
                       strerror(ERRNO));
12431
0
    return;
12432
0
  }
12433
12434
0
  fclose_on_exec(&file.access, conn);
12435
0
  range = mg_get_header(conn, "Content-Range");
12436
0
  r1 = r2 = 0;
12437
0
  if ((range != NULL) && parse_range_header(range, &r1, &r2) > 0) {
12438
0
    conn->status_code = 206; /* Partial content */
12439
0
    if (0 != fseeko(file.access.fp, r1, SEEK_SET)) {
12440
0
      mg_send_http_error(conn,
12441
0
                         500,
12442
0
                         "Error: Internal error processing file %s",
12443
0
                         path);
12444
0
      return;
12445
0
    }
12446
0
  }
12447
12448
0
  if (!forward_body_data(conn, file.access.fp, INVALID_SOCKET, NULL)) {
12449
    /* forward_body_data failed.
12450
     * The error code has already been sent to the client,
12451
     * and conn->status_code is already set. */
12452
0
    (void)mg_fclose(&file.access);
12453
0
    return;
12454
0
  }
12455
12456
0
  if (mg_fclose(&file.access) != 0) {
12457
    /* fclose failed. This might have different reasons, but a likely
12458
     * one is "no space on disk", http 507. */
12459
0
    conn->status_code = 507;
12460
0
  }
12461
12462
  /* Create response (status_code has been set before) */
12463
0
  mg_response_header_start(conn, conn->status_code);
12464
0
  send_no_cache_header(conn);
12465
0
  send_additional_header(conn);
12466
0
  mg_response_header_add(conn, "Content-Length", "0", -1);
12467
12468
  /* Send all headers - there is no body */
12469
0
  mg_response_header_send(conn);
12470
0
}
12471
12472
12473
static void
12474
delete_file(struct mg_connection *conn, const char *path)
12475
0
{
12476
0
  struct de de;
12477
0
  memset(&de.file, 0, sizeof(de.file));
12478
0
  if (!mg_stat(conn, path, &de.file)) {
12479
    /* mg_stat returns 0 if the file does not exist */
12480
0
    mg_send_http_error(conn,
12481
0
                       404,
12482
0
                       "Error: Cannot delete file\nFile %s not found",
12483
0
                       path);
12484
0
    return;
12485
0
  }
12486
12487
0
  DEBUG_TRACE("delete %s", path);
12488
12489
0
  if (de.file.is_directory) {
12490
0
    if (remove_directory(conn, path)) {
12491
      /* Delete is successful: Return 204 without content. */
12492
0
      mg_send_http_error(conn, 204, "%s", "");
12493
0
    } else {
12494
      /* Delete is not successful: Return 500 (Server error). */
12495
0
      mg_send_http_error(conn, 500, "Error: Could not delete %s", path);
12496
0
    }
12497
0
    return;
12498
0
  }
12499
12500
  /* This is an existing file (not a directory).
12501
   * Check if write permission is granted. */
12502
0
  if (access(path, W_OK) != 0) {
12503
    /* File is read only */
12504
0
    mg_send_http_error(
12505
0
        conn,
12506
0
        403,
12507
0
        "Error: Delete not possible\nDeleting %s is not allowed",
12508
0
        path);
12509
0
    return;
12510
0
  }
12511
12512
  /* Try to delete it. */
12513
0
  if (mg_remove(conn, path) == 0) {
12514
    /* Delete was successful: Return 204 without content. */
12515
0
    mg_response_header_start(conn, 204);
12516
0
    send_no_cache_header(conn);
12517
0
    send_additional_header(conn);
12518
0
    mg_response_header_add(conn, "Content-Length", "0", -1);
12519
0
    mg_response_header_send(conn);
12520
12521
0
  } else {
12522
    /* Delete not successful (file locked). */
12523
0
    mg_send_http_error(conn,
12524
0
                       423,
12525
0
                       "Error: Cannot delete file\nremove(%s): %s",
12526
0
                       path,
12527
0
                       strerror(ERRNO));
12528
0
  }
12529
0
}
12530
#endif /* !NO_FILES */
12531
12532
12533
#if !defined(NO_FILESYSTEMS)
12534
static void
12535
send_ssi_file(struct mg_connection *, const char *, struct mg_file *, int);
12536
12537
12538
static void
12539
do_ssi_include(struct mg_connection *conn,
12540
               const char *ssi,
12541
               char *tag,
12542
               int include_level)
12543
0
{
12544
0
  char file_name[MG_BUF_LEN], path[512], *p;
12545
0
  struct mg_file file = STRUCT_FILE_INITIALIZER;
12546
0
  size_t len;
12547
0
  int truncated = 0;
12548
12549
0
  if (conn == NULL) {
12550
0
    return;
12551
0
  }
12552
12553
  /* sscanf() is safe here, since send_ssi_file() also uses buffer
12554
   * of size MG_BUF_LEN to get the tag. So strlen(tag) is
12555
   * always < MG_BUF_LEN. */
12556
0
  if (sscanf(tag, " virtual=\"%511[^\"]\"", file_name) == 1) {
12557
    /* File name is relative to the webserver root */
12558
0
    file_name[511] = 0;
12559
0
    (void)mg_snprintf(conn,
12560
0
                      &truncated,
12561
0
                      path,
12562
0
                      sizeof(path),
12563
0
                      "%s/%s",
12564
0
                      conn->dom_ctx->config[DOCUMENT_ROOT],
12565
0
                      file_name);
12566
12567
0
  } else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) {
12568
    /* File name is relative to the webserver working directory
12569
     * or it is absolute system path */
12570
0
    file_name[511] = 0;
12571
0
    (void)
12572
0
        mg_snprintf(conn, &truncated, path, sizeof(path), "%s", file_name);
12573
12574
0
  } else if ((sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1)
12575
0
             || (sscanf(tag, " \"%511[^\"]\"", file_name) == 1)) {
12576
    /* File name is relative to the current document */
12577
0
    file_name[511] = 0;
12578
0
    (void)mg_snprintf(conn, &truncated, path, sizeof(path), "%s", ssi);
12579
12580
0
    if (!truncated) {
12581
0
      if ((p = strrchr(path, '/')) != NULL) {
12582
0
        p[1] = '\0';
12583
0
      }
12584
0
      len = strlen(path);
12585
0
      (void)mg_snprintf(conn,
12586
0
                        &truncated,
12587
0
                        path + len,
12588
0
                        sizeof(path) - len,
12589
0
                        "%s",
12590
0
                        file_name);
12591
0
    }
12592
12593
0
  } else {
12594
0
    mg_cry_internal(conn, "Bad SSI #include: [%s]", tag);
12595
0
    return;
12596
0
  }
12597
12598
0
  if (truncated) {
12599
0
    mg_cry_internal(conn, "SSI #include path length overflow: [%s]", tag);
12600
0
    return;
12601
0
  }
12602
12603
0
  if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, &file)) {
12604
0
    mg_cry_internal(conn,
12605
0
                    "Cannot open SSI #include: [%s]: fopen(%s): %s",
12606
0
                    tag,
12607
0
                    path,
12608
0
                    strerror(ERRNO));
12609
0
  } else {
12610
0
    fclose_on_exec(&file.access, conn);
12611
0
    if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path)
12612
0
        > 0) {
12613
0
      send_ssi_file(conn, path, &file, include_level + 1);
12614
0
    } else {
12615
0
      send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
12616
0
    }
12617
0
    (void)mg_fclose(&file.access); /* Ignore errors for readonly files */
12618
0
  }
12619
0
}
12620
12621
12622
#if !defined(NO_POPEN)
12623
static void
12624
do_ssi_exec(struct mg_connection *conn, char *tag)
12625
0
{
12626
0
  char cmd[1024] = "";
12627
0
  struct mg_file file = STRUCT_FILE_INITIALIZER;
12628
12629
0
  if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) {
12630
0
    mg_cry_internal(conn, "Bad SSI #exec: [%s]", tag);
12631
0
  } else {
12632
0
    cmd[1023] = 0;
12633
0
    if ((file.access.fp = popen(cmd, "r")) == NULL) {
12634
0
      mg_cry_internal(conn,
12635
0
                      "Cannot SSI #exec: [%s]: %s",
12636
0
                      cmd,
12637
0
                      strerror(ERRNO));
12638
0
    } else {
12639
0
      send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
12640
0
      pclose(file.access.fp);
12641
0
    }
12642
0
  }
12643
0
}
12644
#endif /* !NO_POPEN */
12645
12646
12647
static int
12648
mg_fgetc(struct mg_file *filep)
12649
0
{
12650
0
  if (filep == NULL) {
12651
0
    return EOF;
12652
0
  }
12653
12654
0
  if (filep->access.fp != NULL) {
12655
0
    return fgetc(filep->access.fp);
12656
0
  } else {
12657
0
    return EOF;
12658
0
  }
12659
0
}
12660
12661
12662
static void
12663
send_ssi_file(struct mg_connection *conn,
12664
              const char *path,
12665
              struct mg_file *filep,
12666
              int include_level)
12667
0
{
12668
0
  char buf[MG_BUF_LEN];
12669
0
  int ch, len, in_tag, in_ssi_tag;
12670
12671
0
  if (include_level > 10) {
12672
0
    mg_cry_internal(conn, "SSI #include level is too deep (%s)", path);
12673
0
    return;
12674
0
  }
12675
12676
0
  in_tag = in_ssi_tag = len = 0;
12677
12678
  /* Read file, byte by byte, and look for SSI include tags */
12679
0
  while ((ch = mg_fgetc(filep)) != EOF) {
12680
12681
0
    if (in_tag) {
12682
      /* We are in a tag, either SSI tag or html tag */
12683
12684
0
      if (ch == '>') {
12685
        /* Tag is closing */
12686
0
        buf[len++] = '>';
12687
12688
0
        if (in_ssi_tag) {
12689
          /* Handle SSI tag */
12690
0
          buf[len] = 0;
12691
12692
0
          if ((len > 12) && !memcmp(buf + 5, "include", 7)) {
12693
0
            do_ssi_include(conn, path, buf + 12, include_level + 1);
12694
0
#if !defined(NO_POPEN)
12695
0
          } else if ((len > 9) && !memcmp(buf + 5, "exec", 4)) {
12696
0
            do_ssi_exec(conn, buf + 9);
12697
0
#endif /* !NO_POPEN */
12698
0
          } else {
12699
0
            mg_cry_internal(conn,
12700
0
                            "%s: unknown SSI "
12701
0
                            "command: \"%s\"",
12702
0
                            path,
12703
0
                            buf);
12704
0
          }
12705
0
          len = 0;
12706
0
          in_ssi_tag = in_tag = 0;
12707
12708
0
        } else {
12709
          /* Not an SSI tag */
12710
          /* Flush buffer */
12711
0
          (void)mg_write(conn, buf, (size_t)len);
12712
0
          len = 0;
12713
0
          in_tag = 0;
12714
0
        }
12715
12716
0
      } else {
12717
        /* Tag is still open */
12718
0
        buf[len++] = (char)(ch & 0xff);
12719
12720
0
        if ((len == 5) && !memcmp(buf, "<!--#", 5)) {
12721
          /* All SSI tags start with <!--# */
12722
0
          in_ssi_tag = 1;
12723
0
        }
12724
12725
0
        if ((len + 2) > (int)sizeof(buf)) {
12726
          /* Tag to long for buffer */
12727
0
          mg_cry_internal(conn, "%s: tag is too large", path);
12728
0
          return;
12729
0
        }
12730
0
      }
12731
12732
0
    } else {
12733
12734
      /* We are not in a tag yet. */
12735
0
      if (ch == '<') {
12736
        /* Tag is opening */
12737
0
        in_tag = 1;
12738
12739
0
        if (len > 0) {
12740
          /* Flush current buffer.
12741
           * Buffer is filled with "len" bytes. */
12742
0
          (void)mg_write(conn, buf, (size_t)len);
12743
0
        }
12744
        /* Store the < */
12745
0
        len = 1;
12746
0
        buf[0] = '<';
12747
12748
0
      } else {
12749
        /* No Tag */
12750
        /* Add data to buffer */
12751
0
        buf[len++] = (char)(ch & 0xff);
12752
        /* Flush if buffer is full */
12753
0
        if (len == (int)sizeof(buf)) {
12754
0
          mg_write(conn, buf, (size_t)len);
12755
0
          len = 0;
12756
0
        }
12757
0
      }
12758
0
    }
12759
0
  }
12760
12761
  /* Send the rest of buffered data */
12762
0
  if (len > 0) {
12763
0
    mg_write(conn, buf, (size_t)len);
12764
0
  }
12765
0
}
12766
12767
12768
static void
12769
handle_ssi_file_request(struct mg_connection *conn,
12770
                        const char *path,
12771
                        struct mg_file *filep)
12772
0
{
12773
0
  char date[64];
12774
0
  time_t curtime = time(NULL);
12775
12776
0
  if ((conn == NULL) || (path == NULL) || (filep == NULL)) {
12777
0
    return;
12778
0
  }
12779
12780
0
  if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) {
12781
    /* File exists (precondition for calling this function),
12782
     * but can not be opened by the server. */
12783
0
    mg_send_http_error(conn,
12784
0
                       500,
12785
0
                       "Error: Cannot read file\nfopen(%s): %s",
12786
0
                       path,
12787
0
                       strerror(ERRNO));
12788
0
  } else {
12789
    /* Set "must_close" for HTTP/1.x, since we do not know the
12790
     * content length */
12791
0
    conn->must_close = 1;
12792
0
    gmt_time_string(date, sizeof(date), &curtime);
12793
0
    fclose_on_exec(&filep->access, conn);
12794
12795
    /* 200 OK response */
12796
0
    mg_response_header_start(conn, 200);
12797
0
    send_no_cache_header(conn);
12798
0
    send_additional_header(conn);
12799
0
    send_cors_header(conn);
12800
0
    mg_response_header_add(conn, "Content-Type", "text/html", -1);
12801
0
    mg_response_header_send(conn);
12802
12803
    /* Header sent, now send body */
12804
0
    send_ssi_file(conn, path, filep, 0);
12805
0
    (void)mg_fclose(&filep->access); /* Ignore errors for readonly files */
12806
0
  }
12807
0
}
12808
#endif /* NO_FILESYSTEMS */
12809
12810
12811
#if !defined(NO_FILES)
12812
static void
12813
send_options(struct mg_connection *conn)
12814
0
{
12815
0
  if (!conn || !all_methods) {
12816
0
    return;
12817
0
  }
12818
12819
  /* We do not set a "Cache-Control" header here, but leave the default.
12820
   * Since browsers do not send an OPTIONS request, we can not test the
12821
   * effect anyway. */
12822
12823
0
  mg_response_header_start(conn, 200);
12824
0
  mg_response_header_add(conn, "Content-Type", "text/html", -1);
12825
12826
0
  if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
12827
    /* Use the same as before */
12828
0
    mg_response_header_add(conn, "Allow", all_methods, -1);
12829
0
    mg_response_header_add(conn, "DAV", "1", -1);
12830
0
  } else {
12831
    /* TODO: Check this later for HTTP/2 */
12832
0
    mg_response_header_add(conn, "Allow", "GET, POST", -1);
12833
0
  }
12834
0
  send_additional_header(conn);
12835
0
  mg_response_header_send(conn);
12836
0
}
12837
12838
12839
/* Writes PROPFIND properties for a collection element */
12840
static int
12841
print_props(struct mg_connection *conn,
12842
            const char *uri,
12843
            const char *name,
12844
            struct mg_file_stat *filep)
12845
0
{
12846
0
  size_t i;
12847
0
  char mtime[64];
12848
0
  char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
12849
0
  char *link_concat;
12850
0
  size_t link_concat_len;
12851
12852
0
  if ((conn == NULL) || (uri == NULL) || (name == NULL) || (filep == NULL)) {
12853
0
    return 0;
12854
0
  }
12855
12856
0
  link_concat_len = strlen(uri) + strlen(name) + 1;
12857
0
  link_concat = mg_malloc_ctx(link_concat_len, conn->phys_ctx);
12858
0
  if (!link_concat) {
12859
0
    return 0;
12860
0
  }
12861
0
  strcpy(link_concat, uri);
12862
0
  strcat(link_concat, name);
12863
12864
  /* Get full link used in request */
12865
0
  mg_construct_local_link(
12866
0
      conn, link_buf, sizeof(link_buf), NULL, 0, link_concat);
12867
12868
  /*
12869
  OutputDebugStringA("print_props:\n  uri: ");
12870
  OutputDebugStringA(uri);
12871
  OutputDebugStringA("\n  name: ");
12872
  OutputDebugStringA(name);
12873
  OutputDebugStringA("\n  link: ");
12874
  OutputDebugStringA(link_buf);
12875
  OutputDebugStringA("\n");
12876
  */
12877
12878
0
  gmt_time_string(mtime, sizeof(mtime), &filep->last_modified);
12879
0
  mg_printf(conn,
12880
0
            "<d:response>"
12881
0
            "<d:href>%s</d:href>"
12882
0
            "<d:propstat>"
12883
0
            "<d:prop>"
12884
0
            "<d:resourcetype>%s</d:resourcetype>"
12885
0
            "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
12886
0
            "<d:getlastmodified>%s</d:getlastmodified>"
12887
0
            "<d:lockdiscovery>",
12888
0
            link_buf,
12889
0
            filep->is_directory ? "<d:collection/>" : "",
12890
0
            filep->size,
12891
0
            mtime);
12892
12893
0
  for (i = 0; i < NUM_WEBDAV_LOCKS; i++) {
12894
0
    struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
12895
0
    if (!strcmp(dav_lock[i].path, link_buf)) {
12896
0
      mg_printf(conn,
12897
0
                "<d:activelock>"
12898
0
                "<d:locktype><d:write/></d:locktype>"
12899
0
                "<d:lockscope><d:exclusive/></d:lockscope>"
12900
0
                "<d:depth>0</d:depth>"
12901
0
                "<d:owner>%s</d:owner>"
12902
0
                "<d:timeout>Second-%u</d:timeout>"
12903
0
                "<d:locktoken>"
12904
0
                "<d:href>%s</d:href>"
12905
0
                "</d:locktoken>"
12906
0
                "</d:activelock>\n",
12907
0
                dav_lock[i].user,
12908
0
                (unsigned)LOCK_DURATION_S,
12909
0
                dav_lock[i].token);
12910
0
    }
12911
0
  }
12912
12913
0
  mg_printf(conn,
12914
0
            "</d:lockdiscovery>"
12915
0
            "</d:prop>"
12916
0
            "<d:status>HTTP/1.1 200 OK</d:status>"
12917
0
            "</d:propstat>"
12918
0
            "</d:response>\n");
12919
12920
0
  mg_free(link_concat);
12921
0
  return 1;
12922
0
}
12923
12924
12925
static int
12926
print_dav_dir_entry(struct de *de, void *data)
12927
0
{
12928
0
  struct mg_connection *conn = (struct mg_connection *)data;
12929
0
  if (!de || !conn
12930
0
      || !print_props(
12931
0
             conn, conn->request_info.local_uri, de->file_name, &de->file)) {
12932
    /* stop scan */
12933
0
    return 1;
12934
0
  }
12935
0
  return 0;
12936
0
}
12937
12938
12939
static void
12940
handle_propfind(struct mg_connection *conn,
12941
                const char *path,
12942
                struct mg_file_stat *filep)
12943
0
{
12944
0
  const char *depth = mg_get_header(conn, "Depth");
12945
12946
0
  if (!conn || !path || !filep || !conn->dom_ctx) {
12947
0
    return;
12948
0
  }
12949
12950
  /* return 207 "Multi-Status" */
12951
0
  conn->must_close = 1;
12952
0
  mg_response_header_start(conn, 207);
12953
0
  send_static_cache_header(conn);
12954
0
  send_additional_header(conn);
12955
0
  mg_response_header_add(conn,
12956
0
                         "Content-Type",
12957
0
                         "application/xml; charset=utf-8",
12958
0
                         -1);
12959
0
  mg_response_header_send(conn);
12960
12961
  /* Content */
12962
0
  mg_printf(conn,
12963
0
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
12964
0
            "<d:multistatus xmlns:d='DAV:'>\n");
12965
12966
  /* Print properties for the requested resource itself */
12967
0
  print_props(conn, conn->request_info.local_uri, "", filep);
12968
12969
  /* If it is a directory, print directory entries too if Depth is not 0
12970
   */
12971
0
  if (filep->is_directory
12972
0
      && !mg_strcasecmp(conn->dom_ctx->config[ENABLE_DIRECTORY_LISTING],
12973
0
                        "yes")
12974
0
      && ((depth == NULL) || (strcmp(depth, "0") != 0))) {
12975
0
    scan_directory(conn, path, conn, &print_dav_dir_entry);
12976
0
  }
12977
12978
0
  mg_printf(conn, "%s\n", "</d:multistatus>");
12979
0
}
12980
12981
12982
static void
12983
dav_lock_file(struct mg_connection *conn, const char *path)
12984
0
{
12985
  /* internal function - therefore conn is assumed to be valid */
12986
0
  char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
12987
0
  uint64_t new_locktime;
12988
0
  int lock_index = -1;
12989
0
  int i;
12990
0
  uint64_t LOCK_DURATION_NS =
12991
0
      (uint64_t)(LOCK_DURATION_S) * (uint64_t)1000000000;
12992
0
  struct twebdav_lock *dav_lock = NULL;
12993
12994
0
  if (!path || !conn || !conn->dom_ctx || !conn->request_info.remote_user
12995
0
      || !conn->phys_ctx) {
12996
0
    return;
12997
0
  }
12998
12999
0
  dav_lock = conn->phys_ctx->webdav_lock;
13000
0
  mg_get_request_link(conn, link_buf, sizeof(link_buf));
13001
13002
  /* const char *refresh = mg_get_header(conn, "If"); */
13003
  /* Link refresh should have an "If" header:
13004
   * http://www.webdav.org/specs/rfc2518.html#n-example---refreshing-a-write-lock
13005
   * But it seems Windows Explorer does not send them.
13006
   */
13007
13008
0
  mg_lock_context(conn->phys_ctx);
13009
0
  new_locktime = mg_get_current_time_ns();
13010
13011
  /* Find a slot for a lock */
13012
0
  while (lock_index < 0) {
13013
    /* find existing lock */
13014
0
    for (i = 0; i < NUM_WEBDAV_LOCKS; i++) {
13015
0
      if (!strcmp(dav_lock[i].path, link_buf)) {
13016
0
        if (!strcmp(conn->request_info.remote_user, dav_lock[i].user)) {
13017
          /* locked by the same user */
13018
0
          dav_lock[i].locktime = new_locktime;
13019
0
          lock_index = i;
13020
0
          break;
13021
0
        } else {
13022
          /* already locked by someone else */
13023
0
          if (new_locktime
13024
0
              > (dav_lock[i].locktime + LOCK_DURATION_NS)) {
13025
            /* Lock expired */
13026
0
            dav_lock[i].path[0] = 0;
13027
0
          } else {
13028
            /* Lock still valid */
13029
0
            mg_unlock_context(conn->phys_ctx);
13030
0
            mg_send_http_error(conn, 423, "%s", "Already locked");
13031
0
            return;
13032
0
          }
13033
0
        }
13034
0
      }
13035
0
    }
13036
13037
    /* create new lock token */
13038
0
    for (i = 0; i < NUM_WEBDAV_LOCKS; i++) {
13039
0
      if (dav_lock[i].path[0] == 0) {
13040
0
        char s[32];
13041
0
        dav_lock[i].locktime = mg_get_current_time_ns();
13042
0
        sprintf(s, "%" UINT64_FMT, (uint64_t)dav_lock[i].locktime);
13043
0
        mg_md5(dav_lock[i].token,
13044
0
               link_buf,
13045
0
               "\x01",
13046
0
               s,
13047
0
               "\x01",
13048
0
               conn->request_info.remote_user,
13049
0
               NULL);
13050
0
        mg_strlcpy(dav_lock[i].path,
13051
0
                   link_buf,
13052
0
                   sizeof(dav_lock[i].path));
13053
0
        mg_strlcpy(dav_lock[i].user,
13054
0
                   conn->request_info.remote_user,
13055
0
                   sizeof(dav_lock[i].user));
13056
0
        lock_index = i;
13057
0
        break;
13058
0
      }
13059
0
    }
13060
0
    if (lock_index < 0) {
13061
      /* too many locks. Find oldest lock */
13062
0
      uint64_t oldest_locktime = dav_lock[0].locktime;
13063
0
      lock_index = 0;
13064
0
      for (i = 1; i < NUM_WEBDAV_LOCKS; i++) {
13065
0
        if (dav_lock[i].locktime < oldest_locktime) {
13066
0
          oldest_locktime = dav_lock[i].locktime;
13067
0
          lock_index = i;
13068
0
        }
13069
0
      }
13070
      /* invalidate oldest lock */
13071
0
      dav_lock[lock_index].path[0] = 0;
13072
0
    }
13073
0
  }
13074
0
  mg_unlock_context(conn->phys_ctx);
13075
13076
  /* return 200 "OK" */
13077
0
  conn->must_close = 1;
13078
0
  mg_response_header_start(conn, 200);
13079
0
  send_static_cache_header(conn);
13080
0
  send_additional_header(conn);
13081
0
  mg_response_header_add(conn,
13082
0
                         "Content-Type",
13083
0
                         "application/xml; charset=utf-8",
13084
0
                         -1);
13085
0
  mg_response_header_add(conn, "Lock-Token", dav_lock[lock_index].token, -1);
13086
0
  mg_response_header_send(conn);
13087
13088
  /* Content */
13089
0
  mg_printf(conn,
13090
0
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
13091
0
            "<d:prop xmlns:d=\"DAV:\">\n"
13092
0
            "     <d:lockdiscovery>\n"
13093
0
            "       <d:activelock>\n"
13094
0
            "         <d:lockscope><d:exclusive/></d:lockscope>\n"
13095
0
            "         <d:locktype><d:write/></d:locktype>\n"
13096
0
            "         <d:owner>\n"
13097
0
            "           <d:href>%s</d:href>\n"
13098
0
            "         </d:owner>\n"
13099
0
            "         <d:timeout>Second-%u</d:timeout>\n"
13100
0
            "         <d:locktoken><d:href>%s</d:href></d:locktoken>\n"
13101
0
            "         <d:lockroot>\n"
13102
0
            "           <d:href>%s</d:href>\n"
13103
0
            "         </d:lockroot>\n"
13104
0
            "       </d:activelock>\n"
13105
0
            "     </d:lockdiscovery>\n"
13106
0
            "   </d:prop>\n",
13107
0
            dav_lock[lock_index].user,
13108
0
            (LOCK_DURATION_S),
13109
0
            dav_lock[lock_index].token,
13110
0
            dav_lock[lock_index].path);
13111
0
}
13112
13113
13114
static void
13115
dav_unlock_file(struct mg_connection *conn, const char *path)
13116
0
{
13117
  /* internal function - therefore conn is assumed to be valid */
13118
0
  char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
13119
0
  struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
13120
0
  int lock_index;
13121
13122
0
  if (!path || !conn->dom_ctx || !conn->request_info.remote_user) {
13123
0
    return;
13124
0
  }
13125
13126
0
  mg_get_request_link(conn, link_buf, sizeof(link_buf));
13127
13128
0
  mg_lock_context(conn->phys_ctx);
13129
  /* find existing lock */
13130
0
  for (lock_index = 0; lock_index < NUM_WEBDAV_LOCKS; lock_index++) {
13131
0
    if (!strcmp(dav_lock[lock_index].path, link_buf)) {
13132
      /* Success: return 204 "No Content" */
13133
0
      mg_unlock_context(conn->phys_ctx);
13134
0
      conn->must_close = 1;
13135
0
      mg_response_header_start(conn, 204);
13136
0
      mg_response_header_send(conn);
13137
0
      return;
13138
0
    }
13139
0
  }
13140
0
  mg_unlock_context(conn->phys_ctx);
13141
13142
  /* Error: Cannot unlock a resource that is not locked */
13143
0
  mg_send_http_error(conn, 423, "%s", "Lock not found");
13144
0
}
13145
13146
13147
static void
13148
dav_proppatch(struct mg_connection *conn, const char *path)
13149
0
{
13150
0
  char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
13151
13152
0
  if (!conn || !path || !conn->dom_ctx) {
13153
0
    return;
13154
0
  }
13155
13156
  /* return 207 "Multi-Status" */
13157
0
  conn->must_close = 1;
13158
0
  mg_response_header_start(conn, 207);
13159
0
  send_static_cache_header(conn);
13160
0
  send_additional_header(conn);
13161
0
  mg_response_header_add(conn,
13162
0
                         "Content-Type",
13163
0
                         "application/xml; charset=utf-8",
13164
0
                         -1);
13165
0
  mg_response_header_send(conn);
13166
13167
0
  mg_get_request_link(conn, link_buf, sizeof(link_buf));
13168
13169
  /* Content */
13170
0
  mg_printf(conn,
13171
0
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
13172
0
            "<d:multistatus xmlns:d='DAV:'>\n"
13173
0
            "<d:response>\n<d:href>%s</d:href>\n",
13174
0
            link_buf);
13175
0
  mg_printf(conn,
13176
0
            "<d:propstat><d:status>HTTP/1.1 403 "
13177
0
            "Forbidden</d:status></d:propstat>\n");
13178
0
  mg_printf(conn, "%s\n", "</d:response></d:multistatus>");
13179
0
}
13180
#endif
13181
13182
13183
CIVETWEB_API void
13184
mg_lock_connection(struct mg_connection *conn)
13185
34
{
13186
34
  if (conn) {
13187
34
    (void)pthread_mutex_lock(&conn->mutex);
13188
34
  }
13189
34
}
13190
13191
13192
CIVETWEB_API void
13193
mg_unlock_connection(struct mg_connection *conn)
13194
34
{
13195
34
  if (conn) {
13196
34
    (void)pthread_mutex_unlock(&conn->mutex);
13197
34
  }
13198
34
}
13199
13200
13201
CIVETWEB_API void
13202
mg_lock_context(struct mg_context *ctx)
13203
0
{
13204
0
  if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
13205
0
    (void)pthread_mutex_lock(&ctx->nonce_mutex);
13206
0
  }
13207
0
}
13208
13209
13210
CIVETWEB_API void
13211
mg_unlock_context(struct mg_context *ctx)
13212
0
{
13213
0
  if (ctx && (ctx->context_type == CONTEXT_SERVER)) {
13214
0
    (void)pthread_mutex_unlock(&ctx->nonce_mutex);
13215
0
  }
13216
0
}
13217
13218
13219
#if defined(USE_LUA)
13220
#include "mod_lua.inl"
13221
#endif /* USE_LUA */
13222
13223
#if defined(USE_DUKTAPE)
13224
#include "mod_duktape.inl"
13225
#endif /* USE_DUKTAPE */
13226
13227
#if defined(USE_WEBSOCKET)
13228
13229
#if !defined(NO_SSL_DL)
13230
#if !defined(OPENSSL_API_3_0)
13231
#define SHA_API static
13232
#include "sha1.inl"
13233
#endif
13234
#endif
13235
13236
static int
13237
send_websocket_handshake(struct mg_connection *conn, const char *websock_key)
13238
{
13239
  static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
13240
  char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
13241
  size_t dst_len = sizeof(b64_sha);
13242
#if !defined(OPENSSL_API_3_0)
13243
  SHA_CTX sha_ctx;
13244
#endif
13245
  int truncated;
13246
13247
  /* Calculate Sec-WebSocket-Accept reply from Sec-WebSocket-Key. */
13248
  mg_snprintf(conn, &truncated, buf, sizeof(buf), "%s%s", websock_key, magic);
13249
  if (truncated) {
13250
    conn->must_close = 1;
13251
    return 0;
13252
  }
13253
13254
  DEBUG_TRACE("%s", "Send websocket handshake");
13255
13256
#if defined(OPENSSL_API_3_0)
13257
  EVP_Digest((unsigned char *)buf,
13258
             (uint32_t)strlen(buf),
13259
             (unsigned char *)sha,
13260
             NULL,
13261
             EVP_get_digestbyname("sha1"),
13262
             NULL);
13263
#else
13264
  SHA1_Init(&sha_ctx);
13265
  SHA1_Update(&sha_ctx, (unsigned char *)buf, (uint32_t)strlen(buf));
13266
  SHA1_Final((unsigned char *)sha, &sha_ctx);
13267
#endif
13268
  mg_base64_encode((unsigned char *)sha, sizeof(sha), b64_sha, &dst_len);
13269
  mg_printf(conn,
13270
            "HTTP/1.1 101 Switching Protocols\r\n"
13271
            "Upgrade: websocket\r\n"
13272
            "Connection: Upgrade\r\n"
13273
            "Sec-WebSocket-Accept: %s\r\n",
13274
            b64_sha);
13275
13276
#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13277
  // Send negotiated compression extension parameters
13278
  websocket_deflate_response(conn);
13279
#endif
13280
13281
  if (conn->request_info.acceptedWebSocketSubprotocol) {
13282
    mg_printf(conn,
13283
              "Sec-WebSocket-Protocol: %s\r\n\r\n",
13284
              conn->request_info.acceptedWebSocketSubprotocol);
13285
  } else {
13286
    mg_printf(conn, "%s", "\r\n");
13287
  }
13288
13289
  return 1;
13290
}
13291
13292
13293
#if !defined(MG_MAX_UNANSWERED_PING)
13294
/* Configuration of the maximum number of websocket PINGs that might
13295
 * stay unanswered before the connection is considered broken.
13296
 * Note: The name of this define may still change (until it is
13297
 * defined as a compile parameter in a documentation).
13298
 */
13299
#define MG_MAX_UNANSWERED_PING (5)
13300
#endif
13301
13302
13303
static void
13304
read_websocket(struct mg_connection *conn,
13305
               mg_websocket_data_handler ws_data_handler,
13306
               void *callback_data)
13307
{
13308
  /* Pointer to the beginning of the portion of the incoming websocket
13309
   * message queue.
13310
   * The original websocket upgrade request is never removed, so the queue
13311
   * begins after it. */
13312
  unsigned char *buf = (unsigned char *)conn->buf + conn->request_len;
13313
  int n, error, exit_by_callback;
13314
  int ret;
13315
13316
  /* body_len is the length of the entire queue in bytes
13317
   * len is the length of the current message
13318
   * data_len is the length of the current message's data payload
13319
   * header_len is the length of the current message's header */
13320
  size_t i, len, mask_len = 0, header_len, body_len;
13321
  uint64_t data_len = 0;
13322
13323
  /* "The masking key is a 32-bit value chosen at random by the client."
13324
   * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5
13325
   */
13326
  unsigned char mask[4];
13327
13328
  /* data points to the place where the message is stored when passed to
13329
   * the websocket_data callback.  This is either mem on the stack, or a
13330
   * dynamically allocated buffer if it is too large. */
13331
  unsigned char mem[4096];
13332
  unsigned char mop; /* mask flag and opcode */
13333
13334
  /* Variables used for connection monitoring */
13335
  double timeout = -1.0;
13336
  int enable_ping_pong = 0;
13337
  int ping_count = 0;
13338
13339
  if (conn->dom_ctx->config[ENABLE_WEBSOCKET_PING_PONG]) {
13340
    enable_ping_pong =
13341
        !mg_strcasecmp(conn->dom_ctx->config[ENABLE_WEBSOCKET_PING_PONG],
13342
                       "yes");
13343
  }
13344
13345
  if (conn->dom_ctx->config[WEBSOCKET_TIMEOUT]) {
13346
    timeout = atoi(conn->dom_ctx->config[WEBSOCKET_TIMEOUT]) / 1000.0;
13347
  }
13348
  if ((timeout <= 0.0) && (conn->dom_ctx->config[REQUEST_TIMEOUT])) {
13349
    timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
13350
  }
13351
  if (timeout <= 0.0) {
13352
    timeout = atof(config_options[REQUEST_TIMEOUT].default_value) / 1000.0;
13353
  }
13354
13355
  /* Enter data processing loop */
13356
  DEBUG_TRACE("Websocket connection %s:%u start data processing loop",
13357
              conn->request_info.remote_addr,
13358
              conn->request_info.remote_port);
13359
  conn->in_websocket_handling = 1;
13360
  mg_set_thread_name("wsock");
13361
13362
  /* Loop continuously, reading messages from the socket, invoking the
13363
   * callback, and waiting repeatedly until an error occurs. */
13364
  while (STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)
13365
         && (!conn->must_close)) {
13366
    header_len = 0;
13367
    DEBUG_ASSERT(conn->data_len >= conn->request_len);
13368
    if ((body_len = (size_t)(conn->data_len - conn->request_len)) >= 2) {
13369
      len = buf[1] & 127;
13370
      mask_len = (buf[1] & 128) ? 4 : 0;
13371
      if ((len < 126) && (body_len >= mask_len)) {
13372
        /* inline 7-bit length field */
13373
        data_len = len;
13374
        header_len = 2 + mask_len;
13375
      } else if ((len == 126) && (body_len >= (4 + mask_len))) {
13376
        /* 16-bit length field */
13377
        header_len = 4 + mask_len;
13378
        data_len = ((((size_t)buf[2]) << 8) + buf[3]);
13379
      } else if (body_len >= (10 + mask_len)) {
13380
        /* 64-bit length field */
13381
        uint32_t l1, l2;
13382
        memcpy(&l1, &buf[2], 4); /* Use memcpy for alignment */
13383
        memcpy(&l2, &buf[6], 4);
13384
        header_len = 10 + mask_len;
13385
        data_len = (((uint64_t)ntohl(l1)) << 32) + ntohl(l2);
13386
13387
        if (data_len > (uint64_t)0x7FFF0000ul) {
13388
          /* no can do */
13389
          mg_cry_internal(
13390
              conn,
13391
              "%s",
13392
              "websocket out of memory; closing connection");
13393
          break;
13394
        }
13395
      }
13396
    }
13397
13398
    if ((header_len > 0) && (body_len >= header_len)) {
13399
      /* Allocate space to hold websocket payload */
13400
      unsigned char *data = mem;
13401
13402
      if ((size_t)data_len > (size_t)sizeof(mem)) {
13403
        data = (unsigned char *)mg_malloc_ctx((size_t)data_len,
13404
                                              conn->phys_ctx);
13405
        if (data == NULL) {
13406
          /* Allocation failed, exit the loop and then close the
13407
           * connection */
13408
          mg_cry_internal(
13409
              conn,
13410
              "%s",
13411
              "websocket out of memory; closing connection");
13412
          break;
13413
        }
13414
      }
13415
13416
      /* Copy the mask before we shift the queue and destroy it */
13417
      if (mask_len > 0) {
13418
        memcpy(mask, buf + header_len - mask_len, sizeof(mask));
13419
      } else {
13420
        memset(mask, 0, sizeof(mask));
13421
      }
13422
13423
      /* Read frame payload from the first message in the queue into
13424
       * data and advance the queue by moving the memory in place. */
13425
      DEBUG_ASSERT(body_len >= header_len);
13426
      if (data_len + (uint64_t)header_len > (uint64_t)body_len) {
13427
        mop = buf[0]; /* current mask and opcode */
13428
                      /* Overflow case */
13429
        len = body_len - header_len;
13430
        memcpy(data, buf + header_len, len);
13431
        error = 0;
13432
        while ((uint64_t)len < data_len) {
13433
          n = pull_inner(NULL,
13434
                         conn,
13435
                         (char *)(data + len),
13436
                         (int)(data_len - len),
13437
                         timeout);
13438
          if (n <= -2) {
13439
            error = 1;
13440
            break;
13441
          } else if (n > 0) {
13442
            len += (size_t)n;
13443
          } else {
13444
            /* Timeout: should retry */
13445
            /* TODO: retry condition */
13446
          }
13447
        }
13448
        if (error) {
13449
          mg_cry_internal(
13450
              conn,
13451
              "%s",
13452
              "Websocket pull failed; closing connection");
13453
          if (data != mem) {
13454
            mg_free(data);
13455
          }
13456
          break;
13457
        }
13458
13459
        conn->data_len = conn->request_len;
13460
13461
      } else {
13462
13463
        mop = buf[0]; /* current mask and opcode, overwritten by
13464
                       * memmove() */
13465
13466
        /* Length of the message being read at the front of the
13467
         * queue. Cast to 31 bit is OK, since we limited
13468
         * data_len before. */
13469
        len = (size_t)data_len + header_len;
13470
13471
        /* Copy the data payload into the data pointer for the
13472
         * callback. Cast to 31 bit is OK, since we
13473
         * limited data_len */
13474
        memcpy(data, buf + header_len, (size_t)data_len);
13475
13476
        /* Move the queue forward len bytes */
13477
        memmove(buf, buf + len, body_len - len);
13478
13479
        /* Mark the queue as advanced */
13480
        conn->data_len -= (int)len;
13481
      }
13482
13483
      /* Apply mask if necessary */
13484
      if (mask_len > 0) {
13485
        for (i = 0; i < (size_t)data_len; i++) {
13486
          data[i] ^= mask[i & 3];
13487
        }
13488
      }
13489
13490
      exit_by_callback = 0;
13491
      if (enable_ping_pong && ((mop & 0xF) == MG_WEBSOCKET_OPCODE_PONG)) {
13492
        /* filter PONG messages */
13493
        DEBUG_TRACE("PONG from %s:%u",
13494
                    conn->request_info.remote_addr,
13495
                    conn->request_info.remote_port);
13496
        /* No unanwered PINGs left */
13497
        ping_count = 0;
13498
      } else if (enable_ping_pong
13499
                 && ((mop & 0xF) == MG_WEBSOCKET_OPCODE_PING)) {
13500
        /* reply PING messages */
13501
        DEBUG_TRACE("Reply PING from %s:%u",
13502
                    conn->request_info.remote_addr,
13503
                    conn->request_info.remote_port);
13504
        ret = mg_websocket_write(conn,
13505
                                 MG_WEBSOCKET_OPCODE_PONG,
13506
                                 (char *)data,
13507
                                 (size_t)data_len);
13508
        if (ret <= 0) {
13509
          /* Error: send failed */
13510
          DEBUG_TRACE("Reply PONG failed (%i)", ret);
13511
          break;
13512
        }
13513
13514
      } else {
13515
        /* Exit the loop if callback signals to exit (server side),
13516
         * or "connection close" opcode received (client side). */
13517
        if (ws_data_handler != NULL) {
13518
#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13519
          if (mop & 0x40) {
13520
            /* Inflate the data received if bit RSV1 is set. */
13521
            if (!conn->websocket_deflate_initialized) {
13522
              if (websocket_deflate_initialize(conn, 1) != Z_OK)
13523
                exit_by_callback = 1;
13524
            }
13525
            if (!exit_by_callback) {
13526
              size_t inflate_buf_size_old = 0;
13527
              size_t inflate_buf_size =
13528
                  data_len
13529
                  * 4; // Initial guess of the inflated message
13530
                       // size. We double the memory when needed.
13531
              Bytef *inflated = NULL;
13532
              Bytef *new_mem = NULL;
13533
              conn->websocket_inflate_state.avail_in =
13534
                  (uInt)(data_len + 4);
13535
              conn->websocket_inflate_state.next_in = data;
13536
              // Add trailing 0x00 0x00 0xff 0xff bytes
13537
              data[data_len] = '\x00';
13538
              data[data_len + 1] = '\x00';
13539
              data[data_len + 2] = '\xff';
13540
              data[data_len + 3] = '\xff';
13541
              do {
13542
                if (inflate_buf_size_old == 0) {
13543
                  new_mem =
13544
                      (Bytef *)mg_calloc(inflate_buf_size,
13545
                                         sizeof(Bytef));
13546
                } else {
13547
                  inflate_buf_size *= 2;
13548
                  new_mem =
13549
                      (Bytef *)mg_realloc(inflated,
13550
                                          inflate_buf_size);
13551
                }
13552
                if (new_mem == NULL) {
13553
                  mg_cry_internal(
13554
                      conn,
13555
                      "Out of memory: Cannot allocate "
13556
                      "inflate buffer of %lu bytes",
13557
                      (unsigned long)inflate_buf_size);
13558
                  exit_by_callback = 1;
13559
                  break;
13560
                }
13561
                inflated = new_mem;
13562
                conn->websocket_inflate_state.avail_out =
13563
                    (uInt)(inflate_buf_size
13564
                           - inflate_buf_size_old);
13565
                conn->websocket_inflate_state.next_out =
13566
                    inflated + inflate_buf_size_old;
13567
                ret = inflate(&conn->websocket_inflate_state,
13568
                              Z_SYNC_FLUSH);
13569
                if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR
13570
                    || ret == Z_MEM_ERROR) {
13571
                  mg_cry_internal(
13572
                      conn,
13573
                      "ZLIB inflate error: %i %s",
13574
                      ret,
13575
                      (conn->websocket_inflate_state.msg
13576
                           ? conn->websocket_inflate_state.msg
13577
                           : "<no error message>"));
13578
                  exit_by_callback = 1;
13579
                  break;
13580
                }
13581
                inflate_buf_size_old = inflate_buf_size;
13582
13583
              } while (conn->websocket_inflate_state.avail_out
13584
                       == 0);
13585
              inflate_buf_size -=
13586
                  conn->websocket_inflate_state.avail_out;
13587
              if (!ws_data_handler(conn,
13588
                                   mop,
13589
                                   (char *)inflated,
13590
                                   inflate_buf_size,
13591
                                   callback_data)) {
13592
                exit_by_callback = 1;
13593
              }
13594
              mg_free(inflated);
13595
            }
13596
          } else
13597
#endif
13598
              if (!ws_data_handler(conn,
13599
                                   mop,
13600
                                   (char *)data,
13601
                                   (size_t)data_len,
13602
                                   callback_data)) {
13603
            exit_by_callback = 1;
13604
          }
13605
        }
13606
      }
13607
13608
      /* It a buffer has been allocated, free it again */
13609
      if (data != mem) {
13610
        mg_free(data);
13611
      }
13612
13613
      if (exit_by_callback) {
13614
        DEBUG_TRACE("Callback requests to close connection from %s:%u",
13615
                    conn->request_info.remote_addr,
13616
                    conn->request_info.remote_port);
13617
        break;
13618
      }
13619
      if ((mop & 0xf) == MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE) {
13620
        /* Opcode == 8, connection close */
13621
        DEBUG_TRACE("Message requests to close connection from %s:%u",
13622
                    conn->request_info.remote_addr,
13623
                    conn->request_info.remote_port);
13624
        break;
13625
      }
13626
13627
      /* Not breaking the loop, process next websocket frame. */
13628
    } else {
13629
      /* Read from the socket into the next available location in the
13630
       * message queue. */
13631
      n = pull_inner(NULL,
13632
                     conn,
13633
                     conn->buf + conn->data_len,
13634
                     conn->buf_size - conn->data_len,
13635
                     timeout);
13636
      if (n <= -2) {
13637
        /* Error, no bytes read */
13638
        DEBUG_TRACE("PULL from %s:%u failed",
13639
                    conn->request_info.remote_addr,
13640
                    conn->request_info.remote_port);
13641
        break;
13642
      }
13643
      if (n > 0) {
13644
        conn->data_len += n;
13645
        /* Reset open PING count */
13646
        ping_count = 0;
13647
      } else {
13648
        if (STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)
13649
            && (!conn->must_close)) {
13650
          if (ping_count > MG_MAX_UNANSWERED_PING) {
13651
            /* Stop sending PING */
13652
            DEBUG_TRACE("Too many (%i) unanswered ping from %s:%u "
13653
                        "- closing connection",
13654
                        ping_count,
13655
                        conn->request_info.remote_addr,
13656
                        conn->request_info.remote_port);
13657
            break;
13658
          }
13659
          if (enable_ping_pong) {
13660
            /* Send Websocket PING message */
13661
            DEBUG_TRACE("PING to %s:%u",
13662
                        conn->request_info.remote_addr,
13663
                        conn->request_info.remote_port);
13664
            ret = mg_websocket_write(conn,
13665
                                     MG_WEBSOCKET_OPCODE_PING,
13666
                                     NULL,
13667
                                     0);
13668
13669
            if (ret <= 0) {
13670
              /* Error: send failed */
13671
              DEBUG_TRACE("Send PING failed (%i)", ret);
13672
              break;
13673
            }
13674
            ping_count++;
13675
          }
13676
        }
13677
        /* Timeout: should retry */
13678
        /* TODO: get timeout def */
13679
      }
13680
    }
13681
  }
13682
13683
  /* Leave data processing loop */
13684
  mg_set_thread_name("worker");
13685
  conn->in_websocket_handling = 0;
13686
  DEBUG_TRACE("Websocket connection %s:%u left data processing loop",
13687
              conn->request_info.remote_addr,
13688
              conn->request_info.remote_port);
13689
}
13690
13691
13692
static int
13693
mg_websocket_write_exec(struct mg_connection *conn,
13694
                        int opcode,
13695
                        const char *data,
13696
                        size_t dataLen,
13697
                        uint32_t masking_key)
13698
{
13699
  unsigned char header[14];
13700
  size_t headerLen;
13701
  int retval;
13702
13703
#if defined(GCC_DIAGNOSTIC)
13704
  /* Disable spurious conversion warning for GCC */
13705
#pragma GCC diagnostic push
13706
#pragma GCC diagnostic ignored "-Wconversion"
13707
#endif
13708
13709
  /* Note that POSIX/Winsock's send() is threadsafe
13710
   * http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid
13711
   * but mongoose's mg_printf/mg_write is not (because of the loop in
13712
   * push(), although that is only a problem if the packet is large or
13713
   * outgoing buffer is full). */
13714
13715
  /* TODO: Check if this lock should be moved to user land.
13716
   * Currently the server sets this lock for websockets, but
13717
   * not for any other connection. It must be set for every
13718
   * conn read/written by more than one thread, no matter if
13719
   * it is a websocket or regular connection. */
13720
  (void)mg_lock_connection(conn);
13721
13722
#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13723
  size_t deflated_size = 0;
13724
  Bytef *deflated = 0;
13725
  // Deflate websocket messages over 100kb
13726
  int use_deflate = dataLen > 100 * 1024 && conn->accept_gzip;
13727
13728
  if (use_deflate) {
13729
    if (!conn->websocket_deflate_initialized) {
13730
      if (websocket_deflate_initialize(conn, 1) != Z_OK)
13731
        return 0;
13732
    }
13733
13734
    // Deflating the message
13735
    header[0] = 0xC0u | (unsigned char)((unsigned)opcode & 0xf);
13736
    conn->websocket_deflate_state.avail_in = (uInt)dataLen;
13737
    conn->websocket_deflate_state.next_in = (unsigned char *)data;
13738
    deflated_size = (size_t)compressBound((uLong)dataLen);
13739
    deflated = mg_calloc(deflated_size, sizeof(Bytef));
13740
    if (deflated == NULL) {
13741
      mg_cry_internal(
13742
          conn,
13743
          "Out of memory: Cannot allocate deflate buffer of %lu bytes",
13744
          (unsigned long)deflated_size);
13745
      mg_unlock_connection(conn);
13746
      return -1;
13747
    }
13748
    conn->websocket_deflate_state.avail_out = (uInt)deflated_size;
13749
    conn->websocket_deflate_state.next_out = deflated;
13750
    deflate(&conn->websocket_deflate_state, conn->websocket_deflate_flush);
13751
    dataLen = deflated_size - conn->websocket_deflate_state.avail_out
13752
              - 4; // Strip trailing 0x00 0x00 0xff 0xff bytes
13753
  } else
13754
#endif
13755
    header[0] = 0x80u | (unsigned char)((unsigned)opcode & 0xf);
13756
13757
#if defined(GCC_DIAGNOSTIC)
13758
#pragma GCC diagnostic pop
13759
#endif
13760
13761
  /* Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 */
13762
  if (dataLen < 126) {
13763
    /* inline 7-bit length field */
13764
    header[1] = (unsigned char)dataLen;
13765
    headerLen = 2;
13766
  } else if (dataLen <= 0xFFFF) {
13767
    /* 16-bit length field */
13768
    uint16_t len = htons((uint16_t)dataLen);
13769
    header[1] = 126;
13770
    memcpy(header + 2, &len, 2);
13771
    headerLen = 4;
13772
  } else {
13773
    /* 64-bit length field */
13774
    uint32_t len1 = htonl((uint32_t)((uint64_t)dataLen >> 32));
13775
    uint32_t len2 = htonl((uint32_t)(dataLen & 0xFFFFFFFFu));
13776
    header[1] = 127;
13777
    memcpy(header + 2, &len1, 4);
13778
    memcpy(header + 6, &len2, 4);
13779
    headerLen = 10;
13780
  }
13781
13782
  if (masking_key) {
13783
    /* add mask */
13784
    header[1] |= 0x80;
13785
    memcpy(header + headerLen, &masking_key, 4);
13786
    headerLen += 4;
13787
  }
13788
13789
  retval = mg_write(conn, header, headerLen);
13790
  if (retval != (int)headerLen) {
13791
    /* Did not send complete header */
13792
    retval = -1;
13793
  } else {
13794
    if (dataLen > 0) {
13795
#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
13796
      if (use_deflate) {
13797
        retval = mg_write(conn, deflated, dataLen);
13798
        mg_free(deflated);
13799
      } else
13800
#endif
13801
        retval = mg_write(conn, data, dataLen);
13802
    }
13803
    /* if dataLen == 0, the header length (2) is returned */
13804
  }
13805
13806
  /* TODO: Remove this unlock as well, when lock is removed. */
13807
  mg_unlock_connection(conn);
13808
13809
  return retval;
13810
}
13811
13812
13813
CIVETWEB_API int
13814
mg_websocket_write(struct mg_connection *conn,
13815
                   int opcode,
13816
                   const char *data,
13817
                   size_t dataLen)
13818
{
13819
  return mg_websocket_write_exec(conn, opcode, data, dataLen, 0);
13820
}
13821
13822
13823
static void
13824
mask_data(const char *in, size_t in_len, uint32_t masking_key, char *out)
13825
{
13826
  size_t i = 0;
13827
13828
  i = 0;
13829
  if ((in_len > 3) && ((ptrdiff_t)in % 4) == 0) {
13830
    /* Convert in 32 bit words, if data is 4 byte aligned */
13831
    while (i < (in_len - 3)) {
13832
      *(uint32_t *)(void *)(out + i) =
13833
          *(uint32_t *)(void *)(in + i) ^ masking_key;
13834
      i += 4;
13835
    }
13836
  }
13837
  if (i != in_len) {
13838
    /* convert 1-3 remaining bytes if ((dataLen % 4) != 0)*/
13839
    while (i < in_len) {
13840
      *(uint8_t *)(void *)(out + i) =
13841
          *(uint8_t *)(void *)(in + i)
13842
          ^ *(((uint8_t *)&masking_key) + (i % 4));
13843
      i++;
13844
    }
13845
  }
13846
}
13847
13848
13849
CIVETWEB_API int
13850
mg_websocket_client_write(struct mg_connection *conn,
13851
                          int opcode,
13852
                          const char *data,
13853
                          size_t dataLen)
13854
{
13855
  int retval = -1;
13856
  char *masked_data =
13857
      (char *)mg_malloc_ctx(((dataLen + 7) / 4) * 4, conn->phys_ctx);
13858
  uint32_t masking_key = 0;
13859
13860
  if (masked_data == NULL) {
13861
    /* Return -1 in an error case */
13862
    mg_cry_internal(conn,
13863
                    "%s",
13864
                    "Cannot allocate buffer for masked websocket response: "
13865
                    "Out of memory");
13866
    return -1;
13867
  }
13868
13869
  do {
13870
    /* Get a masking key - but not 0 */
13871
    masking_key = (uint32_t)get_random();
13872
  } while (masking_key == 0);
13873
13874
  mask_data(data, dataLen, masking_key, masked_data);
13875
13876
  retval = mg_websocket_write_exec(
13877
      conn, opcode, masked_data, dataLen, masking_key);
13878
  mg_free(masked_data);
13879
13880
  return retval;
13881
}
13882
13883
13884
static void
13885
handle_websocket_request(struct mg_connection *conn,
13886
                         const char *path,
13887
                         int is_callback_resource,
13888
                         struct mg_websocket_subprotocols *subprotocols,
13889
                         mg_websocket_connect_handler ws_connect_handler,
13890
                         mg_websocket_ready_handler ws_ready_handler,
13891
                         mg_websocket_data_handler ws_data_handler,
13892
                         mg_websocket_close_handler ws_close_handler,
13893
                         void *cbData)
13894
{
13895
  const char *websock_key = mg_get_header(conn, "Sec-WebSocket-Key");
13896
  const char *version = mg_get_header(conn, "Sec-WebSocket-Version");
13897
  ptrdiff_t lua_websock = 0;
13898
13899
#if !defined(USE_LUA)
13900
  (void)path;
13901
#endif
13902
13903
  /* Step 1: Check websocket protocol version. */
13904
  /* Step 1.1: Check Sec-WebSocket-Key. */
13905
  if (!websock_key) {
13906
    /* The RFC standard version (https://tools.ietf.org/html/rfc6455)
13907
     * requires a Sec-WebSocket-Key header.
13908
     */
13909
    /* It could be the hixie draft version
13910
     * (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76).
13911
     */
13912
    const char *key1 = mg_get_header(conn, "Sec-WebSocket-Key1");
13913
    const char *key2 = mg_get_header(conn, "Sec-WebSocket-Key2");
13914
    char key3[8];
13915
13916
    if ((key1 != NULL) && (key2 != NULL)) {
13917
      /* This version uses 8 byte body data in a GET request */
13918
      conn->content_len = 8;
13919
      if (8 == mg_read(conn, key3, 8)) {
13920
        /* This is the hixie version */
13921
        mg_send_http_error(conn,
13922
                           426,
13923
                           "%s",
13924
                           "Protocol upgrade to RFC 6455 required");
13925
        return;
13926
      }
13927
    }
13928
    /* This is an unknown version */
13929
    mg_send_http_error(conn, 400, "%s", "Malformed websocket request");
13930
    return;
13931
  }
13932
13933
  /* Step 1.2: Check websocket protocol version. */
13934
  /* The RFC version (https://tools.ietf.org/html/rfc6455) is 13. */
13935
  if ((version == NULL) || (strcmp(version, "13") != 0)) {
13936
    /* Reject wrong versions */
13937
    mg_send_http_error(conn, 426, "%s", "Protocol upgrade required");
13938
    return;
13939
  }
13940
13941
  /* Step 1.3: Could check for "Host", but we do not really need this
13942
   * value for anything, so just ignore it. */
13943
13944
  /* Step 2: If a callback is responsible, call it. */
13945
  if (is_callback_resource) {
13946
    /* Step 2.1 check and select subprotocol */
13947
    const char *protocols[64]; // max 64 headers
13948
    int nbSubprotocolHeader = get_req_headers(&conn->request_info,
13949
                                              "Sec-WebSocket-Protocol",
13950
                                              protocols,
13951
                                              64);
13952
13953
    if ((nbSubprotocolHeader > 0) && subprotocols) {
13954
13955
      int headerNo, idx;
13956
      size_t len;
13957
      const char *sep, *curSubProtocol,
13958
          *acceptedWebSocketSubprotocol = NULL;
13959
13960
      /* look for matching subprotocol */
13961
      for (headerNo = 0; headerNo < nbSubprotocolHeader; headerNo++) {
13962
        /* There might be multiple headers ... */
13963
        const char *protocol = protocols[headerNo];
13964
        curSubProtocol = protocol;
13965
13966
        /* ... and in every header there might be a , separated list */
13967
        while (!acceptedWebSocketSubprotocol && (*curSubProtocol)) {
13968
13969
          while ((*curSubProtocol == ' ') || (*curSubProtocol == ','))
13970
            curSubProtocol++;
13971
          sep = strchr(curSubProtocol, ',');
13972
          if (sep) {
13973
            len = (size_t)(sep - curSubProtocol);
13974
          } else {
13975
            len = strlen(curSubProtocol);
13976
          }
13977
13978
          for (idx = 0; idx < subprotocols->nb_subprotocols; idx++) {
13979
            // COMPARE: curSubProtocol ==
13980
            // subprotocols->subprotocols[idx]
13981
            if ((strlen(subprotocols->subprotocols[idx]) == len)
13982
                && (strncmp(curSubProtocol,
13983
                            subprotocols->subprotocols[idx],
13984
                            len)
13985
                    == 0)) {
13986
              acceptedWebSocketSubprotocol =
13987
                  subprotocols->subprotocols[idx];
13988
              break;
13989
            }
13990
          }
13991
          curSubProtocol += len;
13992
        }
13993
      }
13994
13995
      conn->request_info.acceptedWebSocketSubprotocol =
13996
          acceptedWebSocketSubprotocol;
13997
    }
13998
13999
#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
14000
    websocket_deflate_negotiate(conn);
14001
#endif
14002
14003
    if ((ws_connect_handler != NULL)
14004
        && (ws_connect_handler(conn, cbData) != 0)) {
14005
      /* C callback has returned non-zero, do not proceed with
14006
       * handshake.
14007
       */
14008
      /* Note that C callbacks are no longer called when Lua is
14009
       * responsible, so C can no longer filter callbacks for Lua. */
14010
      return;
14011
    }
14012
  }
14013
14014
#if defined(USE_LUA)
14015
  /* Step 3: No callback. Check if Lua is responsible. */
14016
  else {
14017
    /* Step 3.1: Check if Lua is responsible. */
14018
    if (conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS]) {
14019
      lua_websock = match_prefix_strlen(
14020
          conn->dom_ctx->config[LUA_WEBSOCKET_EXTENSIONS], path);
14021
    }
14022
14023
    if (lua_websock > 0) {
14024
      /* Step 3.2: Lua is responsible: call it. */
14025
      conn->lua_websocket_state = lua_websocket_new(path, conn);
14026
      if (!conn->lua_websocket_state) {
14027
        /* Lua rejected the new client */
14028
        return;
14029
      }
14030
    }
14031
  }
14032
#endif
14033
14034
  /* Step 4: Check if there is a responsible websocket handler. */
14035
  if (!is_callback_resource && !lua_websock) {
14036
    /* There is no callback, and Lua is not responsible either. */
14037
    /* Reply with a 404 Not Found. We are still at a standard
14038
     * HTTP request here, before the websocket handshake, so
14039
     * we can still send standard HTTP error replies. */
14040
    mg_send_http_error(conn, 404, "%s", "Not found");
14041
    return;
14042
  }
14043
14044
  /* Step 5: The websocket connection has been accepted */
14045
  if (!send_websocket_handshake(conn, websock_key)) {
14046
    mg_send_http_error(conn, 500, "%s", "Websocket handshake failed");
14047
    return;
14048
  }
14049
14050
  /* Step 6: Call the ready handler */
14051
  if (is_callback_resource) {
14052
    if (ws_ready_handler != NULL) {
14053
      ws_ready_handler(conn, cbData);
14054
    }
14055
#if defined(USE_LUA)
14056
  } else if (lua_websock) {
14057
    if (!lua_websocket_ready(conn, conn->lua_websocket_state)) {
14058
      /* the ready handler returned false */
14059
      return;
14060
    }
14061
#endif
14062
  }
14063
14064
  /* Step 7: Enter the read loop */
14065
  if (is_callback_resource) {
14066
    read_websocket(conn, ws_data_handler, cbData);
14067
#if defined(USE_LUA)
14068
  } else if (lua_websock) {
14069
    read_websocket(conn, lua_websocket_data, conn->lua_websocket_state);
14070
#endif
14071
  }
14072
14073
#if defined(USE_ZLIB) && defined(MG_EXPERIMENTAL_INTERFACES)
14074
  /* Step 8: Close the deflate & inflate buffers */
14075
  if (conn->websocket_deflate_initialized) {
14076
    deflateEnd(&conn->websocket_deflate_state);
14077
    inflateEnd(&conn->websocket_inflate_state);
14078
  }
14079
#endif
14080
14081
  /* Step 9: Call the close handler */
14082
  if (ws_close_handler) {
14083
    ws_close_handler(conn, cbData);
14084
  }
14085
}
14086
#endif /* !USE_WEBSOCKET */
14087
14088
14089
/* Is upgrade request:
14090
 *   0 = regular HTTP/1.0 or HTTP/1.1 request
14091
 *   1 = upgrade to websocket
14092
 *   2 = upgrade to HTTP/2
14093
 * -1 = upgrade to unknown protocol
14094
 */
14095
static int
14096
should_switch_to_protocol(const struct mg_connection *conn)
14097
0
{
14098
0
  const char *connection_headers[8];
14099
0
  const char *upgrade_to;
14100
0
  int connection_header_count, i, should_upgrade;
14101
14102
  /* A websocket protocol has the following HTTP headers:
14103
   *
14104
   * Connection: Upgrade
14105
   * Upgrade: Websocket
14106
   *
14107
   * It seems some clients use multiple headers:
14108
   * see https://github.com/civetweb/civetweb/issues/1083
14109
   */
14110
0
  connection_header_count = get_req_headers(&conn->request_info,
14111
0
                                            "Connection",
14112
0
                                            connection_headers,
14113
0
                                            8);
14114
0
  should_upgrade = 0;
14115
0
  for (i = 0; i < connection_header_count; i++) {
14116
0
    if (0 != mg_strcasestr(connection_headers[i], "upgrade")) {
14117
0
      should_upgrade = 1;
14118
0
    }
14119
0
  }
14120
0
  if (!should_upgrade) {
14121
0
    return PROTOCOL_TYPE_HTTP1;
14122
0
  }
14123
14124
0
  upgrade_to = mg_get_header(conn, "Upgrade");
14125
0
  if (upgrade_to == NULL) {
14126
    /* "Connection: Upgrade" without "Upgrade" Header --> Error */
14127
0
    return -1;
14128
0
  }
14129
14130
  /* Upgrade to ... */
14131
0
  if (0 != mg_strcasestr(upgrade_to, "websocket")) {
14132
    /* The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and
14133
     * "Sec-WebSocket-Version" are also required.
14134
     * Don't check them here, since even an unsupported websocket protocol
14135
     * request still IS a websocket request (in contrast to a standard HTTP
14136
     * request). It will fail later in handle_websocket_request.
14137
     */
14138
0
    return PROTOCOL_TYPE_WEBSOCKET; /* Websocket */
14139
0
  }
14140
0
  if (0 != mg_strcasestr(upgrade_to, "h2")) {
14141
0
    return PROTOCOL_TYPE_HTTP2; /* Websocket */
14142
0
  }
14143
14144
  /* Upgrade to another protocol */
14145
0
  return -1;
14146
0
}
14147
14148
14149
static int
14150
parse_match_net(const struct vec *vec, const union usa *sa, int no_strict)
14151
0
{
14152
0
  int n;
14153
0
  unsigned int a, b, c, d, slash;
14154
14155
0
  if (sscanf(vec->ptr, "%u.%u.%u.%u/%u%n", &a, &b, &c, &d, &slash, &n)
14156
0
      != 5) { // NOLINT(cert-err34-c) 'sscanf' used to convert a string to an
14157
            // integer value, but function will not report conversion
14158
            // errors; consider using 'strtol' instead
14159
0
    slash = 32;
14160
0
    if (sscanf(vec->ptr, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n)
14161
0
        != 4) { // NOLINT(cert-err34-c) 'sscanf' used to convert a string to
14162
              // an integer value, but function will not report conversion
14163
              // errors; consider using 'strtol' instead
14164
0
      n = 0;
14165
0
    }
14166
0
  }
14167
14168
0
  if ((n > 0) && ((size_t)n == vec->len)) {
14169
0
    if ((a < 256) && (b < 256) && (c < 256) && (d < 256) && (slash < 33)) {
14170
      /* IPv4 format */
14171
0
      if (sa->sa.sa_family == AF_INET) {
14172
0
        uint32_t ip = ntohl(sa->sin.sin_addr.s_addr);
14173
0
        uint32_t net = ((uint32_t)a << 24) | ((uint32_t)b << 16)
14174
0
                       | ((uint32_t)c << 8) | (uint32_t)d;
14175
0
        uint32_t mask = slash ? (0xFFFFFFFFu << (32 - slash)) : 0;
14176
0
        return (ip & mask) == net;
14177
0
      }
14178
0
      return 0;
14179
0
    }
14180
0
  }
14181
#if defined(USE_IPV6)
14182
  else {
14183
    char ad[50];
14184
    const char *p;
14185
14186
    if (sscanf(vec->ptr, "[%49[^]]]/%u%n", ad, &slash, &n) != 2) {
14187
      slash = 128;
14188
      if (sscanf(vec->ptr, "[%49[^]]]%n", ad, &n) != 1) {
14189
        n = 0;
14190
      }
14191
    }
14192
14193
    if ((n <= 0) && no_strict) {
14194
      /* no square brackets? */
14195
      p = strchr(vec->ptr, '/');
14196
      if (p && (p < (vec->ptr + vec->len))) {
14197
        if (((size_t)(p - vec->ptr) < sizeof(ad))
14198
            && (sscanf(p, "/%u%n", &slash, &n) == 1)) {
14199
          n += (int)(p - vec->ptr);
14200
          mg_strlcpy(ad, vec->ptr, (size_t)(p - vec->ptr) + 1);
14201
        } else {
14202
          n = 0;
14203
        }
14204
      } else if (vec->len < sizeof(ad)) {
14205
        n = (int)vec->len;
14206
        slash = 128;
14207
        mg_strlcpy(ad, vec->ptr, vec->len + 1);
14208
      }
14209
    }
14210
14211
    if ((n > 0) && ((size_t)n == vec->len) && (slash < 129)) {
14212
      p = ad;
14213
      c = 0;
14214
      /* zone indexes are unsupported, at least two colons are needed */
14215
      while (isxdigit((unsigned char)*p) || (*p == '.') || (*p == ':')) {
14216
        if (*(p++) == ':') {
14217
          c++;
14218
        }
14219
      }
14220
      if ((*p == '\0') && (c >= 2)) {
14221
        struct sockaddr_in6 sin6;
14222
        unsigned int i;
14223
14224
        /* for strict validation, an actual IPv6 argument is needed */
14225
        if (sa->sa.sa_family != AF_INET6) {
14226
          return 0;
14227
        }
14228
        if (mg_inet_pton(AF_INET6, ad, &sin6, sizeof(sin6), 0)) {
14229
          /* IPv6 format */
14230
          for (i = 0; i < 16; i++) {
14231
            uint8_t ip = sa->sin6.sin6_addr.s6_addr[i];
14232
            uint8_t net = sin6.sin6_addr.s6_addr[i];
14233
            uint8_t mask = 0;
14234
14235
            if (8 * i + 8 < slash) {
14236
              mask = 0xFFu;
14237
            } else if (8 * i < slash) {
14238
              mask = (uint8_t)(0xFFu << (8 * i + 8 - slash));
14239
            }
14240
            if ((ip & mask) != net) {
14241
              return 0;
14242
            }
14243
          }
14244
          return 1;
14245
        }
14246
      }
14247
    }
14248
  }
14249
#else
14250
0
  (void)no_strict;
14251
0
#endif
14252
14253
  /* malformed */
14254
0
  return -1;
14255
0
}
14256
14257
14258
static int
14259
set_throttle(const char *spec, const union usa *rsa, const char *uri)
14260
0
{
14261
0
  int throttle = 0;
14262
0
  struct vec vec, val;
14263
0
  char mult;
14264
0
  double v;
14265
14266
0
  while ((spec = next_option(spec, &vec, &val)) != NULL) {
14267
0
    mult = ',';
14268
0
    if ((val.ptr == NULL)
14269
0
        || (sscanf(val.ptr, "%lf%c", &v, &mult)
14270
0
            < 1) // NOLINT(cert-err34-c) 'sscanf' used to convert a string
14271
                 // to an integer value, but function will not report
14272
                 // conversion errors; consider using 'strtol' instead
14273
0
        || (v < 0)
14274
0
        || ((lowercase(&mult) != 'k') && (lowercase(&mult) != 'm')
14275
0
            && (mult != ','))) {
14276
0
      continue;
14277
0
    }
14278
0
    v *= (lowercase(&mult) == 'k')
14279
0
             ? 1024
14280
0
             : ((lowercase(&mult) == 'm') ? 1048576 : 1);
14281
0
    if (vec.len == 1 && vec.ptr[0] == '*') {
14282
0
      throttle = (int)v;
14283
0
    } else {
14284
0
      int matched = parse_match_net(&vec, rsa, 0);
14285
0
      if (matched >= 0) {
14286
        /* a valid IP subnet */
14287
0
        if (matched) {
14288
0
          throttle = (int)v;
14289
0
        }
14290
0
      } else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
14291
0
        throttle = (int)v;
14292
0
      }
14293
0
    }
14294
0
  }
14295
14296
0
  return throttle;
14297
0
}
14298
14299
14300
/* The mg_upload function is superseded by mg_handle_form_request. */
14301
#include "handle_form.inl"
14302
14303
14304
static int
14305
get_first_ssl_listener_index(const struct mg_context *ctx)
14306
0
{
14307
0
  unsigned int i;
14308
0
  int idx = -1;
14309
0
  if (ctx) {
14310
0
    for (i = 0; ((idx == -1) && (i < ctx->num_listening_sockets)); i++) {
14311
0
      idx = ctx->listening_sockets[i].is_ssl ? ((int)(i)) : -1;
14312
0
    }
14313
0
  }
14314
0
  return idx;
14315
0
}
14316
14317
14318
/* Return host (without port) */
14319
static void
14320
get_host_from_request_info(struct vec *host, const struct mg_request_info *ri)
14321
0
{
14322
0
  const char *host_header =
14323
0
      get_header(ri->http_headers, ri->num_headers, "Host");
14324
14325
0
  host->ptr = NULL;
14326
0
  host->len = 0;
14327
14328
0
  if (host_header != NULL) {
14329
0
    const char *pos;
14330
14331
    /* If the "Host" is an IPv6 address, like [::1], parse until ]
14332
     * is found. */
14333
0
    if (*host_header == '[') {
14334
0
      pos = strchr(host_header, ']');
14335
0
      if (!pos) {
14336
        /* Malformed hostname starts with '[', but no ']' found */
14337
0
        DEBUG_TRACE("%s", "Host name format error '[' without ']'");
14338
0
        return;
14339
0
      }
14340
      /* terminate after ']' */
14341
0
      host->ptr = host_header;
14342
0
      host->len = (size_t)(pos + 1 - host_header);
14343
0
    } else {
14344
      /* Otherwise, a ':' separates hostname and port number */
14345
0
      pos = strchr(host_header, ':');
14346
0
      if (pos != NULL) {
14347
0
        host->len = (size_t)(pos - host_header);
14348
0
      } else {
14349
0
        host->len = strlen(host_header);
14350
0
      }
14351
0
      host->ptr = host_header;
14352
0
    }
14353
0
  }
14354
0
}
14355
14356
14357
static int
14358
switch_domain_context(struct mg_connection *conn)
14359
0
{
14360
0
  struct vec host;
14361
14362
0
  get_host_from_request_info(&host, &conn->request_info);
14363
14364
0
  if (host.ptr) {
14365
0
    if (conn->ssl) {
14366
      /* This is a HTTPS connection, maybe we have a hostname
14367
       * from SNI (set in ssl_servername_callback). */
14368
0
      const char *sslhost = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
14369
0
      if (sslhost && (conn->dom_ctx != &(conn->phys_ctx->dd))) {
14370
        /* We are not using the default domain */
14371
0
        if ((strlen(sslhost) != host.len)
14372
0
            || mg_strncasecmp(host.ptr, sslhost, host.len)) {
14373
          /* Mismatch between SNI domain and HTTP domain */
14374
0
          DEBUG_TRACE("Host mismatch: SNI: %s, HTTPS: %.*s",
14375
0
                      sslhost,
14376
0
                      (int)host.len,
14377
0
                      host.ptr);
14378
0
          return 0;
14379
0
        }
14380
0
      }
14381
14382
0
    } else {
14383
0
      struct mg_domain_context *dom = &(conn->phys_ctx->dd);
14384
0
      while (dom) {
14385
0
        const char *domName = dom->config[AUTHENTICATION_DOMAIN];
14386
0
        size_t domNameLen = strlen(domName);
14387
0
        if ((domNameLen == host.len)
14388
0
            && !mg_strncasecmp(host.ptr, domName, host.len)) {
14389
14390
          /* Found matching domain */
14391
0
          DEBUG_TRACE("HTTP domain %s found",
14392
0
                      dom->config[AUTHENTICATION_DOMAIN]);
14393
14394
          /* TODO: Check if this is a HTTP or HTTPS domain */
14395
0
          conn->dom_ctx = dom;
14396
0
          break;
14397
0
        }
14398
0
        mg_lock_context(conn->phys_ctx);
14399
0
        dom = dom->next;
14400
0
        mg_unlock_context(conn->phys_ctx);
14401
0
      }
14402
0
    }
14403
14404
0
    DEBUG_TRACE("HTTP%s Host: %.*s",
14405
0
                conn->ssl ? "S" : "",
14406
0
                (int)host.len,
14407
0
                host.ptr);
14408
14409
0
  } else {
14410
0
    DEBUG_TRACE("HTTP%s Host is not set", conn->ssl ? "S" : "");
14411
0
    return 1;
14412
0
  }
14413
14414
0
  return 1;
14415
0
}
14416
14417
14418
static void
14419
redirect_to_https_port(struct mg_connection *conn, int port)
14420
0
{
14421
0
  char target_url[MG_BUF_LEN];
14422
0
  int truncated = 0;
14423
0
  const char *expect_proto =
14424
0
      (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET) ? "wss" : "https";
14425
14426
  /* Use "308 Permanent Redirect" */
14427
0
  int redirect_code = 308;
14428
14429
  /* In any case, close the current connection */
14430
0
  conn->must_close = 1;
14431
14432
  /* Send host, port, uri and (if it exists) ?query_string */
14433
0
  if (mg_construct_local_link(
14434
0
          conn, target_url, sizeof(target_url), expect_proto, port, NULL)
14435
0
      < 0) {
14436
0
    truncated = 1;
14437
0
  } else if (conn->request_info.query_string != NULL) {
14438
0
    size_t slen1 = strlen(target_url);
14439
0
    size_t slen2 = strlen(conn->request_info.query_string);
14440
0
    if ((slen1 + slen2 + 2) < sizeof(target_url)) {
14441
0
      target_url[slen1] = '?';
14442
0
      memcpy(target_url + slen1 + 1,
14443
0
             conn->request_info.query_string,
14444
0
             slen2);
14445
0
      target_url[slen1 + slen2 + 1] = 0;
14446
0
    } else {
14447
0
      truncated = 1;
14448
0
    }
14449
0
  }
14450
14451
  /* Check overflow in location buffer (will not occur if MG_BUF_LEN
14452
   * is used as buffer size) */
14453
0
  if (truncated) {
14454
0
    mg_send_http_error(conn, 500, "%s", "Redirect URL too long");
14455
0
    return;
14456
0
  }
14457
14458
  /* Use redirect helper function */
14459
0
  mg_send_http_redirect(conn, target_url, redirect_code);
14460
0
}
14461
14462
14463
static void
14464
mg_set_handler_type(struct mg_context *phys_ctx,
14465
                    struct mg_domain_context *dom_ctx,
14466
                    const char *uri,
14467
                    int handler_type,
14468
                    int is_delete_request,
14469
                    mg_request_handler handler,
14470
                    struct mg_websocket_subprotocols *subprotocols,
14471
                    mg_websocket_connect_handler connect_handler,
14472
                    mg_websocket_ready_handler ready_handler,
14473
                    mg_websocket_data_handler data_handler,
14474
                    mg_websocket_close_handler close_handler,
14475
                    mg_authorization_handler auth_handler,
14476
                    void *cbdata)
14477
0
{
14478
0
  struct mg_handler_info *tmp_rh, **lastref;
14479
0
  size_t urilen = strlen(uri);
14480
14481
0
  if (handler_type == WEBSOCKET_HANDLER) {
14482
0
    DEBUG_ASSERT(handler == NULL);
14483
0
    DEBUG_ASSERT(is_delete_request || connect_handler != NULL
14484
0
                 || ready_handler != NULL || data_handler != NULL
14485
0
                 || close_handler != NULL);
14486
14487
0
    DEBUG_ASSERT(auth_handler == NULL);
14488
0
    if (handler != NULL) {
14489
0
      return;
14490
0
    }
14491
0
    if (!is_delete_request && (connect_handler == NULL)
14492
0
        && (ready_handler == NULL) && (data_handler == NULL)
14493
0
        && (close_handler == NULL)) {
14494
0
      return;
14495
0
    }
14496
0
    if (auth_handler != NULL) {
14497
0
      return;
14498
0
    }
14499
14500
0
  } else if (handler_type == REQUEST_HANDLER) {
14501
0
    DEBUG_ASSERT(connect_handler == NULL && ready_handler == NULL
14502
0
                 && data_handler == NULL && close_handler == NULL);
14503
0
    DEBUG_ASSERT(is_delete_request || (handler != NULL));
14504
0
    DEBUG_ASSERT(auth_handler == NULL);
14505
14506
0
    if ((connect_handler != NULL) || (ready_handler != NULL)
14507
0
        || (data_handler != NULL) || (close_handler != NULL)) {
14508
0
      return;
14509
0
    }
14510
0
    if (!is_delete_request && (handler == NULL)) {
14511
0
      return;
14512
0
    }
14513
0
    if (auth_handler != NULL) {
14514
0
      return;
14515
0
    }
14516
14517
0
  } else if (handler_type == AUTH_HANDLER) {
14518
0
    DEBUG_ASSERT(handler == NULL);
14519
0
    DEBUG_ASSERT(connect_handler == NULL && ready_handler == NULL
14520
0
                 && data_handler == NULL && close_handler == NULL);
14521
0
    DEBUG_ASSERT(is_delete_request || (auth_handler != NULL));
14522
0
    if (handler != NULL) {
14523
0
      return;
14524
0
    }
14525
0
    if ((connect_handler != NULL) || (ready_handler != NULL)
14526
0
        || (data_handler != NULL) || (close_handler != NULL)) {
14527
0
      return;
14528
0
    }
14529
0
    if (!is_delete_request && (auth_handler == NULL)) {
14530
0
      return;
14531
0
    }
14532
0
  } else {
14533
    /* Unknown handler type. */
14534
0
    return;
14535
0
  }
14536
14537
0
  if (!phys_ctx || !dom_ctx) {
14538
    /* no context available */
14539
0
    return;
14540
0
  }
14541
14542
0
  mg_lock_context(phys_ctx);
14543
14544
  /* first try to find an existing handler */
14545
0
  do {
14546
0
    lastref = &(dom_ctx->handlers);
14547
0
    for (tmp_rh = dom_ctx->handlers; tmp_rh != NULL;
14548
0
         tmp_rh = tmp_rh->next) {
14549
0
      if (tmp_rh->handler_type == handler_type
14550
0
          && (urilen == tmp_rh->uri_len) && !strcmp(tmp_rh->uri, uri)) {
14551
0
        if (!is_delete_request) {
14552
          /* update existing handler */
14553
0
          if (handler_type == REQUEST_HANDLER) {
14554
            /* Wait for end of use before updating */
14555
0
            if (tmp_rh->refcount) {
14556
0
              mg_unlock_context(phys_ctx);
14557
0
              mg_sleep(1);
14558
0
              mg_lock_context(phys_ctx);
14559
              /* tmp_rh might have been freed, search again. */
14560
0
              break;
14561
0
            }
14562
            /* Ok, the handler is no more use -> Update it */
14563
0
            tmp_rh->handler = handler;
14564
0
          } else if (handler_type == WEBSOCKET_HANDLER) {
14565
0
            tmp_rh->subprotocols = subprotocols;
14566
0
            tmp_rh->connect_handler = connect_handler;
14567
0
            tmp_rh->ready_handler = ready_handler;
14568
0
            tmp_rh->data_handler = data_handler;
14569
0
            tmp_rh->close_handler = close_handler;
14570
0
          } else { /* AUTH_HANDLER */
14571
0
            tmp_rh->auth_handler = auth_handler;
14572
0
          }
14573
0
          tmp_rh->cbdata = cbdata;
14574
0
        } else {
14575
          /* remove existing handler */
14576
0
          if (handler_type == REQUEST_HANDLER) {
14577
            /* Wait for end of use before removing */
14578
0
            if (tmp_rh->refcount) {
14579
0
              tmp_rh->removing = 1;
14580
0
              mg_unlock_context(phys_ctx);
14581
0
              mg_sleep(1);
14582
0
              mg_lock_context(phys_ctx);
14583
              /* tmp_rh might have been freed, search again. */
14584
0
              break;
14585
0
            }
14586
            /* Ok, the handler is no more used */
14587
0
          }
14588
0
          *lastref = tmp_rh->next;
14589
0
          mg_free(tmp_rh->uri);
14590
0
          mg_free(tmp_rh);
14591
0
        }
14592
0
        mg_unlock_context(phys_ctx);
14593
0
        return;
14594
0
      }
14595
0
      lastref = &(tmp_rh->next);
14596
0
    }
14597
0
  } while (tmp_rh != NULL);
14598
14599
0
  if (is_delete_request) {
14600
    /* no handler to set, this was a remove request to a non-existing
14601
     * handler */
14602
0
    mg_unlock_context(phys_ctx);
14603
0
    return;
14604
0
  }
14605
14606
0
  tmp_rh =
14607
0
      (struct mg_handler_info *)mg_calloc_ctx(1,
14608
0
                                              sizeof(struct mg_handler_info),
14609
0
                                              phys_ctx);
14610
0
  if (tmp_rh == NULL) {
14611
0
    mg_unlock_context(phys_ctx);
14612
0
    mg_cry_ctx_internal(phys_ctx,
14613
0
                        "%s",
14614
0
                        "Cannot create new request handler struct, OOM");
14615
0
    return;
14616
0
  }
14617
0
  tmp_rh->uri = mg_strdup_ctx(uri, phys_ctx);
14618
0
  if (!tmp_rh->uri) {
14619
0
    mg_unlock_context(phys_ctx);
14620
0
    mg_free(tmp_rh);
14621
0
    mg_cry_ctx_internal(phys_ctx,
14622
0
                        "%s",
14623
0
                        "Cannot create new request handler struct, OOM");
14624
0
    return;
14625
0
  }
14626
0
  tmp_rh->uri_len = urilen;
14627
0
  if (handler_type == REQUEST_HANDLER) {
14628
0
    tmp_rh->refcount = 0;
14629
0
    tmp_rh->removing = 0;
14630
0
    tmp_rh->handler = handler;
14631
0
  } else if (handler_type == WEBSOCKET_HANDLER) {
14632
0
    tmp_rh->subprotocols = subprotocols;
14633
0
    tmp_rh->connect_handler = connect_handler;
14634
0
    tmp_rh->ready_handler = ready_handler;
14635
0
    tmp_rh->data_handler = data_handler;
14636
0
    tmp_rh->close_handler = close_handler;
14637
0
  } else { /* AUTH_HANDLER */
14638
0
    tmp_rh->auth_handler = auth_handler;
14639
0
  }
14640
0
  tmp_rh->cbdata = cbdata;
14641
0
  tmp_rh->handler_type = handler_type;
14642
0
  tmp_rh->next = NULL;
14643
14644
0
  *lastref = tmp_rh;
14645
0
  mg_unlock_context(phys_ctx);
14646
0
}
14647
14648
14649
CIVETWEB_API void
14650
mg_set_request_handler(struct mg_context *ctx,
14651
                       const char *uri,
14652
                       mg_request_handler handler,
14653
                       void *cbdata)
14654
0
{
14655
0
  mg_set_handler_type(ctx,
14656
0
                      &(ctx->dd),
14657
0
                      uri,
14658
0
                      REQUEST_HANDLER,
14659
0
                      handler == NULL,
14660
0
                      handler,
14661
0
                      NULL,
14662
0
                      NULL,
14663
0
                      NULL,
14664
0
                      NULL,
14665
0
                      NULL,
14666
0
                      NULL,
14667
0
                      cbdata);
14668
0
}
14669
14670
14671
CIVETWEB_API void
14672
mg_set_websocket_handler(struct mg_context *ctx,
14673
                         const char *uri,
14674
                         mg_websocket_connect_handler connect_handler,
14675
                         mg_websocket_ready_handler ready_handler,
14676
                         mg_websocket_data_handler data_handler,
14677
                         mg_websocket_close_handler close_handler,
14678
                         void *cbdata)
14679
0
{
14680
0
  mg_set_websocket_handler_with_subprotocols(ctx,
14681
0
                                             uri,
14682
0
                                             NULL,
14683
0
                                             connect_handler,
14684
0
                                             ready_handler,
14685
0
                                             data_handler,
14686
0
                                             close_handler,
14687
0
                                             cbdata);
14688
0
}
14689
14690
14691
CIVETWEB_API void
14692
mg_set_websocket_handler_with_subprotocols(
14693
    struct mg_context *ctx,
14694
    const char *uri,
14695
    struct mg_websocket_subprotocols *subprotocols,
14696
    mg_websocket_connect_handler connect_handler,
14697
    mg_websocket_ready_handler ready_handler,
14698
    mg_websocket_data_handler data_handler,
14699
    mg_websocket_close_handler close_handler,
14700
    void *cbdata)
14701
0
{
14702
0
  int is_delete_request = (connect_handler == NULL) && (ready_handler == NULL)
14703
0
                          && (data_handler == NULL)
14704
0
                          && (close_handler == NULL);
14705
0
  mg_set_handler_type(ctx,
14706
0
                      &(ctx->dd),
14707
0
                      uri,
14708
0
                      WEBSOCKET_HANDLER,
14709
0
                      is_delete_request,
14710
0
                      NULL,
14711
0
                      subprotocols,
14712
0
                      connect_handler,
14713
0
                      ready_handler,
14714
0
                      data_handler,
14715
0
                      close_handler,
14716
0
                      NULL,
14717
0
                      cbdata);
14718
0
}
14719
14720
14721
CIVETWEB_API void
14722
mg_set_auth_handler(struct mg_context *ctx,
14723
                    const char *uri,
14724
                    mg_authorization_handler handler,
14725
                    void *cbdata)
14726
0
{
14727
0
  mg_set_handler_type(ctx,
14728
0
                      &(ctx->dd),
14729
0
                      uri,
14730
0
                      AUTH_HANDLER,
14731
0
                      handler == NULL,
14732
0
                      NULL,
14733
0
                      NULL,
14734
0
                      NULL,
14735
0
                      NULL,
14736
0
                      NULL,
14737
0
                      NULL,
14738
0
                      handler,
14739
0
                      cbdata);
14740
0
}
14741
14742
14743
static int
14744
get_request_handler(struct mg_connection *conn,
14745
                    int handler_type,
14746
                    mg_request_handler *handler,
14747
                    struct mg_websocket_subprotocols **subprotocols,
14748
                    mg_websocket_connect_handler *connect_handler,
14749
                    mg_websocket_ready_handler *ready_handler,
14750
                    mg_websocket_data_handler *data_handler,
14751
                    mg_websocket_close_handler *close_handler,
14752
                    mg_authorization_handler *auth_handler,
14753
                    void **cbdata,
14754
                    struct mg_handler_info **handler_info)
14755
0
{
14756
0
  const struct mg_request_info *request_info = mg_get_request_info(conn);
14757
0
  if (request_info) {
14758
0
    const char *uri = request_info->local_uri;
14759
0
    size_t urilen = strlen(uri);
14760
0
    struct mg_handler_info *tmp_rh;
14761
0
    int step, matched;
14762
14763
0
    if (!conn || !conn->phys_ctx || !conn->dom_ctx) {
14764
0
      return 0;
14765
0
    }
14766
14767
0
    mg_lock_context(conn->phys_ctx);
14768
14769
0
    for (step = 0; step < 3; step++) {
14770
0
      for (tmp_rh = conn->dom_ctx->handlers; tmp_rh != NULL;
14771
0
           tmp_rh = tmp_rh->next) {
14772
0
        if (tmp_rh->handler_type != handler_type) {
14773
0
          continue;
14774
0
        }
14775
0
        if (step == 0) {
14776
          /* first try for an exact match */
14777
0
          matched = (tmp_rh->uri_len == urilen)
14778
0
                    && (strcmp(tmp_rh->uri, uri) == 0);
14779
0
        } else if (step == 1) {
14780
          /* next try for a partial match, we will accept
14781
          uri/something */
14782
0
          matched =
14783
0
              (tmp_rh->uri_len < urilen)
14784
0
              && (uri[tmp_rh->uri_len] == '/')
14785
0
              && (memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0);
14786
0
        } else {
14787
          /* finally try for pattern match */
14788
0
          matched =
14789
0
              match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0;
14790
0
        }
14791
0
        if (matched) {
14792
0
          if (handler_type == WEBSOCKET_HANDLER) {
14793
0
            *subprotocols = tmp_rh->subprotocols;
14794
0
            *connect_handler = tmp_rh->connect_handler;
14795
0
            *ready_handler = tmp_rh->ready_handler;
14796
0
            *data_handler = tmp_rh->data_handler;
14797
0
            *close_handler = tmp_rh->close_handler;
14798
0
          } else if (handler_type == REQUEST_HANDLER) {
14799
0
            if (tmp_rh->removing) {
14800
              /* Treat as none found */
14801
0
              step = 2;
14802
0
              break;
14803
0
            }
14804
0
            *handler = tmp_rh->handler;
14805
            /* Acquire handler and give it back */
14806
0
            tmp_rh->refcount++;
14807
0
            *handler_info = tmp_rh;
14808
0
          } else { /* AUTH_HANDLER */
14809
0
            *auth_handler = tmp_rh->auth_handler;
14810
0
          }
14811
0
          *cbdata = tmp_rh->cbdata;
14812
0
          mg_unlock_context(conn->phys_ctx);
14813
0
          return 1;
14814
0
        }
14815
0
      }
14816
0
    }
14817
14818
0
    mg_unlock_context(conn->phys_ctx);
14819
0
  }
14820
0
  return 0; /* none found */
14821
0
}
14822
14823
14824
/* Check if the script file is in a path, allowed for script files.
14825
 * This can be used if uploading files is possible not only for the server
14826
 * admin, and the upload mechanism does not check the file extension.
14827
 */
14828
static int
14829
is_in_script_path(const struct mg_connection *conn, const char *path)
14830
0
{
14831
  /* TODO (Feature): Add config value for allowed script path.
14832
   * Default: All allowed. */
14833
0
  (void)conn;
14834
0
  (void)path;
14835
0
  return 1;
14836
0
}
14837
14838
14839
#if defined(USE_WEBSOCKET) && defined(MG_EXPERIMENTAL_INTERFACES)
14840
static int
14841
experimental_websocket_client_data_wrapper(struct mg_connection *conn,
14842
                                           int bits,
14843
                                           char *data,
14844
                                           size_t len,
14845
                                           void *cbdata)
14846
{
14847
  struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
14848
  if (pcallbacks->websocket_data) {
14849
    return pcallbacks->websocket_data(conn, bits, data, len);
14850
  }
14851
  /* No handler set - assume "OK" */
14852
  return 1;
14853
}
14854
14855
14856
static void
14857
experimental_websocket_client_close_wrapper(const struct mg_connection *conn,
14858
                                            void *cbdata)
14859
{
14860
  struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata;
14861
  if (pcallbacks->connection_close) {
14862
    pcallbacks->connection_close(conn);
14863
  }
14864
}
14865
#endif
14866
14867
14868
/* Decrement recount of handler. conn must not be NULL, handler_info may be NULL
14869
 */
14870
static void
14871
release_handler_ref(struct mg_connection *conn,
14872
                    struct mg_handler_info *handler_info)
14873
0
{
14874
0
  if (handler_info != NULL) {
14875
    /* Use context lock for ref counter */
14876
0
    mg_lock_context(conn->phys_ctx);
14877
0
    handler_info->refcount--;
14878
0
    mg_unlock_context(conn->phys_ctx);
14879
0
  }
14880
0
}
14881
14882
14883
/* This is the heart of the Civetweb's logic.
14884
 * This function is called when the request is read, parsed and validated,
14885
 * and Civetweb must decide what action to take: serve a file, or
14886
 * a directory, or call embedded function, etcetera. */
14887
static void
14888
handle_request(struct mg_connection *conn)
14889
0
{
14890
0
  struct mg_request_info *ri = &conn->request_info;
14891
0
  char path[UTF8_PATH_MAX];
14892
0
  int uri_len, ssl_index;
14893
0
  int is_found = 0, is_script_resource = 0, is_websocket_request = 0,
14894
0
      is_put_or_delete_request = 0, is_callback_resource = 0,
14895
0
      is_template_text_file = 0, is_webdav_request = 0;
14896
0
  int i;
14897
0
  struct mg_file file = STRUCT_FILE_INITIALIZER;
14898
0
  mg_request_handler callback_handler = NULL;
14899
0
  struct mg_handler_info *handler_info = NULL;
14900
0
  struct mg_websocket_subprotocols *subprotocols;
14901
0
  mg_websocket_connect_handler ws_connect_handler = NULL;
14902
0
  mg_websocket_ready_handler ws_ready_handler = NULL;
14903
0
  mg_websocket_data_handler ws_data_handler = NULL;
14904
0
  mg_websocket_close_handler ws_close_handler = NULL;
14905
0
  void *callback_data = NULL;
14906
0
  mg_authorization_handler auth_handler = NULL;
14907
0
  void *auth_callback_data = NULL;
14908
0
  int handler_type;
14909
0
  time_t curtime = time(NULL);
14910
0
  char date[64];
14911
0
  char *tmp;
14912
14913
0
  path[0] = 0;
14914
14915
  /* 0. Reset internal state (required for HTTP/2 proxy) */
14916
0
  conn->request_state = 0;
14917
14918
  /* 1. get the request url */
14919
  /* 1.1. split into url and query string */
14920
0
  if ((conn->request_info.query_string = strchr(ri->request_uri, '?'))
14921
0
      != NULL) {
14922
0
    *((char *)conn->request_info.query_string++) = '\0';
14923
0
  }
14924
14925
  /* 1.2. do a https redirect, if required. Do not decode URIs yet. */
14926
0
  if (!conn->client.is_ssl && conn->client.ssl_redir) {
14927
0
    ssl_index = get_first_ssl_listener_index(conn->phys_ctx);
14928
0
    if (ssl_index >= 0) {
14929
0
      int port = (int)ntohs(USA_IN_PORT_UNSAFE(
14930
0
          &(conn->phys_ctx->listening_sockets[ssl_index].lsa)));
14931
0
      redirect_to_https_port(conn, port);
14932
0
    } else {
14933
      /* A http to https forward port has been specified,
14934
       * but no https port to forward to. */
14935
0
      mg_send_http_error(conn,
14936
0
                         503,
14937
0
                         "%s",
14938
0
                         "Error: SSL forward not configured properly");
14939
0
      mg_cry_internal(conn,
14940
0
                      "%s",
14941
0
                      "Can not redirect to SSL, no SSL port available");
14942
0
    }
14943
0
    return;
14944
0
  }
14945
0
  uri_len = (int)strlen(ri->local_uri);
14946
14947
  /* 1.3. decode url (if config says so) */
14948
0
  if (should_decode_url(conn)) {
14949
0
    url_decode_in_place((char *)ri->local_uri);
14950
0
  }
14951
14952
  /* URL decode the query-string only if explicitly set in the configuration
14953
   */
14954
0
  if (conn->request_info.query_string) {
14955
0
    if (should_decode_query_string(conn)) {
14956
0
      url_decode_in_place((char *)conn->request_info.query_string);
14957
0
    }
14958
0
  }
14959
14960
  /* 1.4. clean URIs, so a path like allowed_dir/../forbidden_file is not
14961
   * possible. The fact that we cleaned the URI is stored in that the
14962
   * pointer to ri->local_ur and ri->local_uri_raw are now different.
14963
   * ri->local_uri_raw still points to memory allocated in
14964
   * worker_thread_run(). ri->local_uri is private to the request so we
14965
   * don't have to use preallocated memory here. */
14966
0
  tmp = mg_strdup(ri->local_uri_raw);
14967
0
  if (!tmp) {
14968
    /* Out of memory. We cannot do anything reasonable here. */
14969
0
    return;
14970
0
  }
14971
0
  remove_dot_segments(tmp);
14972
0
  ri->local_uri = tmp;
14973
14974
  /* step 1. completed, the url is known now */
14975
0
  DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri);
14976
14977
  /* 2. if this ip has limited speed, set it for this connection */
14978
0
  conn->throttle = set_throttle(conn->dom_ctx->config[THROTTLE],
14979
0
                                &conn->client.rsa,
14980
0
                                ri->local_uri);
14981
14982
  /* 3. call a "handle everything" callback, if registered */
14983
0
  if (conn->phys_ctx->callbacks.begin_request != NULL) {
14984
    /* Note that since V1.7 the "begin_request" function is called
14985
     * before an authorization check. If an authorization check is
14986
     * required, use a request_handler instead. */
14987
0
    i = conn->phys_ctx->callbacks.begin_request(conn);
14988
0
    if (i > 0) {
14989
      /* callback already processed the request. Store the
14990
      return value as a status code for the access log. */
14991
0
      conn->status_code = i;
14992
0
      if (!conn->must_close) {
14993
0
        discard_unread_request_data(conn);
14994
0
      }
14995
0
      DEBUG_TRACE("%s", "begin_request handled request");
14996
0
      return;
14997
0
    } else if (i == 0) {
14998
      /* civetweb should process the request */
14999
0
    } else {
15000
      /* unspecified - may change with the next version */
15001
0
      DEBUG_TRACE("%s", "done (undocumented behavior)");
15002
0
      return;
15003
0
    }
15004
0
  }
15005
15006
  /* request not yet handled by a handler or redirect, so the request
15007
   * is processed here */
15008
15009
  /* 4. Check for CORS preflight requests and handle them (if configured).
15010
   * https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
15011
   */
15012
0
  if (!strcmp(ri->request_method, "OPTIONS")) {
15013
    /* Send a response to CORS preflights only if
15014
     * access_control_allow_methods is not NULL and not an empty string.
15015
     * In this case, scripts can still handle CORS. */
15016
0
    const char *cors_meth_cfg =
15017
0
        conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
15018
0
    const char *cors_orig_cfg =
15019
0
        conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
15020
0
    const char *cors_origin =
15021
0
        get_header(ri->http_headers, ri->num_headers, "Origin");
15022
0
    const char *cors_acrm = get_header(ri->http_headers,
15023
0
                                       ri->num_headers,
15024
0
                                       "Access-Control-Request-Method");
15025
15026
    /* Todo: check if cors_origin is in cors_orig_cfg.
15027
     * Or, let the client check this. */
15028
15029
0
    if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0)
15030
0
        && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0)
15031
0
        && (cors_origin != NULL) && (cors_acrm != NULL)) {
15032
      /* This is a valid CORS preflight, and the server is configured
15033
       * to handle it automatically. */
15034
0
      const char *cors_acrh =
15035
0
          get_header(ri->http_headers,
15036
0
                     ri->num_headers,
15037
0
                     "Access-Control-Request-Headers");
15038
0
      const char *cors_cred_cfg =
15039
0
          conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_CREDENTIALS];
15040
0
      const char *cors_exphdr_cfg =
15041
0
          conn->dom_ctx->config[ACCESS_CONTROL_EXPOSE_HEADERS];
15042
15043
0
      gmt_time_string(date, sizeof(date), &curtime);
15044
0
      mg_printf(conn,
15045
0
                "HTTP/1.1 200 OK\r\n"
15046
0
                "Date: %s\r\n"
15047
0
                "Access-Control-Allow-Origin: %s\r\n"
15048
0
                "Access-Control-Allow-Methods: %s\r\n"
15049
0
                "Content-Length: 0\r\n"
15050
0
                "Connection: %s\r\n",
15051
0
                date,
15052
0
                cors_orig_cfg,
15053
0
                ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg),
15054
0
                suggest_connection_header(conn));
15055
15056
0
      if (cors_cred_cfg && *cors_cred_cfg) {
15057
0
        mg_printf(conn,
15058
0
                  "Access-Control-Allow-Credentials: %s\r\n",
15059
0
                  cors_cred_cfg);
15060
0
      }
15061
15062
0
      if (cors_exphdr_cfg && *cors_exphdr_cfg) {
15063
0
        mg_printf(conn,
15064
0
                  "Access-Control-Expose-Headers: %s\r\n",
15065
0
                  cors_exphdr_cfg);
15066
0
      }
15067
15068
0
      if (cors_acrh || (cors_cred_cfg && *cors_cred_cfg)) {
15069
        /* CORS request is asking for additional headers */
15070
0
        const char *cors_hdr_cfg =
15071
0
            conn->dom_ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
15072
15073
0
        if ((cors_hdr_cfg != NULL) && (*cors_hdr_cfg != 0)) {
15074
          /* Allow only if access_control_allow_headers is
15075
           * not NULL and not an empty string. If this
15076
           * configuration is set to *, allow everything.
15077
           * Otherwise this configuration must be a list
15078
           * of allowed HTTP header names. */
15079
0
          mg_printf(conn,
15080
0
                    "Access-Control-Allow-Headers: %s\r\n",
15081
0
                    ((cors_hdr_cfg[0] == '*') ? cors_acrh
15082
0
                                              : cors_hdr_cfg));
15083
0
        }
15084
0
      }
15085
0
      mg_printf(conn, "Access-Control-Max-Age: 60\r\n");
15086
0
      mg_printf(conn, "\r\n");
15087
0
      DEBUG_TRACE("%s", "OPTIONS done");
15088
0
      return;
15089
0
    }
15090
0
  }
15091
15092
  /* 5. interpret the url to find out how the request must be handled
15093
   */
15094
  /* 5.1. first test, if the request targets the regular http(s)://
15095
   * protocol namespace or the websocket ws(s):// protocol namespace.
15096
   */
15097
0
  is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET);
15098
#if defined(USE_WEBSOCKET)
15099
  handler_type = is_websocket_request ? WEBSOCKET_HANDLER : REQUEST_HANDLER;
15100
#else
15101
0
  handler_type = REQUEST_HANDLER;
15102
0
#endif /* defined(USE_WEBSOCKET) */
15103
15104
0
  if (is_websocket_request) {
15105
0
    HTTP1_only;
15106
0
  }
15107
15108
  /* 5.2. check if the request will be handled by a callback */
15109
0
  if (get_request_handler(conn,
15110
0
                          handler_type,
15111
0
                          &callback_handler,
15112
0
                          &subprotocols,
15113
0
                          &ws_connect_handler,
15114
0
                          &ws_ready_handler,
15115
0
                          &ws_data_handler,
15116
0
                          &ws_close_handler,
15117
0
                          NULL,
15118
0
                          &callback_data,
15119
0
                          &handler_info)) {
15120
    /* 5.2.1. A callback will handle this request. All requests
15121
     * handled by a callback have to be considered as requests
15122
     * to a script resource. */
15123
0
    is_callback_resource = 1;
15124
0
    is_script_resource = 1;
15125
0
    is_put_or_delete_request = is_put_or_delete_method(conn);
15126
    /* Never handle a C callback according to File WebDav rules,
15127
     * even if it is a webdav method */
15128
0
    is_webdav_request = 0; /* is_civetweb_webdav_method(conn); */
15129
0
  } else {
15130
0
  no_callback_resource:
15131
15132
    /* 5.2.2. No callback is responsible for this request. The URI
15133
     * addresses a file based resource (static content or Lua/cgi
15134
     * scripts in the file system). */
15135
0
    is_callback_resource = 0;
15136
0
    interpret_uri(conn,
15137
0
                  path,
15138
0
                  sizeof(path),
15139
0
                  &file.stat,
15140
0
                  &is_found,
15141
0
                  &is_script_resource,
15142
0
                  &is_websocket_request,
15143
0
                  &is_put_or_delete_request,
15144
0
                  &is_webdav_request,
15145
0
                  &is_template_text_file);
15146
0
  }
15147
15148
  /* 5.3. A webdav request (PROPFIND/PROPPATCH/LOCK/UNLOCK) */
15149
0
  if (is_webdav_request) {
15150
    /* TODO: Do we need a config option? */
15151
0
    const char *webdav_enable = conn->dom_ctx->config[ENABLE_WEBDAV];
15152
0
    if (webdav_enable[0] != 'y') {
15153
0
      mg_send_http_error(conn,
15154
0
                         405,
15155
0
                         "%s method not allowed",
15156
0
                         conn->request_info.request_method);
15157
0
      DEBUG_TRACE("%s", "webdav rejected");
15158
0
      return;
15159
0
    }
15160
0
  }
15161
15162
  /* 6. authorization check */
15163
  /* 6.1. a custom authorization handler is installed */
15164
0
  if (get_request_handler(conn,
15165
0
                          AUTH_HANDLER,
15166
0
                          NULL,
15167
0
                          NULL,
15168
0
                          NULL,
15169
0
                          NULL,
15170
0
                          NULL,
15171
0
                          NULL,
15172
0
                          &auth_handler,
15173
0
                          &auth_callback_data,
15174
0
                          NULL)) {
15175
0
    if (!auth_handler(conn, auth_callback_data)) {
15176
15177
      /* Callback handler will not be used anymore. Release it */
15178
0
      release_handler_ref(conn, handler_info);
15179
0
      DEBUG_TRACE("%s", "auth handler rejected request");
15180
0
      return;
15181
0
    }
15182
0
  } else if (is_put_or_delete_request && !is_script_resource
15183
0
             && !is_callback_resource) {
15184
0
    HTTP1_only;
15185
    /* 6.2. this request is a PUT/DELETE to a real file */
15186
    /* 6.2.1. thus, the server must have real files */
15187
#if defined(NO_FILES)
15188
    if (1) {
15189
#else
15190
0
    if (conn->dom_ctx->config[DOCUMENT_ROOT] == NULL
15191
0
        || conn->dom_ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL) {
15192
0
#endif
15193
      /* This code path will not be called for request handlers */
15194
0
      DEBUG_ASSERT(handler_info == NULL);
15195
15196
      /* This server does not have any real files, thus the
15197
       * PUT/DELETE methods are not valid. */
15198
0
      mg_send_http_error(conn,
15199
0
                         405,
15200
0
                         "%s method not allowed",
15201
0
                         conn->request_info.request_method);
15202
0
      DEBUG_TRACE("%s", "all file based put/delete requests rejected");
15203
0
      return;
15204
0
    }
15205
15206
0
#if !defined(NO_FILES)
15207
    /* 6.2.2. Check if put authorization for static files is
15208
     * available.
15209
     */
15210
0
    if (!is_authorized_for_put(conn)) {
15211
0
      send_authorization_request(conn, NULL);
15212
0
      DEBUG_TRACE("%s", "file write needs authorization");
15213
0
      return;
15214
0
    }
15215
0
#endif
15216
15217
0
  } else {
15218
    /* 6.3. This is either a OPTIONS, GET, HEAD or POST request,
15219
     * or it is a PUT or DELETE request to a resource that does not
15220
     * correspond to a file. Check authorization. */
15221
0
    if (!check_authorization(conn, path)) {
15222
0
      send_authorization_request(conn, NULL);
15223
15224
      /* Callback handler will not be used anymore. Release it */
15225
0
      release_handler_ref(conn, handler_info);
15226
0
      DEBUG_TRACE("%s", "access authorization required");
15227
0
      return;
15228
0
    }
15229
0
  }
15230
15231
  /* request is authorized or does not need authorization */
15232
15233
  /* 7. check if there are request handlers for this uri */
15234
0
  if (is_callback_resource) {
15235
0
    HTTP1_only;
15236
0
    if (!is_websocket_request) {
15237
0
      i = callback_handler(conn, callback_data);
15238
15239
      /* Callback handler will not be used anymore. Release it */
15240
0
      release_handler_ref(conn, handler_info);
15241
15242
0
      if (i > 0) {
15243
        /* Do nothing, callback has served the request. Store
15244
         * then return value as status code for the log and discard
15245
         * all data from the client not used by the callback. */
15246
0
        conn->status_code = i;
15247
0
        if (!conn->must_close) {
15248
0
          discard_unread_request_data(conn);
15249
0
        }
15250
0
      } else {
15251
        /* The handler did NOT handle the request. */
15252
        /* Some proper reactions would be:
15253
         * a) close the connections without sending anything
15254
         * b) send a 404 not found
15255
         * c) try if there is a file matching the URI
15256
         * It would be possible to do a, b or c in the callback
15257
         * implementation, and return 1 - we cannot do anything
15258
         * here, that is not possible in the callback.
15259
         *
15260
         * TODO: What would be the best reaction here?
15261
         * (Note: The reaction may change, if there is a better
15262
         * idea.)
15263
         */
15264
15265
        /* For the moment, use option c: We look for a proper file,
15266
         * but since a file request is not always a script resource,
15267
         * the authorization check might be different. */
15268
0
        callback_handler = NULL;
15269
15270
        /* Here we are at a dead end:
15271
         * According to URI matching, a callback should be
15272
         * responsible for handling the request,
15273
         * we called it, but the callback declared itself
15274
         * not responsible.
15275
         * We use a goto here, to get out of this dead end,
15276
         * and continue with the default handling.
15277
         * A goto here is simpler and better to understand
15278
         * than some curious loop. */
15279
0
        goto no_callback_resource;
15280
0
      }
15281
0
    } else {
15282
#if defined(USE_WEBSOCKET)
15283
      handle_websocket_request(conn,
15284
                               path,
15285
                               is_callback_resource,
15286
                               subprotocols,
15287
                               ws_connect_handler,
15288
                               ws_ready_handler,
15289
                               ws_data_handler,
15290
                               ws_close_handler,
15291
                               callback_data);
15292
#endif
15293
0
    }
15294
0
    DEBUG_TRACE("%s", "websocket handling done");
15295
0
    return;
15296
0
  }
15297
15298
  /* 8. handle websocket requests */
15299
#if defined(USE_WEBSOCKET)
15300
  if (is_websocket_request) {
15301
    HTTP1_only;
15302
    if (is_script_resource) {
15303
15304
      if (is_in_script_path(conn, path)) {
15305
        /* Websocket Lua script */
15306
        handle_websocket_request(conn,
15307
                                 path,
15308
                                 0 /* Lua Script */,
15309
                                 NULL,
15310
                                 NULL,
15311
                                 NULL,
15312
                                 NULL,
15313
                                 NULL,
15314
                                 conn->phys_ctx->user_data);
15315
      } else {
15316
        /* Script was in an illegal path */
15317
        mg_send_http_error(conn, 403, "%s", "Forbidden");
15318
      }
15319
    } else {
15320
      mg_send_http_error(conn, 404, "%s", "Not found");
15321
    }
15322
    DEBUG_TRACE("%s", "websocket script done");
15323
    return;
15324
  } else
15325
#endif
15326
15327
#if defined(NO_FILES)
15328
    /* 9a. In case the server uses only callbacks, this uri is
15329
     * unknown.
15330
     * Then, all request handling ends here. */
15331
    mg_send_http_error(conn, 404, "%s", "Not Found");
15332
15333
#else
15334
  /* 9b. This request is either for a static file or resource handled
15335
   * by a script file. Thus, a DOCUMENT_ROOT must exist. */
15336
0
  if (conn->dom_ctx->config[DOCUMENT_ROOT] == NULL) {
15337
0
    mg_send_http_error(conn, 404, "%s", "Not Found");
15338
0
    DEBUG_TRACE("%s", "no document root available");
15339
0
    return;
15340
0
  }
15341
15342
  /* 10. Request is handled by a script */
15343
0
  if (is_script_resource) {
15344
0
    HTTP1_only;
15345
0
    handle_file_based_request(conn, path, &file);
15346
0
    DEBUG_TRACE("%s", "script handling done");
15347
0
    return;
15348
0
  }
15349
15350
  /* Request was not handled by a callback or script. It will be
15351
   * handled by a server internal method. */
15352
15353
  /* 11. Handle put/delete/mkcol requests */
15354
0
  if (is_put_or_delete_request) {
15355
0
    HTTP1_only;
15356
    /* 11.1. PUT method */
15357
0
    if (!strcmp(ri->request_method, "PUT")) {
15358
0
      put_file(conn, path);
15359
0
      DEBUG_TRACE("handling %s request to %s done",
15360
0
                  ri->request_method,
15361
0
                  path);
15362
0
      return;
15363
0
    }
15364
    /* 11.2. DELETE method */
15365
0
    if (!strcmp(ri->request_method, "DELETE")) {
15366
0
      delete_file(conn, path);
15367
0
      DEBUG_TRACE("handling %s request to %s done",
15368
0
                  ri->request_method,
15369
0
                  path);
15370
0
      return;
15371
0
    }
15372
    /* 11.3. MKCOL method */
15373
0
    if (!strcmp(ri->request_method, "MKCOL")) {
15374
0
      dav_mkcol(conn, path);
15375
0
      DEBUG_TRACE("handling %s request to %s done",
15376
0
                  ri->request_method,
15377
0
                  path);
15378
0
      return;
15379
0
    }
15380
    /* 11.4. MOVE method */
15381
0
    if (!strcmp(ri->request_method, "MOVE")) {
15382
0
      dav_move_file(conn, path, 0);
15383
0
      DEBUG_TRACE("handling %s request to %s done",
15384
0
                  ri->request_method,
15385
0
                  path);
15386
0
      return;
15387
0
    }
15388
0
    if (!strcmp(ri->request_method, "COPY")) {
15389
0
      dav_move_file(conn, path, 1);
15390
0
      DEBUG_TRACE("handling %s request to %s done",
15391
0
                  ri->request_method,
15392
0
                  path);
15393
0
      return;
15394
0
    }
15395
    /* 11.5. LOCK method */
15396
0
    if (!strcmp(ri->request_method, "LOCK")) {
15397
0
      dav_lock_file(conn, path);
15398
0
      DEBUG_TRACE("handling %s request to %s done",
15399
0
                  ri->request_method,
15400
0
                  path);
15401
0
      return;
15402
0
    }
15403
    /* 11.6. UNLOCK method */
15404
0
    if (!strcmp(ri->request_method, "UNLOCK")) {
15405
0
      dav_unlock_file(conn, path);
15406
0
      DEBUG_TRACE("handling %s request to %s done",
15407
0
                  ri->request_method,
15408
0
                  path);
15409
0
      return;
15410
0
    }
15411
    /* 11.7. PROPPATCH method */
15412
0
    if (!strcmp(ri->request_method, "PROPPATCH")) {
15413
0
      dav_proppatch(conn, path);
15414
0
      DEBUG_TRACE("handling %s request to %s done",
15415
0
                  ri->request_method,
15416
0
                  path);
15417
0
      return;
15418
0
    }
15419
    /* 11.8. Other methods, e.g.: PATCH
15420
     * This method is not supported for static resources,
15421
     * only for scripts (Lua, CGI) and callbacks. */
15422
0
    mg_send_http_error(conn,
15423
0
                       405,
15424
0
                       "%s method not allowed",
15425
0
                       conn->request_info.request_method);
15426
0
    DEBUG_TRACE("method %s on %s is not supported",
15427
0
                ri->request_method,
15428
0
                path);
15429
0
    return;
15430
0
  }
15431
15432
  /* 11. File does not exist, or it was configured that it should be
15433
   * hidden */
15434
0
  if (!is_found || (must_hide_file(conn, path))) {
15435
0
    mg_send_http_error(conn, 404, "%s", "Not found");
15436
0
    DEBUG_TRACE("handling %s request to %s: file not found",
15437
0
                ri->request_method,
15438
0
                path);
15439
0
    return;
15440
0
  }
15441
15442
  /* 12. Directory uris should end with a slash */
15443
0
  if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0)
15444
0
      && (ri->local_uri[uri_len - 1] != '/')) {
15445
15446
    /* Path + server root */
15447
0
    size_t buflen = UTF8_PATH_MAX * 2 + 2;
15448
0
    char *new_path;
15449
15450
0
    if (ri->query_string) {
15451
0
      buflen += strlen(ri->query_string);
15452
0
    }
15453
0
    new_path = (char *)mg_malloc_ctx(buflen, conn->phys_ctx);
15454
0
    if (!new_path) {
15455
0
      mg_send_http_error(conn, 500, "out or memory");
15456
0
    } else {
15457
0
      mg_get_request_link(conn, new_path, buflen - 1);
15458
0
      strcat(new_path, "/");
15459
0
      if (ri->query_string) {
15460
        /* Append ? and query string */
15461
0
        strcat(new_path, "?");
15462
0
        strcat(new_path, ri->query_string);
15463
0
      }
15464
0
      mg_send_http_redirect(conn, new_path, 301);
15465
0
      mg_free(new_path);
15466
0
    }
15467
0
    DEBUG_TRACE("%s request to %s: directory redirection sent",
15468
0
                ri->request_method,
15469
0
                path);
15470
0
    return;
15471
0
  }
15472
15473
  /* 13. Handle other methods than GET/HEAD */
15474
  /* 13.1. Handle PROPFIND */
15475
0
  if (!strcmp(ri->request_method, "PROPFIND")) {
15476
0
    handle_propfind(conn, path, &file.stat);
15477
0
    DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15478
0
    return;
15479
0
  }
15480
  /* 13.2. Handle OPTIONS for files */
15481
0
  if (!strcmp(ri->request_method, "OPTIONS")) {
15482
    /* This standard handler is only used for real files.
15483
     * Scripts should support the OPTIONS method themselves, to allow a
15484
     * maximum flexibility.
15485
     * Lua and CGI scripts may fully support CORS this way (including
15486
     * preflights). */
15487
0
    send_options(conn);
15488
0
    DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15489
0
    return;
15490
0
  }
15491
  /* 13.3. everything but GET and HEAD (e.g. POST) */
15492
0
  if ((0 != strcmp(ri->request_method, "GET"))
15493
0
      && (0 != strcmp(ri->request_method, "HEAD"))) {
15494
0
    mg_send_http_error(conn,
15495
0
                       405,
15496
0
                       "%s method not allowed",
15497
0
                       conn->request_info.request_method);
15498
0
    DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15499
0
    return;
15500
0
  }
15501
15502
  /* 14. directories */
15503
0
  if (file.stat.is_directory) {
15504
    /* Substitute files have already been handled above. */
15505
    /* Here we can either generate and send a directory listing,
15506
     * or send an "access denied" error. */
15507
0
    if (!mg_strcasecmp(conn->dom_ctx->config[ENABLE_DIRECTORY_LISTING],
15508
0
                       "yes")) {
15509
0
      handle_directory_request(conn, path);
15510
0
    } else {
15511
0
      mg_send_http_error(conn,
15512
0
                         403,
15513
0
                         "%s",
15514
0
                         "Error: Directory listing denied");
15515
0
    }
15516
0
    DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15517
0
    return;
15518
0
  }
15519
15520
  /* 15. Files with search/replace patterns: LSP and SSI */
15521
0
  if (is_template_text_file) {
15522
0
    HTTP1_only;
15523
0
    handle_file_based_request(conn, path, &file);
15524
0
    DEBUG_TRACE("handling %s request to %s done (template)",
15525
0
                ri->request_method,
15526
0
                path);
15527
0
    return;
15528
0
  }
15529
15530
  /* 16. Static file - maybe cached */
15531
0
#if !defined(NO_CACHING)
15532
0
  if ((!conn->in_error_handler) && is_not_modified(conn, &file.stat)) {
15533
    /* Send 304 "Not Modified" - this must not send any body data */
15534
0
    handle_not_modified_static_file_request(conn, &file);
15535
0
    DEBUG_TRACE("handling %s request to %s done (not modified)",
15536
0
                ri->request_method,
15537
0
                path);
15538
0
    return;
15539
0
  }
15540
0
#endif /* !NO_CACHING */
15541
15542
  /* 17. Static file - not cached */
15543
0
  handle_static_file_request(conn, path, &file, NULL, NULL);
15544
0
  DEBUG_TRACE("handling %s request to %s done (static)",
15545
0
              ri->request_method,
15546
0
              path);
15547
15548
0
#endif /* !defined(NO_FILES) */
15549
0
}
15550
15551
15552
#if !defined(NO_FILESYSTEMS)
15553
static void
15554
handle_file_based_request(struct mg_connection *conn,
15555
                          const char *path,
15556
                          struct mg_file *file)
15557
0
{
15558
0
#if !defined(NO_CGI)
15559
0
  int cgi_config_idx, inc, max;
15560
0
#endif
15561
15562
0
  if (!conn || !conn->dom_ctx) {
15563
0
    return;
15564
0
  }
15565
15566
#if defined(USE_LUA)
15567
  if (match_prefix_strlen(conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
15568
                          path)
15569
      > 0) {
15570
    if (is_in_script_path(conn, path)) {
15571
      /* Lua server page: an SSI like page containing mostly plain
15572
       * html code plus some tags with server generated contents. */
15573
      handle_lsp_request(conn, path, file, NULL);
15574
    } else {
15575
      /* Script was in an illegal path */
15576
      mg_send_http_error(conn, 403, "%s", "Forbidden");
15577
    }
15578
    return;
15579
  }
15580
15581
  if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS], path)
15582
      > 0) {
15583
    if (is_in_script_path(conn, path)) {
15584
      /* Lua in-server module script: a CGI like script used to
15585
       * generate the entire reply. */
15586
      mg_exec_lua_script(conn, path, NULL);
15587
    } else {
15588
      /* Script was in an illegal path */
15589
      mg_send_http_error(conn, 403, "%s", "Forbidden");
15590
    }
15591
    return;
15592
  }
15593
#endif
15594
15595
#if defined(USE_DUKTAPE)
15596
  if (match_prefix_strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
15597
                          path)
15598
      > 0) {
15599
    if (is_in_script_path(conn, path)) {
15600
      /* Call duktape to generate the page */
15601
      mg_exec_duktape_script(conn, path);
15602
    } else {
15603
      /* Script was in an illegal path */
15604
      mg_send_http_error(conn, 403, "%s", "Forbidden");
15605
    }
15606
    return;
15607
  }
15608
#endif
15609
15610
0
#if !defined(NO_CGI)
15611
0
  inc = CGI2_EXTENSIONS - CGI_EXTENSIONS;
15612
0
  max = PUT_DELETE_PASSWORDS_FILE - CGI_EXTENSIONS;
15613
0
  for (cgi_config_idx = 0; cgi_config_idx < max; cgi_config_idx += inc) {
15614
0
    if (conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx] != NULL) {
15615
0
      if (match_prefix_strlen(
15616
0
              conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx],
15617
0
              path)
15618
0
          > 0) {
15619
0
        if (is_in_script_path(conn, path)) {
15620
          /* CGI scripts may support all HTTP methods */
15621
0
          handle_cgi_request(conn, path, cgi_config_idx);
15622
0
        } else {
15623
          /* Script was in an illegal path */
15624
0
          mg_send_http_error(conn, 403, "%s", "Forbidden");
15625
0
        }
15626
0
        return;
15627
0
      }
15628
0
    }
15629
0
  }
15630
0
#endif /* !NO_CGI */
15631
15632
0
  if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path) > 0) {
15633
0
    if (is_in_script_path(conn, path)) {
15634
0
      handle_ssi_file_request(conn, path, file);
15635
0
    } else {
15636
      /* Script was in an illegal path */
15637
0
      mg_send_http_error(conn, 403, "%s", "Forbidden");
15638
0
    }
15639
0
    return;
15640
0
  }
15641
15642
0
#if !defined(NO_CACHING)
15643
0
  if ((!conn->in_error_handler) && is_not_modified(conn, &file->stat)) {
15644
    /* Send 304 "Not Modified" - this must not send any body data */
15645
0
    handle_not_modified_static_file_request(conn, file);
15646
0
    return;
15647
0
  }
15648
0
#endif /* !NO_CACHING */
15649
15650
0
  handle_static_file_request(conn, path, file, NULL, NULL);
15651
0
}
15652
#endif /* NO_FILESYSTEMS */
15653
15654
15655
static void
15656
close_all_listening_sockets(struct mg_context *ctx)
15657
2
{
15658
2
  unsigned int i;
15659
2
  if (!ctx) {
15660
0
    return;
15661
0
  }
15662
15663
4
  for (i = 0; i < ctx->num_listening_sockets; i++) {
15664
2
    closesocket(ctx->listening_sockets[i].sock);
15665
#if defined(USE_X_DOM_SOCKET)
15666
    /* For unix domain sockets, the socket name represents a file that has
15667
     * to be deleted. */
15668
    /* See
15669
     * https://stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix
15670
     */
15671
    if ((ctx->listening_sockets[i].lsa.sin.sin_family == AF_UNIX)
15672
        && (ctx->listening_sockets[i].sock != INVALID_SOCKET)) {
15673
      IGNORE_UNUSED_RESULT(
15674
          remove(ctx->listening_sockets[i].lsa.sun.sun_path));
15675
    }
15676
#endif
15677
2
    ctx->listening_sockets[i].sock = INVALID_SOCKET;
15678
2
  }
15679
2
  mg_free(ctx->listening_sockets);
15680
2
  ctx->listening_sockets = NULL;
15681
2
  mg_free(ctx->listening_socket_fds);
15682
2
  ctx->listening_socket_fds = NULL;
15683
2
}
15684
15685
15686
/* Valid listening port specification is: [ip_address:]port[s]
15687
 * Examples for IPv4: 80, 443s, 127.0.0.1:3128, 192.0.2.3:8080s
15688
 * Examples for IPv6: [::]:80, [::1]:80,
15689
 *   [2001:0db8:7654:3210:FEDC:BA98:7654:3210]:443s
15690
 *   see https://tools.ietf.org/html/rfc3513#section-2.2
15691
 * In order to bind to both, IPv4 and IPv6, you can either add
15692
 * both ports using 8080,[::]:8080, or the short form +8080.
15693
 * Both forms differ in detail: 8080,[::]:8080 create two sockets,
15694
 * one only accepting IPv4 the other only IPv6. +8080 creates
15695
 * one socket accepting IPv4 and IPv6. Depending on the IPv6
15696
 * environment, they might work differently, or might not work
15697
 * at all - it must be tested what options work best in the
15698
 * relevant network environment.
15699
 */
15700
static int
15701
parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
15702
2
{
15703
2
  unsigned int a, b, c, d;
15704
2
  unsigned port;
15705
2
  unsigned long portUL;
15706
2
  int len;
15707
2
  const char *cb;
15708
2
  char *endptr;
15709
#if defined(USE_IPV6)
15710
  char buf[100] = {0};
15711
#endif
15712
15713
  /* MacOS needs that. If we do not zero it, subsequent bind() will fail.
15714
   * Also, all-zeroes in the socket address means binding to all addresses
15715
   * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). */
15716
2
  memset(so, 0, sizeof(*so));
15717
2
  so->lsa.sin.sin_family = AF_INET;
15718
2
  *ip_version = 0;
15719
15720
  /* Initialize len as invalid. */
15721
2
  port = 0;
15722
2
  len = 0;
15723
15724
  /* Test for different ways to format this string */
15725
2
  if (sscanf(vec->ptr,
15726
2
             "%u.%u.%u.%u:%u%n",
15727
2
             &a,
15728
2
             &b,
15729
2
             &c,
15730
2
             &d,
15731
2
             &port,
15732
2
             &len) // NOLINT(cert-err34-c) 'sscanf' used to convert a string
15733
                   // to an integer value, but function will not report
15734
                   // conversion errors; consider using 'strtol' instead
15735
2
      == 5) {
15736
    /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */
15737
0
    so->lsa.sin.sin_addr.s_addr =
15738
0
        htonl((a << 24) | (b << 16) | (c << 8) | d);
15739
0
    so->lsa.sin.sin_port = htons((uint16_t)port);
15740
0
    *ip_version = 4;
15741
15742
#if defined(USE_IPV6)
15743
  } else if (sscanf(vec->ptr, "[%49[^]]]:%u%n", buf, &port, &len) == 2
15744
             && ((size_t)len <= vec->len)
15745
             && mg_inet_pton(
15746
                    AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6), 0)) {
15747
    /* IPv6 address, examples: see above */
15748
    /* so->lsa.sin6.sin6_family = AF_INET6; already set by mg_inet_pton
15749
     */
15750
    so->lsa.sin6.sin6_port = htons((uint16_t)port);
15751
    *ip_version = 6;
15752
#endif
15753
15754
2
  } else if ((vec->ptr[0] == '+')
15755
2
             && (sscanf(vec->ptr + 1, "%u%n", &port, &len)
15756
0
                 == 1)) { // NOLINT(cert-err34-c) 'sscanf' used to convert a
15757
                        // string to an integer value, but function will not
15758
                        // report conversion errors; consider using 'strtol'
15759
                        // instead
15760
15761
    /* Port is specified with a +, bind to IPv6 and IPv4, INADDR_ANY */
15762
    /* Add 1 to len for the + character we skipped before */
15763
0
    len++;
15764
15765
#if defined(USE_IPV6)
15766
    /* Set socket family to IPv6, do not use IPV6_V6ONLY */
15767
    so->lsa.sin6.sin6_family = AF_INET6;
15768
    so->lsa.sin6.sin6_port = htons((uint16_t)port);
15769
    *ip_version = 4 + 6;
15770
#else
15771
    /* Bind to IPv4 only, since IPv6 is not built in. */
15772
0
    so->lsa.sin.sin_port = htons((uint16_t)port);
15773
0
    *ip_version = 4;
15774
0
#endif
15775
15776
2
  } else if (is_valid_port(portUL = strtoul(vec->ptr, &endptr, 0))
15777
2
             && (vec->ptr != endptr)) {
15778
2
    len = (int)(endptr - vec->ptr);
15779
2
    port = (uint16_t)portUL;
15780
    /* If only port is specified, bind to IPv4, INADDR_ANY */
15781
2
    so->lsa.sin.sin_port = htons((uint16_t)port);
15782
2
    *ip_version = 4;
15783
15784
2
  } else if ((cb = strchr(vec->ptr, ':')) != NULL) {
15785
    /* String could be a hostname. This check algorithm
15786
     * will only work for RFC 952 compliant hostnames,
15787
     * starting with a letter, containing only letters,
15788
     * digits and hyphen ('-'). Newer specs may allow
15789
     * more, but this is not guaranteed here, since it
15790
     * may interfere with rules for port option lists. */
15791
15792
    /* According to RFC 1035, hostnames are restricted to 255 characters
15793
     * in total (63 between two dots). */
15794
0
    char hostname[256];
15795
0
    size_t hostnlen = (size_t)(cb - vec->ptr);
15796
15797
0
    if ((hostnlen >= vec->len) || (hostnlen >= sizeof(hostname))) {
15798
      /* This would be invalid in any case */
15799
0
      *ip_version = 0;
15800
0
      return 0;
15801
0
    }
15802
15803
0
    mg_strlcpy(hostname, vec->ptr, hostnlen + 1);
15804
15805
0
    if (mg_inet_pton(
15806
0
            AF_INET, hostname, &so->lsa.sin, sizeof(so->lsa.sin), 1)) {
15807
0
      if (sscanf(cb + 1, "%u%n", &port, &len)
15808
0
          == 1) { // NOLINT(cert-err34-c) 'sscanf' used to convert a
15809
                // string to an integer value, but function will not
15810
                // report conversion errors; consider using 'strtol'
15811
                // instead
15812
0
        *ip_version = 4;
15813
0
        so->lsa.sin.sin_port = htons((uint16_t)port);
15814
0
        len += (int)(hostnlen + 1);
15815
0
      } else {
15816
0
        len = 0;
15817
0
      }
15818
#if defined(USE_IPV6)
15819
    } else if (mg_inet_pton(AF_INET6,
15820
                            hostname,
15821
                            &so->lsa.sin6,
15822
                            sizeof(so->lsa.sin6),
15823
                            1)) {
15824
      if (sscanf(cb + 1, "%u%n", &port, &len) == 1) {
15825
        *ip_version = 6;
15826
        so->lsa.sin6.sin6_port = htons((uint16_t)port);
15827
        len += (int)(hostnlen + 1);
15828
      } else {
15829
        len = 0;
15830
      }
15831
#endif
15832
0
    } else {
15833
0
      len = 0;
15834
0
    }
15835
15836
#if defined(USE_X_DOM_SOCKET)
15837
15838
  } else if (vec->ptr[0] == 'x') {
15839
    /* unix (linux) domain socket */
15840
    if (vec->len < sizeof(so->lsa.sun.sun_path)) {
15841
      len = vec->len;
15842
      so->lsa.sun.sun_family = AF_UNIX;
15843
      memset(so->lsa.sun.sun_path, 0, sizeof(so->lsa.sun.sun_path));
15844
      memcpy(so->lsa.sun.sun_path, (char *)vec->ptr + 1, vec->len - 1);
15845
      port = 0;
15846
      *ip_version = 99;
15847
    } else {
15848
      /* String too long */
15849
      len = 0;
15850
    }
15851
#endif
15852
15853
0
  } else {
15854
    /* Parsing failure. */
15855
0
    len = 0;
15856
0
  }
15857
15858
  /* sscanf and the option splitting code ensure the following condition
15859
   * Make sure the port is valid and vector ends with the port, 'o', 's', or
15860
   * 'r' */
15861
2
  if ((len > 0) && (is_valid_port(port))) {
15862
2
    int bad_suffix = 0;
15863
15864
    /* Parse any suffix character(s) after the port number */
15865
2
    for (size_t i = len; i < vec->len; i++) {
15866
0
      unsigned char *opt = NULL;
15867
0
      switch (vec->ptr[i]) {
15868
0
      case 'o':
15869
0
        opt = &so->is_optional;
15870
0
        break;
15871
0
      case 'r':
15872
0
        opt = &so->ssl_redir;
15873
0
        break;
15874
0
      case 's':
15875
0
        opt = &so->is_ssl;
15876
0
        break;
15877
0
      default: /* empty */
15878
0
        break;
15879
0
      }
15880
15881
0
      if ((opt) && (*opt == 0))
15882
0
        *opt = 1;
15883
0
      else {
15884
0
        bad_suffix = 1;
15885
0
        break;
15886
0
      }
15887
0
    }
15888
15889
2
    if ((bad_suffix == 0) && ((so->is_ssl == 0) || (so->ssl_redir == 0))) {
15890
2
      return 1;
15891
2
    }
15892
2
  }
15893
15894
  /* Reset ip_version to 0 if there is an error */
15895
0
  *ip_version = 0;
15896
0
  return 0;
15897
2
}
15898
15899
15900
/* Is there any SSL port in use? */
15901
static int
15902
is_ssl_port_used(const char *ports)
15903
4
{
15904
4
  if (ports) {
15905
    /* There are several different allowed syntax variants:
15906
     * - "80" for a single port using every network interface
15907
     * - "localhost:80" for a single port using only localhost
15908
     * - "80,localhost:8080" for two ports, one bound to localhost
15909
     * - "80,127.0.0.1:8084,[::1]:8086" for three ports, one bound
15910
     *   to IPv4 localhost, one to IPv6 localhost
15911
     * - "+80" use port 80 for IPv4 and IPv6
15912
     * - "+80r,+443s" port 80 (HTTP) is a redirect to port 443 (HTTPS),
15913
     *   for both: IPv4 and IPv4
15914
     * - "+443s,localhost:8080" port 443 (HTTPS) for every interface,
15915
     *   additionally port 8080 bound to localhost connections
15916
     *
15917
     * If we just look for 's' anywhere in the string, "localhost:80"
15918
     * will be detected as SSL (false positive).
15919
     * Looking for 's' after a digit may cause false positives in
15920
     * "my24service:8080".
15921
     * Looking from 's' backward if there are only ':' and numbers
15922
     * before will not work for "24service:8080" (non SSL, port 8080)
15923
     * or "24s" (SSL, port 24).
15924
     *
15925
     * Remark: Initially hostnames were not allowed to start with a
15926
     * digit (according to RFC 952), this was allowed later (RFC 1123,
15927
     * Section 2.1).
15928
     *
15929
     * To get this correct, the entire string must be parsed as a whole,
15930
     * reading it as a list element for element and parsing with an
15931
     * algorithm equivalent to parse_port_string.
15932
     *
15933
     * In fact, we use local interface names here, not arbitrary
15934
     * hostnames, so in most cases the only name will be "localhost".
15935
     *
15936
     * So, for now, we use this simple algorithm, that may still return
15937
     * a false positive in bizarre cases.
15938
     */
15939
4
    int i;
15940
4
    int portslen = (int)strlen(ports);
15941
4
    char prevIsNumber = 0;
15942
15943
8
    for (i = 0; i < portslen; i++) {
15944
4
      if (prevIsNumber) {
15945
0
        int suffixCharIdx = (ports[i] == 'o')
15946
0
                                ? (i + 1)
15947
0
                                : i; /* allow "os" and "or" suffixes */
15948
0
        if (ports[suffixCharIdx] == 's'
15949
0
            || ports[suffixCharIdx] == 'r') {
15950
0
          return 1;
15951
0
        }
15952
0
      }
15953
4
      if (ports[i] >= '0' && ports[i] <= '9') {
15954
4
        prevIsNumber = 1;
15955
4
      } else {
15956
0
        prevIsNumber = 0;
15957
0
      }
15958
4
    }
15959
4
  }
15960
4
  return 0;
15961
4
}
15962
15963
15964
static int
15965
set_ports_option(struct mg_context *phys_ctx)
15966
2
{
15967
2
  const char *list;
15968
2
  int on = 1;
15969
#if defined(USE_IPV6)
15970
  int off = 0;
15971
#endif
15972
2
  struct vec vec;
15973
2
  struct socket so, *ptr;
15974
15975
2
  struct mg_pollfd *pfd;
15976
2
  union usa usa;
15977
2
  socklen_t len;
15978
2
  int ip_version;
15979
15980
2
  int portsTotal = 0;
15981
2
  int portsOk = 0;
15982
15983
2
  const char *opt_txt;
15984
2
  long opt_listen_backlog;
15985
15986
2
  if (!phys_ctx) {
15987
0
    return 0;
15988
0
  }
15989
15990
2
  memset(&so, 0, sizeof(so));
15991
2
  memset(&usa, 0, sizeof(usa));
15992
2
  len = sizeof(usa);
15993
2
  list = phys_ctx->dd.config[LISTENING_PORTS];
15994
15995
4
  while ((list = next_option(list, &vec, NULL)) != NULL) {
15996
15997
2
    portsTotal++;
15998
15999
2
    if (!parse_port_string(&vec, &so, &ip_version)) {
16000
0
      mg_cry_ctx_internal(
16001
0
          phys_ctx,
16002
0
          "%.*s: invalid port spec (entry %i). Expecting list of: %s",
16003
0
          (int)vec.len,
16004
0
          vec.ptr,
16005
0
          portsTotal,
16006
0
          "[IP_ADDRESS:]PORT[s|r]");
16007
0
      continue;
16008
0
    }
16009
16010
2
#if !defined(NO_SSL)
16011
2
    if (so.is_ssl && phys_ctx->dd.ssl_ctx == NULL) {
16012
16013
0
      mg_cry_ctx_internal(phys_ctx,
16014
0
                          "Cannot add SSL socket (entry %i)",
16015
0
                          portsTotal);
16016
0
      continue;
16017
0
    }
16018
2
#endif
16019
    /* Create socket. */
16020
    /* For a list of protocol numbers (e.g., TCP==6) see:
16021
     * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
16022
     */
16023
2
    if ((so.sock =
16024
2
             socket(so.lsa.sa.sa_family,
16025
2
                    SOCK_STREAM,
16026
2
                    (ip_version == 99) ? (/* LOCAL */ 0) : (/* TCP */ 6)))
16027
2
        == INVALID_SOCKET) {
16028
16029
0
      mg_cry_ctx_internal(phys_ctx,
16030
0
                          "cannot create socket (entry %i)",
16031
0
                          portsTotal);
16032
0
      continue;
16033
0
    }
16034
16035
#if defined(_WIN32)
16036
    /* Windows SO_REUSEADDR lets many procs binds to a
16037
     * socket, SO_EXCLUSIVEADDRUSE makes the bind fail
16038
     * if someone already has the socket -- DTL */
16039
    /* NOTE: If SO_EXCLUSIVEADDRUSE is used,
16040
     * Windows might need a few seconds before
16041
     * the same port can be used again in the
16042
     * same process, so a short Sleep may be
16043
     * required between mg_stop and mg_start.
16044
     */
16045
    if (setsockopt(so.sock,
16046
                   SOL_SOCKET,
16047
                   SO_EXCLUSIVEADDRUSE,
16048
                   (SOCK_OPT_TYPE)&on,
16049
                   sizeof(on))
16050
        != 0) {
16051
16052
      /* Set reuse option, but don't abort on errors. */
16053
      mg_cry_ctx_internal(
16054
          phys_ctx,
16055
          "cannot set socket option SO_EXCLUSIVEADDRUSE (entry %i)",
16056
          portsTotal);
16057
    }
16058
#else
16059
2
    if (setsockopt(so.sock,
16060
2
                   SOL_SOCKET,
16061
2
                   SO_REUSEADDR,
16062
2
                   (SOCK_OPT_TYPE)&on,
16063
2
                   sizeof(on))
16064
2
        != 0) {
16065
16066
      /* Set reuse option, but don't abort on errors. */
16067
0
      mg_cry_ctx_internal(
16068
0
          phys_ctx,
16069
0
          "cannot set socket option SO_REUSEADDR (entry %i)",
16070
0
          portsTotal);
16071
0
    }
16072
2
#endif
16073
16074
#if defined(USE_X_DOM_SOCKET)
16075
    if (ip_version == 99) {
16076
      /* Unix domain socket */
16077
    } else
16078
#endif
16079
16080
2
        if (ip_version > 4) {
16081
      /* Could be 6 for IPv6 onlyor 10 (4+6) for IPv4+IPv6 */
16082
#if defined(USE_IPV6)
16083
      if (ip_version > 6) {
16084
        if (so.lsa.sa.sa_family == AF_INET6
16085
            && setsockopt(so.sock,
16086
                          IPPROTO_IPV6,
16087
                          IPV6_V6ONLY,
16088
                          (void *)&off,
16089
                          sizeof(off))
16090
                   != 0) {
16091
16092
          /* Set IPv6 only option, but don't abort on errors. */
16093
          mg_cry_ctx_internal(phys_ctx,
16094
                              "cannot set socket option "
16095
                              "IPV6_V6ONLY=off (entry %i)",
16096
                              portsTotal);
16097
        }
16098
      } else {
16099
        if (so.lsa.sa.sa_family == AF_INET6
16100
            && setsockopt(so.sock,
16101
                          IPPROTO_IPV6,
16102
                          IPV6_V6ONLY,
16103
                          (void *)&on,
16104
                          sizeof(on))
16105
                   != 0) {
16106
16107
          /* Set IPv6 only option, but don't abort on errors. */
16108
          mg_cry_ctx_internal(phys_ctx,
16109
                              "cannot set socket option "
16110
                              "IPV6_V6ONLY=on (entry %i)",
16111
                              portsTotal);
16112
        }
16113
      }
16114
#else
16115
0
      mg_cry_ctx_internal(phys_ctx, "%s", "IPv6 not available");
16116
0
      closesocket(so.sock);
16117
0
      so.sock = INVALID_SOCKET;
16118
0
      continue;
16119
0
#endif
16120
0
    }
16121
16122
2
    if (so.lsa.sa.sa_family == AF_INET) {
16123
16124
2
      len = sizeof(so.lsa.sin);
16125
2
      if (bind(so.sock, &so.lsa.sa, len) != 0) {
16126
0
        mg_cry_ctx_internal(phys_ctx,
16127
0
                            "cannot bind to %.*s: %d (%s)",
16128
0
                            (int)vec.len,
16129
0
                            vec.ptr,
16130
0
                            (int)ERRNO,
16131
0
                            strerror(errno));
16132
0
        closesocket(so.sock);
16133
0
        so.sock = INVALID_SOCKET;
16134
0
        if (so.is_optional) {
16135
0
          portsOk++; /* it's okay if we couldn't bind, this port is
16136
                        optional anyway */
16137
0
        }
16138
0
        continue;
16139
0
      }
16140
2
    }
16141
#if defined(USE_IPV6)
16142
    else if (so.lsa.sa.sa_family == AF_INET6) {
16143
16144
      len = sizeof(so.lsa.sin6);
16145
      if (bind(so.sock, &so.lsa.sa, len) != 0) {
16146
        mg_cry_ctx_internal(phys_ctx,
16147
                            "cannot bind to IPv6 %.*s: %d (%s)",
16148
                            (int)vec.len,
16149
                            vec.ptr,
16150
                            (int)ERRNO,
16151
                            strerror(errno));
16152
        closesocket(so.sock);
16153
        so.sock = INVALID_SOCKET;
16154
        if (so.is_optional) {
16155
          portsOk++; /* it's okay if we couldn't bind, this port is
16156
                        optional anyway */
16157
        }
16158
        continue;
16159
      }
16160
    }
16161
#endif
16162
#if defined(USE_X_DOM_SOCKET)
16163
    else if (so.lsa.sa.sa_family == AF_UNIX) {
16164
16165
      len = sizeof(so.lsa.sun);
16166
      if (bind(so.sock, &so.lsa.sa, len) != 0) {
16167
        mg_cry_ctx_internal(phys_ctx,
16168
                            "cannot bind to unix socket %s: %d (%s)",
16169
                            so.lsa.sun.sun_path,
16170
                            (int)ERRNO,
16171
                            strerror(errno));
16172
        closesocket(so.sock);
16173
        so.sock = INVALID_SOCKET;
16174
        if (so.is_optional) {
16175
          portsOk++; /* it's okay if we couldn't bind, this port is
16176
                        optional anyway */
16177
        }
16178
        continue;
16179
      }
16180
    }
16181
#endif
16182
0
    else {
16183
0
      mg_cry_ctx_internal(
16184
0
          phys_ctx,
16185
0
          "cannot bind: address family not supported (entry %i)",
16186
0
          portsTotal);
16187
0
      closesocket(so.sock);
16188
0
      so.sock = INVALID_SOCKET;
16189
0
      continue;
16190
0
    }
16191
16192
2
    opt_txt = phys_ctx->dd.config[LISTEN_BACKLOG_SIZE];
16193
2
    opt_listen_backlog = strtol(opt_txt, NULL, 10);
16194
2
    if ((opt_listen_backlog > INT_MAX) || (opt_listen_backlog < 1)) {
16195
0
      mg_cry_ctx_internal(phys_ctx,
16196
0
                          "%s value \"%s\" is invalid",
16197
0
                          config_options[LISTEN_BACKLOG_SIZE].name,
16198
0
                          opt_txt);
16199
0
      closesocket(so.sock);
16200
0
      so.sock = INVALID_SOCKET;
16201
0
      continue;
16202
0
    }
16203
16204
2
    if (listen(so.sock, (int)opt_listen_backlog) != 0) {
16205
16206
0
      mg_cry_ctx_internal(phys_ctx,
16207
0
                          "cannot listen to %.*s: %d (%s)",
16208
0
                          (int)vec.len,
16209
0
                          vec.ptr,
16210
0
                          (int)ERRNO,
16211
0
                          strerror(errno));
16212
0
      closesocket(so.sock);
16213
0
      so.sock = INVALID_SOCKET;
16214
0
      continue;
16215
0
    }
16216
16217
2
    if ((getsockname(so.sock, &(usa.sa), &len) != 0)
16218
2
        || (usa.sa.sa_family != so.lsa.sa.sa_family)) {
16219
16220
0
      int err = (int)ERRNO;
16221
0
      mg_cry_ctx_internal(phys_ctx,
16222
0
                          "call to getsockname failed %.*s: %d (%s)",
16223
0
                          (int)vec.len,
16224
0
                          vec.ptr,
16225
0
                          err,
16226
0
                          strerror(errno));
16227
0
      closesocket(so.sock);
16228
0
      so.sock = INVALID_SOCKET;
16229
0
      continue;
16230
0
    }
16231
16232
    /* Update lsa port in case of random free ports */
16233
#if defined(USE_IPV6)
16234
    if (so.lsa.sa.sa_family == AF_INET6) {
16235
      so.lsa.sin6.sin6_port = usa.sin6.sin6_port;
16236
    } else
16237
#endif
16238
2
    {
16239
2
      so.lsa.sin.sin_port = usa.sin.sin_port;
16240
2
    }
16241
16242
2
    if ((ptr = (struct socket *)
16243
2
             mg_realloc_ctx(phys_ctx->listening_sockets,
16244
2
                            (phys_ctx->num_listening_sockets + 1)
16245
2
                                * sizeof(phys_ctx->listening_sockets[0]),
16246
2
                            phys_ctx))
16247
2
        == NULL) {
16248
16249
0
      mg_cry_ctx_internal(phys_ctx, "%s", "Out of memory");
16250
0
      closesocket(so.sock);
16251
0
      so.sock = INVALID_SOCKET;
16252
0
      continue;
16253
0
    }
16254
16255
    /* The +2 below includes the original +1 (for the socket we're about to
16256
     * add), plus another +1 for the thread_shutdown_notification_socket
16257
     * that we'll also want to poll() on so that mg_stop() can return
16258
     * quickly
16259
     */
16260
2
    if ((pfd = (struct mg_pollfd *)
16261
2
             mg_realloc_ctx(phys_ctx->listening_socket_fds,
16262
2
                            (phys_ctx->num_listening_sockets + 2)
16263
2
                                * sizeof(phys_ctx->listening_socket_fds[0]),
16264
2
                            phys_ctx))
16265
2
        == NULL) {
16266
16267
0
      mg_cry_ctx_internal(phys_ctx, "%s", "Out of memory");
16268
0
      closesocket(so.sock);
16269
0
      so.sock = INVALID_SOCKET;
16270
0
      mg_free(ptr);
16271
0
      continue;
16272
0
    }
16273
16274
2
    set_close_on_exec(so.sock, NULL, phys_ctx);
16275
2
    phys_ctx->listening_sockets = ptr;
16276
2
    phys_ctx->listening_sockets[phys_ctx->num_listening_sockets] = so;
16277
2
    phys_ctx->listening_socket_fds = pfd;
16278
2
    phys_ctx->num_listening_sockets++;
16279
2
    portsOk++;
16280
2
  }
16281
16282
2
  if (portsOk != portsTotal) {
16283
0
    close_all_listening_sockets(phys_ctx);
16284
0
    portsOk = 0;
16285
0
  }
16286
16287
2
  return portsOk;
16288
2
}
16289
16290
16291
static const char *
16292
header_val(const struct mg_connection *conn, const char *header)
16293
0
{
16294
0
  const char *header_value;
16295
16296
0
  if ((header_value = mg_get_header(conn, header)) == NULL) {
16297
0
    return "-";
16298
0
  } else {
16299
0
    return header_value;
16300
0
  }
16301
0
}
16302
16303
16304
#if defined(MG_EXTERNAL_FUNCTION_log_access)
16305
#include "external_log_access.inl"
16306
#elif !defined(NO_FILESYSTEMS)
16307
16308
static void
16309
log_access(const struct mg_connection *conn)
16310
0
{
16311
0
  const struct mg_request_info *ri;
16312
0
  struct mg_file fi;
16313
0
  char date[64], src_addr[IP_ADDR_STR_LEN];
16314
#if defined(REENTRANT_TIME)
16315
  struct tm _tm;
16316
  struct tm *tm = &_tm;
16317
#else
16318
0
  struct tm *tm;
16319
0
#endif
16320
16321
0
  const char *referer;
16322
0
  const char *user_agent;
16323
16324
0
  char log_buf[4096];
16325
16326
0
  if (!conn || !conn->dom_ctx) {
16327
0
    return;
16328
0
  }
16329
16330
  /* Set log message to "empty" */
16331
0
  log_buf[0] = 0;
16332
16333
#if defined(USE_LUA)
16334
  if (conn->phys_ctx->lua_bg_log_available) {
16335
    int ret;
16336
    struct mg_context *ctx = conn->phys_ctx;
16337
    lua_State *lstate = (lua_State *)ctx->lua_background_state;
16338
    pthread_mutex_lock(&ctx->lua_bg_mutex);
16339
    /* call "log()" in Lua */
16340
    lua_getglobal(lstate, "log");
16341
    prepare_lua_request_info_inner(conn, lstate);
16342
    push_lua_response_log_data(conn, lstate);
16343
16344
    ret = lua_pcall(lstate, /* args */ 2, /* results */ 1, 0);
16345
    if (ret == 0) {
16346
      int t = lua_type(lstate, -1);
16347
      if (t == LUA_TBOOLEAN) {
16348
        if (lua_toboolean(lstate, -1) == 0) {
16349
          /* log() returned false: do not log */
16350
          pthread_mutex_unlock(&ctx->lua_bg_mutex);
16351
          return;
16352
        }
16353
        /* log returned true: continue logging */
16354
      } else if (t == LUA_TSTRING) {
16355
        size_t len;
16356
        const char *txt = lua_tolstring(lstate, -1, &len);
16357
        if ((len == 0) || (*txt == 0)) {
16358
          /* log() returned empty string: do not log */
16359
          pthread_mutex_unlock(&ctx->lua_bg_mutex);
16360
          return;
16361
        }
16362
        /* Copy test from Lua into log_buf */
16363
        if (len >= sizeof(log_buf)) {
16364
          len = sizeof(log_buf) - 1;
16365
        }
16366
        memcpy(log_buf, txt, len);
16367
        log_buf[len] = 0;
16368
      }
16369
    } else {
16370
      lua_cry(conn, ret, lstate, "lua_background_script", "log");
16371
    }
16372
    pthread_mutex_unlock(&ctx->lua_bg_mutex);
16373
  }
16374
#endif
16375
16376
0
  if (conn->dom_ctx->config[ACCESS_LOG_FILE] != NULL) {
16377
0
    if (mg_fopen(conn,
16378
0
                 conn->dom_ctx->config[ACCESS_LOG_FILE],
16379
0
                 MG_FOPEN_MODE_APPEND,
16380
0
                 &fi)
16381
0
        == 0) {
16382
0
      fi.access.fp = NULL;
16383
0
    }
16384
0
  } else {
16385
0
    fi.access.fp = NULL;
16386
0
  }
16387
16388
  /* Log is written to a file and/or a callback. If both are not set,
16389
   * executing the rest of the function is pointless. */
16390
0
  if ((fi.access.fp == NULL)
16391
0
      && (conn->phys_ctx->callbacks.log_access == NULL)) {
16392
0
    return;
16393
0
  }
16394
16395
  /* If we did not get a log message from Lua, create it here. */
16396
0
  if (!log_buf[0]) {
16397
#if defined(REENTRANT_TIME)
16398
    localtime_r(&conn->conn_birth_time, tm);
16399
#else
16400
0
    tm = localtime(&conn->conn_birth_time);
16401
0
#endif
16402
0
    if (tm != NULL) {
16403
0
      strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", tm);
16404
0
    } else {
16405
0
      mg_strlcpy(date, "01/Jan/1970:00:00:00 +0000", sizeof(date));
16406
0
    }
16407
16408
0
    ri = &conn->request_info;
16409
16410
0
    sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
16411
0
    referer = header_val(conn, "Referer");
16412
0
    user_agent = header_val(conn, "User-Agent");
16413
16414
0
    mg_snprintf(conn,
16415
0
                NULL, /* Ignore truncation in access log */
16416
0
                log_buf,
16417
0
                sizeof(log_buf),
16418
0
                "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d %" INT64_FMT
16419
0
                " %s %s",
16420
0
                src_addr,
16421
0
                (ri->remote_user == NULL) ? "-" : ri->remote_user,
16422
0
                date,
16423
0
                ri->request_method ? ri->request_method : "-",
16424
0
                ri->request_uri ? ri->request_uri : "-",
16425
0
                ri->query_string ? "?" : "",
16426
0
                ri->query_string ? ri->query_string : "",
16427
0
                ri->http_version,
16428
0
                conn->status_code,
16429
0
                conn->num_bytes_sent,
16430
0
                referer,
16431
0
                user_agent);
16432
0
  }
16433
16434
  /* Here we have a log message in log_buf. Call the callback */
16435
0
  if (conn->phys_ctx->callbacks.log_access) {
16436
0
    if (conn->phys_ctx->callbacks.log_access(conn, log_buf)) {
16437
      /* do not log if callback returns non-zero */
16438
0
      if (fi.access.fp) {
16439
0
        mg_fclose(&fi.access);
16440
0
      }
16441
0
      return;
16442
0
    }
16443
0
  }
16444
16445
  /* Store in file */
16446
0
  if (fi.access.fp) {
16447
0
    int ok = 1;
16448
0
    flockfile(fi.access.fp);
16449
0
    if (fprintf(fi.access.fp, "%s\n", log_buf) < 1) {
16450
0
      ok = 0;
16451
0
    }
16452
0
    if (fflush(fi.access.fp) != 0) {
16453
0
      ok = 0;
16454
0
    }
16455
0
    funlockfile(fi.access.fp);
16456
0
    if (mg_fclose(&fi.access) != 0) {
16457
0
      ok = 0;
16458
0
    }
16459
0
    if (!ok) {
16460
0
      mg_cry_internal(conn,
16461
0
                      "Error writing log file %s",
16462
0
                      conn->dom_ctx->config[ACCESS_LOG_FILE]);
16463
0
    }
16464
0
  }
16465
0
}
16466
#else
16467
#error "Either enable filesystems or provide a custom log_access implementation"
16468
#endif /* Externally provided function */
16469
16470
16471
/* Verify given socket address against the ACL.
16472
 * Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
16473
 */
16474
static int
16475
check_acl(struct mg_context *phys_ctx, const union usa *sa)
16476
2
{
16477
2
  int allowed, flag, matched;
16478
2
  struct vec vec;
16479
16480
2
  if (phys_ctx) {
16481
2
    const char *list = phys_ctx->dd.config[ACCESS_CONTROL_LIST];
16482
16483
    /* If any ACL is set, deny by default */
16484
2
    allowed = (list == NULL) ? '+' : '-';
16485
16486
2
    while ((list = next_option(list, &vec, NULL)) != NULL) {
16487
0
      flag = vec.ptr[0];
16488
0
      matched = -1;
16489
0
      if ((vec.len > 0) && ((flag == '+') || (flag == '-'))) {
16490
0
        vec.ptr++;
16491
0
        vec.len--;
16492
0
        matched = parse_match_net(&vec, sa, 1);
16493
0
      }
16494
0
      if (matched < 0) {
16495
0
        mg_cry_ctx_internal(phys_ctx,
16496
0
                            "%s: subnet must be [+|-]IP-addr[/x]",
16497
0
                            __func__);
16498
0
        return -1;
16499
0
      }
16500
0
      if (matched) {
16501
0
        allowed = flag;
16502
0
      }
16503
0
    }
16504
16505
2
    return allowed == '+';
16506
2
  }
16507
0
  return -1;
16508
2
}
16509
16510
16511
#if !defined(_WIN32) && !defined(__ZEPHYR__)
16512
static int
16513
set_uid_option(struct mg_context *phys_ctx)
16514
2
{
16515
2
  int success = 0;
16516
16517
2
  if (phys_ctx) {
16518
    /* We are currently running as curr_uid. */
16519
2
    const uid_t curr_uid = getuid();
16520
    /* If set, we want to run as run_as_user. */
16521
2
    const char *run_as_user = phys_ctx->dd.config[RUN_AS_USER];
16522
2
    const struct passwd *to_pw = NULL;
16523
16524
2
    if ((run_as_user != NULL) && (to_pw = getpwnam(run_as_user)) == NULL) {
16525
      /* run_as_user does not exist on the system. We can't proceed
16526
       * further. */
16527
0
      mg_cry_ctx_internal(phys_ctx,
16528
0
                          "%s: unknown user [%s]",
16529
0
                          __func__,
16530
0
                          run_as_user);
16531
2
    } else if ((run_as_user == NULL) || (curr_uid == to_pw->pw_uid)) {
16532
      /* There was either no request to change user, or we're already
16533
       * running as run_as_user. Nothing else to do.
16534
       */
16535
2
      success = 1;
16536
2
    } else {
16537
      /* Valid change request.  */
16538
0
      if (setgid(to_pw->pw_gid) == -1) {
16539
0
        mg_cry_ctx_internal(phys_ctx,
16540
0
                            "%s: setgid(%s): %s",
16541
0
                            __func__,
16542
0
                            run_as_user,
16543
0
                            strerror(errno));
16544
0
      } else if (setgroups(0, NULL) == -1) {
16545
0
        mg_cry_ctx_internal(phys_ctx,
16546
0
                            "%s: setgroups(): %s",
16547
0
                            __func__,
16548
0
                            strerror(errno));
16549
0
      } else if (setuid(to_pw->pw_uid) == -1) {
16550
0
        mg_cry_ctx_internal(phys_ctx,
16551
0
                            "%s: setuid(%s): %s",
16552
0
                            __func__,
16553
0
                            run_as_user,
16554
0
                            strerror(errno));
16555
0
      } else {
16556
0
        success = 1;
16557
0
      }
16558
0
    }
16559
2
  }
16560
16561
2
  return success;
16562
2
}
16563
#endif /* !_WIN32 */
16564
16565
16566
static void
16567
tls_dtor(void *key)
16568
0
{
16569
0
  struct mg_workerTLS *tls = (struct mg_workerTLS *)key;
16570
  /* key == pthread_getspecific(sTlsKey); */
16571
16572
0
  if (tls) {
16573
0
    if (tls->is_master == 2) {
16574
0
      tls->is_master = -3; /* Mark memory as dead */
16575
0
      mg_free(tls);
16576
0
    }
16577
0
  }
16578
0
  pthread_setspecific(sTlsKey, NULL);
16579
0
}
16580
16581
16582
#if defined(USE_MBEDTLS)
16583
/* Check if SSL is required.
16584
 * If so, set up ctx->ssl_ctx pointer. */
16585
static int
16586
mg_sslctx_init(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
16587
{
16588
  if (!phys_ctx) {
16589
    return 0;
16590
  }
16591
16592
  if (!dom_ctx) {
16593
    dom_ctx = &(phys_ctx->dd);
16594
  }
16595
16596
  if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
16597
    /* No SSL port is set. No need to setup SSL. */
16598
    return 1;
16599
  }
16600
16601
  dom_ctx->ssl_ctx = (SSL_CTX *)mg_calloc(1, sizeof(*dom_ctx->ssl_ctx));
16602
  if (dom_ctx->ssl_ctx == NULL) {
16603
    fprintf(stderr, "ssl_ctx malloc failed\n");
16604
    return 0;
16605
  }
16606
16607
  return mbed_sslctx_init(dom_ctx->ssl_ctx, dom_ctx->config[SSL_CERTIFICATE])
16608
                 == 0
16609
             ? 1
16610
             : 0;
16611
}
16612
16613
#elif !defined(NO_SSL)
16614
16615
static int ssl_use_pem_file(struct mg_context *phys_ctx,
16616
                            struct mg_domain_context *dom_ctx,
16617
                            const char *pem,
16618
                            const char *chain);
16619
static const char *ssl_error(void);
16620
16621
16622
static int
16623
refresh_trust(struct mg_connection *conn)
16624
0
{
16625
0
  struct stat cert_buf;
16626
0
  int64_t t = 0;
16627
0
  const char *pem;
16628
0
  const char *chain;
16629
0
  int should_verify_peer;
16630
16631
0
  if ((pem = conn->dom_ctx->config[SSL_CERTIFICATE]) == NULL) {
16632
    /* If pem is NULL and conn->phys_ctx->callbacks.init_ssl is not,
16633
     * refresh_trust still can not work. */
16634
0
    return 0;
16635
0
  }
16636
0
  chain = conn->dom_ctx->config[SSL_CERTIFICATE_CHAIN];
16637
0
  if (chain == NULL) {
16638
    /* pem is not NULL here */
16639
0
    chain = pem;
16640
0
  }
16641
0
  if (*chain == 0) {
16642
0
    chain = NULL;
16643
0
  }
16644
16645
0
  if (stat(pem, &cert_buf) != -1) {
16646
0
    t = (int64_t)cert_buf.st_mtime;
16647
0
  }
16648
16649
0
  mg_lock_context(conn->phys_ctx);
16650
0
  if ((t != 0) && (conn->dom_ctx->ssl_cert_last_mtime != t)) {
16651
0
    conn->dom_ctx->ssl_cert_last_mtime = t;
16652
16653
0
    should_verify_peer = 0;
16654
0
    if (conn->dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) {
16655
0
      if (mg_strcasecmp(conn->dom_ctx->config[SSL_DO_VERIFY_PEER], "yes")
16656
0
          == 0) {
16657
0
        should_verify_peer = 1;
16658
0
      } else if (mg_strcasecmp(conn->dom_ctx->config[SSL_DO_VERIFY_PEER],
16659
0
                               "optional")
16660
0
                 == 0) {
16661
0
        should_verify_peer = 1;
16662
0
      }
16663
0
    }
16664
16665
0
    if (should_verify_peer) {
16666
0
      char *ca_path = conn->dom_ctx->config[SSL_CA_PATH];
16667
0
      char *ca_file = conn->dom_ctx->config[SSL_CA_FILE];
16668
0
      if (SSL_CTX_load_verify_locations(conn->dom_ctx->ssl_ctx,
16669
0
                                        ca_file,
16670
0
                                        ca_path)
16671
0
          != 1) {
16672
0
        mg_unlock_context(conn->phys_ctx);
16673
0
        mg_cry_ctx_internal(
16674
0
            conn->phys_ctx,
16675
0
            "SSL_CTX_load_verify_locations error: %s "
16676
0
            "ssl_verify_peer requires setting "
16677
0
            "either ssl_ca_path or ssl_ca_file. Is any of them "
16678
0
            "present in "
16679
0
            "the .conf file?",
16680
0
            ssl_error());
16681
0
        return 0;
16682
0
      }
16683
0
    }
16684
16685
0
    if (ssl_use_pem_file(conn->phys_ctx, conn->dom_ctx, pem, chain) == 0) {
16686
0
      mg_unlock_context(conn->phys_ctx);
16687
0
      return 0;
16688
0
    }
16689
0
  }
16690
0
  mg_unlock_context(conn->phys_ctx);
16691
16692
0
  return 1;
16693
0
}
16694
16695
#if defined(OPENSSL_API_1_1)
16696
#else
16697
static pthread_mutex_t *ssl_mutexes;
16698
#endif /* OPENSSL_API_1_1 */
16699
16700
static int
16701
sslize(struct mg_connection *conn,
16702
       int (*func)(SSL *),
16703
       const struct mg_client_options *client_options)
16704
0
{
16705
0
  int ret, err;
16706
0
  int short_trust;
16707
0
  unsigned timeout = 1024;
16708
0
  unsigned i;
16709
16710
0
  if (!conn) {
16711
0
    return 0;
16712
0
  }
16713
16714
0
  short_trust =
16715
0
      (conn->dom_ctx->config[SSL_SHORT_TRUST] != NULL)
16716
0
      && (mg_strcasecmp(conn->dom_ctx->config[SSL_SHORT_TRUST], "yes") == 0);
16717
16718
0
  if (short_trust) {
16719
0
    int trust_ret = refresh_trust(conn);
16720
0
    if (!trust_ret) {
16721
0
      return trust_ret;
16722
0
    }
16723
0
  }
16724
16725
0
  mg_lock_context(conn->phys_ctx);
16726
0
  conn->ssl = SSL_new(conn->dom_ctx->ssl_ctx);
16727
0
  mg_unlock_context(conn->phys_ctx);
16728
0
  if (conn->ssl == NULL) {
16729
0
    mg_cry_internal(conn, "sslize error: %s", ssl_error());
16730
0
    OPENSSL_REMOVE_THREAD_STATE();
16731
0
    return 0;
16732
0
  }
16733
0
  SSL_set_app_data(conn->ssl, (char *)conn);
16734
16735
0
  ret = SSL_set_fd(conn->ssl, conn->client.sock);
16736
0
  if (ret != 1) {
16737
0
    mg_cry_internal(conn, "sslize error: %s", ssl_error());
16738
0
    SSL_free(conn->ssl);
16739
0
    conn->ssl = NULL;
16740
0
    OPENSSL_REMOVE_THREAD_STATE();
16741
0
    return 0;
16742
0
  }
16743
16744
0
  if (client_options) {
16745
0
    if (client_options->host_name) {
16746
0
      SSL_set_tlsext_host_name(conn->ssl, client_options->host_name);
16747
0
    }
16748
0
  }
16749
16750
  /* Reuse the request timeout for the SSL_Accept/SSL_connect timeout  */
16751
0
  if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
16752
    /* NOTE: The loop below acts as a back-off, so we can end
16753
     * up sleeping for more (or less) than the REQUEST_TIMEOUT. */
16754
0
    int to = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]);
16755
0
    if (to >= 0) {
16756
0
      timeout = (unsigned)to;
16757
0
    }
16758
0
  }
16759
16760
  /* SSL functions may fail and require to be called again:
16761
   * see https://www.openssl.org/docs/manmaster/ssl/SSL_get_error.html
16762
   * Here "func" could be SSL_connect or SSL_accept. */
16763
0
  for (i = 0; i <= timeout; i += 50) {
16764
0
    ERR_clear_error();
16765
    /* conn->dom_ctx may be changed here (see ssl_servername_callback) */
16766
0
    ret = func(conn->ssl);
16767
0
    if (ret != 1) {
16768
0
      err = SSL_get_error(conn->ssl, ret);
16769
0
      if ((err == SSL_ERROR_WANT_CONNECT)
16770
0
          || (err == SSL_ERROR_WANT_ACCEPT)
16771
0
          || (err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)
16772
0
          || (err == SSL_ERROR_WANT_X509_LOOKUP)) {
16773
0
        if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
16774
          /* Don't wait if the server is going to be stopped. */
16775
0
          break;
16776
0
        }
16777
0
        if (err == SSL_ERROR_WANT_X509_LOOKUP) {
16778
          /* Simply retry the function call. */
16779
0
          mg_sleep(50);
16780
0
        } else {
16781
          /* Need to retry the function call "later".
16782
           * See https://linux.die.net/man/3/ssl_get_error
16783
           * This is typical for non-blocking sockets. */
16784
0
          struct mg_pollfd pfd[2];
16785
0
          int pollres;
16786
0
          pfd[0].fd = conn->client.sock;
16787
0
          pfd[0].events = ((err == SSL_ERROR_WANT_CONNECT)
16788
0
                           || (err == SSL_ERROR_WANT_WRITE))
16789
0
                              ? POLLOUT
16790
0
                              : POLLIN;
16791
16792
0
          pfd[1].fd =
16793
0
              conn->phys_ctx->thread_shutdown_notification_socket;
16794
0
          pfd[1].events = POLLIN;
16795
0
          pollres = mg_poll(pfd, 2, 50, &(conn->phys_ctx->stop_flag));
16796
0
          if (pollres < 0) {
16797
            /* Break if error occurred (-1)
16798
             * or server shutdown (-2) */
16799
0
            break;
16800
0
          }
16801
0
        }
16802
16803
0
      } else if (err == SSL_ERROR_SYSCALL) {
16804
        /* This is an IO error. Look at errno. */
16805
0
        mg_cry_internal(conn, "SSL syscall error %i", ERRNO);
16806
0
        break;
16807
16808
0
      } else {
16809
        /* This is an SSL specific error, e.g. SSL_ERROR_SSL */
16810
0
        mg_cry_internal(conn, "sslize error: %s", ssl_error());
16811
0
        break;
16812
0
      }
16813
16814
0
    } else {
16815
      /* success */
16816
0
      break;
16817
0
    }
16818
0
  }
16819
0
  ERR_clear_error();
16820
16821
0
  if (ret != 1) {
16822
0
    SSL_free(conn->ssl);
16823
0
    conn->ssl = NULL;
16824
0
    OPENSSL_REMOVE_THREAD_STATE();
16825
0
    return 0;
16826
0
  }
16827
16828
0
  return 1;
16829
0
}
16830
16831
16832
/* Return OpenSSL error message (from CRYPTO lib) */
16833
static const char *
16834
ssl_error(void)
16835
0
{
16836
0
  unsigned long err;
16837
0
  err = ERR_get_error();
16838
0
  return ((err == 0) ? "" : ERR_error_string(err, NULL));
16839
0
}
16840
16841
16842
static int
16843
hexdump2string(void *mem, int memlen, char *buf, int buflen)
16844
0
{
16845
0
  int i;
16846
0
  const char hexdigit[] = "0123456789abcdef";
16847
16848
0
  if ((memlen <= 0) || (buflen <= 0)) {
16849
0
    return 0;
16850
0
  }
16851
0
  if (buflen < (3 * memlen)) {
16852
0
    return 0;
16853
0
  }
16854
16855
0
  for (i = 0; i < memlen; i++) {
16856
0
    if (i > 0) {
16857
0
      buf[3 * i - 1] = ' ';
16858
0
    }
16859
0
    buf[3 * i] = hexdigit[(((uint8_t *)mem)[i] >> 4) & 0xF];
16860
0
    buf[3 * i + 1] = hexdigit[((uint8_t *)mem)[i] & 0xF];
16861
0
  }
16862
0
  buf[3 * memlen - 1] = 0;
16863
16864
0
  return 1;
16865
0
}
16866
16867
16868
static int
16869
ssl_get_client_cert_info(const struct mg_connection *conn,
16870
                         struct mg_client_cert *client_cert)
16871
0
{
16872
0
  X509 *cert = SSL_get_peer_certificate(conn->ssl);
16873
0
  if (cert) {
16874
0
    char str_buf[1024];
16875
0
    unsigned char buf[256];
16876
0
    char *str_serial = NULL;
16877
0
    unsigned int ulen;
16878
0
    int ilen;
16879
0
    unsigned char *tmp_buf;
16880
0
    unsigned char *tmp_p;
16881
16882
    /* Handle to algorithm used for fingerprint */
16883
0
    const EVP_MD *digest = EVP_get_digestbyname("sha1");
16884
16885
    /* Get Subject and issuer */
16886
0
    X509_NAME *subj = X509_get_subject_name(cert);
16887
0
    X509_NAME *iss = X509_get_issuer_name(cert);
16888
16889
    /* Get serial number */
16890
0
    ASN1_INTEGER *serial = X509_get_serialNumber(cert);
16891
16892
    /* Translate serial number to a hex string */
16893
0
    BIGNUM *serial_bn = ASN1_INTEGER_to_BN(serial, NULL);
16894
0
    if (serial_bn) {
16895
0
      str_serial = BN_bn2hex(serial_bn);
16896
0
      BN_free(serial_bn);
16897
0
    }
16898
0
    client_cert->serial =
16899
0
        str_serial ? mg_strdup_ctx(str_serial, conn->phys_ctx) : NULL;
16900
16901
    /* Translate subject and issuer to a string */
16902
0
    (void)X509_NAME_oneline(subj, str_buf, (int)sizeof(str_buf));
16903
0
    client_cert->subject = mg_strdup_ctx(str_buf, conn->phys_ctx);
16904
0
    (void)X509_NAME_oneline(iss, str_buf, (int)sizeof(str_buf));
16905
0
    client_cert->issuer = mg_strdup_ctx(str_buf, conn->phys_ctx);
16906
16907
    /* Calculate SHA1 fingerprint and store as a hex string */
16908
0
    ulen = 0;
16909
16910
    /* ASN1_digest is deprecated. Do the calculation manually,
16911
     * using EVP_Digest. */
16912
0
    ilen = i2d_X509(cert, NULL);
16913
0
    tmp_buf = (ilen > 0)
16914
0
                  ? (unsigned char *)mg_malloc_ctx((unsigned)ilen + 1,
16915
0
                                                   conn->phys_ctx)
16916
0
                  : NULL;
16917
0
    if (tmp_buf) {
16918
0
      tmp_p = tmp_buf;
16919
0
      (void)i2d_X509(cert, &tmp_p);
16920
0
      if (!EVP_Digest(
16921
0
              tmp_buf, (unsigned)ilen, buf, &ulen, digest, NULL)) {
16922
0
        ulen = 0;
16923
0
      }
16924
0
      mg_free(tmp_buf);
16925
0
    }
16926
16927
0
    if (!hexdump2string(buf, (int)ulen, str_buf, (int)sizeof(str_buf))) {
16928
0
      *str_buf = 0;
16929
0
    }
16930
0
    client_cert->finger = mg_strdup_ctx(str_buf, conn->phys_ctx);
16931
16932
0
    client_cert->peer_cert = (void *)cert;
16933
16934
    /* Strings returned from bn_bn2hex must be freed using OPENSSL_free,
16935
     * see https://linux.die.net/man/3/bn_bn2hex */
16936
0
    OPENSSL_free(str_serial);
16937
0
    return 1;
16938
0
  }
16939
0
  return 0;
16940
0
}
16941
16942
16943
#if defined(OPENSSL_API_1_1)
16944
#else
16945
static void
16946
ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
16947
{
16948
  (void)line;
16949
  (void)file;
16950
16951
  if (mode & 1) {
16952
    /* 1 is CRYPTO_LOCK */
16953
    (void)pthread_mutex_lock(&ssl_mutexes[mutex_num]);
16954
  } else {
16955
    (void)pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
16956
  }
16957
}
16958
#endif /* OPENSSL_API_1_1 */
16959
16960
16961
#if !defined(NO_SSL_DL)
16962
/* Load a DLL/Shared Object with a TLS/SSL implementation. */
16963
static void *
16964
load_tls_dll(char *ebuf,
16965
             size_t ebuf_len,
16966
             const char *dll_name,
16967
             struct ssl_func *sw,
16968
             int *feature_missing)
16969
0
{
16970
0
  union {
16971
0
    void *p;
16972
0
    void (*fp)(void);
16973
0
  } u;
16974
0
  void *dll_handle;
16975
0
  struct ssl_func *fp;
16976
0
  int ok;
16977
0
  int truncated = 0;
16978
16979
0
  if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
16980
0
    mg_snprintf(NULL,
16981
0
                NULL, /* No truncation check for ebuf */
16982
0
                ebuf,
16983
0
                ebuf_len,
16984
0
                "%s: cannot load %s",
16985
0
                __func__,
16986
0
                dll_name);
16987
0
    return NULL;
16988
0
  }
16989
16990
0
  ok = 1;
16991
0
  for (fp = sw; fp->name != NULL; fp++) {
16992
#if defined(_WIN32)
16993
    /* GetProcAddress() returns pointer to function */
16994
    u.fp = (void (*)(void))dlsym(dll_handle, fp->name);
16995
#else
16996
    /* dlsym() on UNIX returns void *. ISO C forbids casts of data
16997
     * pointers to function pointers. We need to use a union to make a
16998
     * cast. */
16999
0
    u.p = dlsym(dll_handle, fp->name);
17000
0
#endif /* _WIN32 */
17001
17002
    /* Set pointer (might be NULL) */
17003
0
    fp->ptr = u.fp;
17004
17005
0
    if (u.fp == NULL) {
17006
0
      DEBUG_TRACE("Missing function: %s\n", fp->name);
17007
0
      if (feature_missing) {
17008
0
        feature_missing[fp->required]++;
17009
0
      }
17010
0
      if (fp->required == TLS_Mandatory) {
17011
        /* Mandatory function is missing */
17012
0
        if (ok) {
17013
          /* This is the first missing function.
17014
           * Create a new error message. */
17015
0
          mg_snprintf(NULL,
17016
0
                      &truncated,
17017
0
                      ebuf,
17018
0
                      ebuf_len,
17019
0
                      "%s: %s: cannot find %s",
17020
0
                      __func__,
17021
0
                      dll_name,
17022
0
                      fp->name);
17023
0
          ok = 0;
17024
0
        } else {
17025
          /* This is yet anothermissing function.
17026
           * Append existing error message. */
17027
0
          size_t cur_len = strlen(ebuf);
17028
0
          if (!truncated && ((ebuf_len - cur_len) > 3)) {
17029
0
            mg_snprintf(NULL,
17030
0
                        &truncated,
17031
0
                        ebuf + cur_len,
17032
0
                        ebuf_len - cur_len - 3,
17033
0
                        ", %s",
17034
0
                        fp->name);
17035
0
            if (truncated) {
17036
              /* If truncated, add "..." */
17037
0
              strcat(ebuf, "...");
17038
0
            }
17039
0
          }
17040
0
        }
17041
0
      }
17042
0
    }
17043
0
  }
17044
17045
0
  if (!ok) {
17046
0
    (void)dlclose(dll_handle);
17047
0
    return NULL;
17048
0
  }
17049
17050
0
  return dll_handle;
17051
0
}
17052
17053
17054
static void *ssllib_dll_handle;    /* Store the ssl library handle. */
17055
static void *cryptolib_dll_handle; /* Store the crypto library handle. */
17056
17057
#endif /* NO_SSL_DL */
17058
17059
17060
#if defined(SSL_ALREADY_INITIALIZED)
17061
static volatile ptrdiff_t cryptolib_users =
17062
    1; /* Reference counter for crypto library. */
17063
#else
17064
static volatile ptrdiff_t cryptolib_users =
17065
    0; /* Reference counter for crypto library. */
17066
#endif
17067
17068
17069
static int
17070
initialize_openssl(char *ebuf, size_t ebuf_len)
17071
0
{
17072
#if !defined(OPENSSL_API_1_1) && !defined(OPENSSL_API_3_0)
17073
  int i, num_locks;
17074
  size_t size;
17075
#endif
17076
17077
0
  if (ebuf_len > 0) {
17078
0
    ebuf[0] = 0;
17079
0
  }
17080
17081
0
#if !defined(NO_SSL_DL)
17082
0
  if (!cryptolib_dll_handle) {
17083
0
    memset(tls_feature_missing, 0, sizeof(tls_feature_missing));
17084
0
    cryptolib_dll_handle = load_tls_dll(
17085
0
        ebuf, ebuf_len, CRYPTO_LIB, crypto_sw, tls_feature_missing);
17086
0
    if (!cryptolib_dll_handle) {
17087
0
      mg_snprintf(NULL,
17088
0
                  NULL, /* No truncation check for ebuf */
17089
0
                  ebuf,
17090
0
                  ebuf_len,
17091
0
                  "%s: error loading library %s",
17092
0
                  __func__,
17093
0
                  CRYPTO_LIB);
17094
0
      DEBUG_TRACE("%s", ebuf);
17095
0
      return 0;
17096
0
    }
17097
0
  }
17098
0
#endif /* NO_SSL_DL */
17099
17100
0
  if (mg_atomic_inc(&cryptolib_users) > 1) {
17101
0
    return 1;
17102
0
  }
17103
17104
#if !defined(OPENSSL_API_1_1) && !defined(OPENSSL_API_3_0)
17105
  /* Initialize locking callbacks, needed for thread safety.
17106
   * http://www.openssl.org/support/faq.html#PROG1
17107
   */
17108
  num_locks = CRYPTO_num_locks();
17109
  if (num_locks < 0) {
17110
    num_locks = 0;
17111
  }
17112
  size = sizeof(pthread_mutex_t) * ((size_t)(num_locks));
17113
17114
  /* allocate mutex array, if required */
17115
  if (num_locks == 0) {
17116
    /* No mutex array required */
17117
    ssl_mutexes = NULL;
17118
  } else {
17119
    /* Mutex array required - allocate it */
17120
    ssl_mutexes = (pthread_mutex_t *)mg_malloc(size);
17121
17122
    /* Check OOM */
17123
    if (ssl_mutexes == NULL) {
17124
      mg_snprintf(NULL,
17125
                  NULL, /* No truncation check for ebuf */
17126
                  ebuf,
17127
                  ebuf_len,
17128
                  "%s: cannot allocate mutexes: %s",
17129
                  __func__,
17130
                  ssl_error());
17131
      DEBUG_TRACE("%s", ebuf);
17132
      return 0;
17133
    }
17134
17135
    /* initialize mutex array */
17136
    for (i = 0; i < num_locks; i++) {
17137
      if (0 != pthread_mutex_init(&ssl_mutexes[i], &pthread_mutex_attr)) {
17138
        mg_snprintf(NULL,
17139
                    NULL, /* No truncation check for ebuf */
17140
                    ebuf,
17141
                    ebuf_len,
17142
                    "%s: error initializing mutex %i of %i",
17143
                    __func__,
17144
                    i,
17145
                    num_locks);
17146
        DEBUG_TRACE("%s", ebuf);
17147
        mg_free(ssl_mutexes);
17148
        return 0;
17149
      }
17150
    }
17151
  }
17152
17153
  CRYPTO_set_locking_callback(&ssl_locking_callback);
17154
  CRYPTO_set_id_callback(&mg_current_thread_id);
17155
#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
17156
17157
0
#if !defined(NO_SSL_DL)
17158
0
  if (!ssllib_dll_handle) {
17159
0
    ssllib_dll_handle =
17160
0
        load_tls_dll(ebuf, ebuf_len, SSL_LIB, ssl_sw, tls_feature_missing);
17161
0
    if (!ssllib_dll_handle) {
17162
#if !defined(OPENSSL_API_1_1)
17163
      mg_free(ssl_mutexes);
17164
#endif
17165
0
      DEBUG_TRACE("%s", ebuf);
17166
0
      return 0;
17167
0
    }
17168
0
  }
17169
0
#endif /* NO_SSL_DL */
17170
17171
0
#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0))                     \
17172
0
    && !defined(NO_SSL_DL)
17173
  /* Initialize SSL library */
17174
0
  OPENSSL_init_ssl(0, NULL);
17175
0
  OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS
17176
0
                       | OPENSSL_INIT_LOAD_CRYPTO_STRINGS,
17177
0
                   NULL);
17178
#else
17179
  /* Initialize SSL library */
17180
  SSL_library_init();
17181
  SSL_load_error_strings();
17182
#endif
17183
17184
0
  return 1;
17185
0
}
17186
17187
17188
static int
17189
ssl_use_pem_file(struct mg_context *phys_ctx,
17190
                 struct mg_domain_context *dom_ctx,
17191
                 const char *pem,
17192
                 const char *chain)
17193
0
{
17194
0
  if (SSL_CTX_use_certificate_file(dom_ctx->ssl_ctx, pem, 1) == 0) {
17195
0
    mg_cry_ctx_internal(phys_ctx,
17196
0
                        "%s: cannot open certificate file %s: %s",
17197
0
                        __func__,
17198
0
                        pem,
17199
0
                        ssl_error());
17200
0
    return 0;
17201
0
  }
17202
17203
  /* could use SSL_CTX_set_default_passwd_cb_userdata */
17204
0
  if (SSL_CTX_use_PrivateKey_file(dom_ctx->ssl_ctx, pem, 1) == 0) {
17205
0
    mg_cry_ctx_internal(phys_ctx,
17206
0
                        "%s: cannot open private key file %s: %s",
17207
0
                        __func__,
17208
0
                        pem,
17209
0
                        ssl_error());
17210
0
    return 0;
17211
0
  }
17212
17213
0
  if (SSL_CTX_check_private_key(dom_ctx->ssl_ctx) == 0) {
17214
0
    mg_cry_ctx_internal(phys_ctx,
17215
0
                        "%s: certificate and private key do not match: %s",
17216
0
                        __func__,
17217
0
                        pem);
17218
0
    return 0;
17219
0
  }
17220
17221
  /* In contrast to OpenSSL, wolfSSL does not support certificate
17222
   * chain files that contain private keys and certificates in
17223
   * SSL_CTX_use_certificate_chain_file.
17224
   * The CivetWeb-Server used pem-Files that contained both information.
17225
   * In order to make wolfSSL work, it is split in two files.
17226
   * One file that contains key and certificate used by the server and
17227
   * an optional chain file for the ssl stack.
17228
   */
17229
0
  if (chain) {
17230
0
    if (SSL_CTX_use_certificate_chain_file(dom_ctx->ssl_ctx, chain) == 0) {
17231
0
      mg_cry_ctx_internal(phys_ctx,
17232
0
                          "%s: cannot use certificate chain file %s: %s",
17233
0
                          __func__,
17234
0
                          chain,
17235
0
                          ssl_error());
17236
0
      return 0;
17237
0
    }
17238
0
  }
17239
0
  return 1;
17240
0
}
17241
17242
17243
#if defined(OPENSSL_API_1_1)
17244
static unsigned long
17245
ssl_get_protocol(int version_id)
17246
0
{
17247
0
  long unsigned ret = (long unsigned)SSL_OP_ALL;
17248
0
  if (version_id > 0)
17249
0
    ret |= SSL_OP_NO_SSLv2;
17250
0
  if (version_id > 1)
17251
0
    ret |= SSL_OP_NO_SSLv3;
17252
0
  if (version_id > 2)
17253
0
    ret |= SSL_OP_NO_TLSv1;
17254
0
  if (version_id > 3)
17255
0
    ret |= SSL_OP_NO_TLSv1_1;
17256
0
  if (version_id > 4)
17257
0
    ret |= SSL_OP_NO_TLSv1_2;
17258
0
#if defined(SSL_OP_NO_TLSv1_3)
17259
0
  if (version_id > 5)
17260
0
    ret |= SSL_OP_NO_TLSv1_3;
17261
0
#endif
17262
0
  return ret;
17263
0
}
17264
#else
17265
static long
17266
ssl_get_protocol(int version_id)
17267
{
17268
  unsigned long ret = (unsigned long)SSL_OP_ALL;
17269
  if (version_id > 0)
17270
    ret |= SSL_OP_NO_SSLv2;
17271
  if (version_id > 1)
17272
    ret |= SSL_OP_NO_SSLv3;
17273
  if (version_id > 2)
17274
    ret |= SSL_OP_NO_TLSv1;
17275
  if (version_id > 3)
17276
    ret |= SSL_OP_NO_TLSv1_1;
17277
  if (version_id > 4)
17278
    ret |= SSL_OP_NO_TLSv1_2;
17279
#if defined(SSL_OP_NO_TLSv1_3)
17280
  if (version_id > 5)
17281
    ret |= SSL_OP_NO_TLSv1_3;
17282
#endif
17283
  return (long)ret;
17284
}
17285
#endif /* OPENSSL_API_1_1 */
17286
17287
17288
/* SSL callback documentation:
17289
 * https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_info_callback.html
17290
 * https://wiki.openssl.org/index.php/Manual:SSL_CTX_set_info_callback(3)
17291
 * https://linux.die.net/man/3/ssl_set_info_callback */
17292
/* Note: There is no "const" for the first argument in the documentation
17293
 * examples, however some (maybe most, but not all) headers of OpenSSL
17294
 * versions / OpenSSL compatibility layers have it. Having a different
17295
 * definition will cause a warning in C and an error in C++. Use "const SSL
17296
 * *", while automatic conversion from "SSL *" works for all compilers,
17297
 * but not other way around */
17298
static void
17299
ssl_info_callback(const SSL *ssl, int what, int ret)
17300
0
{
17301
0
  (void)ret;
17302
17303
0
  if (what & SSL_CB_HANDSHAKE_START) {
17304
0
    SSL_get_app_data(ssl);
17305
0
  }
17306
0
  if (what & SSL_CB_HANDSHAKE_DONE) {
17307
    /* TODO: check for openSSL 1.1 */
17308
    //#define SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS 0x0001
17309
    // ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
17310
0
  }
17311
0
}
17312
17313
17314
static int
17315
ssl_servername_callback(SSL *ssl, int *ad, void *arg)
17316
0
{
17317
#if defined(GCC_DIAGNOSTIC)
17318
#pragma GCC diagnostic push
17319
#pragma GCC diagnostic ignored "-Wcast-align"
17320
#endif /* defined(GCC_DIAGNOSTIC) */
17321
17322
  /* We used an aligned pointer in SSL_set_app_data */
17323
0
  struct mg_connection *conn = (struct mg_connection *)SSL_get_app_data(ssl);
17324
17325
#if defined(GCC_DIAGNOSTIC)
17326
#pragma GCC diagnostic pop
17327
#endif /* defined(GCC_DIAGNOSTIC) */
17328
17329
0
  const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
17330
17331
0
  (void)ad;
17332
0
  (void)arg;
17333
17334
0
  if ((conn == NULL) || (conn->phys_ctx == NULL)) {
17335
0
    DEBUG_ASSERT(0);
17336
0
    return SSL_TLSEXT_ERR_NOACK;
17337
0
  }
17338
0
  conn->dom_ctx = &(conn->phys_ctx->dd);
17339
17340
  /* Old clients (Win XP) will not support SNI. Then, there
17341
   * is no server name available in the request - we can
17342
   * only work with the default certificate.
17343
   * Multiple HTTPS hosts on one IP+port are only possible
17344
   * with a certificate containing all alternative names.
17345
   */
17346
0
  if ((servername == NULL) || (*servername == 0)) {
17347
0
    DEBUG_TRACE("%s", "SSL connection not supporting SNI");
17348
0
    mg_lock_context(conn->phys_ctx);
17349
0
    SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx);
17350
0
    mg_unlock_context(conn->phys_ctx);
17351
0
    return SSL_TLSEXT_ERR_NOACK;
17352
0
  }
17353
17354
0
  DEBUG_TRACE("TLS connection to host %s", servername);
17355
17356
0
  while (conn->dom_ctx) {
17357
0
    if (!mg_strcasecmp(servername,
17358
0
                       conn->dom_ctx->config[AUTHENTICATION_DOMAIN])) {
17359
      /* Found matching domain */
17360
0
      DEBUG_TRACE("TLS domain %s found",
17361
0
                  conn->dom_ctx->config[AUTHENTICATION_DOMAIN]);
17362
0
      break;
17363
0
    }
17364
0
    mg_lock_context(conn->phys_ctx);
17365
0
    conn->dom_ctx = conn->dom_ctx->next;
17366
0
    mg_unlock_context(conn->phys_ctx);
17367
0
  }
17368
17369
0
  if (conn->dom_ctx == NULL) {
17370
    /* Default domain */
17371
0
    DEBUG_TRACE("TLS default domain %s used",
17372
0
                conn->phys_ctx->dd.config[AUTHENTICATION_DOMAIN]);
17373
0
    conn->dom_ctx = &(conn->phys_ctx->dd);
17374
0
  }
17375
0
  mg_lock_context(conn->phys_ctx);
17376
0
  SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx);
17377
0
  mg_unlock_context(conn->phys_ctx);
17378
0
  return SSL_TLSEXT_ERR_OK;
17379
0
}
17380
17381
17382
#if defined(USE_ALPN)
17383
static const char alpn_proto_list[] = "\x02h2\x08http/1.1\x08http/1.0";
17384
static const char *alpn_proto_order_http1[] = {alpn_proto_list + 3,
17385
                                               alpn_proto_list + 3 + 8,
17386
                                               NULL};
17387
#if defined(USE_HTTP2)
17388
static const char *alpn_proto_order_http2[] = {alpn_proto_list,
17389
                                               alpn_proto_list + 3,
17390
                                               alpn_proto_list + 3 + 8,
17391
                                               NULL};
17392
#endif
17393
17394
static int
17395
alpn_select_cb(SSL *ssl,
17396
               const unsigned char **out,
17397
               unsigned char *outlen,
17398
               const unsigned char *in,
17399
               unsigned int inlen,
17400
               void *arg)
17401
{
17402
  struct mg_domain_context *dom_ctx = (struct mg_domain_context *)arg;
17403
  unsigned int i, j, enable_http2 = 0;
17404
  const char **alpn_proto_order = alpn_proto_order_http1;
17405
17406
  struct mg_workerTLS *tls =
17407
      (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
17408
17409
  (void)ssl;
17410
17411
  if (tls == NULL) {
17412
    /* Need to store protocol in Thread Local Storage */
17413
    /* If there is no Thread Local Storage, don't use ALPN */
17414
    return SSL_TLSEXT_ERR_NOACK;
17415
  }
17416
17417
#if defined(USE_HTTP2)
17418
  enable_http2 = (0 == strcmp(dom_ctx->config[ENABLE_HTTP2], "yes"));
17419
  if (enable_http2) {
17420
    alpn_proto_order = alpn_proto_order_http2;
17421
  }
17422
#endif
17423
17424
  for (j = 0; alpn_proto_order[j] != NULL; j++) {
17425
    /* check all accepted protocols in this order */
17426
    const char *alpn_proto = alpn_proto_order[j];
17427
    /* search input for matching protocol */
17428
    for (i = 0; i < inlen; i++) {
17429
      if (!memcmp(in + i, alpn_proto, (unsigned char)alpn_proto[0])) {
17430
        *out = in + i + 1;
17431
        *outlen = in[i];
17432
        tls->alpn_proto = alpn_proto;
17433
        return SSL_TLSEXT_ERR_OK;
17434
      }
17435
    }
17436
  }
17437
17438
  /* Nothing found */
17439
  return SSL_TLSEXT_ERR_NOACK;
17440
}
17441
17442
17443
static int
17444
next_protos_advertised_cb(SSL *ssl,
17445
                          const unsigned char **data,
17446
                          unsigned int *len,
17447
                          void *arg)
17448
{
17449
  struct mg_domain_context *dom_ctx = (struct mg_domain_context *)arg;
17450
  *data = (const unsigned char *)alpn_proto_list;
17451
  *len = (unsigned int)strlen((const char *)data);
17452
17453
  (void)ssl;
17454
  (void)dom_ctx;
17455
17456
  return SSL_TLSEXT_ERR_OK;
17457
}
17458
17459
17460
static int
17461
init_alpn(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
17462
{
17463
  unsigned int alpn_len = (unsigned int)strlen((char *)alpn_proto_list);
17464
  int ret = SSL_CTX_set_alpn_protos(dom_ctx->ssl_ctx,
17465
                                    (const unsigned char *)alpn_proto_list,
17466
                                    alpn_len);
17467
  if (ret != 0) {
17468
    mg_cry_ctx_internal(phys_ctx,
17469
                        "SSL_CTX_set_alpn_protos error: %s",
17470
                        ssl_error());
17471
  }
17472
17473
  SSL_CTX_set_alpn_select_cb(dom_ctx->ssl_ctx,
17474
                             alpn_select_cb,
17475
                             (void *)dom_ctx);
17476
17477
  SSL_CTX_set_next_protos_advertised_cb(dom_ctx->ssl_ctx,
17478
                                        next_protos_advertised_cb,
17479
                                        (void *)dom_ctx);
17480
17481
  return ret;
17482
}
17483
#endif
17484
17485
17486
/* Setup SSL CTX as required by CivetWeb */
17487
static int
17488
init_ssl_ctx_impl(struct mg_context *phys_ctx,
17489
                  struct mg_domain_context *dom_ctx,
17490
                  const char *pem,
17491
                  const char *chain)
17492
0
{
17493
0
  int callback_ret;
17494
0
  int should_verify_peer;
17495
0
  int peer_certificate_optional;
17496
0
  const char *ca_path;
17497
0
  const char *ca_file;
17498
0
  int use_default_verify_paths;
17499
0
  int verify_depth;
17500
0
  struct timespec now_mt;
17501
0
  md5_byte_t ssl_context_id[16];
17502
0
  md5_state_t md5state;
17503
0
  int protocol_ver;
17504
0
  int ssl_cache_timeout;
17505
17506
0
#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0))                     \
17507
0
    && !defined(NO_SSL_DL)
17508
0
  if ((dom_ctx->ssl_ctx = SSL_CTX_new(TLS_server_method())) == NULL) {
17509
0
    mg_cry_ctx_internal(phys_ctx,
17510
0
                        "SSL_CTX_new (server) error: %s",
17511
0
                        ssl_error());
17512
0
    return 0;
17513
0
  }
17514
#else
17515
  if ((dom_ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
17516
    mg_cry_ctx_internal(phys_ctx,
17517
                        "SSL_CTX_new (server) error: %s",
17518
                        ssl_error());
17519
    return 0;
17520
  }
17521
#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
17522
17523
0
#if defined(SSL_OP_NO_TLSv1_3)
17524
0
  SSL_CTX_clear_options(dom_ctx->ssl_ctx,
17525
0
                        SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1
17526
0
                            | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2
17527
0
                            | SSL_OP_NO_TLSv1_3);
17528
#else
17529
  SSL_CTX_clear_options(dom_ctx->ssl_ctx,
17530
                        SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1
17531
                            | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);
17532
#endif
17533
17534
0
  protocol_ver = atoi(dom_ctx->config[SSL_PROTOCOL_VERSION]);
17535
0
  SSL_CTX_set_options(dom_ctx->ssl_ctx, ssl_get_protocol(protocol_ver));
17536
0
  SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_SINGLE_DH_USE);
17537
0
  SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
17538
0
  SSL_CTX_set_options(dom_ctx->ssl_ctx,
17539
0
                      SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
17540
0
  SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_NO_COMPRESSION);
17541
17542
0
#if defined(SSL_OP_NO_RENEGOTIATION)
17543
0
  SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_NO_RENEGOTIATION);
17544
0
#endif
17545
17546
0
#if !defined(NO_SSL_DL)
17547
0
  SSL_CTX_set_ecdh_auto(dom_ctx->ssl_ctx, 1);
17548
0
#endif /* NO_SSL_DL */
17549
17550
  /* In SSL documentation examples callback defined without const
17551
   * specifier 'void (*)(SSL *, int, int)'   See:
17552
   * https://www.openssl.org/docs/man1.0.2/ssl/ssl.html
17553
   * https://www.openssl.org/docs/man1.1.0/ssl/ssl.html
17554
   * But in the source code const SSL is used:
17555
   * 'void (*)(const SSL *, int, int)' See:
17556
   * https://github.com/openssl/openssl/blob/1d97c8435171a7af575f73c526d79e1ef0ee5960/ssl/ssl.h#L1173
17557
   * Problem about wrong documentation described, but not resolved:
17558
   * https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/1147526
17559
   * Wrong const cast ignored on C or can be suppressed by compiler flags.
17560
   * But when compiled with modern C++ compiler, correct const should be
17561
   * provided
17562
   */
17563
0
  SSL_CTX_set_info_callback(dom_ctx->ssl_ctx, ssl_info_callback);
17564
17565
0
  SSL_CTX_set_tlsext_servername_callback(dom_ctx->ssl_ctx,
17566
0
                                         ssl_servername_callback);
17567
17568
  /* If a callback has been specified, call it. */
17569
0
  callback_ret = (phys_ctx->callbacks.init_ssl == NULL)
17570
0
                     ? 0
17571
0
                     : (phys_ctx->callbacks.init_ssl(dom_ctx->ssl_ctx,
17572
0
                                                     phys_ctx->user_data));
17573
17574
  /* If callback returns 0, civetweb sets up the SSL certificate.
17575
   * If it returns 1, civetweb assumes the callback already did this.
17576
   * If it returns -1, initializing ssl fails. */
17577
0
  if (callback_ret < 0) {
17578
0
    mg_cry_ctx_internal(phys_ctx,
17579
0
                        "SSL callback returned error: %i",
17580
0
                        callback_ret);
17581
0
    return 0;
17582
0
  }
17583
0
  if (callback_ret > 0) {
17584
    /* Callback did everything. */
17585
0
    return 1;
17586
0
  }
17587
17588
  /* If a domain callback has been specified, call it. */
17589
0
  callback_ret = (phys_ctx->callbacks.init_ssl_domain == NULL)
17590
0
                     ? 0
17591
0
                     : (phys_ctx->callbacks.init_ssl_domain(
17592
0
                           dom_ctx->config[AUTHENTICATION_DOMAIN],
17593
0
                           dom_ctx->ssl_ctx,
17594
0
                           phys_ctx->user_data));
17595
17596
  /* If domain callback returns 0, civetweb sets up the SSL certificate.
17597
   * If it returns 1, civetweb assumes the callback already did this.
17598
   * If it returns -1, initializing ssl fails. */
17599
0
  if (callback_ret < 0) {
17600
0
    mg_cry_ctx_internal(phys_ctx,
17601
0
                        "Domain SSL callback returned error: %i",
17602
0
                        callback_ret);
17603
0
    return 0;
17604
0
  }
17605
0
  if (callback_ret > 0) {
17606
    /* Domain callback did everything. */
17607
0
    return 1;
17608
0
  }
17609
17610
  /* Use some combination of start time, domain and port as a SSL
17611
   * context ID. This should be unique on the current machine. */
17612
0
  md5_init(&md5state);
17613
0
  clock_gettime(CLOCK_MONOTONIC, &now_mt);
17614
0
  md5_append(&md5state, (const md5_byte_t *)&now_mt, sizeof(now_mt));
17615
0
  md5_append(&md5state,
17616
0
             (const md5_byte_t *)phys_ctx->dd.config[LISTENING_PORTS],
17617
0
             strlen(phys_ctx->dd.config[LISTENING_PORTS]));
17618
0
  md5_append(&md5state,
17619
0
             (const md5_byte_t *)dom_ctx->config[AUTHENTICATION_DOMAIN],
17620
0
             strlen(dom_ctx->config[AUTHENTICATION_DOMAIN]));
17621
0
  md5_append(&md5state, (const md5_byte_t *)phys_ctx, sizeof(*phys_ctx));
17622
0
  md5_append(&md5state, (const md5_byte_t *)dom_ctx, sizeof(*dom_ctx));
17623
0
  md5_finish(&md5state, ssl_context_id);
17624
17625
0
  SSL_CTX_set_session_id_context(dom_ctx->ssl_ctx,
17626
0
                                 (unsigned char *)ssl_context_id,
17627
0
                                 sizeof(ssl_context_id));
17628
17629
0
  if (pem != NULL) {
17630
0
    if (!ssl_use_pem_file(phys_ctx, dom_ctx, pem, chain)) {
17631
0
      return 0;
17632
0
    }
17633
0
  }
17634
17635
  /* Should we support client certificates? */
17636
  /* Default is "no". */
17637
0
  should_verify_peer = 0;
17638
0
  peer_certificate_optional = 0;
17639
0
  if (dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) {
17640
0
    if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER], "yes") == 0) {
17641
      /* Yes, they are mandatory */
17642
0
      should_verify_peer = 1;
17643
0
    } else if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER],
17644
0
                             "optional")
17645
0
               == 0) {
17646
      /* Yes, they are optional */
17647
0
      should_verify_peer = 1;
17648
0
      peer_certificate_optional = 1;
17649
0
    }
17650
0
  }
17651
17652
0
  use_default_verify_paths =
17653
0
      (dom_ctx->config[SSL_DEFAULT_VERIFY_PATHS] != NULL)
17654
0
      && (mg_strcasecmp(dom_ctx->config[SSL_DEFAULT_VERIFY_PATHS], "yes")
17655
0
          == 0);
17656
17657
0
  if (should_verify_peer) {
17658
0
    ca_path = dom_ctx->config[SSL_CA_PATH];
17659
0
    ca_file = dom_ctx->config[SSL_CA_FILE];
17660
0
    if (SSL_CTX_load_verify_locations(dom_ctx->ssl_ctx, ca_file, ca_path)
17661
0
        != 1) {
17662
0
      mg_cry_ctx_internal(phys_ctx,
17663
0
                          "SSL_CTX_load_verify_locations error: %s "
17664
0
                          "ssl_verify_peer requires setting "
17665
0
                          "either ssl_ca_path or ssl_ca_file. "
17666
0
                          "Is any of them present in the "
17667
0
                          ".conf file?",
17668
0
                          ssl_error());
17669
0
      return 0;
17670
0
    }
17671
17672
0
    if (peer_certificate_optional) {
17673
0
      SSL_CTX_set_verify(dom_ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
17674
0
    } else {
17675
0
      SSL_CTX_set_verify(dom_ctx->ssl_ctx,
17676
0
                         SSL_VERIFY_PEER
17677
0
                             | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
17678
0
                         NULL);
17679
0
    }
17680
17681
0
    if (use_default_verify_paths
17682
0
        && (SSL_CTX_set_default_verify_paths(dom_ctx->ssl_ctx) != 1)) {
17683
0
      mg_cry_ctx_internal(phys_ctx,
17684
0
                          "SSL_CTX_set_default_verify_paths error: %s",
17685
0
                          ssl_error());
17686
0
      return 0;
17687
0
    }
17688
17689
0
    if (dom_ctx->config[SSL_VERIFY_DEPTH]) {
17690
0
      verify_depth = atoi(dom_ctx->config[SSL_VERIFY_DEPTH]);
17691
0
      SSL_CTX_set_verify_depth(dom_ctx->ssl_ctx, verify_depth);
17692
0
    }
17693
0
  }
17694
17695
0
  if (dom_ctx->config[SSL_CIPHER_LIST] != NULL) {
17696
0
    if (SSL_CTX_set_cipher_list(dom_ctx->ssl_ctx,
17697
0
                                dom_ctx->config[SSL_CIPHER_LIST])
17698
0
        != 1) {
17699
0
      mg_cry_ctx_internal(phys_ctx,
17700
0
                          "SSL_CTX_set_cipher_list error: %s",
17701
0
                          ssl_error());
17702
0
    }
17703
0
  }
17704
17705
  /* SSL session caching */
17706
0
  ssl_cache_timeout = ((dom_ctx->config[SSL_CACHE_TIMEOUT] != NULL)
17707
0
                           ? atoi(dom_ctx->config[SSL_CACHE_TIMEOUT])
17708
0
                           : 0);
17709
0
  if (ssl_cache_timeout > 0) {
17710
0
    SSL_CTX_set_session_cache_mode(dom_ctx->ssl_ctx, SSL_SESS_CACHE_BOTH);
17711
    /* SSL_CTX_sess_set_cache_size(dom_ctx->ssl_ctx, 10000);  ... use
17712
     * default */
17713
0
    SSL_CTX_set_timeout(dom_ctx->ssl_ctx, (long)ssl_cache_timeout);
17714
0
  }
17715
17716
#if defined(USE_ALPN)
17717
  /* Initialize ALPN only of TLS library (OpenSSL version) supports ALPN */
17718
#if !defined(NO_SSL_DL)
17719
  if (!tls_feature_missing[TLS_ALPN])
17720
#endif
17721
  {
17722
    init_alpn(phys_ctx, dom_ctx);
17723
  }
17724
#endif
17725
17726
0
  return 1;
17727
0
}
17728
17729
17730
/* Check if SSL is required.
17731
 * If so, dynamically load SSL library
17732
 * and set up ctx->ssl_ctx pointer. */
17733
static int
17734
init_ssl_ctx(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
17735
2
{
17736
2
  void *ssl_ctx = 0;
17737
2
  int callback_ret;
17738
2
  const char *pem;
17739
2
  const char *chain;
17740
2
  char ebuf[128];
17741
17742
2
  if (!phys_ctx) {
17743
0
    return 0;
17744
0
  }
17745
17746
2
  if (!dom_ctx) {
17747
2
    dom_ctx = &(phys_ctx->dd);
17748
2
  }
17749
17750
2
  if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
17751
    /* No SSL port is set. No need to setup SSL. */
17752
2
    return 1;
17753
2
  }
17754
17755
  /* Check for external SSL_CTX */
17756
0
  callback_ret =
17757
0
      (phys_ctx->callbacks.external_ssl_ctx == NULL)
17758
0
          ? 0
17759
0
          : (phys_ctx->callbacks.external_ssl_ctx(&ssl_ctx,
17760
0
                                                  phys_ctx->user_data));
17761
17762
0
  if (callback_ret < 0) {
17763
    /* Callback exists and returns <0: Initializing failed. */
17764
0
    mg_cry_ctx_internal(phys_ctx,
17765
0
                        "external_ssl_ctx callback returned error: %i",
17766
0
                        callback_ret);
17767
0
    return 0;
17768
0
  } else if (callback_ret > 0) {
17769
    /* Callback exists and returns >0: Initializing complete,
17770
     * civetweb should not modify the SSL context. */
17771
0
    dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx;
17772
0
    if (!initialize_openssl(ebuf, sizeof(ebuf))) {
17773
0
      mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
17774
0
      return 0;
17775
0
    }
17776
0
    return 1;
17777
0
  }
17778
  /* If the callback does not exist or return 0, civetweb must initialize
17779
   * the SSL context. Handle "domain" callback next. */
17780
17781
  /* Check for external domain SSL_CTX callback. */
17782
0
  callback_ret = (phys_ctx->callbacks.external_ssl_ctx_domain == NULL)
17783
0
                     ? 0
17784
0
                     : (phys_ctx->callbacks.external_ssl_ctx_domain(
17785
0
                           dom_ctx->config[AUTHENTICATION_DOMAIN],
17786
0
                           &ssl_ctx,
17787
0
                           phys_ctx->user_data));
17788
17789
0
  if (callback_ret < 0) {
17790
    /* Callback < 0: Error. Abort init. */
17791
0
    mg_cry_ctx_internal(
17792
0
        phys_ctx,
17793
0
        "external_ssl_ctx_domain callback returned error: %i",
17794
0
        callback_ret);
17795
0
    return 0;
17796
0
  } else if (callback_ret > 0) {
17797
    /* Callback > 0: Consider init done. */
17798
0
    dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx;
17799
0
    if (!initialize_openssl(ebuf, sizeof(ebuf))) {
17800
0
      mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
17801
0
      return 0;
17802
0
    }
17803
0
    return 1;
17804
0
  }
17805
  /* else: external_ssl_ctx/external_ssl_ctx_domain do not exist or return
17806
   * 0, CivetWeb should continue initializing SSL */
17807
17808
  /* If PEM file is not specified and the init_ssl callbacks
17809
   * are not specified, setup will fail. */
17810
0
  if (((pem = dom_ctx->config[SSL_CERTIFICATE]) == NULL)
17811
0
      && (phys_ctx->callbacks.init_ssl == NULL)
17812
0
      && (phys_ctx->callbacks.init_ssl_domain == NULL)) {
17813
    /* No certificate and no init_ssl callbacks:
17814
     * Essential data to set up TLS is missing.
17815
     */
17816
0
    mg_cry_ctx_internal(phys_ctx,
17817
0
                        "Initializing SSL failed: -%s is not set",
17818
0
                        config_options[SSL_CERTIFICATE].name);
17819
0
    return 0;
17820
0
  }
17821
17822
  /* If a certificate chain is configured, use it. */
17823
0
  chain = dom_ctx->config[SSL_CERTIFICATE_CHAIN];
17824
0
  if (chain == NULL) {
17825
    /* Default: certificate chain in PEM file */
17826
0
    chain = pem;
17827
0
  }
17828
0
  if ((chain != NULL) && (*chain == 0)) {
17829
    /* If the chain is an empty string, don't use it. */
17830
0
    chain = NULL;
17831
0
  }
17832
17833
0
  if (!initialize_openssl(ebuf, sizeof(ebuf))) {
17834
0
    mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
17835
0
    return 0;
17836
0
  }
17837
17838
0
  return init_ssl_ctx_impl(phys_ctx, dom_ctx, pem, chain);
17839
0
}
17840
17841
17842
static void
17843
uninitialize_openssl(void)
17844
0
{
17845
0
#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
17846
17847
0
  if (mg_atomic_dec(&cryptolib_users) == 0) {
17848
17849
    /* Shutdown according to
17850
     * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
17851
     * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl
17852
     */
17853
0
    CONF_modules_unload(1);
17854
#else
17855
  int i;
17856
17857
  if (mg_atomic_dec(&cryptolib_users) == 0) {
17858
17859
    /* Shutdown according to
17860
     * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
17861
     * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl
17862
     */
17863
    CRYPTO_set_locking_callback(NULL);
17864
    CRYPTO_set_id_callback(NULL);
17865
    ENGINE_cleanup();
17866
    CONF_modules_unload(1);
17867
    ERR_free_strings();
17868
    EVP_cleanup();
17869
    CRYPTO_cleanup_all_ex_data();
17870
    OPENSSL_REMOVE_THREAD_STATE();
17871
17872
    for (i = 0; i < CRYPTO_num_locks(); i++) {
17873
      pthread_mutex_destroy(&ssl_mutexes[i]);
17874
    }
17875
    mg_free(ssl_mutexes);
17876
    ssl_mutexes = NULL;
17877
#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
17878
0
  }
17879
0
}
17880
#endif /* !defined(NO_SSL) && !defined(USE_MBEDTLS) */
17881
17882
17883
#if !defined(NO_FILESYSTEMS)
17884
static int
17885
set_gpass_option(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
17886
2
{
17887
2
  if (phys_ctx) {
17888
2
    struct mg_file file = STRUCT_FILE_INITIALIZER;
17889
2
    const char *path;
17890
2
    struct mg_connection fc;
17891
2
    if (!dom_ctx) {
17892
2
      dom_ctx = &(phys_ctx->dd);
17893
2
    }
17894
2
    path = dom_ctx->config[GLOBAL_PASSWORDS_FILE];
17895
2
    if ((path != NULL)
17896
2
        && !mg_stat(fake_connection(&fc, phys_ctx), path, &file.stat)) {
17897
0
      mg_cry_ctx_internal(phys_ctx,
17898
0
                          "Cannot open %s: %s",
17899
0
                          path,
17900
0
                          strerror(ERRNO));
17901
0
      return 0;
17902
0
    }
17903
2
    return 1;
17904
2
  }
17905
0
  return 0;
17906
2
}
17907
#endif /* NO_FILESYSTEMS */
17908
17909
17910
static int
17911
set_acl_option(struct mg_context *phys_ctx)
17912
2
{
17913
2
  union usa sa;
17914
2
  memset(&sa, 0, sizeof(sa));
17915
#if defined(USE_IPV6)
17916
  sa.sin6.sin6_family = AF_INET6;
17917
#else
17918
2
  sa.sin.sin_family = AF_INET;
17919
2
#endif
17920
2
  return check_acl(phys_ctx, &sa) != -1;
17921
2
}
17922
17923
17924
static void
17925
reset_per_request_attributes(struct mg_connection *conn)
17926
34
{
17927
34
  if (!conn) {
17928
0
    return;
17929
0
  }
17930
17931
34
  conn->num_bytes_sent = conn->consumed_content = 0;
17932
17933
34
  conn->path_info = NULL;
17934
34
  conn->status_code = -1;
17935
34
  conn->content_len = -1;
17936
34
  conn->is_chunked = 0;
17937
34
  conn->must_close = 0;
17938
34
  conn->request_len = 0;
17939
34
  conn->request_state = 0;
17940
34
  conn->throttle = 0;
17941
34
  conn->accept_gzip = 0;
17942
17943
34
  conn->response_info.content_length = conn->request_info.content_length = -1;
17944
34
  conn->response_info.http_version = conn->request_info.http_version = NULL;
17945
34
  conn->response_info.num_headers = conn->request_info.num_headers = 0;
17946
34
  conn->response_info.status_text = NULL;
17947
34
  conn->response_info.status_code = 0;
17948
17949
34
  conn->request_info.remote_user = NULL;
17950
34
  conn->request_info.request_method = NULL;
17951
34
  conn->request_info.request_uri = NULL;
17952
17953
  /* Free cleaned local URI (if any) */
17954
34
  if (conn->request_info.local_uri != conn->request_info.local_uri_raw) {
17955
0
    mg_free((void *)conn->request_info.local_uri);
17956
0
    conn->request_info.local_uri = NULL;
17957
0
  }
17958
34
  conn->request_info.local_uri = NULL;
17959
17960
#if defined(USE_SERVER_STATS)
17961
  conn->processing_time = 0;
17962
#endif
17963
34
}
17964
17965
17966
static int
17967
set_tcp_nodelay(const struct socket *so, int nodelay_on)
17968
0
{
17969
0
  if ((so->lsa.sa.sa_family == AF_INET)
17970
0
      || (so->lsa.sa.sa_family == AF_INET6)) {
17971
    /* Only for TCP sockets */
17972
0
    if (setsockopt(so->sock,
17973
0
                   IPPROTO_TCP,
17974
0
                   TCP_NODELAY,
17975
0
                   (SOCK_OPT_TYPE)&nodelay_on,
17976
0
                   sizeof(nodelay_on))
17977
0
        != 0) {
17978
      /* Error */
17979
0
      return 1;
17980
0
    }
17981
0
  }
17982
  /* OK */
17983
0
  return 0;
17984
0
}
17985
17986
17987
#if !defined(__ZEPHYR__)
17988
static void
17989
close_socket_gracefully(struct mg_connection *conn)
17990
34
{
17991
#if defined(_WIN32)
17992
  char buf[MG_BUF_LEN];
17993
  int n;
17994
#endif
17995
34
  struct linger linger;
17996
34
  int error_code = 0;
17997
34
  int linger_timeout = -2;
17998
34
  socklen_t opt_len = sizeof(error_code);
17999
18000
34
  if (!conn) {
18001
0
    return;
18002
0
  }
18003
18004
  /* http://msdn.microsoft.com/en-us/library/ms739165(v=vs.85).aspx:
18005
   * "Note that enabling a nonzero timeout on a nonblocking socket
18006
   * is not recommended.", so set it to blocking now */
18007
34
  set_blocking_mode(conn->client.sock);
18008
18009
  /* Send FIN to the client */
18010
34
  shutdown(conn->client.sock, SHUTDOWN_WR);
18011
18012
#if defined(_WIN32)
18013
  /* Read and discard pending incoming data. If we do not do that and
18014
   * close
18015
   * the socket, the data in the send buffer may be discarded. This
18016
   * behaviour is seen on Windows, when client keeps sending data
18017
   * when server decides to close the connection; then when client
18018
   * does recv() it gets no data back. */
18019
  do {
18020
    n = pull_inner(NULL, conn, buf, sizeof(buf), /* Timeout in s: */ 1.0);
18021
  } while (n > 0);
18022
#endif
18023
18024
34
  if (conn->dom_ctx->config[LINGER_TIMEOUT]) {
18025
0
    linger_timeout = atoi(conn->dom_ctx->config[LINGER_TIMEOUT]);
18026
0
  }
18027
18028
  /* Set linger option according to configuration */
18029
34
  if (linger_timeout >= 0) {
18030
    /* Set linger option to avoid socket hanging out after close. This
18031
     * prevent ephemeral port exhaust problem under high QPS. */
18032
0
    linger.l_onoff = 1;
18033
18034
#if defined(_MSC_VER)
18035
#pragma warning(push)
18036
#pragma warning(disable : 4244)
18037
#endif
18038
#if defined(GCC_DIAGNOSTIC)
18039
#pragma GCC diagnostic push
18040
#pragma GCC diagnostic ignored "-Wconversion"
18041
#endif
18042
    /* Data type of linger structure elements may differ,
18043
     * so we don't know what cast we need here.
18044
     * Disable type conversion warnings. */
18045
18046
0
    linger.l_linger = (linger_timeout + 999) / 1000;
18047
18048
#if defined(GCC_DIAGNOSTIC)
18049
#pragma GCC diagnostic pop
18050
#endif
18051
#if defined(_MSC_VER)
18052
#pragma warning(pop)
18053
#endif
18054
18055
34
  } else {
18056
34
    linger.l_onoff = 0;
18057
34
    linger.l_linger = 0;
18058
34
  }
18059
18060
34
  if (linger_timeout < -1) {
18061
    /* Default: don't configure any linger */
18062
34
  } else if (getsockopt(conn->client.sock,
18063
0
                        SOL_SOCKET,
18064
0
                        SO_ERROR,
18065
#if defined(_WIN32) /* WinSock uses different data type here */
18066
                        (char *)&error_code,
18067
#else
18068
0
                        &error_code,
18069
0
#endif
18070
0
                        &opt_len)
18071
0
             != 0) {
18072
    /* Cannot determine if socket is already closed. This should
18073
     * not occur and never did in a test. Log an error message
18074
     * and continue. */
18075
0
    mg_cry_internal(conn,
18076
0
                    "%s: getsockopt(SOL_SOCKET SO_ERROR) failed: %s",
18077
0
                    __func__,
18078
0
                    strerror(ERRNO));
18079
#if defined(_WIN32)
18080
  } else if (error_code == WSAECONNRESET) {
18081
#else
18082
0
  } else if (error_code == ECONNRESET) {
18083
0
#endif
18084
    /* Socket already closed by client/peer, close socket without linger
18085
     */
18086
0
  } else {
18087
18088
    /* Set linger timeout */
18089
0
    if (setsockopt(conn->client.sock,
18090
0
                   SOL_SOCKET,
18091
0
                   SO_LINGER,
18092
0
                   (char *)&linger,
18093
0
                   sizeof(linger))
18094
0
        != 0) {
18095
0
      mg_cry_internal(
18096
0
          conn,
18097
0
          "%s: setsockopt(SOL_SOCKET SO_LINGER(%i,%i)) failed: %s",
18098
0
          __func__,
18099
0
          linger.l_onoff,
18100
0
          linger.l_linger,
18101
0
          strerror(ERRNO));
18102
0
    }
18103
0
  }
18104
18105
  /* Now we know that our FIN is ACK-ed, safe to close */
18106
34
  closesocket(conn->client.sock);
18107
34
  conn->client.sock = INVALID_SOCKET;
18108
34
}
18109
#endif
18110
18111
18112
static void
18113
close_connection(struct mg_connection *conn)
18114
34
{
18115
#if defined(USE_SERVER_STATS)
18116
  conn->conn_state = 6; /* to close */
18117
#endif
18118
18119
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
18120
  if (conn->lua_websocket_state) {
18121
    lua_websocket_close(conn, conn->lua_websocket_state);
18122
    conn->lua_websocket_state = NULL;
18123
  }
18124
#endif
18125
18126
34
  mg_lock_connection(conn);
18127
18128
  /* Set close flag, so keep-alive loops will stop */
18129
34
  conn->must_close = 1;
18130
18131
  /* call the connection_close callback if assigned */
18132
34
  if (conn->phys_ctx->callbacks.connection_close != NULL) {
18133
0
    if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
18134
0
      conn->phys_ctx->callbacks.connection_close(conn);
18135
0
    }
18136
0
  }
18137
18138
  /* Reset user data, after close callback is called.
18139
   * Do not reuse it. If the user needs a destructor,
18140
   * it must be done in the connection_close callback. */
18141
34
  mg_set_user_connection_data(conn, NULL);
18142
18143
#if defined(USE_SERVER_STATS)
18144
  conn->conn_state = 7; /* closing */
18145
#endif
18146
18147
#if defined(USE_MBEDTLS)
18148
  if (conn->ssl != NULL) {
18149
    mbed_ssl_close(conn->ssl);
18150
    conn->ssl = NULL;
18151
  }
18152
#elif !defined(NO_SSL)
18153
34
  if (conn->ssl != NULL) {
18154
    /* Run SSL_shutdown twice to ensure completely close SSL connection
18155
     */
18156
0
    SSL_shutdown(conn->ssl);
18157
0
    SSL_free(conn->ssl);
18158
0
    OPENSSL_REMOVE_THREAD_STATE();
18159
0
    conn->ssl = NULL;
18160
0
  }
18161
34
#endif
18162
34
  if (conn->client.sock != INVALID_SOCKET) {
18163
#if defined(__ZEPHYR__)
18164
    closesocket(conn->client.sock);
18165
#else
18166
34
    close_socket_gracefully(conn);
18167
34
#endif
18168
34
    conn->client.sock = INVALID_SOCKET;
18169
34
  }
18170
18171
  /* call the connection_closed callback if assigned */
18172
34
  if (conn->phys_ctx->callbacks.connection_closed != NULL) {
18173
0
    if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
18174
0
      conn->phys_ctx->callbacks.connection_closed(conn);
18175
0
    }
18176
0
  }
18177
18178
34
  mg_unlock_connection(conn);
18179
18180
#if defined(USE_SERVER_STATS)
18181
  conn->conn_state = 8; /* closed */
18182
#endif
18183
34
}
18184
18185
18186
CIVETWEB_API void
18187
mg_close_connection(struct mg_connection *conn)
18188
34
{
18189
34
  if ((conn == NULL) || (conn->phys_ctx == NULL)) {
18190
0
    return;
18191
0
  }
18192
18193
#if defined(USE_WEBSOCKET)
18194
  if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
18195
    if (conn->in_websocket_handling) {
18196
      /* Set close flag, so the server thread can exit. */
18197
      conn->must_close = 1;
18198
      return;
18199
    }
18200
  }
18201
  if (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT) {
18202
18203
    unsigned int i;
18204
18205
    /* client context: loops must end */
18206
    STOP_FLAG_ASSIGN(&conn->phys_ctx->stop_flag, 1);
18207
    conn->must_close = 1;
18208
18209
    /* We need to get the client thread out of the select/recv call
18210
     * here. */
18211
    /* Since we use a sleep quantum of some seconds to check for recv
18212
     * timeouts, we will just wait a few seconds in mg_join_thread. */
18213
18214
    /* join worker thread */
18215
    for (i = 0; i < conn->phys_ctx->spawned_worker_threads; i++) {
18216
      mg_join_thread(conn->phys_ctx->worker_threadids[i]);
18217
    }
18218
  }
18219
#endif /* defined(USE_WEBSOCKET) */
18220
18221
34
  close_connection(conn);
18222
18223
34
#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
18224
34
  if (((conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT)
18225
34
       || (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT))
18226
34
      && (conn->phys_ctx->dd.ssl_ctx != NULL)) {
18227
0
    SSL_CTX_free(conn->phys_ctx->dd.ssl_ctx);
18228
0
  }
18229
34
#endif
18230
18231
#if defined(USE_WEBSOCKET)
18232
  if (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT) {
18233
    mg_free(conn->phys_ctx->worker_threadids);
18234
    (void)pthread_mutex_destroy(&conn->mutex);
18235
    mg_free(conn);
18236
  } else if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) {
18237
    (void)pthread_mutex_destroy(&conn->mutex);
18238
    mg_free(conn);
18239
  }
18240
#else
18241
34
  if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) { /* Client */
18242
34
    (void)pthread_mutex_destroy(&conn->mutex);
18243
34
    mg_free(conn);
18244
34
  }
18245
34
#endif /* defined(USE_WEBSOCKET) */
18246
34
}
18247
18248
18249
static struct mg_connection *
18250
mg_connect_client_impl(const struct mg_client_options *client_options,
18251
                       int use_ssl,
18252
                       struct mg_init_data *init,
18253
                       struct mg_error_data *error)
18254
34
{
18255
34
  struct mg_connection *conn = NULL;
18256
34
  SOCKET sock;
18257
34
  union usa sa;
18258
34
  struct sockaddr *psa;
18259
34
  socklen_t len;
18260
18261
34
  unsigned max_req_size =
18262
34
      (unsigned)atoi(config_options[MAX_REQUEST_SIZE].default_value);
18263
18264
  /* Size of structures, aligned to 8 bytes */
18265
34
  size_t conn_size = ((sizeof(struct mg_connection) + 7) >> 3) << 3;
18266
34
  size_t ctx_size = ((sizeof(struct mg_context) + 7) >> 3) << 3;
18267
34
  size_t alloc_size = conn_size + ctx_size + max_req_size;
18268
18269
34
  (void)init; /* TODO: Implement required options */
18270
18271
34
  conn = (struct mg_connection *)mg_calloc(1, alloc_size);
18272
18273
34
  if (error != NULL) {
18274
34
    error->code = MG_ERROR_DATA_CODE_OK;
18275
34
    error->code_sub = 0;
18276
34
    if (error->text_buffer_size > 0) {
18277
34
      error->text[0] = 0;
18278
34
    }
18279
34
  }
18280
18281
34
  if (conn == NULL) {
18282
0
    if (error != NULL) {
18283
0
      error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
18284
0
      error->code_sub = (unsigned)alloc_size;
18285
0
      mg_snprintf(NULL,
18286
0
                  NULL, /* No truncation check for ebuf */
18287
0
                  error->text,
18288
0
                  error->text_buffer_size,
18289
0
                  "calloc(): %s",
18290
0
                  strerror(ERRNO));
18291
0
    }
18292
0
    return NULL;
18293
0
  }
18294
18295
#if defined(GCC_DIAGNOSTIC)
18296
#pragma GCC diagnostic push
18297
#pragma GCC diagnostic ignored "-Wcast-align"
18298
#endif /* defined(GCC_DIAGNOSTIC) */
18299
  /* conn_size is aligned to 8 bytes */
18300
18301
34
  conn->phys_ctx = (struct mg_context *)(((char *)conn) + conn_size);
18302
18303
#if defined(GCC_DIAGNOSTIC)
18304
#pragma GCC diagnostic pop
18305
#endif /* defined(GCC_DIAGNOSTIC) */
18306
18307
34
  conn->buf = (((char *)conn) + conn_size + ctx_size);
18308
34
  conn->buf_size = (int)max_req_size;
18309
34
  conn->phys_ctx->context_type = CONTEXT_HTTP_CLIENT;
18310
34
  conn->dom_ctx = &(conn->phys_ctx->dd);
18311
18312
34
  if (!connect_socket(conn->phys_ctx,
18313
34
                      client_options->host,
18314
34
                      client_options->port,
18315
34
                      use_ssl,
18316
34
                      error,
18317
34
                      &sock,
18318
34
                      &sa)) {
18319
    /* "error" will be set by connect_socket. */
18320
    /* free all memory and return NULL; */
18321
0
    mg_free(conn);
18322
0
    return NULL;
18323
0
  }
18324
18325
34
#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
18326
34
#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0))                     \
18327
34
    && !defined(NO_SSL_DL)
18328
18329
34
  if (use_ssl
18330
34
      && (conn->dom_ctx->ssl_ctx = SSL_CTX_new(TLS_client_method()))
18331
0
             == NULL) {
18332
0
    if (error != NULL) {
18333
0
      error->code = MG_ERROR_DATA_CODE_INIT_TLS_FAILED;
18334
0
      mg_snprintf(NULL,
18335
0
                  NULL, /* No truncation check for ebuf */
18336
0
                  error->text,
18337
0
                  error->text_buffer_size,
18338
0
                  "SSL_CTX_new error: %s",
18339
0
                  ssl_error());
18340
0
    }
18341
18342
0
    closesocket(sock);
18343
0
    mg_free(conn);
18344
0
    return NULL;
18345
0
  }
18346
18347
#else
18348
18349
  if (use_ssl
18350
      && (conn->dom_ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method()))
18351
             == NULL) {
18352
    if (error != NULL) {
18353
      error->code = MG_ERROR_DATA_CODE_INIT_TLS_FAILED;
18354
      mg_snprintf(NULL,
18355
                  NULL, /* No truncation check for ebuf */
18356
                  error->text,
18357
                  error->text_buffer_size,
18358
                  "SSL_CTX_new error: %s",
18359
                  ssl_error());
18360
    }
18361
18362
    closesocket(sock);
18363
    mg_free(conn);
18364
    return NULL;
18365
  }
18366
18367
#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
18368
34
#endif /* NO_SSL */
18369
18370
#if defined(USE_IPV6)
18371
  len = (sa.sa.sa_family == AF_INET) ? sizeof(conn->client.rsa.sin)
18372
                                     : sizeof(conn->client.rsa.sin6);
18373
  psa = (sa.sa.sa_family == AF_INET)
18374
            ? (struct sockaddr *)&(conn->client.rsa.sin)
18375
            : (struct sockaddr *)&(conn->client.rsa.sin6);
18376
#else
18377
34
  len = sizeof(conn->client.rsa.sin);
18378
34
  psa = (struct sockaddr *)&(conn->client.rsa.sin);
18379
34
#endif
18380
18381
34
  conn->client.sock = sock;
18382
34
  conn->client.lsa = sa;
18383
18384
34
  if (getsockname(sock, psa, &len) != 0) {
18385
0
    mg_cry_internal(conn,
18386
0
                    "%s: getsockname() failed: %s",
18387
0
                    __func__,
18388
0
                    strerror(ERRNO));
18389
0
  }
18390
18391
34
  conn->client.is_ssl = use_ssl ? 1 : 0;
18392
34
  if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) {
18393
0
    if (error != NULL) {
18394
0
      error->code = MG_ERROR_DATA_CODE_OS_ERROR;
18395
0
      error->code_sub = (unsigned)ERRNO;
18396
0
      mg_snprintf(NULL,
18397
0
                  NULL, /* No truncation check for ebuf */
18398
0
                  error->text,
18399
0
                  error->text_buffer_size,
18400
0
                  "Can not create mutex");
18401
0
    }
18402
0
#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
18403
0
    SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18404
0
#endif
18405
0
    closesocket(sock);
18406
0
    mg_free(conn);
18407
0
    return NULL;
18408
0
  }
18409
18410
34
#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
18411
34
  if (use_ssl) {
18412
    /* TODO: Check ssl_verify_peer and ssl_ca_path here.
18413
     * SSL_CTX_set_verify call is needed to switch off server
18414
     * certificate checking, which is off by default in OpenSSL and
18415
     * on in yaSSL. */
18416
    /* TODO: SSL_CTX_set_verify(conn->dom_ctx,
18417
     * SSL_VERIFY_PEER, verify_ssl_server); */
18418
18419
0
    if (client_options->client_cert) {
18420
0
      if (!ssl_use_pem_file(conn->phys_ctx,
18421
0
                            conn->dom_ctx,
18422
0
                            client_options->client_cert,
18423
0
                            NULL)) {
18424
0
        if (error != NULL) {
18425
0
          error->code = MG_ERROR_DATA_CODE_TLS_CLIENT_CERT_ERROR;
18426
0
          mg_snprintf(NULL,
18427
0
                      NULL, /* No truncation check for ebuf */
18428
0
                      error->text,
18429
0
                      error->text_buffer_size,
18430
0
                      "Can not use SSL client certificate");
18431
0
        }
18432
18433
0
        SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18434
0
        closesocket(sock);
18435
0
        mg_free(conn);
18436
0
        return NULL;
18437
0
      }
18438
0
    }
18439
18440
0
    if (client_options->server_cert) {
18441
0
      if (SSL_CTX_load_verify_locations(conn->dom_ctx->ssl_ctx,
18442
0
                                        client_options->server_cert,
18443
0
                                        NULL)
18444
0
          != 1) {
18445
0
        if (error != NULL) {
18446
0
          error->code = MG_ERROR_DATA_CODE_TLS_SERVER_CERT_ERROR;
18447
0
          mg_snprintf(NULL,
18448
0
                      NULL, /* No truncation check for ebuf */
18449
0
                      error->text,
18450
0
                      error->text_buffer_size,
18451
0
                      "SSL_CTX_load_verify_locations error: %s",
18452
0
                      ssl_error());
18453
0
        }
18454
0
        SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18455
0
        closesocket(sock);
18456
0
        mg_free(conn);
18457
0
        return NULL;
18458
0
      }
18459
0
      SSL_CTX_set_verify(conn->dom_ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
18460
0
    } else {
18461
0
      SSL_CTX_set_verify(conn->dom_ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
18462
0
    }
18463
18464
0
    if (!sslize(conn, SSL_connect, client_options)) {
18465
0
      if (error != NULL) {
18466
0
        error->code = MG_ERROR_DATA_CODE_TLS_CONNECT_ERROR;
18467
0
        mg_snprintf(NULL,
18468
0
                    NULL, /* No truncation check for ebuf */
18469
0
                    error->text,
18470
0
                    error->text_buffer_size,
18471
0
                    "SSL connection error");
18472
0
      }
18473
0
      SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18474
0
      closesocket(sock);
18475
0
      mg_free(conn);
18476
0
      return NULL;
18477
0
    }
18478
0
  }
18479
34
#endif
18480
18481
34
  return conn;
18482
34
}
18483
18484
18485
CIVETWEB_API struct mg_connection *
18486
mg_connect_client_secure(const struct mg_client_options *client_options,
18487
                         char *error_buffer,
18488
                         size_t error_buffer_size)
18489
0
{
18490
0
  struct mg_init_data init;
18491
0
  struct mg_error_data error;
18492
18493
0
  memset(&init, 0, sizeof(init));
18494
0
  memset(&error, 0, sizeof(error));
18495
0
  error.text_buffer_size = error_buffer_size;
18496
0
  error.text = error_buffer;
18497
0
  return mg_connect_client_impl(client_options, 1, &init, &error);
18498
0
}
18499
18500
18501
CIVETWEB_API struct mg_connection *
18502
mg_connect_client(const char *host,
18503
                  int port,
18504
                  int use_ssl,
18505
                  char *error_buffer,
18506
                  size_t error_buffer_size)
18507
34
{
18508
34
  struct mg_client_options opts;
18509
34
  struct mg_init_data init;
18510
34
  struct mg_error_data error;
18511
18512
34
  memset(&init, 0, sizeof(init));
18513
18514
34
  memset(&error, 0, sizeof(error));
18515
34
  error.text_buffer_size = error_buffer_size;
18516
34
  error.text = error_buffer;
18517
18518
34
  memset(&opts, 0, sizeof(opts));
18519
34
  opts.host = host;
18520
34
  opts.port = port;
18521
34
  if (use_ssl) {
18522
0
    opts.host_name = host;
18523
0
  }
18524
18525
34
  return mg_connect_client_impl(&opts, use_ssl, &init, &error);
18526
34
}
18527
18528
18529
#if defined(MG_EXPERIMENTAL_INTERFACES)
18530
CIVETWEB_API struct mg_connection *
18531
mg_connect_client2(const char *host,
18532
                   const char *protocol,
18533
                   int port,
18534
                   const char *path,
18535
                   struct mg_init_data *init,
18536
                   struct mg_error_data *error)
18537
{
18538
  (void)path;
18539
18540
  int is_ssl, is_ws;
18541
  /* void *user_data = (init != NULL) ? init->user_data : NULL; -- TODO */
18542
18543
  if (error != NULL) {
18544
    error->code = MG_ERROR_DATA_CODE_OK;
18545
    error->code_sub = 0;
18546
    if (error->text_buffer_size > 0) {
18547
      *error->text = 0;
18548
    }
18549
  }
18550
18551
  if ((host == NULL) || (protocol == NULL)) {
18552
    if (error != NULL) {
18553
      error->code = MG_ERROR_DATA_CODE_INVALID_PARAM;
18554
      mg_snprintf(NULL,
18555
                  NULL, /* No truncation check for error buffers */
18556
                  error->text,
18557
                  error->text_buffer_size,
18558
                  "%s",
18559
                  "Invalid parameters");
18560
    }
18561
    return NULL;
18562
  }
18563
18564
  /* check all known protocols */
18565
  if (!mg_strcasecmp(protocol, "http")) {
18566
    is_ssl = 0;
18567
    is_ws = 0;
18568
  } else if (!mg_strcasecmp(protocol, "https")) {
18569
    is_ssl = 1;
18570
    is_ws = 0;
18571
#if defined(USE_WEBSOCKET)
18572
  } else if (!mg_strcasecmp(protocol, "ws")) {
18573
    is_ssl = 0;
18574
    is_ws = 1;
18575
  } else if (!mg_strcasecmp(protocol, "wss")) {
18576
    is_ssl = 1;
18577
    is_ws = 1;
18578
#endif
18579
  } else {
18580
    if (error != NULL) {
18581
      error->code = MG_ERROR_DATA_CODE_INVALID_PARAM;
18582
      mg_snprintf(NULL,
18583
                  NULL, /* No truncation check for error buffers */
18584
                  error->text,
18585
                  error->text_buffer_size,
18586
                  "Protocol %s not supported",
18587
                  protocol);
18588
    }
18589
    return NULL;
18590
  }
18591
18592
  /* TODO: The current implementation here just calls the old
18593
   * implementations, without using any new options. This is just a first
18594
   * step to test the new interfaces. */
18595
#if defined(USE_WEBSOCKET)
18596
  if (is_ws) {
18597
    /* TODO: implement all options */
18598
    return mg_connect_websocket_client(
18599
        host,
18600
        port,
18601
        is_ssl,
18602
        ((error != NULL) ? error->text : NULL),
18603
        ((error != NULL) ? error->text_buffer_size : 0),
18604
        (path ? path : ""),
18605
        NULL /* TODO: origin */,
18606
        experimental_websocket_client_data_wrapper,
18607
        experimental_websocket_client_close_wrapper,
18608
        (void *)init->callbacks);
18609
  }
18610
#else
18611
  (void)is_ws;
18612
#endif
18613
18614
  /* TODO: all additional options */
18615
  struct mg_client_options opts;
18616
18617
  memset(&opts, 0, sizeof(opts));
18618
  opts.host = host;
18619
  opts.port = port;
18620
18621
  return mg_connect_client_impl(&opts, is_ssl, init, error);
18622
}
18623
#endif
18624
18625
18626
static const struct {
18627
  const char *proto;
18628
  size_t proto_len;
18629
  unsigned default_port;
18630
} abs_uri_protocols[] = {{"http://", 7, 80},
18631
                         {"https://", 8, 443},
18632
                         {"ws://", 5, 80},
18633
                         {"wss://", 6, 443},
18634
                         {NULL, 0, 0}};
18635
18636
18637
/* Check if the uri is valid.
18638
 * return 0 for invalid uri,
18639
 * return 1 for *,
18640
 * return 2 for relative uri,
18641
 * return 3 for absolute uri without port,
18642
 * return 4 for absolute uri with port */
18643
static int
18644
get_uri_type(const char *uri)
18645
0
{
18646
0
  int i;
18647
0
  const char *hostend, *portbegin;
18648
0
  char *portend;
18649
0
  unsigned long port;
18650
18651
  /* According to the HTTP standard
18652
   * http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
18653
   * URI can be an asterisk (*) or should start with slash (relative uri),
18654
   * or it should start with the protocol (absolute uri). */
18655
0
  if ((uri[0] == '*') && (uri[1] == '\0')) {
18656
    /* asterisk */
18657
0
    return 1;
18658
0
  }
18659
18660
  /* Valid URIs according to RFC 3986
18661
   * (https://www.ietf.org/rfc/rfc3986.txt)
18662
   * must only contain reserved characters :/?#[]@!$&'()*+,;=
18663
   * and unreserved characters A-Z a-z 0-9 and -._~
18664
   * and % encoded symbols.
18665
   */
18666
0
  for (i = 0; uri[i] != 0; i++) {
18667
0
    if ((unsigned char)uri[i] < 33) {
18668
      /* control characters and spaces are invalid */
18669
0
      return 0;
18670
0
    }
18671
    /* Allow everything else here (See #894) */
18672
0
  }
18673
18674
  /* A relative uri starts with a / character */
18675
0
  if (uri[0] == '/') {
18676
    /* relative uri */
18677
0
    return 2;
18678
0
  }
18679
18680
  /* It could be an absolute uri: */
18681
  /* This function only checks if the uri is valid, not if it is
18682
   * addressing the current server. So civetweb can also be used
18683
   * as a proxy server. */
18684
0
  for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
18685
0
    if (mg_strncasecmp(uri,
18686
0
                       abs_uri_protocols[i].proto,
18687
0
                       abs_uri_protocols[i].proto_len)
18688
0
        == 0) {
18689
18690
0
      hostend = strchr(uri + abs_uri_protocols[i].proto_len, '/');
18691
0
      if (!hostend) {
18692
0
        return 0;
18693
0
      }
18694
0
      portbegin = strchr(uri + abs_uri_protocols[i].proto_len, ':');
18695
0
      if (!portbegin) {
18696
0
        return 3;
18697
0
      }
18698
18699
0
      port = strtoul(portbegin + 1, &portend, 10);
18700
0
      if ((portend != hostend) || (port <= 0) || !is_valid_port(port)) {
18701
0
        return 0;
18702
0
      }
18703
18704
0
      return 4;
18705
0
    }
18706
0
  }
18707
18708
0
  return 0;
18709
0
}
18710
18711
18712
/* Return NULL or the relative uri at the current server */
18713
static const char *
18714
get_rel_url_at_current_server(const char *uri, const struct mg_connection *conn)
18715
0
{
18716
0
  const char *server_domain;
18717
0
  size_t server_domain_len;
18718
0
  size_t request_domain_len = 0;
18719
0
  unsigned long port = 0;
18720
0
  int i, auth_domain_check_enabled;
18721
0
  const char *hostbegin = NULL;
18722
0
  const char *hostend = NULL;
18723
0
  const char *portbegin;
18724
0
  char *portend;
18725
18726
0
  auth_domain_check_enabled =
18727
0
      !mg_strcasecmp(conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK], "yes");
18728
18729
  /* DNS is case insensitive, so use case insensitive string compare here
18730
   */
18731
0
  for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
18732
0
    if (mg_strncasecmp(uri,
18733
0
                       abs_uri_protocols[i].proto,
18734
0
                       abs_uri_protocols[i].proto_len)
18735
0
        == 0) {
18736
18737
0
      hostbegin = uri + abs_uri_protocols[i].proto_len;
18738
0
      hostend = strchr(hostbegin, '/');
18739
0
      if (!hostend) {
18740
0
        return 0;
18741
0
      }
18742
0
      portbegin = strchr(hostbegin, ':');
18743
0
      if ((!portbegin) || (portbegin > hostend)) {
18744
0
        port = abs_uri_protocols[i].default_port;
18745
0
        request_domain_len = (size_t)(hostend - hostbegin);
18746
0
      } else {
18747
0
        port = strtoul(portbegin + 1, &portend, 10);
18748
0
        if ((portend != hostend) || (port <= 0)
18749
0
            || !is_valid_port(port)) {
18750
0
          return 0;
18751
0
        }
18752
0
        request_domain_len = (size_t)(portbegin - hostbegin);
18753
0
      }
18754
      /* protocol found, port set */
18755
0
      break;
18756
0
    }
18757
0
  }
18758
18759
0
  if (!port) {
18760
    /* port remains 0 if the protocol is not found */
18761
0
    return 0;
18762
0
  }
18763
18764
  /* Check if the request is directed to a different server. */
18765
  /* First check if the port is the same. */
18766
0
  if (ntohs(USA_IN_PORT_UNSAFE(&conn->client.lsa)) != port) {
18767
    /* Request is directed to a different port */
18768
0
    return 0;
18769
0
  }
18770
18771
  /* Finally check if the server corresponds to the authentication
18772
   * domain of the server (the server domain).
18773
   * Allow full matches (like http://mydomain.com/path/file.ext), and
18774
   * allow subdomain matches (like http://www.mydomain.com/path/file.ext),
18775
   * but do not allow substrings (like
18776
   * http://notmydomain.com/path/file.ext
18777
   * or http://mydomain.com.fake/path/file.ext).
18778
   */
18779
0
  if (auth_domain_check_enabled) {
18780
0
    server_domain = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
18781
0
    server_domain_len = strlen(server_domain);
18782
0
    if ((server_domain_len == 0) || (hostbegin == NULL)) {
18783
0
      return 0;
18784
0
    }
18785
0
    if ((request_domain_len == server_domain_len)
18786
0
        && (!memcmp(server_domain, hostbegin, server_domain_len))) {
18787
      /* Request is directed to this server - full name match. */
18788
0
    } else {
18789
0
      if (request_domain_len < (server_domain_len + 2)) {
18790
        /* Request is directed to another server: The server name
18791
         * is longer than the request name.
18792
         * Drop this case here to avoid overflows in the
18793
         * following checks. */
18794
0
        return 0;
18795
0
      }
18796
0
      if (hostbegin[request_domain_len - server_domain_len - 1] != '.') {
18797
        /* Request is directed to another server: It could be a
18798
         * substring
18799
         * like notmyserver.com */
18800
0
        return 0;
18801
0
      }
18802
0
      if (0
18803
0
          != memcmp(server_domain,
18804
0
                    hostbegin + request_domain_len - server_domain_len,
18805
0
                    server_domain_len)) {
18806
        /* Request is directed to another server:
18807
         * The server name is different. */
18808
0
        return 0;
18809
0
      }
18810
0
    }
18811
0
  }
18812
18813
0
  return hostend;
18814
0
}
18815
18816
18817
static int
18818
get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
18819
34
{
18820
34
  if (ebuf_len > 0) {
18821
34
    ebuf[0] = '\0';
18822
34
  }
18823
34
  *err = 0;
18824
18825
34
  reset_per_request_attributes(conn);
18826
18827
34
  if (!conn) {
18828
0
    mg_snprintf(conn,
18829
0
                NULL, /* No truncation check for ebuf */
18830
0
                ebuf,
18831
0
                ebuf_len,
18832
0
                "%s",
18833
0
                "Internal error");
18834
0
    *err = 500;
18835
0
    return 0;
18836
0
  }
18837
18838
  /* Set the time the request was received. This value should be used for
18839
   * timeouts. */
18840
34
  clock_gettime(CLOCK_MONOTONIC, &(conn->req_time));
18841
18842
34
  conn->request_len =
18843
34
      read_message(NULL, conn, conn->buf, conn->buf_size, &conn->data_len);
18844
34
  DEBUG_ASSERT(conn->request_len < 0 || conn->data_len >= conn->request_len);
18845
34
  if ((conn->request_len >= 0) && (conn->data_len < conn->request_len)) {
18846
0
    mg_snprintf(conn,
18847
0
                NULL, /* No truncation check for ebuf */
18848
0
                ebuf,
18849
0
                ebuf_len,
18850
0
                "%s",
18851
0
                "Invalid message size");
18852
0
    *err = 500;
18853
0
    return 0;
18854
0
  }
18855
18856
34
  if ((conn->request_len == 0) && (conn->data_len == conn->buf_size)) {
18857
0
    mg_snprintf(conn,
18858
0
                NULL, /* No truncation check for ebuf */
18859
0
                ebuf,
18860
0
                ebuf_len,
18861
0
                "%s",
18862
0
                "Message too large");
18863
0
    *err = 413;
18864
0
    return 0;
18865
0
  }
18866
18867
34
  if (conn->request_len <= 0) {
18868
34
    if (conn->data_len > 0) {
18869
0
      mg_snprintf(conn,
18870
0
                  NULL, /* No truncation check for ebuf */
18871
0
                  ebuf,
18872
0
                  ebuf_len,
18873
0
                  "%s",
18874
0
                  conn->request_len == -3 ? "Request timeout"
18875
0
                                          : "Malformed message");
18876
0
      *err = 400;
18877
34
    } else {
18878
      /* Server did not recv anything -> just close the connection */
18879
34
      conn->must_close = 1;
18880
34
      mg_snprintf(conn,
18881
34
                  NULL, /* No truncation check for ebuf */
18882
34
                  ebuf,
18883
34
                  ebuf_len,
18884
34
                  "%s",
18885
34
                  "No data received");
18886
34
      *err = 0;
18887
34
    }
18888
34
    return 0;
18889
34
  }
18890
0
  return 1;
18891
34
}
18892
18893
18894
static int
18895
get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
18896
0
{
18897
0
  const char *cl;
18898
18899
0
  conn->connection_type =
18900
0
      CONNECTION_TYPE_REQUEST; /* request (valid of not) */
18901
18902
0
  if (!get_message(conn, ebuf, ebuf_len, err)) {
18903
0
    return 0;
18904
0
  }
18905
18906
0
  if (parse_http_request(conn->buf, conn->buf_size, &conn->request_info)
18907
0
      <= 0) {
18908
0
    mg_snprintf(conn,
18909
0
                NULL, /* No truncation check for ebuf */
18910
0
                ebuf,
18911
0
                ebuf_len,
18912
0
                "%s",
18913
0
                "Bad request");
18914
0
    *err = 400;
18915
0
    return 0;
18916
0
  }
18917
18918
  /* Message is a valid request */
18919
18920
0
  if (!switch_domain_context(conn)) {
18921
0
    mg_snprintf(conn,
18922
0
                NULL, /* No truncation check for ebuf */
18923
0
                ebuf,
18924
0
                ebuf_len,
18925
0
                "%s",
18926
0
                "Bad request: Host mismatch");
18927
0
    *err = 400;
18928
0
    return 0;
18929
0
  }
18930
18931
#if USE_ZLIB
18932
  if (((cl = get_header(conn->request_info.http_headers,
18933
                        conn->request_info.num_headers,
18934
                        "Accept-Encoding"))
18935
       != NULL)
18936
      && strstr(cl, "gzip")) {
18937
    conn->accept_gzip = 1;
18938
  }
18939
#endif
18940
0
  if (((cl = get_header(conn->request_info.http_headers,
18941
0
                        conn->request_info.num_headers,
18942
0
                        "Transfer-Encoding"))
18943
0
       != NULL)
18944
0
      && mg_strcasecmp(cl, "identity")) {
18945
0
    if (mg_strcasecmp(cl, "chunked")) {
18946
0
      mg_snprintf(conn,
18947
0
                  NULL, /* No truncation check for ebuf */
18948
0
                  ebuf,
18949
0
                  ebuf_len,
18950
0
                  "%s",
18951
0
                  "Bad request");
18952
0
      *err = 400;
18953
0
      return 0;
18954
0
    }
18955
0
    conn->is_chunked = 1;
18956
0
    conn->content_len = 0; /* not yet read */
18957
0
  } else if ((cl = get_header(conn->request_info.http_headers,
18958
0
                              conn->request_info.num_headers,
18959
0
                              "Content-Length"))
18960
0
             != NULL) {
18961
    /* Request has content length set */
18962
0
    char *endptr = NULL;
18963
0
    conn->content_len = strtoll(cl, &endptr, 10);
18964
0
    if ((endptr == cl) || (conn->content_len < 0)) {
18965
0
      mg_snprintf(conn,
18966
0
                  NULL, /* No truncation check for ebuf */
18967
0
                  ebuf,
18968
0
                  ebuf_len,
18969
0
                  "%s",
18970
0
                  "Bad request");
18971
0
      *err = 411;
18972
0
      return 0;
18973
0
    }
18974
    /* Publish the content length back to the request info. */
18975
0
    conn->request_info.content_length = conn->content_len;
18976
0
  } else {
18977
    /* There is no exception, see RFC7230. */
18978
0
    conn->content_len = 0;
18979
0
  }
18980
18981
0
  return 1;
18982
0
}
18983
18984
18985
/* conn is assumed to be valid in this internal function */
18986
static int
18987
get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
18988
34
{
18989
34
  const char *cl;
18990
18991
34
  conn->connection_type =
18992
34
      CONNECTION_TYPE_RESPONSE; /* response (valid or not) */
18993
18994
34
  if (!get_message(conn, ebuf, ebuf_len, err)) {
18995
34
    return 0;
18996
34
  }
18997
18998
0
  if (parse_http_response(conn->buf, conn->buf_size, &conn->response_info)
18999
0
      <= 0) {
19000
0
    mg_snprintf(conn,
19001
0
                NULL, /* No truncation check for ebuf */
19002
0
                ebuf,
19003
0
                ebuf_len,
19004
0
                "%s",
19005
0
                "Bad response");
19006
0
    *err = 400;
19007
0
    return 0;
19008
0
  }
19009
19010
  /* Message is a valid response */
19011
19012
0
  if (((cl = get_header(conn->response_info.http_headers,
19013
0
                        conn->response_info.num_headers,
19014
0
                        "Transfer-Encoding"))
19015
0
       != NULL)
19016
0
      && mg_strcasecmp(cl, "identity")) {
19017
0
    if (mg_strcasecmp(cl, "chunked")) {
19018
0
      mg_snprintf(conn,
19019
0
                  NULL, /* No truncation check for ebuf */
19020
0
                  ebuf,
19021
0
                  ebuf_len,
19022
0
                  "%s",
19023
0
                  "Bad request");
19024
0
      *err = 400;
19025
0
      return 0;
19026
0
    }
19027
0
    conn->is_chunked = 1;
19028
0
    conn->content_len = 0; /* not yet read */
19029
0
  } else if ((cl = get_header(conn->response_info.http_headers,
19030
0
                              conn->response_info.num_headers,
19031
0
                              "Content-Length"))
19032
0
             != NULL) {
19033
0
    char *endptr = NULL;
19034
0
    conn->content_len = strtoll(cl, &endptr, 10);
19035
0
    if ((endptr == cl) || (conn->content_len < 0)) {
19036
0
      mg_snprintf(conn,
19037
0
                  NULL, /* No truncation check for ebuf */
19038
0
                  ebuf,
19039
0
                  ebuf_len,
19040
0
                  "%s",
19041
0
                  "Bad request");
19042
0
      *err = 411;
19043
0
      return 0;
19044
0
    }
19045
    /* Publish the content length back to the response info. */
19046
0
    conn->response_info.content_length = conn->content_len;
19047
19048
    /* TODO: check if it is still used in response_info */
19049
0
    conn->request_info.content_length = conn->content_len;
19050
19051
    /* TODO: we should also consider HEAD method */
19052
0
    if (conn->response_info.status_code == 304) {
19053
0
      conn->content_len = 0;
19054
0
    }
19055
0
  } else {
19056
    /* TODO: we should also consider HEAD method */
19057
0
    if (((conn->response_info.status_code >= 100)
19058
0
         && (conn->response_info.status_code <= 199))
19059
0
        || (conn->response_info.status_code == 204)
19060
0
        || (conn->response_info.status_code == 304)) {
19061
0
      conn->content_len = 0;
19062
0
    } else {
19063
0
      conn->content_len = -1; /* unknown content length */
19064
0
    }
19065
0
  }
19066
19067
0
  return 1;
19068
0
}
19069
19070
19071
CIVETWEB_API int
19072
mg_get_response(struct mg_connection *conn,
19073
                char *ebuf,
19074
                size_t ebuf_len,
19075
                int timeout)
19076
34
{
19077
34
  int err, ret;
19078
34
  char txt[32]; /* will not overflow */
19079
34
  char *save_timeout;
19080
34
  char *new_timeout;
19081
19082
34
  if (ebuf_len > 0) {
19083
34
    ebuf[0] = '\0';
19084
34
  }
19085
19086
34
  if (!conn) {
19087
0
    mg_snprintf(conn,
19088
0
                NULL, /* No truncation check for ebuf */
19089
0
                ebuf,
19090
0
                ebuf_len,
19091
0
                "%s",
19092
0
                "Parameter error");
19093
0
    return -1;
19094
0
  }
19095
19096
  /* Reset the previous responses */
19097
34
  conn->data_len = 0;
19098
19099
  /* Implementation of API function for HTTP clients */
19100
34
  save_timeout = conn->dom_ctx->config[REQUEST_TIMEOUT];
19101
19102
34
  if (timeout >= 0) {
19103
34
    mg_snprintf(conn, NULL, txt, sizeof(txt), "%i", timeout);
19104
34
    new_timeout = txt;
19105
34
  } else {
19106
0
    new_timeout = NULL;
19107
0
  }
19108
19109
34
  conn->dom_ctx->config[REQUEST_TIMEOUT] = new_timeout;
19110
34
  ret = get_response(conn, ebuf, ebuf_len, &err);
19111
34
  conn->dom_ctx->config[REQUEST_TIMEOUT] = save_timeout;
19112
19113
  /* TODO: here, the URI is the http response code */
19114
34
  conn->request_info.local_uri_raw = conn->request_info.request_uri;
19115
34
  conn->request_info.local_uri = conn->request_info.local_uri_raw;
19116
19117
  /* TODO (mid): Define proper return values - maybe return length?
19118
   * For the first test use <0 for error and >0 for OK */
19119
34
  return (ret == 0) ? -1 : +1;
19120
34
}
19121
19122
19123
CIVETWEB_API struct mg_connection *
19124
mg_download(const char *host,
19125
            int port,
19126
            int use_ssl,
19127
            char *ebuf,
19128
            size_t ebuf_len,
19129
            const char *fmt,
19130
            ...)
19131
0
{
19132
0
  struct mg_connection *conn;
19133
0
  va_list ap;
19134
0
  int i;
19135
0
  int reqerr;
19136
19137
0
  if (ebuf_len > 0) {
19138
0
    ebuf[0] = '\0';
19139
0
  }
19140
19141
0
  va_start(ap, fmt);
19142
19143
  /* open a connection */
19144
0
  conn = mg_connect_client(host, port, use_ssl, ebuf, ebuf_len);
19145
19146
0
  if (conn != NULL) {
19147
0
    i = mg_vprintf(conn, fmt, ap);
19148
0
    if (i <= 0) {
19149
0
      mg_snprintf(conn,
19150
0
                  NULL, /* No truncation check for ebuf */
19151
0
                  ebuf,
19152
0
                  ebuf_len,
19153
0
                  "%s",
19154
0
                  "Error sending request");
19155
0
    } else {
19156
      /* make sure the buffer is clear */
19157
0
      conn->data_len = 0;
19158
0
      get_response(conn, ebuf, ebuf_len, &reqerr);
19159
19160
      /* TODO: here, the URI is the http response code */
19161
0
      conn->request_info.local_uri = conn->request_info.request_uri;
19162
0
    }
19163
0
  }
19164
19165
  /* if an error occurred, close the connection */
19166
0
  if ((ebuf[0] != '\0') && (conn != NULL)) {
19167
0
    mg_close_connection(conn);
19168
0
    conn = NULL;
19169
0
  }
19170
19171
0
  va_end(ap);
19172
0
  return conn;
19173
0
}
19174
19175
19176
struct websocket_client_thread_data {
19177
  struct mg_connection *conn;
19178
  mg_websocket_data_handler data_handler;
19179
  mg_websocket_close_handler close_handler;
19180
  void *callback_data;
19181
};
19182
19183
19184
#if defined(USE_WEBSOCKET)
19185
#if defined(_WIN32)
19186
static unsigned __stdcall websocket_client_thread(void *data)
19187
#else
19188
static void *
19189
websocket_client_thread(void *data)
19190
#endif
19191
{
19192
  struct websocket_client_thread_data *cdata =
19193
      (struct websocket_client_thread_data *)data;
19194
19195
  void *user_thread_ptr = NULL;
19196
19197
#if !defined(_WIN32) && !defined(__ZEPHYR__)
19198
  struct sigaction sa;
19199
19200
  /* Ignore SIGPIPE */
19201
  memset(&sa, 0, sizeof(sa));
19202
  sa.sa_handler = SIG_IGN;
19203
  sigaction(SIGPIPE, &sa, NULL);
19204
#endif
19205
19206
  mg_set_thread_name("ws-clnt");
19207
19208
  if (cdata->conn->phys_ctx) {
19209
    if (cdata->conn->phys_ctx->callbacks.init_thread) {
19210
      /* 3 indicates a websocket client thread */
19211
      /* TODO: check if conn->phys_ctx can be set */
19212
      user_thread_ptr = cdata->conn->phys_ctx->callbacks.init_thread(
19213
          cdata->conn->phys_ctx, 3);
19214
    }
19215
  }
19216
19217
  read_websocket(cdata->conn, cdata->data_handler, cdata->callback_data);
19218
19219
  DEBUG_TRACE("%s", "Websocket client thread exited\n");
19220
19221
  if (cdata->close_handler != NULL) {
19222
    cdata->close_handler(cdata->conn, cdata->callback_data);
19223
  }
19224
19225
  /* The websocket_client context has only this thread. If it runs out,
19226
  set the stop_flag to 2 (= "stopped"). */
19227
  STOP_FLAG_ASSIGN(&cdata->conn->phys_ctx->stop_flag, 2);
19228
19229
  if (cdata->conn->phys_ctx->callbacks.exit_thread) {
19230
    cdata->conn->phys_ctx->callbacks.exit_thread(cdata->conn->phys_ctx,
19231
                                                 3,
19232
                                                 user_thread_ptr);
19233
  }
19234
19235
  mg_free((void *)cdata);
19236
19237
#if defined(_WIN32)
19238
  return 0;
19239
#else
19240
  return NULL;
19241
#endif
19242
}
19243
#endif
19244
19245
19246
static struct mg_connection *
19247
mg_connect_websocket_client_impl(const struct mg_client_options *client_options,
19248
                                 int use_ssl,
19249
                                 char *error_buffer,
19250
                                 size_t error_buffer_size,
19251
                                 const char *path,
19252
                                 const char *origin,
19253
                                 const char *extensions,
19254
                                 mg_websocket_data_handler data_func,
19255
                                 mg_websocket_close_handler close_func,
19256
                                 void *user_data)
19257
0
{
19258
0
  struct mg_connection *conn = NULL;
19259
19260
#if defined(USE_WEBSOCKET)
19261
  struct websocket_client_thread_data *thread_data;
19262
  static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
19263
19264
  const char *host = client_options->host;
19265
  int i;
19266
19267
  struct mg_init_data init;
19268
  struct mg_error_data error;
19269
19270
  memset(&init, 0, sizeof(init));
19271
  memset(&error, 0, sizeof(error));
19272
  error.text_buffer_size = error_buffer_size;
19273
  error.text = error_buffer;
19274
19275
#if defined(__clang__)
19276
#pragma clang diagnostic push
19277
#pragma clang diagnostic ignored "-Wformat-nonliteral"
19278
#endif
19279
19280
  /* Establish the client connection and request upgrade */
19281
  conn = mg_connect_client_impl(client_options, use_ssl, &init, &error);
19282
19283
  /* Connection object will be null if something goes wrong */
19284
  if (conn == NULL) {
19285
    /* error_buffer should be already filled ... */
19286
    if (!error_buffer[0]) {
19287
      /* ... if not add an error message */
19288
      mg_snprintf(conn,
19289
                  NULL, /* No truncation check for ebuf */
19290
                  error_buffer,
19291
                  error_buffer_size,
19292
                  "Unexpected error");
19293
    }
19294
    return NULL;
19295
  }
19296
19297
  if (origin != NULL) {
19298
    if (extensions != NULL) {
19299
      i = mg_printf(conn,
19300
                    "GET %s HTTP/1.1\r\n"
19301
                    "Host: %s\r\n"
19302
                    "Upgrade: websocket\r\n"
19303
                    "Connection: Upgrade\r\n"
19304
                    "Sec-WebSocket-Key: %s\r\n"
19305
                    "Sec-WebSocket-Version: 13\r\n"
19306
                    "Sec-WebSocket-Extensions: %s\r\n"
19307
                    "Origin: %s\r\n"
19308
                    "\r\n",
19309
                    path,
19310
                    host,
19311
                    magic,
19312
                    extensions,
19313
                    origin);
19314
    } else {
19315
      i = mg_printf(conn,
19316
                    "GET %s HTTP/1.1\r\n"
19317
                    "Host: %s\r\n"
19318
                    "Upgrade: websocket\r\n"
19319
                    "Connection: Upgrade\r\n"
19320
                    "Sec-WebSocket-Key: %s\r\n"
19321
                    "Sec-WebSocket-Version: 13\r\n"
19322
                    "Origin: %s\r\n"
19323
                    "\r\n",
19324
                    path,
19325
                    host,
19326
                    magic,
19327
                    origin);
19328
    }
19329
  } else {
19330
19331
    if (extensions != NULL) {
19332
      i = mg_printf(conn,
19333
                    "GET %s HTTP/1.1\r\n"
19334
                    "Host: %s\r\n"
19335
                    "Upgrade: websocket\r\n"
19336
                    "Connection: Upgrade\r\n"
19337
                    "Sec-WebSocket-Key: %s\r\n"
19338
                    "Sec-WebSocket-Version: 13\r\n"
19339
                    "Sec-WebSocket-Extensions: %s\r\n"
19340
                    "\r\n",
19341
                    path,
19342
                    host,
19343
                    magic,
19344
                    extensions);
19345
    } else {
19346
      i = mg_printf(conn,
19347
                    "GET %s HTTP/1.1\r\n"
19348
                    "Host: %s\r\n"
19349
                    "Upgrade: websocket\r\n"
19350
                    "Connection: Upgrade\r\n"
19351
                    "Sec-WebSocket-Key: %s\r\n"
19352
                    "Sec-WebSocket-Version: 13\r\n"
19353
                    "\r\n",
19354
                    path,
19355
                    host,
19356
                    magic);
19357
    }
19358
  }
19359
  if (i <= 0) {
19360
    mg_snprintf(conn,
19361
                NULL, /* No truncation check for ebuf */
19362
                error_buffer,
19363
                error_buffer_size,
19364
                "%s",
19365
                "Error sending request");
19366
    mg_close_connection(conn);
19367
    return NULL;
19368
  }
19369
19370
  conn->data_len = 0;
19371
  if (!get_response(conn, error_buffer, error_buffer_size, &i)) {
19372
    mg_close_connection(conn);
19373
    return NULL;
19374
  }
19375
  conn->request_info.local_uri_raw = conn->request_info.request_uri;
19376
  conn->request_info.local_uri = conn->request_info.local_uri_raw;
19377
19378
#if defined(__clang__)
19379
#pragma clang diagnostic pop
19380
#endif
19381
19382
  if (conn->response_info.status_code != 101) {
19383
    /* We sent an "upgrade" request. For a correct websocket
19384
     * protocol handshake, we expect a "101 Continue" response.
19385
     * Otherwise it is a protocol violation. Maybe the HTTP
19386
     * Server does not know websockets. */
19387
    if (!*error_buffer) {
19388
      /* set an error, if not yet set */
19389
      mg_snprintf(conn,
19390
                  NULL, /* No truncation check for ebuf */
19391
                  error_buffer,
19392
                  error_buffer_size,
19393
                  "Unexpected server reply");
19394
    }
19395
19396
    DEBUG_TRACE("Websocket client connect error: %s\r\n", error_buffer);
19397
    mg_close_connection(conn);
19398
    return NULL;
19399
  }
19400
19401
  thread_data = (struct websocket_client_thread_data *)mg_calloc_ctx(
19402
      1, sizeof(struct websocket_client_thread_data), conn->phys_ctx);
19403
  if (!thread_data) {
19404
    DEBUG_TRACE("%s\r\n", "Out of memory");
19405
    mg_close_connection(conn);
19406
    return NULL;
19407
  }
19408
19409
  thread_data->conn = conn;
19410
  thread_data->data_handler = data_func;
19411
  thread_data->close_handler = close_func;
19412
  thread_data->callback_data = user_data;
19413
19414
  conn->phys_ctx->worker_threadids =
19415
      (pthread_t *)mg_calloc_ctx(1, sizeof(pthread_t), conn->phys_ctx);
19416
  if (!conn->phys_ctx->worker_threadids) {
19417
    DEBUG_TRACE("%s\r\n", "Out of memory");
19418
    mg_free(thread_data);
19419
    mg_close_connection(conn);
19420
    return NULL;
19421
  }
19422
19423
  /* Now upgrade to ws/wss client context */
19424
  conn->phys_ctx->user_data = user_data;
19425
  conn->phys_ctx->context_type = CONTEXT_WS_CLIENT;
19426
  conn->phys_ctx->cfg_max_worker_threads = 1; /* one worker thread */
19427
  conn->phys_ctx->spawned_worker_threads = 1; /* one worker thread */
19428
19429
  /* Start a thread to read the websocket client connection
19430
   * This thread will automatically stop when mg_disconnect is
19431
   * called on the client connection */
19432
  if (mg_start_thread_with_id(websocket_client_thread,
19433
                              thread_data,
19434
                              conn->phys_ctx->worker_threadids)
19435
      != 0) {
19436
    conn->phys_ctx->spawned_worker_threads = 0;
19437
    mg_free(thread_data);
19438
    mg_close_connection(conn);
19439
    conn = NULL;
19440
    DEBUG_TRACE("%s",
19441
                "Websocket client connect thread could not be started\r\n");
19442
  }
19443
19444
#else
19445
  /* Appease "unused parameter" warnings */
19446
0
  (void)client_options;
19447
0
  (void)use_ssl;
19448
0
  (void)error_buffer;
19449
0
  (void)error_buffer_size;
19450
0
  (void)path;
19451
0
  (void)origin;
19452
0
  (void)extensions;
19453
0
  (void)user_data;
19454
0
  (void)data_func;
19455
0
  (void)close_func;
19456
0
#endif
19457
19458
0
  return conn;
19459
0
}
19460
19461
19462
CIVETWEB_API struct mg_connection *
19463
mg_connect_websocket_client(const char *host,
19464
                            int port,
19465
                            int use_ssl,
19466
                            char *error_buffer,
19467
                            size_t error_buffer_size,
19468
                            const char *path,
19469
                            const char *origin,
19470
                            mg_websocket_data_handler data_func,
19471
                            mg_websocket_close_handler close_func,
19472
                            void *user_data)
19473
0
{
19474
0
  struct mg_client_options client_options;
19475
0
  memset(&client_options, 0, sizeof(client_options));
19476
0
  client_options.host = host;
19477
0
  client_options.port = port;
19478
19479
0
  return mg_connect_websocket_client_impl(&client_options,
19480
0
                                          use_ssl,
19481
0
                                          error_buffer,
19482
0
                                          error_buffer_size,
19483
0
                                          path,
19484
0
                                          origin,
19485
0
                                          NULL,
19486
0
                                          data_func,
19487
0
                                          close_func,
19488
0
                                          user_data);
19489
0
}
19490
19491
19492
CIVETWEB_API struct mg_connection *
19493
mg_connect_websocket_client_secure(
19494
    const struct mg_client_options *client_options,
19495
    char *error_buffer,
19496
    size_t error_buffer_size,
19497
    const char *path,
19498
    const char *origin,
19499
    mg_websocket_data_handler data_func,
19500
    mg_websocket_close_handler close_func,
19501
    void *user_data)
19502
0
{
19503
0
  if (!client_options) {
19504
0
    return NULL;
19505
0
  }
19506
0
  return mg_connect_websocket_client_impl(client_options,
19507
0
                                          1,
19508
0
                                          error_buffer,
19509
0
                                          error_buffer_size,
19510
0
                                          path,
19511
0
                                          origin,
19512
0
                                          NULL,
19513
0
                                          data_func,
19514
0
                                          close_func,
19515
0
                                          user_data);
19516
0
}
19517
19518
19519
CIVETWEB_API struct mg_connection *
19520
mg_connect_websocket_client_extensions(const char *host,
19521
                                       int port,
19522
                                       int use_ssl,
19523
                                       char *error_buffer,
19524
                                       size_t error_buffer_size,
19525
                                       const char *path,
19526
                                       const char *origin,
19527
                                       const char *extensions,
19528
                                       mg_websocket_data_handler data_func,
19529
                                       mg_websocket_close_handler close_func,
19530
                                       void *user_data)
19531
0
{
19532
0
  struct mg_client_options client_options;
19533
0
  memset(&client_options, 0, sizeof(client_options));
19534
0
  client_options.host = host;
19535
0
  client_options.port = port;
19536
19537
0
  return mg_connect_websocket_client_impl(&client_options,
19538
0
                                          use_ssl,
19539
0
                                          error_buffer,
19540
0
                                          error_buffer_size,
19541
0
                                          path,
19542
0
                                          origin,
19543
0
                                          extensions,
19544
0
                                          data_func,
19545
0
                                          close_func,
19546
0
                                          user_data);
19547
0
}
19548
19549
19550
CIVETWEB_API struct mg_connection *
19551
mg_connect_websocket_client_secure_extensions(
19552
    const struct mg_client_options *client_options,
19553
    char *error_buffer,
19554
    size_t error_buffer_size,
19555
    const char *path,
19556
    const char *origin,
19557
    const char *extensions,
19558
    mg_websocket_data_handler data_func,
19559
    mg_websocket_close_handler close_func,
19560
    void *user_data)
19561
0
{
19562
0
  if (!client_options) {
19563
0
    return NULL;
19564
0
  }
19565
0
  return mg_connect_websocket_client_impl(client_options,
19566
0
                                          1,
19567
0
                                          error_buffer,
19568
0
                                          error_buffer_size,
19569
0
                                          path,
19570
0
                                          origin,
19571
0
                                          extensions,
19572
0
                                          data_func,
19573
0
                                          close_func,
19574
0
                                          user_data);
19575
0
}
19576
19577
19578
/* Prepare connection data structure */
19579
static void
19580
init_connection(struct mg_connection *conn)
19581
0
{
19582
  /* Is keep alive allowed by the server */
19583
0
  int keep_alive_enabled =
19584
0
      !mg_strcasecmp(conn->dom_ctx->config[ENABLE_KEEP_ALIVE], "yes");
19585
19586
0
  if (!keep_alive_enabled) {
19587
0
    conn->must_close = 1;
19588
0
  }
19589
19590
  /* Important: on new connection, reset the receiving buffer. Credit
19591
   * goes to crule42. */
19592
0
  conn->data_len = 0;
19593
0
  conn->handled_requests = 0;
19594
0
  conn->connection_type = CONNECTION_TYPE_INVALID;
19595
0
  conn->request_info.acceptedWebSocketSubprotocol = NULL;
19596
0
  mg_set_user_connection_data(conn, NULL);
19597
19598
#if defined(USE_SERVER_STATS)
19599
  conn->conn_state = 2; /* init */
19600
#endif
19601
19602
  /* call the init_connection callback if assigned */
19603
0
  if (conn->phys_ctx->callbacks.init_connection != NULL) {
19604
0
    if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
19605
0
      void *conn_data = NULL;
19606
0
      conn->phys_ctx->callbacks.init_connection(conn, &conn_data);
19607
0
      mg_set_user_connection_data(conn, conn_data);
19608
0
    }
19609
0
  }
19610
0
}
19611
19612
19613
/* Process a connection - may handle multiple requests
19614
 * using the same connection.
19615
 * Must be called with a valid connection (conn  and
19616
 * conn->phys_ctx must be valid).
19617
 */
19618
static void
19619
process_new_connection(struct mg_connection *conn)
19620
0
{
19621
0
  struct mg_request_info *ri = &conn->request_info;
19622
0
  int keep_alive, discard_len;
19623
0
  char ebuf[100];
19624
0
  const char *hostend;
19625
0
  int reqerr, uri_type;
19626
19627
#if defined(USE_SERVER_STATS)
19628
  ptrdiff_t mcon = mg_atomic_inc(&(conn->phys_ctx->active_connections));
19629
  mg_atomic_add(&(conn->phys_ctx->total_connections), 1);
19630
  mg_atomic_max(&(conn->phys_ctx->max_active_connections), mcon);
19631
#endif
19632
19633
0
  DEBUG_TRACE("Start processing connection from %s",
19634
0
              conn->request_info.remote_addr);
19635
19636
  /* Loop over multiple requests sent using the same connection
19637
   * (while "keep alive"). */
19638
0
  do {
19639
0
    DEBUG_TRACE("calling get_request (%i times for this connection)",
19640
0
                conn->handled_requests + 1);
19641
19642
#if defined(USE_SERVER_STATS)
19643
    conn->conn_state = 3; /* ready */
19644
#endif
19645
19646
0
    if (!get_request(conn, ebuf, sizeof(ebuf), &reqerr)) {
19647
      /* The request sent by the client could not be understood by
19648
       * the server, or it was incomplete or a timeout. Send an
19649
       * error message and close the connection. */
19650
0
      if (reqerr > 0) {
19651
0
        DEBUG_ASSERT(ebuf[0] != '\0');
19652
0
        mg_send_http_error(conn, reqerr, "%s", ebuf);
19653
0
      }
19654
19655
0
    } else if (strcmp(ri->http_version, "1.0")
19656
0
               && strcmp(ri->http_version, "1.1")) {
19657
      /* HTTP/2 is not allowed here */
19658
0
      mg_snprintf(conn,
19659
0
                  NULL, /* No truncation check for ebuf */
19660
0
                  ebuf,
19661
0
                  sizeof(ebuf),
19662
0
                  "Bad HTTP version: [%s]",
19663
0
                  ri->http_version);
19664
0
      mg_send_http_error(conn, 505, "%s", ebuf);
19665
0
    }
19666
19667
0
    if (ebuf[0] == '\0') {
19668
0
      uri_type = get_uri_type(conn->request_info.request_uri);
19669
0
      switch (uri_type) {
19670
0
      case 1:
19671
        /* Asterisk */
19672
0
        conn->request_info.local_uri_raw = 0;
19673
        /* TODO: Deal with '*'. */
19674
0
        break;
19675
0
      case 2:
19676
        /* relative uri */
19677
0
        conn->request_info.local_uri_raw =
19678
0
            conn->request_info.request_uri;
19679
0
        break;
19680
0
      case 3:
19681
0
      case 4:
19682
        /* absolute uri (with/without port) */
19683
0
        hostend = get_rel_url_at_current_server(
19684
0
            conn->request_info.request_uri, conn);
19685
0
        if (hostend) {
19686
0
          conn->request_info.local_uri_raw = hostend;
19687
0
        } else {
19688
0
          conn->request_info.local_uri_raw = NULL;
19689
0
        }
19690
0
        break;
19691
0
      default:
19692
0
        mg_snprintf(conn,
19693
0
                    NULL, /* No truncation check for ebuf */
19694
0
                    ebuf,
19695
0
                    sizeof(ebuf),
19696
0
                    "Invalid URI");
19697
0
        mg_send_http_error(conn, 400, "%s", ebuf);
19698
0
        conn->request_info.local_uri_raw = NULL;
19699
0
        break;
19700
0
      }
19701
0
      conn->request_info.local_uri =
19702
0
          (char *)conn->request_info.local_uri_raw;
19703
0
    }
19704
19705
0
    if (ebuf[0] != '\0') {
19706
0
      conn->protocol_type = -1;
19707
19708
0
    } else {
19709
      /* HTTP/1 allows protocol upgrade */
19710
0
      conn->protocol_type = should_switch_to_protocol(conn);
19711
19712
0
      if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
19713
        /* This will occur, if a HTTP/1.1 request should be upgraded
19714
         * to HTTP/2 - but not if HTTP/2 is negotiated using ALPN.
19715
         * Since most (all?) major browsers only support HTTP/2 using
19716
         * ALPN, this is hard to test and very low priority.
19717
         * Deactivate it (at least for now).
19718
         */
19719
0
        conn->protocol_type = PROTOCOL_TYPE_HTTP1;
19720
0
      }
19721
0
    }
19722
19723
0
    DEBUG_TRACE("http: %s, error: %s",
19724
0
                (ri->http_version ? ri->http_version : "none"),
19725
0
                (ebuf[0] ? ebuf : "none"));
19726
19727
0
    if (ebuf[0] == '\0') {
19728
0
      if (conn->request_info.local_uri) {
19729
19730
        /* handle request to local server */
19731
0
        handle_request_stat_log(conn);
19732
19733
0
      } else {
19734
        /* TODO: handle non-local request (PROXY) */
19735
0
        conn->must_close = 1;
19736
0
      }
19737
0
    } else {
19738
0
      conn->must_close = 1;
19739
0
    }
19740
19741
    /* Response complete. Free header buffer */
19742
0
    free_buffered_response_header_list(conn);
19743
19744
0
    if (ri->remote_user != NULL) {
19745
0
      mg_free((void *)ri->remote_user);
19746
      /* Important! When having connections with and without auth
19747
       * would cause double free and then crash */
19748
0
      ri->remote_user = NULL;
19749
0
    }
19750
19751
    /* NOTE(lsm): order is important here. should_keep_alive() call
19752
     * is using parsed request, which will be invalid after
19753
     * memmove's below.
19754
     * Therefore, memorize should_keep_alive() result now for later
19755
     * use in loop exit condition. */
19756
    /* Enable it only if this request is completely discardable. */
19757
0
    keep_alive = STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)
19758
0
                 && should_keep_alive(conn) && (conn->content_len >= 0)
19759
0
                 && (conn->request_len > 0)
19760
0
                 && ((conn->is_chunked == 4)
19761
0
                     || (!conn->is_chunked
19762
0
                         && ((conn->consumed_content == conn->content_len)
19763
0
                             || ((conn->request_len + conn->content_len)
19764
0
                                 <= conn->data_len))))
19765
0
                 && (conn->protocol_type == PROTOCOL_TYPE_HTTP1);
19766
19767
0
    if (keep_alive) {
19768
      /* Discard all buffered data for this request */
19769
0
      discard_len =
19770
0
          ((conn->request_len + conn->content_len) < conn->data_len)
19771
0
              ? (int)(conn->request_len + conn->content_len)
19772
0
              : conn->data_len;
19773
0
      conn->data_len -= discard_len;
19774
19775
0
      if (conn->data_len > 0) {
19776
0
        DEBUG_TRACE("discard_len = %d", discard_len);
19777
0
        memmove(conn->buf,
19778
0
                conn->buf + discard_len,
19779
0
                (size_t)conn->data_len);
19780
0
      }
19781
0
    }
19782
19783
0
    DEBUG_ASSERT(conn->data_len >= 0);
19784
0
    DEBUG_ASSERT(conn->data_len <= conn->buf_size);
19785
19786
0
    if ((conn->data_len < 0) || (conn->data_len > conn->buf_size)) {
19787
0
      DEBUG_TRACE("internal error: data_len = %li, buf_size = %li",
19788
0
                  (long int)conn->data_len,
19789
0
                  (long int)conn->buf_size);
19790
0
      break;
19791
0
    }
19792
0
    conn->handled_requests++;
19793
0
  } while (keep_alive);
19794
19795
0
  DEBUG_TRACE("Done processing connection from %s (%f sec)",
19796
0
              conn->request_info.remote_addr,
19797
0
              difftime(time(NULL), conn->conn_birth_time));
19798
19799
0
  close_connection(conn);
19800
19801
#if defined(USE_SERVER_STATS)
19802
  mg_atomic_add(&(conn->phys_ctx->total_requests), conn->handled_requests);
19803
  mg_atomic_dec(&(conn->phys_ctx->active_connections));
19804
#endif
19805
0
}
19806
19807
static int
19808
mg_start_worker_thread(struct mg_context *ctx,
19809
                       int only_if_no_idle_threads); /* forward declaration */
19810
19811
#if defined(ALTERNATIVE_QUEUE)
19812
19813
static void
19814
produce_socket(struct mg_context *ctx, const struct socket *sp)
19815
{
19816
  unsigned int i;
19817
19818
  (void)mg_start_worker_thread(
19819
      ctx, 1); /* will start a worker-thread only if there aren't currently
19820
                  any idle worker-threads */
19821
19822
  while (!ctx->stop_flag) {
19823
    for (i = 0; i < ctx->spawned_worker_threads; i++) {
19824
      /* find a free worker slot and signal it */
19825
      if (ctx->client_socks[i].in_use == 2) {
19826
        (void)pthread_mutex_lock(&ctx->thread_mutex);
19827
        if ((ctx->client_socks[i].in_use == 2) && !ctx->stop_flag) {
19828
          ctx->client_socks[i] = *sp;
19829
          ctx->client_socks[i].in_use = 1;
19830
          /* socket has been moved to the consumer */
19831
          (void)pthread_mutex_unlock(&ctx->thread_mutex);
19832
          (void)event_signal(ctx->client_wait_events[i]);
19833
          return;
19834
        }
19835
        (void)pthread_mutex_unlock(&ctx->thread_mutex);
19836
      }
19837
    }
19838
    /* queue is full */
19839
    mg_sleep(1);
19840
  }
19841
  /* must consume */
19842
  set_blocking_mode(sp->sock);
19843
  closesocket(sp->sock);
19844
}
19845
19846
19847
static int
19848
consume_socket(struct mg_context *ctx,
19849
               struct socket *sp,
19850
               int thread_index,
19851
               int counter_was_preincremented)
19852
{
19853
  DEBUG_TRACE("%s", "going idle");
19854
  (void)pthread_mutex_lock(&ctx->thread_mutex);
19855
  if (counter_was_preincremented
19856
      == 0) { /* first call only: the master-thread pre-incremented this
19857
               before he spawned us */
19858
    ctx->idle_worker_thread_count++;
19859
  }
19860
  ctx->client_socks[thread_index].in_use = 2;
19861
  (void)pthread_mutex_unlock(&ctx->thread_mutex);
19862
19863
  event_wait(ctx->client_wait_events[thread_index]);
19864
19865
  (void)pthread_mutex_lock(&ctx->thread_mutex);
19866
  *sp = ctx->client_socks[thread_index];
19867
  if (ctx->stop_flag) {
19868
    (void)pthread_mutex_unlock(&ctx->thread_mutex);
19869
    if (sp->in_use == 1) {
19870
      /* must consume */
19871
      set_blocking_mode(sp->sock);
19872
      closesocket(sp->sock);
19873
    }
19874
    return 0;
19875
  }
19876
  ctx->idle_worker_thread_count--;
19877
  (void)pthread_mutex_unlock(&ctx->thread_mutex);
19878
  if (sp->in_use == 1) {
19879
    DEBUG_TRACE("grabbed socket %d, going busy", sp->sock);
19880
    return 1;
19881
  }
19882
  /* must not reach here */
19883
  DEBUG_ASSERT(0);
19884
  return 0;
19885
}
19886
19887
#else /* ALTERNATIVE_QUEUE */
19888
19889
/* Worker threads take accepted socket from the queue */
19890
static int
19891
consume_socket(struct mg_context *ctx,
19892
               struct socket *sp,
19893
               int thread_index,
19894
               int counter_was_preincremented)
19895
0
{
19896
0
  (void)thread_index;
19897
19898
0
  DEBUG_TRACE("%s", "going idle");
19899
0
  (void)pthread_mutex_lock(&ctx->thread_mutex);
19900
0
  if (counter_was_preincremented
19901
0
      == 0) { /* first call only: the master-thread pre-incremented this
19902
               before he spawned us */
19903
0
    ctx->idle_worker_thread_count++;
19904
0
  }
19905
19906
  /* If the queue is empty, wait. We're idle at this point. */
19907
0
  while ((ctx->sq_head == ctx->sq_tail)
19908
0
         && (STOP_FLAG_IS_ZERO(&ctx->stop_flag))) {
19909
0
    pthread_cond_wait(&ctx->sq_full, &ctx->thread_mutex);
19910
0
  }
19911
19912
  /* If we're stopping, sq_head may be equal to sq_tail. */
19913
0
  if (ctx->sq_head > ctx->sq_tail) {
19914
    /* Copy socket from the queue and increment tail */
19915
0
    *sp = ctx->squeue[ctx->sq_tail % ctx->sq_size];
19916
0
    ctx->sq_tail++;
19917
19918
0
    DEBUG_TRACE("grabbed socket %d, going busy", sp ? sp->sock : -1);
19919
19920
    /* Wrap pointers if needed */
19921
0
    while (ctx->sq_tail > ctx->sq_size) {
19922
0
      ctx->sq_tail -= ctx->sq_size;
19923
0
      ctx->sq_head -= ctx->sq_size;
19924
0
    }
19925
0
  }
19926
19927
0
  (void)pthread_cond_signal(&ctx->sq_empty);
19928
19929
0
  ctx->idle_worker_thread_count--;
19930
0
  (void)pthread_mutex_unlock(&ctx->thread_mutex);
19931
19932
0
  return STOP_FLAG_IS_ZERO(&ctx->stop_flag);
19933
0
}
19934
19935
19936
/* Master thread adds accepted socket to a queue */
19937
static void
19938
produce_socket(struct mg_context *ctx, const struct socket *sp)
19939
0
{
19940
0
  int queue_filled;
19941
19942
0
  (void)pthread_mutex_lock(&ctx->thread_mutex);
19943
19944
0
  queue_filled = ctx->sq_head - ctx->sq_tail;
19945
19946
  /* If the queue is full, wait */
19947
0
  while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
19948
0
         && (queue_filled >= ctx->sq_size)) {
19949
0
    ctx->sq_blocked = 1; /* Status information: All threads busy */
19950
#if defined(USE_SERVER_STATS)
19951
    if (queue_filled > ctx->sq_max_fill) {
19952
      ctx->sq_max_fill = queue_filled;
19953
    }
19954
#endif
19955
0
    (void)pthread_cond_wait(&ctx->sq_empty, &ctx->thread_mutex);
19956
0
    ctx->sq_blocked = 0; /* Not blocked now */
19957
0
    queue_filled = ctx->sq_head - ctx->sq_tail;
19958
0
  }
19959
19960
0
  if (queue_filled < ctx->sq_size) {
19961
    /* Copy socket to the queue and increment head */
19962
0
    ctx->squeue[ctx->sq_head % ctx->sq_size] = *sp;
19963
0
    ctx->sq_head++;
19964
0
    DEBUG_TRACE("queued socket %d", sp ? sp->sock : -1);
19965
0
  }
19966
19967
0
  queue_filled = ctx->sq_head - ctx->sq_tail;
19968
#if defined(USE_SERVER_STATS)
19969
  if (queue_filled > ctx->sq_max_fill) {
19970
    ctx->sq_max_fill = queue_filled;
19971
  }
19972
#endif
19973
19974
0
  (void)pthread_cond_signal(&ctx->sq_full);
19975
0
  (void)pthread_mutex_unlock(&ctx->thread_mutex);
19976
19977
0
  (void)mg_start_worker_thread(
19978
0
      ctx, 1); /* will start a worker-thread only if there aren't currently
19979
                  any idle worker-threads */
19980
0
}
19981
#endif /* ALTERNATIVE_QUEUE */
19982
19983
19984
static void
19985
worker_thread_run(struct mg_connection *conn)
19986
0
{
19987
0
  struct mg_context *ctx = conn->phys_ctx;
19988
0
  int thread_index;
19989
0
  struct mg_workerTLS tls;
19990
0
  int first_call_to_consume_socket = 1;
19991
19992
0
  mg_set_thread_name("worker");
19993
19994
0
  tls.is_master = 0;
19995
0
  tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
19996
#if defined(_WIN32)
19997
  tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
19998
#endif
19999
20000
  /* Initialize thread local storage before calling any callback */
20001
0
  pthread_setspecific(sTlsKey, &tls);
20002
20003
  /* Check if there is a user callback */
20004
0
  if (ctx->callbacks.init_thread) {
20005
    /* call init_thread for a worker thread (type 1), and store the
20006
     * return value */
20007
0
    tls.user_ptr = ctx->callbacks.init_thread(ctx, 1);
20008
0
  } else {
20009
    /* No callback: set user pointer to NULL */
20010
0
    tls.user_ptr = NULL;
20011
0
  }
20012
20013
  /* Connection structure has been pre-allocated */
20014
0
  thread_index = (int)(conn - ctx->worker_connections);
20015
0
  if ((thread_index < 0)
20016
0
      || ((unsigned)thread_index >= (unsigned)ctx->cfg_max_worker_threads)) {
20017
0
    mg_cry_ctx_internal(ctx,
20018
0
                        "Internal error: Invalid worker index %i",
20019
0
                        thread_index);
20020
0
    return;
20021
0
  }
20022
20023
  /* Request buffers are not pre-allocated. They are private to the
20024
   * request and do not contain any state information that might be
20025
   * of interest to anyone observing a server status.  */
20026
0
  conn->buf = (char *)mg_malloc_ctx(ctx->max_request_size, conn->phys_ctx);
20027
0
  if (conn->buf == NULL) {
20028
0
    mg_cry_ctx_internal(
20029
0
        ctx,
20030
0
        "Out of memory: Cannot allocate buffer for worker %i",
20031
0
        thread_index);
20032
0
    return;
20033
0
  }
20034
0
  conn->buf_size = (int)ctx->max_request_size;
20035
20036
0
  conn->dom_ctx = &(ctx->dd); /* Use default domain and default host */
20037
20038
0
  conn->tls_user_ptr = tls.user_ptr; /* store ptr for quick access */
20039
20040
0
  conn->request_info.user_data = ctx->user_data;
20041
  /* Allocate a mutex for this connection to allow communication both
20042
   * within the request handler and from elsewhere in the application
20043
   */
20044
0
  if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) {
20045
0
    mg_free(conn->buf);
20046
0
    mg_cry_ctx_internal(ctx, "%s", "Cannot create mutex");
20047
0
    return;
20048
0
  }
20049
20050
#if defined(USE_SERVER_STATS)
20051
  conn->conn_state = 1; /* not consumed */
20052
#endif
20053
20054
  /* Call consume_socket() even when ctx->stop_flag > 0, to let it
20055
   * signal sq_empty condvar to wake up the master waiting in
20056
   * produce_socket() */
20057
0
  while (consume_socket(
20058
0
      ctx, &conn->client, thread_index, first_call_to_consume_socket)) {
20059
0
    first_call_to_consume_socket = 0;
20060
20061
    /* New connections must start with new protocol negotiation */
20062
0
    tls.alpn_proto = NULL;
20063
20064
#if defined(USE_SERVER_STATS)
20065
    conn->conn_close_time = 0;
20066
#endif
20067
0
    conn->conn_birth_time = time(NULL);
20068
20069
    /* Fill in IP, port info early so even if SSL setup below fails,
20070
     * error handler would have the corresponding info.
20071
     * Thanks to Johannes Winkelmann for the patch.
20072
     */
20073
0
    conn->request_info.remote_port =
20074
0
        ntohs(USA_IN_PORT_UNSAFE(&conn->client.rsa));
20075
20076
0
    conn->request_info.server_port =
20077
0
        ntohs(USA_IN_PORT_UNSAFE(&conn->client.lsa));
20078
20079
0
    sockaddr_to_string(conn->request_info.remote_addr,
20080
0
                       sizeof(conn->request_info.remote_addr),
20081
0
                       &conn->client.rsa);
20082
20083
0
    DEBUG_TRACE("Incoming %sconnection from %s",
20084
0
                (conn->client.is_ssl ? "SSL " : ""),
20085
0
                conn->request_info.remote_addr);
20086
20087
0
    conn->request_info.is_ssl = conn->client.is_ssl;
20088
20089
0
    if (conn->client.is_ssl) {
20090
20091
#if defined(USE_MBEDTLS)
20092
      /* HTTPS connection */
20093
      if (mbed_ssl_accept(&(conn->ssl),
20094
                          conn->dom_ctx->ssl_ctx,
20095
                          (int *)&(conn->client.sock),
20096
                          conn->phys_ctx)
20097
          == 0) {
20098
        /* conn->dom_ctx is set in get_request */
20099
        /* process HTTPS connection */
20100
        init_connection(conn);
20101
        conn->connection_type = CONNECTION_TYPE_REQUEST;
20102
        conn->protocol_type = PROTOCOL_TYPE_HTTP1;
20103
        process_new_connection(conn);
20104
      } else {
20105
        /* make sure the connection is cleaned up on SSL failure */
20106
        close_connection(conn);
20107
      }
20108
20109
#elif !defined(NO_SSL)
20110
      /* HTTPS connection */
20111
0
      if (sslize(conn, SSL_accept, NULL)) {
20112
        /* conn->dom_ctx is set in get_request */
20113
20114
        /* Get SSL client certificate information (if set) */
20115
0
        struct mg_client_cert client_cert;
20116
0
        if (ssl_get_client_cert_info(conn, &client_cert)) {
20117
0
          conn->request_info.client_cert = &client_cert;
20118
0
        }
20119
20120
        /* process HTTPS connection */
20121
#if defined(USE_HTTP2)
20122
        if ((tls.alpn_proto != NULL)
20123
            && (!memcmp(tls.alpn_proto, "\x02h2", 3))) {
20124
          /* process HTTPS/2 connection */
20125
          init_connection(conn);
20126
          conn->connection_type = CONNECTION_TYPE_REQUEST;
20127
          conn->protocol_type = PROTOCOL_TYPE_HTTP2;
20128
          conn->content_len =
20129
              -1;               /* content length is not predefined */
20130
          conn->is_chunked = 0; /* HTTP2 is never chunked */
20131
          process_new_http2_connection(conn);
20132
        } else
20133
#endif
20134
0
        {
20135
          /* process HTTPS/1.x or WEBSOCKET-SECURE connection */
20136
0
          init_connection(conn);
20137
0
          conn->connection_type = CONNECTION_TYPE_REQUEST;
20138
          /* Start with HTTP, WS will be an "upgrade" request later */
20139
0
          conn->protocol_type = PROTOCOL_TYPE_HTTP1;
20140
0
          process_new_connection(conn);
20141
0
        }
20142
20143
        /* Free client certificate info */
20144
0
        if (conn->request_info.client_cert) {
20145
0
          mg_free((void *)(conn->request_info.client_cert->subject));
20146
0
          mg_free((void *)(conn->request_info.client_cert->issuer));
20147
0
          mg_free((void *)(conn->request_info.client_cert->serial));
20148
0
          mg_free((void *)(conn->request_info.client_cert->finger));
20149
          /* Free certificate memory */
20150
0
          X509_free(
20151
0
              (X509 *)conn->request_info.client_cert->peer_cert);
20152
0
          conn->request_info.client_cert->peer_cert = 0;
20153
0
          conn->request_info.client_cert->subject = 0;
20154
0
          conn->request_info.client_cert->issuer = 0;
20155
0
          conn->request_info.client_cert->serial = 0;
20156
0
          conn->request_info.client_cert->finger = 0;
20157
0
          conn->request_info.client_cert = 0;
20158
0
        }
20159
0
      } else {
20160
        /* make sure the connection is cleaned up on SSL failure */
20161
0
        close_connection(conn);
20162
0
      }
20163
0
#endif
20164
20165
0
    } else {
20166
      /* process HTTP connection */
20167
0
      init_connection(conn);
20168
0
      conn->connection_type = CONNECTION_TYPE_REQUEST;
20169
      /* Start with HTTP, WS will be an "upgrade" request later */
20170
0
      conn->protocol_type = PROTOCOL_TYPE_HTTP1;
20171
0
      process_new_connection(conn);
20172
0
    }
20173
20174
0
    DEBUG_TRACE("%s", "Connection closed");
20175
20176
#if defined(USE_SERVER_STATS)
20177
    conn->conn_close_time = time(NULL);
20178
#endif
20179
0
  }
20180
20181
  /* Call exit thread user callback */
20182
0
  if (ctx->callbacks.exit_thread) {
20183
0
    ctx->callbacks.exit_thread(ctx, 1, tls.user_ptr);
20184
0
  }
20185
20186
  /* delete thread local storage objects */
20187
0
  pthread_setspecific(sTlsKey, NULL);
20188
#if defined(_WIN32)
20189
  CloseHandle(tls.pthread_cond_helper_mutex);
20190
#endif
20191
0
  pthread_mutex_destroy(&conn->mutex);
20192
20193
  /* Free the request buffer. */
20194
0
  conn->buf_size = 0;
20195
0
  mg_free(conn->buf);
20196
0
  conn->buf = NULL;
20197
20198
  /* Free cleaned URI (if any) */
20199
0
  if (conn->request_info.local_uri != conn->request_info.local_uri_raw) {
20200
0
    mg_free((void *)conn->request_info.local_uri);
20201
0
    conn->request_info.local_uri = NULL;
20202
0
  }
20203
20204
#if defined(USE_SERVER_STATS)
20205
  conn->conn_state = 9; /* done */
20206
#endif
20207
20208
0
  DEBUG_TRACE("%s", "exiting");
20209
0
}
20210
20211
20212
/* Threads have different return types on Windows and Unix. */
20213
#if defined(_WIN32)
20214
static unsigned __stdcall worker_thread(void *thread_func_param)
20215
{
20216
  worker_thread_run((struct mg_connection *)thread_func_param);
20217
  return 0;
20218
}
20219
#else
20220
static void *
20221
worker_thread(void *thread_func_param)
20222
0
{
20223
0
#if !defined(__ZEPHYR__)
20224
0
  struct sigaction sa;
20225
20226
  /* Ignore SIGPIPE */
20227
0
  memset(&sa, 0, sizeof(sa));
20228
0
  sa.sa_handler = SIG_IGN;
20229
0
  sigaction(SIGPIPE, &sa, NULL);
20230
0
#endif
20231
20232
0
  worker_thread_run((struct mg_connection *)thread_func_param);
20233
0
  return NULL;
20234
0
}
20235
#endif /* _WIN32 */
20236
20237
20238
/* This is an internal function, thus all arguments are expected to be
20239
 * valid - a NULL check is not required. */
20240
static void
20241
accept_new_connection(const struct socket *listener, struct mg_context *ctx)
20242
0
{
20243
0
  struct socket so;
20244
0
  char src_addr[IP_ADDR_STR_LEN];
20245
0
  socklen_t len = sizeof(so.rsa);
20246
0
#if !defined(__ZEPHYR__)
20247
0
  int on = 1;
20248
0
#endif
20249
0
  memset(&so, 0, sizeof(so));
20250
20251
0
  if ((so.sock = accept(listener->sock, &so.rsa.sa, &len))
20252
0
      == INVALID_SOCKET) {
20253
0
  } else if (check_acl(ctx, &so.rsa) != 1) {
20254
0
    sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
20255
0
    mg_cry_ctx_internal(ctx,
20256
0
                        "%s: %s is not allowed to connect",
20257
0
                        __func__,
20258
0
                        src_addr);
20259
0
    closesocket(so.sock);
20260
0
  } else {
20261
    /* Put so socket structure into the queue */
20262
0
    DEBUG_TRACE("Accepted socket %d", (int)so.sock);
20263
0
    set_close_on_exec(so.sock, NULL, ctx);
20264
0
    so.is_ssl = listener->is_ssl;
20265
0
    so.ssl_redir = listener->ssl_redir;
20266
0
    so.is_optional = listener->is_optional;
20267
0
    if (getsockname(so.sock, &so.lsa.sa, &len) != 0) {
20268
0
      mg_cry_ctx_internal(ctx,
20269
0
                          "%s: getsockname() failed: %s",
20270
0
                          __func__,
20271
0
                          strerror(ERRNO));
20272
0
    }
20273
20274
0
#if !defined(__ZEPHYR__)
20275
0
    if ((so.lsa.sa.sa_family == AF_INET)
20276
0
        || (so.lsa.sa.sa_family == AF_INET6)) {
20277
      /* Set TCP keep-alive for TCP sockets (IPv4 and IPv6).
20278
       * This is needed because if HTTP-level keep-alive
20279
       * is enabled, and client resets the connection, server won't get
20280
       * TCP FIN or RST and will keep the connection open forever. With
20281
       * TCP keep-alive, next keep-alive handshake will figure out that
20282
       * the client is down and will close the server end.
20283
       * Thanks to Igor Klopov who suggested the patch. */
20284
0
      if (setsockopt(so.sock,
20285
0
                     SOL_SOCKET,
20286
0
                     SO_KEEPALIVE,
20287
0
                     (SOCK_OPT_TYPE)&on,
20288
0
                     sizeof(on))
20289
0
          != 0) {
20290
0
        mg_cry_ctx_internal(
20291
0
            ctx,
20292
0
            "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
20293
0
            __func__,
20294
0
            strerror(ERRNO));
20295
0
      }
20296
0
    }
20297
0
#endif
20298
20299
    /* Disable TCP Nagle's algorithm. Normally TCP packets are coalesced
20300
     * to effectively fill up the underlying IP packet payload and
20301
     * reduce the overhead of sending lots of small buffers. However
20302
     * this hurts the server's throughput (ie. operations per second)
20303
     * when HTTP 1.1 persistent connections are used and the responses
20304
     * are relatively small (eg. less than 1400 bytes).
20305
     */
20306
0
    if ((ctx->dd.config[CONFIG_TCP_NODELAY] != NULL)
20307
0
        && (!strcmp(ctx->dd.config[CONFIG_TCP_NODELAY], "1"))) {
20308
0
      if (set_tcp_nodelay(&so, 1) != 0) {
20309
0
        mg_cry_ctx_internal(
20310
0
            ctx,
20311
0
            "%s: setsockopt(IPPROTO_TCP TCP_NODELAY) failed: %s",
20312
0
            __func__,
20313
0
            strerror(ERRNO));
20314
0
      }
20315
0
    }
20316
20317
    /* The "non blocking" property should already be
20318
     * inherited from the parent socket. Set it for
20319
     * non-compliant socket implementations. */
20320
0
    set_non_blocking_mode(so.sock);
20321
20322
0
    so.in_use = 0;
20323
0
    produce_socket(ctx, &so);
20324
0
  }
20325
0
}
20326
20327
20328
static void
20329
master_thread_run(struct mg_context *ctx)
20330
2
{
20331
2
  struct mg_workerTLS tls;
20332
2
  struct mg_pollfd *pfd;
20333
2
  unsigned int i;
20334
2
  unsigned int workerthreadcount;
20335
20336
2
  if (!ctx) {
20337
0
    return;
20338
0
  }
20339
20340
2
  mg_set_thread_name("master");
20341
20342
  /* Increase priority of the master thread */
20343
#if defined(_WIN32)
20344
  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
20345
#elif defined(USE_MASTER_THREAD_PRIORITY)
20346
  int min_prio = sched_get_priority_min(SCHED_RR);
20347
  int max_prio = sched_get_priority_max(SCHED_RR);
20348
  if ((min_prio >= 0) && (max_prio >= 0)
20349
      && ((USE_MASTER_THREAD_PRIORITY) <= max_prio)
20350
      && ((USE_MASTER_THREAD_PRIORITY) >= min_prio)) {
20351
    struct sched_param sched_param = {0};
20352
    sched_param.sched_priority = (USE_MASTER_THREAD_PRIORITY);
20353
    pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
20354
  }
20355
#endif
20356
20357
  /* Initialize thread local storage */
20358
#if defined(_WIN32)
20359
  tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
20360
#endif
20361
2
  tls.is_master = 1;
20362
2
  pthread_setspecific(sTlsKey, &tls);
20363
20364
2
  if (ctx->callbacks.init_thread) {
20365
    /* Callback for the master thread (type 0) */
20366
0
    tls.user_ptr = ctx->callbacks.init_thread(ctx, 0);
20367
2
  } else {
20368
2
    tls.user_ptr = NULL;
20369
2
  }
20370
20371
  /* Lua background script "start" event */
20372
#if defined(USE_LUA)
20373
  if (ctx->lua_background_state) {
20374
    lua_State *lstate = (lua_State *)ctx->lua_background_state;
20375
    pthread_mutex_lock(&ctx->lua_bg_mutex);
20376
20377
    /* call "start()" in Lua */
20378
    lua_getglobal(lstate, "start");
20379
    if (lua_type(lstate, -1) == LUA_TFUNCTION) {
20380
      int ret = lua_pcall(lstate, /* args */ 0, /* results */ 0, 0);
20381
      if (ret != 0) {
20382
        struct mg_connection fc;
20383
        lua_cry(fake_connection(&fc, ctx),
20384
                ret,
20385
                lstate,
20386
                "lua_background_script",
20387
                "start");
20388
      }
20389
    } else {
20390
      lua_pop(lstate, 1);
20391
    }
20392
20393
    /* determine if there is a "log()" function in Lua background script */
20394
    lua_getglobal(lstate, "log");
20395
    if (lua_type(lstate, -1) == LUA_TFUNCTION) {
20396
      ctx->lua_bg_log_available = 1;
20397
    }
20398
    lua_pop(lstate, 1);
20399
20400
    pthread_mutex_unlock(&ctx->lua_bg_mutex);
20401
  }
20402
#endif
20403
20404
  /* Server starts *now* */
20405
2
  ctx->start_time = time(NULL);
20406
20407
  /* Server accept loop */
20408
2
  pfd = ctx->listening_socket_fds;
20409
8
  while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
20410
12
    for (i = 0; i < ctx->num_listening_sockets; i++) {
20411
6
      pfd[i].fd = ctx->listening_sockets[i].sock;
20412
6
      pfd[i].events = POLLIN;
20413
6
    }
20414
20415
    /* We listen on this socket just so that mg_stop() can cause mg_poll()
20416
     * to return ASAP. Don't worry, we did allocate an extra slot at the end
20417
     * of listening_socket_fds[] just to hold this
20418
     */
20419
6
    pfd[ctx->num_listening_sockets].fd =
20420
6
        ctx->thread_shutdown_notification_socket;
20421
6
    pfd[ctx->num_listening_sockets].events = POLLIN;
20422
20423
6
    if (mg_poll(pfd,
20424
6
                ctx->num_listening_sockets
20425
6
                    + 1, // +1 for the thread_shutdown_notification_socket
20426
6
                SOCKET_TIMEOUT_QUANTUM,
20427
6
                &(ctx->stop_flag))
20428
6
        > 0) {
20429
4
      for (i = 0; i < ctx->num_listening_sockets; i++) {
20430
        /* NOTE(lsm): on QNX, poll() returns POLLRDNORM after the
20431
         * successful poll, and POLLIN is defined as
20432
         * (POLLRDNORM | POLLRDBAND)
20433
         * Therefore, we're checking pfd[i].revents & POLLIN, not
20434
         * pfd[i].revents == POLLIN. */
20435
2
        if (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
20436
2
            && (pfd[i].revents & POLLIN)) {
20437
0
          accept_new_connection(&ctx->listening_sockets[i], ctx);
20438
0
        }
20439
2
      }
20440
2
    }
20441
6
  }
20442
20443
  /* Here stop_flag is 1 - Initiate shutdown. */
20444
2
  DEBUG_TRACE("%s", "stopping workers");
20445
20446
  /* Stop signal received: somebody called mg_stop. Quit. */
20447
2
  close_all_listening_sockets(ctx);
20448
20449
  /* Wakeup workers that are waiting for connections to handle. */
20450
#if defined(ALTERNATIVE_QUEUE)
20451
  for (i = 0; i < ctx->spawned_worker_threads; i++) {
20452
    event_signal(ctx->client_wait_events[i]);
20453
  }
20454
#else
20455
2
  (void)pthread_mutex_lock(&ctx->thread_mutex);
20456
2
  pthread_cond_broadcast(&ctx->sq_full);
20457
2
  (void)pthread_mutex_unlock(&ctx->thread_mutex);
20458
2
#endif
20459
20460
  /* Join all worker threads to avoid leaking threads. */
20461
2
  workerthreadcount = ctx->spawned_worker_threads;
20462
2
  for (i = 0; i < workerthreadcount; i++) {
20463
0
    if (ctx->worker_threadids[i] != 0) {
20464
0
      mg_join_thread(ctx->worker_threadids[i]);
20465
0
    }
20466
0
  }
20467
20468
#if defined(USE_LUA)
20469
  /* Free Lua state of lua background task */
20470
  if (ctx->lua_background_state) {
20471
    lua_State *lstate = (lua_State *)ctx->lua_background_state;
20472
    ctx->lua_bg_log_available = 0;
20473
20474
    /* call "stop()" in Lua */
20475
    pthread_mutex_lock(&ctx->lua_bg_mutex);
20476
    lua_getglobal(lstate, "stop");
20477
    if (lua_type(lstate, -1) == LUA_TFUNCTION) {
20478
      int ret = lua_pcall(lstate, /* args */ 0, /* results */ 0, 0);
20479
      if (ret != 0) {
20480
        struct mg_connection fc;
20481
        lua_cry(fake_connection(&fc, ctx),
20482
                ret,
20483
                lstate,
20484
                "lua_background_script",
20485
                "stop");
20486
      }
20487
    }
20488
    DEBUG_TRACE("Close Lua background state %p", lstate);
20489
    lua_close(lstate);
20490
20491
    ctx->lua_background_state = 0;
20492
    pthread_mutex_unlock(&ctx->lua_bg_mutex);
20493
  }
20494
#endif
20495
20496
2
  DEBUG_TRACE("%s", "exiting");
20497
20498
  /* call exit thread callback */
20499
2
  if (ctx->callbacks.exit_thread) {
20500
    /* Callback for the master thread (type 0) */
20501
0
    ctx->callbacks.exit_thread(ctx, 0, tls.user_ptr);
20502
0
  }
20503
20504
#if defined(_WIN32)
20505
  CloseHandle(tls.pthread_cond_helper_mutex);
20506
#endif
20507
2
  pthread_setspecific(sTlsKey, NULL);
20508
20509
  /* Signal mg_stop() that we're done.
20510
   * WARNING: This must be the very last thing this
20511
   * thread does, as ctx becomes invalid after this line. */
20512
2
  STOP_FLAG_ASSIGN(&ctx->stop_flag, 2);
20513
2
}
20514
20515
20516
/* Threads have different return types on Windows and Unix. */
20517
#if defined(_WIN32)
20518
static unsigned __stdcall master_thread(void *thread_func_param)
20519
{
20520
  master_thread_run((struct mg_context *)thread_func_param);
20521
  return 0;
20522
}
20523
#else
20524
static void *
20525
master_thread(void *thread_func_param)
20526
2
{
20527
2
#if !defined(__ZEPHYR__)
20528
2
  struct sigaction sa;
20529
20530
  /* Ignore SIGPIPE */
20531
2
  memset(&sa, 0, sizeof(sa));
20532
2
  sa.sa_handler = SIG_IGN;
20533
2
  sigaction(SIGPIPE, &sa, NULL);
20534
2
#endif
20535
20536
2
  master_thread_run((struct mg_context *)thread_func_param);
20537
2
  return NULL;
20538
2
}
20539
#endif /* _WIN32 */
20540
20541
20542
static void
20543
free_context(struct mg_context *ctx)
20544
2
{
20545
2
  int i;
20546
2
  struct mg_handler_info *tmp_rh;
20547
20548
2
  if (ctx == NULL) {
20549
0
    return;
20550
0
  }
20551
20552
  /* Call user callback */
20553
2
  if (ctx->callbacks.exit_context) {
20554
0
    ctx->callbacks.exit_context(ctx);
20555
0
  }
20556
20557
  /* All threads exited, no sync is needed. Destroy thread mutex and
20558
   * condvars
20559
   */
20560
2
  (void)pthread_mutex_destroy(&ctx->thread_mutex);
20561
20562
#if defined(ALTERNATIVE_QUEUE)
20563
  mg_free(ctx->client_socks);
20564
  if (ctx->client_wait_events != NULL) {
20565
    for (i = 0; (unsigned)i < ctx->spawned_worker_threads; i++) {
20566
      event_destroy(ctx->client_wait_events[i]);
20567
    }
20568
    mg_free(ctx->client_wait_events);
20569
  }
20570
#else
20571
2
  (void)pthread_cond_destroy(&ctx->sq_empty);
20572
2
  (void)pthread_cond_destroy(&ctx->sq_full);
20573
2
  mg_free(ctx->squeue);
20574
2
#endif
20575
20576
  /* Destroy other context global data structures mutex */
20577
2
  (void)pthread_mutex_destroy(&ctx->nonce_mutex);
20578
20579
#if defined(USE_LUA)
20580
  (void)pthread_mutex_destroy(&ctx->lua_bg_mutex);
20581
#endif
20582
20583
  /* Deallocate shutdown-triggering socket-pair */
20584
2
  if (ctx->user_shutdown_notification_socket >= 0) {
20585
0
    closesocket(ctx->user_shutdown_notification_socket);
20586
0
  }
20587
2
  if (ctx->thread_shutdown_notification_socket >= 0) {
20588
2
    closesocket(ctx->thread_shutdown_notification_socket);
20589
2
  }
20590
20591
  /* Deallocate config parameters */
20592
132
  for (i = 0; i < NUM_OPTIONS; i++) {
20593
130
    if (ctx->dd.config[i] != NULL) {
20594
#if defined(_MSC_VER)
20595
#pragma warning(suppress : 6001)
20596
#endif
20597
72
      mg_free(ctx->dd.config[i]);
20598
72
    }
20599
130
  }
20600
20601
  /* Deallocate request handlers */
20602
2
  while (ctx->dd.handlers) {
20603
0
    tmp_rh = ctx->dd.handlers;
20604
0
    ctx->dd.handlers = tmp_rh->next;
20605
0
    mg_free(tmp_rh->uri);
20606
0
    mg_free(tmp_rh);
20607
0
  }
20608
20609
#if defined(USE_MBEDTLS)
20610
  if (ctx->dd.ssl_ctx != NULL) {
20611
    mbed_sslctx_uninit(ctx->dd.ssl_ctx);
20612
    mg_free(ctx->dd.ssl_ctx);
20613
    ctx->dd.ssl_ctx = NULL;
20614
  }
20615
20616
#elif !defined(NO_SSL)
20617
  /* Deallocate SSL context */
20618
2
  if (ctx->dd.ssl_ctx != NULL) {
20619
0
    void *ssl_ctx = (void *)ctx->dd.ssl_ctx;
20620
0
    int callback_ret =
20621
0
        (ctx->callbacks.external_ssl_ctx == NULL)
20622
0
            ? 0
20623
0
            : (ctx->callbacks.external_ssl_ctx(&ssl_ctx, ctx->user_data));
20624
20625
0
    if (callback_ret == 0) {
20626
0
      SSL_CTX_free(ctx->dd.ssl_ctx);
20627
0
    }
20628
    /* else: ignore error and omit SSL_CTX_free in case
20629
     * callback_ret is 1 */
20630
0
  }
20631
2
#endif /* !NO_SSL */
20632
20633
  /* Deallocate worker thread ID array */
20634
2
  mg_free(ctx->worker_threadids);
20635
20636
  /* Deallocate worker thread ID array */
20637
2
  mg_free(ctx->worker_connections);
20638
20639
  /* deallocate system name string */
20640
2
  mg_free(ctx->systemName);
20641
20642
  /* Deallocate context itself */
20643
2
  mg_free(ctx);
20644
2
}
20645
20646
20647
CIVETWEB_API void
20648
mg_stop(struct mg_context *ctx)
20649
2
{
20650
2
  pthread_t mt;
20651
2
  if (!ctx) {
20652
0
    return;
20653
0
  }
20654
20655
  /* We don't use a lock here. Calling mg_stop with the same ctx from
20656
   * two threads is not allowed. */
20657
2
  mt = ctx->masterthreadid;
20658
2
  if (mt == 0) {
20659
0
    return;
20660
0
  }
20661
20662
2
  ctx->masterthreadid = 0;
20663
20664
  /* Set stop flag, so all threads know they have to exit. */
20665
2
  STOP_FLAG_ASSIGN(&ctx->stop_flag, 1);
20666
20667
  /* Closing this socket will cause mg_poll() in all the I/O threads to return
20668
   * immediately */
20669
2
  closesocket(ctx->user_shutdown_notification_socket);
20670
2
  ctx->user_shutdown_notification_socket =
20671
2
      -1; /* to avoid calling closesocket() again in free_context() */
20672
20673
  /* Join timer thread */
20674
#if defined(USE_TIMERS)
20675
  timers_exit(ctx);
20676
#endif
20677
20678
  /* Wait until everything has stopped. */
20679
4
  while (!STOP_FLAG_IS_TWO(&ctx->stop_flag)) {
20680
2
    (void)mg_sleep(10);
20681
2
  }
20682
20683
  /* Wait to stop master thread */
20684
2
  mg_join_thread(mt);
20685
20686
  /* Close remaining Lua states */
20687
#if defined(USE_LUA)
20688
  lua_ctx_exit(ctx);
20689
#endif
20690
20691
  /* Free memory */
20692
2
  free_context(ctx);
20693
2
}
20694
20695
20696
static void
20697
get_system_name(char **sysName)
20698
2
{
20699
#if defined(_WIN32)
20700
  char name[128];
20701
  DWORD dwVersion = 0;
20702
  DWORD dwMajorVersion = 0;
20703
  DWORD dwMinorVersion = 0;
20704
  DWORD dwBuild = 0;
20705
  BOOL wowRet, isWoW = FALSE;
20706
20707
#if defined(_MSC_VER)
20708
#pragma warning(push)
20709
  /* GetVersion was declared deprecated */
20710
#pragma warning(disable : 4996)
20711
#endif
20712
  dwVersion = GetVersion();
20713
#if defined(_MSC_VER)
20714
#pragma warning(pop)
20715
#endif
20716
20717
  dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
20718
  dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
20719
  dwBuild = ((dwVersion < 0x80000000) ? (DWORD)(HIWORD(dwVersion)) : 0);
20720
  (void)dwBuild;
20721
20722
  wowRet = IsWow64Process(GetCurrentProcess(), &isWoW);
20723
20724
  sprintf(name,
20725
          "Windows %u.%u%s",
20726
          (unsigned)dwMajorVersion,
20727
          (unsigned)dwMinorVersion,
20728
          (wowRet ? (isWoW ? " (WoW64)" : "") : " (?)"));
20729
20730
  *sysName = mg_strdup(name);
20731
20732
#elif defined(__ZEPHYR__)
20733
  *sysName = mg_strdup("Zephyr OS");
20734
#else
20735
2
  struct utsname name;
20736
2
  memset(&name, 0, sizeof(name));
20737
2
  uname(&name);
20738
2
  *sysName = mg_strdup(name.sysname);
20739
2
#endif
20740
2
}
20741
20742
20743
static void
20744
legacy_init(const char **options)
20745
2
{
20746
2
  const char *ports_option = config_options[LISTENING_PORTS].default_value;
20747
20748
2
  if (options) {
20749
2
    const char **run_options = options;
20750
2
    const char *optname = config_options[LISTENING_PORTS].name;
20751
20752
    /* Try to find the "listening_ports" option */
20753
6
    while (*run_options) {
20754
4
      if (!strcmp(*run_options, optname)) {
20755
2
        ports_option = run_options[1];
20756
2
      }
20757
4
      run_options += 2;
20758
4
    }
20759
2
  }
20760
20761
2
  if (is_ssl_port_used(ports_option)) {
20762
    /* Initialize with SSL support */
20763
0
    mg_init_library(MG_FEATURES_TLS);
20764
2
  } else {
20765
    /* Initialize without SSL support */
20766
2
    mg_init_library(MG_FEATURES_DEFAULT);
20767
2
  }
20768
2
}
20769
20770
/* we'll assume it's only Windows that doesn't have socketpair() available */
20771
#if !defined(HAVE_SOCKETPAIR) && !defined(_WIN32)
20772
#define HAVE_SOCKETPAIR 1
20773
#endif
20774
20775
static int
20776
mg_socketpair(int *sockA, int *sockB)
20777
2
{
20778
2
  int temp[2] = {-1, -1};
20779
2
  int asock = -1;
20780
20781
  /** Default to unallocated */
20782
2
  *sockA = -1;
20783
2
  *sockB = -1;
20784
20785
2
#if defined(HAVE_SOCKETPAIR)
20786
2
  int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, temp);
20787
2
  if (ret == 0) {
20788
2
    *sockA = temp[0];
20789
2
    *sockB = temp[1];
20790
2
    set_close_on_exec(*sockA, NULL, NULL);
20791
2
    set_close_on_exec(*sockB, NULL, NULL);
20792
2
  }
20793
2
  (void)asock; /* not used */
20794
2
  return ret;
20795
#else
20796
  /** No socketpair() call is available, so we'll have to roll our own
20797
   * implementation */
20798
  asock = socket(PF_INET, SOCK_STREAM, 0);
20799
  if (asock >= 0) {
20800
    struct sockaddr_in addr;
20801
    struct sockaddr *pa = (struct sockaddr *)&addr;
20802
    socklen_t addrLen = sizeof(addr);
20803
20804
    memset(&addr, 0, sizeof(addr));
20805
    addr.sin_family = AF_INET;
20806
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
20807
    addr.sin_port = 0;
20808
20809
    if ((bind(asock, pa, sizeof(addr)) == 0)
20810
        && (getsockname(asock, pa, &addrLen) == 0)
20811
        && (listen(asock, 1) == 0)) {
20812
      temp[0] = socket(PF_INET, SOCK_STREAM, 0);
20813
      if ((temp[0] >= 0) && (connect(temp[0], pa, sizeof(addr)) == 0)) {
20814
        temp[1] = accept(asock, pa, &addrLen);
20815
        if (temp[1] >= 0) {
20816
          closesocket(asock);
20817
          *sockA = temp[0];
20818
          *sockB = temp[1];
20819
          set_close_on_exec(*sockA, NULL, NULL);
20820
          set_close_on_exec(*sockB, NULL, NULL);
20821
          return 0; /* success! */
20822
        }
20823
      }
20824
    }
20825
  }
20826
20827
  /* Cleanup */
20828
  if (asock >= 0)
20829
    closesocket(asock);
20830
  if (temp[0] >= 0)
20831
    closesocket(temp[0]);
20832
  if (temp[1] >= 0)
20833
    closesocket(temp[1]);
20834
  return -1; /* fail! */
20835
#endif
20836
2
}
20837
20838
static int
20839
mg_start_worker_thread(struct mg_context *ctx, int only_if_no_idle_threads)
20840
0
{
20841
0
  const unsigned int i = ctx->spawned_worker_threads;
20842
0
  if (i >= ctx->cfg_max_worker_threads) {
20843
0
    return -1; /* Oops, we hit our worker-thread limit!  No more worker
20844
                  threads, ever! */
20845
0
  }
20846
20847
0
  (void)pthread_mutex_lock(&ctx->thread_mutex);
20848
#if defined(ALTERNATIVE_QUEUE)
20849
  if ((only_if_no_idle_threads) && (ctx->idle_worker_thread_count > 0)) {
20850
#else
20851
0
  if ((only_if_no_idle_threads)
20852
0
      && (ctx->idle_worker_thread_count
20853
0
          > (unsigned)(ctx->sq_head - ctx->sq_tail))) {
20854
0
#endif
20855
0
    (void)pthread_mutex_unlock(&ctx->thread_mutex);
20856
0
    return -2; /* There are idle threads available, so no need to spawn a
20857
                  new worker thread now */
20858
0
  }
20859
0
  ctx->idle_worker_thread_count++; /* we do this here to avoid a race
20860
                                      condition while the thread is starting
20861
                                      up */
20862
0
  (void)pthread_mutex_unlock(&ctx->thread_mutex);
20863
20864
0
  ctx->worker_connections[i].phys_ctx = ctx;
20865
0
  int ret = mg_start_thread_with_id(worker_thread,
20866
0
                                    &ctx->worker_connections[i],
20867
0
                                    &ctx->worker_threadids[i]);
20868
0
  if (ret == 0) {
20869
0
    ctx->spawned_worker_threads++; /* note that we've filled another slot in
20870
                                      the table */
20871
0
    DEBUG_TRACE("Started worker_thread #%i", ctx->spawned_worker_threads);
20872
0
  } else {
20873
0
    (void)pthread_mutex_lock(&ctx->thread_mutex);
20874
0
    ctx->idle_worker_thread_count--; /* whoops, roll-back on error */
20875
0
    (void)pthread_mutex_unlock(&ctx->thread_mutex);
20876
0
  }
20877
0
  return ret;
20878
0
}
20879
20880
CIVETWEB_API struct mg_context *
20881
mg_start2(struct mg_init_data *init, struct mg_error_data *error)
20882
2
{
20883
2
  struct mg_context *ctx;
20884
2
  const char *name, *value, *default_value;
20885
2
  int idx, ok, prespawnthreadcount, workerthreadcount;
20886
2
  unsigned int i;
20887
2
  int itmp;
20888
2
  void (*exit_callback)(const struct mg_context *ctx) = 0;
20889
2
  const char **options =
20890
2
      ((init != NULL) ? (init->configuration_options) : (NULL));
20891
20892
2
  struct mg_workerTLS tls;
20893
20894
2
  if (error != NULL) {
20895
0
    error->code = MG_ERROR_DATA_CODE_OK;
20896
0
    error->code_sub = 0;
20897
0
    if (error->text_buffer_size > 0) {
20898
0
      *error->text = 0;
20899
0
    }
20900
0
  }
20901
20902
2
  if (mg_init_library_called == 0) {
20903
    /* Legacy INIT, if mg_start is called without mg_init_library.
20904
     * Note: This will cause a memory leak when unloading the library.
20905
     */
20906
2
    legacy_init(options);
20907
2
  }
20908
2
  if (mg_init_library_called == 0) {
20909
0
    if (error != NULL) {
20910
0
      error->code = MG_ERROR_DATA_CODE_INIT_LIBRARY_FAILED;
20911
0
      mg_snprintf(NULL,
20912
0
                  NULL, /* No truncation check for error buffers */
20913
0
                  error->text,
20914
0
                  error->text_buffer_size,
20915
0
                  "%s",
20916
0
                  "Library uninitialized");
20917
0
    }
20918
0
    return NULL;
20919
0
  }
20920
20921
  /* Allocate context and initialize reasonable general case defaults. */
20922
2
  ctx = (struct mg_context *)mg_calloc(1, sizeof(*ctx));
20923
2
  if (ctx == NULL) {
20924
0
    if (error != NULL) {
20925
0
      error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
20926
0
      error->code_sub = (unsigned)sizeof(*ctx);
20927
0
      mg_snprintf(NULL,
20928
0
                  NULL, /* No truncation check for error buffers */
20929
0
                  error->text,
20930
0
                  error->text_buffer_size,
20931
0
                  "%s",
20932
0
                  "Out of memory");
20933
0
    }
20934
0
    return NULL;
20935
0
  }
20936
20937
  /* Random number generator will initialize at the first call */
20938
2
  ctx->dd.auth_nonce_mask =
20939
2
      (uint64_t)get_random() ^ (uint64_t)(ptrdiff_t)(options);
20940
20941
  /* Save started thread index to reuse in other external API calls
20942
   * For the sake of thread synchronization all non-civetweb threads
20943
   * can be considered as single external thread */
20944
2
  ctx->starter_thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
20945
2
  tls.is_master = -1; /* Thread calling mg_start */
20946
2
  tls.thread_idx = ctx->starter_thread_idx;
20947
#if defined(_WIN32)
20948
  tls.pthread_cond_helper_mutex = NULL;
20949
#endif
20950
2
  pthread_setspecific(sTlsKey, &tls);
20951
20952
2
  ok = (0 == pthread_mutex_init(&ctx->thread_mutex, &pthread_mutex_attr));
20953
2
#if !defined(ALTERNATIVE_QUEUE)
20954
2
  ok &= (0 == pthread_cond_init(&ctx->sq_empty, NULL));
20955
2
  ok &= (0 == pthread_cond_init(&ctx->sq_full, NULL));
20956
2
  ctx->sq_blocked = 0;
20957
2
#endif
20958
2
  ok &= (0 == pthread_mutex_init(&ctx->nonce_mutex, &pthread_mutex_attr));
20959
#if defined(USE_LUA)
20960
  ok &= (0 == pthread_mutex_init(&ctx->lua_bg_mutex, &pthread_mutex_attr));
20961
#endif
20962
20963
  /** mg_stop() will close the user_shutdown_notification_socket, and that
20964
   * will cause poll() to return immediately in the master-thread, so that
20965
   * mg_stop() can also return immediately.
20966
   */
20967
2
  ok &= (0
20968
2
         == mg_socketpair(&ctx->user_shutdown_notification_socket,
20969
2
                          &ctx->thread_shutdown_notification_socket));
20970
20971
2
  if (!ok) {
20972
0
    unsigned error_id = (unsigned)ERRNO;
20973
0
    const char *err_msg =
20974
0
        "Cannot initialize thread synchronization objects";
20975
    /* Fatal error - abort start. However, this situation should never
20976
     * occur in practice. */
20977
20978
0
    mg_cry_ctx_internal(ctx, "%s", err_msg);
20979
0
    if (error != NULL) {
20980
0
      error->code = MG_ERROR_DATA_CODE_OS_ERROR;
20981
0
      error->code_sub = error_id;
20982
0
      mg_snprintf(NULL,
20983
0
                  NULL, /* No truncation check for error buffers */
20984
0
                  error->text,
20985
0
                  error->text_buffer_size,
20986
0
                  "%s",
20987
0
                  err_msg);
20988
0
    }
20989
20990
0
    mg_free(ctx);
20991
0
    pthread_setspecific(sTlsKey, NULL);
20992
0
    return NULL;
20993
0
  }
20994
20995
2
  if ((init != NULL) && (init->callbacks != NULL)) {
20996
    /* Set all callbacks except exit_context. */
20997
2
    ctx->callbacks = *init->callbacks;
20998
2
    exit_callback = init->callbacks->exit_context;
20999
    /* The exit callback is activated once the context is successfully
21000
     * created. It should not be called, if an incomplete context object
21001
     * is deleted during a failed initialization. */
21002
2
    ctx->callbacks.exit_context = 0;
21003
2
  }
21004
2
  ctx->user_data = ((init != NULL) ? (init->user_data) : (NULL));
21005
2
  ctx->dd.handlers = NULL;
21006
2
  ctx->dd.next = NULL;
21007
21008
#if defined(USE_LUA)
21009
  lua_ctx_init(ctx);
21010
#endif
21011
21012
  /* Store options */
21013
6
  while (options && (name = *options++) != NULL) {
21014
4
    idx = get_option_index(name);
21015
4
    if (idx == -1) {
21016
0
      mg_cry_ctx_internal(ctx, "Invalid option: %s", name);
21017
0
      if (error != NULL) {
21018
0
        error->code = MG_ERROR_DATA_CODE_INVALID_OPTION;
21019
0
        error->code_sub = (unsigned)-1;
21020
0
        mg_snprintf(NULL,
21021
0
                    NULL, /* No truncation check for error buffers */
21022
0
                    error->text,
21023
0
                    error->text_buffer_size,
21024
0
                    "Invalid configuration option: %s",
21025
0
                    name);
21026
0
      }
21027
21028
0
      free_context(ctx);
21029
0
      pthread_setspecific(sTlsKey, NULL);
21030
0
      return NULL;
21031
21032
4
    } else if ((value = *options++) == NULL) {
21033
0
      mg_cry_ctx_internal(ctx, "%s: option value cannot be NULL", name);
21034
0
      if (error != NULL) {
21035
0
        error->code = MG_ERROR_DATA_CODE_INVALID_OPTION;
21036
0
        error->code_sub = (unsigned)idx;
21037
0
        mg_snprintf(NULL,
21038
0
                    NULL, /* No truncation check for error buffers */
21039
0
                    error->text,
21040
0
                    error->text_buffer_size,
21041
0
                    "Invalid configuration option value: %s",
21042
0
                    name);
21043
0
      }
21044
21045
0
      free_context(ctx);
21046
0
      pthread_setspecific(sTlsKey, NULL);
21047
0
      return NULL;
21048
0
    }
21049
4
    if (ctx->dd.config[idx] != NULL) {
21050
      /* A duplicate configuration option is not an error - the last
21051
       * option value will be used. */
21052
0
      mg_cry_ctx_internal(ctx, "warning: %s: duplicate option", name);
21053
0
      mg_free(ctx->dd.config[idx]);
21054
0
    }
21055
4
    ctx->dd.config[idx] = mg_strdup_ctx(value, ctx);
21056
4
    DEBUG_TRACE("[%s] -> [%s]", name, value);
21057
4
  }
21058
21059
  /* Set default value if needed */
21060
132
  for (i = 0; config_options[i].name != NULL; i++) {
21061
130
    default_value = config_options[i].default_value;
21062
130
    if ((ctx->dd.config[i] == NULL) && (default_value != NULL)) {
21063
68
      ctx->dd.config[i] = mg_strdup_ctx(default_value, ctx);
21064
68
    }
21065
130
  }
21066
21067
  /* Request size option */
21068
2
  itmp = atoi(ctx->dd.config[MAX_REQUEST_SIZE]);
21069
2
  if (itmp < 1024) {
21070
0
    mg_cry_ctx_internal(ctx,
21071
0
                        "%s too small",
21072
0
                        config_options[MAX_REQUEST_SIZE].name);
21073
0
    if (error != NULL) {
21074
0
      error->code = MG_ERROR_DATA_CODE_INVALID_OPTION;
21075
0
      error->code_sub = (unsigned)MAX_REQUEST_SIZE;
21076
0
      mg_snprintf(NULL,
21077
0
                  NULL, /* No truncation check for error buffers */
21078
0
                  error->text,
21079
0
                  error->text_buffer_size,
21080
0
                  "Invalid configuration option value: %s",
21081
0
                  config_options[MAX_REQUEST_SIZE].name);
21082
0
    }
21083
21084
0
    free_context(ctx);
21085
0
    pthread_setspecific(sTlsKey, NULL);
21086
0
    return NULL;
21087
0
  }
21088
2
  ctx->max_request_size = (unsigned)itmp;
21089
21090
  /* Queue length */
21091
2
#if !defined(ALTERNATIVE_QUEUE)
21092
2
  itmp = atoi(ctx->dd.config[CONNECTION_QUEUE_SIZE]);
21093
2
  if (itmp < 1) {
21094
0
    mg_cry_ctx_internal(ctx,
21095
0
                        "%s too small",
21096
0
                        config_options[CONNECTION_QUEUE_SIZE].name);
21097
0
    if (error != NULL) {
21098
0
      error->code = MG_ERROR_DATA_CODE_INVALID_OPTION;
21099
0
      error->code_sub = CONNECTION_QUEUE_SIZE;
21100
0
      mg_snprintf(NULL,
21101
0
                  NULL, /* No truncation check for error buffers */
21102
0
                  error->text,
21103
0
                  error->text_buffer_size,
21104
0
                  "Invalid configuration option value: %s",
21105
0
                  config_options[CONNECTION_QUEUE_SIZE].name);
21106
0
    }
21107
21108
0
    free_context(ctx);
21109
0
    pthread_setspecific(sTlsKey, NULL);
21110
0
    return NULL;
21111
0
  }
21112
2
  ctx->squeue =
21113
2
      (struct socket *)mg_calloc((unsigned int)itmp, sizeof(struct socket));
21114
2
  if (ctx->squeue == NULL) {
21115
0
    mg_cry_ctx_internal(ctx,
21116
0
                        "Out of memory: Cannot allocate %s",
21117
0
                        config_options[CONNECTION_QUEUE_SIZE].name);
21118
0
    if (error != NULL) {
21119
0
      error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
21120
0
      error->code_sub = (unsigned)itmp * (unsigned)sizeof(struct socket);
21121
0
      mg_snprintf(NULL,
21122
0
                  NULL, /* No truncation check for error buffers */
21123
0
                  error->text,
21124
0
                  error->text_buffer_size,
21125
0
                  "Out of memory: Cannot allocate %s",
21126
0
                  config_options[CONNECTION_QUEUE_SIZE].name);
21127
0
    }
21128
21129
0
    free_context(ctx);
21130
0
    pthread_setspecific(sTlsKey, NULL);
21131
0
    return NULL;
21132
0
  }
21133
2
  ctx->sq_size = itmp;
21134
2
#endif
21135
21136
  /* Worker thread count option */
21137
2
  workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]);
21138
2
  prespawnthreadcount = atoi(ctx->dd.config[PRESPAWN_THREADS]);
21139
21140
2
  if ((prespawnthreadcount < 0)
21141
2
      || (prespawnthreadcount > workerthreadcount)) {
21142
0
    prespawnthreadcount =
21143
0
        workerthreadcount; /* can't prespawn more than all of them! */
21144
0
  }
21145
21146
2
  if ((workerthreadcount > MAX_WORKER_THREADS) || (workerthreadcount <= 0)) {
21147
0
    if (workerthreadcount <= 0) {
21148
0
      mg_cry_ctx_internal(ctx, "%s", "Invalid number of worker threads");
21149
0
    } else {
21150
0
      mg_cry_ctx_internal(ctx, "%s", "Too many worker threads");
21151
0
    }
21152
0
    if (error != NULL) {
21153
0
      error->code = MG_ERROR_DATA_CODE_INVALID_OPTION;
21154
0
      error->code_sub = NUM_THREADS;
21155
0
      mg_snprintf(NULL,
21156
0
                  NULL, /* No truncation check for error buffers */
21157
0
                  error->text,
21158
0
                  error->text_buffer_size,
21159
0
                  "Invalid configuration option value: %s",
21160
0
                  config_options[NUM_THREADS].name);
21161
0
    }
21162
21163
0
    free_context(ctx);
21164
0
    pthread_setspecific(sTlsKey, NULL);
21165
0
    return NULL;
21166
0
  }
21167
21168
  /* Document root */
21169
#if defined(NO_FILES)
21170
  if (ctx->dd.config[DOCUMENT_ROOT] != NULL) {
21171
    mg_cry_ctx_internal(ctx, "%s", "Document root must not be set");
21172
    if (error != NULL) {
21173
      error->code = MG_ERROR_DATA_CODE_INVALID_OPTION;
21174
      error->code_sub = (unsigned)DOCUMENT_ROOT;
21175
      mg_snprintf(NULL,
21176
                  NULL, /* No truncation check for error buffers */
21177
                  error->text,
21178
                  error->text_buffer_size,
21179
                  "Invalid configuration option value: %s",
21180
                  config_options[DOCUMENT_ROOT].name);
21181
    }
21182
21183
    free_context(ctx);
21184
    pthread_setspecific(sTlsKey, NULL);
21185
    return NULL;
21186
  }
21187
#endif
21188
21189
2
  get_system_name(&ctx->systemName);
21190
21191
#if defined(USE_LUA)
21192
  /* If a Lua background script has been configured, start it. */
21193
  ctx->lua_bg_log_available = 0;
21194
  if (ctx->dd.config[LUA_BACKGROUND_SCRIPT] != NULL) {
21195
    char ebuf[256];
21196
    struct vec opt_vec;
21197
    struct vec eq_vec;
21198
    const char *sparams;
21199
21200
    memset(ebuf, 0, sizeof(ebuf));
21201
    pthread_mutex_lock(&ctx->lua_bg_mutex);
21202
21203
    /* Create a Lua state, load all standard libraries and the mg table */
21204
    lua_State *state = mg_lua_context_script_prepare(
21205
        ctx->dd.config[LUA_BACKGROUND_SCRIPT], ctx, ebuf, sizeof(ebuf));
21206
    if (!state) {
21207
      mg_cry_ctx_internal(ctx,
21208
                          "lua_background_script load error: %s",
21209
                          ebuf);
21210
      if (error != NULL) {
21211
        error->code = MG_ERROR_DATA_CODE_SCRIPT_ERROR;
21212
        mg_snprintf(NULL,
21213
                    NULL, /* No truncation check for error buffers */
21214
                    error->text,
21215
                    error->text_buffer_size,
21216
                    "Error in script %s: %s",
21217
                    config_options[LUA_BACKGROUND_SCRIPT].name,
21218
                    ebuf);
21219
      }
21220
21221
      pthread_mutex_unlock(&ctx->lua_bg_mutex);
21222
21223
      free_context(ctx);
21224
      pthread_setspecific(sTlsKey, NULL);
21225
      return NULL;
21226
    }
21227
21228
    /* Add a table with parameters into mg.params */
21229
    sparams = ctx->dd.config[LUA_BACKGROUND_SCRIPT_PARAMS];
21230
    if (sparams && sparams[0]) {
21231
      lua_getglobal(state, "mg");
21232
      lua_pushstring(state, "params");
21233
      lua_newtable(state);
21234
21235
      while ((sparams = next_option(sparams, &opt_vec, &eq_vec))
21236
             != NULL) {
21237
        reg_llstring(
21238
            state, opt_vec.ptr, opt_vec.len, eq_vec.ptr, eq_vec.len);
21239
        if (mg_strncasecmp(sparams, opt_vec.ptr, opt_vec.len) == 0)
21240
          break;
21241
      }
21242
      lua_rawset(state, -3);
21243
      lua_pop(state, 1);
21244
    }
21245
21246
    /* Call script */
21247
    state = mg_lua_context_script_run(state,
21248
                                      ctx->dd.config[LUA_BACKGROUND_SCRIPT],
21249
                                      ctx,
21250
                                      ebuf,
21251
                                      sizeof(ebuf));
21252
    if (!state) {
21253
      mg_cry_ctx_internal(ctx,
21254
                          "lua_background_script start error: %s",
21255
                          ebuf);
21256
      if (error != NULL) {
21257
        error->code = MG_ERROR_DATA_CODE_SCRIPT_ERROR;
21258
        mg_snprintf(NULL,
21259
                    NULL, /* No truncation check for error buffers */
21260
                    error->text,
21261
                    error->text_buffer_size,
21262
                    "Error in script %s: %s",
21263
                    config_options[DOCUMENT_ROOT].name,
21264
                    ebuf);
21265
      }
21266
      pthread_mutex_unlock(&ctx->lua_bg_mutex);
21267
21268
      free_context(ctx);
21269
      pthread_setspecific(sTlsKey, NULL);
21270
      return NULL;
21271
    }
21272
21273
    /* state remains valid */
21274
    ctx->lua_background_state = (void *)state;
21275
    pthread_mutex_unlock(&ctx->lua_bg_mutex);
21276
21277
  } else {
21278
    ctx->lua_background_state = 0;
21279
  }
21280
#endif
21281
21282
  /* Step by step initialization of ctx - depending on build options */
21283
2
#if !defined(NO_FILESYSTEMS)
21284
2
  if (!set_gpass_option(ctx, NULL)) {
21285
0
    const char *err_msg = "Invalid global password file";
21286
    /* Fatal error - abort start. */
21287
0
    mg_cry_ctx_internal(ctx, "%s", err_msg);
21288
21289
0
    if (error != NULL) {
21290
0
      error->code = MG_ERROR_DATA_CODE_INVALID_PASS_FILE;
21291
0
      mg_snprintf(NULL,
21292
0
                  NULL, /* No truncation check for error buffers */
21293
0
                  error->text,
21294
0
                  error->text_buffer_size,
21295
0
                  "%s",
21296
0
                  err_msg);
21297
0
    }
21298
0
    free_context(ctx);
21299
0
    pthread_setspecific(sTlsKey, NULL);
21300
0
    return NULL;
21301
0
  }
21302
2
#endif
21303
21304
#if defined(USE_MBEDTLS)
21305
  if (!mg_sslctx_init(ctx, NULL)) {
21306
    const char *err_msg = "Error initializing SSL context";
21307
    /* Fatal error - abort start. */
21308
    mg_cry_ctx_internal(ctx, "%s", err_msg);
21309
21310
    if (error != NULL) {
21311
      error->code = MG_ERROR_DATA_CODE_INIT_TLS_FAILED;
21312
      mg_snprintf(NULL,
21313
                  NULL, /* No truncation check for error buffers */
21314
                  error->text,
21315
                  error->text_buffer_size,
21316
                  "%s",
21317
                  err_msg);
21318
    }
21319
21320
    free_context(ctx);
21321
    pthread_setspecific(sTlsKey, NULL);
21322
    return NULL;
21323
  }
21324
21325
#elif !defined(NO_SSL)
21326
2
  if (!init_ssl_ctx(ctx, NULL)) {
21327
0
    const char *err_msg = "Error initializing SSL context";
21328
    /* Fatal error - abort start. */
21329
0
    mg_cry_ctx_internal(ctx, "%s", err_msg);
21330
21331
0
    if (error != NULL) {
21332
0
      error->code = MG_ERROR_DATA_CODE_INIT_TLS_FAILED;
21333
0
      mg_snprintf(NULL,
21334
0
                  NULL, /* No truncation check for error buffers */
21335
0
                  error->text,
21336
0
                  error->text_buffer_size,
21337
0
                  "%s",
21338
0
                  err_msg);
21339
0
    }
21340
21341
0
    free_context(ctx);
21342
0
    pthread_setspecific(sTlsKey, NULL);
21343
0
    return NULL;
21344
0
  }
21345
2
#endif
21346
21347
2
  if (!set_ports_option(ctx)) {
21348
0
    const char *err_msg = "Failed to setup server ports";
21349
    /* Fatal error - abort start. */
21350
0
    mg_cry_ctx_internal(ctx, "%s", err_msg);
21351
21352
0
    if (error != NULL) {
21353
0
      error->code = MG_ERROR_DATA_CODE_INIT_PORTS_FAILED;
21354
0
      mg_snprintf(NULL,
21355
0
                  NULL, /* No truncation check for error buffers */
21356
0
                  error->text,
21357
0
                  error->text_buffer_size,
21358
0
                  "%s",
21359
0
                  err_msg);
21360
0
    }
21361
21362
0
    free_context(ctx);
21363
0
    pthread_setspecific(sTlsKey, NULL);
21364
0
    return NULL;
21365
0
  }
21366
21367
2
#if !defined(_WIN32) && !defined(__ZEPHYR__)
21368
2
  if (!set_uid_option(ctx)) {
21369
0
    const char *err_msg = "Failed to run as configured user";
21370
    /* Fatal error - abort start. */
21371
0
    mg_cry_ctx_internal(ctx, "%s", err_msg);
21372
21373
0
    if (error != NULL) {
21374
0
      error->code = MG_ERROR_DATA_CODE_INIT_USER_FAILED;
21375
0
      mg_snprintf(NULL,
21376
0
                  NULL, /* No truncation check for error buffers */
21377
0
                  error->text,
21378
0
                  error->text_buffer_size,
21379
0
                  "%s",
21380
0
                  err_msg);
21381
0
    }
21382
21383
0
    free_context(ctx);
21384
0
    pthread_setspecific(sTlsKey, NULL);
21385
0
    return NULL;
21386
0
  }
21387
2
#endif
21388
21389
2
  if (!set_acl_option(ctx)) {
21390
0
    const char *err_msg = "Failed to setup access control list";
21391
    /* Fatal error - abort start. */
21392
0
    mg_cry_ctx_internal(ctx, "%s", err_msg);
21393
21394
0
    if (error != NULL) {
21395
0
      error->code = MG_ERROR_DATA_CODE_INIT_ACL_FAILED;
21396
0
      mg_snprintf(NULL,
21397
0
                  NULL, /* No truncation check for error buffers */
21398
0
                  error->text,
21399
0
                  error->text_buffer_size,
21400
0
                  "%s",
21401
0
                  err_msg);
21402
0
    }
21403
21404
0
    free_context(ctx);
21405
0
    pthread_setspecific(sTlsKey, NULL);
21406
0
    return NULL;
21407
0
  }
21408
21409
2
  ctx->cfg_max_worker_threads = ((unsigned int)(workerthreadcount));
21410
2
  ctx->worker_threadids =
21411
2
      (pthread_t *)mg_calloc_ctx(ctx->cfg_max_worker_threads,
21412
2
                                 sizeof(pthread_t),
21413
2
                                 ctx);
21414
21415
2
  if (ctx->worker_threadids == NULL) {
21416
0
    const char *err_msg = "Not enough memory for worker thread ID array";
21417
0
    mg_cry_ctx_internal(ctx, "%s", err_msg);
21418
21419
0
    if (error != NULL) {
21420
0
      error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
21421
0
      error->code_sub = (unsigned)ctx->cfg_max_worker_threads
21422
0
                        * (unsigned)sizeof(pthread_t);
21423
0
      mg_snprintf(NULL,
21424
0
                  NULL, /* No truncation check for error buffers */
21425
0
                  error->text,
21426
0
                  error->text_buffer_size,
21427
0
                  "%s",
21428
0
                  err_msg);
21429
0
    }
21430
21431
0
    free_context(ctx);
21432
0
    pthread_setspecific(sTlsKey, NULL);
21433
0
    return NULL;
21434
0
  }
21435
2
  ctx->worker_connections =
21436
2
      (struct mg_connection *)mg_calloc_ctx(ctx->cfg_max_worker_threads,
21437
2
                                            sizeof(struct mg_connection),
21438
2
                                            ctx);
21439
2
  if (ctx->worker_connections == NULL) {
21440
0
    const char *err_msg =
21441
0
        "Not enough memory for worker thread connection array";
21442
0
    mg_cry_ctx_internal(ctx, "%s", err_msg);
21443
21444
0
    if (error != NULL) {
21445
0
      error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
21446
0
      error->code_sub = (unsigned)ctx->cfg_max_worker_threads
21447
0
                        * (unsigned)sizeof(struct mg_connection);
21448
0
      mg_snprintf(NULL,
21449
0
                  NULL, /* No truncation check for error buffers */
21450
0
                  error->text,
21451
0
                  error->text_buffer_size,
21452
0
                  "%s",
21453
0
                  err_msg);
21454
0
    }
21455
21456
0
    free_context(ctx);
21457
0
    pthread_setspecific(sTlsKey, NULL);
21458
0
    return NULL;
21459
0
  }
21460
21461
#if defined(ALTERNATIVE_QUEUE)
21462
  ctx->client_wait_events =
21463
      (void **)mg_calloc_ctx(ctx->cfg_max_worker_threads,
21464
                             sizeof(ctx->client_wait_events[0]),
21465
                             ctx);
21466
  if (ctx->client_wait_events == NULL) {
21467
    const char *err_msg = "Not enough memory for worker event array";
21468
    mg_cry_ctx_internal(ctx, "%s", err_msg);
21469
    mg_free(ctx->worker_threadids);
21470
21471
    if (error != NULL) {
21472
      error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
21473
      error->code_sub = (unsigned)ctx->cfg_max_worker_threads
21474
                        * (unsigned)sizeof(ctx->client_wait_events[0]);
21475
      mg_snprintf(NULL,
21476
                  NULL, /* No truncation check for error buffers */
21477
                  error->text,
21478
                  error->text_buffer_size,
21479
                  "%s",
21480
                  err_msg);
21481
    }
21482
21483
    free_context(ctx);
21484
    pthread_setspecific(sTlsKey, NULL);
21485
    return NULL;
21486
  }
21487
21488
  ctx->client_socks =
21489
      (struct socket *)mg_calloc_ctx(ctx->cfg_max_worker_threads,
21490
                                     sizeof(ctx->client_socks[0]),
21491
                                     ctx);
21492
  if (ctx->client_socks == NULL) {
21493
    const char *err_msg = "Not enough memory for worker socket array";
21494
    mg_cry_ctx_internal(ctx, "%s", err_msg);
21495
    mg_free(ctx->client_wait_events);
21496
    mg_free(ctx->worker_threadids);
21497
21498
    if (error != NULL) {
21499
      error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
21500
      error->code_sub = (unsigned)ctx->cfg_max_worker_threads
21501
                        * (unsigned)sizeof(ctx->client_socks[0]);
21502
      mg_snprintf(NULL,
21503
                  NULL, /* No truncation check for error buffers */
21504
                  error->text,
21505
                  error->text_buffer_size,
21506
                  "%s",
21507
                  err_msg);
21508
    }
21509
21510
    free_context(ctx);
21511
    pthread_setspecific(sTlsKey, NULL);
21512
    return NULL;
21513
  }
21514
21515
  for (i = 0; (unsigned)i < ctx->cfg_max_worker_threads; i++) {
21516
    ctx->client_wait_events[i] = event_create();
21517
    if (ctx->client_wait_events[i] == 0) {
21518
      const char *err_msg = "Error creating worker event %i";
21519
      mg_cry_ctx_internal(ctx, err_msg, i);
21520
      while (i > 0) {
21521
        i--;
21522
        event_destroy(ctx->client_wait_events[i]);
21523
      }
21524
      mg_free(ctx->client_socks);
21525
      mg_free(ctx->client_wait_events);
21526
      mg_free(ctx->worker_threadids);
21527
21528
      if (error != NULL) {
21529
        error->code = MG_ERROR_DATA_CODE_OS_ERROR;
21530
        error->code_sub = (unsigned)ERRNO;
21531
        mg_snprintf(NULL,
21532
                    NULL, /* No truncation check for error buffers */
21533
                    error->text,
21534
                    error->text_buffer_size,
21535
                    err_msg,
21536
                    i);
21537
      }
21538
21539
      free_context(ctx);
21540
      pthread_setspecific(sTlsKey, NULL);
21541
      return NULL;
21542
    }
21543
  }
21544
#endif
21545
21546
#if defined(USE_TIMERS)
21547
  if (timers_init(ctx) != 0) {
21548
    const char *err_msg = "Error creating timers";
21549
    mg_cry_ctx_internal(ctx, "%s", err_msg);
21550
21551
    if (error != NULL) {
21552
      error->code = MG_ERROR_DATA_CODE_OS_ERROR;
21553
      error->code_sub = (unsigned)ERRNO;
21554
      mg_snprintf(NULL,
21555
                  NULL, /* No truncation check for error buffers */
21556
                  error->text,
21557
                  error->text_buffer_size,
21558
                  "%s",
21559
                  err_msg);
21560
    }
21561
21562
    free_context(ctx);
21563
    pthread_setspecific(sTlsKey, NULL);
21564
    return NULL;
21565
  }
21566
#endif
21567
21568
  /* Context has been created - init user libraries */
21569
2
  if (ctx->callbacks.init_context) {
21570
0
    ctx->callbacks.init_context(ctx);
21571
0
  }
21572
21573
  /* From now, the context is successfully created.
21574
   * When it is destroyed, the exit callback should be called. */
21575
2
  ctx->callbacks.exit_context = exit_callback;
21576
2
  ctx->context_type = CONTEXT_SERVER; /* server context */
21577
21578
  /* Start worker threads */
21579
2
  for (i = 0; (int)i < prespawnthreadcount; i++) {
21580
    /* worker_thread sets up the other fields */
21581
0
    if (mg_start_worker_thread(ctx, 0) != 0) {
21582
0
      long error_no = (long)ERRNO;
21583
21584
      /* thread was not created */
21585
0
      if (ctx->spawned_worker_threads > 0) {
21586
        /* If the second, third, ... thread cannot be created, set a
21587
         * warning, but keep running. */
21588
0
        mg_cry_ctx_internal(ctx,
21589
0
                            "Cannot start worker thread %i: error %ld",
21590
0
                            ctx->spawned_worker_threads + 1,
21591
0
                            error_no);
21592
21593
        /* If the server initialization should stop here, all
21594
         * threads that have already been created must be stopped
21595
         * first, before any free_context(ctx) call.
21596
         */
21597
21598
0
      } else {
21599
        /* If the first worker thread cannot be created, stop
21600
         * initialization and free the entire server context. */
21601
0
        mg_cry_ctx_internal(ctx,
21602
0
                            "Cannot create threads: error %ld",
21603
0
                            error_no);
21604
21605
0
        if (error != NULL) {
21606
0
          error->code = MG_ERROR_DATA_CODE_OS_ERROR;
21607
0
          error->code_sub = (unsigned)error_no;
21608
0
          mg_snprintf(
21609
0
              NULL,
21610
0
              NULL, /* No truncation check for error buffers */
21611
0
              error->text,
21612
0
              error->text_buffer_size,
21613
0
              "Cannot create first worker thread: error %ld",
21614
0
              error_no);
21615
0
        }
21616
21617
0
        free_context(ctx);
21618
0
        pthread_setspecific(sTlsKey, NULL);
21619
0
        return NULL;
21620
0
      }
21621
0
      break;
21622
0
    }
21623
0
  }
21624
21625
  /* Start master (listening) thread */
21626
2
  mg_start_thread_with_id(master_thread, ctx, &ctx->masterthreadid);
21627
21628
2
  pthread_setspecific(sTlsKey, NULL);
21629
2
  return ctx;
21630
2
}
21631
21632
21633
CIVETWEB_API struct mg_context *
21634
mg_start(const struct mg_callbacks *callbacks,
21635
         void *user_data,
21636
         const char **options)
21637
2
{
21638
2
  struct mg_init_data init = {0};
21639
2
  init.callbacks = callbacks;
21640
2
  init.user_data = user_data;
21641
2
  init.configuration_options = options;
21642
21643
2
  return mg_start2(&init, NULL);
21644
2
}
21645
21646
21647
/* Add an additional domain to an already running web server. */
21648
CIVETWEB_API int
21649
mg_start_domain2(struct mg_context *ctx,
21650
                 const char **options,
21651
                 struct mg_error_data *error)
21652
0
{
21653
0
  const char *name;
21654
0
  const char *value;
21655
0
  const char *default_value;
21656
0
  struct mg_domain_context *new_dom;
21657
0
  struct mg_domain_context *dom;
21658
0
  int idx, i;
21659
21660
0
  if (error != NULL) {
21661
0
    error->code = MG_ERROR_DATA_CODE_OK;
21662
0
    error->code_sub = 0;
21663
0
    if (error->text_buffer_size > 0) {
21664
0
      *error->text = 0;
21665
0
    }
21666
0
  }
21667
21668
0
  if ((ctx == NULL) || (options == NULL)) {
21669
0
    if (error != NULL) {
21670
0
      error->code = MG_ERROR_DATA_CODE_INVALID_PARAM;
21671
0
      mg_snprintf(NULL,
21672
0
                  NULL, /* No truncation check for error buffers */
21673
0
                  error->text,
21674
0
                  error->text_buffer_size,
21675
0
                  "%s",
21676
0
                  "Invalid parameters");
21677
0
    }
21678
0
    return -1;
21679
0
  }
21680
21681
0
  if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
21682
0
    if (error != NULL) {
21683
0
      error->code = MG_ERROR_DATA_CODE_SERVER_STOPPED;
21684
0
      mg_snprintf(NULL,
21685
0
                  NULL, /* No truncation check for error buffers */
21686
0
                  error->text,
21687
0
                  error->text_buffer_size,
21688
0
                  "%s",
21689
0
                  "Server already stopped");
21690
0
    }
21691
0
    return -7;
21692
0
  }
21693
21694
0
  new_dom = (struct mg_domain_context *)
21695
0
      mg_calloc_ctx(1, sizeof(struct mg_domain_context), ctx);
21696
21697
0
  if (!new_dom) {
21698
    /* Out of memory */
21699
0
    if (error != NULL) {
21700
0
      error->code = MG_ERROR_DATA_CODE_OUT_OF_MEMORY;
21701
0
      error->code_sub = (unsigned)sizeof(struct mg_domain_context);
21702
0
      mg_snprintf(NULL,
21703
0
                  NULL, /* No truncation check for error buffers */
21704
0
                  error->text,
21705
0
                  error->text_buffer_size,
21706
0
                  "%s",
21707
0
                  "Out or memory");
21708
0
    }
21709
0
    return -6;
21710
0
  }
21711
21712
  /* Store options - TODO: unite duplicate code */
21713
0
  while (options && (name = *options++) != NULL) {
21714
0
    idx = get_option_index(name);
21715
0
    if (idx == -1) {
21716
0
      mg_cry_ctx_internal(ctx, "Invalid option: %s", name);
21717
0
      if (error != NULL) {
21718
0
        error->code = MG_ERROR_DATA_CODE_INVALID_OPTION;
21719
0
        error->code_sub = (unsigned)-1;
21720
0
        mg_snprintf(NULL,
21721
0
                    NULL, /* No truncation check for error buffers */
21722
0
                    error->text,
21723
0
                    error->text_buffer_size,
21724
0
                    "Invalid option: %s",
21725
0
                    name);
21726
0
      }
21727
0
      mg_free(new_dom);
21728
0
      return -2;
21729
0
    } else if ((value = *options++) == NULL) {
21730
0
      mg_cry_ctx_internal(ctx, "%s: option value cannot be NULL", name);
21731
0
      if (error != NULL) {
21732
0
        error->code = MG_ERROR_DATA_CODE_INVALID_OPTION;
21733
0
        error->code_sub = (unsigned)idx;
21734
0
        mg_snprintf(NULL,
21735
0
                    NULL, /* No truncation check for error buffers */
21736
0
                    error->text,
21737
0
                    error->text_buffer_size,
21738
0
                    "Invalid option value: %s",
21739
0
                    name);
21740
0
      }
21741
0
      mg_free(new_dom);
21742
0
      return -2;
21743
0
    }
21744
0
    if (new_dom->config[idx] != NULL) {
21745
      /* Duplicate option: Later values overwrite earlier ones. */
21746
0
      mg_cry_ctx_internal(ctx, "warning: %s: duplicate option", name);
21747
0
      mg_free(new_dom->config[idx]);
21748
0
    }
21749
0
    new_dom->config[idx] = mg_strdup_ctx(value, ctx);
21750
0
    DEBUG_TRACE("[%s] -> [%s]", name, value);
21751
0
  }
21752
21753
  /* Authentication domain is mandatory */
21754
  /* TODO: Maybe use a new option hostname? */
21755
0
  if (!new_dom->config[AUTHENTICATION_DOMAIN]) {
21756
0
    mg_cry_ctx_internal(ctx, "%s", "authentication domain required");
21757
0
    if (error != NULL) {
21758
0
      error->code = MG_ERROR_DATA_CODE_MISSING_OPTION;
21759
0
      error->code_sub = AUTHENTICATION_DOMAIN;
21760
0
      mg_snprintf(NULL,
21761
0
                  NULL, /* No truncation check for error buffers */
21762
0
                  error->text,
21763
0
                  error->text_buffer_size,
21764
0
                  "Mandatory option %s missing",
21765
0
                  config_options[AUTHENTICATION_DOMAIN].name);
21766
0
    }
21767
0
    mg_free(new_dom);
21768
0
    return -4;
21769
0
  }
21770
21771
  /* Set default value if needed. Take the config value from
21772
   * ctx as a default value. */
21773
0
  for (i = 0; config_options[i].name != NULL; i++) {
21774
0
    default_value = ctx->dd.config[i];
21775
0
    if ((new_dom->config[i] == NULL) && (default_value != NULL)) {
21776
0
      new_dom->config[i] = mg_strdup_ctx(default_value, ctx);
21777
0
    }
21778
0
  }
21779
21780
0
  new_dom->handlers = NULL;
21781
0
  new_dom->next = NULL;
21782
0
  new_dom->nonce_count = 0;
21783
0
  new_dom->auth_nonce_mask = get_random() ^ (get_random() << 31);
21784
21785
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
21786
  new_dom->shared_lua_websockets = NULL;
21787
#endif
21788
21789
0
#if !defined(NO_SSL) && !defined(USE_MBEDTLS)
21790
0
  if (!init_ssl_ctx(ctx, new_dom)) {
21791
    /* Init SSL failed */
21792
0
    if (error != NULL) {
21793
0
      error->code = MG_ERROR_DATA_CODE_INIT_TLS_FAILED;
21794
0
      mg_snprintf(NULL,
21795
0
                  NULL, /* No truncation check for error buffers */
21796
0
                  error->text,
21797
0
                  error->text_buffer_size,
21798
0
                  "%s",
21799
0
                  "Initializing SSL context failed");
21800
0
    }
21801
0
    mg_free(new_dom);
21802
0
    return -3;
21803
0
  }
21804
0
#endif
21805
21806
  /* Add element to linked list. */
21807
0
  mg_lock_context(ctx);
21808
21809
0
  idx = 0;
21810
0
  dom = &(ctx->dd);
21811
0
  for (;;) {
21812
0
    if (!mg_strcasecmp(new_dom->config[AUTHENTICATION_DOMAIN],
21813
0
                       dom->config[AUTHENTICATION_DOMAIN])) {
21814
      /* Domain collision */
21815
0
      mg_cry_ctx_internal(ctx,
21816
0
                          "domain %s already in use",
21817
0
                          new_dom->config[AUTHENTICATION_DOMAIN]);
21818
0
      if (error != NULL) {
21819
0
        error->code = MG_ERROR_DATA_CODE_DUPLICATE_DOMAIN;
21820
0
        mg_snprintf(NULL,
21821
0
                    NULL, /* No truncation check for error buffers */
21822
0
                    error->text,
21823
0
                    error->text_buffer_size,
21824
0
                    "Domain %s specified by %s is already in use",
21825
0
                    new_dom->config[AUTHENTICATION_DOMAIN],
21826
0
                    config_options[AUTHENTICATION_DOMAIN].name);
21827
0
      }
21828
0
      mg_free(new_dom);
21829
0
      mg_unlock_context(ctx);
21830
0
      return -5;
21831
0
    }
21832
21833
    /* Count number of domains */
21834
0
    idx++;
21835
21836
0
    if (dom->next == NULL) {
21837
0
      dom->next = new_dom;
21838
0
      break;
21839
0
    }
21840
0
    dom = dom->next;
21841
0
  }
21842
21843
0
  mg_unlock_context(ctx);
21844
21845
  /* Return domain number */
21846
0
  return idx;
21847
0
}
21848
21849
21850
CIVETWEB_API int
21851
mg_start_domain(struct mg_context *ctx, const char **options)
21852
0
{
21853
0
  return mg_start_domain2(ctx, options, NULL);
21854
0
}
21855
21856
21857
/* Feature check API function */
21858
CIVETWEB_API unsigned
21859
mg_check_feature(unsigned feature)
21860
2
{
21861
2
  static const unsigned feature_set = 0
21862
  /* Set bits for available features according to API documentation.
21863
   * This bit mask is created at compile time, according to the active
21864
   * preprocessor defines. It is a single const value at runtime. */
21865
2
#if !defined(NO_FILES)
21866
2
                                      | MG_FEATURES_FILES
21867
2
#endif
21868
2
#if !defined(NO_SSL) || defined(USE_MBEDTLS)
21869
2
                                      | MG_FEATURES_SSL
21870
2
#endif
21871
2
#if !defined(NO_CGI)
21872
2
                                      | MG_FEATURES_CGI
21873
2
#endif
21874
#if defined(USE_IPV6)
21875
                                      | MG_FEATURES_IPV6
21876
#endif
21877
#if defined(USE_WEBSOCKET)
21878
                                      | MG_FEATURES_WEBSOCKET
21879
#endif
21880
#if defined(USE_LUA)
21881
                                      | MG_FEATURES_LUA
21882
#endif
21883
#if defined(USE_DUKTAPE)
21884
                                      | MG_FEATURES_SSJS
21885
#endif
21886
2
#if !defined(NO_CACHING)
21887
2
                                      | MG_FEATURES_CACHE
21888
2
#endif
21889
#if defined(USE_SERVER_STATS)
21890
                                      | MG_FEATURES_STATS
21891
#endif
21892
#if defined(USE_ZLIB)
21893
                                      | MG_FEATURES_COMPRESSION
21894
#endif
21895
#if defined(USE_HTTP2)
21896
                                      | MG_FEATURES_HTTP2
21897
#endif
21898
#if defined(USE_X_DOM_SOCKET)
21899
                                      | MG_FEATURES_X_DOMAIN_SOCKET
21900
#endif
21901
21902
  /* Set some extra bits not defined in the API documentation.
21903
   * These bits may change without further notice. */
21904
#if defined(MG_LEGACY_INTERFACE)
21905
                                      | 0x80000000u
21906
#endif
21907
#if defined(MG_EXPERIMENTAL_INTERFACES)
21908
                                      | 0x40000000u
21909
#endif
21910
2
#if !defined(NO_RESPONSE_BUFFERING)
21911
2
                                      | 0x20000000u
21912
2
#endif
21913
#if defined(MEMORY_DEBUGGING)
21914
                                      | 0x10000000u
21915
#endif
21916
2
      ;
21917
2
  return (feature & feature_set);
21918
2
}
21919
21920
21921
static size_t
21922
mg_str_append(char **dst, char *end, const char *src)
21923
0
{
21924
0
  size_t len = strlen(src);
21925
0
  if (*dst != end) {
21926
    /* Append src if enough space, or close dst. */
21927
0
    if ((size_t)(end - *dst) > len) {
21928
0
      strcpy(*dst, src);
21929
0
      *dst += len;
21930
0
    } else {
21931
0
      *dst = end;
21932
0
    }
21933
0
  }
21934
0
  return len;
21935
0
}
21936
21937
21938
/* Get system information. It can be printed or stored by the caller.
21939
 * Return the size of available information. */
21940
CIVETWEB_API int
21941
mg_get_system_info(char *buffer, int buflen)
21942
0
{
21943
0
  char *end, *append_eoobj = NULL, block[256];
21944
0
  size_t system_info_length = 0;
21945
21946
#if defined(_WIN32)
21947
  static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n";
21948
#else
21949
0
  static const char eol[] = "\n", eoobj[] = "\n}\n";
21950
0
#endif
21951
21952
0
  if ((buffer == NULL) || (buflen < 1)) {
21953
0
    buflen = 0;
21954
0
    end = buffer;
21955
0
  } else {
21956
0
    *buffer = 0;
21957
0
    end = buffer + buflen;
21958
0
  }
21959
0
  if (buflen > (int)(sizeof(eoobj) - 1)) {
21960
    /* has enough space to append eoobj */
21961
0
    append_eoobj = buffer;
21962
0
    if (end) {
21963
0
      end -= sizeof(eoobj) - 1;
21964
0
    }
21965
0
  }
21966
21967
0
  system_info_length += mg_str_append(&buffer, end, "{");
21968
21969
  /* Server version */
21970
0
  {
21971
0
    const char *version = mg_version();
21972
0
    mg_snprintf(NULL,
21973
0
                NULL,
21974
0
                block,
21975
0
                sizeof(block),
21976
0
                "%s\"version\" : \"%s\"",
21977
0
                eol,
21978
0
                version);
21979
0
    system_info_length += mg_str_append(&buffer, end, block);
21980
0
  }
21981
21982
  /* System info */
21983
0
  {
21984
#if defined(_WIN32)
21985
    DWORD dwVersion = 0;
21986
    DWORD dwMajorVersion = 0;
21987
    DWORD dwMinorVersion = 0;
21988
    SYSTEM_INFO si;
21989
21990
    GetSystemInfo(&si);
21991
21992
#if defined(_MSC_VER)
21993
#pragma warning(push)
21994
    /* GetVersion was declared deprecated */
21995
#pragma warning(disable : 4996)
21996
#endif
21997
    dwVersion = GetVersion();
21998
#if defined(_MSC_VER)
21999
#pragma warning(pop)
22000
#endif
22001
22002
    dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
22003
    dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
22004
22005
    mg_snprintf(NULL,
22006
                NULL,
22007
                block,
22008
                sizeof(block),
22009
                ",%s\"os\" : \"Windows %u.%u\"",
22010
                eol,
22011
                (unsigned)dwMajorVersion,
22012
                (unsigned)dwMinorVersion);
22013
    system_info_length += mg_str_append(&buffer, end, block);
22014
22015
    mg_snprintf(NULL,
22016
                NULL,
22017
                block,
22018
                sizeof(block),
22019
                ",%s\"cpu\" : \"type %u, cores %u, mask %x\"",
22020
                eol,
22021
                (unsigned)si.wProcessorArchitecture,
22022
                (unsigned)si.dwNumberOfProcessors,
22023
                (unsigned)si.dwActiveProcessorMask);
22024
    system_info_length += mg_str_append(&buffer, end, block);
22025
#elif defined(__ZEPHYR__)
22026
    mg_snprintf(NULL,
22027
                NULL,
22028
                block,
22029
                sizeof(block),
22030
                ",%s\"os\" : \"%s %s\"",
22031
                eol,
22032
                "Zephyr OS",
22033
                ZEPHYR_VERSION);
22034
    system_info_length += mg_str_append(&buffer, end, block);
22035
#else
22036
0
    struct utsname name;
22037
0
    memset(&name, 0, sizeof(name));
22038
0
    uname(&name);
22039
22040
0
    mg_snprintf(NULL,
22041
0
                NULL,
22042
0
                block,
22043
0
                sizeof(block),
22044
0
                ",%s\"os\" : \"%s %s (%s) - %s\"",
22045
0
                eol,
22046
0
                name.sysname,
22047
0
                name.version,
22048
0
                name.release,
22049
0
                name.machine);
22050
0
    system_info_length += mg_str_append(&buffer, end, block);
22051
0
#endif
22052
0
  }
22053
22054
  /* Features */
22055
0
  {
22056
0
    mg_snprintf(NULL,
22057
0
                NULL,
22058
0
                block,
22059
0
                sizeof(block),
22060
0
                ",%s\"features\" : %lu"
22061
0
                ",%s\"feature_list\" : \"Server:%s%s%s%s%s%s%s%s%s\"",
22062
0
                eol,
22063
0
                (unsigned long)mg_check_feature(0xFFFFFFFFu),
22064
0
                eol,
22065
0
                mg_check_feature(MG_FEATURES_FILES) ? " Files" : "",
22066
0
                mg_check_feature(MG_FEATURES_SSL) ? " HTTPS" : "",
22067
0
                mg_check_feature(MG_FEATURES_CGI) ? " CGI" : "",
22068
0
                mg_check_feature(MG_FEATURES_IPV6) ? " IPv6" : "",
22069
0
                mg_check_feature(MG_FEATURES_WEBSOCKET) ? " WebSockets"
22070
0
                                                        : "",
22071
0
                mg_check_feature(MG_FEATURES_LUA) ? " Lua" : "",
22072
0
                mg_check_feature(MG_FEATURES_SSJS) ? " JavaScript" : "",
22073
0
                mg_check_feature(MG_FEATURES_CACHE) ? " Cache" : "",
22074
0
                mg_check_feature(MG_FEATURES_STATS) ? " Stats" : "");
22075
0
    system_info_length += mg_str_append(&buffer, end, block);
22076
22077
#if defined(USE_LUA)
22078
    mg_snprintf(NULL,
22079
                NULL,
22080
                block,
22081
                sizeof(block),
22082
                ",%s\"lua_version\" : \"%u (%s)\"",
22083
                eol,
22084
                (unsigned)LUA_VERSION_NUM,
22085
                LUA_RELEASE);
22086
    system_info_length += mg_str_append(&buffer, end, block);
22087
#endif
22088
#if defined(USE_DUKTAPE)
22089
    mg_snprintf(NULL,
22090
                NULL,
22091
                block,
22092
                sizeof(block),
22093
                ",%s\"javascript\" : \"Duktape %u.%u.%u\"",
22094
                eol,
22095
                (unsigned)DUK_VERSION / 10000,
22096
                ((unsigned)DUK_VERSION / 100) % 100,
22097
                (unsigned)DUK_VERSION % 100);
22098
    system_info_length += mg_str_append(&buffer, end, block);
22099
#endif
22100
0
  }
22101
22102
  /* Build identifier. If BUILD_DATE is not set, __DATE__ will be used. */
22103
0
  {
22104
#if defined(BUILD_DATE)
22105
    const char *bd = BUILD_DATE;
22106
#else
22107
#if defined(GCC_DIAGNOSTIC)
22108
#if GCC_VERSION >= 40900
22109
#pragma GCC diagnostic push
22110
    /* Disable idiotic compiler warning -Wdate-time, appeared in gcc5. This
22111
     * does not work in some versions. If "BUILD_DATE" is defined to some
22112
     * string, it is used instead of __DATE__. */
22113
#pragma GCC diagnostic ignored "-Wdate-time"
22114
#endif
22115
#endif
22116
0
    const char *bd = __DATE__;
22117
#if defined(GCC_DIAGNOSTIC)
22118
#if GCC_VERSION >= 40900
22119
#pragma GCC diagnostic pop
22120
#endif
22121
#endif
22122
0
#endif
22123
22124
0
    mg_snprintf(
22125
0
        NULL, NULL, block, sizeof(block), ",%s\"build\" : \"%s\"", eol, bd);
22126
22127
0
    system_info_length += mg_str_append(&buffer, end, block);
22128
0
  }
22129
22130
  /* Compiler information */
22131
  /* http://sourceforge.net/p/predef/wiki/Compilers/ */
22132
0
  {
22133
#if defined(_MSC_VER)
22134
    mg_snprintf(NULL,
22135
                NULL,
22136
                block,
22137
                sizeof(block),
22138
                ",%s\"compiler\" : \"MSC: %u (%u)\"",
22139
                eol,
22140
                (unsigned)_MSC_VER,
22141
                (unsigned)_MSC_FULL_VER);
22142
    system_info_length += mg_str_append(&buffer, end, block);
22143
#elif defined(__MINGW64__)
22144
    mg_snprintf(NULL,
22145
                NULL,
22146
                block,
22147
                sizeof(block),
22148
                ",%s\"compiler\" : \"MinGW64: %u.%u\"",
22149
                eol,
22150
                (unsigned)__MINGW64_VERSION_MAJOR,
22151
                (unsigned)__MINGW64_VERSION_MINOR);
22152
    system_info_length += mg_str_append(&buffer, end, block);
22153
    mg_snprintf(NULL,
22154
                NULL,
22155
                block,
22156
                sizeof(block),
22157
                ",%s\"compiler\" : \"MinGW32: %u.%u\"",
22158
                eol,
22159
                (unsigned)__MINGW32_MAJOR_VERSION,
22160
                (unsigned)__MINGW32_MINOR_VERSION);
22161
    system_info_length += mg_str_append(&buffer, end, block);
22162
#elif defined(__MINGW32__)
22163
    mg_snprintf(NULL,
22164
                NULL,
22165
                block,
22166
                sizeof(block),
22167
                ",%s\"compiler\" : \"MinGW32: %u.%u\"",
22168
                eol,
22169
                (unsigned)__MINGW32_MAJOR_VERSION,
22170
                (unsigned)__MINGW32_MINOR_VERSION);
22171
    system_info_length += mg_str_append(&buffer, end, block);
22172
#elif defined(__clang__)
22173
0
    mg_snprintf(NULL,
22174
0
                NULL,
22175
0
                block,
22176
0
                sizeof(block),
22177
0
                ",%s\"compiler\" : \"clang: %u.%u.%u (%s)\"",
22178
0
                eol,
22179
0
                __clang_major__,
22180
0
                __clang_minor__,
22181
0
                __clang_patchlevel__,
22182
0
                __clang_version__);
22183
0
    system_info_length += mg_str_append(&buffer, end, block);
22184
#elif defined(__GNUC__)
22185
    mg_snprintf(NULL,
22186
                NULL,
22187
                block,
22188
                sizeof(block),
22189
                ",%s\"compiler\" : \"gcc: %u.%u.%u\"",
22190
                eol,
22191
                (unsigned)__GNUC__,
22192
                (unsigned)__GNUC_MINOR__,
22193
                (unsigned)__GNUC_PATCHLEVEL__);
22194
    system_info_length += mg_str_append(&buffer, end, block);
22195
#elif defined(__INTEL_COMPILER)
22196
    mg_snprintf(NULL,
22197
                NULL,
22198
                block,
22199
                sizeof(block),
22200
                ",%s\"compiler\" : \"Intel C/C++: %u\"",
22201
                eol,
22202
                (unsigned)__INTEL_COMPILER);
22203
    system_info_length += mg_str_append(&buffer, end, block);
22204
#elif defined(__BORLANDC__)
22205
    mg_snprintf(NULL,
22206
                NULL,
22207
                block,
22208
                sizeof(block),
22209
                ",%s\"compiler\" : \"Borland C: 0x%x\"",
22210
                eol,
22211
                (unsigned)__BORLANDC__);
22212
    system_info_length += mg_str_append(&buffer, end, block);
22213
#elif defined(__SUNPRO_C)
22214
    mg_snprintf(NULL,
22215
                NULL,
22216
                block,
22217
                sizeof(block),
22218
                ",%s\"compiler\" : \"Solaris: 0x%x\"",
22219
                eol,
22220
                (unsigned)__SUNPRO_C);
22221
    system_info_length += mg_str_append(&buffer, end, block);
22222
#else
22223
    mg_snprintf(NULL,
22224
                NULL,
22225
                block,
22226
                sizeof(block),
22227
                ",%s\"compiler\" : \"other\"",
22228
                eol);
22229
    system_info_length += mg_str_append(&buffer, end, block);
22230
#endif
22231
0
  }
22232
22233
  /* Determine 32/64 bit data mode.
22234
   * see https://en.wikipedia.org/wiki/64-bit_computing */
22235
0
  {
22236
0
    mg_snprintf(NULL,
22237
0
                NULL,
22238
0
                block,
22239
0
                sizeof(block),
22240
0
                ",%s\"data_model\" : \"int:%u/%u/%u/%u, float:%u/%u/%u, "
22241
0
                "char:%u/%u, "
22242
0
                "ptr:%u, size:%u, time:%u\"",
22243
0
                eol,
22244
0
                (unsigned)sizeof(short),
22245
0
                (unsigned)sizeof(int),
22246
0
                (unsigned)sizeof(long),
22247
0
                (unsigned)sizeof(long long),
22248
0
                (unsigned)sizeof(float),
22249
0
                (unsigned)sizeof(double),
22250
0
                (unsigned)sizeof(long double),
22251
0
                (unsigned)sizeof(char),
22252
0
                (unsigned)sizeof(wchar_t),
22253
0
                (unsigned)sizeof(void *),
22254
0
                (unsigned)sizeof(size_t),
22255
0
                (unsigned)sizeof(time_t));
22256
0
    system_info_length += mg_str_append(&buffer, end, block);
22257
0
  }
22258
22259
  /* Terminate string */
22260
0
  if (append_eoobj) {
22261
0
    strcat(append_eoobj, eoobj);
22262
0
  }
22263
0
  system_info_length += sizeof(eoobj) - 1;
22264
22265
0
  return (int)system_info_length;
22266
0
}
22267
22268
22269
/* Get context information. It can be printed or stored by the caller.
22270
 * Return the size of available information. */
22271
CIVETWEB_API int
22272
mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen)
22273
0
{
22274
#if defined(USE_SERVER_STATS)
22275
  char *end, *append_eoobj = NULL, block[256];
22276
  size_t context_info_length = 0;
22277
22278
#if defined(_WIN32)
22279
  static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n";
22280
#else
22281
  static const char eol[] = "\n", eoobj[] = "\n}\n";
22282
#endif
22283
  struct mg_memory_stat *ms = get_memory_stat((struct mg_context *)ctx);
22284
22285
  if ((buffer == NULL) || (buflen < 1)) {
22286
    buflen = 0;
22287
    end = buffer;
22288
  } else {
22289
    *buffer = 0;
22290
    end = buffer + buflen;
22291
  }
22292
  if (buflen > (int)(sizeof(eoobj) - 1)) {
22293
    /* has enough space to append eoobj */
22294
    append_eoobj = buffer;
22295
    end -= sizeof(eoobj) - 1;
22296
  }
22297
22298
  context_info_length += mg_str_append(&buffer, end, "{");
22299
22300
  if (ms) { /* <-- should be always true */
22301
          /* Memory information */
22302
    int blockCount = (int)ms->blockCount;
22303
    int64_t totalMemUsed = ms->totalMemUsed;
22304
    int64_t maxMemUsed = ms->maxMemUsed;
22305
    if (totalMemUsed > maxMemUsed) {
22306
      maxMemUsed = totalMemUsed;
22307
    }
22308
22309
    mg_snprintf(NULL,
22310
                NULL,
22311
                block,
22312
                sizeof(block),
22313
                "%s\"memory\" : {%s"
22314
                "\"blocks\" : %i,%s"
22315
                "\"used\" : %" INT64_FMT ",%s"
22316
                "\"maxUsed\" : %" INT64_FMT "%s"
22317
                "}",
22318
                eol,
22319
                eol,
22320
                blockCount,
22321
                eol,
22322
                totalMemUsed,
22323
                eol,
22324
                maxMemUsed,
22325
                eol);
22326
    context_info_length += mg_str_append(&buffer, end, block);
22327
  }
22328
22329
  if (ctx) {
22330
    /* Declare all variables at begin of the block, to comply
22331
     * with old C standards. */
22332
    char start_time_str[64] = {0};
22333
    char now_str[64] = {0};
22334
    time_t start_time = ctx->start_time;
22335
    time_t now = time(NULL);
22336
    int64_t total_data_read, total_data_written;
22337
    int active_connections = (int)ctx->active_connections;
22338
    int max_active_connections = (int)ctx->max_active_connections;
22339
    int total_connections = (int)ctx->total_connections;
22340
    if (active_connections > max_active_connections) {
22341
      max_active_connections = active_connections;
22342
    }
22343
    if (active_connections > total_connections) {
22344
      total_connections = active_connections;
22345
    }
22346
22347
    /* Connections information */
22348
    mg_snprintf(NULL,
22349
                NULL,
22350
                block,
22351
                sizeof(block),
22352
                ",%s\"connections\" : {%s"
22353
                "\"active\" : %i,%s"
22354
                "\"maxActive\" : %i,%s"
22355
                "\"total\" : %i%s"
22356
                "}",
22357
                eol,
22358
                eol,
22359
                active_connections,
22360
                eol,
22361
                max_active_connections,
22362
                eol,
22363
                total_connections,
22364
                eol);
22365
    context_info_length += mg_str_append(&buffer, end, block);
22366
22367
    /* Queue information */
22368
#if !defined(ALTERNATIVE_QUEUE)
22369
    mg_snprintf(NULL,
22370
                NULL,
22371
                block,
22372
                sizeof(block),
22373
                ",%s\"queue\" : {%s"
22374
                "\"length\" : %i,%s"
22375
                "\"filled\" : %i,%s"
22376
                "\"maxFilled\" : %i,%s"
22377
                "\"full\" : %s%s"
22378
                "}",
22379
                eol,
22380
                eol,
22381
                ctx->sq_size,
22382
                eol,
22383
                ctx->sq_head - ctx->sq_tail,
22384
                eol,
22385
                ctx->sq_max_fill,
22386
                eol,
22387
                (ctx->sq_blocked ? "true" : "false"),
22388
                eol);
22389
    context_info_length += mg_str_append(&buffer, end, block);
22390
#endif
22391
22392
    /* Requests information */
22393
    mg_snprintf(NULL,
22394
                NULL,
22395
                block,
22396
                sizeof(block),
22397
                ",%s\"requests\" : {%s"
22398
                "\"total\" : %lu%s"
22399
                "}",
22400
                eol,
22401
                eol,
22402
                (unsigned long)ctx->total_requests,
22403
                eol);
22404
    context_info_length += mg_str_append(&buffer, end, block);
22405
22406
    /* Data information */
22407
    total_data_read =
22408
        mg_atomic_add64((volatile int64_t *)&ctx->total_data_read, 0);
22409
    total_data_written =
22410
        mg_atomic_add64((volatile int64_t *)&ctx->total_data_written, 0);
22411
    mg_snprintf(NULL,
22412
                NULL,
22413
                block,
22414
                sizeof(block),
22415
                ",%s\"data\" : {%s"
22416
                "\"read\" : %" INT64_FMT ",%s"
22417
                "\"written\" : %" INT64_FMT "%s"
22418
                "}",
22419
                eol,
22420
                eol,
22421
                total_data_read,
22422
                eol,
22423
                total_data_written,
22424
                eol);
22425
    context_info_length += mg_str_append(&buffer, end, block);
22426
22427
    /* Execution time information */
22428
    gmt_time_string(start_time_str,
22429
                    sizeof(start_time_str) - 1,
22430
                    &start_time);
22431
    gmt_time_string(now_str, sizeof(now_str) - 1, &now);
22432
22433
    mg_snprintf(NULL,
22434
                NULL,
22435
                block,
22436
                sizeof(block),
22437
                ",%s\"time\" : {%s"
22438
                "\"uptime\" : %.0f,%s"
22439
                "\"start\" : \"%s\",%s"
22440
                "\"now\" : \"%s\"%s"
22441
                "}",
22442
                eol,
22443
                eol,
22444
                difftime(now, start_time),
22445
                eol,
22446
                start_time_str,
22447
                eol,
22448
                now_str,
22449
                eol);
22450
    context_info_length += mg_str_append(&buffer, end, block);
22451
  }
22452
22453
  /* Terminate string */
22454
  if (append_eoobj) {
22455
    strcat(append_eoobj, eoobj);
22456
  }
22457
  context_info_length += sizeof(eoobj) - 1;
22458
22459
  return (int)context_info_length;
22460
#else
22461
0
  (void)ctx;
22462
0
  if ((buffer != NULL) && (buflen > 0)) {
22463
0
    *buffer = 0;
22464
0
  }
22465
0
  return 0;
22466
0
#endif
22467
0
}
22468
22469
22470
CIVETWEB_API void
22471
mg_disable_connection_keep_alive(struct mg_connection *conn)
22472
0
{
22473
  /* https://github.com/civetweb/civetweb/issues/727 */
22474
0
  if (conn != NULL) {
22475
0
    conn->must_close = 1;
22476
0
  }
22477
0
}
22478
22479
22480
#if defined(MG_EXPERIMENTAL_INTERFACES)
22481
/* Get connection information. It can be printed or stored by the caller.
22482
 * Return the size of available information. */
22483
CIVETWEB_API int
22484
mg_get_connection_info(const struct mg_context *ctx,
22485
                       int idx,
22486
                       char *buffer,
22487
                       int buflen)
22488
{
22489
  const struct mg_connection *conn;
22490
  const struct mg_request_info *ri;
22491
  char *end, *append_eoobj = NULL, block[256];
22492
  size_t connection_info_length = 0;
22493
  int state = 0;
22494
  const char *state_str = "unknown";
22495
22496
#if defined(_WIN32)
22497
  static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n";
22498
#else
22499
  static const char eol[] = "\n", eoobj[] = "\n}\n";
22500
#endif
22501
22502
  if ((buffer == NULL) || (buflen < 1)) {
22503
    buflen = 0;
22504
    end = buffer;
22505
  } else {
22506
    *buffer = 0;
22507
    end = buffer + buflen;
22508
  }
22509
  if (buflen > (int)(sizeof(eoobj) - 1)) {
22510
    /* has enough space to append eoobj */
22511
    append_eoobj = buffer;
22512
    end -= sizeof(eoobj) - 1;
22513
  }
22514
22515
  if ((ctx == NULL) || (idx < 0)) {
22516
    /* Parameter error */
22517
    return 0;
22518
  }
22519
22520
  if ((unsigned)idx >= ctx->cfg_max_worker_threads) {
22521
    /* Out of range */
22522
    return 0;
22523
  }
22524
22525
  /* Take connection [idx]. This connection is not locked in
22526
   * any way, so some other thread might use it. */
22527
  conn = (ctx->worker_connections) + idx;
22528
22529
  /* Initialize output string */
22530
  connection_info_length += mg_str_append(&buffer, end, "{");
22531
22532
  /* Init variables */
22533
  ri = &(conn->request_info);
22534
22535
#if defined(USE_SERVER_STATS)
22536
  state = conn->conn_state;
22537
22538
  /* State as string */
22539
  switch (state) {
22540
  case 0:
22541
    state_str = "undefined";
22542
    break;
22543
  case 1:
22544
    state_str = "not used";
22545
    break;
22546
  case 2:
22547
    state_str = "init";
22548
    break;
22549
  case 3:
22550
    state_str = "ready";
22551
    break;
22552
  case 4:
22553
    state_str = "processing";
22554
    break;
22555
  case 5:
22556
    state_str = "processed";
22557
    break;
22558
  case 6:
22559
    state_str = "to close";
22560
    break;
22561
  case 7:
22562
    state_str = "closing";
22563
    break;
22564
  case 8:
22565
    state_str = "closed";
22566
    break;
22567
  case 9:
22568
    state_str = "done";
22569
    break;
22570
  }
22571
#endif
22572
22573
  /* Connection info */
22574
  if ((state >= 3) && (state < 9)) {
22575
    mg_snprintf(NULL,
22576
                NULL,
22577
                block,
22578
                sizeof(block),
22579
                "%s\"connection\" : {%s"
22580
                "\"remote\" : {%s"
22581
                "\"protocol\" : \"%s\",%s"
22582
                "\"addr\" : \"%s\",%s"
22583
                "\"port\" : %u%s"
22584
                "},%s"
22585
                "\"handled_requests\" : %u%s"
22586
                "}",
22587
                eol,
22588
                eol,
22589
                eol,
22590
                get_proto_name(conn),
22591
                eol,
22592
                ri->remote_addr,
22593
                eol,
22594
                ri->remote_port,
22595
                eol,
22596
                eol,
22597
                conn->handled_requests,
22598
                eol);
22599
    connection_info_length += mg_str_append(&buffer, end, block);
22600
  }
22601
22602
  /* Request info */
22603
  if ((state >= 4) && (state < 6)) {
22604
    mg_snprintf(NULL,
22605
                NULL,
22606
                block,
22607
                sizeof(block),
22608
                "%s%s\"request_info\" : {%s"
22609
                "\"method\" : \"%s\",%s"
22610
                "\"uri\" : \"%s\",%s"
22611
                "\"query\" : %s%s%s%s"
22612
                "}",
22613
                (connection_info_length > 1 ? "," : ""),
22614
                eol,
22615
                eol,
22616
                ri->request_method,
22617
                eol,
22618
                ri->request_uri,
22619
                eol,
22620
                ri->query_string ? "\"" : "",
22621
                ri->query_string ? ri->query_string : "null",
22622
                ri->query_string ? "\"" : "",
22623
                eol);
22624
    connection_info_length += mg_str_append(&buffer, end, block);
22625
  }
22626
22627
  /* Execution time information */
22628
  if ((state >= 2) && (state < 9)) {
22629
    char start_time_str[64] = {0};
22630
    char close_time_str[64] = {0};
22631
    time_t start_time = conn->conn_birth_time;
22632
    time_t close_time = 0;
22633
    double time_diff;
22634
22635
    gmt_time_string(start_time_str,
22636
                    sizeof(start_time_str) - 1,
22637
                    &start_time);
22638
#if defined(USE_SERVER_STATS)
22639
    close_time = conn->conn_close_time;
22640
#endif
22641
    if (close_time != 0) {
22642
      time_diff = difftime(close_time, start_time);
22643
      gmt_time_string(close_time_str,
22644
                      sizeof(close_time_str) - 1,
22645
                      &close_time);
22646
    } else {
22647
      time_t now = time(NULL);
22648
      time_diff = difftime(now, start_time);
22649
      close_time_str[0] = 0; /* or use "now" ? */
22650
    }
22651
22652
    mg_snprintf(NULL,
22653
                NULL,
22654
                block,
22655
                sizeof(block),
22656
                "%s%s\"time\" : {%s"
22657
                "\"uptime\" : %.0f,%s"
22658
                "\"start\" : \"%s\",%s"
22659
                "\"closed\" : \"%s\"%s"
22660
                "}",
22661
                (connection_info_length > 1 ? "," : ""),
22662
                eol,
22663
                eol,
22664
                time_diff,
22665
                eol,
22666
                start_time_str,
22667
                eol,
22668
                close_time_str,
22669
                eol);
22670
    connection_info_length += mg_str_append(&buffer, end, block);
22671
  }
22672
22673
  /* Remote user name */
22674
  if ((ri->remote_user) && (state < 9)) {
22675
    mg_snprintf(NULL,
22676
                NULL,
22677
                block,
22678
                sizeof(block),
22679
                "%s%s\"user\" : {%s"
22680
                "\"name\" : \"%s\",%s"
22681
                "}",
22682
                (connection_info_length > 1 ? "," : ""),
22683
                eol,
22684
                eol,
22685
                ri->remote_user,
22686
                eol);
22687
    connection_info_length += mg_str_append(&buffer, end, block);
22688
  }
22689
22690
  /* Data block */
22691
  if (state >= 3) {
22692
    mg_snprintf(NULL,
22693
                NULL,
22694
                block,
22695
                sizeof(block),
22696
                "%s%s\"data\" : {%s"
22697
                "\"read\" : %" INT64_FMT ",%s"
22698
                "\"written\" : %" INT64_FMT "%s"
22699
                "}",
22700
                (connection_info_length > 1 ? "," : ""),
22701
                eol,
22702
                eol,
22703
                conn->consumed_content,
22704
                eol,
22705
                conn->num_bytes_sent,
22706
                eol);
22707
    connection_info_length += mg_str_append(&buffer, end, block);
22708
  }
22709
22710
  /* State */
22711
  mg_snprintf(NULL,
22712
              NULL,
22713
              block,
22714
              sizeof(block),
22715
              "%s%s\"state\" : \"%s\"",
22716
              (connection_info_length > 1 ? "," : ""),
22717
              eol,
22718
              state_str);
22719
  connection_info_length += mg_str_append(&buffer, end, block);
22720
22721
  /* Terminate string */
22722
  if (append_eoobj) {
22723
    strcat(append_eoobj, eoobj);
22724
  }
22725
  connection_info_length += sizeof(eoobj) - 1;
22726
22727
  return (int)connection_info_length;
22728
}
22729
22730
22731
#if 0
22732
/* Get handler information. Not fully implemented. Is it required? */
22733
CIVETWEB_API int
22734
mg_get_handler_info(struct mg_context *ctx,
22735
  char *buffer,
22736
  int buflen)
22737
{
22738
  int handler_info_len = 0;
22739
  struct mg_handler_info *tmp_rh;
22740
  mg_lock_context(ctx);
22741
22742
  for (tmp_rh = ctx->dd.handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
22743
22744
    if (buflen > handler_info_len + tmp_rh->uri_len) {
22745
      memcpy(buffer + handler_info_len, tmp_rh->uri, tmp_rh->uri_len);
22746
    }
22747
    handler_info_len += tmp_rh->uri_len;
22748
22749
    switch (tmp_rh->handler_type) {
22750
    case REQUEST_HANDLER:
22751
      (void)tmp_rh->handler;
22752
      break;
22753
    case WEBSOCKET_HANDLER:
22754
      (void)tmp_rh->connect_handler;
22755
      (void)tmp_rh->ready_handler;
22756
      (void)tmp_rh->data_handler;
22757
      (void)tmp_rh->close_handler;
22758
      break;
22759
    case AUTH_HANDLER:
22760
      (void)tmp_rh->auth_handler;
22761
      break;
22762
    }
22763
    (void)cbdata;
22764
  }
22765
22766
  mg_unlock_context(ctx);
22767
  return handler_info_len;
22768
}
22769
#endif
22770
#endif
22771
22772
22773
/* Initialize this library. This function does not need to be thread safe.
22774
 */
22775
CIVETWEB_API unsigned
22776
mg_init_library(unsigned features)
22777
2
{
22778
2
  unsigned features_to_init = mg_check_feature(features & 0xFFu);
22779
2
  unsigned features_inited = features_to_init;
22780
22781
2
  if (mg_init_library_called <= 0) {
22782
    /* Not initialized yet */
22783
2
    if (0 != pthread_mutex_init(&global_lock_mutex, NULL)) {
22784
0
      return 0;
22785
0
    }
22786
2
  }
22787
22788
2
  mg_global_lock();
22789
22790
2
  if (mg_init_library_called <= 0) {
22791
2
    int i;
22792
2
    size_t len;
22793
22794
#if defined(_WIN32)
22795
    int file_mutex_init = 1;
22796
    int wsa = 1;
22797
#else
22798
2
    int mutexattr_init = 1;
22799
2
#endif
22800
2
    int failed = 1;
22801
2
    int key_create = pthread_key_create(&sTlsKey, tls_dtor);
22802
22803
2
    if (key_create == 0) {
22804
#if defined(_WIN32)
22805
      file_mutex_init =
22806
          pthread_mutex_init(&global_log_file_lock, &pthread_mutex_attr);
22807
      if (file_mutex_init == 0) {
22808
        /* Start WinSock */
22809
        WSADATA data;
22810
        failed = wsa = WSAStartup(MAKEWORD(2, 2), &data);
22811
      }
22812
#else
22813
2
      mutexattr_init = pthread_mutexattr_init(&pthread_mutex_attr);
22814
2
      if (mutexattr_init == 0) {
22815
2
        failed = pthread_mutexattr_settype(&pthread_mutex_attr,
22816
2
                                           PTHREAD_MUTEX_RECURSIVE);
22817
2
      }
22818
2
#endif
22819
2
    }
22820
22821
2
    if (failed) {
22822
#if defined(_WIN32)
22823
      if (wsa == 0) {
22824
        (void)WSACleanup();
22825
      }
22826
      if (file_mutex_init == 0) {
22827
        (void)pthread_mutex_destroy(&global_log_file_lock);
22828
      }
22829
#else
22830
0
      if (mutexattr_init == 0) {
22831
0
        (void)pthread_mutexattr_destroy(&pthread_mutex_attr);
22832
0
      }
22833
0
#endif
22834
0
      if (key_create == 0) {
22835
0
        (void)pthread_key_delete(sTlsKey);
22836
0
      }
22837
0
      mg_global_unlock();
22838
0
      (void)pthread_mutex_destroy(&global_lock_mutex);
22839
0
      return 0;
22840
0
    }
22841
22842
2
    len = 1;
22843
34
    for (i = 0; http_methods[i].name != NULL; i++) {
22844
32
      size_t sl = strlen(http_methods[i].name);
22845
32
      len += sl;
22846
32
      if (i > 0) {
22847
30
        len += 2;
22848
30
      }
22849
32
    }
22850
2
    all_methods = (char *)mg_malloc(len);
22851
2
    if (!all_methods) {
22852
      /* Must never happen */
22853
0
      mg_global_unlock();
22854
0
      (void)pthread_mutex_destroy(&global_lock_mutex);
22855
0
      return 0;
22856
0
    }
22857
2
    all_methods[0] = 0;
22858
34
    for (i = 0; http_methods[i].name != NULL; i++) {
22859
32
      if (i > 0) {
22860
30
        strcat(all_methods, ", ");
22861
30
        strcat(all_methods, http_methods[i].name);
22862
30
      } else {
22863
2
        strcpy(all_methods, http_methods[i].name);
22864
2
      }
22865
32
    }
22866
2
  }
22867
22868
#if defined(USE_LUA)
22869
  lua_init_optional_libraries();
22870
#endif
22871
22872
2
#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1)                      \
22873
2
     || defined(OPENSSL_API_3_0))                                              \
22874
2
    && !defined(NO_SSL)
22875
22876
2
  if (features_to_init & MG_FEATURES_SSL) {
22877
0
    if (!mg_openssl_initialized) {
22878
0
      char ebuf[128];
22879
0
      if (initialize_openssl(ebuf, sizeof(ebuf))) {
22880
0
        mg_openssl_initialized = 1;
22881
0
      } else {
22882
0
        (void)ebuf;
22883
0
        DEBUG_TRACE("Initializing SSL failed: %s", ebuf);
22884
0
        features_inited &= ~((unsigned)(MG_FEATURES_SSL));
22885
0
      }
22886
0
    } else {
22887
      /* ssl already initialized */
22888
0
    }
22889
0
  }
22890
22891
2
#endif
22892
22893
2
  if (mg_init_library_called <= 0) {
22894
2
    mg_init_library_called = 1;
22895
2
  } else {
22896
0
    mg_init_library_called++;
22897
0
  }
22898
2
  mg_global_unlock();
22899
22900
2
  return features_inited;
22901
2
}
22902
22903
22904
/* Un-initialize this library. */
22905
CIVETWEB_API unsigned
22906
mg_exit_library(void)
22907
0
{
22908
0
  if (mg_init_library_called <= 0) {
22909
0
    return 0;
22910
0
  }
22911
22912
0
  mg_global_lock();
22913
22914
0
  mg_init_library_called--;
22915
0
  if (mg_init_library_called == 0) {
22916
0
#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1)) && !defined(NO_SSL)
22917
0
    if (mg_openssl_initialized) {
22918
0
      uninitialize_openssl();
22919
0
      mg_openssl_initialized = 0;
22920
0
    }
22921
0
#endif
22922
22923
#if defined(_WIN32)
22924
    (void)WSACleanup();
22925
    (void)pthread_mutex_destroy(&global_log_file_lock);
22926
#else
22927
0
    (void)pthread_mutexattr_destroy(&pthread_mutex_attr);
22928
0
#endif
22929
22930
0
    (void)pthread_key_delete(sTlsKey);
22931
22932
#if defined(USE_LUA)
22933
    lua_exit_optional_libraries();
22934
#endif
22935
0
    mg_free(all_methods);
22936
0
    all_methods = NULL;
22937
22938
0
    mg_global_unlock();
22939
0
    (void)pthread_mutex_destroy(&global_lock_mutex);
22940
0
    return 1;
22941
0
  }
22942
22943
0
  mg_global_unlock();
22944
0
  return 1;
22945
0
}
22946
22947
22948
/* End of civetweb.c */