MIDAS
Loading...
Searching...
No Matches
mongoose4.cxx
Go to the documentation of this file.
1// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
2// Copyright (c) 2013 Cesanta Software Limited
3// All rights reserved
4//
5// This library is dual-licensed: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License version 2 as
7// published by the Free Software Foundation. For the terms of this
8// license, see <http://www.gnu.org/licenses/>.
9//
10// You are free to use this library under the terms of the GNU General
11// Public License, but WITHOUT ANY WARRANTY; without even the implied
12// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13// See the GNU General Public License for more details.
14//
15// Alternatively, you can license this library under a commercial
16// license, as set out in <http://cesanta.com/products.html>.
17
18// build MIDAS with explicit dependance on OpenSSL
19#define NO_SSL_DL 1
20
21// if requested a no-openssl build, use dynamically linked openssl option -
22// mongoose has no support for completely no-openssl build.
23#if defined(NO_SSL)
24#undef NO_SSL_DL
25#endif
26
27#if defined(_WIN32)
28#undef _UNICODE
29#undef _MBCS
30#define _MBCS
31#if !defined(_CRT_SECURE_NO_WARNINGS)
32#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
33#endif
34#else
35#ifdef __linux__
36#define _XOPEN_SOURCE 600 // For flockfile() on Linux
37#endif
38#if !defined(_LARGEFILE_SOURCE)
39#define _LARGEFILE_SOURCE // Enable 64-bit file offsets
40#endif
41#define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++
42#define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX
43#endif
44
45#if defined (_MSC_VER)
46// conditional expression is constant: introduced by FD_SET(..)
47#pragma warning (disable : 4127)
48// non-constant aggregate initializer: issued due to missing C99 support
49#pragma warning (disable : 4204)
50#endif
51
52// Disable WIN32_LEAN_AND_MEAN.
53// This makes windows.h always include winsock2.h
54#ifdef WIN32_LEAN_AND_MEAN
55#undef WIN32_LEAN_AND_MEAN
56#endif
57
58#if defined(__SYMBIAN32__)
59#define NO_SSL // SSL is not supported
60#define NO_CGI // CGI is not supported
61#define PATH_MAX FILENAME_MAX
62#endif // __SYMBIAN32__
63
64#ifndef _WIN32_WCE // Some ANSI #includes are not available on Windows CE
65#include <sys/types.h>
66#include <sys/stat.h>
67#include <errno.h>
68#include <signal.h>
69#include <fcntl.h>
70#endif // !_WIN32_WCE
71
72#include <time.h>
73#include <stdlib.h>
74#include <stdarg.h>
75#include <assert.h>
76#include <string.h>
77#include <ctype.h>
78#include <limits.h>
79#include <stddef.h>
80#include <stdio.h>
81
82#if defined(_WIN32) && !defined(__SYMBIAN32__) // Windows specific
83#undef _WIN32_WINNT
84#define _WIN32_WINNT 0x0400 // To make it link in VS2005
85#include <windows.h>
86
87#ifndef PATH_MAX
88#define PATH_MAX MAX_PATH
89#endif
90
91#ifndef _WIN32_WCE
92#include <process.h>
93#include <direct.h>
94#include <io.h>
95#else // _WIN32_WCE
96#define NO_CGI // WinCE has no pipes
97
98typedef long off_t;
99
100#define errno GetLastError()
101#define strerror(x) _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10)
102#endif // _WIN32_WCE
103
104#define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \
105 ((uint64_t)((uint32_t)(hi))) << 32))
106#define RATE_DIFF 10000000 // 100 nsecs
107#define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de)
108#define SYS2UNIX_TIME(lo, hi) \
109 (time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)
110
111// Visual Studio 6 does not know __func__ or __FUNCTION__
112// The rest of MS compilers use __FUNCTION__, not C99 __func__
113// Also use _strtoui64 on modern M$ compilers
114#if defined(_MSC_VER) && _MSC_VER < 1300
115#define STRX(x) #x
116#define STR(x) STRX(x)
117#define __func__ __FILE__ ":" STR(__LINE__)
118#define strtoull(x, y, z) (unsigned __int64) _atoi64(x)
119#define strtoll(x, y, z) _atoi64(x)
120#else
121#define __func__ __FUNCTION__
122#define strtoull(x, y, z) _strtoui64(x, y, z)
123#define strtoll(x, y, z) _strtoi64(x, y, z)
124#endif // _MSC_VER
125
126#define ERRNO GetLastError()
127#define NO_SOCKLEN_T
128#define SSL_LIB "ssleay32.dll"
129#define CRYPTO_LIB "libeay32.dll"
130#define O_NONBLOCK 0
131#if !defined(EWOULDBLOCK)
132#define EWOULDBLOCK WSAEWOULDBLOCK
133#endif // !EWOULDBLOCK
134#define _POSIX_
135#define INT64_FMT "I64d"
136
137#define WINCDECL __cdecl
138#define SHUT_WR 1
139#define snprintf _snprintf
140#define vsnprintf _vsnprintf
141#define mg_sleep(x) Sleep(x)
142
143#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
144#ifndef popen
145#define popen(x, y) _popen(x, y)
146#endif
147#ifndef pclose
148#define pclose(x) _pclose(x)
149#endif
150#define close(x) _close(x)
151#define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y))
152#define RTLD_LAZY 0
153#define fseeko(x, y, z) _lseeki64(_fileno(x), (y), (z))
154#define fdopen(x, y) _fdopen((x), (y))
155#define write(x, y, z) _write((x), (y), (unsigned) z)
156#define read(x, y, z) _read((x), (y), (unsigned) z)
157#define flockfile(x)
158#define funlockfile(x)
159#define sleep(x) Sleep((x) * 1000)
160#define rmdir(x) _rmdir(x)
161
162#if !defined(va_copy)
163#define va_copy(x, y) x = y
164#endif // !va_copy MINGW #defines va_copy
165
166#if !defined(fileno)
167#define fileno(x) _fileno(x)
168#endif // !fileno MINGW #defines fileno
169
170typedef HANDLE pthread_mutex_t;
171typedef struct {HANDLE signal, broadcast;} pthread_cond_t;
172typedef DWORD pthread_t;
173#define pid_t HANDLE // MINGW typedefs pid_t to int. Using #define here.
174
177static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len);
178
179#if defined(HAVE_STDINT)
180#include <stdint.h>
181#else
182typedef unsigned int uint32_t;
183typedef unsigned short uint16_t;
184typedef unsigned __int64 uint64_t;
185typedef __int64 int64_t;
186#define INT64_MAX 9223372036854775807
187#endif // HAVE_STDINT
188
189// POSIX dirent interface
190struct dirent {
191 char d_name[PATH_MAX];
192};
193
194typedef struct DIR {
195 HANDLE handle;
197 struct dirent result;
198} DIR;
199
200#ifndef HAVE_POLL
201struct pollfd {
202 SOCKET fd;
203 short events;
204 short revents;
205};
206#define POLLIN 1
207#endif
208
209
210// Mark required libraries
211#ifdef _MSC_VER
212#pragma comment(lib, "Ws2_32.lib")
213#endif
214
215#else // UNIX specific
216#include <sys/wait.h>
217#include <sys/socket.h>
218#include <sys/poll.h>
219#include <netinet/in.h>
220#include <arpa/inet.h>
221#include <sys/time.h>
222#include <stdint.h>
223#include <inttypes.h>
224#include <netdb.h>
225
226#include <pwd.h>
227#include <unistd.h>
228#include <dirent.h>
229#if !defined(NO_SSL_DL) && !defined(NO_SSL)
230#include <dlfcn.h>
231#endif
232#include <pthread.h>
233#if defined(__MACH__)
234#define SSL_LIB "libssl.dylib"
235#define CRYPTO_LIB "libcrypto.dylib"
236#else
237#if !defined(SSL_LIB)
238#define SSL_LIB "libssl.so"
239#endif
240#if !defined(CRYPTO_LIB)
241#define CRYPTO_LIB "libcrypto.so"
242#endif
243#endif
244#ifndef O_BINARY
245#define O_BINARY 0
246#endif // O_BINARY
247#define closesocket(a) close(a)
248#define mg_mkdir(x, y) mkdir(x, y)
249#define mg_remove(x) remove(x)
250#define mg_sleep(x) usleep((x) * 1000)
251#define ERRNO errno
252#define INVALID_SOCKET (-1)
253#define INT64_FMT PRId64
254typedef int SOCKET;
255#define WINCDECL
256
257#endif // End of Windows and UNIX specific includes
258
259#include "mongoose4.h"
260
261#define MONGOOSE_VERSION "4.2"
262#define PASSWORDS_FILE_NAME ".htpasswd"
263#define CGI_ENVIRONMENT_SIZE 4096
264#define MAX_CGI_ENVIR_VARS 64
265#define MG_BUF_LEN 8192
266#define MAX_REQUEST_SIZE 16384
267#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
268
269#ifdef DEBUG_TRACE
270#undef DEBUG_TRACE
271#define DEBUG_TRACE(x)
272#else
273#if defined(DEBUG)
274#define DEBUG_TRACE(x) do { \
275 flockfile(stdout); \
276 printf("*** %lu.%p.%s.%d: ", \
277 (unsigned long) time(NULL), (void *) pthread_self(), \
278 __func__, __LINE__); \
279 printf x; \
280 putchar('\n'); \
281 fflush(stdout); \
282 funlockfile(stdout); \
283} while (0)
284#else
285#define DEBUG_TRACE(x)
286#endif // DEBUG
287#endif // DEBUG_TRACE
288
289// Darwin prior to 7.0 and Win32 do not have socklen_t
290#ifdef NO_SOCKLEN_T
291typedef int socklen_t;
292#endif // NO_SOCKLEN_T
293#define _DARWIN_UNLIMITED_SELECT
294
295#define IP_ADDR_STR_LEN 50 // IPv6 hex string is 46 chars
296
297#if !defined(MSG_NOSIGNAL)
298#define MSG_NOSIGNAL 0
299#endif
300
301#if !defined(SOMAXCONN)
302#define SOMAXCONN 100
303#endif
304
305#if !defined(PATH_MAX)
306#define PATH_MAX 4096
307#endif
308
309// Size of the accepted socket queue
310#if !defined(MGSQLEN)
311#define MGSQLEN 20
312#endif
313
314// Extra HTTP headers to send in every static file reply
315#if !defined(EXTRA_HTTP_HEADERS)
316#define EXTRA_HTTP_HEADERS ""
317#endif
318
319static const char *http_500_error = "Internal Server Error";
320
321#if defined(NO_SSL_DL)
322#include <openssl/ssl.h>
323#include <openssl/err.h>
324#else
325// SSL loaded dynamically from DLL.
326// I put the prototypes here to be independent from OpenSSL source installation.
327typedef struct ssl_st SSL;
328typedef struct ssl_method_st SSL_METHOD;
329typedef struct ssl_ctx_st SSL_CTX;
330
331/* As server, disallow session resumption on renegotiation */
332#define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION 0x00010000L
333
334/* If set, always create a new key when using tmp_ecdh parameters */
335#define SSL_OP_SINGLE_ECDH_USE 0x00080000L
336/* If set, always create a new key when using tmp_dh parameters */
337#define SSL_OP_SINGLE_DH_USE 0x00100000L
338
339#define SSL_OP_NO_SSLv2 0x01000000L
340#define SSL_OP_NO_SSLv3 0x02000000L
341#define SSL_OP_NO_TLSv1 0x04000000L
342#define SSL_OP_NO_TLSv1_2 0x08000000L
343#define SSL_OP_NO_TLSv1_1 0x10000000L
344
345//#include <openssl/ec.h>
346typedef void* EC_KEY;
347#define NID_X9_62_prime256v1 415
348
349#define SSL_CTRL_SET_TMP_RSA 2
350#define SSL_CTRL_SET_TMP_DH 3
351#define SSL_CTRL_SET_TMP_ECDH 4
352#define SSL_CTRL_SET_TMP_RSA_CB 5
353#define SSL_CTRL_SET_TMP_DH_CB 6
354#define SSL_CTRL_SET_TMP_ECDH_CB 7
355
356#define SSL_CTRL_OPTIONS 32
357
358#define SSL_CTX_set_options(ctx,op) SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL)
359#define SSL_CTX_clear_options(ctx,op) SSL_CTX_ctrl((ctx),SSL_CTRL_CLEAR_OPTIONS,(op),NULL)
360#define SSL_CTX_get_options(ctx) SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,0,NULL)
361
362#define SSL_CTX_set_tmp_dh(ctx,dh) SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TMP_DH,0,(char *)dh)
363#define SSL_CTX_set_tmp_ecdh(ctx,ecdh) SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TMP_ECDH,0,(char *)ecdh)
364
365struct ssl_func {
366 const char *name; // SSL function name
367 void (*ptr)(void); // Function pointer
368};
369
370#define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
371#define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
372#define SSL_connect (* (int (*)(SSL *)) ssl_sw[2].ptr)
373#define SSL_read (* (int (*)(SSL *, void *, int)) ssl_sw[3].ptr)
374#define SSL_write (* (int (*)(SSL *, const void *,int)) ssl_sw[4].ptr)
375#define SSL_get_error (* (int (*)(SSL *, int)) ssl_sw[5].ptr)
376#define SSL_set_fd (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr)
377#define SSL_new (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)
378#define SSL_CTX_new (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)
379#define SSLv23_server_method (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr)
380#define SSL_library_init (* (int (*)(void)) ssl_sw[10].ptr)
381#define SSL_CTX_use_PrivateKey_file (* (int (*)(SSL_CTX *, \
382 const char *, int)) ssl_sw[11].ptr)
383#define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \
384 const char *, int)) ssl_sw[12].ptr)
385#define SSL_CTX_set_default_passwd_cb \
386 (* (void (*)(SSL_CTX *, mg_event_handler_t)) ssl_sw[13].ptr)
387#define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)
388#define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr)
389#define SSL_CTX_use_certificate_chain_file \
390 (* (int (*)(SSL_CTX *, const char *)) ssl_sw[16].ptr)
391#define SSLv23_client_method (* (SSL_METHOD * (*)(void)) ssl_sw[17].ptr)
392#define SSL_pending (* (int (*)(SSL *)) ssl_sw[18].ptr)
393#define SSL_CTX_set_verify (* (void (*)(SSL_CTX *, int, int)) ssl_sw[19].ptr)
394#define SSL_shutdown (* (int (*)(SSL *)) ssl_sw[20].ptr)
395#define SSL_CTX_ctrl (* (int (*)(SSL_CTX *ctx, int cmd, long larg, void *parg)) ssl_sw[21].ptr)
396#define SSL_CTX_set_cipher_list (* (int (*)(SSL_CTX *ctx, const char *str)) ssl_sw[22].ptr)
397#define EC_KEY_new_by_curve_name (* (EC_KEY* (*)(int nid)) ssl_sw[23].ptr)
398#define EC_KEY_free (* (int (*)(EC_KEY *)) ssl_sw[24].ptr)
399#define SSL_get_cipher_list (* (const char* (*)(const SSL*, int)) ssl_sw[25].ptr)
400
401#define CRYPTO_num_locks (* (int (*)(void)) crypto_sw[0].ptr)
402#define CRYPTO_set_locking_callback \
403 (* (void (*)(void (*)(int, int, const char *, int))) crypto_sw[1].ptr)
404#define CRYPTO_set_id_callback \
405 (* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr)
406#define ERR_get_error (* (unsigned long (*)(void)) crypto_sw[3].ptr)
407#define ERR_error_string (* (char * (*)(unsigned long,char *)) crypto_sw[4].ptr)
408#endif // NO_SSL_DL
409
410// Unified socket address. For IPv6 support, add IPv6 address structure
411// in the union u.
412union usa {
413 struct sockaddr sa;
415#if defined(USE_IPV6)
416 struct sockaddr_in6 sin6;
417#endif
418};
419
420// Describes a string (chunk of memory).
421struct vec {
422 const char *ptr;
423 size_t len;
424};
425
426struct file {
430 // set to 1 if the content is gzipped
431 // in which case we need a content-encoding: gzip header
433};
434#define STRUCT_FILE_INITIALIZER { 0, 0, 0, 0 }
435
436// Describes listening socket, or socket which was accept()-ed by the master
437// thread and queued for future handling by the worker thread.
438struct socket {
439 SOCKET sock; // Listening socket
440 union usa lsa; // Local socket address
441 union usa rsa; // Remote socket address
442 unsigned is_ssl:1; // Is port SSL-ed
443 unsigned ssl_redir:1; // Is port supposed to redirect everything to SSL port
444};
445
446// NOTE(lsm): this enum shoulds be in sync with the config_options.
447enum {
456
458 volatile int stop_flag; // Should we stop event loop
459 SSL_CTX *ssl_ctx; // SSL context
460 char *config[NUM_OPTIONS]; // Mongoose configuration parameters
461 mg_event_handler_t event_handler; // User-defined callback function
462 void *user_data; // User-defined data
463
466
467 volatile int num_threads; // Number of threads
468 pthread_mutex_t mutex; // Protects (max|num)_threads
469 pthread_cond_t cond; // Condvar for tracking workers terminations
470
471 struct socket queue[MGSQLEN]; // Accepted sockets
472 volatile int sq_head; // Head of the socket queue
473 volatile int sq_tail; // Tail of the socket queue
474 pthread_cond_t sq_full; // Signaled when socket is produced
475 pthread_cond_t sq_empty; // Signaled when socket is consumed
476};
477
478struct mg_connection {
482 SSL *ssl; // SSL descriptor
483 SSL_CTX *client_ssl_ctx; // SSL context for client connections
484 struct socket client; // Connected client
485 time_t birth_time; // Time when request was received
486 int64_t num_bytes_sent; // Total bytes sent to client
487 int64_t content_len; // Content-Length header value
488 int64_t num_bytes_read; // Bytes read from a remote socket
489 char *buf; // Buffer for received data
490 char *path_info; // PATH_INFO part of the URL
491 int must_close; // 1 if connection must be closed
492 int buf_size; // Buffer size
493 int request_len; // Size of the request + headers in a buffer
494 int data_len; // Total size of data in a buffer
495 int status_code; // HTTP reply status code, e.g. 200
496 int throttle; // Throttling, bytes/sec. <= 0 means no throttle
497 time_t last_throttle_time; // Last time throttled data was sent
498 int64_t last_throttle_bytes;// Bytes sent this second
499};
500
501// Directory entry
502struct de {
505 struct file file;
506};
507
508static FILE *mg_fopen(const char *path, const char *mode);
509static int mg_stat(const char *path, struct file *filep);
510static void send_http_error(struct mg_connection *, int, const char *,
511 PRINTF_FORMAT_STRING(const char *fmt), ...)
512 PRINTF_ARGS(4, 5);
514 PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
515static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len);
516
517#ifdef USE_LUA
518#include "lua_5.2.1.h"
519static int handle_lsp_request(struct mg_connection *, const char *,
520 struct file *, struct lua_State *);
521#endif
522
523// Return fake connection structure. Used for logging, if connection
524// is not applicable at the moment of logging.
525static struct mg_connection *fc(struct mg_context *ctx) {
526 static struct mg_connection fake_connection;
528 // See https://github.com/cesanta/mongoose/issues/236
530 return &fake_connection;
531}
532
533static void mg_strlcpy(register char *dst, register const char *src, size_t n) {
534 for (; *src != '\0' && n > 1; n--) {
535 *dst++ = *src++;
536 }
537 *dst = '\0';
538}
539
540static int lowercase(const char *s) {
541 return tolower(* (const unsigned char *) s);
542}
543
544static int mg_strncasecmp(const char *s1, const char *s2, size_t len) {
545 int diff = 0;
546
547 if (len > 0)
548 do {
549 diff = lowercase(s1++) - lowercase(s2++);
550 } while (diff == 0 && s1[-1] != '\0' && --len > 0);
551
552 return diff;
553}
554
555static int mg_strcasecmp(const char *s1, const char *s2) {
556 int diff;
557
558 do {
559 diff = lowercase(s1++) - lowercase(s2++);
560 } while (diff == 0 && s1[-1] != '\0');
561
562 return diff;
563}
564
565static char * mg_strndup(const char *ptr, size_t len) {
566 char *p;
567
568 if ((p = (char *) malloc(len + 1)) != NULL) {
569 mg_strlcpy(p, ptr, len + 1);
570 }
571
572 return p;
573}
574
575static char * mg_strdup(const char *str) {
576 return mg_strndup(str, strlen(str));
577}
578
579static const char *mg_strcasestr(const char *big_str, const char *small_str) {
581
582 for (i = 0; i <= big_len - small_len; i++) {
584 return big_str + i;
585 }
586 }
587
588 return NULL;
589}
590
591// Like snprintf(), but never returns negative value, or a value
592// that is larger than a supplied buffer.
593// Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability
594// in his audit report.
595static int mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap) {
596 int n;
597
598 if (buflen == 0) {
599 return 0;
600 }
601
602 n = vsnprintf(buf, buflen, fmt, ap);
603
604 if (n < 0) {
605 n = 0;
606 } else if (n >= (int) buflen) {
607 n = (int) buflen - 1;
608 }
609 buf[n] = '\0';
610
611 return n;
612}
613
614static int mg_snprintf(char *buf, size_t buflen,
615 PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(3, 4);
616
617static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) {
618 va_list ap;
619 int n;
620
621 va_start(ap, fmt);
622 n = mg_vsnprintf(buf, buflen, fmt, ap);
623 va_end(ap);
624
625 return n;
626}
627
628// Skip the characters until one of the delimiters characters found.
629// 0-terminate resulting word. Skip the delimiter and following whitespaces.
630// Advance pointer to buffer to the next word. Return found 0-terminated word.
631// Delimiters can be quoted with quotechar.
632static char *skip_quoted(char **buf, const char *delimiters,
633 const char *whitespace, char quotechar) {
634 char *p, *begin_word, *end_word, *end_whitespace;
635
636 begin_word = *buf;
638
639 // Check for quotechar
640 if (end_word > begin_word) {
641 p = end_word - 1;
642 while (*p == quotechar) {
643 // If there is anything beyond end_word, copy it
644 if (*end_word == '\0') {
645 *p = '\0';
646 break;
647 } else {
648 size_t end_off = strcspn(end_word + 1, delimiters);
649 memmove (p, end_word, end_off + 1);
650 p += end_off; // p must correspond to end_word - 1
651 end_word += end_off + 1;
652 }
653 }
654 for (p++; p < end_word; p++) {
655 *p = '\0';
656 }
657 }
658
659 if (*end_word == '\0') {
660 *buf = end_word;
661 } else {
663
664 for (p = end_word; p < end_whitespace; p++) {
665 *p = '\0';
666 }
667
669 }
670
671 return begin_word;
672}
673
674// Simplified version of skip_quoted without quote char
675// and whitespace == delimiters
676static char *skip(char **buf, const char *delimiters) {
678}
679
680
681// Return HTTP header value, or NULL if not found.
682static const char *get_header(const struct mg_request_info *ri,
683 const char *name) {
684 int i;
685
686 for (i = 0; i < ri->num_headers; i++)
687 if (!mg_strcasecmp(name, ri->http_headers[i].name))
688 return ri->http_headers[i].value;
689
690 return NULL;
691}
692
693const char *mg_get_header(const struct mg_connection *conn, const char *name) {
694 return get_header(&conn->request_info, name);
695}
696
697// A helper function for traversing a comma separated list of values.
698// It returns a list pointer shifted to the next value, or NULL if the end
699// of the list found.
700// Value is stored in val vector. If value has form "x=y", then eq_val
701// vector is initialized to point to the "y" part, and val vector length
702// is adjusted to point only to "x".
703static const char *next_option(const char *list, struct vec *val,
704 struct vec *eq_val) {
705 if (list == NULL || *list == '\0') {
706 // End of the list
707 list = NULL;
708 } else {
709 val->ptr = list;
710 if ((list = strchr(val->ptr, ',')) != NULL) {
711 // Comma found. Store length and shift the list ptr
712 val->len = list - val->ptr;
713 list++;
714 } else {
715 // This value is the last one
716 list = val->ptr + strlen(val->ptr);
717 val->len = list - val->ptr;
718 }
719
720 if (eq_val != NULL) {
721 // Value has form "x=y", adjust pointers and lengths
722 // so that val points to "x", and eq_val points to "y".
723 eq_val->len = 0;
724 eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len);
725 if (eq_val->ptr != NULL) {
726 eq_val->ptr++; // Skip over '=' character
727 eq_val->len = val->ptr + val->len - eq_val->ptr;
728 val->len = (eq_val->ptr - val->ptr) - 1;
729 }
730 }
731 }
732
733 return list;
734}
735
736// Perform case-insensitive match of string against pattern
737static int match_prefix(const char *pattern, int pattern_len, const char *str) {
738 const char *or_str;
739 int i, j, len, res;
740
741 if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) {
742 res = match_prefix(pattern, or_str - pattern, str);
743 return res > 0 ? res :
744 match_prefix(or_str + 1, (pattern + pattern_len) - (or_str + 1), str);
745 }
746
747 i = j = 0;
748 res = -1;
749 for (; i < pattern_len; i++, j++) {
750 if (pattern[i] == '?' && str[j] != '\0') {
751 continue;
752 } else if (pattern[i] == '$') {
753 return str[j] == '\0' ? j : -1;
754 } else if (pattern[i] == '*') {
755 i++;
756 if (pattern[i] == '*') {
757 i++;
758 len = (int) strlen(str + j);
759 } else {
760 len = (int) strcspn(str + j, "/");
761 }
762 if (i == pattern_len) {
763 return j + len;
764 }
765 do {
766 res = match_prefix(pattern + i, pattern_len - i, str + j + len);
767 } while (res == -1 && len-- > 0);
768 return res == -1 ? -1 : j + res + len;
769 } else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
770 return -1;
771 }
772 }
773 return j;
774}
775
776
777static const char *month_names[] = {
778 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
779 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
780};
781
782// Convert month to the month number. Return -1 on error, or month number
783static int get_month_index(const char *s) {
784 int i;
785
786 for (i = 0; i < (int) ARRAY_SIZE(month_names); i++)
787 if (!strcmp(s, month_names[i]))
788 return i;
789
790 return -1;
791}
792
793static int num_leap_years(int year) {
794 return year / 4 - year / 100 + year / 400;
795}
796
797// Parse UTC date-time string, and return the corresponding time_t value.
798static time_t parse_date_string(const char *datetime) {
799 static const unsigned short days_before_month[] = {
800 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
801 };
802 char month_str[32];
804 time_t result = (time_t) 0;
805
806 if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d",
807 &day, month_str, &year, &hour, &minute, &second) == 6) ||
808 (sscanf(datetime, "%d %3s %d %d:%d:%d",
809 &day, month_str, &year, &hour, &minute, &second) == 6) ||
810 (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d",
811 &day, month_str, &year, &hour, &minute, &second) == 6) ||
812 (sscanf(datetime, "%d-%3s-%d %d:%d:%d",
813 &day, month_str, &year, &hour, &minute, &second) == 6)) &&
814 year > 1970 &&
815 (month = get_month_index(month_str)) != -1) {
817 year -= 1970;
818 days = year * 365 + days_before_month[month] + (day - 1) + leap_days;
819 result = days * 24 * 3600 + hour * 3600 + minute * 60 + second;
820 }
821
822 return result;
823}
824
825// This array must be in sync with enum in internal.h
826static const char *config_options[] = {
827 "cgi_pattern", "**.cgi$|**.pl$|**.php$",
828 "cgi_environment", NULL,
829 "put_delete_auth_file", NULL,
830 "cgi_interpreter", NULL,
831 "protect_uri", NULL,
832 "authentication_domain", "mydomain.com",
833 "ssi_pattern", "**.shtml$|**.shtm$",
834 "throttle", NULL,
835 "access_log_file", NULL,
836 "enable_directory_listing", "yes",
837 "error_log_file", NULL,
838 "global_auth_file", NULL,
839 "index_files",
840 "index.html,index.htm,index.cgi,index.shtml,index.php,index.lp",
841 "enable_keep_alive", "no",
842 "access_control_list", NULL,
843 "extra_mime_types", NULL,
844 "listening_ports", "8080",
845 "document_root", NULL,
846 "ssl_certificate", NULL,
847 "num_threads", "50",
848 "run_as_user", NULL,
849 "url_rewrite_patterns", NULL,
850 "hide_files_patterns", NULL,
851 "request_timeout_ms", "30000",
852 NULL
853};
854
855const char **mg_get_valid_option_names(void) {
856 return config_options;
857}
858
859static int get_option_index(const char *name) {
860 int i;
861
862 for (i = 0; config_options[i * 2] != NULL; i++) {
863 if (strcmp(config_options[i * 2], name) == 0) {
864 return i;
865 }
866 }
867 return -1;
868}
869
870const char *mg_get_option(const struct mg_context *ctx, const char *name) {
871 int i;
872 if ((i = get_option_index(name)) == -1) {
873 return NULL;
874 } else if (ctx->config[i] == NULL) {
875 return "";
876 } else {
877 return ctx->config[i];
878 }
879}
880static int is_big_endian(void) {
881 static const int n = 1;
882 return ((char *) &n)[0] == 0;
883}
884
885#ifndef HAVE_MD5
886typedef struct MD5Context {
887 uint32_t buf[4];
888 uint32_t bits[2];
889 unsigned char in[64];
891
892static void byteReverse(unsigned char *buf, unsigned longs) {
893 uint32_t t;
894
895 // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
896 if (is_big_endian()) {
897 do {
898 t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
899 ((unsigned) buf[1] << 8 | buf[0]);
900 * (uint32_t *) buf = t;
901 buf += 4;
902 } while (--longs);
903 }
904}
905
906#define F1(x, y, z) (z ^ (x & (y ^ z)))
907#define F2(x, y, z) F1(z, x, y)
908#define F3(x, y, z) (x ^ y ^ z)
909#define F4(x, y, z) (y ^ (x | ~z))
910
911#define MD5STEP(f, w, x, y, z, data, s) \
912 ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
913
914// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
915// initialization constants.
916static void MD5Init(MD5_CTX *ctx) {
917 ctx->buf[0] = 0x67452301;
918 ctx->buf[1] = 0xefcdab89;
919 ctx->buf[2] = 0x98badcfe;
920 ctx->buf[3] = 0x10325476;
921
922 ctx->bits[0] = 0;
923 ctx->bits[1] = 0;
924}
925
926static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
927 register uint32_t a, b, c, d;
928
929 a = buf[0];
930 b = buf[1];
931 c = buf[2];
932 d = buf[3];
933
934 MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
935 MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
936 MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
937 MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
938 MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
939 MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
940 MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
941 MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
942 MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
943 MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
944 MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
945 MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
946 MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
947 MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
948 MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
949 MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
950
951 MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
952 MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
953 MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
954 MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
955 MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
956 MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
957 MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
958 MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
959 MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
960 MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
961 MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
962 MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
963 MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
964 MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
965 MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
966 MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
967
968 MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
969 MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
970 MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
971 MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
972 MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
973 MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
974 MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
975 MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
976 MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
977 MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
978 MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
979 MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
980 MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
981 MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
982 MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
983 MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
984
985 MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
986 MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
987 MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
988 MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
989 MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
990 MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
991 MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
992 MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
993 MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
994 MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
995 MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
996 MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
997 MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
998 MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
999 MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
1000 MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
1001
1002 buf[0] += a;
1003 buf[1] += b;
1004 buf[2] += c;
1005 buf[3] += d;
1006}
1007
1008static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
1009 uint32_t t;
1010
1011 t = ctx->bits[0];
1012 if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
1013 ctx->bits[1]++;
1014 ctx->bits[1] += len >> 29;
1015
1016 t = (t >> 3) & 0x3f;
1017
1018 if (t) {
1019 unsigned char *p = (unsigned char *) ctx->in + t;
1020
1021 t = 64 - t;
1022 if (len < t) {
1023 memcpy(p, buf, len);
1024 return;
1025 }
1026 memcpy(p, buf, t);
1027 byteReverse(ctx->in, 16);
1028 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
1029 buf += t;
1030 len -= t;
1031 }
1032
1033 while (len >= 64) {
1034 memcpy(ctx->in, buf, 64);
1035 byteReverse(ctx->in, 16);
1036 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
1037 buf += 64;
1038 len -= 64;
1039 }
1040
1041 memcpy(ctx->in, buf, len);
1042}
1043
1044static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
1045 unsigned count;
1046 unsigned char *p;
1047 uint32_t *a;
1048
1049 count = (ctx->bits[0] >> 3) & 0x3F;
1050
1051 p = ctx->in + count;
1052 *p++ = 0x80;
1053 count = 64 - 1 - count;
1054 if (count < 8) {
1055 memset(p, 0, count);
1056 byteReverse(ctx->in, 16);
1057 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
1058 memset(ctx->in, 0, 56);
1059 } else {
1060 memset(p, 0, count - 8);
1061 }
1062 byteReverse(ctx->in, 14);
1063
1064 a = (uint32_t *)ctx->in;
1065 a[14] = ctx->bits[0];
1066 a[15] = ctx->bits[1];
1067
1068 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
1069 byteReverse((unsigned char *) ctx->buf, 4);
1070 memcpy(digest, ctx->buf, 16);
1071 memset((char *) ctx, 0, sizeof(*ctx));
1072}
1073#endif // !HAVE_MD5
1074
1075
1076
1077// Stringify binary data. Output buffer must be twice as big as input,
1078// because each byte takes 2 bytes in string representation
1079static void bin2str(char *to, const unsigned char *p, size_t len) {
1080 static const char *hex = "0123456789abcdef";
1081
1082 for (; len--; p++) {
1083 *to++ = hex[p[0] >> 4];
1084 *to++ = hex[p[0] & 0x0f];
1085 }
1086 *to = '\0';
1087}
1088
1089// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
1090char *mg_md5(char buf[33], ...) {
1091 unsigned char hash[16];
1092 const char *p;
1093 va_list ap;
1094 MD5_CTX ctx;
1095
1096 MD5Init(&ctx);
1097
1098 va_start(ap, buf);
1099 while ((p = va_arg(ap, const char *)) != NULL) {
1100 MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
1101 }
1102 va_end(ap);
1103
1104 MD5Final(hash, &ctx);
1105 bin2str(buf, hash, sizeof(hash));
1106 return buf;
1107}
1108
1109// Check the user's password, return 1 if OK
1110static int check_password(const char *method, const char *ha1, const char *uri,
1111 const char *nonce, const char *nc, const char *cnonce,
1112 const char *qop, const char *response) {
1113 char ha2[32 + 1], expected_response[32 + 1];
1114
1115 // Some of the parameters may be NULL
1116 if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL ||
1117 qop == NULL || response == NULL) {
1118 return 0;
1119 }
1120
1121 // NOTE(lsm): due to a bug in MSIE, we do not compare the URI
1122 // TODO(lsm): check for authentication timeout
1123 if (// strcmp(dig->uri, c->ouri) != 0 ||
1124 strlen(response) != 32
1125 // || now - strtoul(dig->nonce, NULL, 10) > 3600
1126 ) {
1127 return 0;
1128 }
1129
1130 mg_md5(ha2, method, ":", uri, NULL);
1131 mg_md5(expected_response, ha1, ":", nonce, ":", nc,
1132 ":", cnonce, ":", qop, ":", ha2, NULL);
1133
1135}
1136
1137// Use the global passwords file, if specified by auth_gpass option,
1138// or search for .htpasswd in the requested directory.
1139static FILE *open_auth_file(struct mg_connection *conn, const char *path) {
1140 char name[PATH_MAX];
1141 const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE];
1143 FILE *fp = NULL;
1144
1145 if (gpass != NULL) {
1146 // Use global passwords file
1147 fp = mg_fopen(gpass, "r");
1148 // Important: using local struct file to test path for is_directory flag.
1149 // If filep is used, mg_stat() makes it appear as if auth file was opened.
1150 } else if (mg_stat(path, &file) && file.is_directory) {
1151 mg_snprintf(name, sizeof(name), "%s%c%s",
1152 path, '/', PASSWORDS_FILE_NAME);
1153 fp = mg_fopen(name, "r");
1154 } else {
1155 // Try to find .htpasswd in requested directory.
1156 for (p = path, e = p + strlen(p) - 1; e > p; e--)
1157 if (e[0] == '/')
1158 break;
1159 mg_snprintf(name, sizeof(name), "%.*s%c%s",
1160 (int) (e - p), p, '/', PASSWORDS_FILE_NAME);
1161 fp = mg_fopen(name, "r");
1162 }
1163
1164 return fp;
1165}
1166
1167// Parsed Authorization header
1168struct ah {
1169 char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
1170};
1171
1172// Return 1 on success. Always initializes the ah structure.
1173static int parse_auth_header(struct mg_connection *conn, char *buf,
1174 size_t buf_size, struct ah *ah) {
1175 char *name, *value, *s;
1176 const char *auth_header;
1177
1178 (void) memset(ah, 0, sizeof(*ah));
1179 if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||
1180 mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
1181 return 0;
1182 }
1183
1184 // Make modifiable copy of the auth header
1185 (void) mg_strlcpy(buf, auth_header + 7, buf_size);
1186 s = buf;
1187
1188 // Parse authorization header
1189 for (;;) {
1190 // Gobble initial spaces
1191 while (isspace(* (unsigned char *) s)) {
1192 s++;
1193 }
1194 name = skip_quoted(&s, "=", " ", 0);
1195 // Value is either quote-delimited, or ends at first comma or space.
1196 if (s[0] == '\"') {
1197 s++;
1198 value = skip_quoted(&s, "\"", " ", '\\');
1199 if (s[0] == ',') {
1200 s++;
1201 }
1202 } else {
1203 value = skip_quoted(&s, ", ", " ", 0); // IE uses commas, FF uses spaces
1204 }
1205 if (*name == '\0') {
1206 break;
1207 }
1208
1209 if (!strcmp(name, "username")) {
1210 ah->user = value;
1211 } else if (!strcmp(name, "cnonce")) {
1212 ah->cnonce = value;
1213 } else if (!strcmp(name, "response")) {
1214 ah->response = value;
1215 } else if (!strcmp(name, "uri")) {
1216 ah->uri = value;
1217 } else if (!strcmp(name, "qop")) {
1218 ah->qop = value;
1219 } else if (!strcmp(name, "nc")) {
1220 ah->nc = value;
1221 } else if (!strcmp(name, "nonce")) {
1222 ah->nonce = value;
1223 }
1224 }
1225
1226 // CGI needs it as REMOTE_USER
1227 if (ah->user != NULL) {
1229 } else {
1230 return 0;
1231 }
1232
1233 return 1;
1234}
1235
1236// Authorize against the opened passwords file. Return 1 if authorized.
1237static int authorize(struct mg_connection *conn, FILE *fp) {
1238 struct ah ah;
1239 char line[256], f_user[256], ha1[256], f_domain[256], buf[MG_BUF_LEN];
1240
1241 if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) {
1242 return 0;
1243 }
1244
1245 // Loop over passwords file
1246 while (fgets(line, sizeof(line), fp) != NULL) {
1247 if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) {
1248 continue;
1249 }
1250
1251 if (!strcmp(ah.user, f_user) &&
1255 }
1256
1257 return 0;
1258}
1259
1260// Return 1 if request is authorised, 0 otherwise.
1261static int check_authorization(struct mg_connection *conn, const char *path) {
1262 char fname[PATH_MAX];
1263 struct vec uri_vec, filename_vec;
1264 const char *list;
1265 FILE *fp = NULL;
1266 int authorized = 1;
1267
1268 list = conn->ctx->config[PROTECT_URI];
1269 while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
1270 if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
1271 mg_snprintf(fname, sizeof(fname), "%.*s",
1272 (int) filename_vec.len, filename_vec.ptr);
1273 fp = mg_fopen(fname, "r");
1274 break;
1275 }
1276 }
1277
1278 if (fp == NULL) {
1279 fp = open_auth_file(conn, path);
1280 }
1281
1282 if (fp != NULL) {
1283 authorized = authorize(conn, fp);
1284 fclose(fp);
1285 }
1286
1287 return authorized;
1288}
1289
1291 conn->status_code = 401;
1292 mg_printf(conn,
1293 "HTTP/1.1 401 Unauthorized\r\n"
1294 "Content-Length: 0\r\n"
1295 "WWW-Authenticate: Digest qop=\"auth\", "
1296 "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
1298 (unsigned long) time(NULL));
1299}
1300
1301static int is_authorized_for_put(struct mg_connection *conn) {
1302 const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE];
1303 FILE *fp;
1304 int ret = 0;
1305
1306 if (passfile != NULL && (fp = mg_fopen(passfile, "r")) != NULL) {
1307 ret = authorize(conn, fp);
1308 fclose(fp);
1309 }
1310
1311 return ret;
1312}
1313
1314int mg_modify_passwords_file(const char *fname, const char *domain,
1315 const char *user, const char *pass) {
1316 int found;
1317 char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
1318 FILE *fp, *fp2;
1319
1320 found = 0;
1321 fp = fp2 = NULL;
1322
1323 // Regard empty password as no password - remove user record.
1324 if (pass != NULL && pass[0] == '\0') {
1325 pass = NULL;
1326 }
1327
1328 (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
1329
1330 // Create the file if does not exist
1331 if ((fp = fopen(fname, "a+")) != NULL) {
1332 fclose(fp);
1333 }
1334
1335 // Open the given file and temporary file
1336 if ((fp = fopen(fname, "r")) == NULL) {
1337 return 0;
1338 } else if ((fp2 = fopen(tmp, "w+")) == NULL) {
1339 fclose(fp);
1340 return 0;
1341 }
1342
1343 // Copy the stuff to temporary file
1344 while (fgets(line, sizeof(line), fp) != NULL) {
1345 if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
1346 continue;
1347 }
1348
1349 if (!strcmp(u, user) && !strcmp(d, domain)) {
1350 found++;
1351 if (pass != NULL) {
1352 mg_md5(ha1, user, ":", domain, ":", pass, NULL);
1353 fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
1354 }
1355 } else {
1356 fprintf(fp2, "%s", line);
1357 }
1358 }
1359
1360 // If new user, just add it
1361 if (!found && pass != NULL) {
1362 mg_md5(ha1, user, ":", domain, ":", pass, NULL);
1363 fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
1364 }
1365
1366 // Close files
1367 fclose(fp);
1368 fclose(fp2);
1369
1370 // Put the temp file in place of real file
1371 remove(fname);
1372 rename(tmp, fname);
1373
1374 return 1;
1375}
1376
1377#if defined(_WIN32)
1378static pthread_t pthread_self(void) {
1379 return GetCurrentThreadId();
1380}
1381
1382static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) {
1383 (void) unused;
1384 *mutex = CreateMutex(NULL, FALSE, NULL);
1385 return *mutex == NULL ? -1 : 0;
1386}
1387
1388static int pthread_mutex_destroy(pthread_mutex_t *mutex) {
1389 return CloseHandle(*mutex) == 0 ? -1 : 0;
1390}
1391
1392static int pthread_mutex_lock(pthread_mutex_t *mutex) {
1393 return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
1394}
1395
1396static int pthread_mutex_unlock(pthread_mutex_t *mutex) {
1397 return ReleaseMutex(*mutex) == 0 ? -1 : 0;
1398}
1399
1400static int pthread_cond_init(pthread_cond_t *cv, const void *unused) {
1401 (void) unused;
1402 cv->signal = CreateEvent(NULL, FALSE, FALSE, NULL);
1403 cv->broadcast = CreateEvent(NULL, TRUE, FALSE, NULL);
1404 return cv->signal != NULL && cv->broadcast != NULL ? 0 : -1;
1405}
1406
1408 HANDLE handles[] = {cv->signal, cv->broadcast};
1409 ReleaseMutex(*mutex);
1411 return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
1412}
1413
1415 return SetEvent(cv->signal) == 0 ? -1 : 0;
1416}
1417
1419 // Implementation with PulseEvent() has race condition, see
1420 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
1421 return PulseEvent(cv->broadcast) == 0 ? -1 : 0;
1422}
1423
1425 return CloseHandle(cv->signal) && CloseHandle(cv->broadcast) ? 0 : -1;
1426}
1427
1428// For Windows, change all slashes to backslashes in path names.
1429static void change_slashes_to_backslashes(char *path) {
1430 int i;
1431
1432 for (i = 0; path[i] != '\0'; i++) {
1433 if (path[i] == '/')
1434 path[i] = '\\';
1435 // i > 0 check is to preserve UNC paths, like \\server\file.txt
1436 if (path[i] == '\\' && i > 0)
1437 while (path[i + 1] == '\\' || path[i + 1] == '/')
1438 (void) memmove(path + i + 1,
1439 path + i + 2, strlen(path + i + 1));
1440 }
1441}
1442
1443// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
1444// wbuf and wbuf_len is a target buffer and its length.
1445static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) {
1446 char buf[PATH_MAX * 2], buf2[PATH_MAX * 2];
1447
1448 mg_strlcpy(buf, path, sizeof(buf));
1450
1451 // Convert to Unicode and back. If doubly-converted string does not
1452 // match the original, something is fishy, reject.
1453 memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
1454 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
1455 WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
1456 NULL, NULL);
1457 if (strcmp(buf, buf2) != 0) {
1458 wbuf[0] = L'\0';
1459 }
1460}
1461
1462#if defined(_WIN32_WCE)
1463static time_t time(time_t *ptime) {
1464 time_t t;
1465 SYSTEMTIME st;
1466 FILETIME ft;
1467
1468 GetSystemTime(&st);
1470 t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
1471
1472 if (ptime != NULL) {
1473 *ptime = t;
1474 }
1475
1476 return t;
1477}
1478
1479static struct tm *localtime(const time_t *ptime, struct tm *ptm) {
1481 FILETIME ft, lft;
1482 SYSTEMTIME st;
1484
1485 if (ptm == NULL) {
1486 return NULL;
1487 }
1488
1489 * (int64_t *) &ft = t;
1492 ptm->tm_year = st.wYear - 1900;
1493 ptm->tm_mon = st.wMonth - 1;
1494 ptm->tm_wday = st.wDayOfWeek;
1495 ptm->tm_mday = st.wDay;
1496 ptm->tm_hour = st.wHour;
1497 ptm->tm_min = st.wMinute;
1498 ptm->tm_sec = st.wSecond;
1499 ptm->tm_yday = 0; // hope nobody uses this
1500 ptm->tm_isdst =
1502
1503 return ptm;
1504}
1505
1506static struct tm *gmtime(const time_t *ptime, struct tm *ptm) {
1507 // FIXME(lsm): fix this.
1508 return localtime(ptime, ptm);
1509}
1510
1511static size_t strftime(char *dst, size_t dst_size, const char *fmt,
1512 const struct tm *tm) {
1513 (void) snprintf(dst, dst_size, "implement strftime() for WinCE");
1514 return 0;
1515}
1516#endif
1517
1518// Windows happily opens files with some garbage at the end of file name.
1519// For example, fopen("a.cgi ", "r") on Windows successfully opens
1520// "a.cgi", despite one would expect an error back.
1521// This function returns non-0 if path ends with some garbage.
1522static int path_cannot_disclose_cgi(const char *path) {
1523 static const char *allowed_last_characters = "_-";
1524 int last = path[strlen(path) - 1];
1526}
1527
1528static int mg_stat(const char *path, struct file *filep) {
1529 wchar_t wbuf[PATH_MAX] = L"\\\\?\\";
1531
1532 filep->modification_time = 0;
1533 to_unicode(path, wbuf + 4, ARRAY_SIZE(wbuf) - 4);
1535 filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
1536 filep->modification_time = SYS2UNIX_TIME(
1537 info.ftLastWriteTime.dwLowDateTime,
1538 info.ftLastWriteTime.dwHighDateTime);
1539 filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
1540 // If file name is fishy, reset the file structure and return error.
1541 // Note it is important to reset, not just return the error, cause
1542 // functions like is_file_opened() check the struct.
1543 if (!filep->is_directory && !path_cannot_disclose_cgi(path)) {
1544 memset(filep, 0, sizeof(*filep));
1545 }
1546 }
1547
1548 return filep->modification_time != 0;
1549}
1550
1551static int mg_remove(const char *path) {
1552 wchar_t wbuf[PATH_MAX];
1553 to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
1554 return DeleteFileW(wbuf) ? 0 : -1;
1555}
1556
1557static int mg_mkdir(const char *path, int mode) {
1558 char buf[PATH_MAX];
1559 wchar_t wbuf[PATH_MAX];
1560
1561 (void) mode;
1562 mg_strlcpy(buf, path, sizeof(buf));
1564
1566
1567 return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
1568}
1569
1570// Implementation of POSIX opendir/closedir/readdir for Windows.
1571static DIR * opendir(const char *name) {
1572 DIR *dir = NULL;
1573 wchar_t wpath[PATH_MAX];
1574 DWORD attrs;
1575
1576 if (name == NULL) {
1578 } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
1580 } else {
1583 if (attrs != 0xFFFFFFFF &&
1585 (void) wcscat(wpath, L"\\*");
1586 dir->handle = FindFirstFileW(wpath, &dir->info);
1587 dir->result.d_name[0] = '\0';
1588 } else {
1589 free(dir);
1590 dir = NULL;
1591 }
1592 }
1593
1594 return dir;
1595}
1596
1597static int closedir(DIR *dir) {
1598 int result = 0;
1599
1600 if (dir != NULL) {
1601 if (dir->handle != INVALID_HANDLE_VALUE)
1602 result = FindClose(dir->handle) ? 0 : -1;
1603
1604 free(dir);
1605 } else {
1606 result = -1;
1608 }
1609
1610 return result;
1611}
1612
1613static struct dirent *readdir(DIR *dir) {
1614 struct dirent *result = 0;
1615
1616 if (dir) {
1617 if (dir->handle != INVALID_HANDLE_VALUE) {
1618 result = &dir->result;
1620 dir->info.cFileName, -1, result->d_name,
1621 sizeof(result->d_name), NULL, NULL);
1622
1623 if (!FindNextFileW(dir->handle, &dir->info)) {
1624 (void) FindClose(dir->handle);
1625 dir->handle = INVALID_HANDLE_VALUE;
1626 }
1627
1628 } else {
1630 }
1631 } else {
1633 }
1634
1635 return result;
1636}
1637
1638#ifndef HAVE_POLL
1639static int poll(struct pollfd *pfd, int n, int milliseconds) {
1640 struct timeval tv;
1641 fd_set set;
1642 int i, result;
1643 SOCKET maxfd = 0;
1644
1645 tv.tv_sec = milliseconds / 1000;
1646 tv.tv_usec = (milliseconds % 1000) * 1000;
1647 FD_ZERO(&set);
1648
1649 for (i = 0; i < n; i++) {
1650 FD_SET((SOCKET) pfd[i].fd, &set);
1651 pfd[i].revents = 0;
1652
1653 if (pfd[i].fd > maxfd) {
1654 maxfd = pfd[i].fd;
1655 }
1656 }
1657
1658 if ((result = select(maxfd + 1, &set, NULL, NULL, &tv)) > 0) {
1659 for (i = 0; i < n; i++) {
1660 if (FD_ISSET(pfd[i].fd, &set)) {
1661 pfd[i].revents = POLLIN;
1662 }
1663 }
1664 }
1665
1666 return result;
1667}
1668#endif // HAVE_POLL
1669
1670static void set_close_on_exec(SOCKET sock) {
1672}
1673
1674int mg_start_thread(mg_thread_func_t f, void *p) {
1675 return (long)_beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0;
1676}
1677
1678static HANDLE dlopen(const char *dll_name, int flags) {
1679 wchar_t wbuf[PATH_MAX];
1680 (void) flags;
1682 return LoadLibraryW(wbuf);
1683}
1684
1685#if !defined(NO_CGI)
1686#define SIGKILL 0
1687static int kill(pid_t pid, int sig_num) {
1689 (void) CloseHandle(pid);
1690 return 0;
1691}
1692
1693static void trim_trailing_whitespaces(char *s) {
1694 char *e = s + strlen(s) - 1;
1695 while (e > s && isspace(* (unsigned char *) e)) {
1696 *e-- = '\0';
1697 }
1698}
1699
1700static pid_t spawn_process(struct mg_connection *conn, const char *prog,
1701 char *envblk, char *envp[], int fdin,
1702 int fdout, const char *dir) {
1703 HANDLE me;
1705 cmdline[PATH_MAX], buf[PATH_MAX];
1706 FILE *fp;
1708 PROCESS_INFORMATION pi = { 0 };
1709
1710 (void) envp;
1711
1712 memset(&si, 0, sizeof(si));
1713 si.cb = sizeof(si);
1714
1715 // TODO(lsm): redirect CGI errors to the error log file
1717 si.wShowWindow = SW_HIDE;
1718
1721 &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
1723 &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
1724
1725 // If CGI file is a script, try to read the interpreter line
1726 interp = conn->ctx->config[CGI_INTERPRETER];
1727 if (interp == NULL) {
1728 buf[0] = buf[1] = '\0';
1729
1730 // Read the first line of the script into the buffer
1731 snprintf(cmdline, sizeof(cmdline), "%s%c%s", dir, '/', prog);
1732 if ((fp = mg_fopen(cmdline, "r")) != NULL) {
1733 fgets(buf, sizeof(buf), fp);
1734 fclose(fp);
1735 buf[sizeof(buf) - 1] = '\0';
1736 }
1737
1738 if (buf[0] == '#' && buf[1] == '!') {
1740 } else {
1741 buf[2] = '\0';
1742 }
1743 interp = buf + 2;
1744 }
1745
1746 if (interp[0] != '\0') {
1749 }
1750 GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL);
1751
1752 mg_snprintf(cmdline, sizeof(cmdline), "%s%s\"%s\\%s\"",
1753 interp, interp[0] == '\0' ? "" : " ", full_dir, prog);
1754
1755 DEBUG_TRACE(("Running [%s]", cmdline));
1758 cry(conn, "%s: CreateProcess(%s): %ld",
1760 pi.hProcess = (pid_t) -1;
1761 }
1762
1763 (void) CloseHandle(si.hStdOutput);
1764 (void) CloseHandle(si.hStdInput);
1765 (void) CloseHandle(pi.hThread);
1766
1767 return (pid_t) pi.hProcess;
1768}
1769#endif // !NO_CGI
1770
1771static int set_non_blocking_mode(SOCKET sock) {
1772 unsigned long on = 1;
1773 return ioctlsocket(sock, FIONBIO, &on);
1774}
1775#endif
1776
1777#if !defined(_WIN32)
1778static int mg_stat(const char *path, struct file *filep) {
1779 struct stat st;
1780
1781 filep->modification_time = (time_t) 0;
1782 if (stat(path, &st) == 0) {
1783 filep->size = st.st_size;
1784 filep->modification_time = st.st_mtime;
1785 filep->is_directory = S_ISDIR(st.st_mode);
1786
1787 // See https://github.com/cesanta/mongoose/issues/109
1788 // Some filesystems report modification time as 0. Artificially
1789 // bump it up to mark mg_stat() success.
1790 if (filep->modification_time == (time_t) 0) {
1791 filep->modification_time = (time_t) 1;
1792 }
1793 }
1794
1795 return filep->modification_time != (time_t) 0;
1796}
1797
1798static void set_close_on_exec(int fd) {
1799 fcntl(fd, F_SETFD, FD_CLOEXEC);
1800}
1801
1803 pthread_t thread_id;
1805 int result;
1806
1809
1810#if USE_STACK_SIZE > 1
1811 // Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384
1813#endif
1814
1815 result = pthread_create(&thread_id, &attr, func, param);
1817
1818 return result;
1819}
1820
1821#ifndef NO_CGI
1822static pid_t spawn_process(struct mg_connection *conn, const char *prog,
1823 char *envblk, char *envp[], int fdin,
1824 int fdout, const char *dir) {
1825 pid_t pid;
1826 const char *interp;
1827
1828 (void) envblk;
1829
1830 if ((pid = fork()) == -1) {
1831 // Parent
1832 send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO));
1833 } else if (pid == 0) {
1834 // Child
1835 if (chdir(dir) != 0) {
1836 cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
1837 } else if (dup2(fdin, 0) == -1) {
1838 cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, strerror(ERRNO));
1839 } else if (dup2(fdout, 1) == -1) {
1840 cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout, strerror(ERRNO));
1841 } else {
1842 // Not redirecting stderr to stdout, to avoid output being littered
1843 // with the error messages.
1844 (void) close(fdin);
1845 (void) close(fdout);
1846
1847 // After exec, all signal handlers are restored to their default values,
1848 // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
1849 // implementation, SIGCHLD's handler will leave unchanged after exec
1850 // if it was set to be ignored. Restore it to default action.
1852
1853 interp = conn->ctx->config[CGI_INTERPRETER];
1854 if (interp == NULL) {
1855 (void) execle(prog, prog, NULL, envp);
1856 cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
1857 } else {
1859 cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
1860 strerror(ERRNO));
1861 }
1862 }
1863 exit(EXIT_FAILURE);
1864 }
1865
1866 return pid;
1867}
1868#endif // !NO_CGI
1869
1871 int flags;
1872
1873 flags = fcntl(sock, F_GETFL, 0);
1874 (void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
1875
1876 return 0;
1877}
1878#endif // _WIN32
1879
1880
1881// Print message to buffer. If buffer is large enough to hold the message,
1882// return buffer. If buffer is to small, allocate large enough buffer on heap,
1883// and return allocated buffer.
1884static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) {
1886 int len;
1887
1888 // Windows is not standard-compliant, and vsnprintf() returns -1 if
1889 // buffer is too small. Also, older versions of msvcrt.dll do not have
1890 // _vscprintf(). However, if size is 0, vsnprintf() behaves correctly.
1891 // Therefore, we make two passes: on first pass, get required message length.
1892 // On second pass, actually print the message.
1893 va_copy(ap_copy, ap);
1894 len = vsnprintf(NULL, 0, fmt, ap_copy);
1895
1896 if (len > (int) size &&
1897 (size = len + 1) > 0 &&
1898 (*buf = (char *) malloc(size)) == NULL) {
1899 len = -1; // Allocation failed, mark failure
1900 } else {
1901 va_copy(ap_copy, ap);
1902 vsnprintf(*buf, size, fmt, ap_copy);
1903 }
1904
1905 return len;
1906}
1907
1908int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) {
1909 char mem[MG_BUF_LEN], *buf = mem;
1910 int len;
1911
1912 if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
1913 len = mg_write(conn, buf, (size_t) len);
1914 }
1915 if (buf != mem && buf != NULL) {
1916 free(buf);
1917 }
1918
1919 return len;
1920}
1921
1922int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
1923 va_list ap;
1924 va_start(ap, fmt);
1925 return mg_vprintf(conn, fmt, ap);
1926}
1927
1928static int mg_chunked_printf(struct mg_connection *conn, const char *fmt, ...) {
1929 char mem[MG_BUF_LEN], *buf = mem;
1930 int len;
1931
1932 va_list ap;
1933 va_start(ap, fmt);
1934 if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
1935 len = mg_printf(conn, "%X\r\n%s\r\n", len, buf);
1936 }
1937
1938 if (buf != mem && buf != NULL) {
1939 free(buf);
1940 }
1941
1942 return len;
1943}
1944
1945
1946
1947#if !defined(NO_SSL)
1948
1949#if !defined(NO_SSL_DL)
1950// set_ssl_option() function updates this array.
1951// It loads SSL library dynamically and changes NULLs to the actual addresses
1952// of respective functions. The macros above (like SSL_connect()) are really
1953// just calling these functions indirectly via the pointer.
1954static struct ssl_func ssl_sw[] = {
1955 {"SSL_free", NULL},
1956 {"SSL_accept", NULL},
1957 {"SSL_connect", NULL},
1958 {"SSL_read", NULL},
1959 {"SSL_write", NULL},
1960 {"SSL_get_error", NULL},
1961 {"SSL_set_fd", NULL},
1962 {"SSL_new", NULL},
1963 {"SSL_CTX_new", NULL},
1964 {"SSLv23_server_method", NULL},
1965 {"SSL_library_init", NULL},
1966 {"SSL_CTX_use_PrivateKey_file", NULL},
1967 {"SSL_CTX_use_certificate_file",NULL},
1968 {"SSL_CTX_set_default_passwd_cb",NULL},
1969 {"SSL_CTX_free", NULL},
1970 {"SSL_load_error_strings", NULL},
1971 {"SSL_CTX_use_certificate_chain_file", NULL},
1972 {"SSLv23_client_method", NULL},
1973 {"SSL_pending", NULL},
1974 {"SSL_CTX_set_verify", NULL},
1975 {"SSL_shutdown", NULL},
1976 {"SSL_CTX_ctrl", NULL},
1977 {"SSL_CTX_set_cipher_list", NULL},
1978 {"EC_KEY_new_by_curve_name", NULL},
1979 {"EC_KEY_free", NULL},
1980 {"SSL_get_cipher_list", NULL},
1981 {NULL, NULL}
1982};
1983
1984// Similar array as ssl_sw. These functions could be located in different lib.
1985static struct ssl_func crypto_sw[] = {
1986 {"CRYPTO_num_locks", NULL},
1987 {"CRYPTO_set_locking_callback", NULL},
1988 {"CRYPTO_set_id_callback", NULL},
1989 {"ERR_get_error", NULL},
1990 {"ERR_error_string", NULL},
1991 {NULL, NULL}
1992};
1993#endif
1994
1996
1997static int sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *)) {
1998 return (conn->ssl = SSL_new(s)) != NULL &&
1999 SSL_set_fd(conn->ssl, conn->client.sock) == 1 &&
2000 func(conn->ssl) == 1;
2001}
2002
2003// Return OpenSSL error message
2004static const char *ssl_error(void) {
2005 unsigned long err;
2006 err = ERR_get_error();
2007 return err == 0 ? "" : ERR_error_string(err, NULL);
2008}
2009
2010static void ssl_locking_callback(int mode, int mutex_num, const char *file,
2011 int line) {
2012 (void) line;
2013 (void) file;
2014
2015 if (mode & 1) { // 1 is CRYPTO_LOCK
2017 } else {
2019 }
2020}
2021
2022static unsigned long ssl_id_callback(void) {
2023 return (unsigned long) pthread_self();
2024}
2025
2026#if !defined(NO_SSL_DL)
2027static int load_dll(struct mg_context *ctx, const char *dll_name,
2028 struct ssl_func *sw) {
2029 union {void *p; void (*fp)(void);} u;
2030 void *dll_handle;
2031 struct ssl_func *fp;
2032
2033 if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
2034 cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
2035 return 0;
2036 }
2037
2038 for (fp = sw; fp->name != NULL; fp++) {
2039#ifdef _WIN32
2040 // GetProcAddress() returns pointer to function
2041 u.fp = (void (*)(void)) dlsym(dll_handle, fp->name);
2042#else
2043 // dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to
2044 // function pointers. We need to use a union to make a cast.
2045 u.p = dlsym(dll_handle, fp->name);
2046#endif // _WIN32
2047 if (u.fp == NULL) {
2048 cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name);
2049 return 0;
2050 } else {
2051 fp->ptr = u.fp;
2052 }
2053 }
2054
2055 return 1;
2056}
2057#endif // NO_SSL_DL
2058
2059// Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
2060static int set_ssl_option(struct mg_context *ctx) {
2061 int i, size;
2062 const char *pem;
2063 int ecdh_enabled = 0;
2064 int ecdh_available = 0;
2065
2066 // If PEM file is not specified and the init_ssl callback
2067 // is not specified, skip SSL initialization.
2068 if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL) {
2069 // MG_INIT_SSL
2070 // ctx->callbacks.init_ssl == NULL) {
2071 return 1;
2072 }
2073
2074#if !defined(NO_SSL_DL)
2075 if (!load_dll(ctx, SSL_LIB, ssl_sw) ||
2076 !load_dll(ctx, CRYPTO_LIB, crypto_sw)) {
2077 return 0;
2078 }
2079#endif // NO_SSL_DL
2080
2081 // Initialize SSL library
2084
2085 if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
2086 cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error());
2087 return 0;
2088 }
2089
2092
2093#if 0
2094 {
2095 //
2096 // enable Diffie-Hellman key exchange - the DHE series of ciphers
2097 //
2098 // this code is from here:
2099 // https://www.openssl.org/docs/ssl/SSL_CTX_set_tmp_dh_callback.html
2100 // to generate the pem file: openssl dhparam -out dh_param_2048.pem 2048
2101
2102 /* Set up ephemeral DH parameters. */
2103 void *dh_2048 = NULL;
2104 FILE *paramfile;
2105 paramfile = fopen("dh_param_2048.pem", "r");
2106 if (paramfile) {
2109 } else {
2110 /* Error. */
2111 }
2112 if (dh_2048 == NULL) {
2113 /* Error. */
2114 }
2115 if (SSL_CTX_set_tmp_dh(ctx->ssl_ctx, dh_2048) != 1) {
2116 /* Error. */
2117 }
2118 }
2119#endif
2120
2121#ifdef OPENSSL_EC_NAMED_CURVE
2122 {
2123 //
2124 // enable Elliptic Curve Diffie-Hellman key exchange - the ECDHE series of ciphers
2125 //
2126
2128 if (! ecdh) {
2129 cry(fc(ctx), "%s: EC_KEY_new_by_curve_name (NID_X9_62_prime256v1) failed: %s", __func__, ssl_error());
2130 } else {
2131 if (1 != SSL_CTX_set_tmp_ecdh (ctx->ssl_ctx, ecdh)) {
2132 cry(fc(ctx), "%s: SSL_CTX_set_tmp_ecdh (ctx->ssl_ctx, ecdh) failed: %s", __func__, ssl_error());
2133 }
2134 EC_KEY_free (ecdh);
2135 ecdh_enabled = 1;
2136 }
2137 }
2138#else
2139#warning Very old openssl, no EC_KEY, no ECDH for modern criptography!
2140#endif
2141
2144
2145 // get cipher list
2146
2147 {
2148 int priority;
2149 SSL* ssl = SSL_new(ctx->ssl_ctx);
2150 for (priority=0; 1; priority++) {
2153 break;
2154 //printf("priority %d cipher list: %s\n", priority, available_cipher_list);
2155 if (strstr(available_cipher_list, "ECDHE") != NULL)
2156 ecdh_available = 1;
2157 }
2158 SSL_free(ssl);
2159 }
2160
2161 if (!ecdh_available || !ecdh_enabled) {
2162 cry(fc(ctx), "%s: openssl \"modern cryptography\" ECDH ciphers not available", __func__);
2163 }
2164
2165 // disable weak ciphers
2166 const char* cipher_list = "ALL:!RC4:!DES-CBC-SHA:!DES:!DES-CBC3-SHA:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW";
2167
2168 // enable only "modern cryptography" ciphers if ECDHE ciphers available
2170 cipher_list = "ALL:!AECDH:!RC4:!DES-CBC-SHA:!DES:!AES128-SHA+RSA:!AES256-SHA+RSA:!DES-CBC3-SHA:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW";
2171
2173
2174 // If user callback returned non-NULL, that means that user callback has
2175 // set up certificate itself. In this case, skip sertificate setting.
2176 // MG_INIT_SSL
2177 if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 ||
2178 SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) {
2179 cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
2180 return 0;
2181 }
2182
2183 if (pem != NULL) {
2185 }
2186
2187 // Initialize locking callbacks, needed for thread safety.
2188 // http://www.openssl.org/support/faq.html#PROG1
2189 size = sizeof(pthread_mutex_t) * CRYPTO_num_locks();
2190 if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) {
2191 cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error());
2192 return 0;
2193 }
2194
2195 for (i = 0; i < CRYPTO_num_locks(); i++) {
2197 }
2198
2201
2202 return 1;
2203}
2204
2205static void uninitialize_ssl(struct mg_context *ctx) {
2206 int i;
2207 if (ctx->ssl_ctx != NULL) {
2209 for (i = 0; i < CRYPTO_num_locks(); i++) {
2211 }
2214 }
2215}
2216#endif // !NO_SSL
2217
2218static SOCKET conn2(const char *host, int port, int use_ssl,
2219 char *ebuf, size_t ebuf_len) {
2220 struct sockaddr_in sin;
2221 struct hostent *he = NULL;
2222 SOCKET sock = INVALID_SOCKET;
2223
2224 (void) use_ssl; // Prevent warning for -DNO_SSL case
2225
2226 if (host == NULL) {
2227 snprintf(ebuf, ebuf_len, "%s", "NULL host");
2228#ifndef NO_SSL
2229 } else if (use_ssl && (void *)SSLv23_client_method == NULL) {
2230 snprintf(ebuf, ebuf_len, "%s", "SSL is not initialized");
2231 // TODO(lsm): use something threadsafe instead of gethostbyname()
2232#endif
2233 } else if ((he = gethostbyname(host)) == NULL) {
2234 snprintf(ebuf, ebuf_len, "gethostbyname(%s): %s", host, strerror(ERRNO));
2235 } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
2236 snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO));
2237 } else {
2238 set_close_on_exec(sock);
2239 sin.sin_family = AF_INET;
2240 sin.sin_port = htons((uint16_t) port);
2241 sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
2242 if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
2243 snprintf(ebuf, ebuf_len, "connect(%s:%d): %s",
2244 host, port, strerror(ERRNO));
2245 closesocket(sock);
2246 sock = INVALID_SOCKET;
2247 }
2248 }
2249 return sock;
2250}
2251
2252struct mg_connection *mg_connect(const char *host, int port, int use_ssl,
2253 char *ebuf, size_t ebuf_len) {
2254 static struct mg_context fake_ctx;
2255 struct mg_connection *conn = NULL;
2256 SOCKET sock;
2257
2258 if ((sock = conn2(host, port, use_ssl, ebuf, ebuf_len)) == INVALID_SOCKET) {
2259 } else if ((conn = (struct mg_connection *)
2260 calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) {
2261 snprintf(ebuf, ebuf_len, "calloc(): %s", strerror(ERRNO));
2263#ifndef NO_SSL
2264 } else if (use_ssl && (conn->client_ssl_ctx =
2266 snprintf(ebuf, ebuf_len, "SSL_CTX_new error");
2268 free(conn);
2269 conn = NULL;
2270#endif // NO_SSL
2271 } else {
2272 socklen_t len = sizeof(struct sockaddr);
2273 conn->buf_size = MAX_REQUEST_SIZE;
2274 conn->buf = (char *) (conn + 1);
2275 conn->ctx = &fake_ctx;
2276 conn->client.sock = sock;
2277 getsockname(sock, &conn->client.rsa.sa, &len);
2278 conn->client.is_ssl = use_ssl;
2279#ifndef NO_SSL
2280 if (use_ssl) {
2281 // SSL_CTX_set_verify call is needed to switch off server certificate
2282 // checking, which is off by default in OpenSSL and on in yaSSL.
2284 sslize(conn, conn->client_ssl_ctx, SSL_connect);
2285 }
2286#endif
2287 }
2288
2289 return conn;
2290}
2291
2292struct mg_connection *mg_download(const char *host, int port, int use_ssl,
2293 char *ebuf, size_t ebuf_len,
2294 const char *fmt, ...) {
2295 struct mg_connection *conn;
2296 va_list ap;
2297
2298 va_start(ap, fmt);
2299 ebuf[0] = '\0';
2300 if ((conn = mg_connect(host, port, use_ssl, ebuf, ebuf_len)) == NULL) {
2301 } else if (mg_vprintf(conn, fmt, ap) <= 0) {
2302 snprintf(ebuf, ebuf_len, "%s", "Error sending request");
2303 } else {
2304 getreq(conn, ebuf, ebuf_len);
2305 }
2306 if (ebuf[0] != '\0' && conn != NULL) {
2307 mg_close_connection(conn);
2308 conn = NULL;
2309 }
2310
2311 return conn;
2312}
2313
2314// Return number of bytes left to read for this connection
2315static int64_t left_to_read(const struct mg_connection *conn) {
2316 return conn->content_len + conn->request_len - conn->num_bytes_read;
2317}
2318
2319static int call_user(int type, struct mg_connection *conn, void *p) {
2320 if (conn != NULL && conn->ctx != NULL) {
2321 conn->event.user_data = conn->ctx->user_data;
2322 conn->event.type = type;
2323 conn->event.event_param = p;
2324 conn->event.request_info = &conn->request_info;
2325 conn->event.conn = conn;
2326 }
2327 return conn == NULL || conn->ctx == NULL || conn->ctx->event_handler == NULL ?
2328 0 : conn->ctx->event_handler(&conn->event);
2329}
2330
2331static FILE *mg_fopen(const char *path, const char *mode) {
2332#ifdef _WIN32
2333 wchar_t wbuf[PATH_MAX], wmode[20];
2334 to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
2336 return _wfopen(wbuf, wmode);
2337#else
2338 return fopen(path, mode);
2339#endif
2340}
2341
2342static void sockaddr_to_string(char *buf, size_t len,
2343 const union usa *usa) {
2344 buf[0] = '\0';
2345#if defined(USE_IPV6)
2346 inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ?
2347 (void *) &usa->sin.sin_addr :
2348 (void *) &usa->sin6.sin6_addr, buf, len);
2349#elif defined(_WIN32)
2350 // Only Windoze Vista (and newer) have inet_ntop()
2351 strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
2352#else
2353 inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len);
2354#endif
2355}
2356
2357// Print error message to the opened error log stream.
2358static void cry(struct mg_connection *conn, const char *fmt, ...) {
2360 va_list ap;
2361 FILE *fp;
2362 time_t timestamp;
2363
2364 va_start(ap, fmt);
2365 (void) vsnprintf(buf, sizeof(buf), fmt, ap);
2366 va_end(ap);
2367
2368 // Do not lock when getting the callback value, here and below.
2369 // I suppose this is fine, since function cannot disappear in the
2370 // same way string option can.
2371 if (call_user(MG_EVENT_LOG, conn, buf) == 0) {
2372 fp = conn->ctx == NULL || conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
2373 fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");
2374
2375 if (fp != NULL) {
2376 flockfile(fp);
2377 timestamp = time(NULL);
2378
2379 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
2380 fprintf(fp, "[%010lu] [error] [client %s] ", (unsigned long) timestamp,
2381 src_addr);
2382
2383 if (conn->request_info.request_method != NULL) {
2384 fprintf(fp, "%s %s: ", conn->request_info.request_method,
2385 conn->request_info.uri);
2386 }
2387
2388 fprintf(fp, "%s", buf);
2389 fputc('\n', fp);
2390 funlockfile(fp);
2391 fclose(fp);
2392 }
2393 }
2394}
2395
2396const char *mg_version(void) {
2397 return MONGOOSE_VERSION;
2398}
2399
2400// HTTP 1.1 assumes keep alive if "Connection:" header is not set
2401// This function must tolerate situations when connection info is not
2402// set up, for example if request parsing failed.
2403static int should_keep_alive(const struct mg_connection *conn) {
2404 const char *http_version = conn->request_info.http_version;
2405 const char *header = mg_get_header(conn, "Connection");
2406 if (conn->must_close ||
2407 conn->status_code == 401 ||
2408 mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 ||
2409 (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) ||
2410 (header == NULL && http_version && strcmp(http_version, "1.1"))) {
2411 return 0;
2412 }
2413 return 1;
2414}
2415
2416static const char *suggest_connection_header(const struct mg_connection *conn) {
2417 return should_keep_alive(conn) ? "keep-alive" : "close";
2418}
2419
2420static void send_http_error(struct mg_connection *conn, int status,
2421 const char *reason, const char *fmt, ...) {
2422 char buf[MG_BUF_LEN];
2423 va_list ap;
2424 int len = 0;
2425
2426 conn->status_code = status;
2427 buf[0] = '\0';
2428
2429 // Errors 1xx, 204 and 304 MUST NOT send a body
2430 if (status > 199 && status != 204 && status != 304) {
2431 len = mg_snprintf(buf, sizeof(buf), "Error %d: %s", status, reason);
2432 buf[len++] = '\n';
2433
2434 va_start(ap, fmt);
2435 len += mg_vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
2436 va_end(ap);
2437 }
2438 DEBUG_TRACE(("[%s]", buf));
2439
2440 if (call_user(MG_HTTP_ERROR, conn, (void *) (long) status) == 0) {
2441 mg_printf(conn, "HTTP/1.1 %d %s\r\n"
2442 "Content-Length: %d\r\n"
2443 "Connection: %s\r\n\r\n", status, reason, len,
2445 conn->num_bytes_sent += mg_printf(conn, "%s", buf);
2446 }
2447}
2448
2449// Write data to the IO channel - opened file descriptor, socket or SSL
2450// descriptor. Return number of bytes written.
2451static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
2452 int64_t len) {
2453 int64_t sent;
2454 int n, k;
2455
2456 (void) ssl; // Get rid of warning
2457 sent = 0;
2458 while (sent < len) {
2459
2460 // How many bytes we send in this iteration
2461 k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
2462
2463#if !defined(NO_SSL)
2464 if (ssl != NULL) {
2465 n = SSL_write(ssl, buf + sent, k);
2466 } else
2467#endif
2468 if (fp != NULL) {
2469 n = (int) fwrite(buf + sent, 1, (size_t) k, fp);
2470 if (ferror(fp))
2471 n = -1;
2472 } else {
2473 n = send(sock, buf + sent, (size_t) k, MSG_NOSIGNAL);
2474 }
2475
2476 if (n <= 0)
2477 break;
2478
2479 sent += n;
2480 }
2481
2482 return sent;
2483}
2484
2485// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
2486// Return negative value on error, or number of bytes read on success.
2487static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
2488 int nread;
2489
2490 if (len <= 0) return 0;
2491 if (fp != NULL) {
2492 // Use read() instead of fread(), because if we're reading from the CGI
2493 // pipe, fread() may block until IO buffer is filled up. We cannot afford
2494 // to block and must pass all read bytes immediately to the client.
2495 nread = read(fileno(fp), buf, (size_t) len);
2496#ifndef NO_SSL
2497 } else if (conn->ssl != NULL) {
2498 nread = SSL_read(conn->ssl, buf, len);
2499#endif
2500 } else {
2501 nread = recv(conn->client.sock, buf, (size_t) len, 0);
2502 }
2503 if (nread > 0) {
2504 conn->num_bytes_read += nread;
2505 }
2506
2507 return conn->ctx->stop_flag ? -1 : nread;
2508}
2509
2510static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) {
2511 int n, nread = 0;
2512
2513 while (len > 0 && conn->ctx->stop_flag == 0) {
2514 n = pull(fp, conn, buf + nread, len);
2515 if (n < 0) {
2516 nread = n; // Propagate the error
2517 break;
2518 } else if (n == 0) {
2519 break; // No more data to read
2520 } else {
2521 nread += n;
2522 len -= n;
2523 }
2524 }
2525
2526 return nread;
2527}
2528
2529int mg_read(struct mg_connection *conn, void *buf, int len) {
2530 int n, buffered_len, nread = 0;
2531 int64_t left;
2532
2533 if (conn->content_len <= 0) {
2534 return 0;
2535 }
2536
2537 // conn->buf body
2538 // |=================|==========|===============|
2539 // |<--request_len-->| |
2540 // |<-----------data_len------->| conn->buf + conn->buf_size
2541
2542 // First, check for data buffered in conn->buf by read_request().
2543 if (len > 0 && (buffered_len = conn->data_len - conn->request_len) > 0) {
2544 char *body = conn->buf + conn->request_len;
2545 if (buffered_len > len) buffered_len = len;
2546 if (buffered_len > conn->content_len) buffered_len = (int)conn->content_len;
2547
2548 memcpy(buf, body, (size_t) buffered_len);
2549 memmove(body, body + buffered_len,
2550 &conn->buf[conn->data_len] - &body[buffered_len]);
2551 len -= buffered_len;
2552 conn->data_len -= buffered_len;
2554 }
2555
2556 // Read data from the socket.
2557 if (len > 0 && (left = left_to_read(conn)) > 0) {
2558 if (left < len) {
2559 len = (int) left;
2560 }
2561 n = pull_all(NULL, conn, (char *) buf + nread, (int) len);
2562 nread = n >= 0 ? nread + n : n;
2563 }
2564
2565 return nread;
2566}
2567
2568int mg_write(struct mg_connection *conn, const void *buf, int len) {
2569 time_t now;
2571
2572 if (conn->throttle > 0) {
2573 if ((now = time(NULL)) != conn->last_throttle_time) {
2574 conn->last_throttle_time = now;
2575 conn->last_throttle_bytes = 0;
2576 }
2577 allowed = conn->throttle - conn->last_throttle_bytes;
2578 if (allowed > (int64_t) len) {
2579 allowed = len;
2580 }
2581 if ((total = push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
2582 (int64_t) allowed)) == allowed) {
2583 buf = (char *) buf + total;
2584 conn->last_throttle_bytes += total;
2585 while (total < (int64_t) len && conn->ctx->stop_flag == 0) {
2586 allowed = conn->throttle > (int64_t) len - total ?
2587 (int64_t) len - total : conn->throttle;
2588 if ((n = push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
2589 (int64_t) allowed)) != allowed) {
2590 break;
2591 }
2592 sleep(1);
2594 conn->last_throttle_time = time(NULL);
2595 buf = (char *) buf + n;
2596 total += n;
2597 }
2598 }
2599 } else {
2600 total = push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
2601 (int64_t) len);
2602 }
2603 return (int) total;
2604}
2605
2606int mg_url_decode(const char *src, int src_len, char *dst,
2607 int dst_len, int is_form_url_encoded) {
2608 int i, j, a, b;
2609#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
2610
2611 for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
2612 if (src[i] == '%' && i < src_len - 2 &&
2613 isxdigit(* (const unsigned char *) (src + i + 1)) &&
2614 isxdigit(* (const unsigned char *) (src + i + 2))) {
2615 a = tolower(* (const unsigned char *) (src + i + 1));
2616 b = tolower(* (const unsigned char *) (src + i + 2));
2617 dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
2618 i += 2;
2619 } else if (is_form_url_encoded && src[i] == '+') {
2620 dst[j] = ' ';
2621 } else {
2622 dst[j] = src[i];
2623 }
2624 }
2625
2626 dst[j] = '\0'; // Null-terminate the destination
2627
2628 return i >= src_len ? j : -1;
2629}
2630
2631int mg_get_var(const char *data, size_t data_len, const char *name,
2632 char *dst, size_t dst_len) {
2633 const char *p, *e, *s;
2634 size_t name_len;
2635 int len;
2636
2637 if (dst == NULL || dst_len == 0) {
2638 len = -2;
2639 } else if (data == NULL || name == NULL || data_len == 0) {
2640 len = -1;
2641 dst[0] = '\0';
2642 } else {
2643 name_len = strlen(name);
2644 e = data + data_len;
2645 len = -1;
2646 dst[0] = '\0';
2647
2648 // data is "var1=val1&var2=val2...". Find variable first
2649 for (p = data; p + name_len < e; p++) {
2650 if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
2651 !mg_strncasecmp(name, p, name_len)) {
2652
2653 // Point p to variable value
2654 p += name_len + 1;
2655
2656 // Point s to the end of the value
2657 s = (const char *) memchr(p, '&', (size_t)(e - p));
2658 if (s == NULL) {
2659 s = e;
2660 }
2661 assert(s >= p);
2662
2663 // Decode variable into destination buffer
2664 len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
2665
2666 // Redirect error code from -1 to -2 (destination buffer too small).
2667 if (len == -1) {
2668 len = -2;
2669 }
2670 break;
2671 }
2672 }
2673 }
2674
2675 return len;
2676}
2677
2678int mg_get_cookie(const char *cookie_header, const char *var_name,
2679 char *dst, size_t dst_size) {
2680 const char *s, *p, *end;
2681 int name_len, len = -1;
2682
2683 if (dst == NULL || dst_size == 0) {
2684 len = -2;
2685 } else if (var_name == NULL || (s = cookie_header) == NULL) {
2686 len = -1;
2687 dst[0] = '\0';
2688 } else {
2689 name_len = (int) strlen(var_name);
2690 end = s + strlen(s);
2691 dst[0] = '\0';
2692
2693 for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) {
2694 if (s[name_len] == '=') {
2695 s += name_len + 1;
2696 if ((p = strchr(s, ' ')) == NULL)
2697 p = end;
2698 if (p[-1] == ';')
2699 p--;
2700 if (*s == '"' && p[-1] == '"' && p > s + 1) {
2701 s++;
2702 p--;
2703 }
2704 if ((size_t) (p - s) < dst_size) {
2705 len = p - s;
2706 mg_strlcpy(dst, s, (size_t) len + 1);
2707 } else {
2708 len = -3;
2709 }
2710 break;
2711 }
2712 }
2713 }
2714 return len;
2715}
2716
2717// Return 1 if real file has been found, 0 otherwise
2718static int convert_uri_to_file_name(struct mg_connection *conn, char *buf,
2719 size_t buf_len, struct file *filep) {
2720 struct vec a, b;
2721 const char *rewrite, *uri = conn->request_info.uri,
2722 *root = conn->ctx->config[DOCUMENT_ROOT];
2723 char *p;
2724 int match_len;
2725 char gz_path[PATH_MAX];
2726 char const* accept_encoding;
2727
2728 // No filesystem access
2729 if (root == NULL) {
2730 return 0;
2731 }
2732
2733 // Using buf_len - 1 because memmove() for PATH_INFO may shift part
2734 // of the path one byte on the right.
2735 // If document_root is NULL, leave the file empty.
2736 mg_snprintf(buf, buf_len - 1, "%s%s", root, uri);
2737
2738 rewrite = conn->ctx->config[REWRITE];
2739 while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
2740 if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
2741 mg_snprintf(buf, buf_len - 1, "%.*s%s", (int) b.len, b.ptr,
2742 uri + match_len);
2743 break;
2744 }
2745 }
2746
2747 if (mg_stat(buf, filep)) {
2748 return 1;
2749 }
2750
2751 // if we can't find the actual file, look for the file
2752 // with the same name but a .gz extension. If we find it,
2753 // use that and set the gzipped flag in the file struct
2754 // to indicate that the response need to have the content-
2755 // encoding: gzip header
2756 // we can only do this if the browser declares support
2757 if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) {
2758 if (strstr(accept_encoding,"gzip") != NULL) {
2759 snprintf(gz_path, sizeof(gz_path), "%s.gz", buf);
2760 if (mg_stat(gz_path, filep)) {
2761 filep->gzipped = 1;
2762 return 1;
2763 }
2764 }
2765 }
2766
2767 // Support PATH_INFO for CGI scripts.
2768 for (p = buf + strlen(root == NULL ? "" : root); *p != '\0'; p++) {
2769 if (*p == '/') {
2770 *p = '\0';
2772 strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 &&
2773 mg_stat(buf, filep)) {
2774 // Shift PATH_INFO block one character right, e.g.
2775 // "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
2776 // conn->path_info is pointing to the local variable "path" declared
2777 // in handle_request(), so PATH_INFO is not valid after
2778 // handle_request returns.
2779 conn->path_info = p + 1;
2780 memmove(p + 2, p + 1, strlen(p + 1) + 1); // +1 is for trailing \0
2781 p[1] = '/';
2782 return 1;
2783 } else {
2784 *p = '/';
2785 }
2786 }
2787 }
2788
2789 return 0;
2790}
2791
2792// Check whether full request is buffered. Return:
2793// -1 if request is malformed
2794// 0 if request is not yet fully buffered
2795// >0 actual request length, including last \r\n\r\n
2796static int get_request_len(const char *buf, int buf_len) {
2797 int i;
2798
2799 for (i = 0; i < buf_len; i++) {
2800 // Control characters are not allowed but >=128 is.
2801 // Abort scan as soon as one malformed character is found;
2802 // don't let subsequent \r\n\r\n win us over anyhow
2803 if (!isprint(* (const unsigned char *) &buf[i]) && buf[i] != '\r' &&
2804 buf[i] != '\n' && * (const unsigned char *) &buf[i] < 128) {
2805 return -1;
2806 } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') {
2807 return i + 2;
2808 } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' &&
2809 buf[i + 2] == '\n') {
2810 return i + 3;
2811 }
2812 }
2813
2814 return 0;
2815}
2816
2817// Protect against directory disclosure attack by removing '..',
2818// excessive '/' and '\' characters
2820 char *p = s;
2821
2822 while (*s != '\0') {
2823 *p++ = *s++;
2824 if (s[-1] == '/' || s[-1] == '\\') {
2825 // Skip all following slashes, backslashes and double-dots
2826 while (s[0] != '\0') {
2827 if (s[0] == '/' || s[0] == '\\') {
2828 s++;
2829 } else if (s[0] == '.' && s[1] == '.') {
2830 s += 2;
2831 } else {
2832 break;
2833 }
2834 }
2835 }
2836 }
2837 *p = '\0';
2838}
2839
2840static const struct {
2841 const char *extension;
2842 size_t ext_len;
2843 const char *mime_type;
2844} builtin_mime_types[] = {
2845 {".html", 5, "text/html"},
2846 {".htm", 4, "text/html"},
2847 {".shtm", 5, "text/html"},
2848 {".shtml", 6, "text/html"},
2849 {".css", 4, "text/css"},
2850 {".js", 3, "application/x-javascript"},
2851 {".ico", 4, "image/x-icon"},
2852 {".gif", 4, "image/gif"},
2853 {".jpg", 4, "image/jpeg"},
2854 {".jpeg", 5, "image/jpeg"},
2855 {".png", 4, "image/png"},
2856 {".svg", 4, "image/svg+xml"},
2857 {".txt", 4, "text/plain"},
2858 {".torrent", 8, "application/x-bittorrent"},
2859 {".wav", 4, "audio/x-wav"},
2860 {".mp3", 4, "audio/x-mp3"},
2861 {".mid", 4, "audio/mid"},
2862 {".m3u", 4, "audio/x-mpegurl"},
2863 {".ogg", 4, "application/ogg"},
2864 {".ram", 4, "audio/x-pn-realaudio"},
2865 {".xml", 4, "text/xml"},
2866 {".json", 5, "text/json"},
2867 {".xslt", 5, "application/xml"},
2868 {".xsl", 4, "application/xml"},
2869 {".ra", 3, "audio/x-pn-realaudio"},
2870 {".doc", 4, "application/msword"},
2871 {".exe", 4, "application/octet-stream"},
2872 {".zip", 4, "application/x-zip-compressed"},
2873 {".xls", 4, "application/excel"},
2874 {".tgz", 4, "application/x-tar-gz"},
2875 {".tar", 4, "application/x-tar"},
2876 {".gz", 3, "application/x-gunzip"},
2877 {".arj", 4, "application/x-arj-compressed"},
2878 {".rar", 4, "application/x-arj-compressed"},
2879 {".rtf", 4, "application/rtf"},
2880 {".pdf", 4, "application/pdf"},
2881 {".swf", 4, "application/x-shockwave-flash"},
2882 {".mpg", 4, "video/mpeg"},
2883 {".webm", 5, "video/webm"},
2884 {".mpeg", 5, "video/mpeg"},
2885 {".mov", 4, "video/quicktime"},
2886 {".mp4", 4, "video/mp4"},
2887 {".m4v", 4, "video/x-m4v"},
2888 {".asf", 4, "video/x-ms-asf"},
2889 {".avi", 4, "video/x-msvideo"},
2890 {".bmp", 4, "image/bmp"},
2891 {".ttf", 4, "application/x-font-ttf"},
2892 {NULL, 0, NULL}
2894
2895const char *mg_get_builtin_mime_type(const char *path) {
2896 const char *ext;
2897 size_t i, path_len;
2898
2899 path_len = strlen(path);
2900
2901 for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
2902 ext = path + (path_len - builtin_mime_types[i].ext_len);
2905 return builtin_mime_types[i].mime_type;
2906 }
2907 }
2908
2909 return "text/plain";
2910}
2911
2912// Look at the "path" extension and figure what mime type it has.
2913// Store mime type in the vector.
2914static void get_mime_type(struct mg_context *ctx, const char *path,
2915 struct vec *vec) {
2916 struct vec ext_vec, mime_vec;
2917 const char *list, *ext;
2918 size_t path_len;
2919
2920 path_len = strlen(path);
2921
2922 // Scan user-defined mime types first, in case user wants to
2923 // override default mime types.
2925 while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
2926 // ext now points to the path suffix
2927 ext = path + path_len - ext_vec.len;
2928 if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
2929 *vec = mime_vec;
2930 return;
2931 }
2932 }
2933
2935 vec->len = strlen(vec->ptr);
2936}
2937
2938void mg_url_encode(const char *src, char *dst, size_t dst_len) {
2939 static const char *dont_escape = "._-$,;~()";
2940 static const char *hex = "0123456789abcdef";
2941 const char *end = dst + dst_len - 1;
2942
2943 for (; *src != '\0' && dst < end; src++, dst++) {
2944 if (isalnum(*(const unsigned char *) src) ||
2945 strchr(dont_escape, * (const unsigned char *) src) != NULL) {
2946 *dst = *src;
2947 } else if (dst + 2 < end) {
2948 dst[0] = '%';
2949 dst[1] = hex[(* (const unsigned char *) src) >> 4];
2950 dst[2] = hex[(* (const unsigned char *) src) & 0xf];
2951 dst += 2;
2952 }
2953 }
2954
2955 *dst = '\0';
2956}
2957
2958static void print_dir_entry(const struct de *de) {
2959 char size[64], mod[64], href[PATH_MAX * 3];
2960 const char *slash = de->file.is_directory ? "/" : "";
2961
2962 if (de->file.is_directory) {
2963 mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]");
2964 } else {
2965 // We use (signed) cast below because MSVC 6 compiler cannot
2966 // convert unsigned __int64 to double. Sigh.
2967 if (de->file.size < 1024) {
2968 mg_snprintf(size, sizeof(size), "%d", (int) de->file.size);
2969 } else if (de->file.size < 0x100000) {
2970 mg_snprintf(size, sizeof(size),
2971 "%.1fk", (double) de->file.size / 1024.0);
2972 } else if (de->file.size < 0x40000000) {
2973 mg_snprintf(size, sizeof(size),
2974 "%.1fM", (double) de->file.size / 1048576);
2975 } else {
2976 mg_snprintf(size, sizeof(size),
2977 "%.1fG", (double) de->file.size / 1073741824);
2978 }
2979 }
2980 strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
2982 mg_url_encode(de->file_name, href, sizeof(href));
2984 "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
2985 "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
2987}
2988
2989// This function is called from send_directory() and used for
2990// sorting directory entries by size, or name, or modification time.
2991// On windows, __cdecl specification is needed in case if project is built
2992// with __stdcall convention. qsort always requires __cdels callback.
2993static int WINCDECL compare_dir_entries(const void *p1, const void *p2) {
2994 const struct de *a = (const struct de *) p1, *b = (const struct de *) p2;
2995 const char *query_string = a->conn->request_info.query_string;
2996 int cmp_result = 0;
2997
2998 if (query_string == NULL) {
2999 query_string = "na";
3000 }
3001
3002 if (a->file.is_directory && !b->file.is_directory) {
3003 return -1; // Always put directories on top
3004 } else if (!a->file.is_directory && b->file.is_directory) {
3005 return 1; // Always put directories on top
3006 } else if (*query_string == 'n') {
3007 cmp_result = strcmp(a->file_name, b->file_name);
3008 } else if (*query_string == 's') {
3009 cmp_result = a->file.size == b->file.size ? 0 :
3010 a->file.size > b->file.size ? 1 : -1;
3011 } else if (*query_string == 'd') {
3012 cmp_result = a->file.modification_time == b->file.modification_time ? 0 :
3013 a->file.modification_time > b->file.modification_time ? 1 : -1;
3014 }
3015
3016 return query_string[1] == 'd' ? -cmp_result : cmp_result;
3017}
3018
3019static int must_hide_file(struct mg_connection *conn, const char *path) {
3020 const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
3021 const char *pattern = conn->ctx->config[HIDE_FILES];
3022 return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 ||
3023 (pattern != NULL && match_prefix(pattern, strlen(pattern), path) > 0);
3024}
3025
3026static int scan_directory(struct mg_connection *conn, const char *dir,
3027 void *data, void (*cb)(struct de *, void *)) {
3028 char path[PATH_MAX];
3029 struct dirent *dp;
3030 DIR *dirp;
3031 struct de de;
3032
3033 if ((dirp = opendir(dir)) == NULL) {
3034 return 0;
3035 } else {
3036 de.conn = conn;
3037
3038 while ((dp = readdir(dirp)) != NULL) {
3039 // Do not show current dir and hidden files
3040 if (!strcmp(dp->d_name, ".") ||
3041 !strcmp(dp->d_name, "..") ||
3042 must_hide_file(conn, dp->d_name)) {
3043 continue;
3044 }
3045
3046 mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
3047
3048 // If we don't memset stat structure to zero, mtime will have
3049 // garbage and strftime() will segfault later on in
3050 // print_dir_entry(). memset is required only if mg_stat()
3051 // fails. For more details, see
3052 // http://code.google.com/p/mongoose/issues/detail?id=79
3053 memset(&de.file, 0, sizeof(de.file));
3054 mg_stat(path, &de.file);
3055
3056 de.file_name = dp->d_name;
3057 cb(&de, data);
3058 }
3059 (void) closedir(dirp);
3060 }
3061 return 1;
3062}
3063
3064static int remove_directory(struct mg_connection *conn, const char *dir) {
3065 char path[PATH_MAX];
3066 struct dirent *dp;
3067 DIR *dirp;
3068 struct de de;
3069
3070 if ((dirp = opendir(dir)) == NULL) {
3071 return 0;
3072 } else {
3073 de.conn = conn;
3074
3075 while ((dp = readdir(dirp)) != NULL) {
3076 // Do not show current dir, but show hidden files
3077 if (!strcmp(dp->d_name, ".") ||
3078 !strcmp(dp->d_name, "..")) {
3079 continue;
3080 }
3081
3082 mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
3083
3084 // If we don't memset stat structure to zero, mtime will have
3085 // garbage and strftime() will segfault later on in
3086 // print_dir_entry(). memset is required only if mg_stat()
3087 // fails. For more details, see
3088 // http://code.google.com/p/mongoose/issues/detail?id=79
3089 memset(&de.file, 0, sizeof(de.file));
3090 mg_stat(path, &de.file);
3092 if(de.file.is_directory) {
3093 remove_directory(conn, path);
3094 } else {
3095 mg_remove(path);
3096 }
3097 }
3098
3099 }
3100 (void) closedir(dirp);
3101
3102 rmdir(dir);
3103 }
3104
3105 return 1;
3106}
3107
3113
3114// Behaves like realloc(), but frees original pointer on failure
3115static void *realloc2(void *ptr, size_t size) {
3116 void *new_ptr = realloc(ptr, size);
3117 if (new_ptr == NULL) {
3118 free(ptr);
3119 }
3120 return new_ptr;
3121}
3122
3123static void dir_scan_callback(struct de *de, void *data) {
3124 struct dir_scan_data *dsd = (struct dir_scan_data *) data;
3125
3126 if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) {
3127 dsd->arr_size *= 2;
3128 dsd->entries = (struct de *) realloc2(dsd->entries, dsd->arr_size *
3129 sizeof(dsd->entries[0]));
3130 }
3131 if (dsd->entries == NULL) {
3132 // TODO(lsm): propagate an error to the caller
3133 dsd->num_entries = 0;
3134 } else {
3135 dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
3136 dsd->entries[dsd->num_entries].file = de->file;
3137 dsd->entries[dsd->num_entries].conn = de->conn;
3138 dsd->num_entries++;
3139 }
3140}
3141
3143 const char *dir) {
3144 int i, sort_direction;
3145 struct dir_scan_data data = { NULL, 0, 128 };
3146
3147 if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
3148 send_http_error(conn, 500, "Cannot open directory",
3149 "Error: opendir(%s): %s", dir, strerror(ERRNO));
3150 return;
3151 }
3152
3154 conn->request_info.query_string[1] == 'd' ? 'a' : 'd';
3155
3156 conn->must_close = 1;
3157 mg_printf(conn, "%s",
3158 "HTTP/1.1 200 OK\r\n"
3159 "Transfer-Encoding: Chunked\r\n"
3160 "Content-Type: text/html; charset=utf-8\r\n\r\n");
3161
3162 conn->num_bytes_sent += mg_chunked_printf(conn,
3163 "<html><head><title>Index of %s</title>"
3164 "<style>th {text-align: left;}</style></head>"
3165 "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
3166 "<tr><th><a href=\"?n%c\">Name</a></th>"
3167 "<th><a href=\"?d%c\">Modified</a></th>"
3168 "<th><a href=\"?s%c\">Size</a></th></tr>"
3169 "<tr><td colspan=\"3\"><hr></td></tr>",
3170 conn->request_info.uri, conn->request_info.uri,
3172
3173 // Print first entry - link to a parent directory
3174 conn->num_bytes_sent += mg_chunked_printf(conn,
3175 "<tr><td><a href=\"%s%s\">%s</a></td>"
3176 "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
3177 conn->request_info.uri, "..", "Parent directory", "-", "-");
3178
3179 // Sort and print directory entries
3180 qsort(data.entries, (size_t) data.num_entries, sizeof(data.entries[0]),
3182 for (i = 0; i < data.num_entries; i++) {
3183 print_dir_entry(&data.entries[i]);
3184 free(data.entries[i].file_name);
3185 }
3186 free(data.entries);
3187
3188 conn->num_bytes_sent += mg_chunked_printf(conn, "%s",
3189 "</table></body></html>");
3190 conn->num_bytes_sent += mg_write(conn, "0\r\n\r\n", 5);
3191 conn->status_code = 200;
3192}
3193
3194// Send len bytes from the opened file to the client.
3195static void send_file_data(struct mg_connection *conn, FILE *fp,
3196 int64_t offset, int64_t len) {
3197 char buf[MG_BUF_LEN];
3199
3200 // If offset is beyond file boundaries, don't send anything
3201 if (offset > 0 && fseeko(fp, offset, SEEK_SET) != 0) {
3202 return;
3203 }
3204
3205 while (len > 0) {
3206 // Calculate how much to read from the file in the buffer
3207 to_read = sizeof(buf);
3208 if ((int64_t) to_read > len) {
3209 to_read = (int) len;
3210 }
3211
3212 // Read from file, exit the loop on error
3213 if ((num_read = fread(buf, 1, (size_t) to_read, fp)) <= 0) {
3214 break;
3215 }
3216
3217 // Send read bytes to the client, exit the loop on error
3218 if ((num_written = mg_write(conn, buf, (size_t) num_read)) != num_read) {
3219 break;
3220 }
3221
3222 // Both read and were successful, adjust counters
3223 conn->num_bytes_sent += num_written;
3224 len -= num_written;
3225 }
3226}
3227
3228static int parse_range_header(const char *header, int64_t *a, int64_t *b) {
3229 return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
3230}
3231
3232static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
3233 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
3234}
3235
3236static void construct_etag(char *buf, size_t buf_len,
3237 const struct file *filep) {
3238 snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"",
3239 (unsigned long) filep->modification_time, filep->size);
3240}
3241
3242static void fclose_on_exec(FILE *fp) {
3243 if (fp != NULL) {
3244#ifndef _WIN32
3246#endif
3247 }
3248}
3249
3250static void handle_file_request(struct mg_connection *conn, const char *path,
3251 struct file *filep) {
3252 char date[64], lm[64], etag[64], range[64];
3253 const char *msg = "OK", *hdr;
3254 time_t curtime = time(NULL);
3255 int64_t cl, r1, r2;
3256 struct vec mime_vec;
3257 int n;
3258 char gz_path[PATH_MAX];
3259 char const* encoding = "";
3260 FILE *fp;
3261
3262 get_mime_type(conn->ctx, path, &mime_vec);
3263 cl = filep->size;
3264 conn->status_code = 200;
3265 range[0] = '\0';
3266
3267 // if this file is in fact a pre-gzipped file, rewrite its filename
3268 // it's important to rewrite the filename after resolving
3269 // the mime type from it, to preserve the actual file's type
3270 if (filep->gzipped) {
3271 snprintf(gz_path, sizeof(gz_path), "%s.gz", path);
3272 path = gz_path;
3273 encoding = "Content-Encoding: gzip\r\n";
3274 }
3275
3276 if ((fp = mg_fopen(path, "rb")) == NULL) {
3278 "fopen(%s): %s", path, strerror(ERRNO));
3279 return;
3280 }
3281
3283
3284 // If Range: header specified, act accordingly
3285 r1 = r2 = 0;
3286 hdr = mg_get_header(conn, "Range");
3287 if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 &&
3288 r1 >= 0 && r2 >= 0) {
3289 // actually, range requests don't play well with a pre-gzipped
3290 // file (since the range is specified in the uncmpressed space)
3291 if (filep->gzipped) {
3292 send_http_error(conn, 501, "Not Implemented",
3293 "range requests in gzipped files are not supported");
3294 return;
3295 }
3296 conn->status_code = 206;
3297 cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1;
3298 mg_snprintf(range, sizeof(range),
3299 "Content-Range: bytes "
3300 "%" INT64_FMT "-%"
3301 INT64_FMT "/%" INT64_FMT "\r\n",
3302 r1, r1 + cl - 1, filep->size);
3303 msg = "Partial Content";
3304 }
3305
3306 // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
3307 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
3308 gmt_time_string(date, sizeof(date), &curtime);
3309 gmt_time_string(lm, sizeof(lm), &filep->modification_time);
3310 construct_etag(etag, sizeof(etag), filep);
3311
3312 (void) mg_printf(conn,
3313 "HTTP/1.1 %d %s\r\n"
3314 "Date: %s\r\n"
3315 "Last-Modified: %s\r\n"
3316 "Etag: %s\r\n"
3317 "Content-Type: %.*s\r\n"
3318 "Content-Length: %" INT64_FMT "\r\n"
3319 "Connection: %s\r\n"
3320 "Accept-Ranges: bytes\r\n"
3321 "%s%s%s\r\n",
3322 conn->status_code, msg, date, lm, etag, (int) mime_vec.len,
3323 mime_vec.ptr, cl, suggest_connection_header(conn), range, encoding,
3325
3326 if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
3327 send_file_data(conn, fp, r1, cl);
3328 }
3329 fclose(fp);
3330}
3331
3332void mg_send_file(struct mg_connection *conn, const char *path) {
3334 if (mg_stat(path, &file)) {
3335 handle_file_request(conn, path, &file);
3336 } else {
3337 send_http_error(conn, 404, "Not Found", "%s", "File not found");
3338 }
3339}
3340
3341
3342// Parse HTTP headers from the given buffer, advance buffer to the point
3343// where parsing stopped.
3344static void parse_http_headers(char **buf, struct mg_request_info *ri) {
3345 int i;
3346
3347 for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
3348 ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
3349 ri->http_headers[i].value = skip(buf, "\r\n");
3350 if (ri->http_headers[i].name[0] == '\0')
3351 break;
3352 ri->num_headers = i + 1;
3353 }
3354}
3355
3356static int is_valid_http_method(const char *method) {
3357 return !strcmp(method, "GET") || !strcmp(method, "POST") ||
3358 !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
3359 !strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
3360 !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND")
3361 || !strcmp(method, "MKCOL")
3362 ;
3363}
3364
3365// Parse HTTP request, fill in mg_request_info structure.
3366// This function modifies the buffer by NUL-terminating
3367// HTTP request components, header names and header values.
3368static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
3369 int is_request, request_length = get_request_len(buf, len);
3370 if (request_length > 0) {
3371 // Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
3372 ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL;
3373 ri->num_headers = 0;
3374
3375 buf[request_length - 1] = '\0';
3376
3377 // RFC says that all initial whitespaces should be ingored
3378 while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
3379 buf++;
3380 }
3381 ri->request_method = skip(&buf, " ");
3382 ri->uri = skip(&buf, " ");
3383 ri->http_version = skip(&buf, "\r\n");
3384
3385 // HTTP message could be either HTTP request or HTTP response, e.g.
3386 // "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..."
3387 is_request = is_valid_http_method(ri->request_method);
3388 if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) ||
3389 (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) {
3390 request_length = -1;
3391 } else {
3392 if (is_request) {
3393 ri->http_version += 5;
3394 }
3395 parse_http_headers(&buf, ri);
3396 }
3397 }
3398 return request_length;
3399}
3400
3401// Keep reading the input (either opened file descriptor fd, or socket sock,
3402// or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
3403// buffer (which marks the end of HTTP request). Buffer buf may already
3404// have some data. The length of the data is stored in nread.
3405// Upon every read operation, increase nread by the number of bytes read.
3406static int read_request(FILE *fp, struct mg_connection *conn,
3407 char *buf, int bufsiz, int *nread) {
3408 int request_len, n = 0;
3409
3410 request_len = get_request_len(buf, *nread);
3411 while (conn->ctx->stop_flag == 0 &&
3412 *nread < bufsiz &&
3413 request_len == 0 &&
3414 (n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) {
3415 *nread += n;
3416 assert(*nread <= bufsiz);
3417 request_len = get_request_len(buf, *nread);
3418 }
3419
3420 return request_len <= 0 && n <= 0 ? -1 : request_len;
3421}
3422
3423// For given directory path, substitute it to valid index file.
3424// Return 0 if index file has been found, -1 if not found.
3425// If the file is found, it's stats is returned in stp.
3426static int substitute_index_file(struct mg_connection *conn, char *path,
3427 size_t path_len, struct file *filep) {
3428 const char *list = conn->ctx->config[INDEX_FILES];
3430 struct vec filename_vec;
3431 size_t n = strlen(path);
3432 int found = 0;
3433
3434 // The 'path' given to us points to the directory. Remove all trailing
3435 // directory separator characters from the end of the path, and
3436 // then append single directory separator character.
3437 while (n > 0 && path[n - 1] == '/') {
3438 n--;
3439 }
3440 path[n] = '/';
3441
3442 // Traverse index files list. For each entry, append it to the given
3443 // path and see if the file exists. If it exists, break the loop
3444 while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
3445
3446 // Ignore too long entries that may overflow path buffer
3447 if (filename_vec.len > path_len - (n + 2))
3448 continue;
3449
3450 // Prepare full path to the index file
3451 mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
3452
3453 // Does it exist?
3454 if (mg_stat(path, &file)) {
3455 // Yes it does, break the loop
3456 *filep = file;
3457 found = 1;
3458 break;
3459 }
3460 }
3461
3462 // If no index file exists, restore directory path
3463 if (!found) {
3464 path[n] = '\0';
3465 }
3466
3467 return found;
3468}
3469
3470// Return True if we should reply 304 Not Modified.
3471static int is_not_modified(const struct mg_connection *conn,
3472 const struct file *filep) {
3473 char etag[64];
3474 const char *ims = mg_get_header(conn, "If-Modified-Since");
3475 const char *inm = mg_get_header(conn, "If-None-Match");
3476 construct_etag(etag, sizeof(etag), filep);
3477 return (inm != NULL && !mg_strcasecmp(etag, inm)) ||
3478 (ims != NULL && filep->modification_time <= parse_date_string(ims));
3479}
3480
3481static int forward_body_data(struct mg_connection *conn, FILE *fp,
3482 SOCKET sock, SSL *ssl) {
3483 const char *expect, *body;
3484 char buf[MG_BUF_LEN];
3485 int nread, buffered_len, success = 0;
3486 int64_t left;
3487
3488 expect = mg_get_header(conn, "Expect");
3489 assert(fp != NULL);
3490
3491 if (conn->content_len == INT64_MAX) {
3492 send_http_error(conn, 411, "Length Required", "%s", "");
3493 } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {
3494 send_http_error(conn, 417, "Expectation Failed", "%s", "");
3495 } else {
3496 if (expect != NULL) {
3497 (void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
3498 }
3499
3500 buffered_len = conn->data_len - conn->request_len;
3501 body = conn->buf + conn->request_len;
3502 assert(buffered_len >= 0);
3503
3504 if (buffered_len > 0) {
3505 if ((int64_t) buffered_len > conn->content_len) {
3506 buffered_len = (int) conn->content_len;
3507 }
3508 push(fp, sock, ssl, body, (int64_t) buffered_len);
3509 memmove((char *) body, body + buffered_len, buffered_len);
3510 conn->data_len -= buffered_len;
3511 }
3512
3513 nread = 0;
3514 while (conn->num_bytes_read < conn->content_len + conn->request_len) {
3515 left = left_to_read(conn);
3516 if (left > (int64_t) sizeof(buf)) {
3517 left = sizeof(buf);
3518 }
3519 nread = pull(NULL, conn, buf, (int) left);
3520 if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) {
3521 break;
3522 }
3523 }
3524
3525 if (left_to_read(conn) == 0) {
3526 success = nread >= 0;
3527 }
3528
3529 // Each error code path in this function must send an error
3530 if (!success) {
3531 send_http_error(conn, 577, http_500_error, "%s", "");
3532 }
3533 }
3534
3535 return success;
3536}
3537
3538#if !defined(NO_CGI)
3539// This structure helps to create an environment for the spawned CGI program.
3540// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
3541// last element must be NULL.
3542// However, on Windows there is a requirement that all these VARIABLE=VALUE\0
3543// strings must reside in a contiguous buffer. The end of the buffer is
3544// marked by two '\0' characters.
3545// We satisfy both worlds: we create an envp array (which is vars), all
3546// entries are actually pointers inside buf.
3549 char buf[CGI_ENVIRONMENT_SIZE]; // Environment buffer
3550 int len; // Space taken
3551 char *vars[MAX_CGI_ENVIR_VARS]; // char **envp
3552 int nvars; // Number of variables
3553};
3554
3555static char *addenv(struct cgi_env_block *block,
3556 PRINTF_FORMAT_STRING(const char *fmt), ...)
3557 PRINTF_ARGS(2, 3);
3558
3559// Append VARIABLE=VALUE\0 string to the buffer, and add a respective
3560// pointer into the vars array.
3561static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
3562 int n, space;
3563 char *added;
3564 va_list ap;
3565
3566 // Calculate how much space is left in the buffer
3567 space = sizeof(block->buf) - block->len - 2;
3568 assert(space >= 0);
3569
3570 // Make a pointer to the free space int the buffer
3571 added = block->buf + block->len;
3572
3573 // Copy VARIABLE=VALUE\0 string into the free space
3574 va_start(ap, fmt);
3575 n = mg_vsnprintf(added, (size_t) space, fmt, ap);
3576 va_end(ap);
3577
3578 // Make sure we do not overflow buffer and the envp array
3579 if (n > 0 && n + 1 < space &&
3580 block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
3581 // Append a pointer to the added string into the envp array
3582 block->vars[block->nvars++] = added;
3583 // Bump up used length counter. Include \0 terminator
3584 block->len += n + 1;
3585 } else {
3586 cry(block->conn, "%s: CGI env buffer truncated for [%s]", __func__, fmt);
3587 }
3588
3589 return added;
3590}
3591
3593 const char *prog,
3594 struct cgi_env_block *blk) {
3595 const struct mg_request_info *ri = &conn->request_info;
3596 const char *s, *slash;
3597 struct vec var_vec;
3598 char *p, src_addr[IP_ADDR_STR_LEN];
3599 int i;
3600
3601 blk->len = blk->nvars = 0;
3602 blk->conn = conn;
3603 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
3604
3605 addenv(blk, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]);
3606 addenv(blk, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
3607 addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
3608 addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", mg_version());
3609
3610 // Prepare the environment block
3611 addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
3612 addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
3613 addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP
3614
3615 // TODO(lsm): fix this for IPv6 case
3616 addenv(blk, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port));
3617
3618 addenv(blk, "REQUEST_METHOD=%s", ri->request_method);
3619 addenv(blk, "REMOTE_ADDR=%s", src_addr);
3620 addenv(blk, "REMOTE_PORT=%d", ri->remote_port);
3621 addenv(blk, "REQUEST_URI=%s%s%s", ri->uri,
3622 ri->query_string == NULL ? "" : "?",
3623 ri->query_string == NULL ? "" : ri->query_string);
3624
3625 // SCRIPT_NAME
3626 if (conn->path_info != NULL) {
3627 addenv(blk, "SCRIPT_NAME=%.*s",
3628 (int) (strlen(ri->uri) - strlen(conn->path_info)), ri->uri);
3629 addenv(blk, "PATH_INFO=%s", conn->path_info);
3630 } else {
3631 s = strrchr(prog, '/');
3632 slash = strrchr(ri->uri, '/');
3633 addenv(blk, "SCRIPT_NAME=%.*s%s",
3634 slash == NULL ? 0 : (int) (slash - ri->uri), ri->uri,
3635 s == NULL ? prog : s);
3636 }
3637
3638 addenv(blk, "SCRIPT_FILENAME=%s", prog);
3639 addenv(blk, "PATH_TRANSLATED=%s", prog);
3640 addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
3641
3642 if ((s = mg_get_header(conn, "Content-Type")) != NULL)
3643 addenv(blk, "CONTENT_TYPE=%s", s);
3644
3645 if (ri->query_string != NULL) {
3646 addenv(blk, "QUERY_STRING=%s", ri->query_string);
3647 }
3648
3649 if ((s = mg_get_header(conn, "Content-Length")) != NULL)
3650 addenv(blk, "CONTENT_LENGTH=%s", s);
3651
3652 if ((s = getenv("PATH")) != NULL)
3653 addenv(blk, "PATH=%s", s);
3654
3655#if defined(_WIN32)
3656 if ((s = getenv("COMSPEC")) != NULL) {
3657 addenv(blk, "COMSPEC=%s", s);
3658 }
3659 if ((s = getenv("SYSTEMROOT")) != NULL) {
3660 addenv(blk, "SYSTEMROOT=%s", s);
3661 }
3662 if ((s = getenv("SystemDrive")) != NULL) {
3663 addenv(blk, "SystemDrive=%s", s);
3664 }
3665 if ((s = getenv("ProgramFiles")) != NULL) {
3666 addenv(blk, "ProgramFiles=%s", s);
3667 }
3668 if ((s = getenv("ProgramFiles(x86)")) != NULL) {
3669 addenv(blk, "ProgramFiles(x86)=%s", s);
3670 }
3671 if ((s = getenv("CommonProgramFiles(x86)")) != NULL) {
3672 addenv(blk, "CommonProgramFiles(x86)=%s", s);
3673 }
3674#else
3675 if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
3676 addenv(blk, "LD_LIBRARY_PATH=%s", s);
3677#endif // _WIN32
3678
3679 if ((s = getenv("PERLLIB")) != NULL)
3680 addenv(blk, "PERLLIB=%s", s);
3681
3682 if (ri->remote_user != NULL) {
3683 addenv(blk, "REMOTE_USER=%s", ri->remote_user);
3684 addenv(blk, "%s", "AUTH_TYPE=Digest");
3685 }
3686
3687 // Add all headers as HTTP_* variables
3688 for (i = 0; i < ri->num_headers; i++) {
3689 p = addenv(blk, "HTTP_%s=%s",
3690 ri->http_headers[i].name, ri->http_headers[i].value);
3691
3692 // Convert variable name into uppercase, and change - to _
3693 for (; *p != '=' && *p != '\0'; p++) {
3694 if (*p == '-')
3695 *p = '_';
3696 *p = (char) toupper(* (unsigned char *) p);
3697 }
3698 }
3699
3700 // Add user-specified variables
3701 s = conn->ctx->config[CGI_ENVIRONMENT];
3702 while ((s = next_option(s, &var_vec, NULL)) != NULL) {
3703 addenv(blk, "%.*s", (int) var_vec.len, var_vec.ptr);
3704 }
3705
3706 blk->vars[blk->nvars++] = NULL;
3707 blk->buf[blk->len++] = '\0';
3708
3709 assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
3710 assert(blk->len > 0);
3711 assert(blk->len < (int) sizeof(blk->buf));
3712}
3713
3714static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
3715 int headers_len, data_len, i, fdin[2], fdout[2];
3716 const char *status, *status_text;
3717 char buf[16384], *pbuf, dir[PATH_MAX], *p;
3718 struct mg_request_info ri;
3719 struct cgi_env_block blk;
3720 FILE *in = NULL, *out = NULL;
3721 pid_t pid = (pid_t) -1;
3722
3724
3725 // CGI must be executed in its own directory. 'dir' must point to the
3726 // directory containing executable program, 'p' must point to the
3727 // executable program name relative to 'dir'.
3728 (void) mg_snprintf(dir, sizeof(dir), "%s", prog);
3729 if ((p = strrchr(dir, '/')) != NULL) {
3730 *p++ = '\0';
3731 } else {
3732 dir[0] = '.', dir[1] = '\0';
3733 p = (char *) prog;
3734 }
3735
3736 if (pipe(fdin) != 0 || pipe(fdout) != 0) {
3738 "Cannot create CGI pipe: %s", strerror(ERRNO));
3739 goto done;
3740 }
3741
3742 pid = spawn_process(conn, p, blk.buf, blk.vars, fdin[0], fdout[1], dir);
3743 if (pid == (pid_t) -1) {
3745 "Cannot spawn CGI process [%s]: %s", prog, strerror(ERRNO));
3746 goto done;
3747 }
3748
3749 // Make sure child closes all pipe descriptors. It must dup them to 0,1
3754
3755 // Parent closes only one side of the pipes.
3756 // If we don't mark them as closed, close() attempt before
3757 // return from this function throws an exception on Windows.
3758 // Windows does not like when closed descriptor is closed again.
3759 (void) close(fdin[0]);
3760 (void) close(fdout[1]);
3761 fdin[0] = fdout[1] = -1;
3762
3763
3764 if ((in = fdopen(fdin[1], "wb")) == NULL ||
3765 (out = fdopen(fdout[0], "rb")) == NULL) {
3767 "fopen: %s", strerror(ERRNO));
3768 goto done;
3769 }
3770
3771 setbuf(in, NULL);
3772 setbuf(out, NULL);
3773
3774 // Send POST data to the CGI process if needed
3775 if (!strcmp(conn->request_info.request_method, "POST") &&
3777 goto done;
3778 }
3779
3780 // Close so child gets an EOF.
3781 fclose(in);
3782 in = NULL;
3783 fdin[1] = -1;
3784
3785 // Now read CGI reply into a buffer. We need to set correct
3786 // status code, thus we need to see all HTTP headers first.
3787 // Do not send anything back to client, until we buffer in all
3788 // HTTP headers.
3789 data_len = 0;
3790 headers_len = read_request(out, conn, buf, sizeof(buf), &data_len);
3791 if (headers_len <= 0) {
3793 "CGI program sent malformed or too big (>%u bytes) "
3794 "HTTP headers: [%.*s]",
3795 (unsigned) sizeof(buf), data_len, buf);
3796 goto done;
3797 }
3798 pbuf = buf;
3799 buf[headers_len - 1] = '\0';
3801
3802 // Make up and send the status line
3803 status_text = "OK";
3804 if ((status = get_header(&ri, "Status")) != NULL) {
3807 while (isdigit(* (unsigned char *) status_text) || *status_text == ' ') {
3808 status_text++;
3809 }
3810 } else if (get_header(&ri, "Location") != NULL) {
3811 conn->status_code = 302;
3812 } else {
3813 conn->status_code = 200;
3814 }
3815 if (get_header(&ri, "Connection") != NULL &&
3816 !mg_strcasecmp(get_header(&ri, "Connection"), "keep-alive")) {
3817 conn->must_close = 1;
3818 }
3819 (void) mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code,
3820 status_text);
3821
3822 // Send headers
3823 for (i = 0; i < ri.num_headers; i++) {
3824 mg_printf(conn, "%s: %s\r\n",
3825 ri.http_headers[i].name, ri.http_headers[i].value);
3826 }
3827 mg_write(conn, "\r\n", 2);
3828
3829 // Send chunk of data that may have been read after the headers
3831 (size_t)(data_len - headers_len));
3832
3833 // Read the rest of CGI output and send to the client
3835
3836done:
3837 if (pid != (pid_t) -1) {
3838 kill(pid, SIGKILL);
3839 }
3840 if (fdin[0] != -1) {
3841 close(fdin[0]);
3842 }
3843 if (fdout[1] != -1) {
3844 close(fdout[1]);
3845 }
3846
3847 if (in != NULL) {
3848 fclose(in);
3849 } else if (fdin[1] != -1) {
3850 close(fdin[1]);
3851 }
3852
3853 if (out != NULL) {
3854 fclose(out);
3855 } else if (fdout[0] != -1) {
3856 close(fdout[0]);
3857 }
3858}
3859#endif // !NO_CGI
3860
3861// For a given PUT path, create all intermediate subdirectories
3862// for given path. Return 0 if the path itself is a directory,
3863// or -1 on error, 1 if OK.
3864static int put_dir(const char *path) {
3865 char buf[PATH_MAX];
3866 const char *s, *p;
3868 int len, res = 1;
3869
3870 for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
3871 len = p - path;
3872 if (len >= (int) sizeof(buf)) {
3873 res = -1;
3874 break;
3875 }
3876 memcpy(buf, path, len);
3877 buf[len] = '\0';
3878
3879 // Try to create intermediate directory
3880 DEBUG_TRACE(("mkdir(%s)", buf));
3881 if (!mg_stat(buf, &file) && mg_mkdir(buf, 0755) != 0) {
3882 res = -1;
3883 break;
3884 }
3885
3886 // Is path itself a directory?
3887 if (p[1] == '\0') {
3888 res = 0;
3889 }
3890 }
3891
3892 return res;
3893}
3894
3895static void mkcol(struct mg_connection *conn, const char *path) {
3896 int rc, body_len;
3897 struct de de;
3898
3899 memset(&de.file, 0, sizeof(de.file));
3900 mg_stat(path, &de.file);
3901
3903 send_http_error(conn, 405, "Method Not Allowed",
3904 "mkcol(%s): %s", path, strerror(ERRNO));
3905 return;
3906 }
3907
3908 body_len = conn->data_len - conn->request_len;
3909 if(body_len > 0) {
3910 send_http_error(conn, 415, "Unsupported media type",
3911 "mkcol(%s): %s", path, strerror(ERRNO));
3912 return;
3913 }
3914
3915 rc = mg_mkdir(path, 0755);
3916
3917 if (rc == 0) {
3918 conn->status_code = 201;
3919 mg_printf(conn, "HTTP/1.1 %d Created\r\n\r\n", conn->status_code);
3920 } else if (rc == -1) {
3921 if(errno == EEXIST)
3922 send_http_error(conn, 405, "Method Not Allowed",
3923 "mkcol(%s): %s", path, strerror(ERRNO));
3924 else if(errno == EACCES)
3925 send_http_error(conn, 403, "Forbidden",
3926 "mkcol(%s): %s", path, strerror(ERRNO));
3927 else if(errno == ENOENT)
3928 send_http_error(conn, 409, "Conflict",
3929 "mkcol(%s): %s", path, strerror(ERRNO));
3930 else
3932 "fopen(%s): %s", path, strerror(ERRNO));
3933 }
3934}
3935
3936static void put_file(struct mg_connection *conn, const char *path) {
3938 FILE *fp;
3939 const char *range;
3940 int64_t r1, r2;
3941 int rc;
3942
3943 conn->status_code = mg_stat(path, &file) ? 200 : 201;
3944
3945 if ((rc = put_dir(path)) == 0) {
3946 mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", conn->status_code);
3947 } else if (rc == -1) {
3949 "put_dir(%s): %s", path, strerror(ERRNO));
3950 } else if ((fp = mg_fopen(path, "wb+")) == NULL) {
3951 fclose(fp);
3953 "fopen(%s): %s", path, strerror(ERRNO));
3954 } else {
3956 range = mg_get_header(conn, "Content-Range");
3957 r1 = r2 = 0;
3958 if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
3959 conn->status_code = 206;
3960 fseeko(fp, r1, SEEK_SET);
3961 }
3962 if (!forward_body_data(conn, fp, INVALID_SOCKET, NULL)) {
3963 conn->status_code = 500;
3964 }
3965 mg_printf(conn, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n",
3966 conn->status_code);
3967 fclose(fp);
3968 }
3969}
3970
3971static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);
3972static void do_ssi_include(struct mg_connection *conn, const char *ssi,
3973 char *tag, int include_level) {
3974 char file_name[MG_BUF_LEN], path[PATH_MAX], *p;
3975 FILE *fp;
3976
3977 // sscanf() is safe here, since send_ssi_file() also uses buffer
3978 // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN.
3979 if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
3980 // File name is relative to the webserver root
3981 (void) mg_snprintf(path, sizeof(path), "%s%c%s",
3982 conn->ctx->config[DOCUMENT_ROOT], '/', file_name);
3983 } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) {
3984 // File name is relative to the webserver working directory
3985 // or it is absolute system path
3986 (void) mg_snprintf(path, sizeof(path), "%s", file_name);
3987 } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 ||
3988 sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
3989 // File name is relative to the currect document
3990 (void) mg_snprintf(path, sizeof(path), "%s", ssi);
3991 if ((p = strrchr(path, '/')) != NULL) {
3992 p[1] = '\0';
3993 }
3994 (void) mg_snprintf(path + strlen(path),
3995 sizeof(path) - strlen(path), "%s", file_name);
3996 } else {
3997 cry(conn, "Bad SSI #include: [%s]", tag);
3998 return;
3999 }
4000
4001 if ((fp = mg_fopen(path, "rb")) == NULL) {
4002 cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
4003 tag, path, strerror(ERRNO));
4004 } else {
4007 strlen(conn->ctx->config[SSI_EXTENSIONS]), path) > 0) {
4008 send_ssi_file(conn, path, fp, include_level + 1);
4009 } else {
4010 send_file_data(conn, fp, 0, INT64_MAX);
4011 }
4012 fclose(fp);
4013 }
4014}
4015
4016#if !defined(NO_POPEN)
4017static void do_ssi_exec(struct mg_connection *conn, char *tag) {
4018 char cmd[MG_BUF_LEN];
4019 FILE *fp;
4020
4021 if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
4022 cry(conn, "Bad SSI #exec: [%s]", tag);
4023 } else if ((fp = popen(cmd, "r")) == NULL) {
4024 cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
4025 } else {
4026 send_file_data(conn, fp, 0, INT64_MAX);
4027 pclose(fp);
4028 }
4029}
4030#endif // !NO_POPEN
4031
4032static void send_ssi_file(struct mg_connection *conn, const char *path,
4033 FILE *fp, int include_level) {
4034 char buf[MG_BUF_LEN];
4035 int ch, offset, len, in_ssi_tag;
4036
4037 if (include_level > 10) {
4038 cry(conn, "SSI #include level is too deep (%s)", path);
4039 return;
4040 }
4041
4042 in_ssi_tag = len = offset = 0;
4043 while ((ch = fgetc(fp)) != EOF) {
4044 if (in_ssi_tag && ch == '>') {
4045 in_ssi_tag = 0;
4046 buf[len++] = (char) ch;
4047 buf[len] = '\0';
4048 assert(len <= (int) sizeof(buf));
4049 if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
4050 // Not an SSI tag, pass it
4051 (void) mg_write(conn, buf, (size_t) len);
4052 } else {
4053 if (!memcmp(buf + 5, "include", 7)) {
4054 do_ssi_include(conn, path, buf + 12, include_level);
4055#if !defined(NO_POPEN)
4056 } else if (!memcmp(buf + 5, "exec", 4)) {
4057 do_ssi_exec(conn, buf + 9);
4058#endif // !NO_POPEN
4059 } else {
4060 cry(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
4061 }
4062 }
4063 len = 0;
4064 } else if (in_ssi_tag) {
4065 if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
4066 // Not an SSI tag
4067 in_ssi_tag = 0;
4068 } else if (len == (int) sizeof(buf) - 2) {
4069 cry(conn, "%s: SSI tag is too large", path);
4070 len = 0;
4071 }
4072 buf[len++] = ch & 0xff;
4073 } else if (ch == '<') {
4074 in_ssi_tag = 1;
4075 if (len > 0) {
4076 mg_write(conn, buf, (size_t) len);
4077 }
4078 len = 0;
4079 buf[len++] = ch & 0xff;
4080 } else {
4081 buf[len++] = ch & 0xff;
4082 if (len == (int) sizeof(buf)) {
4083 mg_write(conn, buf, (size_t) len);
4084 len = 0;
4085 }
4086 }
4087 }
4088
4089 // Send the rest of buffered data
4090 if (len > 0) {
4091 mg_write(conn, buf, (size_t) len);
4092 }
4093}
4094
4096 const char *path) {
4097 struct vec mime_vec;
4098 FILE *fp;
4099
4100 if ((fp = mg_fopen(path, "rb")) == NULL) {
4101 send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path,
4102 strerror(ERRNO));
4103 } else {
4104 conn->must_close = 1;
4106 get_mime_type(conn->ctx, path, &mime_vec);
4107 mg_printf(conn, "HTTP/1.1 200 OK\r\n"
4108 "Content-Type: %.*s\r\n"
4109 "Connection: close\r\n\r\n",
4110 (int) mime_vec.len, mime_vec.ptr);
4111 send_ssi_file(conn, path, fp, 0);
4112 fclose(fp);
4113 }
4114}
4115
4116static void handle_options_request(struct mg_connection *conn) {
4117 static const char reply[] = "HTTP/1.1 200 OK\r\n"
4118 "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\n"
4119 "DAV: 1\r\n\r\n";
4120
4121 conn->status_code = 200;
4122 mg_write(conn, reply, sizeof(reply) - 1);
4123}
4124
4125// Writes PROPFIND properties for a collection element
4126static void print_props(struct mg_connection *conn, const char* uri,
4127 struct file *filep) {
4128 char mtime[64];
4129 gmt_time_string(mtime, sizeof(mtime), &filep->modification_time);
4130 conn->num_bytes_sent += mg_printf(conn,
4131 "<d:response>"
4132 "<d:href>%s</d:href>"
4133 "<d:propstat>"
4134 "<d:prop>"
4135 "<d:resourcetype>%s</d:resourcetype>"
4136 "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
4137 "<d:getlastmodified>%s</d:getlastmodified>"
4138 "</d:prop>"
4139 "<d:status>HTTP/1.1 200 OK</d:status>"
4140 "</d:propstat>"
4141 "</d:response>\n",
4142 uri,
4143 filep->is_directory ? "<d:collection/>" : "",
4144 filep->size,
4145 mtime);
4146}
4147
4148static void print_dav_dir_entry(struct de *de, void *data) {
4149 char href[PATH_MAX];
4150 char href_encoded[PATH_MAX];
4151 struct mg_connection *conn = (struct mg_connection *) data;
4152 mg_snprintf(href, sizeof(href), "%s%s",
4153 conn->request_info.uri, de->file_name);
4155 print_props(conn, href_encoded, &de->file);
4156}
4157
4158static void handle_propfind(struct mg_connection *conn, const char *path,
4159 struct file *filep) {
4160 const char *depth = mg_get_header(conn, "Depth");
4161
4162 conn->must_close = 1;
4163 conn->status_code = 207;
4164 mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
4165 "Connection: close\r\n"
4166 "Content-Type: text/xml; charset=utf-8\r\n\r\n");
4167
4168 conn->num_bytes_sent += mg_printf(conn,
4169 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
4170 "<d:multistatus xmlns:d='DAV:'>\n");
4171
4172 // Print properties for the requested resource itself
4173 print_props(conn, conn->request_info.uri, filep);
4174
4175 // If it is a directory, print directory entries too if Depth is not 0
4176 if (filep->is_directory &&
4178 (depth == NULL || strcmp(depth, "0") != 0)) {
4179 scan_directory(conn, path, conn, &print_dav_dir_entry);
4180 }
4181
4182 conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>");
4183}
4184
4185#if defined(USE_WEBSOCKET)
4186
4187// START OF SHA-1 code
4188// Copyright(c) By Steve Reid <steve@edmweb.com>
4189#define SHA1HANDSOFF
4190#if defined(__sun)
4191#include "solarisfixes.h"
4192#endif
4193
4194union char64long16 { unsigned char c[64]; uint32_t l[16]; };
4195
4196#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
4197
4198static uint32_t blk0(union char64long16 *block, int i) {
4199 // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN
4200 if (!is_big_endian()) {
4201 block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
4202 (rol(block->l[i], 8) & 0x00FF00FF);
4203 }
4204 return block->l[i];
4205}
4206
4207#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
4208 ^block->l[(i+2)&15]^block->l[i&15],1))
4209#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
4210#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
4211#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
4212#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
4213#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
4214
4215typedef struct {
4216 uint32_t state[5];
4217 uint32_t count[2];
4218 unsigned char buffer[64];
4219} SHA1_CTX;
4220
4221static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) {
4222 uint32_t a, b, c, d, e;
4223 union char64long16 block[1];
4224
4225 memcpy(block, buffer, 64);
4226 a = state[0];
4227 b = state[1];
4228 c = state[2];
4229 d = state[3];
4230 e = state[4];
4231 R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
4232 R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
4233 R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
4234 R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
4235 R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
4236 R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
4237 R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
4238 R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
4239 R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
4240 R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
4241 R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
4242 R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
4243 R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
4244 R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
4245 R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
4246 R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
4247 R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
4248 R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
4249 R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
4250 R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
4251 state[0] += a;
4252 state[1] += b;
4253 state[2] += c;
4254 state[3] += d;
4255 state[4] += e;
4256 a = b = c = d = e = 0;
4257 memset(block, '\0', sizeof(block));
4258}
4259
4260static void SHA1Init(SHA1_CTX* context) {
4261 context->state[0] = 0x67452301;
4262 context->state[1] = 0xEFCDAB89;
4263 context->state[2] = 0x98BADCFE;
4264 context->state[3] = 0x10325476;
4265 context->state[4] = 0xC3D2E1F0;
4266 context->count[0] = context->count[1] = 0;
4267}
4268
4269static void SHA1Update(SHA1_CTX* context, const unsigned char* data,
4270 uint32_t len) {
4271 uint32_t i, j;
4272
4273 j = context->count[0];
4274 if ((context->count[0] += len << 3) < j)
4275 context->count[1]++;
4276 context->count[1] += (len>>29);
4277 j = (j >> 3) & 63;
4278 if ((j + len) > 63) {
4279 memcpy(&context->buffer[j], data, (i = 64-j));
4280 SHA1Transform(context->state, context->buffer);
4281 for ( ; i + 63 < len; i += 64) {
4282 SHA1Transform(context->state, &data[i]);
4283 }
4284 j = 0;
4285 }
4286 else i = 0;
4287 memcpy(&context->buffer[j], &data[i], len - i);
4288}
4289
4290static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) {
4291 unsigned i;
4292 unsigned char finalcount[8], c;
4293
4294 for (i = 0; i < 8; i++) {
4295 finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
4296 >> ((3-(i & 3)) * 8) ) & 255);
4297 }
4298 c = 0200;
4299 SHA1Update(context, &c, 1);
4300 while ((context->count[0] & 504) != 448) {
4301 c = 0000;
4302 SHA1Update(context, &c, 1);
4303 }
4304 SHA1Update(context, finalcount, 8);
4305 for (i = 0; i < 20; i++) {
4306 digest[i] = (unsigned char)
4307 ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
4308 }
4309 memset(context, '\0', sizeof(*context));
4310 memset(&finalcount, '\0', sizeof(finalcount));
4311}
4312// END OF SHA1 CODE
4313
4314static void base64_encode(const unsigned char *src, int src_len, char *dst) {
4315 static const char *b64 =
4316 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4317 int i, j, a, b, c;
4318
4319 for (i = j = 0; i < src_len; i += 3) {
4320 a = src[i];
4321 b = i + 1 >= src_len ? 0 : src[i + 1];
4322 c = i + 2 >= src_len ? 0 : src[i + 2];
4323
4324 dst[j++] = b64[a >> 2];
4325 dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
4326 if (i + 1 < src_len) {
4327 dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
4328 }
4329 if (i + 2 < src_len) {
4330 dst[j++] = b64[c & 63];
4331 }
4332 }
4333 while (j % 4 != 0) {
4334 dst[j++] = '=';
4335 }
4336 dst[j++] = '\0';
4337}
4338
4339void mg_websocket_handshake(struct mg_connection *conn) {
4340 static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
4341 char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
4343
4344 mg_snprintf(buf, sizeof(buf), "%s%s",
4345 mg_get_header(conn, "Sec-WebSocket-Key"), magic);
4346 SHA1Init(&sha_ctx);
4347 SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf));
4348 SHA1Final((unsigned char *) sha, &sha_ctx);
4349 base64_encode((unsigned char *) sha, sizeof(sha), b64_sha);
4350 mg_printf(conn, "%s%s%s",
4351 "HTTP/1.1 101 Switching Protocols\r\n"
4352 "Upgrade: websocket\r\n"
4353 "Connection: Upgrade\r\n"
4354 "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n");
4355}
4356
4357int mg_websocket_read(struct mg_connection *conn, int *bits, char **data) {
4358 // Pointer to the beginning of the portion of the incoming websocket message
4359 // queue. The original websocket upgrade request is never removed,
4360 // so the queue begins after it.
4361 unsigned char *buf = (unsigned char *) conn->buf + conn->request_len;
4362 int n, stop = 0;
4363 size_t i, len, mask_len, data_len, header_len, body_len;
4364 char mask[4];
4365
4366 assert(conn->content_len == 0);
4367
4368 // Loop continuously, reading messages from the socket, invoking the callback,
4369 // and waiting repeatedly until an error occurs.
4370 while (!stop) {
4371 header_len = 0;
4372 // body_len is the length of the entire queue in bytes
4373 // len is the length of the current message
4374 // data_len is the length of the current message's data payload
4375 // header_len is the length of the current message's header
4376 if ((body_len = conn->data_len - conn->request_len) >= 2) {
4377 len = buf[1] & 127;
4378 mask_len = buf[1] & 128 ? 4 : 0;
4380 data_len = len;
4381 header_len = 2 + mask_len;
4382 } else if (len == 126 && body_len >= 4 + mask_len) {
4383 header_len = 4 + mask_len;
4384 data_len = ((((int) buf[2]) << 8) + buf[3]);
4385 } else if (body_len >= 10 + mask_len) {
4386 header_len = 10 + mask_len;
4387 data_len = (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) +
4388 htonl(* (uint32_t *) &buf[6]);
4389 }
4390 }
4391
4392 // Data layout is as follows:
4393 // conn->buf buf
4394 // v v frame1 | frame2
4395 // |---------------------|----------------|--------------|-------
4396 // | |<--header_len-->|<--data_len-->|
4397 // |<-conn->request_len->|<-----body_len----------->|
4398 // |<-------------------conn->data_len------------->|
4399
4400 if (header_len > 0) {
4401 // Allocate space to hold websocket payload
4402 if ((*data = malloc(data_len)) == NULL) {
4403 // Allocation failed, exit the loop and then close the connection
4404 // TODO: notify user about the failure
4405 data_len = 0;
4406 break;
4407 }
4408
4409 // Save mask and bits, otherwise it may be clobbered by memmove below
4410 *bits = buf[0];
4412
4413 // Read frame payload into the allocated buffer.
4414 assert(body_len >= header_len);
4415 if (data_len + header_len > body_len) {
4416 len = body_len - header_len;
4417 memcpy(*data, buf + header_len, len);
4418 // TODO: handle pull error
4419 pull_all(NULL, conn, *data + len, data_len - len);
4420 conn->data_len = conn->request_len;
4421 } else {
4422 len = data_len + header_len;
4423 memcpy(*data, buf + header_len, data_len);
4424 memmove(buf, buf + len, body_len - len);
4425 conn->data_len -= len;
4426 }
4427
4428 // Apply mask if necessary
4429 if (mask_len > 0) {
4430 for (i = 0; i < data_len; i++) {
4431 (*data)[i] ^= mask[i % 4];
4432 }
4433 }
4434
4435 return data_len;
4436 } else {
4437 // Buffering websocket request
4438 if ((n = pull(NULL, conn, conn->buf + conn->data_len,
4439 conn->buf_size - conn->data_len)) <= 0) {
4440 break;
4441 }
4442 conn->data_len += n;
4443 }
4444 }
4445
4446 return 0;
4447}
4448
4449int mg_websocket_write(struct mg_connection* conn, int opcode,
4450 const char *data, size_t data_len) {
4451 unsigned char *copy;
4452 size_t copy_len = 0;
4453 int retval = -1;
4454
4455 if ((copy = (unsigned char *) malloc(data_len + 10)) == NULL) {
4456 return -1;
4457 }
4458
4459 copy[0] = 0x80 + (opcode & 0x0f);
4460
4461 // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2
4462 if (data_len < 126) {
4463 // Inline 7-bit length field
4464 copy[1] = data_len;
4465 memcpy(copy + 2, data, data_len);
4466 copy_len = 2 + data_len;
4467 } else if (data_len <= 0xFFFF) {
4468 // 16-bit length field
4469 copy[1] = 126;
4470 * (uint16_t *) (copy + 2) = htons(data_len);
4471 memcpy(copy + 4, data, data_len);
4472 copy_len = 4 + data_len;
4473 } else {
4474 // 64-bit length field
4475 copy[1] = 127;
4476 * (uint32_t *) (copy + 2) = htonl((uint64_t) data_len >> 32);
4477 * (uint32_t *) (copy + 6) = htonl(data_len & 0xffffffff);
4478 memcpy(copy + 10, data, data_len);
4479 copy_len = 10 + data_len;
4480 }
4481
4482 // Not thread safe
4483 if (copy_len > 0) {
4484 retval = mg_write(conn, copy, copy_len);
4485 }
4486 free(copy);
4487
4488 return retval;
4489}
4490#endif // !USE_WEBSOCKET
4491
4492static int isbyte(int n) {
4493 return n >= 0 && n <= 255;
4494}
4495
4496static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
4497 int n, a, b, c, d, slash = 32, len = 0;
4498
4499 if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 ||
4500 sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) &&
4501 isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) &&
4502 slash >= 0 && slash < 33) {
4503 len = n;
4504 *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | d;
4505 *mask = slash ? 0xffffffffU << (32 - slash) : 0;
4506 }
4507
4508 return len;
4509}
4510
4511static int set_throttle(const char *spec, uint32_t remote_ip, const char *uri) {
4512 int throttle = 0;
4513 struct vec vec, val;
4514 uint32_t net, mask;
4515 char mult;
4516 double v;
4517
4518 while ((spec = next_option(spec, &vec, &val)) != NULL) {
4519 mult = ',';
4520 if (sscanf(val.ptr, "%lf%c", &v, &mult) < 1 || v < 0 ||
4521 (lowercase(&mult) != 'k' && lowercase(&mult) != 'm' && mult != ',')) {
4522 continue;
4523 }
4524 v *= lowercase(&mult) == 'k' ? 1024 : lowercase(&mult) == 'm' ? 1048576 : 1;
4525 if (vec.len == 1 && vec.ptr[0] == '*') {
4526 throttle = (int) v;
4527 } else if (parse_net(vec.ptr, &net, &mask) > 0) {
4528 if ((remote_ip & mask) == net) {
4529 throttle = (int) v;
4530 }
4531 } else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
4532 throttle = (int) v;
4533 }
4534 }
4535
4536 return throttle;
4537}
4538
4539static uint32_t get_remote_ip(const struct mg_connection *conn) {
4540 return ntohl(* (uint32_t *) &conn->client.rsa.sin.sin_addr);
4541}
4542
4543FILE *mg_upload(struct mg_connection *conn, const char *destination_dir,
4544 char *path, int path_len) {
4545 const char *content_type_header, *boundary_start;
4546 char *buf, fname[1024], boundary[100], *s;
4547 int bl, n, i, j, headers_len, boundary_len, eof, buf_len, to_read, len = 0;
4548 FILE *fp;
4549
4550 // Request looks like this:
4551 //
4552 // POST /upload HTTP/1.1
4553 // Host: 127.0.0.1:8080
4554 // Content-Length: 244894
4555 // Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryRVr
4556 //
4557 // ------WebKitFormBoundaryRVr
4558 // Content-Disposition: form-data; name="file"; filename="accum.png"
4559 // Content-Type: image/png
4560 //
4561 // <89>PNG
4562 // <PNG DATA>
4563 // ------WebKitFormBoundaryRVr
4564
4565 // Extract boundary string from the Content-Type header
4566 if ((content_type_header = mg_get_header(conn, "Content-Type")) == NULL ||
4568 "boundary=")) == NULL ||
4569 (sscanf(boundary_start, "boundary=\"%99[^\"]\"", boundary) == 0 &&
4570 sscanf(boundary_start, "boundary=%99s", boundary) == 0) ||
4571 boundary[0] == '\0') {
4572 return NULL;
4573 }
4574
4575 boundary_len = strlen(boundary);
4576 bl = boundary_len + 4; // \r\n--<boundary>
4577
4578 // buf
4579 // conn->buf |<--------- buf_len ------>|
4580 // |=================|==========|===============|
4581 // |<--request_len-->|<--len--->| |
4582 // |<-----------data_len------->| conn->buf + conn->buf_size
4583
4584 buf = conn->buf + conn->request_len;
4585 buf_len = conn->buf_size - conn->request_len;
4586 len = conn->data_len - conn->request_len;
4587
4588 for (;;) {
4589 // Pull in headers
4590 assert(len >= 0 && len <= buf_len);
4591 to_read = buf_len - len;
4592 if (to_read > left_to_read(conn)) {
4593 to_read = (int) left_to_read(conn);
4594 }
4595 while (len < buf_len &&
4596 (n = pull(NULL, conn, buf + len, to_read)) > 0) {
4597 len += n;
4598 }
4599 if ((headers_len = get_request_len(buf, len)) <= 0) {
4600 break;
4601 }
4602
4603 // Fetch file name.
4604 fname[0] = '\0';
4605 for (i = j = 0; i < headers_len; i++) {
4606 if (buf[i] == '\r' && buf[i + 1] == '\n') {
4607 buf[i] = buf[i + 1] = '\0';
4608 // TODO(lsm): don't expect filename to be the 3rd field,
4609 // parse the header properly instead.
4610 sscanf(&buf[j], "Content-Disposition: %*s %*s filename=\"%1023[^\"]",
4611 fname);
4612 j = i + 2;
4613 }
4614 }
4615
4616 // Give up if the headers are not what we expect
4617 if (fname[0] == '\0') {
4618 break;
4619 }
4620
4621 // Move data to the beginning of the buffer
4622 assert(len >= headers_len);
4623 memmove(buf, &buf[headers_len], len - headers_len);
4624 len -= headers_len;
4625 conn->data_len = conn->request_len + len;
4626
4627 // We open the file with exclusive lock held. This guarantee us
4628 // there is no other thread can save into the same file simultaneously.
4629 fp = NULL;
4630
4631 // Construct destination file name. Do not allow paths to have slashes.
4632 if ((s = strrchr(fname, '/')) == NULL &&
4633 (s = strrchr(fname, '\\')) == NULL) {
4634 s = fname;
4635 }
4636
4637 // Open file in binary mode. TODO: set an exclusive lock.
4638 snprintf(path, path_len, "%s/%s", destination_dir, s);
4639 if ((fp = fopen(path, "wb")) == NULL) {
4640 break;
4641 }
4642
4643 // Read POST data, write into file until boundary is found.
4644 eof = n = 0;
4645 do {
4646 len += n;
4647 for (i = 0; i < len - bl; i++) {
4648 if (!memcmp(&buf[i], "\r\n--", 4) &&
4649 !memcmp(&buf[i + 4], boundary, boundary_len)) {
4650 // Found boundary, that's the end of file data.
4651 fwrite(buf, 1, i, fp);
4652 eof = 1;
4653 memmove(buf, &buf[i + bl], len - (i + bl));
4654 len -= i + bl;
4655 break;
4656 }
4657 }
4658 if (!eof && len > bl) {
4659 fwrite(buf, 1, len - bl, fp);
4660 memmove(buf, &buf[len - bl], bl);
4661 len = bl;
4662 }
4663 to_read = buf_len - len;
4664 if (to_read > left_to_read(conn)) {
4665 to_read = (int) left_to_read(conn);
4666 }
4667 } while (!eof && (n = pull(NULL, conn, buf + len, to_read)) > 0);
4668 conn->data_len = conn->request_len + len;
4669
4670 if (eof) {
4671 rewind(fp);
4672 return fp;
4673 } else {
4674 fclose(fp);
4675 }
4676 }
4677
4678 return NULL;
4679}
4680
4681static int is_put_or_delete_request(const struct mg_connection *conn) {
4682 const char *s = conn->request_info.request_method;
4683 return s != NULL && (!strcmp(s, "PUT") ||
4684 !strcmp(s, "DELETE") ||
4685 !strcmp(s, "MKCOL"));
4686}
4687
4688static int get_first_ssl_listener_index(const struct mg_context *ctx) {
4689 int i, index = -1;
4690 for (i = 0; index == -1 && i < ctx->num_listening_sockets; i++) {
4691 index = ctx->listening_sockets[i].is_ssl ? i : -1;
4692 }
4693 return index;
4694}
4695
4696static void redirect_to_https_port(struct mg_connection *conn, int ssl_index) {
4697 char host[1025];
4698 const char *host_header;
4699
4700 if ((host_header = mg_get_header(conn, "Host")) == NULL ||
4701 sscanf(host_header, "%1024[^:]", host) == 0) {
4702 // Cannot get host from the Host: header. Fallback to our IP address.
4703 sockaddr_to_string(host, sizeof(host), &conn->client.lsa);
4704 }
4705
4706 mg_printf(conn, "HTTP/1.1 302 Found\r\nLocation: https://%s:%d%s\r\n\r\n",
4707 host, (int) ntohs(conn->ctx->listening_sockets[ssl_index].
4708 lsa.sin.sin_port), conn->request_info.uri);
4709}
4710
4711static void handle_delete_request(struct mg_connection *conn,
4712 const char *path) {
4714
4715 if (!mg_stat(path, &file)) {
4716 send_http_error(conn, 404, "Not Found", "%s", "File not found");
4717 } else if (!file.modification_time) {
4718 send_http_error(conn, 500, http_500_error, "remove(%s): %s", path,
4719 strerror(ERRNO));
4720 } else if (file.is_directory) {
4721 remove_directory(conn, path);
4722 send_http_error(conn, 204, "No Content", "%s", "");
4723 } else if (mg_remove(path) == 0) {
4724 send_http_error(conn, 204, "No Content", "%s", "");
4725 } else {
4726 send_http_error(conn, 423, "Locked", "remove(%s): %s", path,
4727 strerror(ERRNO));
4728 }
4729}
4730
4731// This is the heart of the Mongoose's logic.
4732// This function is called when the request is read, parsed and validated,
4733// and Mongoose must decide what action to take: serve a file, or
4734// a directory, or call embedded function, etcetera.
4735static void handle_request(struct mg_connection *conn) {
4736 struct mg_request_info *ri = &conn->request_info;
4737 char path[PATH_MAX];
4738 int uri_len, ssl_index;
4740
4741 if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
4742 * ((char *) conn->request_info.query_string++) = '\0';
4743 }
4744 uri_len = (int) strlen(ri->uri);
4745 mg_url_decode(ri->uri, uri_len, (char *) ri->uri, uri_len + 1, 0);
4747 conn->throttle = set_throttle(conn->ctx->config[THROTTLE],
4748 get_remote_ip(conn), ri->uri);
4749 path[0] = '\0';
4750 convert_uri_to_file_name(conn, path, sizeof(path), &file);
4751
4752 // Perform redirect and auth checks before calling begin_request() handler.
4753 // Otherwise, begin_request() would need to perform auth checks and redirects.
4754 if (!conn->client.is_ssl && conn->client.ssl_redir &&
4755 (ssl_index = get_first_ssl_listener_index(conn->ctx)) > -1) {
4757 } else if (!strcmp(ri->request_method, "OPTIONS") && (call_user(MG_REQUEST_BEGIN, conn, (void *) ri->uri) == 1)) {
4758 // CORS "pre-flight" "OPTIONS" requests are sent without authentication, duh!
4759 } else if (!is_put_or_delete_request(conn) &&
4760 !check_authorization(conn, path)) {
4762 } else if (call_user(MG_REQUEST_BEGIN, conn, (void *) ri->uri) == 1) {
4763 // Do nothing, callback has served the request
4764 } else if (!strcmp(ri->request_method, "OPTIONS")) {
4766 } else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
4767 send_http_error(conn, 404, "Not Found", "Not Found");
4768 } else if (is_put_or_delete_request(conn) &&
4769 (is_authorized_for_put(conn) != 1)) {
4771 } else if (!strcmp(ri->request_method, "PUT")) {
4772 put_file(conn, path);
4773 } else if (!strcmp(ri->request_method, "MKCOL")) {
4774 mkcol(conn, path);
4775 } else if (!strcmp(ri->request_method, "DELETE")) {
4776 handle_delete_request(conn, path);
4777 } else if (file.modification_time == (time_t) 0 ||
4778 must_hide_file(conn, path)) {
4779 send_http_error(conn, 404, "Not Found", "%s", "File not found");
4780 } else if (file.is_directory && ri->uri[uri_len - 1] != '/') {
4781 mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n"
4782 "Location: %s/\r\n\r\n", ri->uri);
4783 } else if (!strcmp(ri->request_method, "PROPFIND")) {
4784 handle_propfind(conn, path, &file);
4785 } else if (file.is_directory &&
4786 !substitute_index_file(conn, path, sizeof(path), &file)) {
4787 if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")) {
4788 handle_directory_request(conn, path);
4789 } else {
4790 send_http_error(conn, 403, "Directory Listing Denied",
4791 "Directory listing denied");
4792 }
4793#ifdef USE_LUA
4794 } else if (match_prefix("**.lp$", 6, path) > 0) {
4795 handle_lsp_request(conn, path, &file, NULL);
4796#endif
4797#if !defined(NO_CGI)
4798 } else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
4800 path) > 0) {
4801 if (strcmp(ri->request_method, "POST") &&
4802 strcmp(ri->request_method, "HEAD") &&
4803 strcmp(ri->request_method, "GET")) {
4804 send_http_error(conn, 501, "Not Implemented",
4805 "Method %s is not implemented", ri->request_method);
4806 } else {
4807 handle_cgi_request(conn, path);
4808 }
4809#endif // !NO_CGI
4810 } else if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
4812 path) > 0) {
4813 handle_ssi_file_request(conn, path);
4814 } else if (is_not_modified(conn, &file)) {
4815 send_http_error(conn, 304, "Not Modified", "%s", "");
4816 } else {
4817 handle_file_request(conn, path, &file);
4818 }
4819}
4820
4821static void close_all_listening_sockets(struct mg_context *ctx) {
4822 int i;
4823 for (i = 0; i < ctx->num_listening_sockets; i++) {
4825 }
4826 free(ctx->listening_sockets);
4827}
4828
4829static int is_valid_port(unsigned int port) {
4830 return port > 0 && port < 0xffff;
4831}
4832
4833// Valid listening port specification is: [ip_address:]port[s]
4834// Examples: 80, 443s, 127.0.0.1:3128, 1.2.3.4:8080s
4835// TODO(lsm): add parsing of the IPv6 address
4836static int parse_port_string(const struct vec *vec, struct socket *so) {
4837 unsigned int a, b, c, d, ch, port;
4838 int len;
4839#if defined(USE_IPV6)
4840 char buf[100];
4841#endif
4842
4843 // MacOS needs that. If we do not zero it, subsequent bind() will fail.
4844 // Also, all-zeroes in the socket address means binding to all addresses
4845 // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
4846 memset(so, 0, sizeof(*so));
4847 so->lsa.sin.sin_family = AF_INET;
4848
4849 if (sscanf(vec->ptr, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
4850 // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
4851 so->lsa.sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
4852 so->lsa.sin.sin_port = htons((uint16_t) port);
4853#if defined(USE_IPV6)
4854
4855 } else if (sscanf(vec->ptr, "[%49[^]]]:%d%n", buf, &port, &len) == 2 &&
4856 inet_pton(AF_INET6, buf, &so->lsa.sin6.sin6_addr)) {
4857 // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
4858 so->lsa.sin6.sin6_family = AF_INET6;
4859 so->lsa.sin6.sin6_port = htons((uint16_t) port);
4860#endif
4861 } else if (sscanf(vec->ptr, "%u%n", &port, &len) == 1) {
4862 // If only port is specified, bind to IPv4, INADDR_ANY
4863 so->lsa.sin.sin_port = htons((uint16_t) port);
4864 } else {
4865 port = len = 0; // Parsing failure. Make port invalid.
4866 }
4867
4868 ch = vec->ptr[len]; // Next character after the port number
4869 so->is_ssl = ch == 's';
4870 so->ssl_redir = ch == 'r';
4871
4872 // Make sure the port is valid and vector ends with 's', 'r' or ','
4873 return is_valid_port(port) &&
4874 (ch == '\0' || ch == 's' || ch == 'r' || ch == ',');
4875}
4876
4877static int set_ports_option(struct mg_context *ctx) {
4878 const char *list = ctx->config[LISTENING_PORTS];
4879 int on = 1, success = 1;
4880#if defined(USE_IPV6)
4881 int off = 0;
4882#endif
4883 struct vec vec;
4884 struct socket so, *ptr;
4885
4886 while (success && (list = next_option(list, &vec, NULL)) != NULL) {
4887 if (!parse_port_string(&vec, &so)) {
4888 cry(fc(ctx), "%s: %.*s: invalid port spec. Expecting list of: %s",
4889 __func__, (int) vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|r]");
4890 success = 0;
4891 } else if (so.is_ssl && ctx->ssl_ctx == NULL) {
4892 cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
4893 success = 0;
4894 } else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
4896 // On Windows, SO_REUSEADDR is recommended only for
4897 // broadcast UDP sockets
4899 (void *) &on, sizeof(on)) != 0 ||
4900#if defined(USE_IPV6)
4901 (so.lsa.sa.sa_family == AF_INET6 &&
4902 setsockopt(so.sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &off,
4903 sizeof(off)) != 0) ||
4904#endif
4905 bind(so.sock, &so.lsa.sa, so.lsa.sa.sa_family == AF_INET ?
4906 sizeof(so.lsa.sin) : sizeof(so.lsa)) != 0 ||
4907 listen(so.sock, SOMAXCONN) != 0) {
4908 cry(fc(ctx), "%s: cannot bind to %.*s: %d (%s)", __func__,
4909 (int) vec.len, vec.ptr, ERRNO, strerror(errno));
4910 closesocket(so.sock);
4911 success = 0;
4912 } else if ((ptr = (struct socket *) realloc(ctx->listening_sockets,
4913 (ctx->num_listening_sockets + 1) *
4914 sizeof(ctx->listening_sockets[0]))) == NULL) {
4915 closesocket(so.sock);
4916 success = 0;
4917 } else {
4918 set_close_on_exec(so.sock);
4919 ctx->listening_sockets = ptr;
4921 ctx->num_listening_sockets++;
4922 }
4923 }
4924
4925 if (!success) {
4927 }
4928
4929 return success;
4930}
4931
4932static void log_header(const struct mg_connection *conn, const char *header,
4933 FILE *fp) {
4934 const char *header_value;
4935
4936 if ((header_value = mg_get_header(conn, header)) == NULL) {
4937 (void) fprintf(fp, "%s", " -");
4938 } else {
4939 (void) fprintf(fp, " \"%s\"", header_value);
4940 }
4941}
4942
4943static void log_access(const struct mg_connection *conn) {
4944 const struct mg_request_info *ri;
4945 FILE *fp;
4946 char date[64], src_addr[IP_ADDR_STR_LEN];
4947
4948 fp = conn->ctx->config[ACCESS_LOG_FILE] == NULL ? NULL :
4949 fopen(conn->ctx->config[ACCESS_LOG_FILE], "a+");
4950
4951 if (fp == NULL)
4952 return;
4953
4954 strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
4955 localtime(&conn->birth_time));
4956
4957 ri = &conn->request_info;
4958 flockfile(fp);
4959
4960 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
4961 fprintf(fp, "%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
4962 src_addr, ri->remote_user == NULL ? "-" : ri->remote_user, date,
4963 ri->request_method ? ri->request_method : "-",
4964 ri->uri ? ri->uri : "-", ri->http_version,
4965 conn->status_code, conn->num_bytes_sent);
4966 log_header(conn, "Referer", fp);
4967 log_header(conn, "User-Agent", fp);
4968 fputc('\n', fp);
4969 fflush(fp);
4970
4971 funlockfile(fp);
4972 fclose(fp);
4973}
4974
4975// Verify given socket address against the ACL.
4976// Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
4977static int check_acl(struct mg_context *ctx, uint32_t remote_ip) {
4978 int allowed, flag;
4979 uint32_t net, mask;
4980 struct vec vec;
4981 const char *list = ctx->config[ACCESS_CONTROL_LIST];
4982
4983 // If any ACL is set, deny by default
4984 allowed = list == NULL ? '+' : '-';
4985
4986 while ((list = next_option(list, &vec, NULL)) != NULL) {
4987 flag = vec.ptr[0];
4988 if ((flag != '+' && flag != '-') ||
4989 parse_net(&vec.ptr[1], &net, &mask) == 0) {
4990 cry(fc(ctx), "%s: subnet must be [+|-]x.x.x.x[/x]", __func__);
4991 return -1;
4992 }
4993
4994 if (net == (remote_ip & mask)) {
4995 allowed = flag;
4996 }
4997 }
4998
4999 return allowed == '+';
5000}
5001
5002#if !defined(_WIN32)
5003static int set_uid_option(struct mg_context *ctx) {
5004 struct passwd *pw;
5005 const char *uid = ctx->config[RUN_AS_USER];
5006 int success = 0;
5007
5008 if (uid == NULL) {
5009 success = 1;
5010 } else {
5011 if ((pw = getpwnam(uid)) == NULL) {
5012 cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
5013 } else if (setgid(pw->pw_gid) == -1) {
5014 cry(fc(ctx), "%s: setgid(%s): %s", __func__, uid, strerror(errno));
5015 } else if (setuid(pw->pw_uid) == -1) {
5016 cry(fc(ctx), "%s: setuid(%s): %s", __func__, uid, strerror(errno));
5017 } else {
5018 success = 1;
5019 }
5020 }
5021
5022 return success;
5023}
5024#endif // !_WIN32
5025
5026static int set_gpass_option(struct mg_context *ctx) {
5028 const char *path = ctx->config[GLOBAL_PASSWORDS_FILE];
5029 if (path != NULL && !mg_stat(path, &file)) {
5030 cry(fc(ctx), "Cannot open %s: %s", path, strerror(ERRNO));
5031 return 0;
5032 }
5033 return 1;
5034}
5035
5036static int set_acl_option(struct mg_context *ctx) {
5037 return check_acl(ctx, (uint32_t) 0x7f000001UL) != -1;
5038}
5039
5041 conn->path_info = NULL;
5042 conn->num_bytes_sent = conn->num_bytes_read = 0;
5043 conn->status_code = -1;
5044 conn->must_close = conn->request_len = conn->throttle = 0;
5045}
5046
5047static void close_socket_gracefully(struct mg_connection *conn) {
5048#if defined(_WIN32)
5049 char buf[MG_BUF_LEN];
5050 int n;
5051#endif
5052 struct linger linger;
5053
5054 // Set linger option to avoid socket hanging out after close. This prevent
5055 // ephemeral port exhaust problem under high QPS.
5056 linger.l_onoff = 1;
5057 linger.l_linger = 1;
5059 (char *) &linger, sizeof(linger));
5060
5061 // Send FIN to the client
5062 shutdown(conn->client.sock, SHUT_WR);
5064
5065#if defined(_WIN32)
5066 // Read and discard pending incoming data. If we do not do that and close the
5067 // socket, the data in the send buffer may be discarded. This
5068 // behaviour is seen on Windows, when client keeps sending data
5069 // when server decides to close the connection; then when client
5070 // does recv() it gets no data back.
5071 do {
5072 n = pull(NULL, conn, buf, sizeof(buf));
5073 } while (n > 0);
5074#endif
5075
5076 // Now we know that our FIN is ACK-ed, safe to close
5077 closesocket(conn->client.sock);
5078}
5079
5080static void close_connection(struct mg_connection *conn) {
5081 conn->must_close = 1;
5082
5083#ifndef NO_SSL
5084 if (conn->ssl != NULL) {
5085 // Run SSL_shutdown twice to ensure completly close SSL connection
5086 SSL_shutdown(conn->ssl);
5087 SSL_free(conn->ssl);
5088 conn->ssl = NULL;
5089 }
5090#endif
5091 if (conn->client.sock != INVALID_SOCKET) {
5093 conn->client.sock = INVALID_SOCKET;
5094 }
5095}
5096
5098#ifndef NO_SSL
5099 if (conn->client_ssl_ctx != NULL) {
5101 }
5102#endif
5103 close_connection(conn);
5104 free(conn);
5105}
5106
5107static int is_valid_uri(const char *uri) {
5108 // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
5109 // URI can be an asterisk (*) or should start with slash.
5110 return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
5111}
5112
5113static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len) {
5114 const char *cl;
5115
5116 ebuf[0] = '\0';
5118 conn->request_len = read_request(NULL, conn, conn->buf, conn->buf_size,
5119 &conn->data_len);
5120 assert(conn->request_len < 0 || conn->data_len >= conn->request_len);
5121
5122 if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
5123 snprintf(ebuf, ebuf_len, "%s", "Request Too Large");
5124 } else if (conn->request_len <= 0) {
5125 snprintf(ebuf, ebuf_len, "%s", "Client closed connection");
5126 } else if (parse_http_message(conn->buf, conn->buf_size,
5127 &conn->request_info) <= 0) {
5128 snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf);
5129 } else {
5130 // Request is valid. Set content_len attribute by parsing Content-Length
5131 // If Content-Length is absent, set content_len to 0 if request is GET,
5132 // and set it to INT64_MAX otherwise. Setting to INT64_MAX instructs
5133 // mg_read() to read from the socket until socket is closed.
5134 // The reason for treating GET and POST/PUT differently is that libraries
5135 // like jquery do not set Content-Length in GET requests, and we don't
5136 // want mg_read() to hang waiting until socket is timed out.
5137 // See https://github.com/cesanta/mongoose/pull/121 for more.
5138 conn->content_len = INT64_MAX;
5139 if (!mg_strcasecmp(conn->request_info.request_method, "GET")) {
5140 conn->content_len = 0;
5141 }
5142 if ((cl = get_header(&conn->request_info, "Content-Length")) != NULL) {
5143 conn->content_len = strtoll(cl, NULL, 10);
5144 }
5145 conn->birth_time = time(NULL);
5146 }
5147 return ebuf[0] == '\0';
5148}
5149
5150static void process_new_connection(struct mg_connection *conn) {
5151 struct mg_request_info *ri = &conn->request_info;
5152 int keep_alive_enabled, keep_alive, discard_len;
5153 char ebuf[100];
5154
5156 keep_alive = 0;
5157
5158 // Important: on new connection, reset the receiving buffer. Credit goes
5159 // to crule42.
5160 conn->data_len = 0;
5161 do {
5162 if (!getreq(conn, ebuf, sizeof(ebuf))) {
5163 send_http_error(conn, 500, "Server Error", "%s", ebuf);
5164 conn->must_close = 1;
5165 } else if (!is_valid_uri(conn->request_info.uri)) {
5166 snprintf(ebuf, sizeof(ebuf), "Invalid URI: [%s]", ri->uri);
5167 send_http_error(conn, 400, "Bad Request", "%s", ebuf);
5168 } else if (strcmp(ri->http_version, "1.0") &&
5169 strcmp(ri->http_version, "1.1")) {
5170 snprintf(ebuf, sizeof(ebuf), "Bad HTTP version: [%s]", ri->http_version);
5171 send_http_error(conn, 505, "Bad HTTP version", "%s", ebuf);
5172 }
5173
5174 if (ebuf[0] == '\0') {
5175 handle_request(conn);
5176 call_user(MG_REQUEST_END, conn, (void *) (long) conn->status_code);
5177 log_access(conn);
5178 }
5179 if (ri->remote_user != NULL) {
5180 free((void *) ri->remote_user);
5181 // Important! When having connections with and without auth
5182 // would cause double free and then crash
5183 ri->remote_user = NULL;
5184 }
5185
5186 // NOTE(lsm): order is important here. should_keep_alive() call
5187 // is using parsed request, which will be invalid after memmove's below.
5188 // Therefore, memorize should_keep_alive() result now for later use
5189 // in loop exit condition.
5190 keep_alive = conn->ctx->stop_flag == 0 && keep_alive_enabled &&
5191 conn->content_len >= 0 && should_keep_alive(conn);
5192
5193 // Discard all buffered data for this request
5194 discard_len = conn->content_len >= 0 && conn->request_len > 0 &&
5195 conn->request_len + conn->content_len < (int64_t) conn->data_len ?
5196 (int) (conn->request_len + conn->content_len) : conn->data_len;
5197 assert(discard_len >= 0);
5198 memmove(conn->buf, conn->buf + discard_len, conn->data_len - discard_len);
5199 conn->data_len -= discard_len;
5200 assert(conn->data_len >= 0);
5201 assert(conn->data_len <= conn->buf_size);
5202 } while (keep_alive);
5203}
5204
5205// Worker threads take accepted socket from the queue
5206static int consume_socket(struct mg_context *ctx, struct socket *sp) {
5207 (void) pthread_mutex_lock(&ctx->mutex);
5208 DEBUG_TRACE(("going idle"));
5209
5210 // If the queue is empty, wait. We're idle at this point.
5211 while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {
5212 pthread_cond_wait(&ctx->sq_full, &ctx->mutex);
5213 }
5214
5215 // If we're stopping, sq_head may be equal to sq_tail.
5216 if (ctx->sq_head > ctx->sq_tail) {
5217 // Copy socket from the queue and increment tail
5218 *sp = ctx->queue[ctx->sq_tail % ARRAY_SIZE(ctx->queue)];
5219 ctx->sq_tail++;
5220 DEBUG_TRACE(("grabbed socket %d, going busy", sp->sock));
5221
5222 // Wrap pointers if needed
5223 while (ctx->sq_tail > (int) ARRAY_SIZE(ctx->queue)) {
5224 ctx->sq_tail -= ARRAY_SIZE(ctx->queue);
5225 ctx->sq_head -= ARRAY_SIZE(ctx->queue);
5226 }
5227 }
5228
5231
5232 return !ctx->stop_flag;
5233}
5234
5236 struct mg_context *ctx = (struct mg_context *) thread_func_param;
5237 struct mg_connection *conn;
5238
5239 conn = (struct mg_connection *) calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE);
5240 if (conn == NULL) {
5241 cry(fc(ctx), "%s", "Cannot create new connection struct, OOM");
5242 } else {
5243 conn->buf_size = MAX_REQUEST_SIZE;
5244 conn->buf = (char *) (conn + 1);
5245 conn->ctx = ctx;
5246 conn->event.user_data = ctx->user_data;
5247
5249
5250 // Call consume_socket() even when ctx->stop_flag > 0, to let it signal
5251 // sq_empty condvar to wake up the master waiting in produce_socket()
5252 while (consume_socket(ctx, &conn->client)) {
5253 conn->birth_time = time(NULL);
5254
5255 // Fill in IP, port info early so even if SSL setup below fails,
5256 // error handler would have the corresponding info.
5257 // Thanks to Johannes Winkelmann for the patch.
5258 // TODO(lsm): Fix IPv6 case
5259 conn->request_info.remote_port = ntohs(conn->client.rsa.sin.sin_port);
5261 &conn->client.rsa.sin.sin_addr.s_addr, 4);
5263 conn->request_info.is_ssl = conn->client.is_ssl;
5264
5265 if (!conn->client.is_ssl
5267 || sslize(conn, conn->ctx->ssl_ctx, SSL_accept)
5268#endif
5269 ) {
5271 }
5272
5273 close_connection(conn);
5274 }
5276 free(conn);
5277 }
5278
5279 // Signal master that we're done with connection and exiting
5281 ctx->num_threads--;
5283 assert(ctx->num_threads >= 0);
5285
5286 DEBUG_TRACE(("exiting"));
5287 return NULL;
5288}
5289
5290// Master thread adds accepted socket to a queue
5291static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
5293
5294 // If the queue is full, wait
5295 while (ctx->stop_flag == 0 &&
5296 ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {
5298 }
5299
5300 if (ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue)) {
5301 // Copy socket to the queue and increment head
5303 ctx->sq_head++;
5304 DEBUG_TRACE(("queued socket %d", sp->sock));
5305 }
5306
5309}
5310
5312#ifdef _WIN32
5313 DWORD t = milliseconds;
5314#else
5315 struct timeval t;
5316 t.tv_sec = milliseconds / 1000;
5317 t.tv_usec = (milliseconds * 1000) % 1000000;
5318#endif
5319 return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *) &t, sizeof(t)) ||
5320 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (void *) &t, sizeof(t));
5321}
5322
5323static void accept_new_connection(const struct socket *listener,
5324 struct mg_context *ctx) {
5325 struct socket so;
5327 socklen_t len = sizeof(so.rsa);
5328 int on = 1;
5329 extern int check_midas_acl(const struct sockaddr *sa, int len);
5330
5331 if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) == INVALID_SOCKET) {
5332 } else if ((!check_acl(ctx, ntohl(* (uint32_t *) &so.rsa.sin.sin_addr))) || (!check_midas_acl(&so.rsa.sa, len))) {
5333 sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
5334 cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr);
5335 closesocket(so.sock);
5336 } else {
5337 // Put so socket structure into the queue
5338 DEBUG_TRACE(("Accepted socket %d", (int) so.sock));
5339 set_close_on_exec(so.sock);
5340 so.is_ssl = listener->is_ssl;
5341 so.ssl_redir = listener->ssl_redir;
5342 getsockname(so.sock, &so.lsa.sa, &len);
5343 // Set TCP keep-alive. This is needed because if HTTP-level keep-alive
5344 // is enabled, and client resets the connection, server won't get
5345 // TCP FIN or RST and will keep the connection open forever. With TCP
5346 // keep-alive, next keep-alive handshake will figure out that the client
5347 // is down and will close the server end.
5348 // Thanks to Igor Klopov who suggested the patch.
5349 setsockopt(so.sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &on, sizeof(on));
5351 produce_socket(ctx, &so);
5352 }
5353}
5354
5356 struct mg_context *ctx = (struct mg_context *) thread_func_param;
5357 struct pollfd *pfd;
5358 int i;
5359
5360 // Increase priority of the master thread
5361#if defined(_WIN32)
5363#endif
5364
5365#if defined(ISSUE_317)
5366 struct sched_param sched_param;
5369#endif
5370
5372
5373 pfd = (struct pollfd *) calloc(ctx->num_listening_sockets, sizeof(pfd[0]));
5374 while (pfd != NULL && ctx->stop_flag == 0) {
5375 for (i = 0; i < ctx->num_listening_sockets; i++) {
5376 pfd[i].fd = ctx->listening_sockets[i].sock;
5377 pfd[i].events = POLLIN;
5378 }
5379
5380 if (poll(pfd, ctx->num_listening_sockets, 200) > 0) {
5381 for (i = 0; i < ctx->num_listening_sockets; i++) {
5382 // NOTE(lsm): on QNX, poll() returns POLLRDNORM after the
5383 // successfull poll, and POLLIN is defined as (POLLRDNORM | POLLRDBAND)
5384 // Therefore, we're checking pfd[i].revents & POLLIN, not
5385 // pfd[i].revents == POLLIN.
5386 if (ctx->stop_flag == 0 && (pfd[i].revents & POLLIN)) {
5388 }
5389 }
5390 }
5391 }
5392 free(pfd);
5393 DEBUG_TRACE(("stopping workers"));
5394
5395 // Stop signal received: somebody called mg_stop. Quit.
5397
5398 // Wakeup workers that are waiting for connections to handle.
5400
5401 // Wait until all threads finish
5402 (void) pthread_mutex_lock(&ctx->mutex);
5403 while (ctx->num_threads > 0) {
5404 (void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
5405 }
5407
5408 // All threads exited, no sync is needed. Destroy mutex and condvars
5413
5414#if !defined(NO_SSL)
5415 uninitialize_ssl(ctx);
5416#endif
5417 DEBUG_TRACE(("exiting"));
5418
5420
5421 // Signal mg_stop() that we're done.
5422 // WARNING: This must be the very last thing this
5423 // thread does, as ctx becomes invalid after this line.
5424 ctx->stop_flag = 2;
5425 return NULL;
5426}
5427
5428static void free_context(struct mg_context *ctx) {
5429 int i;
5430
5431 // Deallocate config parameters
5432 for (i = 0; i < NUM_OPTIONS; i++) {
5433 if (ctx->config[i] != NULL)
5434 free(ctx->config[i]);
5435 }
5436
5437#ifndef NO_SSL
5438 // Deallocate SSL context
5439 if (ctx->ssl_ctx != NULL) {
5440 SSL_CTX_free(ctx->ssl_ctx);
5441 }
5442 if (ssl_mutexes != NULL) {
5443 free(ssl_mutexes);
5444 ssl_mutexes = NULL;
5445 }
5446#endif // !NO_SSL
5447
5448 // Deallocate context itself
5449 free(ctx);
5450}
5451
5452void mg_stop(struct mg_context *ctx) {
5453 ctx->stop_flag = 1;
5454
5455 // Wait until mg_fini() stops
5456 while (ctx->stop_flag != 2) {
5457 (void) mg_sleep(10);
5458 }
5459 free_context(ctx);
5460
5461#if defined(_WIN32) && !defined(__SYMBIAN32__)
5462 (void) WSACleanup();
5463#endif // _WIN32
5464}
5465
5466struct mg_context *mg_start(const char **options,
5467 mg_event_handler_t func,
5468 void *user_data) {
5469 struct mg_context *ctx;
5470 const char *name, *value, *default_value;
5471 int i;
5472
5473#if defined(_WIN32) && !defined(__SYMBIAN32__)
5474 WSADATA data;
5475 WSAStartup(MAKEWORD(2,2), &data);
5476#endif // _WIN32
5477
5478 // Allocate context and initialize reasonable general case defaults.
5479 // TODO(lsm): do proper error handling here.
5480 if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) {
5481 return NULL;
5482 }
5483 ctx->event_handler = func;
5484 ctx->user_data = user_data;
5485
5486 while (options && (name = *options++) != NULL) {
5487 if ((i = get_option_index(name)) == -1) {
5488 cry(fc(ctx), "Invalid option: %s", name);
5489 free_context(ctx);
5490 return NULL;
5491 } else if ((value = *options++) == NULL) {
5492 cry(fc(ctx), "%s: option value cannot be NULL", name);
5493 free_context(ctx);
5494 return NULL;
5495 }
5496 if (ctx->config[i] != NULL) {
5497 cry(fc(ctx), "warning: %s: duplicate option", name);
5498 free(ctx->config[i]);
5499 }
5500 ctx->config[i] = mg_strdup(value);
5501 DEBUG_TRACE(("[%s] -> [%s]", name, value));
5502 }
5503
5504 // Set default value if needed
5505 for (i = 0; config_options[i * 2] != NULL; i++) {
5506 default_value = config_options[i * 2 + 1];
5507 if (ctx->config[i] == NULL && default_value != NULL) {
5509 }
5510 }
5511
5512 // NOTE(lsm): order is important here. SSL certificates must
5513 // be initialized before listening ports. UID must be set last.
5514 if (!set_gpass_option(ctx) ||
5515#if !defined(NO_SSL)
5516 !set_ssl_option(ctx) ||
5517#endif
5518 !set_ports_option(ctx) ||
5519#if !defined(_WIN32)
5520 !set_uid_option(ctx) ||
5521#endif
5522 !set_acl_option(ctx)) {
5523 free_context(ctx);
5524 return NULL;
5525 }
5526
5527#if !defined(_WIN32) && !defined(__SYMBIAN32__)
5528 // Ignore SIGPIPE signal, so if browser cancels the request, it
5529 // won't kill the whole process.
5531#endif // !_WIN32
5532
5534 (void) pthread_cond_init(&ctx->cond, NULL);
5537
5538 // Start master (listening) thread
5540
5541 // Start worker threads
5542 for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
5543 if (mg_start_thread(worker_thread, ctx) != 0) {
5544 cry(fc(ctx), "Cannot start worker thread: %ld", (long) ERRNO);
5545 } else {
5546 ctx->num_threads++;
5547 }
5548 }
5549
5550 return ctx;
5551}
5552
5553#ifdef USE_LUA
5554#ifdef _WIN32
5555static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
5556 int offset) {
5558 HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
5559 void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
5560 CloseHandle(mh);
5561 return p;
5562}
5563#define munmap(x, y) UnmapViewOfFile(x)
5564#define MAP_FAILED NULL
5565#define MAP_PRIVATE 0
5566#define PROT_READ 0
5567#else
5568#include <sys/mman.h>
5569#endif
5570
5571static const char *LUASOCKET = "luasocket";
5572
5573// Forward declarations
5574static int handle_lsp_request(struct mg_connection *, const char *,
5575 struct file *, struct lua_State *);
5576
5577static void reg_string(struct lua_State *L, const char *name, const char *val) {
5580 lua_rawset(L, -3);
5581}
5582
5583static void reg_int(struct lua_State *L, const char *name, int val) {
5586 lua_rawset(L, -3);
5587}
5588
5589static void reg_function(struct lua_State *L, const char *name,
5590 lua_CFunction func, struct mg_connection *conn) {
5592 lua_pushlightuserdata(L, conn);
5593 lua_pushcclosure(L, func, 1);
5594 lua_rawset(L, -3);
5595}
5596
5597static int lsp_sock_close(lua_State *L) {
5598 if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
5599 lua_getfield(L, -1, "sock");
5601 } else {
5602 return luaL_error(L, "invalid :close() call");
5603 }
5604 return 1;
5605}
5606
5607static int lsp_sock_recv(lua_State *L) {
5608 char buf[2000];
5609 int n;
5610
5611 if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
5612 lua_getfield(L, -1, "sock");
5613 n = recv((SOCKET) lua_tonumber(L, -1), buf, sizeof(buf), 0);
5614 if (n <= 0) {
5615 lua_pushnil(L);
5616 } else {
5617 lua_pushlstring(L, buf, n);
5618 }
5619 } else {
5620 return luaL_error(L, "invalid :close() call");
5621 }
5622 return 1;
5623}
5624
5625static int lsp_sock_send(lua_State *L) {
5626 const char *buf;
5627 size_t len, sent = 0;
5628 int n, sock;
5629
5630 if (lua_gettop(L) > 1 && lua_istable(L, -2) && lua_isstring(L, -1)) {
5631 buf = lua_tolstring(L, -1, &len);
5632 lua_getfield(L, -2, "sock");
5633 sock = (int) lua_tonumber(L, -1);
5634 while (sent < len) {
5635 if ((n = send(sock, buf + sent, len - sent, 0)) <= 0) {
5636 break;
5637 }
5638 sent += n;
5639 }
5640 lua_pushnumber(L, n);
5641 } else {
5642 return luaL_error(L, "invalid :close() call");
5643 }
5644 return 1;
5645}
5646
5647static const struct luaL_Reg luasocket_methods[] = {
5648 {"close", lsp_sock_close},
5649 {"send", lsp_sock_send},
5650 {"recv", lsp_sock_recv},
5651 {NULL, NULL}
5652};
5653
5654static int lsp_connect(lua_State *L) {
5655 char ebuf[100];
5656 SOCKET sock;
5657
5658 if (lua_isstring(L, -3) && lua_isnumber(L, -2) && lua_isnumber(L, -1)) {
5659 sock = conn2(lua_tostring(L, -3), (int) lua_tonumber(L, -2),
5660 (int) lua_tonumber(L, -1), ebuf, sizeof(ebuf));
5661 if (sock == INVALID_SOCKET) {
5662 return luaL_error(L, ebuf);
5663 } else {
5664 lua_newtable(L);
5665 reg_int(L, "sock", sock);
5666 reg_string(L, "host", lua_tostring(L, -4));
5668 lua_setmetatable(L, -2);
5669 }
5670 } else {
5671 return luaL_error(L, "connect(host,port,is_ssl): invalid parameter given.");
5672 }
5673 return 1;
5674}
5675
5676static int lsp_error(lua_State *L) {
5677 lua_getglobal(L, "mg");
5678 lua_getfield(L, -1, "onerror");
5679 lua_pushvalue(L, -3);
5680 lua_pcall(L, 1, 0, 0);
5681 return 0;
5682}
5683
5684// Silently stop processing chunks.
5685static void lsp_abort(lua_State *L) {
5686 int top = lua_gettop(L);
5687 lua_getglobal(L, "mg");
5688 lua_pushnil(L);
5689 lua_setfield(L, -2, "onerror");
5690 lua_settop(L, top);
5691 lua_pushstring(L, "aborting");
5692 lua_error(L);
5693}
5694
5695static int lsp(struct mg_connection *conn, const char *path,
5696 const char *p, int64_t len, lua_State *L) {
5697 int i, j, pos = 0, lines = 1, lualines = 0;
5698 char chunkname[MG_BUF_LEN];
5699
5700 for (i = 0; i < len; i++) {
5701 if (p[i] == '\n') lines++;
5702 if (p[i] == '<' && p[i + 1] == '?') {
5703 for (j = i + 1; j < len ; j++) {
5704 if (p[j] == '\n') lualines++;
5705 if (p[j] == '?' && p[j + 1] == '>') {
5706 mg_write(conn, p + pos, i - pos);
5707
5708 snprintf(chunkname, sizeof(chunkname), "@%s+%i", path, lines);
5709 lua_pushlightuserdata(L, conn);
5711 if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), chunkname)) {
5712 // Syntax error or OOM. Error message is pushed on stack.
5713 lua_pcall(L, 1, 0, 0);
5714 } else {
5715 // Success loading chunk. Call it.
5716 lua_pcall(L, 0, 0, 1);
5717 }
5718
5719 pos = j + 2;
5720 i = pos - 1;
5721 break;
5722 }
5723 }
5724 if (lualines > 0) {
5725 lines += lualines;
5726 lualines = 0;
5727 }
5728 }
5729 }
5730
5731 if (i > pos) {
5732 mg_write(conn, p + pos, i - pos);
5733 }
5734
5735 return 0;
5736}
5737
5738static int lsp_write(lua_State *L) {
5739 int i, num_args;
5740 const char *str;
5741 size_t size;
5742 struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
5743
5745 for (i = 1; i <= num_args; i++) {
5746 if (lua_isstring(L, i)) {
5747 str = lua_tolstring(L, i, &size);
5748 mg_write(conn, str, size);
5749 }
5750 }
5751
5752 return 0;
5753}
5754
5755static int lsp_read(lua_State *L) {
5756 struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
5757 char buf[1024];
5758 int len = mg_read(conn, buf, sizeof(buf));
5759
5760 if (len <= 0) return 0;
5761 lua_pushlstring(L, buf, len);
5762
5763 return 1;
5764}
5765
5766// mg.include: Include another .lp file
5767static int lsp_include(lua_State *L) {
5768 struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
5770 if (handle_lsp_request(conn, lua_tostring(L, -1), &file, L)) {
5771 // handle_lsp_request returned an error code, meaning an error occured in
5772 // the included page and mg.onerror returned non-zero. Stop processing.
5773 lsp_abort(L);
5774 }
5775 return 0;
5776}
5777
5778// mg.cry: Log an error. Default value for mg.onerror.
5779static int lsp_cry(lua_State *L){
5780 struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
5781 cry(conn, "%s", lua_tostring(L, -1));
5782 return 0;
5783}
5784
5785// mg.redirect: Redirect the request (internally).
5786static int lsp_redirect(lua_State *L) {
5787 struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
5788 conn->request_info.uri = lua_tostring(L, -1);
5789 handle_request(conn);
5790 lsp_abort(L);
5791 return 0;
5792}
5793
5794static void prepare_lua_environment(struct mg_connection *conn, lua_State *L) {
5795 const struct mg_request_info *ri = &conn->request_info;
5796 extern void luaL_openlibs(lua_State *);
5797 int i;
5798
5800#ifdef USE_LUA_SQLITE3
5801 { extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); }
5802#endif
5803
5805 lua_pushliteral(L, "__index");
5807 lua_rawset(L, -3);
5808 lua_pop(L, 1);
5809 lua_register(L, "connect", lsp_connect);
5810
5811 if (conn == NULL) return;
5812
5813 // Register mg module
5814 lua_newtable(L);
5815
5816 reg_function(L, "read", lsp_read, conn);
5817 reg_function(L, "write", lsp_write, conn);
5818 reg_function(L, "cry", lsp_cry, conn);
5819 reg_function(L, "include", lsp_include, conn);
5820 reg_function(L, "redirect", lsp_redirect, conn);
5821 reg_string(L, "version", MONGOOSE_VERSION);
5822
5823 // Export request_info
5824 lua_pushstring(L, "request_info");
5825 lua_newtable(L);
5826 reg_string(L, "request_method", ri->request_method);
5827 reg_string(L, "uri", ri->uri);
5828 reg_string(L, "http_version", ri->http_version);
5829 reg_string(L, "query_string", ri->query_string);
5830 reg_int(L, "remote_ip", ri->remote_ip);
5831 reg_int(L, "remote_port", ri->remote_port);
5832 reg_int(L, "num_headers", ri->num_headers);
5833 lua_pushstring(L, "http_headers");
5834 lua_newtable(L);
5835 for (i = 0; i < ri->num_headers; i++) {
5836 reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
5837 }
5838 lua_rawset(L, -3);
5839 lua_rawset(L, -3);
5840
5841 lua_setglobal(L, "mg");
5842
5843 // Register default mg.onerror function
5844 luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua error:\\n', "
5845 "debug.traceback(e, 1)) end");
5846}
5847
5848static int lua_error_handler(lua_State *L) {
5849 const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
5850
5851 lua_getglobal(L, "mg");
5852 if (!lua_isnil(L, -1)) {
5853 lua_getfield(L, -1, "write"); // call mg.write()
5855 lua_pushliteral(L, "\n");
5856 lua_call(L, 2, 0);
5857 luaL_dostring(L, "mg.write(debug.traceback(), '\\n')");
5858 } else {
5859 printf("Lua error: [%s]\n", error_msg);
5860 luaL_dostring(L, "print(debug.traceback(), '\\n')");
5861 }
5862 // TODO(lsm): leave the stack balanced
5863
5864 return 0;
5865}
5866
5867void mg_exec_lua_script(struct mg_connection *conn, const char *path,
5868 const void **exports) {
5869 int i;
5870 lua_State *L;
5871
5872 if (path != NULL && (L = luaL_newstate()) != NULL) {
5875
5877 if (exports != NULL) {
5878 for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
5881 lua_rawset(L, -3);
5882 }
5883 }
5884
5885 if (luaL_loadfile(L, path) != 0) {
5887 }
5888 lua_pcall(L, 0, 0, -2);
5889 lua_close(L);
5890 }
5891}
5892
5893static void lsp_send_err(struct mg_connection *conn, struct lua_State *L,
5894 const char *fmt, ...) {
5895 char buf[MG_BUF_LEN];
5896 va_list ap;
5897
5898 va_start(ap, fmt);
5899 vsnprintf(buf, sizeof(buf), fmt, ap);
5900 va_end(ap);
5901
5902 if (L == NULL) {
5903 send_http_error(conn, 500, http_500_error, "%s", buf);
5904 } else {
5905 lua_pushstring(L, buf);
5906 lua_error(L);
5907 }
5908}
5909
5910static int handle_lsp_request(struct mg_connection *conn, const char *path,
5911 struct file *filep, struct lua_State *ls) {
5912 void *p = NULL;
5913 lua_State *L = NULL;
5914 FILE *fp = NULL;
5915 int error = 1;
5916
5917 // We need both mg_stat to get file size, and mg_fopen to get fd
5918 if (!mg_stat(path, filep) || (fp = mg_fopen(path, "r")) == NULL) {
5919 lsp_send_err(conn, ls, "File [%s] not found", path);
5920 } else if ((p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
5921 fileno(fp), 0)) == MAP_FAILED) {
5922 lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
5923 fileno(fp), strerror(errno));
5924 } else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) {
5925 send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed");
5926 } else {
5927 // We're not sending HTTP headers here, Lua page must do it.
5928 if (ls == NULL) {
5930 }
5931 error = lsp(conn, path, p, filep->size, L);
5932 }
5933
5934 if (L != NULL && ls == NULL) lua_close(L);
5935 if (p != NULL) munmap(p, filep->size);
5936 fclose(fp);
5937
5938 return error;
5939}
5940#endif // USE_LUA
#define FALSE
Definition cfortran.h:309
int done
unsigned int DWORD
Definition mcstd.h:51
int mg_modify_passwords_file(const char *fname, const char *domain, const char *user, const char *pass)
#define MG_EVENT_LOG
Definition mongoose4.h:58
int mg_websocket_read(struct mg_connection *, int *bits, char **data)
void *(* mg_thread_func_t)(void *)
Definition mongoose4.h:218
void mg_close_connection(struct mg_connection *conn)
int mg_websocket_write(struct mg_connection *conn, int opcode, const char *data, size_t data_len)
int type
Definition mongoose4.h:54
#define PRINTF_FORMAT_STRING(s)
Definition mongoose4.h:108
const char * mg_get_option(const struct mg_context *ctx, const char *name)
const char * mg_get_header(const struct mg_connection *conn, const char *name)
int mg_get_var(const char *data, size_t data_len, const char *name, char *dst, size_t dst_len)
int mg_start_thread(mg_thread_func_t func, void *param)
const char * request_method
Definition mongoose4.h:37
struct mg_request_info * request_info
Definition mongoose4.h:67
struct mg_connection * conn
Definition mongoose4.h:66
char * mg_md5(char buf[33],...)
const char * query_string
Definition mongoose4.h:40
#define MG_REQUEST_BEGIN
Definition mongoose4.h:55
const char * uri
Definition mongoose4.h:38
#define MG_THREAD_END
Definition mongoose4.h:60
struct mg_context * mg_start(const char **options, mg_event_handler_t func, void *user_data)
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded)
#define PRINTF_ARGS(x, y)
Definition mongoose4.h:114
int mg_write(struct mg_connection *conn, const void *buf, int len)
const char * mg_get_builtin_mime_type(const char *path)
const char ** mg_get_valid_option_names(void)
FILE * mg_upload(struct mg_connection *conn, const char *destination_dir, char *path, int path_len)
void mg_send_file(struct mg_connection *conn, const char *path)
const char * http_version
Definition mongoose4.h:39
#define MG_THREAD_BEGIN
Definition mongoose4.h:59
#define MG_HTTP_ERROR
Definition mongoose4.h:57
void * user_data
Definition mongoose4.h:62
int mg_get_cookie(const char *cookie_header, const char *var_name, char *dst, size_t dst_size)
void mg_stop(struct mg_context *ctx)
void * event_param
Definition mongoose4.h:64
int(* mg_event_handler_t)(struct mg_event *event)
Definition mongoose4.h:70
const char * remote_user
Definition mongoose4.h:41
#define MG_REQUEST_END
Definition mongoose4.h:56
void mg_websocket_handshake(struct mg_connection *)
const char * mg_version(void)
int mg_read(struct mg_connection *conn, void *buf, int len)
int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap)
unsigned char in[64]
Definition mongoose6.h:933
void * SSL_CTX
Definition mongoose6.h:1175
uint32_t bits[2]
Definition mongoose6.h:932
uint32_t buf[4]
Definition mongoose6.h:931
void * SSL
Definition mongoose6.h:1174
int mg_printf(struct mg_connection *conn, const char *fmt,...)
void ** info
Definition fesimdaq.cxx:41
DWORD n[4]
Definition mana.cxx:247
INT index
Definition mana.cxx:271
char param[10][256]
Definition mana.cxx:250
void * data
Definition mana.cxx:268
INT type
Definition mana.cxx:269
char addr[128]
Definition mcnaf.cxx:104
double count
Definition mdump.cxx:33
INT i
Definition mdump.cxx:32
INT bl
Definition mdump.cxx:29
char response[10000]
Definition melog.cxx:90
void base64_encode(char *s, char *d)
Definition melog.cxx:47
static int offset
Definition mgd.cxx:1500
#define TRUE
Definition midas.h:182
MidasHistoryInterface * mh
#define end
#define read(n, a, f)
#define sleep(ms)
#define mask(slot)
Definition midas_macro.h:54
#define name(x)
Definition midas_macro.h:24
#define set(var, value)
static std::string remove(const std::string s, char c)
Definition mjsonrpc.cxx:253
static FILE * fp
static int is_put_or_delete_request(const struct mg_connection *conn)
static void process_new_connection(struct mg_connection *conn)
static int mg_strncasecmp(const char *s1, const char *s2, size_t len)
static int is_authorized_for_put(struct mg_connection *conn)
#define mg_mkdir(x, y)
static void sockaddr_to_string(char *buf, size_t len, const union usa *usa)
#define SOMAXCONN
#define IP_ADDR_STR_LEN
#define MD5STEP(f, w, x, y, z, data, s)
static int consume_socket(struct mg_context *ctx, struct socket *sp)
static int check_authorization(struct mg_connection *conn, const char *path)
#define HEXTOI(x)
static void mkcol(struct mg_connection *conn, const char *path)
static int check_acl(struct mg_context *ctx, uint32_t remote_ip)
static int64_t left_to_read(const struct mg_connection *conn)
const char * mime_type
static int set_non_blocking_mode(SOCKET sock)
static void ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
static int set_ports_option(struct mg_context *ctx)
static void put_file(struct mg_connection *conn, const char *path)
static int set_uid_option(struct mg_context *ctx)
static void do_ssi_exec(struct mg_connection *conn, char *tag)
static void mg_strlcpy(register char *dst, register const char *src, size_t n)
void mg_url_encode(const char *src, char *dst, size_t dst_len)
static int set_sock_timeout(SOCKET sock, int milliseconds)
static int set_gpass_option(struct mg_context *ctx)
static int mg_strcasecmp(const char *s1, const char *s2)
static void * realloc2(void *ptr, size_t size)
#define INVALID_SOCKET
size_t ext_len
static void send_http_error(struct mg_connection *, int, const char *, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(4
static int set_throttle(const char *spec, uint32_t remote_ip, const char *uri)
static void uninitialize_ssl(struct mg_context *ctx)
static int num_leap_years(int year)
static int WINCDECL compare_dir_entries(const void *p1, const void *p2)
static int should_keep_alive(const struct mg_connection *conn)
static SOCKET conn2(const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len)
static void handle_file_request(struct mg_connection *conn, const char *path, struct file *filep)
static const char * ssl_error(void)
static int must_hide_file(struct mg_connection *conn, const char *path)
static int parse_net(const char *spec, uint32_t *net, uint32_t *mask)
static int substitute_index_file(struct mg_connection *conn, char *path, size_t path_len, struct file *filep)
const char * extension
#define ERRNO
static void close_all_listening_sockets(struct mg_context *ctx)
#define F1(x, y, z)
static void prepare_cgi_environment(struct mg_connection *conn, const char *prog, struct cgi_env_block *blk)
#define closesocket(a)
static int check_password(const char *method, const char *ha1, const char *uri, const char *nonce, const char *nc, const char *cnonce, const char *qop, const char *response)
static const char * get_header(const struct mg_request_info *ri, const char *name)
static int mg_chunked_printf(struct mg_connection *conn, const char *fmt,...)
static void print_dav_dir_entry(struct de *de, void *data)
static void dir_scan_callback(struct de *de, void *data)
static void remove_double_dots_and_double_slashes(char *s)
static void reset_per_request_attributes(struct mg_connection *conn)
static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap)
static pthread_mutex_t * ssl_mutexes
static int is_not_modified(const struct mg_connection *conn, const struct file *filep)
static int get_option_index(const char *name)
static void print_props(struct mg_connection *conn, const char *uri, struct file *filep)
static void handle_propfind(struct mg_connection *conn, const char *path, struct file *filep)
static char * mg_strdup(const char *str)
static void accept_new_connection(const struct socket *listener, struct mg_context *ctx)
#define CRYPTO_LIB
#define CGI_ENVIRONMENT_SIZE
static void MD5Final(unsigned char digest[16], MD5_CTX *ctx)
static int put_dir(const char *path)
struct mg_connection * mg_download(const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len, const char *fmt,...)
static void parse_http_headers(char **buf, struct mg_request_info *ri)
#define INT64_FMT
#define MONGOOSE_VERSION
static void get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec)
static void MD5Transform(uint32_t buf[4], uint32_t const in[16])
static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int64_t len)
#define F4(x, y, z)
static char * skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar)
static FILE * open_auth_file(struct mg_connection *conn, const char *path)
static char * skip(char **buf, const char *delimiters)
#define DEBUG_TRACE(x)
static int forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
static int set_ssl_option(struct mg_context *ctx)
static int sslize(struct mg_connection *conn, SSL_CTX *s, int(*func)(SSL *))
static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len)
static void set_close_on_exec(int fd)
#define ARRAY_SIZE(array)
static int get_first_ssl_listener_index(const struct mg_context *ctx)
static void log_header(const struct mg_connection *conn, const char *header, FILE *fp)
static int isbyte(int n)
static uint32_t get_remote_ip(const struct mg_connection *conn)
static void handle_directory_request(struct mg_connection *conn, const char *dir)
static void gmt_time_string(char *buf, size_t buf_len, time_t *t)
#define MAX_REQUEST_SIZE
static const char * mg_strcasestr(const char *big_str, const char *small_str)
static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
static const char * config_options[]
static void send_file_data(struct mg_connection *conn, FILE *fp, int64_t offset, int64_t len)
static int authorize(struct mg_connection *conn, FILE *fp)
static void bin2str(char *to, const unsigned char *p, size_t len)
static void byteReverse(unsigned char *buf, unsigned longs)
static void handle_options_request(struct mg_connection *conn)
static int is_valid_uri(const char *uri)
static const char * next_option(const char *list, struct vec *val, struct vec *eq_val)
static const char * suggest_connection_header(const struct mg_connection *conn)
#define EXTRA_HTTP_HEADERS
static time_t parse_date_string(const char *datetime)
int SOCKET
static void do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, int include_level)
#define F3(x, y, z)
static int read_request(FILE *fp, struct mg_connection *conn, char *buf, int bufsiz, int *nread)
static void handle_ssi_file_request(struct mg_connection *conn, const char *path)
static void MD5Init(MD5_CTX *ctx)
static int lowercase(const char *s)
static int parse_range_header(const char *header, int64_t *a, int64_t *b)
#define SSL_LIB
static pid_t spawn_process(struct mg_connection *conn, const char *prog, char *envblk, char *envp[], int fdin, int fdout, const char *dir)
static void handle_delete_request(struct mg_connection *conn, const char *path)
#define MSG_NOSIGNAL
#define MAX_CGI_ENVIR_VARS
static int parse_auth_header(struct mg_connection *conn, char *buf, size_t buf_size, struct ah *ah)
static void * master_thread(void *thread_func_param)
@ ENABLE_DIRECTORY_LISTING
@ GLOBAL_PASSWORDS_FILE
@ SSL_CERTIFICATE
@ RUN_AS_USER
@ REWRITE
@ ENABLE_KEEP_ALIVE
@ ACCESS_CONTROL_LIST
@ AUTHENTICATION_DOMAIN
@ HIDE_FILES
@ ERROR_LOG_FILE
@ THROTTLE
@ CGI_EXTENSIONS
@ CGI_ENVIRONMENT
@ PUT_DELETE_PASSWORDS_FILE
@ NUM_OPTIONS
@ NUM_THREADS
@ DOCUMENT_ROOT
@ PROTECT_URI
@ ACCESS_LOG_FILE
@ REQUEST_TIMEOUT
@ SSI_EXTENSIONS
@ CGI_INTERPRETER
@ LISTENING_PORTS
@ INDEX_FILES
@ EXTRA_MIME_TYPES
struct mg_connection * mg_connect(const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len)
static void fclose_on_exec(FILE *fp)
#define mg_remove(x)
static void construct_etag(char *buf, size_t buf_len, const struct file *filep)
static int mg_stat(const char *path, struct file *filep)
static void send_ssi_file(struct mg_connection *, const char *, FILE *, int)
static void print_dir_entry(const struct de *de)
static int scan_directory(struct mg_connection *conn, const char *dir, void *data, void(*cb)(struct de *, void *))
static void produce_socket(struct mg_context *ctx, const struct socket *sp)
static void send_authorization_request(struct mg_connection *conn)
#define MG_BUF_LEN
static void free_context(struct mg_context *ctx)
static int convert_uri_to_file_name(struct mg_connection *conn, char *buf, size_t buf_len, struct file *filep)
static unsigned long ssl_id_callback(void)
static int parse_port_string(const struct vec *vec, struct socket *so)
static const char * month_names[]
static char * addenv(struct cgi_env_block *block, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(2
static const struct @22 builtin_mime_types[]
static int match_prefix(const char *pattern, int pattern_len, const char *str)
static void handle_request(struct mg_connection *conn)
struct MD5Context MD5_CTX
static int is_big_endian(void)
static int remove_directory(struct mg_connection *conn, const char *dir)
static int get_request_len(const char *buf, int buf_len)
static int mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap)
#define WINCDECL
static void handle_cgi_request(struct mg_connection *conn, const char *prog)
static void static void static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len)
static int mg_snprintf(char *buf, size_t buflen, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(3
static struct mg_connection * fc(struct mg_context *ctx)
static void * worker_thread(void *thread_func_param)
static FILE * mg_fopen(const char *path, const char *mode)
static void static void cry(struct mg_connection *conn, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(2
#define F2(x, y, z)
static void close_socket_gracefully(struct mg_connection *conn)
static int call_user(int type, struct mg_connection *conn, void *p)
static int parse_http_message(char *buf, int len, struct mg_request_info *ri)
#define PATH_MAX
#define MGSQLEN
static char * mg_strndup(const char *ptr, size_t len)
static const char * http_500_error
static int set_acl_option(struct mg_context *ctx)
static int get_month_index(const char *s)
#define mg_sleep(x)
static int is_valid_port(unsigned int port)
#define PASSWORDS_FILE_NAME
static void log_access(const struct mg_connection *conn)
static void close_connection(struct mg_connection *conn)
static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len)
static void redirect_to_https_port(struct mg_connection *conn, int ssl_index)
#define STRUCT_FILE_INITIALIZER
static int is_valid_http_method(const char *method)
char * inet_ntoa(struct in_addr n)
const char * inet_ntop(int af, const void *src, char *dst, socklen_t size)
int inet_pton(int af, const char *src, void *dst)
#define R1(v, w, x, y, z, i)
static int left(const struct frozen *f)
#define R2(v, w, x, y, z, i)
#define R0(v, w, x, y, z, i)
static int expect(struct frozen *f, const char *s, int len, enum json_type t)
#define blk(i)
#define R3(v, w, x, y, z, i)
#define rol(value, bits)
static uint32_t blk0(union char64long16 *block, int i)
#define R4(v, w, x, y, z, i)
timeval tv
Definition msysmon.cxx:1095
#define gmtime
Definition msystem.h:266
#define localtime
Definition msystem.h:265
MUTEX_T * tm
Definition odbedit.cxx:39
double total[100]
Definition odbhist.cxx:42
INT j
Definition odbhist.cxx:40
double value[100]
Definition odbhist.cxx:42
INT k
Definition odbhist.cxx:40
char str[256]
Definition odbhist.cxx:33
char file_name[256]
Definition odbhist.cxx:41
DWORD status
Definition odbhist.cxx:39
char var_name[256]
Definition odbhist.cxx:41
TH1X EXPRT * h1_book(const char *name, const char *title, int bins, double min, double max)
Definition rmidas.h:24
char * nc
char * nonce
char * cnonce
char * uri
char * user
char * qop
char * response
char * vars[MAX_CGI_ENVIR_VARS]
struct mg_connection * conn
char buf[CGI_ENVIRONMENT_SIZE]
char * file_name
struct mg_connection * conn
struct file file
struct de * entries
int64_t size
int gzipped
int is_directory
time_t modification_time
time_t last_throttle_time
time_t birth_time
int64_t content_len
SSL_CTX * client_ssl_ctx
int64_t num_bytes_sent
struct socket client
int64_t num_bytes_read
struct mg_event event
struct mg_request_info request_info
int64_t last_throttle_bytes
struct mg_context * ctx
volatile int stop_flag
pthread_cond_t sq_empty
int num_listening_sockets
SSL_CTX * ssl_ctx
mg_event_handler_t event_handler
volatile int sq_head
void * user_data
volatile int num_threads
struct socket * listening_sockets
struct socket queue[MGSQLEN]
pthread_cond_t sq_full
volatile int sq_tail
char * config[NUM_OPTIONS]
pthread_mutex_t mutex
pthread_cond_t cond
union usa lsa
unsigned ssl_redir
unsigned is_ssl
SOCKET sock
union usa rsa
size_t len
const char * ptr
double d
Definition system.cxx:1311
char c
Definition system.cxx:1310
@ DIR
Definition test_init.cxx:7
static double e(void)
Definition tinyexpr.c:136
static double pi(void)
Definition tinyexpr.c:135
static te_expr * list(state *s)
Definition tinyexpr.c:567
unsigned char c[64]
uint32_t l[16]
struct sockaddr sa
struct sockaddr_in sin