1 // Copyright (c) 2004-2013 Sergey Lyubka
\r
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
\r
4 // of this software and associated documentation files (the "Software"), to deal
\r
5 // in the Software without restriction, including without limitation the rights
\r
6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
\r
7 // copies of the Software, and to permit persons to whom the Software is
\r
8 // furnished to do so, subject to the following conditions:
\r
10 // The above copyright notice and this permission notice shall be included in
\r
11 // all copies or substantial portions of the Software.
\r
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
\r
18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
\r
22 #define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
\r
25 #define _XOPEN_SOURCE 600 // For flockfile() on Linux
\r
27 //#define _LARGEFILE_SOURCE // Enable 64-bit file offsets
\r
28 #define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++
\r
29 #define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX
\r
32 #if defined (_MSC_VER)
\r
33 // conditional expression is constant: introduced by FD_SET(..)
\r
34 #pragma warning (disable : 4127)
\r
35 // non-constant aggregate initializer: issued due to missing C99 support
\r
36 #pragma warning (disable : 4204)
\r
39 // Disable WIN32_LEAN_AND_MEAN.
\r
40 // This makes windows.h always include winsock2.h
\r
41 #ifdef WIN32_LEAN_AND_MEAN
\r
42 #undef WIN32_LEAN_AND_MEAN
\r
45 #if defined(__SYMBIAN32__)
\r
46 #define NO_SSL // SSL is not supported
\r
47 #define NO_CGI // CGI is not supported
\r
48 #define PATH_MAX FILENAME_MAX
\r
49 #endif // __SYMBIAN32__
\r
51 #ifndef _WIN32_WCE // Some ANSI #includes are not available on Windows CE
\r
52 #include <sys/types.h>
\r
53 #include <sys/stat.h>
\r
57 #endif // !_WIN32_WCE
\r
69 #if defined(_WIN32) && !defined(__SYMBIAN32__) // Windows specific
\r
70 #define _WIN32_WINNT 0x0400 // To make it link in VS2005
\r
71 #include <windows.h>
\r
74 #define PATH_MAX MAX_PATH
\r
78 #include <process.h>
\r
82 #define NO_CGI // WinCE has no pipes
\r
86 #define errno GetLastError()
\r
87 #define strerror(x) _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10)
\r
88 #endif // _WIN32_WCE
\r
90 #define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \
\r
91 ((uint64_t)((uint32_t)(hi))) << 32))
\r
92 #define RATE_DIFF 10000000 // 100 nsecs
\r
93 #define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de)
\r
94 #define SYS2UNIX_TIME(lo, hi) \
\r
95 (time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)
\r
97 // Visual Studio 6 does not know __func__ or __FUNCTION__
\r
98 // The rest of MS compilers use __FUNCTION__, not C99 __func__
\r
99 // Also use _strtoui64 on modern M$ compilers
\r
100 #if defined(_MSC_VER) && _MSC_VER < 1300
\r
102 #define STR(x) STRX(x)
\r
103 #define __func__ __FILE__ ":" STR(__LINE__)
\r
104 #define strtoull(x, y, z) strtoul(x, y, z)
\r
105 #define strtoll(x, y, z) strtol(x, y, z)
\r
107 #define __func__ __FUNCTION__
\r
108 #define strtoull(x, y, z) _strtoui64(x, y, z)
\r
109 #define strtoll(x, y, z) _strtoi64(x, y, z)
\r
112 #define ERRNO GetLastError()
\r
113 #define NO_SOCKLEN_T
\r
114 #define SSL_LIB "ssleay32.dll"
\r
115 #define CRYPTO_LIB "libeay32.dll"
\r
116 #define O_NONBLOCK 0
\r
117 #if !defined(EWOULDBLOCK)
\r
118 #define EWOULDBLOCK WSAEWOULDBLOCK
\r
119 #endif // !EWOULDBLOCK
\r
121 #define INT64_FMT "I64d"
\r
123 #define WINCDECL __cdecl
\r
125 #define snprintf _snprintf
\r
126 #define vsnprintf _vsnprintf
\r
127 #define mg_sleep(x) Sleep(x)
\r
129 #define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
\r
130 #define popen(x, y) _popen(x, y)
\r
131 #define pclose(x) _pclose(x)
\r
132 #define close(x) _close(x)
\r
133 #define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y))
\r
134 #define RTLD_LAZY 0
\r
135 #define fseeko(x, y, z) _lseeki64(_fileno(x), (y), (z))
\r
136 #define fdopen(x, y) _fdopen((x), (y))
\r
137 #define write(x, y, z) _write((x), (y), (unsigned) z)
\r
138 #define read(x, y, z) _read((x), (y), (unsigned) z)
\r
139 #define flockfile(x) EnterCriticalSection(&global_log_file_lock)
\r
140 #define funlockfile(x) LeaveCriticalSection(&global_log_file_lock)
\r
141 #define sleep(x) Sleep((x) * 1000)
\r
142 #define va_copy(x, y) x = y
\r
144 #if !defined(fileno)
\r
145 #define fileno(x) _fileno(x)
\r
146 #endif // !fileno MINGW #defines fileno
\r
148 typedef HANDLE pthread_mutex_t;
\r
149 typedef struct {HANDLE signal, broadcast;} pthread_cond_t;
\r
150 typedef DWORD pthread_t;
\r
151 #define pid_t HANDLE // MINGW typedefs pid_t to int. Using #define here.
\r
153 static int pthread_mutex_lock(pthread_mutex_t *);
\r
154 static int pthread_mutex_unlock(pthread_mutex_t *);
\r
155 static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len);
\r
157 static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p);
\r
159 #if defined(HAVE_STDINT)
\r
160 #include <stdint.h>
\r
162 typedef unsigned int uint32_t;
\r
163 typedef unsigned short uint16_t;
\r
164 typedef unsigned __int64 uint64_t;
\r
165 typedef __int64 int64_t;
\r
166 #define INT64_MAX 9223372036854775807
\r
167 #endif // HAVE_STDINT
\r
169 // POSIX dirent interface
\r
171 char d_name[PATH_MAX];
\r
174 typedef struct DIR {
\r
176 WIN32_FIND_DATAW info;
\r
177 struct dirent result;
\r
190 // Mark required libraries
\r
191 #pragma comment(lib, "Ws2_32.lib")
\r
193 #else // UNIX specific
\r
194 #include <sys/wait.h>
\r
195 #include <sys/socket.h>
\r
196 #include <sys/poll.h>
\r
197 #include <netinet/in.h>
\r
198 #include <arpa/inet.h>
\r
199 #include <sys/time.h>
\r
200 #include <stdint.h>
\r
201 #include <inttypes.h>
\r
205 #include <unistd.h>
\r
206 #include <dirent.h>
\r
207 #if !defined(NO_SSL_DL) && !defined(NO_SSL)
\r
210 #include <pthread.h>
\r
211 #if defined(__MACH__)
\r
212 #define SSL_LIB "libssl.dylib"
\r
213 #define CRYPTO_LIB "libcrypto.dylib"
\r
215 #if !defined(SSL_LIB)
\r
216 #define SSL_LIB "libssl.so"
\r
218 #if !defined(CRYPTO_LIB)
\r
219 #define CRYPTO_LIB "libcrypto.so"
\r
225 #define closesocket(a) close(a)
\r
226 #define mg_mkdir(x, y) mkdir(x, y)
\r
227 #define mg_remove(x) remove(x)
\r
228 #define mg_rename(x, y) rename(x, y)
\r
229 #define mg_sleep(x) usleep((x) * 1000)
\r
230 #define ERRNO errno
\r
231 #define INVALID_SOCKET (-1)
\r
232 #define INT64_FMT PRId64
\r
233 typedef int SOCKET;
\r
236 #endif // End of Windows and UNIX specific includes
\r
238 #include "mongoose.h"
\r
242 #include <lauxlib.h>
\r
245 #define MONGOOSE_VERSION "3.7"
\r
246 #define PASSWORDS_FILE_NAME ".htpasswd"
\r
247 #define CGI_ENVIRONMENT_SIZE 4096
\r
248 #define MAX_CGI_ENVIR_VARS 64
\r
249 #define MG_BUF_LEN 8192
\r
250 #define MAX_REQUEST_SIZE 16384
\r
251 #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
\r
254 static CRITICAL_SECTION global_log_file_lock;
\r
255 static pthread_t pthread_self(void) {
\r
256 return GetCurrentThreadId();
\r
262 #define DEBUG_TRACE(x)
\r
265 #define DEBUG_TRACE(x) do { \
\r
266 flockfile(stdout); \
\r
267 printf("*** %lu.%p.%s.%d: ", \
\r
268 (unsigned long) time(NULL), (void *) pthread_self(), \
\r
269 __func__, __LINE__); \
\r
273 funlockfile(stdout); \
\r
276 #define DEBUG_TRACE(x)
\r
278 #endif // DEBUG_TRACE
\r
280 // Darwin prior to 7.0 and Win32 do not have socklen_t
\r
281 #ifdef NO_SOCKLEN_T
\r
282 typedef int socklen_t;
\r
283 #endif // NO_SOCKLEN_T
\r
284 #define _DARWIN_UNLIMITED_SELECT
\r
286 #if !defined(MSG_NOSIGNAL)
\r
287 #define MSG_NOSIGNAL 0
\r
290 #if !defined(SOMAXCONN)
\r
291 #define SOMAXCONN 100
\r
294 #if !defined(PATH_MAX)
\r
295 #define PATH_MAX 4096
\r
298 static const char *http_500_error = "Internal Server Error";
\r
300 #if defined(NO_SSL_DL)
\r
301 #include <openssl/ssl.h>
\r
303 // SSL loaded dynamically from DLL.
\r
304 // I put the prototypes here to be independent from OpenSSL source installation.
\r
305 typedef struct ssl_st SSL;
\r
306 typedef struct ssl_method_st SSL_METHOD;
\r
307 typedef struct ssl_ctx_st SSL_CTX;
\r
310 const char *name; // SSL function name
\r
311 void (*ptr)(void); // Function pointer
\r
314 #define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
\r
315 #define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
\r
316 #define SSL_connect (* (int (*)(SSL *)) ssl_sw[2].ptr)
\r
317 #define SSL_read (* (int (*)(SSL *, void *, int)) ssl_sw[3].ptr)
\r
318 #define SSL_write (* (int (*)(SSL *, const void *,int)) ssl_sw[4].ptr)
\r
319 #define SSL_get_error (* (int (*)(SSL *, int)) ssl_sw[5].ptr)
\r
320 #define SSL_set_fd (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr)
\r
321 #define SSL_new (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)
\r
322 #define SSL_CTX_new (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)
\r
323 #define SSLv23_server_method (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr)
\r
324 #define SSL_library_init (* (int (*)(void)) ssl_sw[10].ptr)
\r
325 #define SSL_CTX_use_PrivateKey_file (* (int (*)(SSL_CTX *, \
\r
326 const char *, int)) ssl_sw[11].ptr)
\r
327 #define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \
\r
328 const char *, int)) ssl_sw[12].ptr)
\r
329 #define SSL_CTX_set_default_passwd_cb \
\r
330 (* (void (*)(SSL_CTX *, mg_callback_t)) ssl_sw[13].ptr)
\r
331 #define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)
\r
332 #define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr)
\r
333 #define SSL_CTX_use_certificate_chain_file \
\r
334 (* (int (*)(SSL_CTX *, const char *)) ssl_sw[16].ptr)
\r
335 #define SSLv23_client_method (* (SSL_METHOD * (*)(void)) ssl_sw[17].ptr)
\r
336 #define SSL_pending (* (int (*)(SSL *)) ssl_sw[18].ptr)
\r
337 #define SSL_CTX_set_verify (* (void (*)(SSL_CTX *, int, int)) ssl_sw[19].ptr)
\r
339 #define CRYPTO_num_locks (* (int (*)(void)) crypto_sw[0].ptr)
\r
340 #define CRYPTO_set_locking_callback \
\r
341 (* (void (*)(void (*)(int, int, const char *, int))) crypto_sw[1].ptr)
\r
342 #define CRYPTO_set_id_callback \
\r
343 (* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr)
\r
344 #define ERR_get_error (* (unsigned long (*)(void)) crypto_sw[3].ptr)
\r
345 #define ERR_error_string (* (char * (*)(unsigned long,char *)) crypto_sw[4].ptr)
\r
347 // set_ssl_option() function updates this array.
\r
348 // It loads SSL library dynamically and changes NULLs to the actual addresses
\r
349 // of respective functions. The macros above (like SSL_connect()) are really
\r
350 // just calling these functions indirectly via the pointer.
\r
351 static struct ssl_func ssl_sw[] = {
\r
352 {"SSL_free", NULL},
\r
353 {"SSL_accept", NULL},
\r
354 {"SSL_connect", NULL},
\r
355 {"SSL_read", NULL},
\r
356 {"SSL_write", NULL},
\r
357 {"SSL_get_error", NULL},
\r
358 {"SSL_set_fd", NULL},
\r
360 {"SSL_CTX_new", NULL},
\r
361 {"SSLv23_server_method", NULL},
\r
362 {"SSL_library_init", NULL},
\r
363 {"SSL_CTX_use_PrivateKey_file", NULL},
\r
364 {"SSL_CTX_use_certificate_file",NULL},
\r
365 {"SSL_CTX_set_default_passwd_cb",NULL},
\r
366 {"SSL_CTX_free", NULL},
\r
367 {"SSL_load_error_strings", NULL},
\r
368 {"SSL_CTX_use_certificate_chain_file", NULL},
\r
369 {"SSLv23_client_method", NULL},
\r
370 {"SSL_pending", NULL},
\r
371 {"SSL_CTX_set_verify", NULL},
\r
375 // Similar array as ssl_sw. These functions could be located in different lib.
\r
376 #if !defined(NO_SSL)
\r
377 static struct ssl_func crypto_sw[] = {
\r
378 {"CRYPTO_num_locks", NULL},
\r
379 {"CRYPTO_set_locking_callback", NULL},
\r
380 {"CRYPTO_set_id_callback", NULL},
\r
381 {"ERR_get_error", NULL},
\r
382 {"ERR_error_string", NULL},
\r
386 #endif // NO_SSL_DL
\r
388 static const char *month_names[] = {
\r
389 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
\r
390 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
\r
393 // Unified socket address. For IPv6 support, add IPv6 address structure
\r
396 struct sockaddr sa;
\r
397 struct sockaddr_in sin;
\r
398 #if defined(USE_IPV6)
\r
399 struct sockaddr_in6 sin6;
\r
403 // Describes a string (chunk of memory).
\r
411 time_t modification_time;
\r
414 const char *membuf; // Non-NULL if file data is in memory
\r
416 #define STRUCT_FILE_INITIALIZER {0, 0, 0, NULL, NULL}
\r
418 // Describes listening socket, or socket which was accept()-ed by the master
\r
419 // thread and queued for future handling by the worker thread.
\r
421 SOCKET sock; // Listening socket
\r
422 union usa lsa; // Local socket address
\r
423 union usa rsa; // Remote socket address
\r
424 unsigned is_ssl:1; // Is port SSL-ed
\r
425 unsigned ssl_redir:1; // Is port supposed to redirect everything to SSL port
\r
428 // NOTE(lsm): this enum shoulds be in sync with the config_options below.
\r
430 CGI_EXTENSIONS, CGI_ENVIRONMENT, PUT_DELETE_PASSWORDS_FILE, CGI_INTERPRETER,
\r
431 PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS, THROTTLE,
\r
432 ACCESS_LOG_FILE, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE,
\r
433 GLOBAL_PASSWORDS_FILE, INDEX_FILES, ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST,
\r
434 EXTRA_MIME_TYPES, LISTENING_PORTS, DOCUMENT_ROOT, SSL_CERTIFICATE,
\r
435 NUM_THREADS, RUN_AS_USER, REWRITE, HIDE_FILES, REQUEST_TIMEOUT,
\r
439 static const char *config_options[] = {
\r
440 "C", "cgi_pattern", "**.cgi$|**.pl$|**.php$",
\r
441 "E", "cgi_environment", NULL,
\r
442 "G", "put_delete_auth_file", NULL,
\r
443 "I", "cgi_interpreter", NULL,
\r
444 "P", "protect_uri", NULL,
\r
445 "R", "authentication_domain", "mydomain.com",
\r
446 "S", "ssi_pattern", "**.shtml$|**.shtm$",
\r
447 "T", "throttle", NULL,
\r
448 "a", "access_log_file", NULL,
\r
449 "d", "enable_directory_listing", "yes",
\r
450 "e", "error_log_file", NULL,
\r
451 "g", "global_auth_file", NULL,
\r
452 "i", "index_files", "index.html,index.htm,index.cgi,index.shtml,index.php",
\r
453 "k", "enable_keep_alive", "no",
\r
454 "l", "access_control_list", NULL,
\r
455 "m", "extra_mime_types", NULL,
\r
456 "p", "listening_ports", "8080",
\r
457 "r", "document_root", ".",
\r
458 "s", "ssl_certificate", NULL,
\r
459 "t", "num_threads", "20",
\r
460 "u", "run_as_user", NULL,
\r
461 "w", "url_rewrite_patterns", NULL,
\r
462 "x", "hide_files_patterns", NULL,
\r
463 "z", "request_timeout_ms", "30000",
\r
466 #define ENTRIES_PER_CONFIG_OPTION 3
\r
468 struct mg_context {
\r
469 volatile int stop_flag; // Should we stop event loop
\r
470 SSL_CTX *ssl_ctx; // SSL context
\r
471 char *config[NUM_OPTIONS]; // Mongoose configuration parameters
\r
472 struct mg_callbacks callbacks; // User-defined callback function
\r
473 void *user_data; // User-defined data
\r
475 struct socket *listening_sockets;
\r
476 int num_listening_sockets;
\r
478 volatile int num_threads; // Number of threads
\r
479 pthread_mutex_t mutex; // Protects (max|num)_threads
\r
480 pthread_cond_t cond; // Condvar for tracking workers terminations
\r
482 struct socket queue[20]; // Accepted sockets
\r
483 volatile int sq_head; // Head of the socket queue
\r
484 volatile int sq_tail; // Tail of the socket queue
\r
485 pthread_cond_t sq_full; // Signaled when socket is produced
\r
486 pthread_cond_t sq_empty; // Signaled when socket is consumed
\r
489 struct mg_connection {
\r
490 struct mg_request_info request_info;
\r
491 struct mg_context *ctx;
\r
492 SSL *ssl; // SSL descriptor
\r
493 SSL_CTX *client_ssl_ctx; // SSL context for client connections
\r
494 struct socket client; // Connected client
\r
495 time_t birth_time; // Time when request was received
\r
496 int64_t num_bytes_sent; // Total bytes sent to client
\r
497 int64_t content_len; // Content-Length header value
\r
498 int64_t consumed_content; // How many bytes of content have been read
\r
499 char *buf; // Buffer for received data
\r
500 char *path_info; // PATH_INFO part of the URL
\r
501 int must_close; // 1 if connection must be closed
\r
502 int buf_size; // Buffer size
\r
503 int request_len; // Size of the request + headers in a buffer
\r
504 int data_len; // Total size of data in a buffer
\r
505 int status_code; // HTTP reply status code, e.g. 200
\r
506 int throttle; // Throttling, bytes/sec. <= 0 means no throttle
\r
507 time_t last_throttle_time; // Last time throttled data was sent
\r
508 int64_t last_throttle_bytes;// Bytes sent this second
\r
511 const char **mg_get_valid_option_names(void) {
\r
512 return config_options;
\r
515 static int is_file_in_memory(struct mg_connection *conn, const char *path,
\r
516 struct file *filep) {
\r
518 if ((filep->membuf = conn->ctx->callbacks.open_file == NULL ? NULL :
\r
519 conn->ctx->callbacks.open_file(conn, path, &size)) != NULL) {
\r
520 // NOTE: override filep->size only on success. Otherwise, it might break
\r
521 // constructs like if (!mg_stat() || !mg_fopen()) ...
\r
522 filep->size = size;
\r
524 return filep->membuf != NULL;
\r
527 static int is_file_opened(const struct file *filep) {
\r
528 return filep->membuf != NULL || filep->fp != NULL;
\r
531 static int mg_fopen(struct mg_connection *conn, const char *path,
\r
532 const char *mode, struct file *filep) {
\r
533 if (!is_file_in_memory(conn, path, filep)) {
\r
535 wchar_t wbuf[PATH_MAX], wmode[20];
\r
536 to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
\r
537 MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode));
\r
538 filep->fp = _wfopen(wbuf, wmode);
\r
540 filep->fp = fopen(path, mode);
\r
544 return is_file_opened(filep);
\r
547 static void mg_fclose(struct file *filep) {
\r
548 if (filep != NULL && filep->fp != NULL) {
\r
553 static int get_option_index(const char *name) {
\r
556 for (i = 0; config_options[i] != NULL; i += ENTRIES_PER_CONFIG_OPTION) {
\r
557 if (strcmp(config_options[i], name) == 0 ||
\r
558 strcmp(config_options[i + 1], name) == 0) {
\r
559 return i / ENTRIES_PER_CONFIG_OPTION;
\r
565 const char *mg_get_option(const struct mg_context *ctx, const char *name) {
\r
567 if ((i = get_option_index(name)) == -1) {
\r
569 } else if (ctx->config[i] == NULL) {
\r
572 return ctx->config[i];
\r
576 static void sockaddr_to_string(char *buf, size_t len,
\r
577 const union usa *usa) {
\r
579 #if defined(USE_IPV6)
\r
580 inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ?
\r
581 (void *) &usa->sin.sin_addr :
\r
582 (void *) &usa->sin6.sin6_addr, buf, len);
\r
583 #elif defined(_WIN32)
\r
584 // Only Windoze Vista (and newer) have inet_ntop()
\r
585 strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
\r
587 inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len);
\r
591 static void cry(struct mg_connection *conn,
\r
592 PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
\r
594 // Print error message to the opened error log stream.
\r
595 static void cry(struct mg_connection *conn, const char *fmt, ...) {
\r
596 char buf[MG_BUF_LEN], src_addr[20];
\r
602 (void) vsnprintf(buf, sizeof(buf), fmt, ap);
\r
605 // Do not lock when getting the callback value, here and below.
\r
606 // I suppose this is fine, since function cannot disappear in the
\r
607 // same way string option can.
\r
608 if (conn->ctx->callbacks.log_message == NULL ||
\r
609 conn->ctx->callbacks.log_message(conn, buf) == 0) {
\r
610 fp = conn->ctx == NULL || conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
\r
611 fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");
\r
615 timestamp = time(NULL);
\r
617 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
\r
618 fprintf(fp, "[%010lu] [error] [client %s] ", (unsigned long) timestamp,
\r
621 if (conn->request_info.request_method != NULL) {
\r
622 fprintf(fp, "%s %s: ", conn->request_info.request_method,
\r
623 conn->request_info.uri);
\r
626 fprintf(fp, "%s", buf);
\r
634 // Return fake connection structure. Used for logging, if connection
\r
635 // is not applicable at the moment of logging.
\r
636 static struct mg_connection *fc(struct mg_context *ctx) {
\r
637 static struct mg_connection fake_connection;
\r
638 fake_connection.ctx = ctx;
\r
639 return &fake_connection;
\r
642 const char *mg_version(void) {
\r
643 return MONGOOSE_VERSION;
\r
646 struct mg_request_info *mg_get_request_info(struct mg_connection *conn) {
\r
647 return &conn->request_info;
\r
650 static void mg_strlcpy(register char *dst, register const char *src, size_t n) {
\r
651 for (; *src != '\0' && n > 1; n--) {
\r
657 static int lowercase(const char *s) {
\r
658 return tolower(* (const unsigned char *) s);
\r
661 static int mg_strncasecmp(const char *s1, const char *s2, size_t len) {
\r
666 diff = lowercase(s1++) - lowercase(s2++);
\r
667 } while (diff == 0 && s1[-1] != '\0' && --len > 0);
\r
672 static int mg_strcasecmp(const char *s1, const char *s2) {
\r
676 diff = lowercase(s1++) - lowercase(s2++);
\r
677 } while (diff == 0 && s1[-1] != '\0');
\r
682 static char * mg_strndup(const char *ptr, size_t len) {
\r
685 if ((p = (char *) malloc(len + 1)) != NULL) {
\r
686 mg_strlcpy(p, ptr, len + 1);
\r
692 static char * mg_strdup(const char *str) {
\r
693 return mg_strndup(str, strlen(str));
\r
696 // Like snprintf(), but never returns negative value, or a value
\r
697 // that is larger than a supplied buffer.
\r
698 // Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability
\r
699 // in his audit report.
\r
700 static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen,
\r
701 const char *fmt, va_list ap) {
\r
707 n = vsnprintf(buf, buflen, fmt, ap);
\r
710 cry(conn, "vsnprintf error");
\r
712 } else if (n >= (int) buflen) {
\r
713 cry(conn, "truncating vsnprintf buffer: [%.*s]",
\r
714 n > 200 ? 200 : n, buf);
\r
715 n = (int) buflen - 1;
\r
722 static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
\r
723 PRINTF_FORMAT_STRING(const char *fmt), ...)
\r
726 static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
\r
727 const char *fmt, ...) {
\r
732 n = mg_vsnprintf(conn, buf, buflen, fmt, ap);
\r
738 // Skip the characters until one of the delimiters characters found.
\r
739 // 0-terminate resulting word. Skip the delimiter and following whitespaces.
\r
740 // Advance pointer to buffer to the next word. Return found 0-terminated word.
\r
741 // Delimiters can be quoted with quotechar.
\r
742 static char *skip_quoted(char **buf, const char *delimiters,
\r
743 const char *whitespace, char quotechar) {
\r
744 char *p, *begin_word, *end_word, *end_whitespace;
\r
747 end_word = begin_word + strcspn(begin_word, delimiters);
\r
749 // Check for quotechar
\r
750 if (end_word > begin_word) {
\r
752 while (*p == quotechar) {
\r
753 // If there is anything beyond end_word, copy it
\r
754 if (*end_word == '\0') {
\r
758 size_t end_off = strcspn(end_word + 1, delimiters);
\r
759 memmove (p, end_word, end_off + 1);
\r
760 p += end_off; // p must correspond to end_word - 1
\r
761 end_word += end_off + 1;
\r
764 for (p++; p < end_word; p++) {
\r
769 if (*end_word == '\0') {
\r
772 end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace);
\r
774 for (p = end_word; p < end_whitespace; p++) {
\r
778 *buf = end_whitespace;
\r
784 // Simplified version of skip_quoted without quote char
\r
785 // and whitespace == delimiters
\r
786 static char *skip(char **buf, const char *delimiters) {
\r
787 return skip_quoted(buf, delimiters, delimiters, 0);
\r
791 // Return HTTP header value, or NULL if not found.
\r
792 static const char *get_header(const struct mg_request_info *ri,
\r
793 const char *name) {
\r
796 for (i = 0; i < ri->num_headers; i++)
\r
797 if (!mg_strcasecmp(name, ri->http_headers[i].name))
\r
798 return ri->http_headers[i].value;
\r
803 const char *mg_get_header(const struct mg_connection *conn, const char *name) {
\r
804 return get_header(&conn->request_info, name);
\r
807 // A helper function for traversing a comma separated list of values.
\r
808 // It returns a list pointer shifted to the next value, or NULL if the end
\r
809 // of the list found.
\r
810 // Value is stored in val vector. If value has form "x=y", then eq_val
\r
811 // vector is initialized to point to the "y" part, and val vector length
\r
812 // is adjusted to point only to "x".
\r
813 static const char *next_option(const char *list, struct vec *val,
\r
814 struct vec *eq_val) {
\r
815 if (list == NULL || *list == '\0') {
\r
820 if ((list = strchr(val->ptr, ',')) != NULL) {
\r
821 // Comma found. Store length and shift the list ptr
\r
822 val->len = list - val->ptr;
\r
825 // This value is the last one
\r
826 list = val->ptr + strlen(val->ptr);
\r
827 val->len = list - val->ptr;
\r
830 if (eq_val != NULL) {
\r
831 // Value has form "x=y", adjust pointers and lengths
\r
832 // so that val points to "x", and eq_val points to "y".
\r
834 eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len);
\r
835 if (eq_val->ptr != NULL) {
\r
836 eq_val->ptr++; // Skip over '=' character
\r
837 eq_val->len = val->ptr + val->len - eq_val->ptr;
\r
838 val->len = (eq_val->ptr - val->ptr) - 1;
\r
846 static int match_prefix(const char *pattern, int pattern_len, const char *str) {
\r
847 const char *or_str;
\r
848 int i, j, len, res;
\r
850 if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) {
\r
851 res = match_prefix(pattern, or_str - pattern, str);
\r
852 return res > 0 ? res :
\r
853 match_prefix(or_str + 1, (pattern + pattern_len) - (or_str + 1), str);
\r
858 for (; i < pattern_len; i++, j++) {
\r
859 if (pattern[i] == '?' && str[j] != '\0') {
\r
861 } else if (pattern[i] == '$') {
\r
862 return str[j] == '\0' ? j : -1;
\r
863 } else if (pattern[i] == '*') {
\r
865 if (pattern[i] == '*') {
\r
867 len = (int) strlen(str + j);
\r
869 len = (int) strcspn(str + j, "/");
\r
871 if (i == pattern_len) {
\r
875 res = match_prefix(pattern + i, pattern_len - i, str + j + len);
\r
876 } while (res == -1 && len-- > 0);
\r
877 return res == -1 ? -1 : j + res + len;
\r
878 } else if (pattern[i] != str[j]) {
\r
885 // HTTP 1.1 assumes keep alive if "Connection:" header is not set
\r
886 // This function must tolerate situations when connection info is not
\r
887 // set up, for example if request parsing failed.
\r
888 static int should_keep_alive(const struct mg_connection *conn) {
\r
889 const char *http_version = conn->request_info.http_version;
\r
890 const char *header = mg_get_header(conn, "Connection");
\r
891 if (conn->must_close ||
\r
892 conn->status_code == 401 ||
\r
893 mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 ||
\r
894 (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) ||
\r
895 (header == NULL && http_version && strcmp(http_version, "1.1"))) {
\r
901 static const char *suggest_connection_header(const struct mg_connection *conn) {
\r
902 return should_keep_alive(conn) ? "keep-alive" : "close";
\r
905 static void send_http_error(struct mg_connection *, int, const char *,
\r
906 PRINTF_FORMAT_STRING(const char *fmt), ...)
\r
910 static void send_http_error(struct mg_connection *conn, int status,
\r
911 const char *reason, const char *fmt, ...) {
\r
912 char buf[MG_BUF_LEN];
\r
916 conn->status_code = status;
\r
919 // Errors 1xx, 204 and 304 MUST NOT send a body
\r
920 if (status > 199 && status != 204 && status != 304) {
\r
921 len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
\r
925 len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap);
\r
928 DEBUG_TRACE(("[%s]", buf));
\r
930 mg_printf(conn, "HTTP/1.1 %d %s\r\n"
\r
931 "Content-Length: %d\r\n"
\r
932 "Connection: %s\r\n\r\n", status, reason, len,
\r
933 suggest_connection_header(conn));
\r
934 conn->num_bytes_sent += mg_printf(conn, "%s", buf);
\r
937 #if defined(_WIN32) && !defined(__SYMBIAN32__)
\r
938 static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) {
\r
940 *mutex = CreateMutex(NULL, FALSE, NULL);
\r
941 return *mutex == NULL ? -1 : 0;
\r
944 static int pthread_mutex_destroy(pthread_mutex_t *mutex) {
\r
945 return CloseHandle(*mutex) == 0 ? -1 : 0;
\r
948 static int pthread_mutex_lock(pthread_mutex_t *mutex) {
\r
949 return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
\r
952 static int pthread_mutex_unlock(pthread_mutex_t *mutex) {
\r
953 return ReleaseMutex(*mutex) == 0 ? -1 : 0;
\r
956 static int pthread_cond_init(pthread_cond_t *cv, const void *unused) {
\r
958 cv->signal = CreateEvent(NULL, FALSE, FALSE, NULL);
\r
959 cv->broadcast = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
960 return cv->signal != NULL && cv->broadcast != NULL ? 0 : -1;
\r
963 static int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) {
\r
964 HANDLE handles[] = {cv->signal, cv->broadcast};
\r
965 ReleaseMutex(*mutex);
\r
966 WaitForMultipleObjects(2, handles, FALSE, INFINITE);
\r
967 return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
\r
970 static int pthread_cond_signal(pthread_cond_t *cv) {
\r
971 return SetEvent(cv->signal) == 0 ? -1 : 0;
\r
974 static int pthread_cond_broadcast(pthread_cond_t *cv) {
\r
975 // Implementation with PulseEvent() has race condition, see
\r
976 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
\r
977 return PulseEvent(cv->broadcast) == 0 ? -1 : 0;
\r
980 static int pthread_cond_destroy(pthread_cond_t *cv) {
\r
981 return CloseHandle(cv->signal) && CloseHandle(cv->broadcast) ? 0 : -1;
\r
984 // For Windows, change all slashes to backslashes in path names.
\r
985 static void change_slashes_to_backslashes(char *path) {
\r
988 for (i = 0; path[i] != '\0'; i++) {
\r
989 if (path[i] == '/')
\r
991 // i > 0 check is to preserve UNC paths, like \\server\file.txt
\r
992 if (path[i] == '\\' && i > 0)
\r
993 while (path[i + 1] == '\\' || path[i + 1] == '/')
\r
994 (void) memmove(path + i + 1,
\r
995 path + i + 2, strlen(path + i + 1));
\r
999 // Encode 'path' which is assumed UTF-8 string, into UNICODE string.
\r
1000 // wbuf and wbuf_len is a target buffer and its length.
\r
1001 static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) {
\r
1002 char buf[PATH_MAX], buf2[PATH_MAX], *p;
\r
1004 mg_strlcpy(buf, path, sizeof(buf));
\r
1005 change_slashes_to_backslashes(buf);
\r
1007 // Point p to the end of the file name
\r
1008 p = buf + strlen(buf) - 1;
\r
1010 // Convert to Unicode and back. If doubly-converted string does not
\r
1011 // match the original, something is fishy, reject.
\r
1012 memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
\r
1013 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
\r
1014 WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
\r
1016 if (strcmp(buf, buf2) != 0) {
\r
1021 #if defined(_WIN32_WCE)
\r
1022 static time_t time(time_t *ptime) {
\r
1027 GetSystemTime(&st);
\r
1028 SystemTimeToFileTime(&st, &ft);
\r
1029 t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
\r
1031 if (ptime != NULL) {
\r
1038 static struct tm *localtime(const time_t *ptime, struct tm *ptm) {
\r
1039 int64_t t = ((int64_t) *ptime) * RATE_DIFF + EPOCH_DIFF;
\r
1042 TIME_ZONE_INFORMATION tzinfo;
\r
1044 if (ptm == NULL) {
\r
1048 * (int64_t *) &ft = t;
\r
1049 FileTimeToLocalFileTime(&ft, &lft);
\r
1050 FileTimeToSystemTime(&lft, &st);
\r
1051 ptm->tm_year = st.wYear - 1900;
\r
1052 ptm->tm_mon = st.wMonth - 1;
\r
1053 ptm->tm_wday = st.wDayOfWeek;
\r
1054 ptm->tm_mday = st.wDay;
\r
1055 ptm->tm_hour = st.wHour;
\r
1056 ptm->tm_min = st.wMinute;
\r
1057 ptm->tm_sec = st.wSecond;
\r
1058 ptm->tm_yday = 0; // hope nobody uses this
\r
1060 GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0;
\r
1065 static struct tm *gmtime(const time_t *ptime, struct tm *ptm) {
\r
1066 // FIXME(lsm): fix this.
\r
1067 return localtime(ptime, ptm);
\r
1070 static size_t strftime(char *dst, size_t dst_size, const char *fmt,
\r
1071 const struct tm *tm) {
\r
1072 (void) snprintf(dst, dst_size, "implement strftime() for WinCE");
\r
1077 static int mg_rename(const char* oldname, const char* newname) {
\r
1078 wchar_t woldbuf[PATH_MAX];
\r
1079 wchar_t wnewbuf[PATH_MAX];
\r
1081 to_unicode(oldname, woldbuf, ARRAY_SIZE(woldbuf));
\r
1082 to_unicode(newname, wnewbuf, ARRAY_SIZE(wnewbuf));
\r
1084 return MoveFileW(woldbuf, wnewbuf) ? 0 : -1;
\r
1087 // Windows happily opens files with some garbage at the end of file name.
\r
1088 // For example, fopen("a.cgi ", "r") on Windows successfully opens
\r
1089 // "a.cgi", despite one would expect an error back.
\r
1090 // This function returns non-0 if path ends with some garbage.
\r
1091 static int path_cannot_disclose_cgi(const char *path) {
\r
1092 static const char *allowed_last_characters = "_-";
\r
1093 int last = path[strlen(path) - 1];
\r
1094 return isalnum(last) || strchr(allowed_last_characters, last) != NULL;
\r
1097 static int mg_stat(struct mg_connection *conn, const char *path,
\r
1098 struct file *filep) {
\r
1099 wchar_t wbuf[PATH_MAX];
\r
1100 WIN32_FILE_ATTRIBUTE_DATA info;
\r
1102 if (!is_file_in_memory(conn, path, filep)) {
\r
1103 to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
\r
1104 if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
\r
1105 filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
\r
1106 filep->modification_time = SYS2UNIX_TIME(
\r
1107 info.ftLastWriteTime.dwLowDateTime,
\r
1108 info.ftLastWriteTime.dwHighDateTime);
\r
1109 filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
\r
1110 // If file name is fishy, reset the file structure and return error.
\r
1111 // Note it is important to reset, not just return the error, cause
\r
1112 // functions like is_file_opened() check the struct.
\r
1113 if (!filep->is_directory && !path_cannot_disclose_cgi(path)) {
\r
1114 memset(filep, 0, sizeof(*filep));
\r
1119 return filep->membuf != NULL || filep->modification_time != 0;
\r
1122 static int mg_remove(const char *path) {
\r
1123 wchar_t wbuf[PATH_MAX];
\r
1124 to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
\r
1125 return DeleteFileW(wbuf) ? 0 : -1;
\r
1128 static int mg_mkdir(const char *path, int mode) {
\r
1129 char buf[PATH_MAX];
\r
1130 wchar_t wbuf[PATH_MAX];
\r
1132 mode = 0; // Unused
\r
1133 mg_strlcpy(buf, path, sizeof(buf));
\r
1134 change_slashes_to_backslashes(buf);
\r
1136 (void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, ARRAY_SIZE(wbuf));
\r
1138 return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
\r
1141 // Implementation of POSIX opendir/closedir/readdir for Windows.
\r
1142 static DIR * opendir(const char *name) {
\r
1144 wchar_t wpath[PATH_MAX];
\r
1147 if (name == NULL) {
\r
1148 SetLastError(ERROR_BAD_ARGUMENTS);
\r
1149 } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
\r
1150 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
\r
1152 to_unicode(name, wpath, ARRAY_SIZE(wpath));
\r
1153 attrs = GetFileAttributesW(wpath);
\r
1154 if (attrs != 0xFFFFFFFF &&
\r
1155 ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
\r
1156 (void) wcscat(wpath, L"\\*");
\r
1157 dir->handle = FindFirstFileW(wpath, &dir->info);
\r
1158 dir->result.d_name[0] = '\0';
\r
1168 static int closedir(DIR *dir) {
\r
1171 if (dir != NULL) {
\r
1172 if (dir->handle != INVALID_HANDLE_VALUE)
\r
1173 result = FindClose(dir->handle) ? 0 : -1;
\r
1178 SetLastError(ERROR_BAD_ARGUMENTS);
\r
1184 static struct dirent *readdir(DIR *dir) {
\r
1185 struct dirent *result = 0;
\r
1188 if (dir->handle != INVALID_HANDLE_VALUE) {
\r
1189 result = &dir->result;
\r
1190 (void) WideCharToMultiByte(CP_UTF8, 0,
\r
1191 dir->info.cFileName, -1, result->d_name,
\r
1192 sizeof(result->d_name), NULL, NULL);
\r
1194 if (!FindNextFileW(dir->handle, &dir->info)) {
\r
1195 (void) FindClose(dir->handle);
\r
1196 dir->handle = INVALID_HANDLE_VALUE;
\r
1200 SetLastError(ERROR_FILE_NOT_FOUND);
\r
1203 SetLastError(ERROR_BAD_ARGUMENTS);
\r
1210 static int poll(struct pollfd *pfd, int n, int milliseconds) {
\r
1211 struct timeval tv;
\r
1215 tv.tv_sec = milliseconds / 1000;
\r
1216 tv.tv_usec = (milliseconds % 1000) * 1000;
\r
1219 for (i = 0; i < n; i++) {
\r
1220 FD_SET((SOCKET) pfd[i].fd, &set);
\r
1221 pfd[i].revents = 0;
\r
1224 if ((result = select(0, &set, NULL, NULL, &tv)) > 0) {
\r
1225 for (i = 0; i < n; i++) {
\r
1226 if (FD_ISSET(pfd[i].fd, &set)) {
\r
1227 pfd[i].revents = POLLIN;
\r
1234 #endif // HAVE_POLL
\r
1236 #define set_close_on_exec(x) // No FD_CLOEXEC on Windows
\r
1238 int mg_start_thread(mg_thread_func_t f, void *p) {
\r
1239 return _beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0;
\r
1242 static HANDLE dlopen(const char *dll_name, int flags) {
\r
1243 wchar_t wbuf[PATH_MAX];
\r
1244 flags = 0; // Unused
\r
1245 to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf));
\r
1246 return LoadLibraryW(wbuf);
\r
1249 #if !defined(NO_CGI)
\r
1251 static int kill(pid_t pid, int sig_num) {
\r
1252 (void) TerminateProcess(pid, sig_num);
\r
1253 (void) CloseHandle(pid);
\r
1257 static void trim_trailing_whitespaces(char *s) {
\r
1258 char *e = s + strlen(s) - 1;
\r
1259 while (e > s && isspace(* (unsigned char *) e)) {
\r
1264 static pid_t spawn_process(struct mg_connection *conn, const char *prog,
\r
1265 char *envblk, char *envp[], int fd_stdin,
\r
1266 int fd_stdout, const char *dir) {
\r
1268 char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX],
\r
1269 cmdline[PATH_MAX], buf[PATH_MAX];
\r
1270 struct file file = STRUCT_FILE_INITIALIZER;
\r
1271 STARTUPINFOA si = { sizeof(si) };
\r
1272 PROCESS_INFORMATION pi = { 0 };
\r
1274 envp = NULL; // Unused
\r
1276 // TODO(lsm): redirect CGI errors to the error log file
\r
1277 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
\r
1278 si.wShowWindow = SW_HIDE;
\r
1280 me = GetCurrentProcess();
\r
1281 DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdin), me,
\r
1282 &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
\r
1283 DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdout), me,
\r
1284 &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
\r
1286 // If CGI file is a script, try to read the interpreter line
\r
1287 interp = conn->ctx->config[CGI_INTERPRETER];
\r
1288 if (interp == NULL) {
\r
1289 buf[0] = buf[1] = '\0';
\r
1291 // Read the first line of the script into the buffer
\r
1292 snprintf(cmdline, sizeof(cmdline), "%s%c%s", dir, '/', prog);
\r
1293 if (mg_fopen(conn, cmdline, "r", &file)) {
\r
1294 p = (char *) file.membuf;
\r
1295 mg_fgets(buf, sizeof(buf), &file, &p);
\r
1297 buf[sizeof(buf) - 1] = '\0';
\r
1300 if (buf[0] == '#' && buf[1] == '!') {
\r
1301 trim_trailing_whitespaces(buf + 2);
\r
1308 if (interp[0] != '\0') {
\r
1309 GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL);
\r
1310 interp = full_interp;
\r
1312 GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL);
\r
1314 mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%s%s\\%s",
\r
1315 interp, interp[0] == '\0' ? "" : " ", full_dir, prog);
\r
1317 DEBUG_TRACE(("Running [%s]", cmdline));
\r
1318 if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
\r
1319 CREATE_NEW_PROCESS_GROUP, envblk, NULL, &si, &pi) == 0) {
\r
1320 cry(conn, "%s: CreateProcess(%s): %d",
\r
1321 __func__, cmdline, ERRNO);
\r
1322 pi.hProcess = (pid_t) -1;
\r
1325 // Always close these to prevent handle leakage.
\r
1326 (void) close(fd_stdin);
\r
1327 (void) close(fd_stdout);
\r
1329 (void) CloseHandle(si.hStdOutput);
\r
1330 (void) CloseHandle(si.hStdInput);
\r
1331 (void) CloseHandle(pi.hThread);
\r
1333 return (pid_t) pi.hProcess;
\r
1337 static int set_non_blocking_mode(SOCKET sock) {
\r
1338 unsigned long on = 1;
\r
1339 return ioctlsocket(sock, FIONBIO, &on);
\r
1343 static int mg_stat(struct mg_connection *conn, const char *path,
\r
1344 struct file *filep) {
\r
1347 if (!is_file_in_memory(conn, path, filep) && !stat(path, &st)) {
\r
1348 filep->size = st.st_size;
\r
1349 filep->modification_time = st.st_mtime;
\r
1350 filep->is_directory = S_ISDIR(st.st_mode);
\r
1352 filep->modification_time = (time_t) 0;
\r
1355 return filep->membuf != NULL || filep->modification_time != (time_t) 0;
\r
1358 static void set_close_on_exec(int fd) {
\r
1359 fcntl(fd, F_SETFD, FD_CLOEXEC);
\r
1362 int mg_start_thread(mg_thread_func_t func, void *param) {
\r
1363 pthread_t thread_id;
\r
1364 pthread_attr_t attr;
\r
1366 (void) pthread_attr_init(&attr);
\r
1367 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
\r
1368 // TODO(lsm): figure out why mongoose dies on Linux if next line is enabled
\r
1369 // (void) pthread_attr_setstacksize(&attr, sizeof(struct mg_connection) * 5);
\r
1371 return pthread_create(&thread_id, &attr, func, param);
\r
1375 static pid_t spawn_process(struct mg_connection *conn, const char *prog,
\r
1376 char *envblk, char *envp[], int fd_stdin,
\r
1377 int fd_stdout, const char *dir) {
\r
1379 const char *interp;
\r
1383 if ((pid = fork()) == -1) {
\r
1385 send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO));
\r
1386 } else if (pid == 0) {
\r
1388 if (chdir(dir) != 0) {
\r
1389 cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
\r
1390 } else if (dup2(fd_stdin, 0) == -1) {
\r
1391 cry(conn, "%s: dup2(%d, 0): %s", __func__, fd_stdin, strerror(ERRNO));
\r
1392 } else if (dup2(fd_stdout, 1) == -1) {
\r
1393 cry(conn, "%s: dup2(%d, 1): %s", __func__, fd_stdout, strerror(ERRNO));
\r
1395 (void) dup2(fd_stdout, 2);
\r
1396 (void) close(fd_stdin);
\r
1397 (void) close(fd_stdout);
\r
1399 // After exec, all signal handlers are restored to their default values,
\r
1400 // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
\r
1401 // implementation, SIGCHLD's handler will leave unchanged after exec
\r
1402 // if it was set to be ignored. Restore it to default action.
\r
1403 signal(SIGCHLD, SIG_DFL);
\r
1405 interp = conn->ctx->config[CGI_INTERPRETER];
\r
1406 if (interp == NULL) {
\r
1407 (void) execle(prog, prog, NULL, envp);
\r
1408 cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
\r
1410 (void) execle(interp, interp, prog, NULL, envp);
\r
1411 cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
\r
1415 exit(EXIT_FAILURE);
\r
1418 // Parent. Close stdio descriptors
\r
1419 (void) close(fd_stdin);
\r
1420 (void) close(fd_stdout);
\r
1426 static int set_non_blocking_mode(SOCKET sock) {
\r
1429 flags = fcntl(sock, F_GETFL, 0);
\r
1430 (void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
\r
1436 // Write data to the IO channel - opened file descriptor, socket or SSL
\r
1437 // descriptor. Return number of bytes written.
\r
1438 static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
\r
1444 while (sent < len) {
\r
1446 // How many bytes we send in this iteration
\r
1447 k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
\r
1450 if (ssl != NULL) {
\r
1451 n = SSL_write(ssl, buf + sent, k);
\r
1455 n = (int) fwrite(buf + sent, 1, (size_t) k, fp);
\r
1459 n = send(sock, buf + sent, (size_t) k, MSG_NOSIGNAL);
\r
1471 // Read from IO channel - opened file descriptor, socket, or SSL descriptor.
\r
1472 // Return negative value on error, or number of bytes read on success.
\r
1473 static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
\r
1477 // Use read() instead of fread(), because if we're reading from the CGI
\r
1478 // pipe, fread() may block until IO buffer is filled up. We cannot afford
\r
1479 // to block and must pass all read bytes immediately to the client.
\r
1480 nread = read(fileno(fp), buf, (size_t) len);
\r
1482 } else if (conn->ssl != NULL) {
\r
1483 nread = SSL_read(conn->ssl, buf, len);
\r
1486 nread = recv(conn->client.sock, buf, (size_t) len, 0);
\r
1489 return conn->ctx->stop_flag ? -1 : nread;
\r
1492 int mg_read(struct mg_connection *conn, void *buf, size_t len) {
\r
1493 int n, buffered_len, nread;
\r
1497 if (conn->consumed_content < conn->content_len) {
\r
1498 // Adjust number of bytes to read.
\r
1499 int64_t to_read = conn->content_len - conn->consumed_content;
\r
1500 if (to_read < (int64_t) len) {
\r
1501 len = (size_t) to_read;
\r
1504 // Return buffered data
\r
1505 body = conn->buf + conn->request_len + conn->consumed_content;
\r
1506 buffered_len = &conn->buf[conn->data_len] - body;
\r
1507 if (buffered_len > 0) {
\r
1508 if (len < (size_t) buffered_len) {
\r
1509 buffered_len = (int) len;
\r
1511 memcpy(buf, body, (size_t) buffered_len);
\r
1512 len -= buffered_len;
\r
1513 conn->consumed_content += buffered_len;
\r
1514 nread += buffered_len;
\r
1515 buf = (char *) buf + buffered_len;
\r
1518 // We have returned all buffered data. Read new data from the remote socket.
\r
1520 n = pull(NULL, conn, (char *) buf, (int) len);
\r
1522 nread = n; // Propagate the error
\r
1524 } else if (n == 0) {
\r
1525 break; // No more data to read
\r
1527 buf = (char *) buf + n;
\r
1528 conn->consumed_content += n;
\r
1537 int mg_write(struct mg_connection *conn, const void *buf, size_t len) {
\r
1539 int64_t n, total, allowed;
\r
1541 if (conn->throttle > 0) {
\r
1542 if ((now = time(NULL)) != conn->last_throttle_time) {
\r
1543 conn->last_throttle_time = now;
\r
1544 conn->last_throttle_bytes = 0;
\r
1546 allowed = conn->throttle - conn->last_throttle_bytes;
\r
1547 if (allowed > (int64_t) len) {
\r
1550 if ((total = push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
\r
1551 (int64_t) allowed)) == allowed) {
\r
1552 buf = (char *) buf + total;
\r
1553 conn->last_throttle_bytes += total;
\r
1554 while (total < (int64_t) len && conn->ctx->stop_flag == 0) {
\r
1555 allowed = conn->throttle > (int64_t) len - total ?
\r
1556 (int64_t) len - total : conn->throttle;
\r
1557 if ((n = push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
\r
1558 (int64_t) allowed)) != allowed) {
\r
1562 conn->last_throttle_bytes = allowed;
\r
1563 conn->last_throttle_time = time(NULL);
\r
1564 buf = (char *) buf + n;
\r
1569 total = push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
\r
1572 return (int) total;
\r
1575 // Print message to buffer. If buffer is large enough to hold the message,
\r
1576 // return buffer. If buffer is to small, allocate large enough buffer on heap,
\r
1577 // and return allocated buffer.
\r
1578 static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) {
\r
1582 // Windows is not standard-compliant, and vsnprintf() returns -1 if
\r
1583 // buffer is too small. Also, older versions of msvcrt.dll do not have
\r
1584 // _vscprintf(). However, if size is 0, vsnprintf() behaves correctly.
\r
1585 // Therefore, we make two passes: on first pass, get required message length.
\r
1586 // On second pass, actually print the message.
\r
1587 va_copy(ap_copy, ap);
\r
1588 len = vsnprintf(NULL, 0, fmt, ap_copy);
\r
1590 if (len > (int) size &&
\r
1591 (size = len + 1) > 0 &&
\r
1592 (*buf = (char *) malloc(size)) == NULL) {
\r
1593 len = -1; // Allocation failed, mark failure
\r
1595 va_copy(ap_copy, ap);
\r
1596 vsnprintf(*buf, size, fmt, ap_copy);
\r
1602 int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) {
\r
1603 char mem[MG_BUF_LEN], *buf = mem;
\r
1606 if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
\r
1607 len = mg_write(conn, buf, (size_t) len);
\r
1609 if (buf != mem && buf != NULL) {
\r
1616 int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
\r
1618 va_start(ap, fmt);
\r
1619 return mg_vprintf(conn, fmt, ap);
\r
1622 // URL-decode input buffer into destination buffer.
\r
1623 // 0-terminate the destination buffer. Return the length of decoded data.
\r
1624 // form-url-encoded data differs from URI encoding in a way that it
\r
1625 // uses '+' as character for space, see RFC 1866 section 8.2.1
\r
1626 // http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
\r
1627 static int url_decode(const char *src, int src_len, char *dst,
\r
1628 int dst_len, int is_form_url_encoded) {
\r
1630 #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
\r
1632 for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
\r
1633 if (src[i] == '%' &&
\r
1634 isxdigit(* (const unsigned char *) (src + i + 1)) &&
\r
1635 isxdigit(* (const unsigned char *) (src + i + 2))) {
\r
1636 a = tolower(* (const unsigned char *) (src + i + 1));
\r
1637 b = tolower(* (const unsigned char *) (src + i + 2));
\r
1638 dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
\r
1640 } else if (is_form_url_encoded && src[i] == '+') {
\r
1647 dst[j] = '\0'; // Null-terminate the destination
\r
1649 return i >= src_len ? j : -1;
\r
1652 int mg_get_var(const char *data, size_t data_len, const char *name,
\r
1653 char *dst, size_t dst_len) {
\r
1654 const char *p, *e, *s;
\r
1658 if (dst == NULL || dst_len == 0) {
\r
1660 } else if (data == NULL || name == NULL || data_len == 0) {
\r
1664 name_len = strlen(name);
\r
1665 e = data + data_len;
\r
1669 // data is "var1=val1&var2=val2...". Find variable first
\r
1670 for (p = data; p + name_len < e; p++) {
\r
1671 if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
\r
1672 !mg_strncasecmp(name, p, name_len)) {
\r
1674 // Point p to variable value
\r
1675 p += name_len + 1;
\r
1677 // Point s to the end of the value
\r
1678 s = (const char *) memchr(p, '&', (size_t)(e - p));
\r
1684 // Decode variable into destination buffer
\r
1685 len = url_decode(p, (size_t)(s - p), dst, dst_len, 1);
\r
1687 // Redirect error code from -1 to -2 (destination buffer too small).
\r
1699 int mg_get_cookie(const struct mg_connection *conn, const char *cookie_name,
\r
1700 char *dst, size_t dst_size) {
\r
1701 const char *s, *p, *end;
\r
1702 int name_len, len = -1;
\r
1704 if (dst == NULL || dst_size == 0) {
\r
1706 } else if (cookie_name == NULL || (s = mg_get_header(conn, "Cookie")) == NULL) {
\r
1710 name_len = (int) strlen(cookie_name);
\r
1711 end = s + strlen(s);
\r
1714 for (; (s = strstr(s, cookie_name)) != NULL; s += name_len) {
\r
1715 if (s[name_len] == '=') {
\r
1716 s += name_len + 1;
\r
1717 if ((p = strchr(s, ' ')) == NULL)
\r
1721 if (*s == '"' && p[-1] == '"' && p > s + 1) {
\r
1725 if ((size_t) (p - s) < dst_size) {
\r
1727 mg_strlcpy(dst, s, (size_t) len + 1);
\r
1738 static void convert_uri_to_file_name(struct mg_connection *conn, char *buf,
\r
1739 size_t buf_len, struct file *filep) {
\r
1741 const char *rewrite, *uri = conn->request_info.uri;
\r
1745 // Using buf_len - 1 because memmove() for PATH_INFO may shift part
\r
1746 // of the path one byte on the right.
\r
1747 mg_snprintf(conn, buf, buf_len - 1, "%s%s", conn->ctx->config[DOCUMENT_ROOT],
\r
1750 rewrite = conn->ctx->config[REWRITE];
\r
1751 while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
\r
1752 if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
\r
1753 mg_snprintf(conn, buf, buf_len - 1, "%.*s%s", (int) b.len, b.ptr,
\r
1759 if (!mg_stat(conn, buf, filep)) {
\r
1760 // Support PATH_INFO for CGI scripts.
\r
1761 for (p = buf + strlen(buf); p > buf + 1; p--) {
\r
1764 if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
\r
1765 strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 &&
\r
1766 mg_stat(conn, buf, filep)) {
\r
1767 // Shift PATH_INFO block one character right, e.g.
\r
1768 // "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
\r
1769 // conn->path_info is pointing to the local variable "path" declared
\r
1770 // in handle_request(), so PATH_INFO is not valid after
\r
1771 // handle_request returns.
\r
1772 conn->path_info = p + 1;
\r
1773 memmove(p + 2, p + 1, strlen(p + 1) + 1); // +1 is for trailing \0
\r
1784 // Check whether full request is buffered. Return:
\r
1785 // -1 if request is malformed
\r
1786 // 0 if request is not yet fully buffered
\r
1787 // >0 actual request length, including last \r\n\r\n
\r
1788 static int get_request_len(const char *buf, int buflen) {
\r
1789 const char *s, *e;
\r
1792 for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)
\r
1793 // Control characters are not allowed but >=128 is.
\r
1794 if (!isprint(* (const unsigned char *) s) && *s != '\r' &&
\r
1795 *s != '\n' && * (const unsigned char *) s < 128) {
\r
1797 break; // [i_a] abort scan as soon as one malformed character is found;
\r
1798 // don't let subsequent \r\n\r\n win us over anyhow
\r
1799 } else if (s[0] == '\n' && s[1] == '\n') {
\r
1800 len = (int) (s - buf) + 2;
\r
1801 } else if (s[0] == '\n' && &s[1] < e &&
\r
1802 s[1] == '\r' && s[2] == '\n') {
\r
1803 len = (int) (s - buf) + 3;
\r
1809 // Convert month to the month number. Return -1 on error, or month number
\r
1810 static int get_month_index(const char *s) {
\r
1813 for (i = 0; i < ARRAY_SIZE(month_names); i++)
\r
1814 if (!strcmp(s, month_names[i]))
\r
1820 static int num_leap_years(int year) {
\r
1821 return year / 4 - year / 100 + year / 400;
\r
1824 // Parse UTC date-time string, and return the corresponding time_t value.
\r
1825 static time_t parse_date_string(const char *datetime) {
\r
1826 static const unsigned short days_before_month[] = {
\r
1827 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
\r
1829 char month_str[32];
\r
1830 int second, minute, hour, day, month, year, leap_days, days;
\r
1831 time_t result = (time_t) 0;
\r
1833 if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d",
\r
1834 &day, month_str, &year, &hour, &minute, &second) == 6) ||
\r
1835 (sscanf(datetime, "%d %3s %d %d:%d:%d",
\r
1836 &day, month_str, &year, &hour, &minute, &second) == 6) ||
\r
1837 (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d",
\r
1838 &day, month_str, &year, &hour, &minute, &second) == 6) ||
\r
1839 (sscanf(datetime, "%d-%3s-%d %d:%d:%d",
\r
1840 &day, month_str, &year, &hour, &minute, &second) == 6)) &&
\r
1842 (month = get_month_index(month_str)) != -1) {
\r
1843 leap_days = num_leap_years(year) - num_leap_years(1970);
\r
1845 days = year * 365 + days_before_month[month] + (day - 1) + leap_days;
\r
1846 result = days * 24 * 3600 + hour * 3600 + minute * 60 + second;
\r
1852 // Protect against directory disclosure attack by removing '..',
\r
1853 // excessive '/' and '\' characters
\r
1854 static void remove_double_dots_and_double_slashes(char *s) {
\r
1857 while (*s != '\0') {
\r
1859 if (s[-1] == '/' || s[-1] == '\\') {
\r
1860 // Skip all following slashes, backslashes and double-dots
\r
1861 while (s[0] != '\0') {
\r
1862 if (s[0] == '/' || s[0] == '\\') {
\r
1864 } else if (s[0] == '.' && s[1] == '.') {
\r
1875 static const struct {
\r
1876 const char *extension;
\r
1878 const char *mime_type;
\r
1879 } builtin_mime_types[] = {
\r
1880 {".html", 5, "text/html"},
\r
1881 {".htm", 4, "text/html"},
\r
1882 {".shtm", 5, "text/html"},
\r
1883 {".shtml", 6, "text/html"},
\r
1884 {".css", 4, "text/css"},
\r
1885 {".js", 3, "application/x-javascript"},
\r
1886 {".ico", 4, "image/x-icon"},
\r
1887 {".gif", 4, "image/gif"},
\r
1888 {".jpg", 4, "image/jpeg"},
\r
1889 {".jpeg", 5, "image/jpeg"},
\r
1890 {".png", 4, "image/png"},
\r
1891 {".svg", 4, "image/svg+xml"},
\r
1892 {".txt", 4, "text/plain"},
\r
1893 {".torrent", 8, "application/x-bittorrent"},
\r
1894 {".wav", 4, "audio/x-wav"},
\r
1895 {".mp3", 4, "audio/x-mp3"},
\r
1896 {".mid", 4, "audio/mid"},
\r
1897 {".m3u", 4, "audio/x-mpegurl"},
\r
1898 {".ogg", 4, "audio/ogg"},
\r
1899 {".ram", 4, "audio/x-pn-realaudio"},
\r
1900 {".xml", 4, "text/xml"},
\r
1901 {".json", 5, "text/json"},
\r
1902 {".xslt", 5, "application/xml"},
\r
1903 {".xsl", 4, "application/xml"},
\r
1904 {".ra", 3, "audio/x-pn-realaudio"},
\r
1905 {".doc", 4, "application/msword"},
\r
1906 {".exe", 4, "application/octet-stream"},
\r
1907 {".zip", 4, "application/x-zip-compressed"},
\r
1908 {".xls", 4, "application/excel"},
\r
1909 {".tgz", 4, "application/x-tar-gz"},
\r
1910 {".tar", 4, "application/x-tar"},
\r
1911 {".gz", 3, "application/x-gunzip"},
\r
1912 {".arj", 4, "application/x-arj-compressed"},
\r
1913 {".rar", 4, "application/x-arj-compressed"},
\r
1914 {".rtf", 4, "application/rtf"},
\r
1915 {".pdf", 4, "application/pdf"},
\r
1916 {".swf", 4, "application/x-shockwave-flash"},
\r
1917 {".mpg", 4, "video/mpeg"},
\r
1918 {".webm", 5, "video/webm"},
\r
1919 {".mpeg", 5, "video/mpeg"},
\r
1920 {".mp4", 4, "video/mp4"},
\r
1921 {".m4v", 4, "video/x-m4v"},
\r
1922 {".asf", 4, "video/x-ms-asf"},
\r
1923 {".avi", 4, "video/x-msvideo"},
\r
1924 {".bmp", 4, "image/bmp"},
\r
1928 const char *mg_get_builtin_mime_type(const char *path) {
\r
1930 size_t i, path_len;
\r
1932 path_len = strlen(path);
\r
1934 for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
\r
1935 ext = path + (path_len - builtin_mime_types[i].ext_len);
\r
1936 if (path_len > builtin_mime_types[i].ext_len &&
\r
1937 mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) {
\r
1938 return builtin_mime_types[i].mime_type;
\r
1942 return "text/plain";
\r
1945 // Look at the "path" extension and figure what mime type it has.
\r
1946 // Store mime type in the vector.
\r
1947 static void get_mime_type(struct mg_context *ctx, const char *path,
\r
1948 struct vec *vec) {
\r
1949 struct vec ext_vec, mime_vec;
\r
1950 const char *list, *ext;
\r
1953 path_len = strlen(path);
\r
1955 // Scan user-defined mime types first, in case user wants to
\r
1956 // override default mime types.
\r
1957 list = ctx->config[EXTRA_MIME_TYPES];
\r
1958 while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
\r
1959 // ext now points to the path suffix
\r
1960 ext = path + path_len - ext_vec.len;
\r
1961 if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
\r
1967 vec->ptr = mg_get_builtin_mime_type(path);
\r
1968 vec->len = strlen(vec->ptr);
\r
1971 static int is_big_endian(void) {
\r
1972 static const int n = 1;
\r
1973 return ((char *) &n)[0] == 0;
\r
1977 typedef struct MD5Context {
\r
1980 unsigned char in[64];
\r
1983 static void byteReverse(unsigned char *buf, unsigned longs) {
\r
1986 // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
\r
1987 if (is_big_endian()) {
\r
1989 t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
\r
1990 ((unsigned) buf[1] << 8 | buf[0]);
\r
1991 * (uint32_t *) buf = t;
\r
1993 } while (--longs);
\r
1997 #define F1(x, y, z) (z ^ (x & (y ^ z)))
\r
1998 #define F2(x, y, z) F1(z, x, y)
\r
1999 #define F3(x, y, z) (x ^ y ^ z)
\r
2000 #define F4(x, y, z) (y ^ (x | ~z))
\r
2002 #define MD5STEP(f, w, x, y, z, data, s) \
\r
2003 ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
\r
2005 // Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
\r
2006 // initialization constants.
\r
2007 static void MD5Init(MD5_CTX *ctx) {
\r
2008 ctx->buf[0] = 0x67452301;
\r
2009 ctx->buf[1] = 0xefcdab89;
\r
2010 ctx->buf[2] = 0x98badcfe;
\r
2011 ctx->buf[3] = 0x10325476;
\r
2017 static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
\r
2018 register uint32_t a, b, c, d;
\r
2025 MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
\r
2026 MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
\r
2027 MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
\r
2028 MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
\r
2029 MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
\r
2030 MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
\r
2031 MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
\r
2032 MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
\r
2033 MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
\r
2034 MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
\r
2035 MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
\r
2036 MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
\r
2037 MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
\r
2038 MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
\r
2039 MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
\r
2040 MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
\r
2042 MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
\r
2043 MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
\r
2044 MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
\r
2045 MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
\r
2046 MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
\r
2047 MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
\r
2048 MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
\r
2049 MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
\r
2050 MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
\r
2051 MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
\r
2052 MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
\r
2053 MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
\r
2054 MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
\r
2055 MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
\r
2056 MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
\r
2057 MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
\r
2059 MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
\r
2060 MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
\r
2061 MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
\r
2062 MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
\r
2063 MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
\r
2064 MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
\r
2065 MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
\r
2066 MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
\r
2067 MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
\r
2068 MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
\r
2069 MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
\r
2070 MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
\r
2071 MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
\r
2072 MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
\r
2073 MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
\r
2074 MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
\r
2076 MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
\r
2077 MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
\r
2078 MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
\r
2079 MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
\r
2080 MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
\r
2081 MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
\r
2082 MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
\r
2083 MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
\r
2084 MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
\r
2085 MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
\r
2086 MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
\r
2087 MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
\r
2088 MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
\r
2089 MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
\r
2090 MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
\r
2091 MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
\r
2099 static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
\r
2103 if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
\r
2105 ctx->bits[1] += len >> 29;
\r
2107 t = (t >> 3) & 0x3f;
\r
2110 unsigned char *p = (unsigned char *) ctx->in + t;
\r
2114 memcpy(p, buf, len);
\r
2117 memcpy(p, buf, t);
\r
2118 byteReverse(ctx->in, 16);
\r
2119 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
\r
2124 while (len >= 64) {
\r
2125 memcpy(ctx->in, buf, 64);
\r
2126 byteReverse(ctx->in, 16);
\r
2127 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
\r
2132 memcpy(ctx->in, buf, len);
\r
2135 static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
\r
2139 count = (ctx->bits[0] >> 3) & 0x3F;
\r
2141 p = ctx->in + count;
\r
2143 count = 64 - 1 - count;
\r
2145 memset(p, 0, count);
\r
2146 byteReverse(ctx->in, 16);
\r
2147 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
\r
2148 memset(ctx->in, 0, 56);
\r
2150 memset(p, 0, count - 8);
\r
2152 byteReverse(ctx->in, 14);
\r
2154 ((uint32_t *) ctx->in)[14] = ctx->bits[0];
\r
2155 ((uint32_t *) ctx->in)[15] = ctx->bits[1];
\r
2157 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
\r
2158 byteReverse((unsigned char *) ctx->buf, 4);
\r
2159 memcpy(digest, ctx->buf, 16);
\r
2160 memset((char *) ctx, 0, sizeof(*ctx));
\r
2162 #endif // !HAVE_MD5
\r
2164 // Stringify binary data. Output buffer must be twice as big as input,
\r
2165 // because each byte takes 2 bytes in string representation
\r
2166 static void bin2str(char *to, const unsigned char *p, size_t len) {
\r
2167 static const char *hex = "0123456789abcdef";
\r
2169 for (; len--; p++) {
\r
2170 *to++ = hex[p[0] >> 4];
\r
2171 *to++ = hex[p[0] & 0x0f];
\r
2176 // Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
\r
2177 void mg_md5(char buf[33], ...) {
\r
2178 unsigned char hash[16];
\r
2185 va_start(ap, buf);
\r
2186 while ((p = va_arg(ap, const char *)) != NULL) {
\r
2187 MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
\r
2191 MD5Final(hash, &ctx);
\r
2192 bin2str(buf, hash, sizeof(hash));
\r
2195 // Check the user's password, return 1 if OK
\r
2196 static int check_password(const char *method, const char *ha1, const char *uri,
\r
2197 const char *nonce, const char *nc, const char *cnonce,
\r
2198 const char *qop, const char *response) {
\r
2199 char ha2[32 + 1], expected_response[32 + 1];
\r
2201 // Some of the parameters may be NULL
\r
2202 if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL ||
\r
2203 qop == NULL || response == NULL) {
\r
2207 // NOTE(lsm): due to a bug in MSIE, we do not compare the URI
\r
2208 // TODO(lsm): check for authentication timeout
\r
2209 if (// strcmp(dig->uri, c->ouri) != 0 ||
\r
2210 strlen(response) != 32
\r
2211 // || now - strtoul(dig->nonce, NULL, 10) > 3600
\r
2216 mg_md5(ha2, method, ":", uri, NULL);
\r
2217 mg_md5(expected_response, ha1, ":", nonce, ":", nc,
\r
2218 ":", cnonce, ":", qop, ":", ha2, NULL);
\r
2220 return mg_strcasecmp(response, expected_response) == 0;
\r
2223 // Use the global passwords file, if specified by auth_gpass option,
\r
2224 // or search for .htpasswd in the requested directory.
\r
2225 static void open_auth_file(struct mg_connection *conn, const char *path,
\r
2226 struct file *filep) {
\r
2227 char name[PATH_MAX];
\r
2228 const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE];
\r
2230 if (gpass != NULL) {
\r
2231 // Use global passwords file
\r
2232 if (!mg_fopen(conn, gpass, "r", filep)) {
\r
2233 cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO));
\r
2235 } else if (mg_stat(conn, path, filep) && filep->is_directory) {
\r
2236 mg_snprintf(conn, name, sizeof(name), "%s%c%s",
\r
2237 path, '/', PASSWORDS_FILE_NAME);
\r
2238 mg_fopen(conn, name, "r", filep);
\r
2240 // Try to find .htpasswd in requested directory.
\r
2241 for (p = path, e = p + strlen(p) - 1; e > p; e--)
\r
2244 mg_snprintf(conn, name, sizeof(name), "%.*s%c%s",
\r
2245 (int) (e - p), p, '/', PASSWORDS_FILE_NAME);
\r
2246 mg_fopen(conn, name, "r", filep);
\r
2250 // Parsed Authorization header
\r
2252 char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
\r
2255 // Return 1 on success. Always initializes the ah structure.
\r
2256 static int parse_auth_header(struct mg_connection *conn, char *buf,
\r
2257 size_t buf_size, struct ah *ah) {
\r
2258 char *name, *value, *s;
\r
2259 const char *auth_header;
\r
2261 (void) memset(ah, 0, sizeof(*ah));
\r
2262 if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||
\r
2263 mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
\r
2267 // Make modifiable copy of the auth header
\r
2268 (void) mg_strlcpy(buf, auth_header + 7, buf_size);
\r
2271 // Parse authorization header
\r
2273 // Gobble initial spaces
\r
2274 while (isspace(* (unsigned char *) s)) {
\r
2277 name = skip_quoted(&s, "=", " ", 0);
\r
2278 // Value is either quote-delimited, or ends at first comma or space.
\r
2279 if (s[0] == '\"') {
\r
2281 value = skip_quoted(&s, "\"", " ", '\\');
\r
2282 if (s[0] == ',') {
\r
2286 value = skip_quoted(&s, ", ", " ", 0); // IE uses commas, FF uses spaces
\r
2288 if (*name == '\0') {
\r
2292 if (!strcmp(name, "username")) {
\r
2294 } else if (!strcmp(name, "cnonce")) {
\r
2295 ah->cnonce = value;
\r
2296 } else if (!strcmp(name, "response")) {
\r
2297 ah->response = value;
\r
2298 } else if (!strcmp(name, "uri")) {
\r
2300 } else if (!strcmp(name, "qop")) {
\r
2302 } else if (!strcmp(name, "nc")) {
\r
2304 } else if (!strcmp(name, "nonce")) {
\r
2305 ah->nonce = value;
\r
2309 // CGI needs it as REMOTE_USER
\r
2310 if (ah->user != NULL) {
\r
2311 conn->request_info.remote_user = mg_strdup(ah->user);
\r
2319 static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p) {
\r
2323 if (filep->membuf != NULL && *p != NULL) {
\r
2324 eof = memchr(*p, '\n', &filep->membuf[filep->size] - *p);
\r
2325 len = (size_t) (eof - *p) > size - 1 ? size - 1 : (size_t) (eof - *p);
\r
2326 memcpy(buf, *p, len);
\r
2330 } else if (filep->fp != NULL) {
\r
2331 return fgets(buf, size, filep->fp);
\r
2337 // Authorize against the opened passwords file. Return 1 if authorized.
\r
2338 static int authorize(struct mg_connection *conn, struct file *filep) {
\r
2340 char line[256], f_user[256], ha1[256], f_domain[256], buf[MG_BUF_LEN], *p;
\r
2342 if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) {
\r
2346 // Loop over passwords file
\r
2347 p = (char *) filep->membuf;
\r
2348 while (mg_fgets(line, sizeof(line), filep, &p) != NULL) {
\r
2349 if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) {
\r
2353 if (!strcmp(ah.user, f_user) &&
\r
2354 !strcmp(conn->ctx->config[AUTHENTICATION_DOMAIN], f_domain))
\r
2355 return check_password(conn->request_info.request_method, ha1, ah.uri,
\r
2356 ah.nonce, ah.nc, ah.cnonce, ah.qop, ah.response);
\r
2362 // Return 1 if request is authorised, 0 otherwise.
\r
2363 static int check_authorization(struct mg_connection *conn, const char *path) {
\r
2364 char fname[PATH_MAX];
\r
2365 struct vec uri_vec, filename_vec;
\r
2367 struct file file = STRUCT_FILE_INITIALIZER;
\r
2368 int authorized = 1;
\r
2370 list = conn->ctx->config[PROTECT_URI];
\r
2371 while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
\r
2372 if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
\r
2373 mg_snprintf(conn, fname, sizeof(fname), "%.*s",
\r
2374 (int) filename_vec.len, filename_vec.ptr);
\r
2375 if (!mg_fopen(conn, fname, "r", &file)) {
\r
2376 cry(conn, "%s: cannot open %s: %s", __func__, fname, strerror(errno));
\r
2382 if (!is_file_opened(&file)) {
\r
2383 open_auth_file(conn, path, &file);
\r
2386 if (is_file_opened(&file)) {
\r
2387 authorized = authorize(conn, &file);
\r
2391 return authorized;
\r
2394 static void send_authorization_request(struct mg_connection *conn) {
\r
2395 conn->status_code = 401;
\r
2397 "HTTP/1.1 401 Unauthorized\r\n"
\r
2398 "Content-Length: 0\r\n"
\r
2399 "WWW-Authenticate: Digest qop=\"auth\", "
\r
2400 "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
\r
2401 conn->ctx->config[AUTHENTICATION_DOMAIN],
\r
2402 (unsigned long) time(NULL));
\r
2405 static int is_authorized_for_put(struct mg_connection *conn) {
\r
2406 struct file file = STRUCT_FILE_INITIALIZER;
\r
2407 const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE];
\r
2410 if (passfile != NULL && mg_fopen(conn, passfile, "r", &file)) {
\r
2411 ret = authorize(conn, &file);
\r
2418 int mg_modify_passwords_file(const char *fname, const char *domain,
\r
2419 const char *user, const char *pass) {
\r
2421 char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
\r
2427 // Regard empty password as no password - remove user record.
\r
2428 if (pass != NULL && pass[0] == '\0') {
\r
2432 (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
\r
2434 // Create the file if does not exist
\r
2435 if ((fp = fopen(fname, "a+")) != NULL) {
\r
2436 (void) fclose(fp);
\r
2439 // Open the given file and temporary file
\r
2440 if ((fp = fopen(fname, "r")) == NULL) {
\r
2442 } else if ((fp2 = fopen(tmp, "w+")) == NULL) {
\r
2447 // Copy the stuff to temporary file
\r
2448 while (fgets(line, sizeof(line), fp) != NULL) {
\r
2449 if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
\r
2453 if (!strcmp(u, user) && !strcmp(d, domain)) {
\r
2455 if (pass != NULL) {
\r
2456 mg_md5(ha1, user, ":", domain, ":", pass, NULL);
\r
2457 fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
\r
2460 fprintf(fp2, "%s", line);
\r
2464 // If new user, just add it
\r
2465 if (!found && pass != NULL) {
\r
2466 mg_md5(ha1, user, ":", domain, ":", pass, NULL);
\r
2467 fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
\r
2474 // Put the temp file in place of real file
\r
2476 rename(tmp, fname);
\r
2482 struct mg_connection *conn;
\r
2487 static void url_encode(const char *src, char *dst, size_t dst_len) {
\r
2488 static const char *dont_escape = "._-$,;~()";
\r
2489 static const char *hex = "0123456789abcdef";
\r
2490 const char *end = dst + dst_len - 1;
\r
2492 for (; *src != '\0' && dst < end; src++, dst++) {
\r
2493 if (isalnum(*(const unsigned char *) src) ||
\r
2494 strchr(dont_escape, * (const unsigned char *) src) != NULL) {
\r
2496 } else if (dst + 2 < end) {
\r
2498 dst[1] = hex[(* (const unsigned char *) src) >> 4];
\r
2499 dst[2] = hex[(* (const unsigned char *) src) & 0xf];
\r
2507 static void print_dir_entry(struct de *de) {
\r
2508 char size[64], mod[64], href[PATH_MAX];
\r
2510 if (de->file.is_directory) {
\r
2511 mg_snprintf(de->conn, size, sizeof(size), "%s", "[DIRECTORY]");
\r
2513 // We use (signed) cast below because MSVC 6 compiler cannot
\r
2514 // convert unsigned __int64 to double. Sigh.
\r
2515 if (de->file.size < 1024) {
\r
2516 mg_snprintf(de->conn, size, sizeof(size), "%d", (int) de->file.size);
\r
2517 } else if (de->file.size < 0x100000) {
\r
2518 mg_snprintf(de->conn, size, sizeof(size),
\r
2519 "%.1fk", (double) de->file.size / 1024.0);
\r
2520 } else if (de->file.size < 0x40000000) {
\r
2521 mg_snprintf(de->conn, size, sizeof(size),
\r
2522 "%.1fM", (double) de->file.size / 1048576);
\r
2524 mg_snprintf(de->conn, size, sizeof(size),
\r
2525 "%.1fG", (double) de->file.size / 1073741824);
\r
2528 strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
\r
2529 localtime(&de->file.modification_time));
\r
2530 url_encode(de->file_name, href, sizeof(href));
\r
2531 de->conn->num_bytes_sent += mg_printf(de->conn,
\r
2532 "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
\r
2533 "<td> %s</td><td> %s</td></tr>\n",
\r
2534 de->conn->request_info.uri, href, de->file.is_directory ? "/" : "",
\r
2535 de->file_name, de->file.is_directory ? "/" : "", mod, size);
\r
2538 // This function is called from send_directory() and used for
\r
2539 // sorting directory entries by size, or name, or modification time.
\r
2540 // On windows, __cdecl specification is needed in case if project is built
\r
2541 // with __stdcall convention. qsort always requires __cdels callback.
\r
2542 static int WINCDECL compare_dir_entries(const void *p1, const void *p2) {
\r
2543 const struct de *a = (const struct de *) p1, *b = (const struct de *) p2;
\r
2544 const char *query_string = a->conn->request_info.query_string;
\r
2545 int cmp_result = 0;
\r
2547 if (query_string == NULL) {
\r
2548 query_string = "na";
\r
2551 if (a->file.is_directory && !b->file.is_directory) {
\r
2552 return -1; // Always put directories on top
\r
2553 } else if (!a->file.is_directory && b->file.is_directory) {
\r
2554 return 1; // Always put directories on top
\r
2555 } else if (*query_string == 'n') {
\r
2556 cmp_result = strcmp(a->file_name, b->file_name);
\r
2557 } else if (*query_string == 's') {
\r
2558 cmp_result = a->file.size == b->file.size ? 0 :
\r
2559 a->file.size > b->file.size ? 1 : -1;
\r
2560 } else if (*query_string == 'd') {
\r
2561 cmp_result = a->file.modification_time == b->file.modification_time ? 0 :
\r
2562 a->file.modification_time > b->file.modification_time ? 1 : -1;
\r
2565 return query_string[1] == 'd' ? -cmp_result : cmp_result;
\r
2568 static int must_hide_file(struct mg_connection *conn, const char *path) {
\r
2569 const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
\r
2570 const char *pattern = conn->ctx->config[HIDE_FILES];
\r
2571 return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 ||
\r
2572 (pattern != NULL && match_prefix(pattern, strlen(pattern), path) > 0);
\r
2575 static int scan_directory(struct mg_connection *conn, const char *dir,
\r
2576 void *data, void (*cb)(struct de *, void *)) {
\r
2577 char path[PATH_MAX];
\r
2578 struct dirent *dp;
\r
2582 if ((dirp = opendir(dir)) == NULL) {
\r
2587 while ((dp = readdir(dirp)) != NULL) {
\r
2588 // Do not show current dir and hidden files
\r
2589 if (!strcmp(dp->d_name, ".") ||
\r
2590 !strcmp(dp->d_name, "..") ||
\r
2591 must_hide_file(conn, dp->d_name)) {
\r
2595 mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
\r
2597 // If we don't memset stat structure to zero, mtime will have
\r
2598 // garbage and strftime() will segfault later on in
\r
2599 // print_dir_entry(). memset is required only if mg_stat()
\r
2600 // fails. For more details, see
\r
2601 // http://code.google.com/p/mongoose/issues/detail?id=79
\r
2602 memset(&de.file, 0, sizeof(de.file));
\r
2603 mg_stat(conn, path, &de.file);
\r
2605 de.file_name = dp->d_name;
\r
2608 (void) closedir(dirp);
\r
2613 struct dir_scan_data {
\r
2614 struct de *entries;
\r
2619 static void dir_scan_callback(struct de *de, void *data) {
\r
2620 struct dir_scan_data *dsd = (struct dir_scan_data *) data;
\r
2622 if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) {
\r
2623 dsd->arr_size *= 2;
\r
2624 dsd->entries = (struct de *) realloc(dsd->entries, dsd->arr_size *
\r
2625 sizeof(dsd->entries[0]));
\r
2627 if (dsd->entries == NULL) {
\r
2628 // TODO(lsm): propagate an error to the caller
\r
2629 dsd->num_entries = 0;
\r
2631 dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
\r
2632 dsd->entries[dsd->num_entries].file = de->file;
\r
2633 dsd->entries[dsd->num_entries].conn = de->conn;
\r
2634 dsd->num_entries++;
\r
2638 static void handle_directory_request(struct mg_connection *conn,
\r
2639 const char *dir) {
\r
2640 int i, sort_direction;
\r
2641 struct dir_scan_data data = { NULL, 0, 128 };
\r
2643 if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
\r
2644 send_http_error(conn, 500, "Cannot open directory",
\r
2645 "Error: opendir(%s): %s", dir, strerror(ERRNO));
\r
2649 sort_direction = conn->request_info.query_string != NULL &&
\r
2650 conn->request_info.query_string[1] == 'd' ? 'a' : 'd';
\r
2652 conn->must_close = 1;
\r
2653 mg_printf(conn, "%s",
\r
2654 "HTTP/1.1 200 OK\r\n"
\r
2655 "Connection: close\r\n"
\r
2656 "Content-Type: text/html; charset=utf-8\r\n\r\n");
\r
2658 conn->num_bytes_sent += mg_printf(conn,
\r
2659 "<html><head><title>Index of %s</title>"
\r
2660 "<style>th {text-align: left;}</style></head>"
\r
2661 "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
\r
2662 "<tr><th><a href=\"?n%c\">Name</a></th>"
\r
2663 "<th><a href=\"?d%c\">Modified</a></th>"
\r
2664 "<th><a href=\"?s%c\">Size</a></th></tr>"
\r
2665 "<tr><td colspan=\"3\"><hr></td></tr>",
\r
2666 conn->request_info.uri, conn->request_info.uri,
\r
2667 sort_direction, sort_direction, sort_direction);
\r
2669 // Print first entry - link to a parent directory
\r
2670 conn->num_bytes_sent += mg_printf(conn,
\r
2671 "<tr><td><a href=\"%s%s\">%s</a></td>"
\r
2672 "<td> %s</td><td> %s</td></tr>\n",
\r
2673 conn->request_info.uri, "..", "Parent directory", "-", "-");
\r
2675 // Sort and print directory entries
\r
2676 qsort(data.entries, (size_t) data.num_entries, sizeof(data.entries[0]),
\r
2677 compare_dir_entries);
\r
2678 for (i = 0; i < data.num_entries; i++) {
\r
2679 print_dir_entry(&data.entries[i]);
\r
2680 free(data.entries[i].file_name);
\r
2682 free(data.entries);
\r
2684 conn->num_bytes_sent += mg_printf(conn, "%s", "</table></body></html>");
\r
2685 conn->status_code = 200;
\r
2688 // Send len bytes from the opened file to the client.
\r
2689 static void send_file_data(struct mg_connection *conn, struct file *filep,
\r
2690 int64_t offset, int64_t len) {
\r
2691 char buf[MG_BUF_LEN];
\r
2692 int to_read, num_read, num_written;
\r
2694 if (len > 0 && filep->membuf != NULL && filep->size > 0) {
\r
2695 if (len > filep->size - offset) {
\r
2696 len = filep->size - offset;
\r
2698 mg_write(conn, filep->membuf + offset, (size_t) len);
\r
2699 } else if (len > 0 && filep->fp != NULL) {
\r
2700 fseeko(filep->fp, offset, SEEK_SET);
\r
2702 // Calculate how much to read from the file in the buffer
\r
2703 to_read = sizeof(buf);
\r
2704 if ((int64_t) to_read > len) {
\r
2705 to_read = (int) len;
\r
2708 // Read from file, exit the loop on error
\r
2709 if ((num_read = fread(buf, 1, (size_t) to_read, filep->fp)) <= 0) {
\r
2713 // Send read bytes to the client, exit the loop on error
\r
2714 if ((num_written = mg_write(conn, buf, (size_t) num_read)) != num_read) {
\r
2718 // Both read and were successful, adjust counters
\r
2719 conn->num_bytes_sent += num_written;
\r
2720 len -= num_written;
\r
2725 static int parse_range_header(const char *header, int64_t *a, int64_t *b) {
\r
2726 return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
\r
2729 static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
\r
2730 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
\r
2733 static void construct_etag(char *buf, size_t buf_len,
\r
2734 const struct file *filep) {
\r
2735 snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"",
\r
2736 (unsigned long) filep->modification_time, filep->size);
\r
2739 static void fclose_on_exec(struct file *filep) {
\r
2740 if (filep != NULL && filep->fp != NULL) {
\r
2742 fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC);
\r
2747 static void handle_file_request(struct mg_connection *conn, const char *path,
\r
2748 struct file *filep) {
\r
2749 char date[64], lm[64], etag[64], range[64];
\r
2750 const char *msg = "OK", *hdr;
\r
2751 time_t curtime = time(NULL);
\r
2752 int64_t cl, r1, r2;
\r
2753 struct vec mime_vec;
\r
2756 get_mime_type(conn->ctx, path, &mime_vec);
\r
2758 conn->status_code = 200;
\r
2761 if (!mg_fopen(conn, path, "rb", filep)) {
\r
2762 send_http_error(conn, 500, http_500_error,
\r
2763 "fopen(%s): %s", path, strerror(ERRNO));
\r
2766 fclose_on_exec(filep);
\r
2768 // If Range: header specified, act accordingly
\r
2770 hdr = mg_get_header(conn, "Range");
\r
2771 if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 &&
\r
2772 r1 >= 0 && r2 > 0) {
\r
2773 conn->status_code = 206;
\r
2774 cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1;
\r
2775 mg_snprintf(conn, range, sizeof(range),
\r
2776 "Content-Range: bytes "
\r
2777 "%" INT64_FMT "-%"
\r
2778 INT64_FMT "/%" INT64_FMT "\r\n",
\r
2779 r1, r1 + cl - 1, filep->size);
\r
2780 msg = "Partial Content";
\r
2783 // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
\r
2784 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
\r
2785 gmt_time_string(date, sizeof(date), &curtime);
\r
2786 gmt_time_string(lm, sizeof(lm), &filep->modification_time);
\r
2787 construct_etag(etag, sizeof(etag), filep);
\r
2789 (void) mg_printf(conn,
\r
2790 "HTTP/1.1 %d %s\r\n"
\r
2792 "Last-Modified: %s\r\n"
\r
2794 "Content-Type: %.*s\r\n"
\r
2795 "Content-Length: %" INT64_FMT "\r\n"
\r
2796 "Connection: %s\r\n"
\r
2797 "Accept-Ranges: bytes\r\n"
\r
2799 conn->status_code, msg, date, lm, etag, (int) mime_vec.len,
\r
2800 mime_vec.ptr, cl, suggest_connection_header(conn), range);
\r
2802 if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
\r
2803 send_file_data(conn, filep, r1, cl);
\r
2808 void mg_send_file(struct mg_connection *conn, const char *path) {
\r
2809 struct file file = STRUCT_FILE_INITIALIZER;
\r
2810 if (mg_stat(conn, path, &file)) {
\r
2811 handle_file_request(conn, path, &file);
\r
2813 send_http_error(conn, 404, "Not Found", "%s", "File not found");
\r
2818 // Parse HTTP headers from the given buffer, advance buffer to the point
\r
2819 // where parsing stopped.
\r
2820 static void parse_http_headers(char **buf, struct mg_request_info *ri) {
\r
2823 for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
\r
2824 ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
\r
2825 ri->http_headers[i].value = skip(buf, "\r\n");
\r
2826 if (ri->http_headers[i].name[0] == '\0')
\r
2828 ri->num_headers = i + 1;
\r
2832 static int is_valid_http_method(const char *method) {
\r
2833 return !strcmp(method, "GET") || !strcmp(method, "POST") ||
\r
2834 !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
\r
2835 !strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
\r
2836 !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND");
\r
2839 // Parse HTTP request, fill in mg_request_info structure.
\r
2840 // This function modifies the buffer by NUL-terminating
\r
2841 // HTTP request components, header names and header values.
\r
2842 static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
\r
2843 int is_request, request_length = get_request_len(buf, len);
\r
2844 if (request_length > 0) {
\r
2845 // Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
\r
2846 ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL;
\r
2847 ri->num_headers = 0;
\r
2849 buf[request_length - 1] = '\0';
\r
2851 // RFC says that all initial whitespaces should be ingored
\r
2852 while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
\r
2855 ri->request_method = skip(&buf, " ");
\r
2856 ri->uri = skip(&buf, " ");
\r
2857 ri->http_version = skip(&buf, "\r\n");
\r
2858 if (((is_request = is_valid_http_method(ri->request_method)) &&
\r
2859 memcmp(ri->http_version, "HTTP/", 5) != 0) ||
\r
2860 (!is_request && memcmp(ri->request_method, "HTTP/", 5)) != 0) {
\r
2861 request_length = -1;
\r
2864 ri->http_version += 5;
\r
2866 parse_http_headers(&buf, ri);
\r
2869 return request_length;
\r
2872 // Keep reading the input (either opened file descriptor fd, or socket sock,
\r
2873 // or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
\r
2874 // buffer (which marks the end of HTTP request). Buffer buf may already
\r
2875 // have some data. The length of the data is stored in nread.
\r
2876 // Upon every read operation, increase nread by the number of bytes read.
\r
2877 static int read_request(FILE *fp, struct mg_connection *conn,
\r
2878 char *buf, int bufsiz, int *nread) {
\r
2879 int request_len, n = 0;
\r
2881 request_len = get_request_len(buf, *nread);
\r
2882 while (*nread < bufsiz && request_len == 0 &&
\r
2883 (n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) {
\r
2885 request_len = get_request_len(buf, *nread);
\r
2888 return request_len <= 0 && n <= 0 ? -1 : request_len;
\r
2891 // For given directory path, substitute it to valid index file.
\r
2892 // Return 0 if index file has been found, -1 if not found.
\r
2893 // If the file is found, it's stats is returned in stp.
\r
2894 static int substitute_index_file(struct mg_connection *conn, char *path,
\r
2895 size_t path_len, struct file *filep) {
\r
2896 const char *list = conn->ctx->config[INDEX_FILES];
\r
2897 struct file file = STRUCT_FILE_INITIALIZER;
\r
2898 struct vec filename_vec;
\r
2899 size_t n = strlen(path);
\r
2902 // The 'path' given to us points to the directory. Remove all trailing
\r
2903 // directory separator characters from the end of the path, and
\r
2904 // then append single directory separator character.
\r
2905 while (n > 0 && path[n - 1] == '/') {
\r
2910 // Traverse index files list. For each entry, append it to the given
\r
2911 // path and see if the file exists. If it exists, break the loop
\r
2912 while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
\r
2914 // Ignore too long entries that may overflow path buffer
\r
2915 if (filename_vec.len > path_len - (n + 2))
\r
2918 // Prepare full path to the index file
\r
2919 mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
\r
2922 if (mg_stat(conn, path, &file)) {
\r
2923 // Yes it does, break the loop
\r
2930 // If no index file exists, restore directory path
\r
2938 // Return True if we should reply 304 Not Modified.
\r
2939 static int is_not_modified(const struct mg_connection *conn,
\r
2940 const struct file *filep) {
\r
2942 const char *ims = mg_get_header(conn, "If-Modified-Since");
\r
2943 const char *inm = mg_get_header(conn, "If-None-Match");
\r
2944 construct_etag(etag, sizeof(etag), filep);
\r
2945 return (inm != NULL && !mg_strcasecmp(etag, inm)) ||
\r
2946 (ims != NULL && filep->modification_time <= parse_date_string(ims));
\r
2949 static int forward_body_data(struct mg_connection *conn, FILE *fp,
\r
2950 SOCKET sock, SSL *ssl) {
\r
2951 const char *expect, *body;
\r
2952 char buf[MG_BUF_LEN];
\r
2953 int to_read, nread, buffered_len, success = 0;
\r
2955 expect = mg_get_header(conn, "Expect");
\r
2956 assert(fp != NULL);
\r
2958 if (conn->content_len == -1) {
\r
2959 send_http_error(conn, 411, "Length Required", "%s", "");
\r
2960 } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {
\r
2961 send_http_error(conn, 417, "Expectation Failed", "%s", "");
\r
2963 if (expect != NULL) {
\r
2964 (void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
\r
2967 body = conn->buf + conn->request_len + conn->consumed_content;
\r
2968 buffered_len = &conn->buf[conn->data_len] - body;
\r
2969 assert(buffered_len >= 0);
\r
2970 assert(conn->consumed_content == 0);
\r
2972 if (buffered_len > 0) {
\r
2973 if ((int64_t) buffered_len > conn->content_len) {
\r
2974 buffered_len = (int) conn->content_len;
\r
2976 push(fp, sock, ssl, body, (int64_t) buffered_len);
\r
2977 conn->consumed_content += buffered_len;
\r
2981 while (conn->consumed_content < conn->content_len) {
\r
2982 to_read = sizeof(buf);
\r
2983 if ((int64_t) to_read > conn->content_len - conn->consumed_content) {
\r
2984 to_read = (int) (conn->content_len - conn->consumed_content);
\r
2986 nread = pull(NULL, conn, buf, to_read);
\r
2987 if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) {
\r
2990 conn->consumed_content += nread;
\r
2993 if (conn->consumed_content == conn->content_len) {
\r
2994 success = nread >= 0;
\r
2997 // Each error code path in this function must send an error
\r
2999 send_http_error(conn, 577, http_500_error, "%s", "");
\r
3006 #if !defined(NO_CGI)
\r
3007 // This structure helps to create an environment for the spawned CGI program.
\r
3008 // Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
\r
3009 // last element must be NULL.
\r
3010 // However, on Windows there is a requirement that all these VARIABLE=VALUE\0
\r
3011 // strings must reside in a contiguous buffer. The end of the buffer is
\r
3012 // marked by two '\0' characters.
\r
3013 // We satisfy both worlds: we create an envp array (which is vars), all
\r
3014 // entries are actually pointers inside buf.
\r
3015 struct cgi_env_block {
\r
3016 struct mg_connection *conn;
\r
3017 char buf[CGI_ENVIRONMENT_SIZE]; // Environment buffer
\r
3018 int len; // Space taken
\r
3019 char *vars[MAX_CGI_ENVIR_VARS]; // char **envp
\r
3020 int nvars; // Number of variables
\r
3023 static char *addenv(struct cgi_env_block *block,
\r
3024 PRINTF_FORMAT_STRING(const char *fmt), ...)
\r
3025 PRINTF_ARGS(2, 3);
\r
3027 // Append VARIABLE=VALUE\0 string to the buffer, and add a respective
\r
3028 // pointer into the vars array.
\r
3029 static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
\r
3034 // Calculate how much space is left in the buffer
\r
3035 space = sizeof(block->buf) - block->len - 2;
\r
3036 assert(space >= 0);
\r
3038 // Make a pointer to the free space int the buffer
\r
3039 added = block->buf + block->len;
\r
3041 // Copy VARIABLE=VALUE\0 string into the free space
\r
3042 va_start(ap, fmt);
\r
3043 n = mg_vsnprintf(block->conn, added, (size_t) space, fmt, ap);
\r
3046 // Make sure we do not overflow buffer and the envp array
\r
3047 if (n > 0 && n + 1 < space &&
\r
3048 block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
\r
3049 // Append a pointer to the added string into the envp array
\r
3050 block->vars[block->nvars++] = added;
\r
3051 // Bump up used length counter. Include \0 terminator
\r
3052 block->len += n + 1;
\r
3054 cry(block->conn, "%s: CGI env buffer truncated for [%s]", __func__, fmt);
\r
3060 static void prepare_cgi_environment(struct mg_connection *conn,
\r
3062 struct cgi_env_block *blk) {
\r
3063 const char *s, *slash;
\r
3064 struct vec var_vec;
\r
3065 char *p, src_addr[20];
\r
3068 blk->len = blk->nvars = 0;
\r
3070 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
\r
3072 addenv(blk, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]);
\r
3073 addenv(blk, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
\r
3074 addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
\r
3076 // Prepare the environment block
\r
3077 addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
\r
3078 addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
\r
3079 addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP
\r
3081 // TODO(lsm): fix this for IPv6 case
\r
3082 addenv(blk, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port));
\r
3084 addenv(blk, "REQUEST_METHOD=%s", conn->request_info.request_method);
\r
3085 addenv(blk, "REMOTE_ADDR=%s", src_addr);
\r
3086 addenv(blk, "REMOTE_PORT=%d", conn->request_info.remote_port);
\r
3087 addenv(blk, "REQUEST_URI=%s", conn->request_info.uri);
\r
3090 assert(conn->request_info.uri[0] == '/');
\r
3091 slash = strrchr(conn->request_info.uri, '/');
\r
3092 if ((s = strrchr(prog, '/')) == NULL)
\r
3094 addenv(blk, "SCRIPT_NAME=%.*s%s", (int) (slash - conn->request_info.uri),
\r
3095 conn->request_info.uri, s);
\r
3097 addenv(blk, "SCRIPT_FILENAME=%s", prog);
\r
3098 addenv(blk, "PATH_TRANSLATED=%s", prog);
\r
3099 addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
\r
3101 if ((s = mg_get_header(conn, "Content-Type")) != NULL)
\r
3102 addenv(blk, "CONTENT_TYPE=%s", s);
\r
3104 if (conn->request_info.query_string != NULL)
\r
3105 addenv(blk, "QUERY_STRING=%s", conn->request_info.query_string);
\r
3107 if ((s = mg_get_header(conn, "Content-Length")) != NULL)
\r
3108 addenv(blk, "CONTENT_LENGTH=%s", s);
\r
3110 if ((s = getenv("PATH")) != NULL)
\r
3111 addenv(blk, "PATH=%s", s);
\r
3113 if (conn->path_info != NULL) {
\r
3114 addenv(blk, "PATH_INFO=%s", conn->path_info);
\r
3117 #if defined(_WIN32)
\r
3118 if ((s = getenv("COMSPEC")) != NULL) {
\r
3119 addenv(blk, "COMSPEC=%s", s);
\r
3121 if ((s = getenv("SYSTEMROOT")) != NULL) {
\r
3122 addenv(blk, "SYSTEMROOT=%s", s);
\r
3124 if ((s = getenv("SystemDrive")) != NULL) {
\r
3125 addenv(blk, "SystemDrive=%s", s);
\r
3128 if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
\r
3129 addenv(blk, "LD_LIBRARY_PATH=%s", s);
\r
3132 if ((s = getenv("PERLLIB")) != NULL)
\r
3133 addenv(blk, "PERLLIB=%s", s);
\r
3135 if (conn->request_info.remote_user != NULL) {
\r
3136 addenv(blk, "REMOTE_USER=%s", conn->request_info.remote_user);
\r
3137 addenv(blk, "%s", "AUTH_TYPE=Digest");
\r
3140 // Add all headers as HTTP_* variables
\r
3141 for (i = 0; i < conn->request_info.num_headers; i++) {
\r
3142 p = addenv(blk, "HTTP_%s=%s",
\r
3143 conn->request_info.http_headers[i].name,
\r
3144 conn->request_info.http_headers[i].value);
\r
3146 // Convert variable name into uppercase, and change - to _
\r
3147 for (; *p != '=' && *p != '\0'; p++) {
\r
3150 *p = (char) toupper(* (unsigned char *) p);
\r
3154 // Add user-specified variables
\r
3155 s = conn->ctx->config[CGI_ENVIRONMENT];
\r
3156 while ((s = next_option(s, &var_vec, NULL)) != NULL) {
\r
3157 addenv(blk, "%.*s", (int) var_vec.len, var_vec.ptr);
\r
3160 blk->vars[blk->nvars++] = NULL;
\r
3161 blk->buf[blk->len++] = '\0';
\r
3163 assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
\r
3164 assert(blk->len > 0);
\r
3165 assert(blk->len < (int) sizeof(blk->buf));
\r
3168 static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
\r
3169 int headers_len, data_len, i, fd_stdin[2], fd_stdout[2];
\r
3170 const char *status, *status_text;
\r
3171 char buf[16384], *pbuf, dir[PATH_MAX], *p;
\r
3172 struct mg_request_info ri;
\r
3173 struct cgi_env_block blk;
\r
3175 struct file fout = STRUCT_FILE_INITIALIZER;
\r
3178 prepare_cgi_environment(conn, prog, &blk);
\r
3180 // CGI must be executed in its own directory. 'dir' must point to the
\r
3181 // directory containing executable program, 'p' must point to the
\r
3182 // executable program name relative to 'dir'.
\r
3183 (void) mg_snprintf(conn, dir, sizeof(dir), "%s", prog);
\r
3184 if ((p = strrchr(dir, '/')) != NULL) {
\r
3187 dir[0] = '.', dir[1] = '\0';
\r
3188 p = (char *) prog;
\r
3192 fd_stdin[0] = fd_stdin[1] = fd_stdout[0] = fd_stdout[1] = -1;
\r
3195 if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) {
\r
3196 send_http_error(conn, 500, http_500_error,
\r
3197 "Cannot create CGI pipe: %s", strerror(ERRNO));
\r
3201 pid = spawn_process(conn, p, blk.buf, blk.vars, fd_stdin[0], fd_stdout[1],
\r
3203 // spawn_process() must close those!
\r
3204 // If we don't mark them as closed, close() attempt before
\r
3205 // return from this function throws an exception on Windows.
\r
3206 // Windows does not like when closed descriptor is closed again.
\r
3207 fd_stdin[0] = fd_stdout[1] = -1;
\r
3209 if (pid == (pid_t) -1) {
\r
3210 send_http_error(conn, 500, http_500_error,
\r
3211 "Cannot spawn CGI process [%s]: %s", prog, strerror(ERRNO));
\r
3215 if ((in = fdopen(fd_stdin[1], "wb")) == NULL ||
\r
3216 (out = fdopen(fd_stdout[0], "rb")) == NULL) {
\r
3217 send_http_error(conn, 500, http_500_error,
\r
3218 "fopen: %s", strerror(ERRNO));
\r
3223 setbuf(out, NULL);
\r
3226 // Send POST data to the CGI process if needed
\r
3227 if (!strcmp(conn->request_info.request_method, "POST") &&
\r
3228 !forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
\r
3232 // Close so child gets an EOF.
\r
3237 // Now read CGI reply into a buffer. We need to set correct
\r
3238 // status code, thus we need to see all HTTP headers first.
\r
3239 // Do not send anything back to client, until we buffer in all
\r
3242 headers_len = read_request(out, conn, buf, sizeof(buf), &data_len);
\r
3243 if (headers_len <= 0) {
\r
3244 send_http_error(conn, 500, http_500_error,
\r
3245 "CGI program sent malformed or too big (>%u bytes) "
\r
3246 "HTTP headers: [%.*s]",
\r
3247 (unsigned) sizeof(buf), data_len, buf);
\r
3251 buf[headers_len - 1] = '\0';
\r
3252 parse_http_headers(&pbuf, &ri);
\r
3254 // Make up and send the status line
\r
3255 status_text = "OK";
\r
3256 if ((status = get_header(&ri, "Status")) != NULL) {
\r
3257 conn->status_code = atoi(status);
\r
3258 status_text = status;
\r
3259 while (isdigit(* (unsigned char *) status_text) || *status_text == ' ') {
\r
3262 } else if (get_header(&ri, "Location") != NULL) {
\r
3263 conn->status_code = 302;
\r
3265 conn->status_code = 200;
\r
3267 if (get_header(&ri, "Connection") != NULL &&
\r
3268 !mg_strcasecmp(get_header(&ri, "Connection"), "keep-alive")) {
\r
3269 conn->must_close = 1;
\r
3271 (void) mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code,
\r
3275 for (i = 0; i < ri.num_headers; i++) {
\r
3276 mg_printf(conn, "%s: %s\r\n",
\r
3277 ri.http_headers[i].name, ri.http_headers[i].value);
\r
3279 mg_write(conn, "\r\n", 2);
\r
3281 // Send chunk of data that may have been read after the headers
\r
3282 conn->num_bytes_sent += mg_write(conn, buf + headers_len,
\r
3283 (size_t)(data_len - headers_len));
\r
3285 // Read the rest of CGI output and send to the client
\r
3286 send_file_data(conn, &fout, 0, INT64_MAX);
\r
3289 if (pid != (pid_t) -1) {
\r
3290 kill(pid, SIGKILL);
\r
3292 if (fd_stdin[0] != -1) {
\r
3293 close(fd_stdin[0]);
\r
3295 if (fd_stdout[1] != -1) {
\r
3296 close(fd_stdout[1]);
\r
3301 } else if (fd_stdin[1] != -1) {
\r
3302 close(fd_stdin[1]);
\r
3305 if (out != NULL) {
\r
3307 } else if (fd_stdout[0] != -1) {
\r
3308 close(fd_stdout[0]);
\r
3313 // For a given PUT path, create all intermediate subdirectories
\r
3314 // for given path. Return 0 if the path itself is a directory,
\r
3315 // or -1 on error, 1 if OK.
\r
3316 static int put_dir(struct mg_connection *conn, const char *path) {
\r
3317 char buf[PATH_MAX];
\r
3318 const char *s, *p;
\r
3319 struct file file = STRUCT_FILE_INITIALIZER;
\r
3322 for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
\r
3324 if (len >= (int) sizeof(buf)) {
\r
3328 memcpy(buf, path, len);
\r
3331 // Try to create intermediate directory
\r
3332 DEBUG_TRACE(("mkdir(%s)", buf));
\r
3333 if (!mg_stat(conn, buf, &file) && mg_mkdir(buf, 0755) != 0) {
\r
3338 // Is path itself a directory?
\r
3339 if (p[1] == '\0') {
\r
3347 static void put_file(struct mg_connection *conn, const char *path) {
\r
3348 struct file file = STRUCT_FILE_INITIALIZER;
\r
3349 const char *range;
\r
3353 conn->status_code = mg_stat(conn, path, &file) ? 200 : 201;
\r
3355 if ((rc = put_dir(conn, path)) == 0) {
\r
3356 mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", conn->status_code);
\r
3357 } else if (rc == -1) {
\r
3358 send_http_error(conn, 500, http_500_error,
\r
3359 "put_dir(%s): %s", path, strerror(ERRNO));
\r
3360 } else if (!mg_fopen(conn, path, "wb+", &file) || file.fp == NULL) {
\r
3362 send_http_error(conn, 500, http_500_error,
\r
3363 "fopen(%s): %s", path, strerror(ERRNO));
\r
3365 fclose_on_exec(&file);
\r
3366 range = mg_get_header(conn, "Content-Range");
\r
3368 if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
\r
3369 conn->status_code = 206;
\r
3370 fseeko(file.fp, r1, SEEK_SET);
\r
3372 if (forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) {
\r
3373 mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", conn->status_code);
\r
3379 static void send_ssi_file(struct mg_connection *, const char *,
\r
3380 struct file *, int);
\r
3382 static void do_ssi_include(struct mg_connection *conn, const char *ssi,
\r
3383 char *tag, int include_level) {
\r
3384 char file_name[MG_BUF_LEN], path[PATH_MAX], *p;
\r
3385 struct file file = STRUCT_FILE_INITIALIZER;
\r
3387 // sscanf() is safe here, since send_ssi_file() also uses buffer
\r
3388 // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN.
\r
3389 if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
\r
3390 // File name is relative to the webserver root
\r
3391 (void) mg_snprintf(conn, path, sizeof(path), "%s%c%s",
\r
3392 conn->ctx->config[DOCUMENT_ROOT], '/', file_name);
\r
3393 } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1) {
\r
3394 // File name is relative to the webserver working directory
\r
3395 // or it is absolute system path
\r
3396 (void) mg_snprintf(conn, path, sizeof(path), "%s", file_name);
\r
3397 } else if (sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
\r
3398 // File name is relative to the currect document
\r
3399 (void) mg_snprintf(conn, path, sizeof(path), "%s", ssi);
\r
3400 if ((p = strrchr(path, '/')) != NULL) {
\r
3403 (void) mg_snprintf(conn, path + strlen(path),
\r
3404 sizeof(path) - strlen(path), "%s", file_name);
\r
3406 cry(conn, "Bad SSI #include: [%s]", tag);
\r
3410 if (!mg_fopen(conn, path, "rb", &file)) {
\r
3411 cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
\r
3412 tag, path, strerror(ERRNO));
\r
3414 fclose_on_exec(&file);
\r
3415 if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
\r
3416 strlen(conn->ctx->config[SSI_EXTENSIONS]), path) > 0) {
\r
3417 send_ssi_file(conn, path, &file, include_level + 1);
\r
3419 send_file_data(conn, &file, 0, INT64_MAX);
\r
3425 #if !defined(NO_POPEN)
\r
3426 static void do_ssi_exec(struct mg_connection *conn, char *tag) {
\r
3427 char cmd[MG_BUF_LEN];
\r
3428 struct file file = STRUCT_FILE_INITIALIZER;
\r
3430 if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
\r
3431 cry(conn, "Bad SSI #exec: [%s]", tag);
\r
3432 } else if ((file.fp = popen(cmd, "r")) == NULL) {
\r
3433 cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
\r
3435 send_file_data(conn, &file, 0, INT64_MAX);
\r
3439 #endif // !NO_POPEN
\r
3441 static int mg_fgetc(struct file *filep, int offset) {
\r
3442 if (filep->membuf != NULL && offset >=0 && offset < filep->size) {
\r
3443 return ((unsigned char *) filep->membuf)[offset];
\r
3444 } else if (filep->fp != NULL) {
\r
3445 return fgetc(filep->fp);
\r
3451 static void send_ssi_file(struct mg_connection *conn, const char *path,
\r
3452 struct file *filep, int include_level) {
\r
3453 char buf[MG_BUF_LEN];
\r
3454 int ch, offset, len, in_ssi_tag;
\r
3456 if (include_level > 10) {
\r
3457 cry(conn, "SSI #include level is too deep (%s)", path);
\r
3461 in_ssi_tag = len = offset = 0;
\r
3462 while ((ch = mg_fgetc(filep, offset)) != EOF) {
\r
3463 if (in_ssi_tag && ch == '>') {
\r
3465 buf[len++] = (char) ch;
\r
3467 assert(len <= (int) sizeof(buf));
\r
3468 if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
\r
3469 // Not an SSI tag, pass it
\r
3470 (void) mg_write(conn, buf, (size_t) len);
\r
3472 if (!memcmp(buf + 5, "include", 7)) {
\r
3473 do_ssi_include(conn, path, buf + 12, include_level);
\r
3474 #if !defined(NO_POPEN)
\r
3475 } else if (!memcmp(buf + 5, "exec", 4)) {
\r
3476 do_ssi_exec(conn, buf + 9);
\r
3477 #endif // !NO_POPEN
\r
3479 cry(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
\r
3483 } else if (in_ssi_tag) {
\r
3484 if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
\r
3487 } else if (len == (int) sizeof(buf) - 2) {
\r
3488 cry(conn, "%s: SSI tag is too large", path);
\r
3491 buf[len++] = ch & 0xff;
\r
3492 } else if (ch == '<') {
\r
3495 mg_write(conn, buf, (size_t) len);
\r
3498 buf[len++] = ch & 0xff;
\r
3500 buf[len++] = ch & 0xff;
\r
3501 if (len == (int) sizeof(buf)) {
\r
3502 mg_write(conn, buf, (size_t) len);
\r
3508 // Send the rest of buffered data
\r
3510 mg_write(conn, buf, (size_t) len);
\r
3514 static void handle_ssi_file_request(struct mg_connection *conn,
\r
3515 const char *path) {
\r
3516 struct file file = STRUCT_FILE_INITIALIZER;
\r
3518 if (!mg_fopen(conn, path, "rb", &file)) {
\r
3519 send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path,
\r
3522 conn->must_close = 1;
\r
3523 fclose_on_exec(&file);
\r
3524 mg_printf(conn, "HTTP/1.1 200 OK\r\n"
\r
3525 "Content-Type: text/html\r\nConnection: %s\r\n\r\n",
\r
3526 suggest_connection_header(conn));
\r
3527 send_ssi_file(conn, path, &file, 0);
\r
3532 static void send_options(struct mg_connection *conn) {
\r
3533 conn->status_code = 200;
\r
3535 mg_printf(conn, "%s", "HTTP/1.1 200 OK\r\n"
\r
3536 "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS\r\n"
\r
3537 "DAV: 1\r\n\r\n");
\r
3540 // Writes PROPFIND properties for a collection element
\r
3541 static void print_props(struct mg_connection *conn, const char* uri,
\r
3542 struct file *filep) {
\r
3544 gmt_time_string(mtime, sizeof(mtime), &filep->modification_time);
\r
3545 conn->num_bytes_sent += mg_printf(conn,
\r
3547 "<d:href>%s</d:href>"
\r
3550 "<d:resourcetype>%s</d:resourcetype>"
\r
3551 "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
\r
3552 "<d:getlastmodified>%s</d:getlastmodified>"
\r
3554 "<d:status>HTTP/1.1 200 OK</d:status>"
\r
3556 "</d:response>\n",
\r
3558 filep->is_directory ? "<d:collection/>" : "",
\r
3563 static void print_dav_dir_entry(struct de *de, void *data) {
\r
3564 char href[PATH_MAX];
\r
3565 struct mg_connection *conn = (struct mg_connection *) data;
\r
3566 mg_snprintf(conn, href, sizeof(href), "%s%s",
\r
3567 conn->request_info.uri, de->file_name);
\r
3568 print_props(conn, href, &de->file);
\r
3571 static void handle_propfind(struct mg_connection *conn, const char *path,
\r
3572 struct file *filep) {
\r
3573 const char *depth = mg_get_header(conn, "Depth");
\r
3575 conn->must_close = 1;
\r
3576 conn->status_code = 207;
\r
3577 mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
\r
3578 "Connection: close\r\n"
\r
3579 "Content-Type: text/xml; charset=utf-8\r\n\r\n");
\r
3581 conn->num_bytes_sent += mg_printf(conn,
\r
3582 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
\r
3583 "<d:multistatus xmlns:d='DAV:'>\n");
\r
3585 // Print properties for the requested resource itself
\r
3586 print_props(conn, conn->request_info.uri, filep);
\r
3588 // If it is a directory, print directory entries too if Depth is not 0
\r
3589 if (filep->is_directory &&
\r
3590 !mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes") &&
\r
3591 (depth == NULL || strcmp(depth, "0") != 0)) {
\r
3592 scan_directory(conn, path, conn, &print_dav_dir_entry);
\r
3595 conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>");
\r
3598 #if defined(USE_WEBSOCKET)
\r
3600 // START OF SHA-1 code
\r
3601 // Copyright(c) By Steve Reid <steve@edmweb.com>
\r
3602 #define SHA1HANDSOFF
\r
3603 #if defined(__sun)
\r
3604 #include "solarisfixes.h"
\r
3607 union char64long16 { unsigned char c[64]; uint32_t l[16]; };
\r
3609 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
\r
3611 static uint32_t blk0(union char64long16 *block, int i) {
\r
3612 // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN
\r
3613 if (!is_big_endian()) {
\r
3614 block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
\r
3615 (rol(block->l[i], 8) & 0x00FF00FF);
\r
3617 return block->l[i];
\r
3620 #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
\r
3621 ^block->l[(i+2)&15]^block->l[i&15],1))
\r
3622 #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
\r
3623 #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
\r
3624 #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
\r
3625 #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
\r
3626 #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
\r
3629 uint32_t state[5];
\r
3630 uint32_t count[2];
\r
3631 unsigned char buffer[64];
\r
3634 static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) {
\r
3635 uint32_t a, b, c, d, e;
\r
3636 union char64long16 block[1];
\r
3638 memcpy(block, buffer, 64);
\r
3644 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);
\r
3645 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);
\r
3646 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);
\r
3647 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);
\r
3648 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);
\r
3649 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);
\r
3650 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);
\r
3651 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);
\r
3652 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);
\r
3653 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);
\r
3654 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);
\r
3655 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);
\r
3656 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);
\r
3657 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);
\r
3658 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);
\r
3659 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);
\r
3660 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);
\r
3661 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);
\r
3662 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);
\r
3663 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);
\r
3669 a = b = c = d = e = 0;
\r
3670 memset(block, '\0', sizeof(block));
\r
3673 static void SHA1Init(SHA1_CTX* context) {
\r
3674 context->state[0] = 0x67452301;
\r
3675 context->state[1] = 0xEFCDAB89;
\r
3676 context->state[2] = 0x98BADCFE;
\r
3677 context->state[3] = 0x10325476;
\r
3678 context->state[4] = 0xC3D2E1F0;
\r
3679 context->count[0] = context->count[1] = 0;
\r
3682 static void SHA1Update(SHA1_CTX* context, const unsigned char* data,
\r
3686 j = context->count[0];
\r
3687 if ((context->count[0] += len << 3) < j)
\r
3688 context->count[1]++;
\r
3689 context->count[1] += (len>>29);
\r
3690 j = (j >> 3) & 63;
\r
3691 if ((j + len) > 63) {
\r
3692 memcpy(&context->buffer[j], data, (i = 64-j));
\r
3693 SHA1Transform(context->state, context->buffer);
\r
3694 for ( ; i + 63 < len; i += 64) {
\r
3695 SHA1Transform(context->state, &data[i]);
\r
3700 memcpy(&context->buffer[j], &data[i], len - i);
\r
3703 static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) {
\r
3705 unsigned char finalcount[8], c;
\r
3707 for (i = 0; i < 8; i++) {
\r
3708 finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
\r
3709 >> ((3-(i & 3)) * 8) ) & 255);
\r
3712 SHA1Update(context, &c, 1);
\r
3713 while ((context->count[0] & 504) != 448) {
\r
3715 SHA1Update(context, &c, 1);
\r
3717 SHA1Update(context, finalcount, 8);
\r
3718 for (i = 0; i < 20; i++) {
\r
3719 digest[i] = (unsigned char)
\r
3720 ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
\r
3722 memset(context, '\0', sizeof(*context));
\r
3723 memset(&finalcount, '\0', sizeof(finalcount));
\r
3725 // END OF SHA1 CODE
\r
3727 static void base64_encode(const unsigned char *src, int src_len, char *dst) {
\r
3728 static const char *b64 =
\r
3729 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
\r
3730 int i, j, a, b, c;
\r
3732 for (i = j = 0; i < src_len; i += 3) {
\r
3734 b = i + 1 >= src_len ? 0 : src[i + 1];
\r
3735 c = i + 2 >= src_len ? 0 : src[i + 2];
\r
3737 dst[j++] = b64[a >> 2];
\r
3738 dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
\r
3739 if (i + 1 < src_len) {
\r
3740 dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
\r
3742 if (i + 2 < src_len) {
\r
3743 dst[j++] = b64[c & 63];
\r
3746 while (j % 4 != 0) {
\r
3752 static void send_websocket_handshake(struct mg_connection *conn) {
\r
3753 static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
\r
3754 char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
\r
3757 mg_snprintf(conn, buf, sizeof(buf), "%s%s",
\r
3758 mg_get_header(conn, "Sec-WebSocket-Key"), magic);
\r
3759 SHA1Init(&sha_ctx);
\r
3760 SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf));
\r
3761 SHA1Final((unsigned char *) sha, &sha_ctx);
\r
3762 base64_encode((unsigned char *) sha, sizeof(sha), b64_sha);
\r
3763 mg_printf(conn, "%s%s%s",
\r
3764 "HTTP/1.1 101 Switching Protocols\r\n"
\r
3765 "Upgrade: websocket\r\n"
\r
3766 "Connection: Upgrade\r\n"
\r
3767 "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n");
\r
3770 static void read_websocket(struct mg_connection *conn) {
\r
3771 unsigned char *buf = (unsigned char *) conn->buf + conn->request_len;
\r
3772 int n, len, mask_len, body_len, discard_len;
\r
3775 if ((body_len = conn->data_len - conn->request_len) >= 2) {
\r
3776 len = buf[1] & 127;
\r
3777 mask_len = buf[1] & 128 ? 4 : 0;
\r
3779 conn->content_len = 2 + mask_len + len;
\r
3780 } else if (len == 126 && body_len >= 4) {
\r
3781 conn->content_len = 4 + mask_len + ((((int) buf[2]) << 8) + buf[3]);
\r
3782 } else if (body_len >= 10) {
\r
3783 conn->content_len = 10 + mask_len +
\r
3784 (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) +
\r
3785 htonl(* (uint32_t *) &buf[6]);
\r
3789 if (conn->content_len > 0) {
\r
3790 if (conn->ctx->callbacks.websocket_data != NULL &&
\r
3791 conn->ctx->callbacks.websocket_data(conn) == 0) {
\r
3792 break; // Callback signalled to exit
\r
3794 discard_len = conn->content_len > body_len ?
\r
3795 body_len : (int) conn->content_len;
\r
3796 memmove(buf, buf + discard_len, conn->data_len - discard_len);
\r
3797 conn->data_len -= discard_len;
\r
3798 conn->content_len = conn->consumed_content = 0;
\r
3800 n = pull(NULL, conn, conn->buf + conn->data_len,
\r
3801 conn->buf_size - conn->data_len);
\r
3805 conn->data_len += n;
\r
3810 static void handle_websocket_request(struct mg_connection *conn) {
\r
3811 if (strcmp(mg_get_header(conn, "Sec-WebSocket-Version"), "13") != 0) {
\r
3812 send_http_error(conn, 426, "Upgrade Required", "%s", "Upgrade Required");
\r
3813 } else if (conn->ctx->callbacks.websocket_connect != NULL &&
\r
3814 conn->ctx->callbacks.websocket_connect(conn) != 0) {
\r
3815 // Callback has returned non-zero, do not proceed with handshake
\r
3817 send_websocket_handshake(conn);
\r
3818 if (conn->ctx->callbacks.websocket_ready != NULL) {
\r
3819 conn->ctx->callbacks.websocket_ready(conn);
\r
3821 read_websocket(conn);
\r
3825 static int is_websocket_request(const struct mg_connection *conn) {
\r
3826 const char *host, *upgrade, *connection, *version, *key;
\r
3828 host = mg_get_header(conn, "Host");
\r
3829 upgrade = mg_get_header(conn, "Upgrade");
\r
3830 connection = mg_get_header(conn, "Connection");
\r
3831 key = mg_get_header(conn, "Sec-WebSocket-Key");
\r
3832 version = mg_get_header(conn, "Sec-WebSocket-Version");
\r
3834 return host != NULL && upgrade != NULL && connection != NULL &&
\r
3835 key != NULL && version != NULL &&
\r
3836 strstr(upgrade, "websocket") != NULL &&
\r
3837 strstr(connection, "Upgrade") != NULL;
\r
3839 #endif // !USE_WEBSOCKET
\r
3841 static int isbyte(int n) {
\r
3842 return n >= 0 && n <= 255;
\r
3845 static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
\r
3846 int n, a, b, c, d, slash = 32, len = 0;
\r
3848 if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 ||
\r
3849 sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) &&
\r
3850 isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) &&
\r
3851 slash >= 0 && slash < 33) {
\r
3853 *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | d;
\r
3854 *mask = slash ? 0xffffffffU << (32 - slash) : 0;
\r
3860 static int set_throttle(const char *spec, uint32_t remote_ip, const char *uri) {
\r
3862 struct vec vec, val;
\r
3863 uint32_t net, mask;
\r
3867 while ((spec = next_option(spec, &vec, &val)) != NULL) {
\r
3869 if (sscanf(val.ptr, "%lf%c", &v, &mult) < 1 || v < 0 ||
\r
3870 (lowercase(&mult) != 'k' && lowercase(&mult) != 'm' && mult != ',')) {
\r
3873 v *= lowercase(&mult) == 'k' ? 1024 : lowercase(&mult) == 'm' ? 1048576 : 1;
\r
3874 if (vec.len == 1 && vec.ptr[0] == '*') {
\r
3875 throttle = (int) v;
\r
3876 } else if (parse_net(vec.ptr, &net, &mask) > 0) {
\r
3877 if ((remote_ip & mask) == net) {
\r
3878 throttle = (int) v;
\r
3880 } else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
\r
3881 throttle = (int) v;
\r
3888 static uint32_t get_remote_ip(const struct mg_connection *conn) {
\r
3889 return ntohl(* (uint32_t *) &conn->client.rsa.sin.sin_addr);
\r
3895 static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
\r
3897 HANDLE fh = (HANDLE) _get_osfhandle(fd);
\r
3898 HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
\r
3899 void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
\r
3904 #define munmap(x, y) UnmapViewOfFile(x)
\r
3905 #define MAP_FAILED NULL
\r
3906 #define MAP_PRIVATE 0
\r
3907 #define PROT_READ 0
\r
3909 #include <sys/mman.h>
\r
3912 static void lsp(struct mg_connection *conn, const char *p, int64_t len,
\r
3914 int i, j, pos = 0;
\r
3916 for (i = 0; i < len; i++) {
\r
3917 if (p[i] == '<' && p[i + 1] == '?') {
\r
3918 for (j = i + 1; j < len ; j++) {
\r
3919 if (p[j] == '?' && p[j + 1] == '>') {
\r
3920 mg_write(conn, p + pos, i - pos);
\r
3921 if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), "") == LUA_OK) {
\r
3922 lua_pcall(L, 0, LUA_MULTRET, 0);
\r
3933 mg_write(conn, p + pos, i - pos);
\r
3937 static int lsp_mg_print(lua_State *L) {
\r
3941 struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
\r
3943 num_args = lua_gettop(L);
\r
3944 for (i = 1; i <= num_args; i++) {
\r
3945 if (lua_isstring(L, i)) {
\r
3946 str = lua_tolstring(L, i, &size);
\r
3947 mg_write(conn, str, size);
\r
3954 static int lsp_mg_read(lua_State *L) {
\r
3955 struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
\r
3957 int len = mg_read(conn, buf, sizeof(buf));
\r
3960 lua_pushlstring(L, buf, len);
\r
3965 static void reg_string(struct lua_State *L, const char *name, const char *val) {
\r
3966 lua_pushstring(L, name);
\r
3967 lua_pushstring(L, val);
\r
3968 lua_rawset(L, -3);
\r
3971 static void reg_int(struct lua_State *L, const char *name, int val) {
\r
3972 lua_pushstring(L, name);
\r
3973 lua_pushinteger(L, val);
\r
3974 lua_rawset(L, -3);
\r
3977 static void prepare_lua_environment(struct mg_connection *conn, lua_State *L) {
\r
3978 const struct mg_request_info *ri = mg_get_request_info(conn);
\r
3979 extern void luaL_openlibs(lua_State *);
\r
3983 #ifdef USE_LUA_SQLITE3
\r
3984 { extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); }
\r
3987 // Register "print" function which calls mg_write()
\r
3988 lua_pushlightuserdata(L, conn);
\r
3989 lua_pushcclosure(L, lsp_mg_print, 1);
\r
3990 lua_setglobal(L, "print");
\r
3992 // Register mg_read()
\r
3993 lua_pushlightuserdata(L, conn);
\r
3994 lua_pushcclosure(L, lsp_mg_read, 1);
\r
3995 lua_setglobal(L, "read");
\r
3997 // Export request_info
\r
3999 reg_string(L, "request_method", ri->request_method);
\r
4000 reg_string(L, "uri", ri->uri);
\r
4001 reg_string(L, "http_version", ri->http_version);
\r
4002 reg_string(L, "query_string", ri->query_string);
\r
4003 reg_int(L, "remote_ip", ri->remote_ip);
\r
4004 reg_int(L, "remote_port", ri->remote_port);
\r
4005 reg_int(L, "num_headers", ri->num_headers);
\r
4006 lua_pushstring(L, "http_headers");
\r
4008 for (i = 0; i < ri->num_headers; i++) {
\r
4009 reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
\r
4011 lua_rawset(L, -3);
\r
4012 lua_setglobal(L, "request_info");
\r
4015 static void handle_lsp_request(struct mg_connection *conn, const char *path,
\r
4016 struct file *filep) {
\r
4018 lua_State *L = NULL;
\r
4020 if (!mg_stat(conn, path, filep) || !mg_fopen(conn, path, "r", filep)) {
\r
4021 send_http_error(conn, 404, "Not Found", "%s", "File not found");
\r
4022 } else if (filep->membuf == NULL &&
\r
4023 (p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
\r
4024 fileno(filep->fp), 0)) == MAP_FAILED) {
\r
4025 send_http_error(conn, 500, http_500_error, "mmap(%s, %zu, %d): %s", path,
\r
4026 (size_t) filep->size, fileno(filep->fp), strerror(errno));
\r
4027 } else if ((L = luaL_newstate()) == NULL) {
\r
4028 send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed");
\r
4030 // We're not sending HTTP headers here, Lua page must do it.
\r
4031 prepare_lua_environment(conn, L);
\r
4032 if (conn->ctx->callbacks.init_lua != NULL) {
\r
4033 conn->ctx->callbacks.init_lua(conn, L);
\r
4035 lsp(conn, filep->membuf == NULL ? p : filep->membuf, filep->size, L);
\r
4038 if (L) lua_close(L);
\r
4039 if (p) munmap(p, filep->size);
\r
4044 int mg_upload(struct mg_connection *conn, const char *destination_dir) {
\r
4045 const char *content_type_header, *boundary_start;
\r
4046 char buf[MG_BUF_LEN], path[PATH_MAX], fname[1024], boundary[100], *s;
\r
4048 int bl, n, i, j, headers_len, boundary_len, len = 0, num_uploaded_files = 0;
\r
4050 // Request looks like this:
\r
4052 // POST /upload HTTP/1.1
\r
4053 // Host: 127.0.0.1:8080
\r
4054 // Content-Length: 244894
\r
4055 // Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryRVr
\r
4057 // ------WebKitFormBoundaryRVr
\r
4058 // Content-Disposition: form-data; name="file"; filename="accum.png"
\r
4059 // Content-Type: image/png
\r
4063 // ------WebKitFormBoundaryRVr
\r
4065 // Extract boundary string from the Content-Type header
\r
4066 if ((content_type_header = mg_get_header(conn, "Content-Type")) == NULL ||
\r
4067 (boundary_start = strstr(content_type_header, "boundary=")) == NULL ||
\r
4068 (sscanf(boundary_start, "boundary=\"%99[^\"]\"", boundary) == 0 &&
\r
4069 sscanf(boundary_start, "boundary=%99s", boundary) == 0) ||
\r
4070 boundary[0] == '\0') {
\r
4071 return num_uploaded_files;
\r
4074 boundary_len = strlen(boundary);
\r
4075 bl = boundary_len + 4; // \r\n--<boundary>
\r
4077 // Pull in headers
\r
4078 assert(len >= 0 && len <= (int) sizeof(buf));
\r
4079 while ((n = mg_read(conn, buf + len, sizeof(buf) - len)) > 0) {
\r
4082 if ((headers_len = get_request_len(buf, len)) <= 0) {
\r
4086 // Fetch file name.
\r
4088 for (i = j = 0; i < headers_len; i++) {
\r
4089 if (buf[i] == '\r' && buf[i + 1] == '\n') {
\r
4090 buf[i] = buf[i + 1] = '\0';
\r
4091 // TODO(lsm): don't expect filename to be the 3rd field,
\r
4092 // parse the header properly instead.
\r
4093 sscanf(&buf[j], "Content-Disposition: %*s %*s filename=\"%1023[^\"]",
\r
4099 // Give up if the headers are not what we expect
\r
4100 if (fname[0] == '\0') {
\r
4104 // Move data to the beginning of the buffer
\r
4105 assert(len >= headers_len);
\r
4106 memmove(buf, &buf[headers_len], len - headers_len);
\r
4107 len -= headers_len;
\r
4109 // We open the file with exclusive lock held. This guarantee us
\r
4110 // there is no other thread can save into the same file simultaneously.
\r
4112 // Construct destination file name. Do not allow paths to have slashes.
\r
4113 if ((s = strrchr(fname, '/')) == NULL) {
\r
4116 // Open file in binary mode. TODO: set an exclusive lock.
\r
4117 snprintf(path, sizeof(path), "%s/%s", destination_dir, s);
\r
4118 if ((fp = fopen(path, "wb")) == NULL) {
\r
4122 // Read POST data, write into file until boundary is found.
\r
4126 for (i = 0; i < len - bl; i++) {
\r
4127 if (!memcmp(&buf[i], "\r\n--", 4) &&
\r
4128 !memcmp(&buf[i + 4], boundary, boundary_len)) {
\r
4129 // Found boundary, that's the end of file data.
\r
4130 fwrite(buf, 1, i, fp);
\r
4132 num_uploaded_files++;
\r
4133 if (conn->ctx->callbacks.upload != NULL) {
\r
4134 conn->ctx->callbacks.upload(conn, path);
\r
4136 memmove(buf, &buf[i + bl], len - (i + bl));
\r
4142 fwrite(buf, 1, len - bl, fp);
\r
4143 memmove(buf, &buf[len - bl], bl);
\r
4146 } while ((n = mg_read(conn, buf + len, sizeof(buf) - len)) > 0);
\r
4150 return num_uploaded_files;
\r
4153 static int is_put_or_delete_request(const struct mg_connection *conn) {
\r
4154 const char *s = conn->request_info.request_method;
\r
4155 return s != NULL && (!strcmp(s, "PUT") || !strcmp(s, "DELETE"));
\r
4158 static int get_first_ssl_listener_index(const struct mg_context *ctx) {
\r
4159 int i, index = -1;
\r
4160 for (i = 0; index == -1 && i < ctx->num_listening_sockets; i++) {
\r
4161 index = ctx->listening_sockets[i].is_ssl ? i : -1;
\r
4166 static void redirect_to_https_port(struct mg_connection *conn, int ssl_index) {
\r
4168 const char *host_header;
\r
4170 if ((host_header = mg_get_header(conn, "Host")) == NULL ||
\r
4171 sscanf(host_header, "%1024[^:]", host) == 0) {
\r
4172 // Cannot get host from the Host: header. Fallback to our IP address.
\r
4173 sockaddr_to_string(host, sizeof(host), &conn->client.lsa);
\r
4176 mg_printf(conn, "HTTP/1.1 302 Found\r\nLocation: https://%s:%d%s\r\n\r\n",
\r
4177 host, (int) ntohs(conn->ctx->listening_sockets[ssl_index].
\r
4178 lsa.sin.sin_port), conn->request_info.uri);
\r
4181 // This is the heart of the Mongoose's logic.
\r
4182 // This function is called when the request is read, parsed and validated,
\r
4183 // and Mongoose must decide what action to take: serve a file, or
\r
4184 // a directory, or call embedded function, etcetera.
\r
4185 static void handle_request(struct mg_connection *conn) {
\r
4186 struct mg_request_info *ri = &conn->request_info;
\r
4187 char path[PATH_MAX];
\r
4188 int uri_len, ssl_index;
\r
4189 struct file file = STRUCT_FILE_INITIALIZER;
\r
4191 if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
\r
4192 * ((char *) conn->request_info.query_string++) = '\0';
\r
4194 uri_len = (int) strlen(ri->uri);
\r
4195 url_decode(ri->uri, uri_len, (char *) ri->uri, uri_len + 1, 0);
\r
4196 remove_double_dots_and_double_slashes((char *) ri->uri);
\r
4197 convert_uri_to_file_name(conn, path, sizeof(path), &file);
\r
4198 conn->throttle = set_throttle(conn->ctx->config[THROTTLE],
\r
4199 get_remote_ip(conn), ri->uri);
\r
4201 DEBUG_TRACE(("%s", ri->uri));
\r
4202 if (conn->ctx->callbacks.begin_request != NULL &&
\r
4203 conn->ctx->callbacks.begin_request(conn)) {
\r
4204 // Do nothing, callback has served the request
\r
4205 } else if (!conn->client.is_ssl && conn->client.ssl_redir &&
\r
4206 (ssl_index = get_first_ssl_listener_index(conn->ctx)) > -1) {
\r
4207 redirect_to_https_port(conn, ssl_index);
\r
4208 } else if (!is_put_or_delete_request(conn) &&
\r
4209 !check_authorization(conn, path)) {
\r
4210 send_authorization_request(conn);
\r
4211 #if defined(USE_WEBSOCKET)
\r
4212 } else if (is_websocket_request(conn)) {
\r
4213 handle_websocket_request(conn);
\r
4215 } else if (!strcmp(ri->request_method, "OPTIONS")) {
\r
4216 send_options(conn);
\r
4217 } else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
\r
4218 send_http_error(conn, 404, "Not Found", "Not Found");
\r
4219 } else if (is_put_or_delete_request(conn) &&
\r
4220 (conn->ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL ||
\r
4221 is_authorized_for_put(conn) != 1)) {
\r
4222 send_authorization_request(conn);
\r
4223 } else if (!strcmp(ri->request_method, "PUT")) {
\r
4224 put_file(conn, path);
\r
4225 } else if (!strcmp(ri->request_method, "DELETE")) {
\r
4226 if (mg_remove(path) == 0) {
\r
4227 send_http_error(conn, 200, "OK", "%s", "");
\r
4229 send_http_error(conn, 500, http_500_error, "remove(%s): %s", path,
\r
4232 } else if ((file.membuf == NULL && file.modification_time == (time_t) 0) ||
\r
4233 must_hide_file(conn, path)) {
\r
4234 send_http_error(conn, 404, "Not Found", "%s", "File not found");
\r
4235 } else if (file.is_directory && ri->uri[uri_len - 1] != '/') {
\r
4236 mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n"
\r
4237 "Location: %s/\r\n\r\n", ri->uri);
\r
4238 } else if (!strcmp(ri->request_method, "PROPFIND")) {
\r
4239 handle_propfind(conn, path, &file);
\r
4240 } else if (file.is_directory &&
\r
4241 !substitute_index_file(conn, path, sizeof(path), &file)) {
\r
4242 if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")) {
\r
4243 handle_directory_request(conn, path);
\r
4245 send_http_error(conn, 403, "Directory Listing Denied",
\r
4246 "Directory listing denied");
\r
4249 } else if (match_prefix("**.lp$", 6, path) > 0) {
\r
4250 handle_lsp_request(conn, path, &file);
\r
4252 #if !defined(NO_CGI)
\r
4253 } else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
\r
4254 strlen(conn->ctx->config[CGI_EXTENSIONS]),
\r
4256 if (strcmp(ri->request_method, "POST") &&
\r
4257 strcmp(ri->request_method, "HEAD") &&
\r
4258 strcmp(ri->request_method, "GET")) {
\r
4259 send_http_error(conn, 501, "Not Implemented",
\r
4260 "Method %s is not implemented", ri->request_method);
\r
4262 handle_cgi_request(conn, path);
\r
4265 } else if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
\r
4266 strlen(conn->ctx->config[SSI_EXTENSIONS]),
\r
4268 handle_ssi_file_request(conn, path);
\r
4269 } else if (is_not_modified(conn, &file)) {
\r
4270 send_http_error(conn, 304, "Not Modified", "%s", "");
\r
4272 handle_file_request(conn, path, &file);
\r
4276 static void close_all_listening_sockets(struct mg_context *ctx) {
\r
4278 for (i = 0; i < ctx->num_listening_sockets; i++) {
\r
4279 closesocket(ctx->listening_sockets[i].sock);
\r
4281 free(ctx->listening_sockets);
\r
4284 // Valid listening port specification is: [ip_address:]port[s]
\r
4285 // Examples: 80, 443s, 127.0.0.1:3128, 1.2.3.4:8080s
\r
4286 // TODO(lsm): add parsing of the IPv6 address
\r
4287 static int parse_port_string(const struct vec *vec, struct socket *so) {
\r
4288 int a, b, c, d, port, len;
\r
4290 // MacOS needs that. If we do not zero it, subsequent bind() will fail.
\r
4291 // Also, all-zeroes in the socket address means binding to all addresses
\r
4292 // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
\r
4293 memset(so, 0, sizeof(*so));
\r
4295 if (sscanf(vec->ptr, "%d.%d.%d.%d:%d%n", &a, &b, &c, &d, &port, &len) == 5) {
\r
4296 // Bind to a specific IPv4 address
\r
4297 so->lsa.sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
\r
4298 } else if (sscanf(vec->ptr, "%d%n", &port, &len) != 1 ||
\r
4300 len > (int) vec->len ||
\r
4301 (vec->ptr[len] && vec->ptr[len] != 's' &&
\r
4302 vec->ptr[len] != 'r' && vec->ptr[len] != ',')) {
\r
4306 so->is_ssl = vec->ptr[len] == 's';
\r
4307 so->ssl_redir = vec->ptr[len] == 'r';
\r
4308 #if defined(USE_IPV6)
\r
4309 so->lsa.sin6.sin6_family = AF_INET6;
\r
4310 so->lsa.sin6.sin6_port = htons((uint16_t) port);
\r
4312 so->lsa.sin.sin_family = AF_INET;
\r
4313 so->lsa.sin.sin_port = htons((uint16_t) port);
\r
4319 static int set_ports_option(struct mg_context *ctx) {
\r
4320 const char *list = ctx->config[LISTENING_PORTS];
\r
4321 int on = 1, success = 1;
\r
4325 while (success && (list = next_option(list, &vec, NULL)) != NULL) {
\r
4326 if (!parse_port_string(&vec, &so)) {
\r
4327 cry(fc(ctx), "%s: %.*s: invalid port spec. Expecting list of: %s",
\r
4328 __func__, (int) vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|p]");
\r
4330 } else if (so.is_ssl && ctx->ssl_ctx == NULL) {
\r
4331 cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
\r
4333 } else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
\r
4335 // On Windows, SO_REUSEADDR is recommended only for
\r
4336 // broadcast UDP sockets
\r
4337 setsockopt(so.sock, SOL_SOCKET, SO_REUSEADDR,
\r
4338 (void *) &on, sizeof(on)) != 0 ||
\r
4339 bind(so.sock, &so.lsa.sa, sizeof(so.lsa)) != 0 ||
\r
4340 listen(so.sock, SOMAXCONN) != 0) {
\r
4341 cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__,
\r
4342 (int) vec.len, vec.ptr, strerror(ERRNO));
\r
4343 closesocket(so.sock);
\r
4346 set_close_on_exec(so.sock);
\r
4347 // TODO: handle realloc failure
\r
4348 ctx->listening_sockets = realloc(ctx->listening_sockets,
\r
4349 (ctx->num_listening_sockets + 1) *
\r
4350 sizeof(ctx->listening_sockets[0]));
\r
4351 ctx->listening_sockets[ctx->num_listening_sockets] = so;
\r
4352 ctx->num_listening_sockets++;
\r
4357 close_all_listening_sockets(ctx);
\r
4363 static void log_header(const struct mg_connection *conn, const char *header,
\r
4365 const char *header_value;
\r
4367 if ((header_value = mg_get_header(conn, header)) == NULL) {
\r
4368 (void) fprintf(fp, "%s", " -");
\r
4370 (void) fprintf(fp, " \"%s\"", header_value);
\r
4374 static void log_access(const struct mg_connection *conn) {
\r
4375 const struct mg_request_info *ri;
\r
4377 char date[64], src_addr[20];
\r
4379 fp = conn->ctx->config[ACCESS_LOG_FILE] == NULL ? NULL :
\r
4380 fopen(conn->ctx->config[ACCESS_LOG_FILE], "a+");
\r
4385 strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
\r
4386 localtime(&conn->birth_time));
\r
4388 ri = &conn->request_info;
\r
4391 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
\r
4392 fprintf(fp, "%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
\r
4393 src_addr, ri->remote_user == NULL ? "-" : ri->remote_user, date,
\r
4394 ri->request_method ? ri->request_method : "-",
\r
4395 ri->uri ? ri->uri : "-", ri->http_version,
\r
4396 conn->status_code, conn->num_bytes_sent);
\r
4397 log_header(conn, "Referer", fp);
\r
4398 log_header(conn, "User-Agent", fp);
\r
4406 // Verify given socket address against the ACL.
\r
4407 // Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
\r
4408 static int check_acl(struct mg_context *ctx, uint32_t remote_ip) {
\r
4409 int allowed, flag;
\r
4410 uint32_t net, mask;
\r
4412 const char *list = ctx->config[ACCESS_CONTROL_LIST];
\r
4414 // If any ACL is set, deny by default
\r
4415 allowed = list == NULL ? '+' : '-';
\r
4417 while ((list = next_option(list, &vec, NULL)) != NULL) {
\r
4418 flag = vec.ptr[0];
\r
4419 if ((flag != '+' && flag != '-') ||
\r
4420 parse_net(&vec.ptr[1], &net, &mask) == 0) {
\r
4421 cry(fc(ctx), "%s: subnet must be [+|-]x.x.x.x[/x]", __func__);
\r
4425 if (net == (remote_ip & mask)) {
\r
4430 return allowed == '+';
\r
4433 #if !defined(_WIN32)
\r
4434 static int set_uid_option(struct mg_context *ctx) {
\r
4435 struct passwd *pw;
\r
4436 const char *uid = ctx->config[RUN_AS_USER];
\r
4439 if (uid == NULL) {
\r
4442 if ((pw = getpwnam(uid)) == NULL) {
\r
4443 cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
\r
4444 } else if (setgid(pw->pw_gid) == -1) {
\r
4445 cry(fc(ctx), "%s: setgid(%s): %s", __func__, uid, strerror(errno));
\r
4446 } else if (setuid(pw->pw_uid) == -1) {
\r
4447 cry(fc(ctx), "%s: setuid(%s): %s", __func__, uid, strerror(errno));
\r
4457 #if !defined(NO_SSL)
\r
4458 static pthread_mutex_t *ssl_mutexes;
\r
4460 static int sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *)) {
\r
4461 return (conn->ssl = SSL_new(s)) != NULL &&
\r
4462 SSL_set_fd(conn->ssl, conn->client.sock) == 1 &&
\r
4463 func(conn->ssl) == 1;
\r
4466 // Return OpenSSL error message
\r
4467 static const char *ssl_error(void) {
\r
4468 unsigned long err;
\r
4469 err = ERR_get_error();
\r
4470 return err == 0 ? "" : ERR_error_string(err, NULL);
\r
4473 static void ssl_locking_callback(int mode, int mutex_num, const char *file,
\r
4478 if (mode & 1) { // 1 is CRYPTO_LOCK
\r
4479 (void) pthread_mutex_lock(&ssl_mutexes[mutex_num]);
\r
4481 (void) pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
\r
4485 static unsigned long ssl_id_callback(void) {
\r
4486 return (unsigned long) pthread_self();
\r
4489 #if !defined(NO_SSL_DL)
\r
4490 static int load_dll(struct mg_context *ctx, const char *dll_name,
\r
4491 struct ssl_func *sw) {
\r
4492 union {void *p; void (*fp)(void);} u;
\r
4494 struct ssl_func *fp;
\r
4496 if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
\r
4497 cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
\r
4501 for (fp = sw; fp->name != NULL; fp++) {
\r
4503 // GetProcAddress() returns pointer to function
\r
4504 u.fp = (void (*)(void)) dlsym(dll_handle, fp->name);
\r
4506 // dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to
\r
4507 // function pointers. We need to use a union to make a cast.
\r
4508 u.p = dlsym(dll_handle, fp->name);
\r
4510 if (u.fp == NULL) {
\r
4511 cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name);
\r
4520 #endif // NO_SSL_DL
\r
4522 // Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
\r
4523 static int set_ssl_option(struct mg_context *ctx) {
\r
4527 // If PEM file is not specified, skip SSL initialization.
\r
4528 if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL) {
\r
4532 #if !defined(NO_SSL_DL)
\r
4533 if (!load_dll(ctx, SSL_LIB, ssl_sw) ||
\r
4534 !load_dll(ctx, CRYPTO_LIB, crypto_sw)) {
\r
4537 #endif // NO_SSL_DL
\r
4539 // Initialize SSL library
\r
4540 SSL_library_init();
\r
4541 SSL_load_error_strings();
\r
4543 if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
\r
4544 cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error());
\r
4548 // If user callback returned non-NULL, that means that user callback has
\r
4549 // set up certificate itself. In this case, skip sertificate setting.
\r
4550 if ((ctx->callbacks.init_ssl == NULL ||
\r
4551 !ctx->callbacks.init_ssl(ctx->ssl_ctx)) &&
\r
4552 (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 ||
\r
4553 SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0)) {
\r
4554 cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
\r
4558 if (pem != NULL) {
\r
4559 (void) SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem);
\r
4562 // Initialize locking callbacks, needed for thread safety.
\r
4563 // http://www.openssl.org/support/faq.html#PROG1
\r
4564 size = sizeof(pthread_mutex_t) * CRYPTO_num_locks();
\r
4565 if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) {
\r
4566 cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error());
\r
4570 for (i = 0; i < CRYPTO_num_locks(); i++) {
\r
4571 pthread_mutex_init(&ssl_mutexes[i], NULL);
\r
4574 CRYPTO_set_locking_callback(&ssl_locking_callback);
\r
4575 CRYPTO_set_id_callback(&ssl_id_callback);
\r
4580 static void uninitialize_ssl(struct mg_context *ctx) {
\r
4582 if (ctx->ssl_ctx != NULL) {
\r
4583 CRYPTO_set_locking_callback(NULL);
\r
4584 for (i = 0; i < CRYPTO_num_locks(); i++) {
\r
4585 pthread_mutex_destroy(&ssl_mutexes[i]);
\r
4587 CRYPTO_set_locking_callback(NULL);
\r
4588 CRYPTO_set_id_callback(NULL);
\r
4593 static int set_gpass_option(struct mg_context *ctx) {
\r
4594 struct file file = STRUCT_FILE_INITIALIZER;
\r
4595 const char *path = ctx->config[GLOBAL_PASSWORDS_FILE];
\r
4596 if (path != NULL && !mg_stat(fc(ctx), path, &file)) {
\r
4597 cry(fc(ctx), "Cannot open %s: %s", path, strerror(ERRNO));
\r
4603 static int set_acl_option(struct mg_context *ctx) {
\r
4604 return check_acl(ctx, (uint32_t) 0x7f000001UL) != -1;
\r
4607 static void reset_per_request_attributes(struct mg_connection *conn) {
\r
4608 conn->path_info = NULL;
\r
4609 conn->num_bytes_sent = conn->consumed_content = 0;
\r
4610 conn->status_code = -1;
\r
4611 conn->must_close = conn->request_len = conn->throttle = 0;
\r
4614 static void close_socket_gracefully(struct mg_connection *conn) {
\r
4615 #if defined(_WIN32)
\r
4616 char buf[MG_BUF_LEN];
\r
4619 struct linger linger;
\r
4621 // Set linger option to avoid socket hanging out after close. This prevent
\r
4622 // ephemeral port exhaust problem under high QPS.
\r
4623 linger.l_onoff = 1;
\r
4624 linger.l_linger = 1;
\r
4625 setsockopt(conn->client.sock, SOL_SOCKET, SO_LINGER,
\r
4626 (char *) &linger, sizeof(linger));
\r
4628 // Send FIN to the client
\r
4629 shutdown(conn->client.sock, SHUT_WR);
\r
4630 set_non_blocking_mode(conn->client.sock);
\r
4632 #if defined(_WIN32)
\r
4633 // Read and discard pending incoming data. If we do not do that and close the
\r
4634 // socket, the data in the send buffer may be discarded. This
\r
4635 // behaviour is seen on Windows, when client keeps sending data
\r
4636 // when server decides to close the connection; then when client
\r
4637 // does recv() it gets no data back.
\r
4639 n = pull(NULL, conn, buf, sizeof(buf));
\r
4643 // Now we know that our FIN is ACK-ed, safe to close
\r
4644 closesocket(conn->client.sock);
\r
4647 static void close_connection(struct mg_connection *conn) {
\r
4648 conn->must_close = 1;
\r
4649 if (conn->client.sock != INVALID_SOCKET) {
\r
4650 close_socket_gracefully(conn);
\r
4653 // Must be done AFTER socket is closed
\r
4654 if (conn->ssl != NULL) {
\r
4655 SSL_free(conn->ssl);
\r
4660 void mg_close_connection(struct mg_connection *conn) {
\r
4662 if (conn->client_ssl_ctx != NULL) {
\r
4663 SSL_CTX_free((SSL_CTX *) conn->client_ssl_ctx);
\r
4666 close_connection(conn);
\r
4670 struct mg_connection *mg_connect(const char *host, int port, int use_ssl,
\r
4671 char *ebuf, size_t ebuf_len) {
\r
4672 static struct mg_context fake_ctx;
\r
4673 struct mg_connection *conn = NULL;
\r
4674 struct sockaddr_in sin;
\r
4675 struct hostent *he;
\r
4678 if (host == NULL) {
\r
4679 snprintf(ebuf, ebuf_len, "%s", "NULL host");
\r
4680 } else if (use_ssl && SSLv23_client_method == NULL) {
\r
4681 snprintf(ebuf, ebuf_len, "%s", "SSL is not initialized");
\r
4682 } else if ((he = gethostbyname(host)) == NULL) {
\r
4683 snprintf(ebuf, ebuf_len, "gethostbyname(%s): %s", host, strerror(ERRNO));
\r
4684 } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
4685 snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO));
\r
4687 sin.sin_family = AF_INET;
\r
4688 sin.sin_port = htons((uint16_t) port);
\r
4689 sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
\r
4690 if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
\r
4691 snprintf(ebuf, ebuf_len, "connect(%s:%d): %s",
\r
4692 host, port, strerror(ERRNO));
\r
4693 closesocket(sock);
\r
4694 } else if ((conn = (struct mg_connection *)
\r
4695 calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) {
\r
4696 snprintf(ebuf, ebuf_len, "calloc(): %s", strerror(ERRNO));
\r
4697 closesocket(sock);
\r
4699 } else if (use_ssl && (conn->client_ssl_ctx =
\r
4700 SSL_CTX_new(SSLv23_client_method())) == NULL) {
\r
4701 snprintf(ebuf, ebuf_len, "SSL_CTX_new error");
\r
4702 closesocket(sock);
\r
4707 conn->buf_size = MAX_REQUEST_SIZE;
\r
4708 conn->buf = (char *) (conn + 1);
\r
4709 conn->ctx = &fake_ctx;
\r
4710 conn->client.sock = sock;
\r
4711 conn->client.rsa.sin = sin;
\r
4712 conn->client.is_ssl = use_ssl;
\r
4715 // SSL_CTX_set_verify call is needed to switch off server certificate
\r
4716 // checking, which is off by default in OpenSSL and on in yaSSL.
\r
4717 SSL_CTX_set_verify(conn->client_ssl_ctx, 0, 0);
\r
4718 sslize(conn, conn->client_ssl_ctx, SSL_connect);
\r
4727 static int is_valid_uri(const char *uri) {
\r
4728 // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
\r
4729 // URI can be an asterisk (*) or should start with slash.
\r
4730 return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
\r
4733 static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len) {
\r
4737 reset_per_request_attributes(conn);
\r
4738 conn->request_len = read_request(NULL, conn, conn->buf, conn->buf_size,
\r
4740 assert(conn->request_len < 0 || conn->data_len >= conn->request_len);
\r
4742 if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
\r
4743 snprintf(ebuf, ebuf_len, "%s", "Request Too Large");
\r
4744 } if (conn->request_len <= 0) {
\r
4745 snprintf(ebuf, ebuf_len, "%s", "Client closed connection");
\r
4746 } else if (parse_http_message(conn->buf, conn->buf_size,
\r
4747 &conn->request_info) <= 0) {
\r
4748 snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf);
\r
4750 // Request is valid
\r
4751 if ((cl = get_header(&conn->request_info, "Content-Length")) != NULL) {
\r
4752 conn->content_len = strtoll(cl, NULL, 10);
\r
4753 } else if (!mg_strcasecmp(conn->request_info.request_method, "POST") ||
\r
4754 !mg_strcasecmp(conn->request_info.request_method, "PUT")) {
\r
4755 conn->content_len = -1;
\r
4757 conn->content_len = 0;
\r
4759 conn->birth_time = time(NULL);
\r
4761 return ebuf[0] == '\0';
\r
4764 struct mg_connection *mg_download(const char *host, int port, int use_ssl,
\r
4765 char *ebuf, size_t ebuf_len,
\r
4766 const char *fmt, ...) {
\r
4767 struct mg_connection *conn;
\r
4770 va_start(ap, fmt);
\r
4772 if ((conn = mg_connect(host, port, use_ssl, ebuf, ebuf_len)) == NULL) {
\r
4773 } else if (mg_vprintf(conn, fmt, ap) <= 0) {
\r
4774 snprintf(ebuf, ebuf_len, "%s", "Error sending request");
\r
4776 getreq(conn, ebuf, ebuf_len);
\r
4778 if (ebuf[0] != '\0' && conn != NULL) {
\r
4779 mg_close_connection(conn);
\r
4786 static void process_new_connection(struct mg_connection *conn) {
\r
4787 struct mg_request_info *ri = &conn->request_info;
\r
4788 int keep_alive_enabled, keep_alive, discard_len;
\r
4791 keep_alive_enabled = !strcmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes");
\r
4794 // Important: on new connection, reset the receiving buffer. Credit goes
\r
4796 conn->data_len = 0;
\r
4798 if (!getreq(conn, ebuf, sizeof(ebuf))) {
\r
4799 send_http_error(conn, 500, "Server Error", "%s", ebuf);
\r
4800 } else if (!is_valid_uri(conn->request_info.uri)) {
\r
4801 snprintf(ebuf, sizeof(ebuf), "Invalid URI: [%s]", ri->uri);
\r
4802 send_http_error(conn, 400, "Bad Request", "%s", ebuf);
\r
4803 } else if (strcmp(ri->http_version, "1.0") &&
\r
4804 strcmp(ri->http_version, "1.1")) {
\r
4805 snprintf(ebuf, sizeof(ebuf), "Bad HTTP version: [%s]", ri->http_version);
\r
4806 send_http_error(conn, 505, "Bad HTTP version", "%s", ebuf);
\r
4809 if (ebuf[0] == '\0') {
\r
4810 handle_request(conn);
\r
4811 if (conn->ctx->callbacks.end_request != NULL) {
\r
4812 conn->ctx->callbacks.end_request(conn, conn->status_code);
\r
4816 if (ri->remote_user != NULL) {
\r
4817 free((void *) ri->remote_user);
\r
4820 // NOTE(lsm): order is important here. should_keep_alive() call
\r
4821 // is using parsed request, which will be invalid after memmove's below.
\r
4822 // Therefore, memorize should_keep_alive() result now for later use
\r
4823 // in loop exit condition.
\r
4824 keep_alive = should_keep_alive(conn);
\r
4826 // Discard all buffered data for this request
\r
4827 discard_len = conn->content_len >= 0 &&
\r
4828 conn->request_len + conn->content_len < (int64_t) conn->data_len ?
\r
4829 (int) (conn->request_len + conn->content_len) : conn->data_len;
\r
4830 memmove(conn->buf, conn->buf + discard_len, conn->data_len - discard_len);
\r
4831 conn->data_len -= discard_len;
\r
4832 assert(conn->data_len >= 0);
\r
4833 assert(conn->data_len <= conn->buf_size);
\r
4835 } while (conn->ctx->stop_flag == 0 &&
\r
4836 keep_alive_enabled &&
\r
4837 conn->content_len >= 0 &&
\r
4841 // Worker threads take accepted socket from the queue
\r
4842 static int consume_socket(struct mg_context *ctx, struct socket *sp) {
\r
4843 (void) pthread_mutex_lock(&ctx->mutex);
\r
4844 DEBUG_TRACE(("going idle"));
\r
4846 // If the queue is empty, wait. We're idle at this point.
\r
4847 while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {
\r
4848 pthread_cond_wait(&ctx->sq_full, &ctx->mutex);
\r
4851 // If we're stopping, sq_head may be equal to sq_tail.
\r
4852 if (ctx->sq_head > ctx->sq_tail) {
\r
4853 // Copy socket from the queue and increment tail
\r
4854 *sp = ctx->queue[ctx->sq_tail % ARRAY_SIZE(ctx->queue)];
\r
4856 DEBUG_TRACE(("grabbed socket %d, going busy", sp->sock));
\r
4858 // Wrap pointers if needed
\r
4859 while (ctx->sq_tail > (int) ARRAY_SIZE(ctx->queue)) {
\r
4860 ctx->sq_tail -= ARRAY_SIZE(ctx->queue);
\r
4861 ctx->sq_head -= ARRAY_SIZE(ctx->queue);
\r
4865 (void) pthread_cond_signal(&ctx->sq_empty);
\r
4866 (void) pthread_mutex_unlock(&ctx->mutex);
\r
4868 return !ctx->stop_flag;
\r
4871 static void *worker_thread(void *thread_func_param) {
\r
4872 struct mg_context *ctx = thread_func_param;
\r
4873 struct mg_connection *conn;
\r
4875 conn = (struct mg_connection *) calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE);
\r
4876 if (conn == NULL) {
\r
4877 cry(fc(ctx), "%s", "Cannot create new connection struct, OOM");
\r
4879 conn->buf_size = MAX_REQUEST_SIZE;
\r
4880 conn->buf = (char *) (conn + 1);
\r
4882 conn->request_info.user_data = ctx->user_data;
\r
4884 // Call consume_socket() even when ctx->stop_flag > 0, to let it signal
\r
4885 // sq_empty condvar to wake up the master waiting in produce_socket()
\r
4886 while (consume_socket(ctx, &conn->client)) {
\r
4887 conn->birth_time = time(NULL);
\r
4889 // Fill in IP, port info early so even if SSL setup below fails,
\r
4890 // error handler would have the corresponding info.
\r
4891 // Thanks to Johannes Winkelmann for the patch.
\r
4892 // TODO(lsm): Fix IPv6 case
\r
4893 conn->request_info.remote_port = ntohs(conn->client.rsa.sin.sin_port);
\r
4894 memcpy(&conn->request_info.remote_ip,
\r
4895 &conn->client.rsa.sin.sin_addr.s_addr, 4);
\r
4896 conn->request_info.remote_ip = ntohl(conn->request_info.remote_ip);
\r
4897 conn->request_info.is_ssl = conn->client.is_ssl;
\r
4899 if (!conn->client.is_ssl
\r
4901 || sslize(conn, conn->ctx->ssl_ctx, SSL_accept)
\r
4904 process_new_connection(conn);
\r
4907 close_connection(conn);
\r
4912 // Signal master that we're done with connection and exiting
\r
4913 (void) pthread_mutex_lock(&ctx->mutex);
\r
4914 ctx->num_threads--;
\r
4915 (void) pthread_cond_signal(&ctx->cond);
\r
4916 assert(ctx->num_threads >= 0);
\r
4917 (void) pthread_mutex_unlock(&ctx->mutex);
\r
4919 DEBUG_TRACE(("exiting"));
\r
4923 // Master thread adds accepted socket to a queue
\r
4924 static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
\r
4925 (void) pthread_mutex_lock(&ctx->mutex);
\r
4927 // If the queue is full, wait
\r
4928 while (ctx->stop_flag == 0 &&
\r
4929 ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {
\r
4930 (void) pthread_cond_wait(&ctx->sq_empty, &ctx->mutex);
\r
4933 if (ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue)) {
\r
4934 // Copy socket to the queue and increment head
\r
4935 ctx->queue[ctx->sq_head % ARRAY_SIZE(ctx->queue)] = *sp;
\r
4937 DEBUG_TRACE(("queued socket %d", sp->sock));
\r
4940 (void) pthread_cond_signal(&ctx->sq_full);
\r
4941 (void) pthread_mutex_unlock(&ctx->mutex);
\r
4944 static int set_sock_timeout(SOCKET sock, int milliseconds) {
\r
4946 DWORD t = milliseconds;
\r
4949 t.tv_sec = milliseconds / 1000;
\r
4950 t.tv_usec = (milliseconds * 1000) % 1000000;
\r
4952 return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *) &t, sizeof(t)) ||
\r
4953 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (void *) &t, sizeof(t));
\r
4956 static void accept_new_connection(const struct socket *listener,
\r
4957 struct mg_context *ctx) {
\r
4959 char src_addr[20];
\r
4960 socklen_t len = sizeof(so.rsa);
\r
4963 if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) == INVALID_SOCKET) {
\r
4964 } else if (!check_acl(ctx, ntohl(* (uint32_t *) &so.rsa.sin.sin_addr))) {
\r
4965 sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
\r
4966 cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr);
\r
4967 closesocket(so.sock);
\r
4969 // Put so socket structure into the queue
\r
4970 DEBUG_TRACE(("Accepted socket %d", (int) so.sock));
\r
4971 so.is_ssl = listener->is_ssl;
\r
4972 so.ssl_redir = listener->ssl_redir;
\r
4973 getsockname(so.sock, &so.lsa.sa, &len);
\r
4974 // Set TCP keep-alive. This is needed because if HTTP-level keep-alive
\r
4975 // is enabled, and client resets the connection, server won't get
\r
4976 // TCP FIN or RST and will keep the connection open forever. With TCP
\r
4977 // keep-alive, next keep-alive handshake will figure out that the client
\r
4978 // is down and will close the server end.
\r
4979 // Thanks to Igor Klopov who suggested the patch.
\r
4980 setsockopt(so.sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &on, sizeof(on));
\r
4981 set_sock_timeout(so.sock, atoi(ctx->config[REQUEST_TIMEOUT]));
\r
4982 produce_socket(ctx, &so);
\r
4986 static void *master_thread(void *thread_func_param) {
\r
4987 struct mg_context *ctx = thread_func_param;
\r
4988 struct pollfd *pfd;
\r
4991 // Increase priority of the master thread
\r
4992 #if defined(_WIN32)
\r
4993 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
\r
4996 #if defined(ISSUE_317)
\r
4997 struct sched_param sched_param;
\r
4998 sched_param.sched_priority = sched_get_priority_max(SCHED_RR);
\r
4999 pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
\r
5002 pfd = calloc(ctx->num_listening_sockets, sizeof(pfd[0]));
\r
5003 while (ctx->stop_flag == 0) {
\r
5004 for (i = 0; i < ctx->num_listening_sockets; i++) {
\r
5005 pfd[i].fd = ctx->listening_sockets[i].sock;
\r
5006 pfd[i].events = POLLIN;
\r
5009 if (poll(pfd, ctx->num_listening_sockets, 200) > 0) {
\r
5010 for (i = 0; i < ctx->num_listening_sockets; i++) {
\r
5011 if (ctx->stop_flag == 0 && pfd[i].revents == POLLIN) {
\r
5012 accept_new_connection(&ctx->listening_sockets[i], ctx);
\r
5018 DEBUG_TRACE(("stopping workers"));
\r
5020 // Stop signal received: somebody called mg_stop. Quit.
\r
5021 close_all_listening_sockets(ctx);
\r
5023 // Wakeup workers that are waiting for connections to handle.
\r
5024 pthread_cond_broadcast(&ctx->sq_full);
\r
5026 // Wait until all threads finish
\r
5027 (void) pthread_mutex_lock(&ctx->mutex);
\r
5028 while (ctx->num_threads > 0) {
\r
5029 (void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
\r
5031 (void) pthread_mutex_unlock(&ctx->mutex);
\r
5033 // All threads exited, no sync is needed. Destroy mutex and condvars
\r
5034 (void) pthread_mutex_destroy(&ctx->mutex);
\r
5035 (void) pthread_cond_destroy(&ctx->cond);
\r
5036 (void) pthread_cond_destroy(&ctx->sq_empty);
\r
5037 (void) pthread_cond_destroy(&ctx->sq_full);
\r
5039 #if !defined(NO_SSL)
\r
5040 uninitialize_ssl(ctx);
\r
5042 DEBUG_TRACE(("exiting"));
\r
5044 // Signal mg_stop() that we're done.
\r
5045 // WARNING: This must be the very last thing this
\r
5046 // thread does, as ctx becomes invalid after this line.
\r
5047 ctx->stop_flag = 2;
\r
5051 static void free_context(struct mg_context *ctx) {
\r
5054 // Deallocate config parameters
\r
5055 for (i = 0; i < NUM_OPTIONS; i++) {
\r
5056 if (ctx->config[i] != NULL)
\r
5057 free(ctx->config[i]);
\r
5061 // Deallocate SSL context
\r
5062 if (ctx->ssl_ctx != NULL) {
\r
5063 SSL_CTX_free(ctx->ssl_ctx);
\r
5065 if (ssl_mutexes != NULL) {
\r
5066 free(ssl_mutexes);
\r
5067 ssl_mutexes = NULL;
\r
5071 // Deallocate context itself
\r
5075 void mg_stop(struct mg_context *ctx) {
\r
5076 ctx->stop_flag = 1;
\r
5078 // Wait until mg_fini() stops
\r
5079 while (ctx->stop_flag != 2) {
\r
5080 (void) mg_sleep(10);
\r
5082 free_context(ctx);
\r
5084 #if defined(_WIN32) && !defined(__SYMBIAN32__)
\r
5085 (void) WSACleanup();
\r
5089 struct mg_context *mg_start(const struct mg_callbacks *callbacks,
\r
5091 const char **options) {
\r
5092 struct mg_context *ctx;
\r
5093 const char *name, *value, *default_value;
\r
5096 #if defined(_WIN32) && !defined(__SYMBIAN32__)
\r
5098 WSAStartup(MAKEWORD(2,2), &data);
\r
5099 InitializeCriticalSection(&global_log_file_lock);
\r
5102 // Allocate context and initialize reasonable general case defaults.
\r
5103 // TODO(lsm): do proper error handling here.
\r
5104 if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) {
\r
5107 ctx->callbacks = *callbacks;
\r
5108 ctx->user_data = user_data;
\r
5110 while (options && (name = *options++) != NULL) {
\r
5111 if ((i = get_option_index(name)) == -1) {
\r
5112 cry(fc(ctx), "Invalid option: %s", name);
\r
5113 free_context(ctx);
\r
5115 } else if ((value = *options++) == NULL) {
\r
5116 cry(fc(ctx), "%s: option value cannot be NULL", name);
\r
5117 free_context(ctx);
\r
5120 if (ctx->config[i] != NULL) {
\r
5121 cry(fc(ctx), "warning: %s: duplicate option", name);
\r
5122 free(ctx->config[i]);
\r
5124 ctx->config[i] = mg_strdup(value);
\r
5125 DEBUG_TRACE(("[%s] -> [%s]", name, value));
\r
5128 // Set default value if needed
\r
5129 for (i = 0; config_options[i * ENTRIES_PER_CONFIG_OPTION] != NULL; i++) {
\r
5130 default_value = config_options[i * ENTRIES_PER_CONFIG_OPTION + 2];
\r
5131 if (ctx->config[i] == NULL && default_value != NULL) {
\r
5132 ctx->config[i] = mg_strdup(default_value);
\r
5133 DEBUG_TRACE(("Setting default: [%s] -> [%s]",
\r
5134 config_options[i * ENTRIES_PER_CONFIG_OPTION + 1],
\r
5139 // NOTE(lsm): order is important here. SSL certificates must
\r
5140 // be initialized before listening ports. UID must be set last.
\r
5141 if (!set_gpass_option(ctx) ||
\r
5142 #if !defined(NO_SSL)
\r
5143 !set_ssl_option(ctx) ||
\r
5145 !set_ports_option(ctx) ||
\r
5146 #if !defined(_WIN32)
\r
5147 !set_uid_option(ctx) ||
\r
5149 !set_acl_option(ctx)) {
\r
5150 free_context(ctx);
\r
5154 #if !defined(_WIN32) && !defined(__SYMBIAN32__)
\r
5155 // Ignore SIGPIPE signal, so if browser cancels the request, it
\r
5156 // won't kill the whole process.
\r
5157 (void) signal(SIGPIPE, SIG_IGN);
\r
5158 // Also ignoring SIGCHLD to let the OS to reap zombies properly.
\r
5159 (void) signal(SIGCHLD, SIG_IGN);
\r
5162 (void) pthread_mutex_init(&ctx->mutex, NULL);
\r
5163 (void) pthread_cond_init(&ctx->cond, NULL);
\r
5164 (void) pthread_cond_init(&ctx->sq_empty, NULL);
\r
5165 (void) pthread_cond_init(&ctx->sq_full, NULL);
\r
5167 // Start master (listening) thread
\r
5168 mg_start_thread(master_thread, ctx);
\r
5170 // Start worker threads
\r
5171 for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
\r
5172 if (mg_start_thread(worker_thread, ctx) != 0) {
\r
5173 cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
\r
5175 ctx->num_threads++;
\r