Changeset b5d2d4a in rtems
- Timestamp:
- 12/03/13 23:42:26 (10 years ago)
- Branches:
- 4.11, 5, master
- Children:
- df217b0b
- Parents:
- 0adc8a7
- git-author:
- Nick Withers <nick.withers@…> (12/03/13 23:42:26)
- git-committer:
- Sebastian Huber <sebastian.huber@…> (12/04/13 09:09:16)
- Files:
-
- 1 deleted
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
cpukit/mghttpd/mongoose.c
r0adc8a7 rb5d2d4a 1 // Copyright (c) 2004-201 1Sergey Lyubka1 // Copyright (c) 2004-2013 Sergey Lyubka 2 2 // 3 3 // Permission is hereby granted, free of charge, to any person obtaining a copy … … 20 20 21 21 #if HAVE_CONFIG_H 22 22 #include "config.h" 23 23 #endif 24 24 … … 29 29 30 30 #if defined(_WIN32) 31 #if !defined(_CRT_SECURE_NO_WARNINGS) 31 32 #define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005 33 #endif 32 34 #else 35 #ifdef __linux__ 33 36 #define _XOPEN_SOURCE 600 // For flockfile() on Linux 37 #endif 34 38 #define _LARGEFILE_SOURCE // Enable 64-bit file offsets 35 39 #define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++ 36 40 #define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX 41 #endif 42 43 #if defined (_MSC_VER) 44 // conditional expression is constant: introduced by FD_SET(..) 45 #pragma warning (disable : 4127) 46 // non-constant aggregate initializer: issued due to missing C99 support 47 #pragma warning (disable : 4204) 48 #endif 49 50 // Disable WIN32_LEAN_AND_MEAN. 51 // This makes windows.h always include winsock2.h 52 #ifdef WIN32_LEAN_AND_MEAN 53 #undef WIN32_LEAN_AND_MEAN 37 54 #endif 38 55 … … 62 79 63 80 #if defined(_WIN32) && !defined(__SYMBIAN32__) // Windows specific 81 #undef _WIN32_WINNT 64 82 #define _WIN32_WINNT 0x0400 // To make it link in VS2005 65 83 #include <windows.h> … … 74 92 #include <io.h> 75 93 #else // _WIN32_WCE 76 #include <winsock2.h>77 #include <ws2tcpip.h>78 94 #define NO_CGI // WinCE has no pipes 79 95 80 96 typedef long off_t; 81 #define BUFSIZ 409682 97 83 98 #define errno GetLastError() … … 98 113 #define STRX(x) #x 99 114 #define STR(x) STRX(x) 100 #define __func__ "line" STR(__LINE__)101 #define strtoull(x, y, z) strtoul(x, y, z)102 #define strtoll(x, y, z) strtol(x, y, z)115 #define __func__ __FILE__ ":" STR(__LINE__) 116 #define strtoull(x, y, z) (unsigned __int64) _atoi64(x) 117 #define strtoll(x, y, z) _atoi64(x) 103 118 #else 104 119 #define __func__ __FUNCTION__ … … 111 126 #define SSL_LIB "ssleay32.dll" 112 127 #define CRYPTO_LIB "libeay32.dll" 113 #define DIRSEP '\\'114 #define IS_DIRSEP_CHAR(c) ((c) == '/' || (c) == '\\')115 128 #define O_NONBLOCK 0 116 129 #if !defined(EWOULDBLOCK) … … 126 139 #define mg_sleep(x) Sleep(x) 127 140 128 #define pipe(x) _pipe(x, BUFSIZ, _O_BINARY) 141 #define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY) 142 #ifndef popen 129 143 #define popen(x, y) _popen(x, y) 144 #endif 145 #ifndef pclose 130 146 #define pclose(x) _pclose(x) 147 #endif 131 148 #define close(x) _close(x) 132 149 #define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y)) 133 150 #define RTLD_LAZY 0 134 #define fseeko(x, y, z) fseek((x), (y), (z))151 #define fseeko(x, y, z) _lseeki64(_fileno(x), (y), (z)) 135 152 #define fdopen(x, y) _fdopen((x), (y)) 136 153 #define write(x, y, z) _write((x), (y), (unsigned) z) … … 138 155 #define flockfile(x) EnterCriticalSection(&global_log_file_lock) 139 156 #define funlockfile(x) LeaveCriticalSection(&global_log_file_lock) 157 #define sleep(x) Sleep((x) * 1000) 158 #define rmdir(x) _rmdir(x) 159 160 #if !defined(va_copy) 161 #define va_copy(x, y) x = y 162 #endif // !va_copy MINGW #defines va_copy 140 163 141 164 #if !defined(fileno) … … 148 171 #define pid_t HANDLE // MINGW typedefs pid_t to int. Using #define here. 149 172 150 struct timespec {151 long tv_nsec;152 long tv_sec;153 };154 155 173 static int pthread_mutex_lock(pthread_mutex_t *); 156 174 static int pthread_mutex_unlock(pthread_mutex_t *); 157 static FILE *mg_fopen(const char *path, const char *mode); 175 static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len); 176 struct file; 177 static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p); 158 178 159 179 #if defined(HAVE_STDINT) … … 178 198 } DIR; 179 199 200 201 // Mark required libraries 202 #ifdef _MSC_VER 203 #pragma comment(lib, "Ws2_32.lib") 204 #endif 205 180 206 #else // UNIX specific 181 207 #include <sys/wait.h> 182 208 #include <sys/socket.h> 183 209 #include <sys/select.h> 210 #ifdef HAVE_POLL 211 #include <sys/poll.h> 212 #endif 184 213 #include <netinet/in.h> 185 214 #include <arpa/inet.h> … … 207 236 #endif 208 237 #endif 209 #define DIRSEP '/'210 #define IS_DIRSEP_CHAR(c) ((c) == '/')211 238 #ifndef O_BINARY 212 239 #define O_BINARY 0 213 240 #endif // O_BINARY 214 241 #define closesocket(a) close(a) 215 #define mg_fopen(x, y) fopen(x, y)216 242 #define mg_mkdir(x, y) mkdir(x, y) 217 243 #define mg_remove(x) remove(x) 218 #define mg_rename(x, y) rename(x, y)219 244 #define mg_sleep(x) usleep((x) * 1000) 220 245 #define ERRNO errno … … 226 251 #endif // End of Windows and UNIX specific includes 227 252 253 #ifndef HAVE_POLL 254 struct pollfd { 255 SOCKET fd; 256 short events; 257 short revents; 258 }; 259 #define POLLIN 1 260 #endif 261 228 262 #include "mongoose.h" 229 263 230 #define MONGOOSE_VERSION "3. 2"264 #define MONGOOSE_VERSION "3.9" 231 265 #define PASSWORDS_FILE_NAME ".htpasswd" 232 266 #define CGI_ENVIRONMENT_SIZE 4096 233 267 #define MAX_CGI_ENVIR_VARS 64 268 #define MG_BUF_LEN 8192 269 #define MAX_REQUEST_SIZE 16384 234 270 #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) 235 271 … … 241 277 #endif // _WIN32 242 278 279 #ifdef DEBUG_TRACE 280 #undef DEBUG_TRACE 281 #define DEBUG_TRACE(x) 282 #else 243 283 #if defined(DEBUG) 244 284 #define DEBUG_TRACE(x) do { \ … … 255 295 #define DEBUG_TRACE(x) 256 296 #endif // DEBUG 297 #endif // DEBUG_TRACE 257 298 258 299 // Darwin prior to 7.0 and Win32 do not have socklen_t … … 260 301 typedef int socklen_t; 261 302 #endif // NO_SOCKLEN_T 303 #define _DARWIN_UNLIMITED_SELECT 304 305 #define IP_ADDR_STR_LEN 50 // IPv6 hex string is 46 chars 262 306 263 307 #if !defined(MSG_NOSIGNAL) … … 265 309 #endif 266 310 267 typedef void * (*mg_thread_func_t)(void *); 311 #if !defined(SOMAXCONN) 312 #define SOMAXCONN 100 313 #endif 314 315 #if !defined(PATH_MAX) 316 #define PATH_MAX 4096 317 #endif 318 319 // Size of the accepted socket queue 320 #if !defined(MGSQLEN) 321 #define MGSQLEN 20 322 #endif 268 323 269 324 static const char *http_500_error = "Internal Server Error"; 270 325 271 // Snatched from OpenSSL includes. I put the prototypes here to be independent 272 // from the OpenSSL source installation. Having this, mongoose + SSL can be 273 // built on any system with binary SSL libraries installed. 326 #if defined(NO_SSL_DL) 327 #include <openssl/ssl.h> 328 #include <openssl/err.h> 329 #else 330 // SSL loaded dynamically from DLL. 331 // I put the prototypes here to be independent from OpenSSL source installation. 274 332 typedef struct ssl_st SSL; 275 333 typedef struct ssl_method_st SSL_METHOD; 276 334 typedef struct ssl_ctx_st SSL_CTX; 277 335 278 #define SSL_ERROR_WANT_READ 2279 #define SSL_ERROR_WANT_WRITE 3280 #define SSL_FILETYPE_PEM 1281 #define CRYPTO_LOCK 1282 283 #if defined(NO_SSL_DL)284 extern void SSL_free(SSL *);285 extern int SSL_accept(SSL *);286 extern int SSL_connect(SSL *);287 extern int SSL_read(SSL *, void *, int);288 extern int SSL_write(SSL *, const void *, int);289 extern int SSL_get_error(const SSL *, int);290 extern int SSL_set_fd(SSL *, int);291 extern SSL *SSL_new(SSL_CTX *);292 extern SSL_CTX *SSL_CTX_new(SSL_METHOD *);293 extern SSL_METHOD *SSLv23_server_method(void);294 extern int SSL_library_init(void);295 extern void SSL_load_error_strings(void);296 extern int SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int);297 extern int SSL_CTX_use_certificate_file(SSL_CTX *, const char *, int);298 extern int SSL_CTX_use_certificate_chain_file(SSL_CTX *, const char *);299 extern void SSL_CTX_set_default_passwd_cb(SSL_CTX *, mg_callback_t);300 extern void SSL_CTX_free(SSL_CTX *);301 extern unsigned long ERR_get_error(void);302 extern char *ERR_error_string(unsigned long, char *);303 extern int CRYPTO_num_locks(void);304 extern void CRYPTO_set_locking_callback(void (*)(int, int, const char *, int));305 extern void CRYPTO_set_id_callback(unsigned long (*)(void));306 #else307 // Dynamically loaded SSL functionality308 336 struct ssl_func { 309 337 const char *name; // SSL function name … … 332 360 #define SSL_CTX_use_certificate_chain_file \ 333 361 (* (int (*)(SSL_CTX *, const char *)) ssl_sw[16].ptr) 362 #define SSLv23_client_method (* (SSL_METHOD * (*)(void)) ssl_sw[17].ptr) 363 #define SSL_pending (* (int (*)(SSL *)) ssl_sw[18].ptr) 364 #define SSL_CTX_set_verify (* (void (*)(SSL_CTX *, int, int)) ssl_sw[19].ptr) 365 #define SSL_shutdown (* (int (*)(SSL *)) ssl_sw[20].ptr) 334 366 335 367 #define CRYPTO_num_locks (* (int (*)(void)) crypto_sw[0].ptr) … … 363 395 {"SSL_load_error_strings", NULL}, 364 396 {"SSL_CTX_use_certificate_chain_file", NULL}, 397 {"SSLv23_client_method", NULL}, 398 {"SSL_pending", NULL}, 399 {"SSL_CTX_set_verify", NULL}, 400 {"SSL_shutdown", NULL}, 365 401 {NULL, NULL} 366 402 }; … … 400 436 }; 401 437 402 // Structure used by mg_stat() function. Uses 64 bit file length. 403 struct mgstat { 404 int is_directory; // Directory marker 405 int64_t size; // File size 406 time_t mtime; // Modification time 438 struct file { 439 int is_directory; 440 time_t modification_time; 441 int64_t size; 442 FILE *fp; 443 const char *membuf; // Non-NULL if file data is in memory 444 // set to 1 if the content is gzipped 445 // in which case we need a content-encoding: gzip header 446 int gzipped; 407 447 }; 448 #define STRUCT_FILE_INITIALIZER {0, 0, 0, NULL, NULL, 0} 408 449 409 450 // Describes listening socket, or socket which was accept()-ed by the master 410 451 // thread and queued for future handling by the worker thread. 411 452 struct socket { 412 struct socket *next; // Linkage413 453 SOCKET sock; // Listening socket 414 454 union usa lsa; // Local socket address 415 455 union usa rsa; // Remote socket address 416 int is_ssl; // Is socket SSL-ed 456 unsigned is_ssl:1; // Is port SSL-ed 457 unsigned ssl_redir:1; // Is port supposed to redirect everything to SSL port 417 458 }; 418 459 460 // NOTE(lsm): this enum shoulds be in sync with the config_options below. 419 461 enum { 420 462 CGI_EXTENSIONS, CGI_ENVIRONMENT, PUT_DELETE_PASSWORDS_FILE, CGI_INTERPRETER, 421 PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS, ACCESS_LOG_FILE, 422 SSL_CHAIN_FILE, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE, 423 GLOBAL_PASSWORDS_FILE, INDEX_FILES, 424 ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST, MAX_REQUEST_SIZE, 425 EXTRA_MIME_TYPES, LISTENING_PORTS, 426 DOCUMENT_ROOT, SSL_CERTIFICATE, NUM_THREADS, RUN_AS_USER, REWRITE, 463 PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS, THROTTLE, 464 ACCESS_LOG_FILE, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE, 465 GLOBAL_PASSWORDS_FILE, INDEX_FILES, ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST, 466 EXTRA_MIME_TYPES, LISTENING_PORTS, DOCUMENT_ROOT, SSL_CERTIFICATE, 467 NUM_THREADS, RUN_AS_USER, REWRITE, HIDE_FILES, REQUEST_TIMEOUT, 427 468 THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_POLICY, 428 469 NUM_OPTIONS … … 430 471 431 472 static const char *config_options[] = { 432 "C", "cgi_pattern", "**.cgi$|**.pl$|**.php$", 433 "E", "cgi_environment", NULL, 434 "G", "put_delete_passwords_file", NULL, 435 "I", "cgi_interpreter", NULL, 436 "P", "protect_uri", NULL, 437 "R", "authentication_domain", "mydomain.com", 438 "S", "ssi_pattern", "**.shtml$|**.shtm$", 439 "a", "access_log_file", NULL, 440 "c", "ssl_chain_file", NULL, 441 "d", "enable_directory_listing", "yes", 442 "e", "error_log_file", NULL, 443 "g", "global_passwords_file", NULL, 444 "i", "index_files", "index.html,index.htm,index.cgi,index.shtml,index.php", 445 "k", "enable_keep_alive", "no", 446 "l", "access_control_list", NULL, 447 "M", "max_request_size", "16384", 448 "m", "extra_mime_types", NULL, 449 "p", "listening_ports", "8080", 450 "r", "document_root", ".", 451 "s", "ssl_certificate", NULL, 452 "t", "num_threads", "10", 453 "u", "run_as_user", NULL, 454 "w", "url_rewrite_patterns", NULL, 455 "x", "thread_stack_size", NULL, 456 "y", "thread_priority", NULL, 457 "z", "thread_policy", NULL, 473 "cgi_pattern", "**.cgi$|**.pl$|**.php$", 474 "cgi_environment", NULL, 475 "put_delete_auth_file", NULL, 476 "cgi_interpreter", NULL, 477 "protect_uri", NULL, 478 "authentication_domain", "mydomain.com", 479 "ssi_pattern", "**.shtml$|**.shtm$", 480 "throttle", NULL, 481 "access_log_file", NULL, 482 "enable_directory_listing", "yes", 483 "error_log_file", NULL, 484 "global_auth_file", NULL, 485 "index_files", 486 "index.html,index.htm,index.cgi,index.shtml,index.php,index.lp", 487 "enable_keep_alive", "no", 488 "access_control_list", NULL, 489 "extra_mime_types", NULL, 490 "listening_ports", "8080", 491 "document_root", NULL, 492 "ssl_certificate", NULL, 493 "num_threads", "50", 494 "run_as_user", NULL, 495 "url_rewrite_patterns", NULL, 496 "hide_files_patterns", NULL, 497 "request_timeout_ms", "30000", 498 "thread_stack_size", NULL, 499 "thread_priority", NULL, 500 "thread_policy", NULL, 458 501 NULL 459 502 }; 460 #define ENTRIES_PER_CONFIG_OPTION 3461 503 462 504 struct mg_context { 463 volatile int stop_flag; // Should we stop event loop464 SSL_CTX *ssl_ctx; // SSL context465 char *config[NUM_OPTIONS]; // Mongoose configuration parameters466 mg_callback_t user_callback; // User-defined callback function467 void *user_data; // User-defined data505 volatile int stop_flag; // Should we stop event loop 506 SSL_CTX *ssl_ctx; // SSL context 507 char *config[NUM_OPTIONS]; // Mongoose configuration parameters 508 struct mg_callbacks callbacks; // User-defined callback function 509 void *user_data; // User-defined data 468 510 469 511 struct socket *listening_sockets; 512 int num_listening_sockets; 470 513 471 514 volatile int num_threads; // Number of threads … … 473 516 pthread_cond_t cond; // Condvar for tracking workers terminations 474 517 475 struct socket queue[ 20]; // Accepted sockets518 struct socket queue[MGSQLEN]; // Accepted sockets 476 519 volatile int sq_head; // Head of the socket queue 477 520 volatile int sq_tail; // Tail of the socket queue 478 pthread_cond_t sq_full; // Si ngaled when socket is produced521 pthread_cond_t sq_full; // Signaled when socket is produced 479 522 pthread_cond_t sq_empty; // Signaled when socket is consumed 480 523 }; … … 484 527 struct mg_context *ctx; 485 528 SSL *ssl; // SSL descriptor 529 SSL_CTX *client_ssl_ctx; // SSL context for client connections 486 530 struct socket client; // Connected client 487 time_t birth_time; // Time connection was accepted531 time_t birth_time; // Time when request was received 488 532 int64_t num_bytes_sent; // Total bytes sent to client 489 533 int64_t content_len; // Content-Length header value 490 int64_t consumed_content; // How many bytes of content is alreadyread534 int64_t consumed_content; // How many bytes of content have been read 491 535 char *buf; // Buffer for received data 492 536 char *path_info; // PATH_INFO part of the URL … … 495 539 int request_len; // Size of the request + headers in a buffer 496 540 int data_len; // Total size of data in a buffer 541 int status_code; // HTTP reply status code, e.g. 200 542 int throttle; // Throttling, bytes/sec. <= 0 means no throttle 543 time_t last_throttle_time; // Last time throttled data was sent 544 int64_t last_throttle_bytes;// Bytes sent this second 545 }; 546 547 // Directory entry 548 struct de { 549 struct mg_connection *conn; 550 char *file_name; 551 struct file file; 497 552 }; 498 553 … … 501 556 } 502 557 503 static void *call_user(struct mg_connection *conn, enum mg_event event) { 504 conn->request_info.user_data = conn->ctx->user_data; 505 return conn->ctx->user_callback == NULL ? NULL : 506 conn->ctx->user_callback(event, conn, &conn->request_info); 558 static int is_file_in_memory(struct mg_connection *conn, const char *path, 559 struct file *filep) { 560 size_t size = 0; 561 if ((filep->membuf = conn->ctx->callbacks.open_file == NULL ? NULL : 562 conn->ctx->callbacks.open_file(conn, path, &size)) != NULL) { 563 // NOTE: override filep->size only on success. Otherwise, it might break 564 // constructs like if (!mg_stat() || !mg_fopen()) ... 565 filep->size = size; 566 } 567 return filep->membuf != NULL; 568 } 569 570 static int is_file_opened(const struct file *filep) { 571 return filep->membuf != NULL || filep->fp != NULL; 572 } 573 574 static int mg_fopen(struct mg_connection *conn, const char *path, 575 const char *mode, struct file *filep) { 576 if (!is_file_in_memory(conn, path, filep)) { 577 #ifdef _WIN32 578 wchar_t wbuf[PATH_MAX], wmode[20]; 579 to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); 580 MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode)); 581 filep->fp = _wfopen(wbuf, wmode); 582 #else 583 filep->fp = fopen(path, mode); 584 #endif 585 } 586 587 return is_file_opened(filep); 588 } 589 590 static void mg_fclose(struct file *filep) { 591 if (filep != NULL && filep->fp != NULL) { 592 fclose(filep->fp); 593 } 507 594 } 508 595 … … 510 597 int i; 511 598 512 for (i = 0; config_options[i] != NULL; i += ENTRIES_PER_CONFIG_OPTION) { 513 if (strcmp(config_options[i], name) == 0 || 514 strcmp(config_options[i + 1], name) == 0) { 515 return i / ENTRIES_PER_CONFIG_OPTION; 599 for (i = 0; config_options[i * 2] != NULL; i++) { 600 if (strcmp(config_options[i * 2], name) == 0) { 601 return i; 516 602 } 517 603 } … … 545 631 } 546 632 633 static void cry(struct mg_connection *conn, 634 PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); 635 547 636 // Print error message to the opened error log stream. 548 637 static void cry(struct mg_connection *conn, const char *fmt, ...) { 549 char buf[ BUFSIZ], src_addr[20];638 char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN]; 550 639 va_list ap; 551 640 FILE *fp; … … 559 648 // I suppose this is fine, since function cannot disappear in the 560 649 // same way string option can. 561 conn->request_info.log_message = buf;562 if (call_user(conn, MG_EVENT_LOG) == NULL) {563 fp = conn->ctx ->config[ERROR_LOG_FILE] == NULL ? NULL :564 mg_fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");650 if (conn->ctx->callbacks.log_message == NULL || 651 conn->ctx->callbacks.log_message(conn, buf) == 0) { 652 fp = conn->ctx == NULL || conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL : 653 fopen(conn->ctx->config[ERROR_LOG_FILE], "a+"); 565 654 566 655 if (fp != NULL) { … … 577 666 } 578 667 579 (void)fprintf(fp, "%s", buf);668 fprintf(fp, "%s", buf); 580 669 fputc('\n', fp); 581 670 funlockfile(fp); 582 if (fp != stderr) { 583 fclose(fp); 584 } 585 } 586 } 587 conn->request_info.log_message = NULL; 671 fclose(fp); 672 } 673 } 588 674 } 589 675 … … 600 686 } 601 687 688 struct mg_request_info *mg_get_request_info(struct mg_connection *conn) { 689 return &conn->request_info; 690 } 691 602 692 static void mg_strlcpy(register char *dst, register const char *src, size_t n) { 603 693 for (; *src != '\0' && n > 1; n--) { … … 646 736 } 647 737 648 // Like snprintf(), but never returns negative value, or the value 738 static const char *mg_strcasestr(const char *big_str, const char *small_str) { 739 int i, big_len = strlen(big_str), small_len = strlen(small_str); 740 741 for (i = 0; i <= big_len - small_len; i++) { 742 if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) { 743 return big_str + i; 744 } 745 } 746 747 return NULL; 748 } 749 750 // Like snprintf(), but never returns negative value, or a value 649 751 // that is larger than a supplied buffer. 650 752 // Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability … … 673 775 674 776 static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen, 777 PRINTF_FORMAT_STRING(const char *fmt), ...) 778 PRINTF_ARGS(4, 5); 779 780 static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen, 675 781 const char *fmt, ...) { 676 782 va_list ap; … … 685 791 686 792 // Skip the characters until one of the delimiters characters found. 687 // 0-terminate resulting word. Skip the delimiter and following whitespaces if any.793 // 0-terminate resulting word. Skip the delimiter and following whitespaces. 688 794 // Advance pointer to buffer to the next word. Return found 0-terminated word. 689 795 // Delimiters can be quoted with quotechar. 690 static char *skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar) { 796 static char *skip_quoted(char **buf, const char *delimiters, 797 const char *whitespace, char quotechar) { 691 798 char *p, *begin_word, *end_word, *end_whitespace; 692 799 … … 700 807 // If there is anything beyond end_word, copy it 701 808 if (*end_word == '\0') { 809 702 810 *p = '\0'; 703 811 break; … … 752 860 } 753 861 754 // A helper function for traversing comma separated list of values.755 // It returns a list pointer shifted to the next value, o fNULL if the end862 // A helper function for traversing a comma separated list of values. 863 // It returns a list pointer shifted to the next value, or NULL if the end 756 864 // of the list found. 757 865 // Value is stored in val vector. If value has form "x=y", then eq_val … … 791 899 } 792 900 901 // Perform case-insensitive match of string against pattern 793 902 static int match_prefix(const char *pattern, int pattern_len, const char *str) { 794 903 const char *or_str; … … 812 921 if (pattern[i] == '*') { 813 922 i++; 814 len = strlen(str + j);923 len = (int) strlen(str + j); 815 924 } else { 816 len = strcspn(str + j, "/");925 len = (int) strcspn(str + j, "/"); 817 926 } 818 927 if (i == pattern_len) { … … 823 932 } while (res == -1 && len-- > 0); 824 933 return res == -1 ? -1 : j + res + len; 825 } else if ( pattern[i] != str[j]) {934 } else if (lowercase(&pattern[i]) != lowercase(&str[j])) { 826 935 return -1; 827 936 } … … 837 946 const char *header = mg_get_header(conn, "Connection"); 838 947 if (conn->must_close || 839 conn-> request_info.status_code == 401 ||948 conn->status_code == 401 || 840 949 mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 || 841 950 (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) || … … 850 959 } 851 960 961 static void send_http_error(struct mg_connection *, int, const char *, 962 PRINTF_FORMAT_STRING(const char *fmt), ...) 963 PRINTF_ARGS(4, 5); 964 965 852 966 static void send_http_error(struct mg_connection *conn, int status, 853 967 const char *reason, const char *fmt, ...) { 854 char buf[ BUFSIZ];968 char buf[MG_BUF_LEN]; 855 969 va_list ap; 856 int len ;857 858 conn-> request_info.status_code = status;859 860 if (call_user(conn, MG_HTTP_ERROR) == NULL) {970 int len = 0; 971 972 conn->status_code = status; 973 if (conn->ctx->callbacks.http_error == NULL || 974 conn->ctx->callbacks.http_error(conn, status)) { 861 975 buf[0] = '\0'; 862 len = 0;863 976 864 977 // Errors 1xx, 204 and 304 MUST NOT send a body 865 978 if (status > 199 && status != 204 && status != 304) { 866 979 len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason); 867 cry(conn, "%s", buf);868 980 buf[len++] = '\n'; 869 981 … … 875 987 876 988 mg_printf(conn, "HTTP/1.1 %d %s\r\n" 877 "Content-Type: text/plain\r\n"878 989 "Content-Length: %d\r\n" 879 990 "Connection: %s\r\n\r\n", status, reason, len, … … 885 996 #if defined(_WIN32) && !defined(__SYMBIAN32__) 886 997 static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) { 887 unused = NULL;998 (void) unused; 888 999 *mutex = CreateMutex(NULL, FALSE, NULL); 889 1000 return *mutex == NULL ? -1 : 0; … … 903 1014 904 1015 static int pthread_cond_init(pthread_cond_t *cv, const void *unused) { 905 unused = NULL;1016 (void) unused; 906 1017 cv->signal = CreateEvent(NULL, FALSE, FALSE, NULL); 907 1018 cv->broadcast = CreateEvent(NULL, TRUE, FALSE, NULL); … … 948 1059 // wbuf and wbuf_len is a target buffer and its length. 949 1060 static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) { 950 char buf[PATH_MAX], buf2[PATH_MAX] , *p;1061 char buf[PATH_MAX], buf2[PATH_MAX]; 951 1062 952 1063 mg_strlcpy(buf, path, sizeof(buf)); 953 1064 change_slashes_to_backslashes(buf); 954 1065 955 // Point p to the end of the file name 956 p = buf + strlen(buf) - 1; 957 958 // Trim trailing backslash character 959 while (p > buf && *p == '\\' && p[-1] != ':') { 960 *p-- = '\0'; 961 } 962 963 // Protect from CGI code disclosure. 964 // This is very nasty hole. Windows happily opens files with 965 // some garbage in the end of file name. So fopen("a.cgi ", "r") 966 // actually opens "a.cgi", and does not return an error! 967 if (*p == 0x20 || // No space at the end 968 (*p == 0x2e && p > buf) || // No '.' but allow '.' as full path 969 *p == 0x2b || // No '+' 970 (*p & ~0x7f)) { // And generally no non-ascii chars 971 (void) fprintf(stderr, "Rejecting suspicious path: [%s]", buf); 1066 // Convert to Unicode and back. If doubly-converted string does not 1067 // match the original, something is fishy, reject. 1068 memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); 1069 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); 1070 WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), 1071 NULL, NULL); 1072 if (strcmp(buf, buf2) != 0) { 972 1073 wbuf[0] = L'\0'; 973 } else {974 // Convert to Unicode and back. If doubly-converted string does not975 // match the original, something is fishy, reject.976 memset(wbuf, 0, wbuf_len * sizeof(wchar_t));977 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);978 WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),979 NULL, NULL);980 if (strcmp(buf, buf2) != 0) {981 wbuf[0] = L'\0';982 }983 1074 } 984 1075 } … … 1040 1131 #endif 1041 1132 1042 static int mg_rename(const char* oldname, const char* newname) { 1043 wchar_t woldbuf[PATH_MAX]; 1044 wchar_t wnewbuf[PATH_MAX]; 1045 1046 to_unicode(oldname, woldbuf, ARRAY_SIZE(woldbuf)); 1047 to_unicode(newname, wnewbuf, ARRAY_SIZE(wnewbuf)); 1048 1049 return MoveFileW(woldbuf, wnewbuf) ? 0 : -1; 1050 } 1051 1052 1053 static FILE *mg_fopen(const char *path, const char *mode) { 1054 wchar_t wbuf[PATH_MAX], wmode[20]; 1055 1056 to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); 1057 MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode)); 1058 1059 return _wfopen(wbuf, wmode); 1060 } 1061 1062 static int mg_stat(const char *path, struct mgstat *stp) { 1063 int ok = -1; // Error 1133 // Windows happily opens files with some garbage at the end of file name. 1134 // For example, fopen("a.cgi ", "r") on Windows successfully opens 1135 // "a.cgi", despite one would expect an error back. 1136 // This function returns non-0 if path ends with some garbage. 1137 static int path_cannot_disclose_cgi(const char *path) { 1138 static const char *allowed_last_characters = "_-"; 1139 int last = path[strlen(path) - 1]; 1140 return isalnum(last) || strchr(allowed_last_characters, last) != NULL; 1141 } 1142 1143 static int mg_stat(struct mg_connection *conn, const char *path, 1144 struct file *filep) { 1064 1145 wchar_t wbuf[PATH_MAX]; 1065 1146 WIN32_FILE_ATTRIBUTE_DATA info; 1066 1147 1067 to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); 1068 1069 if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) { 1070 stp->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh); 1071 stp->mtime = SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime, 1072 info.ftLastWriteTime.dwHighDateTime); 1073 stp->is_directory = 1074 info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; 1075 ok = 0; // Success 1076 } 1077 1078 return ok; 1148 if (!is_file_in_memory(conn, path, filep)) { 1149 to_unicode(path, wbuf, ARRAY_SIZE(wbuf)); 1150 if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) { 1151 filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh); 1152 filep->modification_time = SYS2UNIX_TIME( 1153 info.ftLastWriteTime.dwLowDateTime, 1154 info.ftLastWriteTime.dwHighDateTime); 1155 filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; 1156 // If file name is fishy, reset the file structure and return error. 1157 // Note it is important to reset, not just return the error, cause 1158 // functions like is_file_opened() check the struct. 1159 if (!filep->is_directory && !path_cannot_disclose_cgi(path)) { 1160 memset(filep, 0, sizeof(*filep)); 1161 } 1162 } 1163 } 1164 1165 return filep->membuf != NULL || filep->modification_time != 0; 1079 1166 } 1080 1167 … … 1089 1176 wchar_t wbuf[PATH_MAX]; 1090 1177 1091 mode = 0; // Unused1178 (void) mode; 1092 1179 mg_strlcpy(buf, path, sizeof(buf)); 1093 1180 change_slashes_to_backslashes(buf); 1094 1181 1095 (void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));1182 (void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, ARRAY_SIZE(wbuf)); 1096 1183 1097 1184 return CreateDirectoryW(wbuf, NULL) ? 0 : -1; … … 1166 1253 } 1167 1254 1168 #define set_close_on_exec(fd) // No FD_CLOEXEC on Windows 1169 1170 static int start_thread(struct mg_context *ctx, mg_thread_func_t f, void *p) { 1171 return _beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0; 1255 static void set_close_on_exec(SOCKET sock) { 1256 (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); 1257 } 1258 1259 int mg_start_thread(mg_thread_func_t f, void *p) { 1260 return (long)_beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0; 1172 1261 } 1173 1262 1174 1263 static HANDLE dlopen(const char *dll_name, int flags) { 1175 1264 wchar_t wbuf[PATH_MAX]; 1176 flags = 0; // Unused1265 (void) flags; 1177 1266 to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf)); 1178 1267 return LoadLibraryW(wbuf); … … 1187 1276 } 1188 1277 1278 static void trim_trailing_whitespaces(char *s) { 1279 char *e = s + strlen(s) - 1; 1280 while (e > s && isspace(* (unsigned char *) e)) { 1281 *e-- = '\0'; 1282 } 1283 } 1284 1189 1285 static pid_t spawn_process(struct mg_connection *conn, const char *prog, 1190 char *envblk, char *envp[], int fd _stdin,1191 int fd _stdout, const char *dir) {1286 char *envblk, char *envp[], int fdin, 1287 int fdout, const char *dir) { 1192 1288 HANDLE me; 1193 char *p, *interp, cmdline[PATH_MAX], buf[PATH_MAX]; 1194 FILE *fp; 1195 STARTUPINFOA si = { sizeof(si) }; 1289 char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX], 1290 cmdline[PATH_MAX], buf[PATH_MAX]; 1291 struct file file = STRUCT_FILE_INITIALIZER; 1292 STARTUPINFOA si; 1196 1293 PROCESS_INFORMATION pi = { 0 }; 1197 1294 1198 envp = NULL; // Unused 1295 (void) envp; 1296 1297 memset(&si, 0, sizeof(si)); 1298 si.cb = sizeof(si); 1199 1299 1200 1300 // TODO(lsm): redirect CGI errors to the error log file … … 1203 1303 1204 1304 me = GetCurrentProcess(); 1205 (void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdin), me,1206 &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);1207 (void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdout), me,1208 &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);1305 DuplicateHandle(me, (HANDLE) _get_osfhandle(fdin), me, 1306 &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS); 1307 DuplicateHandle(me, (HANDLE) _get_osfhandle(fdout), me, 1308 &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS); 1209 1309 1210 1310 // If CGI file is a script, try to read the interpreter line 1211 1311 interp = conn->ctx->config[CGI_INTERPRETER]; 1212 1312 if (interp == NULL) { 1213 buf[2] = '\0'; 1214 mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%c%s", dir, DIRSEP, prog); 1215 if ((fp = fopen(cmdline, "r")) != NULL) { 1216 (void) fgets(buf, sizeof(buf), fp); 1217 if (buf[0] != '#' || buf[1] != '!') { 1218 // First line does not start with "#!". Do not set interpreter. 1219 buf[2] = '\0'; 1220 } else { 1221 // Trim whitespaces in interpreter name 1222 for (p = &buf[strlen(buf) - 1]; p > buf && isspace(*p); p--) { 1223 *p = '\0'; 1224 } 1225 } 1226 (void) fclose(fp); 1313 buf[0] = buf[1] = '\0'; 1314 1315 // Read the first line of the script into the buffer 1316 snprintf(cmdline, sizeof(cmdline), "%s%c%s", dir, '/', prog); 1317 if (mg_fopen(conn, cmdline, "r", &file)) { 1318 p = (char *) file.membuf; 1319 mg_fgets(buf, sizeof(buf), &file, &p); 1320 mg_fclose(&file); 1321 buf[sizeof(buf) - 1] = '\0'; 1322 } 1323 1324 if (buf[0] == '#' && buf[1] == '!') { 1325 trim_trailing_whitespaces(buf + 2); 1326 } else { 1327 buf[2] = '\0'; 1227 1328 } 1228 1329 interp = buf + 2; 1229 1330 } 1230 1331 1231 (void) mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%s%s%c%s", 1232 interp, interp[0] == '\0' ? "" : " ", dir, DIRSEP, prog); 1332 if (interp[0] != '\0') { 1333 GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL); 1334 interp = full_interp; 1335 } 1336 GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL); 1337 1338 mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%s\"%s\\%s\"", 1339 interp, interp[0] == '\0' ? "" : " ", full_dir, prog); 1233 1340 1234 1341 DEBUG_TRACE(("Running [%s]", cmdline)); 1235 1342 if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, 1236 CREATE_NEW_PROCESS_GROUP, envblk, dir, &si, &pi) == 0) {1237 cry(conn, "%s: CreateProcess(%s): % d",1343 CREATE_NEW_PROCESS_GROUP, envblk, NULL, &si, &pi) == 0) { 1344 cry(conn, "%s: CreateProcess(%s): %ld", 1238 1345 __func__, cmdline, ERRNO); 1239 1346 pi.hProcess = (pid_t) -1; 1240 } else {1241 (void) close(fd_stdin);1242 (void) close(fd_stdout);1243 1347 } 1244 1348 … … 1257 1361 1258 1362 #else 1259 static int mg_stat(const char *path, struct mgstat *stp) { 1363 static int mg_stat(struct mg_connection *conn, const char *path, 1364 struct file *filep) { 1260 1365 struct stat st; 1261 int ok; 1262 1263 if (stat(path, &st) == 0) { 1264 ok = 0; 1265 stp->size = st.st_size; 1266 stp->mtime = st.st_mtime; 1267 stp->is_directory = S_ISDIR(st.st_mode); 1366 1367 if (!is_file_in_memory(conn, path, filep) && !stat(path, &st)) { 1368 filep->size = st.st_size; 1369 filep->modification_time = st.st_mtime; 1370 filep->is_directory = S_ISDIR(st.st_mode); 1268 1371 } else { 1269 ok = -1;1270 } 1271 1272 return ok;1372 filep->modification_time = (time_t) 0; 1373 } 1374 1375 return filep->membuf != NULL || filep->modification_time != (time_t) 0; 1273 1376 } 1274 1377 1275 1378 static void set_close_on_exec(int fd) { 1276 (void) fcntl(fd, F_SETFD, FD_CLOEXEC); 1277 } 1278 1279 static int start_thread(struct mg_context *ctx, mg_thread_func_t func, 1280 void *param) { 1379 fcntl(fd, F_SETFD, FD_CLOEXEC); 1380 } 1381 1382 int mg_start_thread(mg_thread_func_t func, void *param) { 1281 1383 pthread_t thread_id; 1282 1384 pthread_attr_t attr; 1283 int retval; 1385 int result; 1386 struct mg_context* ctx = param; 1284 1387 char* stacksize = ctx->config[THREAD_STACK_SIZE]; 1285 1388 char* priority = ctx->config[THREAD_PRIORITY]; … … 1289 1392 (void) pthread_attr_init(&attr); 1290 1393 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 1291 // TODO(lsm): figure out why mongoose dies on Linux if next line is enabled 1292 // (void) pthread_attr_setstacksize(&attr, sizeof(struct mg_connection) * 5); 1394 1395 #if USE_STACK_SIZE > 1 1396 // Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384; Can be overridden 1397 if (stacksize == NULL) 1398 (void) pthread_attr_setstacksize(&attr, USE_STACK_SIZE); 1399 #endif 1293 1400 1294 1401 if (stacksize != NULL) { … … 1338 1445 } 1339 1446 1340 if ((retval = pthread_create(&thread_id, &attr, func, param)) != 0) { 1341 cry(fc(ctx), "%s: %s", __func__, strerror(retval)); 1342 } 1343 1344 return retval; 1447 result = pthread_create(&thread_id, &attr, func, param); 1448 pthread_attr_destroy(&attr); 1449 1450 return result; 1345 1451 } 1346 1452 1347 1453 #ifndef NO_CGI 1348 1454 static pid_t spawn_process(struct mg_connection *conn, const char *prog, 1349 char *envblk, char *envp[], int fd _stdin,1350 int fd _stdout, const char *dir) {1455 char *envblk, char *envp[], int fdin, 1456 int fdout, const char *dir) { 1351 1457 pid_t pid; 1352 1458 const char *interp; 1353 1459 1354 envblk = NULL; // Unused1460 (void) envblk; 1355 1461 1356 1462 if ((pid = fork()) == -1) { … … 1361 1467 if (chdir(dir) != 0) { 1362 1468 cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO)); 1363 } else if (dup2(fd _stdin, 0) == -1) {1364 cry(conn, "%s: dup2(%d, 0): %s", __func__, fd _stdin, strerror(ERRNO));1365 } else if (dup2(fd _stdout, 1) == -1) {1366 cry(conn, "%s: dup2(%d, 1): %s", __func__, fd _stdout, strerror(ERRNO));1469 } else if (dup2(fdin, 0) == -1) { 1470 cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, strerror(ERRNO)); 1471 } else if (dup2(fdout, 1) == -1) { 1472 cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout, strerror(ERRNO)); 1367 1473 } else { 1368 (void) dup2(fd_stdout, 2); 1369 (void) close(fd_stdin); 1370 (void) close(fd_stdout); 1371 1372 // Execute CGI program. No need to lock: new process 1474 // Not redirecting stderr to stdout, to avoid output being littered 1475 // with the error messages. 1476 (void) close(fdin); 1477 (void) close(fdout); 1478 1479 // After exec, all signal handlers are restored to their default values, 1480 // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's 1481 // implementation, SIGCHLD's handler will leave unchanged after exec 1482 // if it was set to be ignored. Restore it to default action. 1483 signal(SIGCHLD, SIG_DFL); 1484 1373 1485 interp = conn->ctx->config[CGI_INTERPRETER]; 1374 1486 if (interp == NULL) { … … 1382 1494 } 1383 1495 exit(EXIT_FAILURE); 1384 } else {1385 // Parent. Close stdio descriptors1386 (void) close(fd_stdin);1387 (void) close(fd_stdout);1388 1496 } 1389 1497 … … 1401 1509 } 1402 1510 #endif // _WIN32 1511 1512 #ifndef HAVE_POLL 1513 static int poll(struct pollfd *pfd, int n, int milliseconds) { 1514 struct timeval tv; 1515 fd_set set; 1516 int i, result; 1517 SOCKET maxfd = 0; 1518 1519 tv.tv_sec = milliseconds / 1000; 1520 tv.tv_usec = (milliseconds % 1000) * 1000; 1521 FD_ZERO(&set); 1522 1523 for (i = 0; i < n; i++) { 1524 FD_SET((SOCKET) pfd[i].fd, &set); 1525 pfd[i].revents = 0; 1526 1527 if (pfd[i].fd > maxfd) { 1528 maxfd = pfd[i].fd; 1529 } 1530 } 1531 1532 if ((result = select(maxfd + 1, &set, NULL, NULL, &tv)) > 0) { 1533 for (i = 0; i < n; i++) { 1534 if (FD_ISSET(pfd[i].fd, &set)) { 1535 pfd[i].revents = POLLIN; 1536 } 1537 } 1538 } 1539 1540 return result; 1541 } 1542 #endif // HAVE_POLL 1403 1543 1404 1544 // Write data to the IO channel - opened file descriptor, socket or SSL … … 1409 1549 int n, k; 1410 1550 1551 (void) ssl; // Get rid of warning 1411 1552 sent = 0; 1412 1553 while (sent < len) { … … 1415 1556 k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent); 1416 1557 1558 #ifndef NO_SSL 1417 1559 if (ssl != NULL) { 1418 1560 n = SSL_write(ssl, buf + sent, k); 1419 } else if (fp != NULL) { 1420 n = fwrite(buf + sent, 1, (size_t) k, fp); 1561 } else 1562 #endif 1563 if (fp != NULL) { 1564 n = (int) fwrite(buf + sent, 1, (size_t) k, fp); 1421 1565 if (ferror(fp)) 1422 1566 n = -1; … … 1425 1569 } 1426 1570 1427 if (n < 0)1571 if (n <= 0) 1428 1572 break; 1429 1573 … … 1435 1579 1436 1580 // Read from IO channel - opened file descriptor, socket, or SSL descriptor. 1437 // Return n umber of bytes read.1438 static int pull(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int len) {1581 // Return negative value on error, or number of bytes read on success. 1582 static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) { 1439 1583 int nread; 1440 1584 1441 if (ssl != NULL) { 1442 nread = SSL_read(ssl, buf, len); 1443 } else if (fp != NULL) { 1585 if (fp != NULL) { 1444 1586 // Use read() instead of fread(), because if we're reading from the CGI 1445 1587 // pipe, fread() may block until IO buffer is filled up. We cannot afford 1446 1588 // to block and must pass all read bytes immediately to the client. 1447 1589 nread = read(fileno(fp), buf, (size_t) len); 1448 if (ferror(fp)) 1449 nread = -1; 1590 #ifndef NO_SSL 1591 } else if (conn->ssl != NULL) { 1592 nread = SSL_read(conn->ssl, buf, len); 1593 #endif 1450 1594 } else { 1451 nread = recv(sock, buf, (size_t) len, 0); 1595 nread = recv(conn->client.sock, buf, (size_t) len, 0); 1596 } 1597 1598 return conn->ctx->stop_flag ? -1 : nread; 1599 } 1600 1601 static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) { 1602 int n, nread = 0; 1603 1604 while (len > 0 && conn->ctx->stop_flag == 0) { 1605 n = pull(fp, conn, buf + nread, len); 1606 if (n < 0) { 1607 nread = n; // Propagate the error 1608 break; 1609 } else if (n == 0) { 1610 break; // No more data to read 1611 } else { 1612 conn->consumed_content += n; 1613 nread += n; 1614 len -= n; 1615 } 1452 1616 } 1453 1617 … … 1457 1621 int mg_read(struct mg_connection *conn, void *buf, size_t len) { 1458 1622 int n, buffered_len, nread; 1459 const char *buffered; 1460 1461 assert((conn->content_len == -1 && conn->consumed_content == 0) || 1462 conn->consumed_content <= conn->content_len); 1463 DEBUG_TRACE(("%p %zu %lld %lld", buf, len, 1464 conn->content_len, conn->consumed_content)); 1623 const char *body; 1624 1625 // If Content-Length is not set, read until socket is closed 1626 if (conn->consumed_content == 0 && conn->content_len == 0) { 1627 conn->content_len = INT64_MAX; 1628 conn->must_close = 1; 1629 } 1630 1465 1631 nread = 0; 1466 1632 if (conn->consumed_content < conn->content_len) { 1467 1468 1633 // Adjust number of bytes to read. 1469 1634 int64_t to_read = conn->content_len - conn->consumed_content; 1470 1635 if (to_read < (int64_t) len) { 1471 len = (int) to_read; 1472 } 1473 1474 // How many bytes of data we have buffered in the request buffer? 1475 buffered = conn->buf + conn->request_len + conn->consumed_content; 1476 buffered_len = conn->data_len - conn->request_len; 1477 assert(buffered_len >= 0); 1478 1479 // Return buffered data back if we haven't done that yet. 1480 if (conn->consumed_content < (int64_t) buffered_len) { 1481 buffered_len -= (int) conn->consumed_content; 1636 len = (size_t) to_read; 1637 } 1638 1639 // Return buffered data 1640 body = conn->buf + conn->request_len + conn->consumed_content; 1641 buffered_len = &conn->buf[conn->data_len] - body; 1642 if (buffered_len > 0) { 1482 1643 if (len < (size_t) buffered_len) { 1483 buffered_len = len;1644 buffered_len = (int) len; 1484 1645 } 1485 memcpy(buf, b uffered, (size_t)buffered_len);1646 memcpy(buf, body, (size_t) buffered_len); 1486 1647 len -= buffered_len; 1648 conn->consumed_content += buffered_len; 1649 nread += buffered_len; 1487 1650 buf = (char *) buf + buffered_len; 1488 conn->consumed_content += buffered_len;1489 nread = buffered_len;1490 1651 } 1491 1652 1492 1653 // We have returned all buffered data. Read new data from the remote socket. 1493 while (len > 0) { 1494 n = pull(NULL, conn->client.sock, conn->ssl, (char *) buf, (int) len); 1495 if (n <= 0) { 1496 break; 1654 n = pull_all(NULL, conn, (char *) buf, (int) len); 1655 nread = n >= 0 ? nread + n : n; 1656 } 1657 return nread; 1658 } 1659 1660 int mg_write(struct mg_connection *conn, const void *buf, size_t len) { 1661 time_t now; 1662 int64_t n, total, allowed; 1663 1664 if (conn->throttle > 0) { 1665 if ((now = time(NULL)) != conn->last_throttle_time) { 1666 conn->last_throttle_time = now; 1667 conn->last_throttle_bytes = 0; 1668 } 1669 allowed = conn->throttle - conn->last_throttle_bytes; 1670 if (allowed > (int64_t) len) { 1671 allowed = len; 1672 } 1673 if ((total = push(NULL, conn->client.sock, conn->ssl, (const char *) buf, 1674 (int64_t) allowed)) == allowed) { 1675 buf = (char *) buf + total; 1676 conn->last_throttle_bytes += total; 1677 while (total < (int64_t) len && conn->ctx->stop_flag == 0) { 1678 allowed = conn->throttle > (int64_t) len - total ? 1679 (int64_t) len - total : conn->throttle; 1680 if ((n = push(NULL, conn->client.sock, conn->ssl, (const char *) buf, 1681 (int64_t) allowed)) != allowed) { 1682 break; 1683 } 1684 sleep(1); 1685 conn->last_throttle_bytes = allowed; 1686 conn->last_throttle_time = time(NULL); 1687 buf = (char *) buf + n; 1688 total += n; 1497 1689 } 1498 buf = (char *) buf + n; 1499 conn->consumed_content += n; 1500 nread += n; 1501 len -= n; 1502 } 1503 } 1504 return nread; 1505 } 1506 1507 int mg_write(struct mg_connection *conn, const void *buf, size_t len) { 1508 return (int) push(NULL, conn->client.sock, conn->ssl, (const char *) buf, 1509 (int64_t) len); 1690 } 1691 } else { 1692 total = push(NULL, conn->client.sock, conn->ssl, (const char *) buf, 1693 (int64_t) len); 1694 } 1695 return (int) total; 1696 } 1697 1698 // Alternative alloc_vprintf() for non-compliant C runtimes 1699 static int alloc_vprintf2(char **buf, const char *fmt, va_list ap) { 1700 va_list ap_copy; 1701 int size = MG_BUF_LEN; 1702 int len = -1; 1703 1704 *buf = NULL; 1705 while (len == -1) { 1706 if (*buf) free(*buf); 1707 *buf = malloc(size *= 4); 1708 if (!*buf) break; 1709 va_copy(ap_copy, ap); 1710 len = vsnprintf(*buf, size, fmt, ap_copy); 1711 } 1712 1713 return len; 1714 } 1715 1716 // Print message to buffer. If buffer is large enough to hold the message, 1717 // return buffer. If buffer is to small, allocate large enough buffer on heap, 1718 // and return allocated buffer. 1719 static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) { 1720 va_list ap_copy; 1721 int len; 1722 1723 // Windows is not standard-compliant, and vsnprintf() returns -1 if 1724 // buffer is too small. Also, older versions of msvcrt.dll do not have 1725 // _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. 1726 // Therefore, we make two passes: on first pass, get required message length. 1727 // On second pass, actually print the message. 1728 va_copy(ap_copy, ap); 1729 len = vsnprintf(NULL, 0, fmt, ap_copy); 1730 1731 if (len < 0) { 1732 // C runtime is not standard compliant, vsnprintf() returned -1. 1733 // Switch to alternative code path that uses incremental allocations. 1734 va_copy(ap_copy, ap); 1735 len = alloc_vprintf2(buf, fmt, ap); 1736 } else if (len > (int) size && 1737 (size = len + 1) > 0 && 1738 (*buf = (char *) malloc(size)) == NULL) { 1739 len = -1; // Allocation failed, mark failure 1740 } else { 1741 va_copy(ap_copy, ap); 1742 vsnprintf(*buf, size, fmt, ap_copy); 1743 } 1744 1745 return len; 1746 } 1747 1748 static int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) { 1749 char mem[MG_BUF_LEN], *buf = mem; 1750 int len; 1751 1752 if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { 1753 len = mg_write(conn, buf, (size_t) len); 1754 } 1755 if (buf != mem && buf != NULL) { 1756 free(buf); 1757 } 1758 1759 return len; 1510 1760 } 1511 1761 1512 1762 int mg_printf(struct mg_connection *conn, const char *fmt, ...) { 1513 char buf[BUFSIZ];1514 int len;1515 1763 va_list ap; 1516 1517 1764 va_start(ap, fmt); 1518 len = mg_vsnprintf(conn, buf, sizeof(buf), fmt, ap); 1519 va_end(ap); 1520 1521 return mg_write(conn, buf, (size_t)len); 1522 } 1523 1524 // URL-decode input buffer into destination buffer. 1525 // 0-terminate the destination buffer. Return the length of decoded data. 1526 // form-url-encoded data differs from URI encoding in a way that it 1527 // uses '+' as character for space, see RFC 1866 section 8.2.1 1528 // http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt 1529 static size_t url_decode(const char *src, size_t src_len, char *dst, 1530 size_t dst_len, int is_form_url_encoded) { 1531 size_t i, j; 1532 int a, b; 1765 return mg_vprintf(conn, fmt, ap); 1766 } 1767 1768 int mg_url_decode(const char *src, int src_len, char *dst, 1769 int dst_len, int is_form_url_encoded) { 1770 int i, j, a, b; 1533 1771 #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') 1534 1772 1535 1773 for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { 1536 if (src[i] == '%' && 1774 if (src[i] == '%' && i < src_len - 2 && 1537 1775 isxdigit(* (const unsigned char *) (src + i + 1)) && 1538 1776 isxdigit(* (const unsigned char *) (src + i + 2))) { … … 1550 1788 dst[j] = '\0'; // Null-terminate the destination 1551 1789 1552 return j; 1553 } 1554 1555 // Scan given buffer and fetch the value of the given variable. 1556 // It can be specified in query string, or in the POST data. 1557 // Return NULL if the variable not found, or allocated 0-terminated value. 1558 // It is caller's responsibility to free the returned value. 1559 int mg_get_var(const char *buf, size_t buf_len, const char *name, 1790 return i >= src_len ? j : -1; 1791 } 1792 1793 int mg_get_var(const char *data, size_t data_len, const char *name, 1560 1794 char *dst, size_t dst_len) { 1561 1795 const char *p, *e, *s; 1562 size_t name_len, len; 1563 1564 name_len = strlen(name); 1565 e = buf + buf_len; 1566 len = -1; 1567 dst[0] = '\0'; 1568 1569 // buf is "var1=val1&var2=val2...". Find variable first 1570 for (p = buf; p != NULL && p + name_len < e; p++) { 1571 if ((p == buf || p[-1] == '&') && p[name_len] == '=' && 1572 !mg_strncasecmp(name, p, name_len)) { 1573 1574 // Point p to variable value 1575 p += name_len + 1; 1576 1577 // Point s to the end of the value 1578 s = (const char *) memchr(p, '&', (size_t)(e - p)); 1579 if (s == NULL) { 1580 s = e; 1796 size_t name_len; 1797 int len; 1798 1799 if (dst == NULL || dst_len == 0) { 1800 len = -2; 1801 } else if (data == NULL || name == NULL || data_len == 0) { 1802 len = -1; 1803 dst[0] = '\0'; 1804 } else { 1805 name_len = strlen(name); 1806 e = data + data_len; 1807 len = -1; 1808 dst[0] = '\0'; 1809 1810 // data is "var1=val1&var2=val2...". Find variable first 1811 for (p = data; p + name_len < e; p++) { 1812 if ((p == data || p[-1] == '&') && p[name_len] == '=' && 1813 !mg_strncasecmp(name, p, name_len)) { 1814 1815 // Point p to variable value 1816 p += name_len + 1; 1817 1818 // Point s to the end of the value 1819 s = (const char *) memchr(p, '&', (size_t)(e - p)); 1820 if (s == NULL) { 1821 s = e; 1822 } 1823 assert(s >= p); 1824 1825 // Decode variable into destination buffer 1826 len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1); 1827 1828 // Redirect error code from -1 to -2 (destination buffer too small). 1829 if (len == -1) { 1830 len = -2; 1831 } 1832 break; 1581 1833 } 1582 assert(s >= p);1583 1584 // Decode variable into destination buffer1585 if ((size_t) (s - p) < dst_len) {1586 len = url_decode(p, (size_t)(s - p), dst, dst_len, 1);1587 }1588 break;1589 1834 } 1590 1835 } … … 1593 1838 } 1594 1839 1595 int mg_get_cookie(const struct mg_connection *conn, const char *cookie_name,1840 int mg_get_cookie(const char *cookie_header, const char *var_name, 1596 1841 char *dst, size_t dst_size) { 1597 1842 const char *s, *p, *end; 1598 1843 int name_len, len = -1; 1599 1844 1600 dst[0] = '\0'; 1601 if ((s = mg_get_header(conn, "Cookie")) == NULL) { 1602 return 0; 1603 } 1604 1605 name_len = strlen(cookie_name); 1606 end = s + strlen(s); 1607 1608 for (; (s = strstr(s, cookie_name)) != NULL; s += name_len) 1609 if (s[name_len] == '=') { 1610 s += name_len + 1; 1611 if ((p = strchr(s, ' ')) == NULL) 1612 p = end; 1613 if (p[-1] == ';') 1614 p--; 1615 if (*s == '"' && p[-1] == '"' && p > s + 1) { 1616 s++; 1617 p--; 1845 if (dst == NULL || dst_size == 0) { 1846 len = -2; 1847 } else if (var_name == NULL || (s = cookie_header) == NULL) { 1848 len = -1; 1849 dst[0] = '\0'; 1850 } else { 1851 name_len = (int) strlen(var_name); 1852 end = s + strlen(s); 1853 dst[0] = '\0'; 1854 1855 for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) { 1856 if (s[name_len] == '=') { 1857 s += name_len + 1; 1858 if ((p = strchr(s, ' ')) == NULL) 1859 p = end; 1860 if (p[-1] == ';') 1861 p--; 1862 if (*s == '"' && p[-1] == '"' && p > s + 1) { 1863 s++; 1864 p--; 1865 } 1866 if ((size_t) (p - s) < dst_size) { 1867 len = p - s; 1868 mg_strlcpy(dst, s, (size_t) len + 1); 1869 } else { 1870 len = -3; 1871 } 1872 break; 1618 1873 } 1619 if ((size_t) (p - s) < dst_size) { 1620 len = (p - s) + 1; 1621 mg_strlcpy(dst, s, (size_t)len); 1622 } 1623 break; 1624 } 1625 1874 } 1875 } 1626 1876 return len; 1627 1877 } 1628 1878 1629 static intconvert_uri_to_file_name(struct mg_connection *conn, char *buf,1630 size_t buf_len, struct mgstat *st) {1879 static void convert_uri_to_file_name(struct mg_connection *conn, char *buf, 1880 size_t buf_len, struct file *filep) { 1631 1881 struct vec a, b; 1632 const char *rewrite, *uri = conn->request_info.uri; 1882 const char *rewrite, *uri = conn->request_info.uri, 1883 *root = conn->ctx->config[DOCUMENT_ROOT]; 1633 1884 char *p; 1634 int match_len, stat_result; 1635 1636 buf_len--; // This is because memmove() for PATH_INFO may shift part 1637 // of the path one byte on the right. 1638 mg_snprintf(conn, buf, buf_len, "%s%s", conn->ctx->config[DOCUMENT_ROOT], 1639 uri); 1885 int match_len; 1886 char gz_path[PATH_MAX]; 1887 char const* accept_encoding; 1888 1889 // Using buf_len - 1 because memmove() for PATH_INFO may shift part 1890 // of the path one byte on the right. 1891 // If document_root is NULL, leave the file empty. 1892 mg_snprintf(conn, buf, buf_len - 1, "%s%s", 1893 root == NULL ? "" : root, 1894 root == NULL ? "" : uri); 1640 1895 1641 1896 rewrite = conn->ctx->config[REWRITE]; 1642 1897 while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { 1643 1898 if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) { 1644 mg_snprintf(conn, buf, buf_len, "%.*s%s", b.len, b.ptr, uri + match_len); 1899 mg_snprintf(conn, buf, buf_len - 1, "%.*s%s", (int) b.len, b.ptr, 1900 uri + match_len); 1645 1901 break; 1646 1902 } 1647 1903 } 1648 1904 1649 #if defined(_WIN32) && !defined(__SYMBIAN32__) 1650 //change_slashes_to_backslashes(buf); 1651 #endif // _WIN32 1652 1653 if ((stat_result = mg_stat(buf, st)) != 0) { 1654 // Support PATH_INFO for CGI scripts. 1655 for (p = buf + strlen(buf); p > buf + 1; p--) { 1656 if (*p == '/') { 1657 *p = '\0'; 1658 if (match_prefix(conn->ctx->config[CGI_EXTENSIONS], 1659 strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 && 1660 (stat_result = mg_stat(buf, st)) == 0) { 1661 // Shift PATH_INFO block one character right, e.g. 1662 // "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00" 1663 // conn->path_info is pointing to the local variable "path" declared 1664 // in handle_request(), so PATH_INFO not valid after 1665 // handle_request returns. 1666 conn->path_info = p + 1; 1667 memmove(p + 2, p + 1, strlen(p + 1) + 1); // +1 is for trailing \0 1668 p[1] = '/'; 1669 break; 1670 } else { 1671 *p = '/'; 1672 stat_result = -1; 1673 } 1905 if (mg_stat(conn, buf, filep)) return; 1906 1907 // if we can't find the actual file, look for the file 1908 // with the same name but a .gz extension. If we find it, 1909 // use that and set the gzipped flag in the file struct 1910 // to indicate that the response need to have the content- 1911 // encoding: gzip header 1912 // we can only do this if the browser declares support 1913 if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) { 1914 if (strstr(accept_encoding,"gzip") != NULL) { 1915 snprintf(gz_path, sizeof(gz_path), "%s.gz", buf); 1916 if (mg_stat(conn, gz_path, filep)) { 1917 filep->gzipped = 1; 1918 return; 1674 1919 } 1675 1920 } 1676 1921 } 1677 1922 1678 return stat_result; 1679 } 1680 1681 static int sslize(struct mg_connection *conn, int (*func)(SSL *)) { 1682 return (conn->ssl = SSL_new(conn->ctx->ssl_ctx)) != NULL && 1683 SSL_set_fd(conn->ssl, conn->client.sock) == 1 && 1684 func(conn->ssl) == 1; 1923 // Support PATH_INFO for CGI scripts. 1924 for (p = buf + strlen(buf); p > buf + 1; p--) { 1925 if (*p == '/') { 1926 *p = '\0'; 1927 if (match_prefix(conn->ctx->config[CGI_EXTENSIONS], 1928 strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 && 1929 mg_stat(conn, buf, filep)) { 1930 // Shift PATH_INFO block one character right, e.g. 1931 // "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00" 1932 // conn->path_info is pointing to the local variable "path" declared 1933 // in handle_request(), so PATH_INFO is not valid after 1934 // handle_request returns. 1935 conn->path_info = p + 1; 1936 memmove(p + 2, p + 1, strlen(p + 1) + 1); // +1 is for trailing \0 1937 p[1] = '/'; 1938 break; 1939 } else { 1940 *p = '/'; 1941 } 1942 } 1943 } 1685 1944 } 1686 1945 … … 1693 1952 int len = 0; 1694 1953 1695 DEBUG_TRACE(("buf: %p, len: %d", buf, buflen));1696 1954 for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++) 1697 1955 // Control characters are not allowed but >=128 is. … … 1699 1957 *s != '\n' && * (const unsigned char *) s < 128) { 1700 1958 len = -1; 1959 break; // [i_a] abort scan as soon as one malformed character is found; 1960 // don't let subsequent \r\n\r\n win us over anyhow 1701 1961 } else if (s[0] == '\n' && s[1] == '\n') { 1702 1962 len = (int) (s - buf) + 2; … … 1718 1978 1719 1979 return -1; 1980 } 1981 1982 static int num_leap_years(int year) { 1983 return year / 4 - year / 100 + year / 400; 1720 1984 } 1721 1985 … … 1739 2003 year > 1970 && 1740 2004 (month = get_month_index(month_str)) != -1) { 2005 leap_days = num_leap_years(year) - num_leap_years(1970); 1741 2006 year -= 1970; 1742 leap_days = year / 4 - year / 100 + year / 400;1743 2007 days = year * 365 + days_before_month[month] + (day - 1) + leap_days; 1744 2008 result = days * 24 * 3600 + hour * 3600 + minute * 60 + second; … … 1755 2019 while (*s != '\0') { 1756 2020 *p++ = *s++; 1757 if (IS_DIRSEP_CHAR(s[-1])) { 1758 // Skip all following slashes and backslashes 1759 while (IS_DIRSEP_CHAR(s[0])) { 1760 s++; 1761 } 1762 1763 // Skip all double-dots 1764 while (*s == '.' && s[1] == '.') { 1765 s += 2; 2021 if (s[-1] == '/' || s[-1] == '\\') { 2022 // Skip all following slashes, backslashes and double-dots 2023 while (s[0] != '\0') { 2024 if (s[0] == '/' || s[0] == '\\') { 2025 s++; 2026 } else if (s[0] == '.' && s[1] == '.') { 2027 s += 2; 2028 } else { 2029 break; 2030 } 1766 2031 } 1767 2032 } … … 1774 2039 size_t ext_len; 1775 2040 const char *mime_type; 1776 size_t mime_type_len;1777 2041 } builtin_mime_types[] = { 1778 {".html", 5, "text/html", 9}, 1779 {".htm", 4, "text/html", 9}, 1780 {".shtm", 5, "text/html", 9}, 1781 {".shtml", 6, "text/html", 9}, 1782 {".css", 4, "text/css", 8}, 1783 {".js", 3, "application/x-javascript", 24}, 1784 {".ico", 4, "image/x-icon", 12}, 1785 {".gif", 4, "image/gif", 9}, 1786 {".jpg", 4, "image/jpeg", 10}, 1787 {".jpeg", 5, "image/jpeg", 10}, 1788 {".png", 4, "image/png", 9}, 1789 {".svg", 4, "image/svg+xml", 13}, 1790 {".torrent", 8, "application/x-bittorrent", 24}, 1791 {".wav", 4, "audio/x-wav", 11}, 1792 {".mp3", 4, "audio/x-mp3", 11}, 1793 {".mid", 4, "audio/mid", 9}, 1794 {".m3u", 4, "audio/x-mpegurl", 15}, 1795 {".ram", 4, "audio/x-pn-realaudio", 20}, 1796 {".xml", 4, "text/xml", 8}, 1797 {".xslt", 5, "application/xml", 15}, 1798 {".ra", 3, "audio/x-pn-realaudio", 20}, 1799 {".doc", 4, "application/msword", 19}, 1800 {".exe", 4, "application/octet-stream", 24}, 1801 {".zip", 4, "application/x-zip-compressed", 28}, 1802 {".xls", 4, "application/excel", 17}, 1803 {".tgz", 4, "application/x-tar-gz", 20}, 1804 {".tar", 4, "application/x-tar", 17}, 1805 {".gz", 3, "application/x-gunzip", 20}, 1806 {".arj", 4, "application/x-arj-compressed", 28}, 1807 {".rar", 4, "application/x-arj-compressed", 28}, 1808 {".rtf", 4, "application/rtf", 15}, 1809 {".pdf", 4, "application/pdf", 15}, 1810 {".swf", 4, "application/x-shockwave-flash",29}, 1811 {".mpg", 4, "video/mpeg", 10}, 1812 {".mpeg", 5, "video/mpeg", 10}, 1813 {".mp4", 4, "video/mp4", 9}, 1814 {".m4v", 4, "video/x-m4v", 11}, 1815 {".asf", 4, "video/x-ms-asf", 14}, 1816 {".avi", 4, "video/x-msvideo", 15}, 1817 {".bmp", 4, "image/bmp", 9}, 1818 {NULL, 0, NULL, 0} 2042 {".html", 5, "text/html"}, 2043 {".htm", 4, "text/html"}, 2044 {".shtm", 5, "text/html"}, 2045 {".shtml", 6, "text/html"}, 2046 {".css", 4, "text/css"}, 2047 {".js", 3, "application/x-javascript"}, 2048 {".ico", 4, "image/x-icon"}, 2049 {".gif", 4, "image/gif"}, 2050 {".jpg", 4, "image/jpeg"}, 2051 {".jpeg", 5, "image/jpeg"}, 2052 {".png", 4, "image/png"}, 2053 {".svg", 4, "image/svg+xml"}, 2054 {".txt", 4, "text/plain"}, 2055 {".torrent", 8, "application/x-bittorrent"}, 2056 {".wav", 4, "audio/x-wav"}, 2057 {".mp3", 4, "audio/x-mp3"}, 2058 {".mid", 4, "audio/mid"}, 2059 {".m3u", 4, "audio/x-mpegurl"}, 2060 {".ogg", 4, "audio/ogg"}, 2061 {".ram", 4, "audio/x-pn-realaudio"}, 2062 {".xml", 4, "text/xml"}, 2063 {".json", 5, "text/json"}, 2064 {".xslt", 5, "application/xml"}, 2065 {".xsl", 4, "application/xml"}, 2066 {".ra", 3, "audio/x-pn-realaudio"}, 2067 {".doc", 4, "application/msword"}, 2068 {".exe", 4, "application/octet-stream"}, 2069 {".zip", 4, "application/x-zip-compressed"}, 2070 {".xls", 4, "application/excel"}, 2071 {".tgz", 4, "application/x-tar-gz"}, 2072 {".tar", 4, "application/x-tar"}, 2073 {".gz", 3, "application/x-gunzip"}, 2074 {".arj", 4, "application/x-arj-compressed"}, 2075 {".rar", 4, "application/x-arj-compressed"}, 2076 {".rtf", 4, "application/rtf"}, 2077 {".pdf", 4, "application/pdf"}, 2078 {".swf", 4, "application/x-shockwave-flash"}, 2079 {".mpg", 4, "video/mpeg"}, 2080 {".webm", 5, "video/webm"}, 2081 {".mpeg", 5, "video/mpeg"}, 2082 {".mov", 4, "video/quicktime"}, 2083 {".mp4", 4, "video/mp4"}, 2084 {".m4v", 4, "video/x-m4v"}, 2085 {".asf", 4, "video/x-ms-asf"}, 2086 {".avi", 4, "video/x-msvideo"}, 2087 {".bmp", 4, "image/bmp"}, 2088 {".ttf", 4, "application/x-font-ttf"}, 2089 {NULL, 0, NULL} 1819 2090 }; 2091 2092 const char *mg_get_builtin_mime_type(const char *path) { 2093 const char *ext; 2094 size_t i, path_len; 2095 2096 path_len = strlen(path); 2097 2098 for (i = 0; builtin_mime_types[i].extension != NULL; i++) { 2099 ext = path + (path_len - builtin_mime_types[i].ext_len); 2100 if (path_len > builtin_mime_types[i].ext_len && 2101 mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) { 2102 return builtin_mime_types[i].mime_type; 2103 } 2104 } 2105 2106 return "text/plain"; 2107 } 1820 2108 1821 2109 // Look at the "path" extension and figure what mime type it has. … … 1825 2113 struct vec ext_vec, mime_vec; 1826 2114 const char *list, *ext; 1827 size_t i,path_len;2115 size_t path_len; 1828 2116 1829 2117 path_len = strlen(path); … … 1841 2129 } 1842 2130 1843 // Now scan built-in mime types 1844 for (i = 0; builtin_mime_types[i].extension != NULL; i++) { 1845 ext = path + (path_len - builtin_mime_types[i].ext_len); 1846 if (path_len > builtin_mime_types[i].ext_len && 1847 mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) { 1848 vec->ptr = builtin_mime_types[i].mime_type; 1849 vec->len = builtin_mime_types[i].mime_type_len; 1850 return; 1851 } 1852 } 1853 1854 // Nothing found. Fall back to "text/plain" 1855 vec->ptr = "text/plain"; 1856 vec->len = 10; 2131 vec->ptr = mg_get_builtin_mime_type(path); 2132 vec->len = strlen(vec->ptr); 2133 } 2134 2135 static int is_big_endian(void) { 2136 static const int n = 1; 2137 return ((char *) &n)[0] == 0; 1857 2138 } 1858 2139 … … 1864 2145 } MD5_CTX; 1865 2146 1866 #if defined(__BYTE_ORDER) && (__BYTE_ORDER == 1234)1867 #define byteReverse(buf, len) // Do nothing1868 #else1869 2147 static void byteReverse(unsigned char *buf, unsigned longs) { 1870 2148 uint32_t t; 1871 do { 1872 t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | 1873 ((unsigned) buf[1] << 8 | buf[0]); 1874 *(uint32_t *) buf = t; 1875 buf += 4; 1876 } while (--longs); 1877 } 1878 #endif 2149 2150 // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN 2151 if (is_big_endian()) { 2152 do { 2153 t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | 2154 ((unsigned) buf[1] << 8 | buf[0]); 2155 * (uint32_t *) buf = t; 2156 buf += 4; 2157 } while (--longs); 2158 } 2159 } 1879 2160 1880 2161 #define F1(x, y, z) (z ^ (x & (y ^ z))) … … 2019 2300 unsigned count; 2020 2301 unsigned char *p; 2302 uint32_t *a; 2021 2303 2022 2304 count = (ctx->bits[0] >> 3) & 0x3F; … … 2035 2317 byteReverse(ctx->in, 14); 2036 2318 2037 ((uint32_t *) ctx->in)[14] = ctx->bits[0]; 2038 ((uint32_t *) ctx->in)[15] = ctx->bits[1]; 2319 a = (uint32_t *)ctx->in; 2320 a[14] = ctx->bits[0]; 2321 a[15] = ctx->bits[1]; 2039 2322 2040 2323 MD5Transform(ctx->buf, (uint32_t *) ctx->in); … … 2057 2340 } 2058 2341 2059 // Return stringified MD5 hash for list of vectors. Buffer must be 33 bytes.2060 void mg_md5(char *buf, ...) {2342 // Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. 2343 char *mg_md5(char buf[33], ...) { 2061 2344 unsigned char hash[16]; 2062 2345 const char *p; … … 2074 2357 MD5Final(hash, &ctx); 2075 2358 bin2str(buf, hash, sizeof(hash)); 2359 return buf; 2076 2360 } 2077 2361 … … 2106 2390 // Use the global passwords file, if specified by auth_gpass option, 2107 2391 // or search for .htpasswd in the requested directory. 2108 static FILE *open_auth_file(struct mg_connection *conn, const char *path) {2109 struct mg_context *ctx = conn->ctx;2392 static void open_auth_file(struct mg_connection *conn, const char *path, 2393 struct file *filep) { 2110 2394 char name[PATH_MAX]; 2111 const char *p, *e; 2112 struct mgstat st; 2113 FILE *fp; 2114 2115 if (ctx->config[GLOBAL_PASSWORDS_FILE] != NULL) { 2395 const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE]; 2396 struct file file = STRUCT_FILE_INITIALIZER; 2397 2398 if (gpass != NULL) { 2116 2399 // Use global passwords file 2117 fp = mg_fopen(ctx->config[GLOBAL_PASSWORDS_FILE], "r"); 2118 if (fp == NULL) 2119 cry(fc(ctx), "fopen(%s): %s", 2120 ctx->config[GLOBAL_PASSWORDS_FILE], strerror(ERRNO)); 2121 } else if (!mg_stat(path, &st) && st.is_directory) { 2122 (void) mg_snprintf(conn, name, sizeof(name), "%s%c%s", 2123 path, DIRSEP, PASSWORDS_FILE_NAME); 2124 fp = mg_fopen(name, "r"); 2400 if (!mg_fopen(conn, gpass, "r", filep)) { 2401 cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO)); 2402 } 2403 // Important: using local struct file to test path for is_directory flag. 2404 // If filep is used, mg_stat() makes it appear as if auth file was opened. 2405 } else if (mg_stat(conn, path, &file) && file.is_directory) { 2406 mg_snprintf(conn, name, sizeof(name), "%s%c%s", 2407 path, '/', PASSWORDS_FILE_NAME); 2408 mg_fopen(conn, name, "r", filep); 2125 2409 } else { 2126 2410 // Try to find .htpasswd in requested directory. 2127 2411 for (p = path, e = p + strlen(p) - 1; e > p; e--) 2128 if ( IS_DIRSEP_CHAR(*e))2412 if (e[0] == '/') 2129 2413 break; 2130 (void) mg_snprintf(conn, name, sizeof(name), "%.*s%c%s", 2131 (int) (e - p), p, DIRSEP, PASSWORDS_FILE_NAME); 2132 fp = mg_fopen(name, "r"); 2133 } 2134 2135 return fp; 2414 mg_snprintf(conn, name, sizeof(name), "%.*s%c%s", 2415 (int) (e - p), p, '/', PASSWORDS_FILE_NAME); 2416 mg_fopen(conn, name, "r", filep); 2417 } 2136 2418 } 2137 2419 … … 2141 2423 }; 2142 2424 2425 // Return 1 on success. Always initializes the ah structure. 2143 2426 static int parse_auth_header(struct mg_connection *conn, char *buf, 2144 2427 size_t buf_size, struct ah *ah) { … … 2146 2429 const char *auth_header; 2147 2430 2431 (void) memset(ah, 0, sizeof(*ah)); 2148 2432 if ((auth_header = mg_get_header(conn, "Authorization")) == NULL || 2149 2433 mg_strncasecmp(auth_header, "Digest ", 7) != 0) { … … 2153 2437 // Make modifiable copy of the auth header 2154 2438 (void) mg_strlcpy(buf, auth_header + 7, buf_size); 2155 2156 2439 s = buf; 2157 (void) memset(ah, 0, sizeof(*ah));2158 2440 2159 2441 // Parse authorization header … … 2205 2487 } 2206 2488 2489 static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p) { 2490 char *eof; 2491 size_t len; 2492 char *memend; 2493 2494 if (filep->membuf != NULL && *p != NULL) { 2495 memend = (char *) &filep->membuf[filep->size]; 2496 eof = (char *) memchr(*p, '\n', memend - *p); // Search for \n from p till the end of stream 2497 if (eof != NULL) { 2498 eof += 1; // Include \n 2499 } else { 2500 eof = memend; // Copy remaining data 2501 } 2502 len = (size_t) (eof - *p) > size - 1 ? size - 1 : (size_t) (eof - *p); 2503 memcpy(buf, *p, len); 2504 buf[len] = '\0'; 2505 *p += len; 2506 return len ? eof : NULL; 2507 } else if (filep->fp != NULL) { 2508 return fgets(buf, size, filep->fp); 2509 } else { 2510 return NULL; 2511 } 2512 } 2513 2207 2514 // Authorize against the opened passwords file. Return 1 if authorized. 2208 static int authorize(struct mg_connection *conn, FILE *fp) {2515 static int authorize(struct mg_connection *conn, struct file *filep) { 2209 2516 struct ah ah; 2210 char line[256], f_user[256], ha1[256], f_domain[256], buf[ BUFSIZ];2517 char line[256], f_user[256], ha1[256], f_domain[256], buf[MG_BUF_LEN], *p; 2211 2518 2212 2519 if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) { … … 2215 2522 2216 2523 // Loop over passwords file 2217 while (fgets(line, sizeof(line), fp) != NULL) { 2524 p = (char *) filep->membuf; 2525 while (mg_fgets(line, sizeof(line), filep, &p) != NULL) { 2218 2526 if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) { 2219 2527 continue; … … 2222 2530 if (!strcmp(ah.user, f_user) && 2223 2531 !strcmp(conn->ctx->config[AUTHENTICATION_DOMAIN], f_domain)) 2224 return check_password( 2225 conn->request_info.request_method, 2226 ha1, ah.uri, ah.nonce, ah.nc, ah.cnonce, ah.qop, 2227 ah.response); 2532 return check_password(conn->request_info.request_method, ha1, ah.uri, 2533 ah.nonce, ah.nc, ah.cnonce, ah.qop, ah.response); 2228 2534 } 2229 2535 … … 2233 2539 // Return 1 if request is authorised, 0 otherwise. 2234 2540 static int check_authorization(struct mg_connection *conn, const char *path) { 2235 FILE *fp;2236 2541 char fname[PATH_MAX]; 2237 2542 struct vec uri_vec, filename_vec; 2238 2543 const char *list; 2239 int authorized; 2240 2241 fp = NULL; 2242 authorized = 1; 2544 struct file file = STRUCT_FILE_INITIALIZER; 2545 int authorized = 1; 2243 2546 2244 2547 list = conn->ctx->config[PROTECT_URI]; 2245 2548 while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) { 2246 2549 if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) { 2247 (void)mg_snprintf(conn, fname, sizeof(fname), "%.*s",2248 filename_vec.len, filename_vec.ptr);2249 if ( (fp = mg_fopen(fname, "r")) == NULL) {2550 mg_snprintf(conn, fname, sizeof(fname), "%.*s", 2551 (int) filename_vec.len, filename_vec.ptr); 2552 if (!mg_fopen(conn, fname, "r", &file)) { 2250 2553 cry(conn, "%s: cannot open %s: %s", __func__, fname, strerror(errno)); 2251 2554 } … … 2254 2557 } 2255 2558 2256 if ( fp == NULL) {2257 fp = open_auth_file(conn, path);2258 } 2259 2260 if ( fp != NULL) {2261 authorized = authorize(conn, fp);2262 (void) fclose(fp);2559 if (!is_file_opened(&file)) { 2560 open_auth_file(conn, path, &file); 2561 } 2562 2563 if (is_file_opened(&file)) { 2564 authorized = authorize(conn, &file); 2565 mg_fclose(&file); 2263 2566 } 2264 2567 … … 2267 2570 2268 2571 static void send_authorization_request(struct mg_connection *conn) { 2269 conn-> request_info.status_code = 401;2270 (void)mg_printf(conn,2271 "HTTP/1.1 401 Unauthorized\r\n"2272 "Content-Length: 0\r\n"2273 "WWW-Authenticate: Digest qop=\"auth\", "2274 "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",2275 conn->ctx->config[AUTHENTICATION_DOMAIN],2276 (unsigned long) time(NULL));2572 conn->status_code = 401; 2573 mg_printf(conn, 2574 "HTTP/1.1 401 Unauthorized\r\n" 2575 "Content-Length: 0\r\n" 2576 "WWW-Authenticate: Digest qop=\"auth\", " 2577 "realm=\"%s\", nonce=\"%lu\"\r\n\r\n", 2578 conn->ctx->config[AUTHENTICATION_DOMAIN], 2579 (unsigned long) time(NULL)); 2277 2580 } 2278 2581 2279 2582 static int is_authorized_for_put(struct mg_connection *conn) { 2280 FILE *fp; 2583 struct file file = STRUCT_FILE_INITIALIZER; 2584 const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE]; 2281 2585 int ret = 0; 2282 2586 2283 fp = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL ? NULL : 2284 mg_fopen(conn->ctx->config[PUT_DELETE_PASSWORDS_FILE], "r"); 2285 2286 if (fp != NULL) { 2287 ret = authorize(conn, fp); 2288 (void) fclose(fp); 2587 if (passfile != NULL && mg_fopen(conn, passfile, "r", &file)) { 2588 ret = authorize(conn, &file); 2589 mg_fclose(&file); 2289 2590 } 2290 2591 … … 2309 2610 2310 2611 // Create the file if does not exist 2311 if ((fp = mg_fopen(fname, "a+")) != NULL) {2612 if ((fp = fopen(fname, "a+")) != NULL) { 2312 2613 (void) fclose(fp); 2313 2614 } 2314 2615 2315 2616 // Open the given file and temporary file 2316 if ((fp = mg_fopen(fname, "r")) == NULL) {2617 if ((fp = fopen(fname, "r")) == NULL) { 2317 2618 return 0; 2318 } else if ((fp2 = mg_fopen(tmp, "w+")) == NULL) {2619 } else if ((fp2 = fopen(tmp, "w+")) == NULL) { 2319 2620 fclose(fp); 2320 2621 return 0; … … 2334 2635 } 2335 2636 } else { 2336 (void)fprintf(fp2, "%s", line);2637 fprintf(fp2, "%s", line); 2337 2638 } 2338 2639 } … … 2341 2642 if (!found && pass != NULL) { 2342 2643 mg_md5(ha1, user, ":", domain, ":", pass, NULL); 2343 (void)fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);2644 fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); 2344 2645 } 2345 2646 2346 2647 // Close files 2347 (void)fclose(fp);2348 (void)fclose(fp2);2648 fclose(fp); 2649 fclose(fp2); 2349 2650 2350 2651 // Put the temp file in place of real file 2351 (void) mg_remove(fname);2352 (void) mg_rename(tmp, fname);2652 remove(fname); 2653 rename(tmp, fname); 2353 2654 2354 2655 return 1; 2355 2656 } 2356 2657 2357 struct de { 2358 struct mg_connection *conn; 2359 char *file_name; 2360 struct mgstat st; 2361 }; 2362 2363 static void url_encode(const char *src, char *dst, size_t dst_len) { 2658 static SOCKET conn2(const char *host, int port, int use_ssl, 2659 char *ebuf, size_t ebuf_len) { 2660 struct sockaddr_in sin; 2661 struct hostent *he; 2662 SOCKET sock = INVALID_SOCKET; 2663 2664 if (host == NULL) { 2665 snprintf(ebuf, ebuf_len, "%s", "NULL host"); 2666 } else if (use_ssl && SSLv23_client_method == NULL) { 2667 snprintf(ebuf, ebuf_len, "%s", "SSL is not initialized"); 2668 // TODO(lsm): use something threadsafe instead of gethostbyname() 2669 } else if ((he = gethostbyname(host)) == NULL) { 2670 snprintf(ebuf, ebuf_len, "gethostbyname(%s): %s", host, strerror(ERRNO)); 2671 } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { 2672 snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO)); 2673 } else { 2674 set_close_on_exec(sock); 2675 sin.sin_family = AF_INET; 2676 sin.sin_port = htons((uint16_t) port); 2677 sin.sin_addr = * (struct in_addr *) he->h_addr_list[0]; 2678 if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) { 2679 snprintf(ebuf, ebuf_len, "connect(%s:%d): %s", 2680 host, port, strerror(ERRNO)); 2681 closesocket(sock); 2682 sock = INVALID_SOCKET; 2683 } 2684 } 2685 return sock; 2686 } 2687 2688 2689 2690 static void mg_url_encode(const char *src, char *dst, size_t dst_len) { 2364 2691 static const char *dont_escape = "._-$,;~()"; 2365 2692 static const char *hex = "0123456789abcdef"; … … 2384 2711 char size[64], mod[64], href[PATH_MAX]; 2385 2712 2386 if (de-> st.is_directory) {2387 (void)mg_snprintf(de->conn, size, sizeof(size), "%s", "[DIRECTORY]");2713 if (de->file.is_directory) { 2714 mg_snprintf(de->conn, size, sizeof(size), "%s", "[DIRECTORY]"); 2388 2715 } else { 2389 2716 // We use (signed) cast below because MSVC 6 compiler cannot 2390 2717 // convert unsigned __int64 to double. Sigh. 2391 if (de->st.size < 1024) { 2392 (void) mg_snprintf(de->conn, size, sizeof(size), 2393 "%lu", (unsigned long) de->st.size); 2394 } else if (de->st.size < 1024 * 1024) { 2395 (void) mg_snprintf(de->conn, size, sizeof(size), 2396 "%.1fk", (double) de->st.size / 1024.0); 2397 } else if (de->st.size < 1024 * 1024 * 1024) { 2398 (void) mg_snprintf(de->conn, size, sizeof(size), 2399 "%.1fM", (double) de->st.size / 1048576); 2718 if (de->file.size < 1024) { 2719 mg_snprintf(de->conn, size, sizeof(size), "%d", (int) de->file.size); 2720 } else if (de->file.size < 0x100000) { 2721 mg_snprintf(de->conn, size, sizeof(size), 2722 "%.1fk", (double) de->file.size / 1024.0); 2723 } else if (de->file.size < 0x40000000) { 2724 mg_snprintf(de->conn, size, sizeof(size), 2725 "%.1fM", (double) de->file.size / 1048576); 2400 2726 } else { 2401 (void) mg_snprintf(de->conn, size, sizeof(size), 2402 "%.1fG", (double) de->st.size / 1073741824); 2403 } 2404 } 2405 (void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&de->st.mtime)); 2406 url_encode(de->file_name, href, sizeof(href)); 2727 mg_snprintf(de->conn, size, sizeof(size), 2728 "%.1fG", (double) de->file.size / 1073741824); 2729 } 2730 } 2731 strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", 2732 localtime(&de->file.modification_time)); 2733 mg_url_encode(de->file_name, href, sizeof(href)); 2407 2734 de->conn->num_bytes_sent += mg_printf(de->conn, 2408 2735 "<tr><td><a href=\"%s%s%s\">%s%s</a></td>" 2409 2736 "<td> %s</td><td> %s</td></tr>\n", 2410 de->conn->request_info.uri, href, de-> st.is_directory ? "/" : "",2411 de->file_name, de-> st.is_directory ? "/" : "", mod, size);2737 de->conn->request_info.uri, href, de->file.is_directory ? "/" : "", 2738 de->file_name, de->file.is_directory ? "/" : "", mod, size); 2412 2739 } 2413 2740 … … 2425 2752 } 2426 2753 2427 if (a-> st.is_directory && !b->st.is_directory) {2754 if (a->file.is_directory && !b->file.is_directory) { 2428 2755 return -1; // Always put directories on top 2429 } else if (!a-> st.is_directory && b->st.is_directory) {2756 } else if (!a->file.is_directory && b->file.is_directory) { 2430 2757 return 1; // Always put directories on top 2431 2758 } else if (*query_string == 'n') { 2432 2759 cmp_result = strcmp(a->file_name, b->file_name); 2433 2760 } else if (*query_string == 's') { 2434 cmp_result = a-> st.size == b->st.size ? 0 :2435 a-> st.size > b->st.size ? 1 : -1;2761 cmp_result = a->file.size == b->file.size ? 0 : 2762 a->file.size > b->file.size ? 1 : -1; 2436 2763 } else if (*query_string == 'd') { 2437 cmp_result = a-> st.mtime == b->st.mtime ? 0 :2438 a-> st.mtime > b->st.mtime ? 1 : -1;2764 cmp_result = a->file.modification_time == b->file.modification_time ? 0 : 2765 a->file.modification_time > b->file.modification_time ? 1 : -1; 2439 2766 } 2440 2767 2441 2768 return query_string[1] == 'd' ? -cmp_result : cmp_result; 2769 } 2770 2771 static int must_hide_file(struct mg_connection *conn, const char *path) { 2772 const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$"; 2773 const char *pattern = conn->ctx->config[HIDE_FILES]; 2774 return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 || 2775 (pattern != NULL && match_prefix(pattern, strlen(pattern), path) > 0); 2442 2776 } 2443 2777 … … 2455 2789 2456 2790 while ((dp = readdir(dirp)) != NULL) { 2457 // Do not show current dir and passwords file2791 // Do not show current dir and hidden files 2458 2792 if (!strcmp(dp->d_name, ".") || 2459 2793 !strcmp(dp->d_name, "..") || 2460 !strcmp(dp->d_name, PASSWORDS_FILE_NAME))2794 must_hide_file(conn, dp->d_name)) { 2461 2795 continue; 2462 2463 mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, DIRSEP, dp->d_name); 2796 } 2797 2798 mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); 2464 2799 2465 2800 // If we don't memset stat structure to zero, mtime will have … … 2468 2803 // fails. For more details, see 2469 2804 // http://code.google.com/p/mongoose/issues/detail?id=79 2470 if (mg_stat(path, &de.st) != 0) { 2471 memset(&de.st, 0, sizeof(de.st)); 2805 memset(&de.file, 0, sizeof(de.file)); 2806 mg_stat(conn, path, &de.file); 2807 2808 de.file_name = dp->d_name; 2809 cb(&de, data); 2810 } 2811 (void) closedir(dirp); 2812 } 2813 return 1; 2814 } 2815 2816 static int remove_directory(struct mg_connection *conn, const char *dir) { 2817 char path[PATH_MAX]; 2818 struct dirent *dp; 2819 DIR *dirp; 2820 struct de de; 2821 2822 if ((dirp = opendir(dir)) == NULL) { 2823 return 0; 2824 } else { 2825 de.conn = conn; 2826 2827 while ((dp = readdir(dirp)) != NULL) { 2828 // Do not show current dir (but show hidden files as they will also be removed) 2829 if (!strcmp(dp->d_name, ".") || 2830 !strcmp(dp->d_name, "..")) { 2831 continue; 2472 2832 } 2473 de.file_name = dp->d_name; 2474 2475 cb(&de, data); 2833 2834 mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); 2835 2836 // If we don't memset stat structure to zero, mtime will have 2837 // garbage and strftime() will segfault later on in 2838 // print_dir_entry(). memset is required only if mg_stat() 2839 // fails. For more details, see 2840 // http://code.google.com/p/mongoose/issues/detail?id=79 2841 memset(&de.file, 0, sizeof(de.file)); 2842 mg_stat(conn, path, &de.file); 2843 if(de.file.modification_time) { 2844 if(de.file.is_directory) { 2845 remove_directory(conn, path); 2846 } else { 2847 mg_remove(path); 2848 } 2849 } 2850 2476 2851 } 2477 2852 (void) closedir(dirp); 2478 } 2853 2854 rmdir(dir); 2855 } 2856 2479 2857 return 1; 2480 2858 } … … 2486 2864 }; 2487 2865 2866 // Behaves like realloc(), but frees original pointer on failure 2867 static void *realloc2(void *ptr, size_t size) { 2868 void *new_ptr = realloc(ptr, size); 2869 if (new_ptr == NULL) { 2870 free(ptr); 2871 } 2872 return new_ptr; 2873 } 2874 2488 2875 static void dir_scan_callback(struct de *de, void *data) { 2489 2876 struct dir_scan_data *dsd = (struct dir_scan_data *) data; … … 2491 2878 if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) { 2492 2879 dsd->arr_size *= 2; 2493 dsd->entries = (struct de *) realloc (dsd->entries, dsd->arr_size *2494 sizeof(dsd->entries[0]));2880 dsd->entries = (struct de *) realloc2(dsd->entries, dsd->arr_size * 2881 sizeof(dsd->entries[0])); 2495 2882 } 2496 2883 if (dsd->entries == NULL) { … … 2499 2886 } else { 2500 2887 dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name); 2501 dsd->entries[dsd->num_entries]. st = de->st;2888 dsd->entries[dsd->num_entries].file = de->file; 2502 2889 dsd->entries[dsd->num_entries].conn = de->conn; 2503 2890 dsd->num_entries++; … … 2552 2939 2553 2940 conn->num_bytes_sent += mg_printf(conn, "%s", "</table></body></html>"); 2554 conn-> request_info.status_code = 200;2941 conn->status_code = 200; 2555 2942 } 2556 2943 2557 2944 // Send len bytes from the opened file to the client. 2558 static void send_file_data(struct mg_connection *conn, FILE *fp, int64_t len) { 2559 char buf[BUFSIZ]; 2945 static void send_file_data(struct mg_connection *conn, struct file *filep, 2946 int64_t offset, int64_t len) { 2947 char buf[MG_BUF_LEN]; 2560 2948 int to_read, num_read, num_written; 2561 2949 2562 while (len > 0) { 2563 // Calculate how much to read from the file in the buffer 2564 to_read = sizeof(buf); 2565 if ((int64_t) to_read > len) 2566 to_read = (int) len; 2567 2568 // Read from file, exit the loop on error 2569 if ((num_read = fread(buf, 1, (size_t)to_read, fp)) == 0) 2570 break; 2571 2572 // Send read bytes to the client, exit the loop on error 2573 if ((num_written = mg_write(conn, buf, (size_t)num_read)) != num_read) 2574 break; 2575 2576 // Both read and were successful, adjust counters 2577 conn->num_bytes_sent += num_written; 2578 len -= num_written; 2950 // Sanity check the offset 2951 offset = offset < 0 ? 0 : offset > filep->size ? filep->size : offset; 2952 2953 if (len > 0 && filep->membuf != NULL && filep->size > 0) { 2954 if (len > filep->size - offset) { 2955 len = filep->size - offset; 2956 } 2957 mg_write(conn, filep->membuf + offset, (size_t) len); 2958 } else if (len > 0 && filep->fp != NULL) { 2959 fseeko(filep->fp, offset, SEEK_SET); 2960 while (len > 0) { 2961 // Calculate how much to read from the file in the buffer 2962 to_read = sizeof(buf); 2963 if ((int64_t) to_read > len) { 2964 to_read = (int) len; 2965 } 2966 2967 // Read from file, exit the loop on error 2968 if ((num_read = fread(buf, 1, (size_t) to_read, filep->fp)) <= 0) { 2969 break; 2970 } 2971 2972 // Send read bytes to the client, exit the loop on error 2973 if ((num_written = mg_write(conn, buf, (size_t) num_read)) != num_read) { 2974 break; 2975 } 2976 2977 // Both read and were successful, adjust counters 2978 conn->num_bytes_sent += num_written; 2979 len -= num_written; 2980 } 2579 2981 } 2580 2982 } … … 2588 2990 } 2589 2991 2992 static void construct_etag(char *buf, size_t buf_len, 2993 const struct file *filep) { 2994 snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", 2995 (unsigned long) filep->modification_time, filep->size); 2996 } 2997 2998 static void fclose_on_exec(struct file *filep) { 2999 if (filep != NULL && filep->fp != NULL) { 3000 #ifndef _WIN32 3001 fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC); 3002 #endif 3003 } 3004 } 3005 2590 3006 static void handle_file_request(struct mg_connection *conn, const char *path, 2591 struct mgstat *stp) {3007 struct file *filep) { 2592 3008 char date[64], lm[64], etag[64], range[64]; 2593 3009 const char *msg = "OK", *hdr; … … 2595 3011 int64_t cl, r1, r2; 2596 3012 struct vec mime_vec; 2597 FILE *fp;2598 3013 int n; 3014 char gz_path[PATH_MAX]; 3015 char const* encoding = ""; 2599 3016 2600 3017 get_mime_type(conn->ctx, path, &mime_vec); 2601 cl = stp->size;2602 conn-> request_info.status_code = 200;3018 cl = filep->size; 3019 conn->status_code = 200; 2603 3020 range[0] = '\0'; 2604 3021 2605 if ((fp = mg_fopen(path, "rb")) == NULL) { 3022 // if this file is in fact a pre-gzipped file, rewrite its filename 3023 // it's important to rewrite the filename after resolving 3024 // the mime type from it, to preserve the actual file's type 3025 if (filep->gzipped) { 3026 snprintf(gz_path, sizeof(gz_path), "%s.gz", path); 3027 path = gz_path; 3028 encoding = "Content-Encoding: gzip\r\n"; 3029 } 3030 3031 if (!mg_fopen(conn, path, "rb", filep)) { 2606 3032 send_http_error(conn, 500, http_500_error, 2607 "fopen(%s): %s", path, strerror(ERRNO));3033 "fopen(%s): %s", path, strerror(ERRNO)); 2608 3034 return; 2609 3035 } 2610 set_close_on_exec(fileno(fp)); 3036 3037 fclose_on_exec(filep); 2611 3038 2612 3039 // If Range: header specified, act accordingly 2613 3040 r1 = r2 = 0; 2614 3041 hdr = mg_get_header(conn, "Range"); 2615 if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0) { 2616 conn->request_info.status_code = 206; 2617 (void) fseeko(fp, (off_t) r1, SEEK_SET); 2618 cl = n == 2 ? r2 - r1 + 1: cl - r1; 2619 (void) mg_snprintf(conn, range, sizeof(range), 2620 "Content-Range: bytes " 2621 "%" INT64_FMT "-%" 2622 INT64_FMT "/%" INT64_FMT "\r\n", 2623 r1, r1 + cl - 1, stp->size); 3042 if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 && 3043 r1 >= 0 && r2 >= 0) { 3044 // actually, range requests don't play well with a pre-gzipped 3045 // file (since the range is specified in the uncmpressed space) 3046 if (filep->gzipped) { 3047 send_http_error(conn, 501, "Not Implemented", "range requests in gzipped files are not supported"); 3048 return; 3049 } 3050 conn->status_code = 206; 3051 cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1; 3052 mg_snprintf(conn, range, sizeof(range), 3053 "Content-Range: bytes " 3054 "%" INT64_FMT "-%" 3055 INT64_FMT "/%" INT64_FMT "\r\n", 3056 r1, r1 + cl - 1, filep->size); 2624 3057 msg = "Partial Content"; 2625 3058 } … … 2628 3061 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 2629 3062 gmt_time_string(date, sizeof(date), &curtime); 2630 gmt_time_string(lm, sizeof(lm), &stp->mtime); 2631 (void) mg_snprintf(conn, etag, sizeof(etag), "%lx.%lx", 2632 (unsigned long) stp->mtime, (unsigned long) stp->size); 3063 gmt_time_string(lm, sizeof(lm), &filep->modification_time); 3064 construct_etag(etag, sizeof(etag), filep); 2633 3065 2634 3066 (void) mg_printf(conn, … … 2636 3068 "Date: %s\r\n" 2637 3069 "Last-Modified: %s\r\n" 2638 "Etag: \"%s\"\r\n"3070 "Etag: %s\r\n" 2639 3071 "Content-Type: %.*s\r\n" 2640 3072 "Content-Length: %" INT64_FMT "\r\n" 2641 3073 "Connection: %s\r\n" 2642 3074 "Accept-Ranges: bytes\r\n" 2643 "%s \r\n",2644 conn-> request_info.status_code, msg, date, lm, etag, (int) mime_vec.len,2645 mime_vec.ptr, cl, suggest_connection_header(conn), range );3075 "%s%s\r\n", 3076 conn->status_code, msg, date, lm, etag, (int) mime_vec.len, 3077 mime_vec.ptr, cl, suggest_connection_header(conn), range, encoding); 2646 3078 2647 3079 if (strcmp(conn->request_info.request_method, "HEAD") != 0) { 2648 send_file_data(conn, f p, cl);2649 } 2650 (void) fclose(fp);3080 send_file_data(conn, filep, r1, cl); 3081 } 3082 mg_fclose(filep); 2651 3083 } 2652 3084 2653 3085 void mg_send_file(struct mg_connection *conn, const char *path) { 2654 struct mgstat st;2655 if (mg_stat( path, &st) == 0) {2656 handle_file_request(conn, path, & st);3086 struct file file = STRUCT_FILE_INITIALIZER; 3087 if (mg_stat(conn, path, &file)) { 3088 handle_file_request(conn, path, &file); 2657 3089 } else { 2658 3090 send_http_error(conn, 404, "Not Found", "%s", "File not found"); … … 2679 3111 !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") || 2680 3112 !strcmp(method, "PUT") || !strcmp(method, "DELETE") || 2681 !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND"); 3113 !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND") 3114 || !strcmp(method, "MKCOL") 3115 ; 2682 3116 } 2683 3117 2684 3118 // Parse HTTP request, fill in mg_request_info structure. 2685 static int parse_http_request(char *buf, struct mg_request_info *ri) { 2686 int status = 0; 2687 2688 // RFC says that all initial whitespaces should be ingored 2689 while (*buf != '\0' && isspace(* (unsigned char *) buf)) { 2690 buf++; 2691 } 2692 2693 ri->request_method = skip(&buf, " "); 2694 ri->uri = skip(&buf, " "); 2695 ri->http_version = skip(&buf, "\r\n"); 2696 2697 if (is_valid_http_method(ri->request_method) && 2698 strncmp(ri->http_version, "HTTP/", 5) == 0) { 2699 ri->http_version += 5; // Skip "HTTP/" 2700 parse_http_headers(&buf, ri); 2701 status = 1; 2702 } 2703 2704 return status; 3119 // This function modifies the buffer by NUL-terminating 3120 // HTTP request components, header names and header values. 3121 static int parse_http_message(char *buf, int len, struct mg_request_info *ri) { 3122 int is_request, request_length = get_request_len(buf, len); 3123 if (request_length > 0) { 3124 // Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port 3125 ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL; 3126 ri->num_headers = 0; 3127 3128 buf[request_length - 1] = '\0'; 3129 3130 // RFC says that all initial whitespaces should be ingored 3131 while (*buf != '\0' && isspace(* (unsigned char *) buf)) { 3132 buf++; 3133 } 3134 ri->request_method = skip(&buf, " "); 3135 ri->uri = skip(&buf, " "); 3136 ri->http_version = skip(&buf, "\r\n"); 3137 3138 // HTTP message could be either HTTP request or HTTP response, e.g. 3139 // "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..." 3140 is_request = is_valid_http_method(ri->request_method); 3141 if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) || 3142 (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) { 3143 request_length = -1; 3144 } else { 3145 if (is_request) { 3146 ri->http_version += 5; 3147 } 3148 parse_http_headers(&buf, ri); 3149 } 3150 } 3151 return request_length; 2705 3152 } 2706 3153 … … 2710 3157 // have some data. The length of the data is stored in nread. 2711 3158 // Upon every read operation, increase nread by the number of bytes read. 2712 static int read_request(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int bufsiz,2713 int *nread) {3159 static int read_request(FILE *fp, struct mg_connection *conn, 3160 char *buf, int bufsiz, int *nread) { 2714 3161 int request_len, n = 0; 2715 3162 2716 do { 3163 request_len = get_request_len(buf, *nread); 3164 while (conn->ctx->stop_flag == 0 && 3165 *nread < bufsiz && request_len == 0 && 3166 (n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) { 3167 *nread += n; 3168 assert(*nread <= bufsiz); 2717 3169 request_len = get_request_len(buf, *nread); 2718 if (request_len == 0 && 2719 (n = pull(fp, sock, ssl, buf + *nread, bufsiz - *nread)) > 0) { 2720 *nread += n; 2721 } 2722 } while (*nread < bufsiz && request_len == 0 && n > 0); 2723 2724 return request_len; 3170 } 3171 3172 return request_len <= 0 && n <= 0 ? -1 : request_len; 2725 3173 } 2726 3174 … … 2729 3177 // If the file is found, it's stats is returned in stp. 2730 3178 static int substitute_index_file(struct mg_connection *conn, char *path, 2731 size_t path_len, struct mgstat *stp) {3179 size_t path_len, struct file *filep) { 2732 3180 const char *list = conn->ctx->config[INDEX_FILES]; 2733 struct mgstat st;3181 struct file file = STRUCT_FILE_INITIALIZER; 2734 3182 struct vec filename_vec; 2735 3183 size_t n = strlen(path); … … 2739 3187 // directory separator characters from the end of the path, and 2740 3188 // then append single directory separator character. 2741 while (n > 0 && IS_DIRSEP_CHAR(path[n - 1])) {3189 while (n > 0 && path[n - 1] == '/') { 2742 3190 n--; 2743 3191 } 2744 path[n] = DIRSEP;3192 path[n] = '/'; 2745 3193 2746 3194 // Traverse index files list. For each entry, append it to the given … … 2753 3201 2754 3202 // Prepare full path to the index file 2755 (void)mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);3203 mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1); 2756 3204 2757 3205 // Does it exist? 2758 if (mg_stat( path, &st) == 0) {3206 if (mg_stat(conn, path, &file)) { 2759 3207 // Yes it does, break the loop 2760 * stp = st;3208 *filep = file; 2761 3209 found = 1; 2762 3210 break; … … 2774 3222 // Return True if we should reply 304 Not Modified. 2775 3223 static int is_not_modified(const struct mg_connection *conn, 2776 const struct mgstat *stp) { 3224 const struct file *filep) { 3225 char etag[64]; 2777 3226 const char *ims = mg_get_header(conn, "If-Modified-Since"); 2778 return ims != NULL && stp->mtime <= parse_date_string(ims); 3227 const char *inm = mg_get_header(conn, "If-None-Match"); 3228 construct_etag(etag, sizeof(etag), filep); 3229 return (inm != NULL && !mg_strcasecmp(etag, inm)) || 3230 (ims != NULL && filep->modification_time <= parse_date_string(ims)); 2779 3231 } 2780 3232 2781 3233 static int forward_body_data(struct mg_connection *conn, FILE *fp, 2782 3234 SOCKET sock, SSL *ssl) { 2783 const char *expect, *b uffered;2784 char buf[ BUFSIZ];3235 const char *expect, *body; 3236 char buf[MG_BUF_LEN]; 2785 3237 int to_read, nread, buffered_len, success = 0; 2786 3238 … … 2789 3241 2790 3242 if (conn->content_len == -1) { 2791 send_http_error(conn, 411, "Length Required", " ");3243 send_http_error(conn, 411, "Length Required", "%s", ""); 2792 3244 } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) { 2793 send_http_error(conn, 417, "Expectation Failed", " ");3245 send_http_error(conn, 417, "Expectation Failed", "%s", ""); 2794 3246 } else { 2795 3247 if (expect != NULL) { … … 2797 3249 } 2798 3250 2799 b uffered = conn->buf + conn->request_len;2800 buffered_len = conn->data_len - conn->request_len;3251 body = conn->buf + conn->request_len + conn->consumed_content; 3252 buffered_len = &conn->buf[conn->data_len] - body; 2801 3253 assert(buffered_len >= 0); 2802 3254 assert(conn->consumed_content == 0); … … 2806 3258 buffered_len = (int) conn->content_len; 2807 3259 } 2808 push(fp, sock, ssl, b uffered, (int64_t) buffered_len);3260 push(fp, sock, ssl, body, (int64_t) buffered_len); 2809 3261 conn->consumed_content += buffered_len; 2810 3262 } 2811 3263 3264 nread = 0; 2812 3265 while (conn->consumed_content < conn->content_len) { 2813 3266 to_read = sizeof(buf); … … 2815 3268 to_read = (int) (conn->content_len - conn->consumed_content); 2816 3269 } 2817 nread = pull(NULL, conn ->client.sock, conn->ssl, buf, to_read);3270 nread = pull(NULL, conn, buf, to_read); 2818 3271 if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) { 2819 3272 break; … … 2823 3276 2824 3277 if (conn->consumed_content == conn->content_len) { 2825 success = 1;3278 success = nread >= 0; 2826 3279 } 2827 3280 2828 3281 // Each error code path in this function must send an error 2829 3282 if (!success) { 2830 send_http_error(conn, 577, http_500_error, " ");3283 send_http_error(conn, 577, http_500_error, "%s", ""); 2831 3284 } 2832 3285 } … … 2852 3305 }; 2853 3306 3307 static char *addenv(struct cgi_env_block *block, 3308 PRINTF_FORMAT_STRING(const char *fmt), ...) 3309 PRINTF_ARGS(2, 3); 3310 2854 3311 // Append VARIABLE=VALUE\0 string to the buffer, and add a respective 2855 3312 // pointer into the vars array. … … 2872 3329 2873 3330 // Make sure we do not overflow buffer and the envp array 2874 if (n > 0 && n < space &&3331 if (n > 0 && n + 1 < space && 2875 3332 block->nvars < (int) ARRAY_SIZE(block->vars) - 2) { 2876 3333 // Append a pointer to the added string into the envp array 2877 block->vars[block->nvars++] = block->buf + block->len;3334 block->vars[block->nvars++] = added; 2878 3335 // Bump up used length counter. Include \0 terminator 2879 3336 block->len += n + 1; 3337 } else { 3338 cry(block->conn, "%s: CGI env buffer truncated for [%s]", __func__, fmt); 2880 3339 } 2881 3340 … … 2888 3347 const char *s, *slash; 2889 3348 struct vec var_vec; 2890 char *p, src_addr[ 20];3349 char *p, src_addr[IP_ADDR_STR_LEN]; 2891 3350 int i; 2892 3351 … … 2898 3357 addenv(blk, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); 2899 3358 addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); 3359 addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", mg_version()); 2900 3360 2901 3361 // Prepare the environment block … … 2917 3377 if ((s = strrchr(prog, '/')) == NULL) 2918 3378 s = prog; 2919 addenv(blk, "SCRIPT_NAME=%.*s%s", slash - conn->request_info.uri,3379 addenv(blk, "SCRIPT_NAME=%.*s%s", (int) (slash - conn->request_info.uri), 2920 3380 conn->request_info.uri, s); 2921 3381 … … 2949 3409 if ((s = getenv("SystemDrive")) != NULL) { 2950 3410 addenv(blk, "SystemDrive=%s", s); 3411 } 3412 if ((s = getenv("ProgramFiles")) != NULL) { 3413 addenv(blk, "ProgramFiles=%s", s); 3414 } 3415 if ((s = getenv("ProgramFiles(x86)")) != NULL) { 3416 addenv(blk, "ProgramFiles(x86)=%s", s); 2951 3417 } 2952 3418 #else … … 2980 3446 s = conn->ctx->config[CGI_ENVIRONMENT]; 2981 3447 while ((s = next_option(s, &var_vec, NULL)) != NULL) { 2982 addenv(blk, "%.*s", var_vec.len, var_vec.ptr);3448 addenv(blk, "%.*s", (int) var_vec.len, var_vec.ptr); 2983 3449 } 2984 3450 … … 2992 3458 2993 3459 static void handle_cgi_request(struct mg_connection *conn, const char *prog) { 2994 int headers_len, data_len, i, fd _stdin[2], fd_stdout[2];3460 int headers_len, data_len, i, fdin[2], fdout[2]; 2995 3461 const char *status, *status_text; 2996 3462 char buf[16384], *pbuf, dir[PATH_MAX], *p; 2997 3463 struct mg_request_info ri; 2998 3464 struct cgi_env_block blk; 2999 FILE *in, *out; 3000 pid_t pid; 3465 FILE *in = NULL, *out = NULL; 3466 struct file fout = STRUCT_FILE_INITIALIZER; 3467 pid_t pid = (pid_t) -1; 3001 3468 3002 3469 prepare_cgi_environment(conn, prog, &blk); … … 3006 3473 // executable program name relative to 'dir'. 3007 3474 (void) mg_snprintf(conn, dir, sizeof(dir), "%s", prog); 3008 if ((p = strrchr(dir, DIRSEP)) != NULL) {3475 if ((p = strrchr(dir, '/')) != NULL) { 3009 3476 *p++ = '\0'; 3010 3477 } else { … … 3013 3480 } 3014 3481 3015 pid = (pid_t) -1; 3016 fd_stdin[0] = fd_stdin[1] = fd_stdout[0] = fd_stdout[1] = -1; 3017 in = out = NULL; 3018 3019 if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) { 3482 if (pipe(fdin) != 0 || pipe(fdout) != 0) { 3020 3483 send_http_error(conn, 500, http_500_error, 3021 3484 "Cannot create CGI pipe: %s", strerror(ERRNO)); 3022 3485 goto done; 3023 } else if ((pid = spawn_process(conn, p, blk.buf, blk.vars, 3024 fd_stdin[0], fd_stdout[1], dir)) == (pid_t) -1) { 3486 } 3487 3488 pid = spawn_process(conn, p, blk.buf, blk.vars, fdin[0], fdout[1], dir); 3489 if (pid == (pid_t) -1) { 3490 send_http_error(conn, 500, http_500_error, 3491 "Cannot spawn CGI process [%s]: %s", prog, strerror(ERRNO)); 3025 3492 goto done; 3026 } else if ((in = fdopen(fd_stdin[1], "wb")) == NULL || 3027 (out = fdopen(fd_stdout[0], "rb")) == NULL) { 3493 } 3494 3495 // Make sure child closes all pipe descriptors. It must dup them to 0,1 3496 set_close_on_exec(fdin[0]); 3497 set_close_on_exec(fdin[1]); 3498 set_close_on_exec(fdout[0]); 3499 set_close_on_exec(fdout[1]); 3500 3501 // Parent closes only one side of the pipes. 3502 // If we don't mark them as closed, close() attempt before 3503 // return from this function throws an exception on Windows. 3504 // Windows does not like when closed descriptor is closed again. 3505 (void) close(fdin[0]); 3506 (void) close(fdout[1]); 3507 fdin[0] = fdout[1] = -1; 3508 3509 3510 if ((in = fdopen(fdin[1], "wb")) == NULL || 3511 (out = fdopen(fdout[0], "rb")) == NULL) { 3028 3512 send_http_error(conn, 500, http_500_error, 3029 3513 "fopen: %s", strerror(ERRNO)); … … 3033 3517 setbuf(in, NULL); 3034 3518 setbuf(out, NULL); 3035 3036 // spawn_process() must close those! 3037 // If we don't mark them as closed, close() attempt before 3038 // return from this function throws an exception on Windows. 3039 // Windows does not like when closed descriptor is closed again. 3040 fd_stdin[0] = fd_stdout[1] = -1; 3519 fout.fp = out; 3041 3520 3042 3521 // Send POST data to the CGI process if needed … … 3045 3524 goto done; 3046 3525 } 3526 3527 // Close so child gets an EOF. 3528 fclose(in); 3529 in = NULL; 3530 fdin[1] = -1; 3047 3531 3048 3532 // Now read CGI reply into a buffer. We need to set correct … … 3051 3535 // HTTP headers. 3052 3536 data_len = 0; 3053 headers_len = read_request(out, INVALID_SOCKET, NULL, 3054 buf, sizeof(buf), &data_len); 3537 headers_len = read_request(out, conn, buf, sizeof(buf), &data_len); 3055 3538 if (headers_len <= 0) { 3056 3539 send_http_error(conn, 500, http_500_error, 3057 "CGI program sent malformed HTTP headers: [%.*s]", 3058 data_len, buf); 3540 "CGI program sent malformed or too big (>%u bytes) " 3541 "HTTP headers: [%.*s]", 3542 (unsigned) sizeof(buf), data_len, buf); 3059 3543 goto done; 3060 3544 } … … 3066 3550 status_text = "OK"; 3067 3551 if ((status = get_header(&ri, "Status")) != NULL) { 3068 conn-> request_info.status_code = atoi(status);3552 conn->status_code = atoi(status); 3069 3553 status_text = status; 3070 3554 while (isdigit(* (unsigned char *) status_text) || *status_text == ' ') { … … 3072 3556 } 3073 3557 } else if (get_header(&ri, "Location") != NULL) { 3074 conn-> request_info.status_code = 302;3558 conn->status_code = 302; 3075 3559 } else { 3076 conn-> request_info.status_code = 200;3560 conn->status_code = 200; 3077 3561 } 3078 3562 if (get_header(&ri, "Connection") != NULL && … … 3080 3564 conn->must_close = 1; 3081 3565 } 3082 (void) mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn-> request_info.status_code,3566 (void) mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, 3083 3567 status_text); 3084 3568 … … 3088 3572 ri.http_headers[i].name, ri.http_headers[i].value); 3089 3573 } 3090 (void)mg_write(conn, "\r\n", 2);3091 3092 // Send chunk of data that may beread after the headers3574 mg_write(conn, "\r\n", 2); 3575 3576 // Send chunk of data that may have been read after the headers 3093 3577 conn->num_bytes_sent += mg_write(conn, buf + headers_len, 3094 3578 (size_t)(data_len - headers_len)); 3095 3579 3096 3580 // Read the rest of CGI output and send to the client 3097 send_file_data(conn, out, INT64_MAX);3581 send_file_data(conn, &fout, 0, INT64_MAX); 3098 3582 3099 3583 done: … … 3101 3585 kill(pid, SIGKILL); 3102 3586 } 3103 if (fd _stdin[0] != -1) {3104 (void) close(fd_stdin[0]);3105 } 3106 if (fd _stdout[1] != -1) {3107 (void) close(fd_stdout[1]);3587 if (fdin[0] != -1) { 3588 close(fdin[0]); 3589 } 3590 if (fdout[1] != -1) { 3591 close(fdout[1]); 3108 3592 } 3109 3593 3110 3594 if (in != NULL) { 3111 (void)fclose(in);3112 } else if (fd _stdin[1] != -1) {3113 (void) close(fd_stdin[1]);3595 fclose(in); 3596 } else if (fdin[1] != -1) { 3597 close(fdin[1]); 3114 3598 } 3115 3599 3116 3600 if (out != NULL) { 3117 (void)fclose(out);3118 } else if (fd _stdout[0] != -1) {3119 (void) close(fd_stdout[0]);3601 fclose(out); 3602 } else if (fdout[0] != -1) { 3603 close(fdout[0]); 3120 3604 } 3121 3605 } … … 3125 3609 // for given path. Return 0 if the path itself is a directory, 3126 3610 // or -1 on error, 1 if OK. 3127 static int put_dir( const char *path) {3611 static int put_dir(struct mg_connection *conn, const char *path) { 3128 3612 char buf[PATH_MAX]; 3129 3613 const char *s, *p; 3130 struct mgstat st;3614 struct file file = STRUCT_FILE_INITIALIZER; 3131 3615 int len, res = 1; 3132 3616 3133 for (s = p = path + 2; (p = strchr(s, DIRSEP)) != NULL; s = ++p) {3617 for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) { 3134 3618 len = p - path; 3135 3619 if (len >= (int) sizeof(buf)) { … … 3142 3626 // Try to create intermediate directory 3143 3627 DEBUG_TRACE(("mkdir(%s)", buf)); 3144 if ( mg_stat(buf, &st) == -1&& mg_mkdir(buf, 0755) != 0) {3628 if (!mg_stat(conn, buf, &file) && mg_mkdir(buf, 0755) != 0) { 3145 3629 res = -1; 3146 3630 break; … … 3156 3640 } 3157 3641 3642 static void mkcol(struct mg_connection *conn, const char *path) { 3643 int rc, body_len; 3644 struct de de; 3645 memset(&de.file, 0, sizeof(de.file)); 3646 mg_stat(conn, path, &de.file); 3647 3648 if(de.file.modification_time) { 3649 send_http_error(conn, 405, "Method Not Allowed", 3650 "mkcol(%s): %s", path, strerror(ERRNO)); 3651 return; 3652 } 3653 3654 body_len = conn->data_len - conn->request_len; 3655 if(body_len > 0) { 3656 send_http_error(conn, 415, "Unsupported media type", 3657 "mkcol(%s): %s", path, strerror(ERRNO)); 3658 return; 3659 } 3660 3661 rc = mg_mkdir(path, 0755); 3662 3663 if (rc == 0) { 3664 conn->status_code = 201; 3665 mg_printf(conn, "HTTP/1.1 %d Created\r\n\r\n", conn->status_code); 3666 } else if (rc == -1) { 3667 if(errno == EEXIST) 3668 send_http_error(conn, 405, "Method Not Allowed", 3669 "mkcol(%s): %s", path, strerror(ERRNO)); 3670 else if(errno == EACCES) 3671 send_http_error(conn, 403, "Forbidden", 3672 "mkcol(%s): %s", path, strerror(ERRNO)); 3673 else if(errno == ENOENT) 3674 send_http_error(conn, 409, "Conflict", 3675 "mkcol(%s): %s", path, strerror(ERRNO)); 3676 else 3677 send_http_error(conn, 500, http_500_error, 3678 "fopen(%s): %s", path, strerror(ERRNO)); 3679 } 3680 } 3681 3158 3682 static void put_file(struct mg_connection *conn, const char *path) { 3159 struct mgstat st;3683 struct file file = STRUCT_FILE_INITIALIZER; 3160 3684 const char *range; 3161 3685 int64_t r1, r2; 3162 FILE *fp;3163 3686 int rc; 3164 3687 3165 conn-> request_info.status_code = mg_stat(path, &st) == 0? 200 : 201;3166 3167 if ((rc = put_dir( path)) == 0) {3168 mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", conn-> request_info.status_code);3688 conn->status_code = mg_stat(conn, path, &file) ? 200 : 201; 3689 3690 if ((rc = put_dir(conn, path)) == 0) { 3691 mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", conn->status_code); 3169 3692 } else if (rc == -1) { 3170 3693 send_http_error(conn, 500, http_500_error, 3171 "put_dir(%s): %s", path, strerror(ERRNO)); 3172 } else if ((fp = mg_fopen(path, "wb+")) == NULL) { 3694 "put_dir(%s): %s", path, strerror(ERRNO)); 3695 } else if (!mg_fopen(conn, path, "wb+", &file) || file.fp == NULL) { 3696 mg_fclose(&file); 3173 3697 send_http_error(conn, 500, http_500_error, 3174 "fopen(%s): %s", path, strerror(ERRNO));3698 "fopen(%s): %s", path, strerror(ERRNO)); 3175 3699 } else { 3176 set_close_on_exec(fileno(fp));3700 fclose_on_exec(&file); 3177 3701 range = mg_get_header(conn, "Content-Range"); 3178 3702 r1 = r2 = 0; 3179 3703 if (range != NULL && parse_range_header(range, &r1, &r2) > 0) { 3180 conn->request_info.status_code = 206; 3181 // TODO(lsm): handle seek error 3182 (void) fseeko(fp, (off_t) r1, SEEK_SET); 3183 } 3184 if (forward_body_data(conn, fp, INVALID_SOCKET, NULL)) 3185 (void) mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", 3186 conn->request_info.status_code); 3187 (void) fclose(fp); 3188 } 3189 } 3190 3191 static void send_ssi_file(struct mg_connection *, const char *, FILE *, int); 3704 conn->status_code = 206; 3705 fseeko(file.fp, r1, SEEK_SET); 3706 } 3707 if (!forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) { 3708 conn->status_code = 500; 3709 } 3710 mg_printf(conn, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", 3711 conn->status_code); 3712 mg_fclose(&file); 3713 } 3714 } 3715 3716 static void send_ssi_file(struct mg_connection *, const char *, 3717 struct file *, int); 3192 3718 3193 3719 static void do_ssi_include(struct mg_connection *conn, const char *ssi, 3194 3720 char *tag, int include_level) { 3195 char file_name[ BUFSIZ], path[PATH_MAX], *p;3196 FILE *fp;3721 char file_name[MG_BUF_LEN], path[PATH_MAX], *p; 3722 struct file file = STRUCT_FILE_INITIALIZER; 3197 3723 3198 3724 // sscanf() is safe here, since send_ssi_file() also uses buffer 3199 // of size BUFSIZ to get the tag. So strlen(tag) is always < BUFSIZ.3725 // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN. 3200 3726 if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) { 3201 3727 // File name is relative to the webserver root 3202 3728 (void) mg_snprintf(conn, path, sizeof(path), "%s%c%s", 3203 conn->ctx->config[DOCUMENT_ROOT], DIRSEP, file_name);3204 } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1) {3729 conn->ctx->config[DOCUMENT_ROOT], '/', file_name); 3730 } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) { 3205 3731 // File name is relative to the webserver working directory 3206 3732 // or it is absolute system path 3207 3733 (void) mg_snprintf(conn, path, sizeof(path), "%s", file_name); 3208 } else if (sscanf(tag, " \"%[^\"]\"", file_name) == 1) { 3734 } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 || 3735 sscanf(tag, " \"%[^\"]\"", file_name) == 1) { 3209 3736 // File name is relative to the currect document 3210 3737 (void) mg_snprintf(conn, path, sizeof(path), "%s", ssi); 3211 if ((p = strrchr(path, DIRSEP)) != NULL) {3738 if ((p = strrchr(path, '/')) != NULL) { 3212 3739 p[1] = '\0'; 3213 3740 } … … 3219 3746 } 3220 3747 3221 if ( (fp = mg_fopen(path, "rb")) == NULL) {3748 if (!mg_fopen(conn, path, "rb", &file)) { 3222 3749 cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s", 3223 3750 tag, path, strerror(ERRNO)); 3224 3751 } else { 3225 set_close_on_exec(fileno(fp));3752 fclose_on_exec(&file); 3226 3753 if (match_prefix(conn->ctx->config[SSI_EXTENSIONS], 3227 3754 strlen(conn->ctx->config[SSI_EXTENSIONS]), path) > 0) { 3228 send_ssi_file(conn, path, fp, include_level + 1);3755 send_ssi_file(conn, path, &file, include_level + 1); 3229 3756 } else { 3230 send_file_data(conn, fp, INT64_MAX);3231 } 3232 (void) fclose(fp);3757 send_file_data(conn, &file, 0, INT64_MAX); 3758 } 3759 mg_fclose(&file); 3233 3760 } 3234 3761 } … … 3236 3763 #if !defined(NO_POPEN) 3237 3764 static void do_ssi_exec(struct mg_connection *conn, char *tag) { 3238 char cmd[ BUFSIZ];3239 FILE *fp;3765 char cmd[MG_BUF_LEN]; 3766 struct file file = STRUCT_FILE_INITIALIZER; 3240 3767 3241 3768 if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) { 3242 3769 cry(conn, "Bad SSI #exec: [%s]", tag); 3243 } else if ((f p = popen(cmd, "r")) == NULL) {3770 } else if ((file.fp = popen(cmd, "r")) == NULL) { 3244 3771 cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO)); 3245 3772 } else { 3246 send_file_data(conn, fp, INT64_MAX);3247 (void) pclose(fp);3773 send_file_data(conn, &file, 0, INT64_MAX); 3774 pclose(file.fp); 3248 3775 } 3249 3776 } 3250 3777 #endif // !NO_POPEN 3251 3778 3779 static int mg_fgetc(struct file *filep, int offset) { 3780 if (filep->membuf != NULL && offset >=0 && offset < filep->size) { 3781 return ((unsigned char *) filep->membuf)[offset]; 3782 } else if (filep->fp != NULL) { 3783 return fgetc(filep->fp); 3784 } else { 3785 return EOF; 3786 } 3787 } 3788 3252 3789 static void send_ssi_file(struct mg_connection *conn, const char *path, 3253 FILE *fp, int include_level) {3254 char buf[ BUFSIZ];3255 int ch, len, in_ssi_tag;3790 struct file *filep, int include_level) { 3791 char buf[MG_BUF_LEN]; 3792 int ch, offset, len, in_ssi_tag; 3256 3793 3257 3794 if (include_level > 10) { … … 3260 3797 } 3261 3798 3262 in_ssi_tag = 0; 3263 len = 0; 3264 3265 while ((ch = fgetc(fp)) != EOF) { 3799 in_ssi_tag = len = offset = 0; 3800 while ((ch = mg_fgetc(filep, offset)) != EOF) { 3266 3801 if (in_ssi_tag && ch == '>') { 3267 3802 in_ssi_tag = 0; … … 3271 3806 if (len < 6 || memcmp(buf, "<!--#", 5) != 0) { 3272 3807 // Not an SSI tag, pass it 3273 (void) mg_write(conn, buf, (size_t) len);3808 (void) mg_write(conn, buf, (size_t) len); 3274 3809 } else { 3275 3810 if (!memcmp(buf + 5, "include", 7)) { … … 3296 3831 in_ssi_tag = 1; 3297 3832 if (len > 0) { 3298 (void) mg_write(conn, buf, (size_t)len);3833 mg_write(conn, buf, (size_t) len); 3299 3834 } 3300 3835 len = 0; … … 3303 3838 buf[len++] = ch & 0xff; 3304 3839 if (len == (int) sizeof(buf)) { 3305 (void) mg_write(conn, buf, (size_t)len);3840 mg_write(conn, buf, (size_t) len); 3306 3841 len = 0; 3307 3842 } … … 3311 3846 // Send the rest of buffered data 3312 3847 if (len > 0) { 3313 (void) mg_write(conn, buf, (size_t)len);3848 mg_write(conn, buf, (size_t) len); 3314 3849 } 3315 3850 } … … 3317 3852 static void handle_ssi_file_request(struct mg_connection *conn, 3318 3853 const char *path) { 3319 FILE *fp;3320 3321 if ( (fp = mg_fopen(path, "rb")) == NULL) {3854 struct file file = STRUCT_FILE_INITIALIZER; 3855 3856 if (!mg_fopen(conn, path, "rb", &file)) { 3322 3857 send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path, 3323 3858 strerror(ERRNO)); 3324 3859 } else { 3325 3860 conn->must_close = 1; 3326 set_close_on_exec(fileno(fp));3861 fclose_on_exec(&file); 3327 3862 mg_printf(conn, "HTTP/1.1 200 OK\r\n" 3328 3863 "Content-Type: text/html\r\nConnection: %s\r\n\r\n", 3329 3864 suggest_connection_header(conn)); 3330 send_ssi_file(conn, path, fp, 0);3331 (void) fclose(fp);3865 send_ssi_file(conn, path, &file, 0); 3866 mg_fclose(&file); 3332 3867 } 3333 3868 } 3334 3869 3335 3870 static void send_options(struct mg_connection *conn) { 3336 conn->request_info.status_code = 200; 3337 3338 (void) mg_printf(conn, 3339 "HTTP/1.1 200 OK\r\n" 3340 "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS\r\n" 3341 "DAV: 1\r\n\r\n"); 3871 conn->status_code = 200; 3872 3873 mg_printf(conn, "%s", "HTTP/1.1 200 OK\r\n" 3874 "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\n" 3875 "DAV: 1\r\n\r\n"); 3342 3876 } 3343 3877 3344 3878 // Writes PROPFIND properties for a collection element 3345 3879 static void print_props(struct mg_connection *conn, const char* uri, 3346 struct mgstat* st) {3880 struct file *filep) { 3347 3881 char mtime[64]; 3348 gmt_time_string(mtime, sizeof(mtime), & st->mtime);3882 gmt_time_string(mtime, sizeof(mtime), &filep->modification_time); 3349 3883 conn->num_bytes_sent += mg_printf(conn, 3350 3884 "<d:response>" … … 3360 3894 "</d:response>\n", 3361 3895 uri, 3362 st->is_directory ? "<d:collection/>" : "",3363 st->size,3896 filep->is_directory ? "<d:collection/>" : "", 3897 filep->size, 3364 3898 mtime); 3365 3899 } … … 3367 3901 static void print_dav_dir_entry(struct de *de, void *data) { 3368 3902 char href[PATH_MAX]; 3903 char href_encoded[PATH_MAX]; 3369 3904 struct mg_connection *conn = (struct mg_connection *) data; 3370 3905 mg_snprintf(conn, href, sizeof(href), "%s%s", 3371 3906 conn->request_info.uri, de->file_name); 3372 print_props(conn, href, &de->st); 3373 } 3374 3375 static void handle_propfind(struct mg_connection *conn, const char* path, 3376 struct mgstat* st) { 3907 mg_url_encode(href, href_encoded, PATH_MAX-1); 3908 print_props(conn, href_encoded, &de->file); 3909 } 3910 3911 static void handle_propfind(struct mg_connection *conn, const char *path, 3912 struct file *filep) { 3377 3913 const char *depth = mg_get_header(conn, "Depth"); 3378 3914 3379 3915 conn->must_close = 1; 3380 conn-> request_info.status_code = 207;3916 conn->status_code = 207; 3381 3917 mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n" 3382 3918 "Connection: close\r\n" … … 3388 3924 3389 3925 // Print properties for the requested resource itself 3390 print_props(conn, conn->request_info.uri, st);3926 print_props(conn, conn->request_info.uri, filep); 3391 3927 3392 3928 // If it is a directory, print directory entries too if Depth is not 0 3393 if ( st->is_directory &&3929 if (filep->is_directory && 3394 3930 !mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes") && 3395 3931 (depth == NULL || strcmp(depth, "0") != 0)) { … … 3398 3934 3399 3935 conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>"); 3936 } 3937 3938 #if defined(USE_WEBSOCKET) 3939 3940 // START OF SHA-1 code 3941 // Copyright(c) By Steve Reid <steve@edmweb.com> 3942 #define SHA1HANDSOFF 3943 #if defined(__sun) 3944 #include "solarisfixes.h" 3945 #endif 3946 3947 union char64long16 { unsigned char c[64]; uint32_t l[16]; }; 3948 3949 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) 3950 3951 static uint32_t blk0(union char64long16 *block, int i) { 3952 // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN 3953 if (!is_big_endian()) { 3954 block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | 3955 (rol(block->l[i], 8) & 0x00FF00FF); 3956 } 3957 return block->l[i]; 3958 } 3959 3960 #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ 3961 ^block->l[(i+2)&15]^block->l[i&15],1)) 3962 #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30); 3963 #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); 3964 #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 3965 #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); 3966 #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 3967 3968 typedef struct { 3969 uint32_t state[5]; 3970 uint32_t count[2]; 3971 unsigned char buffer[64]; 3972 } SHA1_CTX; 3973 3974 static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) { 3975 uint32_t a, b, c, d, e; 3976 union char64long16 block[1]; 3977 3978 memcpy(block, buffer, 64); 3979 a = state[0]; 3980 b = state[1]; 3981 c = state[2]; 3982 d = state[3]; 3983 e = state[4]; 3984 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); 3985 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); 3986 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); 3987 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); 3988 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); 3989 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); 3990 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); 3991 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); 3992 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); 3993 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); 3994 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); 3995 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); 3996 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); 3997 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); 3998 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); 3999 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); 4000 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); 4001 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); 4002 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); 4003 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); 4004 state[0] += a; 4005 state[1] += b; 4006 state[2] += c; 4007 state[3] += d; 4008 state[4] += e; 4009 a = b = c = d = e = 0; 4010 memset(block, '\0', sizeof(block)); 4011 } 4012 4013 static void SHA1Init(SHA1_CTX* context) { 4014 context->state[0] = 0x67452301; 4015 context->state[1] = 0xEFCDAB89; 4016 context->state[2] = 0x98BADCFE; 4017 context->state[3] = 0x10325476; 4018 context->state[4] = 0xC3D2E1F0; 4019 context->count[0] = context->count[1] = 0; 4020 } 4021 4022 static void SHA1Update(SHA1_CTX* context, const unsigned char* data, 4023 uint32_t len) { 4024 uint32_t i, j; 4025 4026 j = context->count[0]; 4027 if ((context->count[0] += len << 3) < j) 4028 context->count[1]++; 4029 context->count[1] += (len>>29); 4030 j = (j >> 3) & 63; 4031 if ((j + len) > 63) { 4032 memcpy(&context->buffer[j], data, (i = 64-j)); 4033 SHA1Transform(context->state, context->buffer); 4034 for ( ; i + 63 < len; i += 64) { 4035 SHA1Transform(context->state, &data[i]); 4036 } 4037 j = 0; 4038 } 4039 else i = 0; 4040 memcpy(&context->buffer[j], &data[i], len - i); 4041 } 4042 4043 static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { 4044 unsigned i; 4045 unsigned char finalcount[8], c; 4046 4047 for (i = 0; i < 8; i++) { 4048 finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] 4049 >> ((3-(i & 3)) * 8) ) & 255); 4050 } 4051 c = 0200; 4052 SHA1Update(context, &c, 1); 4053 while ((context->count[0] & 504) != 448) { 4054 c = 0000; 4055 SHA1Update(context, &c, 1); 4056 } 4057 SHA1Update(context, finalcount, 8); 4058 for (i = 0; i < 20; i++) { 4059 digest[i] = (unsigned char) 4060 ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); 4061 } 4062 memset(context, '\0', sizeof(*context)); 4063 memset(&finalcount, '\0', sizeof(finalcount)); 4064 } 4065 // END OF SHA1 CODE 4066 4067 static void base64_encode(const unsigned char *src, int src_len, char *dst) { 4068 static const char *b64 = 4069 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 4070 int i, j, a, b, c; 4071 4072 for (i = j = 0; i < src_len; i += 3) { 4073 a = src[i]; 4074 b = i + 1 >= src_len ? 0 : src[i + 1]; 4075 c = i + 2 >= src_len ? 0 : src[i + 2]; 4076 4077 dst[j++] = b64[a >> 2]; 4078 dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; 4079 if (i + 1 < src_len) { 4080 dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; 4081 } 4082 if (i + 2 < src_len) { 4083 dst[j++] = b64[c & 63]; 4084 } 4085 } 4086 while (j % 4 != 0) { 4087 dst[j++] = '='; 4088 } 4089 dst[j++] = '\0'; 4090 } 4091 4092 static void send_websocket_handshake(struct mg_connection *conn) { 4093 static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 4094 char buf[100], sha[20], b64_sha[sizeof(sha) * 2]; 4095 SHA1_CTX sha_ctx; 4096 4097 mg_snprintf(conn, buf, sizeof(buf), "%s%s", 4098 mg_get_header(conn, "Sec-WebSocket-Key"), magic); 4099 SHA1Init(&sha_ctx); 4100 SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf)); 4101 SHA1Final((unsigned char *) sha, &sha_ctx); 4102 base64_encode((unsigned char *) sha, sizeof(sha), b64_sha); 4103 mg_printf(conn, "%s%s%s", 4104 "HTTP/1.1 101 Switching Protocols\r\n" 4105 "Upgrade: websocket\r\n" 4106 "Connection: Upgrade\r\n" 4107 "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n"); 4108 } 4109 4110 static void read_websocket(struct mg_connection *conn) { 4111 // Pointer to the beginning of the portion of the incoming websocket message 4112 // queue. The original websocket upgrade request is never removed, 4113 // so the queue begins after it. 4114 unsigned char *buf = (unsigned char *) conn->buf + conn->request_len; 4115 int bits, n, stop = 0; 4116 size_t i, len, mask_len, data_len, header_len, body_len; 4117 // data points to the place where the message is stored when passed to the 4118 // websocket_data callback. This is either mem on the stack, 4119 // or a dynamically allocated buffer if it is too large. 4120 char mem[4 * 1024], mask[4], *data; 4121 4122 assert(conn->content_len == 0); 4123 4124 // Loop continuously, reading messages from the socket, invoking the callback, 4125 // and waiting repeatedly until an error occurs. 4126 while (!stop) { 4127 header_len = 0; 4128 // body_len is the length of the entire queue in bytes 4129 // len is the length of the current message 4130 // data_len is the length of the current message's data payload 4131 // header_len is the length of the current message's header 4132 if ((body_len = conn->data_len - conn->request_len) >= 2) { 4133 len = buf[1] & 127; 4134 mask_len = buf[1] & 128 ? 4 : 0; 4135 if (len < 126 && body_len >= mask_len) { 4136 data_len = len; 4137 header_len = 2 + mask_len; 4138 } else if (len == 126 && body_len >= 4 + mask_len) { 4139 header_len = 4 + mask_len; 4140 data_len = ((((int) buf[2]) << 8) + buf[3]); 4141 } else if (body_len >= 10 + mask_len) { 4142 header_len = 10 + mask_len; 4143 data_len = (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) + 4144 htonl(* (uint32_t *) &buf[6]); 4145 } 4146 } 4147 4148 // Data layout is as follows: 4149 // conn->buf buf 4150 // v v frame1 | frame2 4151 // |---------------------|----------------|--------------|------- 4152 // | |<--header_len-->|<--data_len-->| 4153 // |<-conn->request_len->|<-----body_len----------->| 4154 // |<-------------------conn->data_len------------->| 4155 4156 if (header_len > 0) { 4157 // Allocate space to hold websocket payload 4158 data = mem; 4159 if (data_len > sizeof(mem) && (data = malloc(data_len)) == NULL) { 4160 // Allocation failed, exit the loop and then close the connection 4161 // TODO: notify user about the failure 4162 break; 4163 } 4164 4165 // Save mask and bits, otherwise it may be clobbered by memmove below 4166 bits = buf[0]; 4167 memcpy(mask, buf + header_len - mask_len, mask_len); 4168 4169 // Read frame payload into the allocated buffer. 4170 assert(body_len >= header_len); 4171 if (data_len + header_len > body_len) { 4172 len = body_len - header_len; 4173 memcpy(data, buf + header_len, len); 4174 // TODO: handle pull error 4175 pull_all(NULL, conn, data + len, data_len - len); 4176 conn->data_len = conn->request_len; 4177 } else { 4178 len = data_len + header_len; 4179 memcpy(data, buf + header_len, data_len); 4180 memmove(buf, buf + len, body_len - len); 4181 conn->data_len -= len; 4182 } 4183 4184 // Apply mask if necessary 4185 if (mask_len > 0) { 4186 for (i = 0; i < data_len; i++) { 4187 data[i] ^= mask[i % 4]; 4188 } 4189 } 4190 4191 // Exit the loop if callback signalled to exit, 4192 // or "connection close" opcode received. 4193 if ((bits & WEBSOCKET_OPCODE_CONNECTION_CLOSE) || 4194 (conn->ctx->callbacks.websocket_data != NULL && 4195 !conn->ctx->callbacks.websocket_data(conn, bits, data, data_len))) { 4196 stop = 1; 4197 } 4198 4199 if (data != mem) { 4200 free(data); 4201 } 4202 // Not breaking the loop, process next websocket frame. 4203 } else { 4204 // Buffering websocket request 4205 if ((n = pull(NULL, conn, conn->buf + conn->data_len, 4206 conn->buf_size - conn->data_len)) <= 0) { 4207 break; 4208 } 4209 conn->data_len += n; 4210 } 4211 } 4212 } 4213 4214 int mg_websocket_write(struct mg_connection* conn, int opcode, 4215 const char *data, size_t data_len) { 4216 unsigned char *copy; 4217 size_t copy_len = 0; 4218 int retval = -1; 4219 4220 if ((copy = (unsigned char *) malloc(data_len + 10)) == NULL) { 4221 return -1; 4222 } 4223 4224 copy[0] = 0x80 + (opcode & 0x0f); 4225 4226 // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 4227 if (data_len < 126) { 4228 // Inline 7-bit length field 4229 copy[1] = data_len; 4230 memcpy(copy + 2, data, data_len); 4231 copy_len = 2 + data_len; 4232 } else if (data_len <= 0xFFFF) { 4233 // 16-bit length field 4234 copy[1] = 126; 4235 * (uint16_t *) (copy + 2) = htons(data_len); 4236 memcpy(copy + 4, data, data_len); 4237 copy_len = 4 + data_len; 4238 } else { 4239 // 64-bit length field 4240 copy[1] = 127; 4241 * (uint32_t *) (copy + 2) = htonl((uint64_t) data_len >> 32); 4242 * (uint32_t *) (copy + 6) = htonl(data_len & 0xffffffff); 4243 memcpy(copy + 10, data, data_len); 4244 copy_len = 10 + data_len; 4245 } 4246 4247 // Not thread safe 4248 if (copy_len > 0) { 4249 retval = mg_write(conn, copy, copy_len); 4250 } 4251 free(copy); 4252 4253 return retval; 4254 } 4255 4256 static void handle_websocket_request(struct mg_connection *conn) { 4257 const char *version = mg_get_header(conn, "Sec-WebSocket-Version"); 4258 if (version == NULL || strcmp(version, "13") != 0) { 4259 send_http_error(conn, 426, "Upgrade Required", "%s", "Upgrade Required"); 4260 } else if (conn->ctx->callbacks.websocket_connect != NULL && 4261 conn->ctx->callbacks.websocket_connect(conn) != 0) { 4262 // Callback has returned non-zero, do not proceed with handshake 4263 } else { 4264 send_websocket_handshake(conn); 4265 if (conn->ctx->callbacks.websocket_ready != NULL) { 4266 conn->ctx->callbacks.websocket_ready(conn); 4267 } 4268 read_websocket(conn); 4269 } 4270 } 4271 4272 static int is_websocket_request(const struct mg_connection *conn) { 4273 const char *host, *upgrade, *connection, *version, *key; 4274 4275 host = mg_get_header(conn, "Host"); 4276 upgrade = mg_get_header(conn, "Upgrade"); 4277 connection = mg_get_header(conn, "Connection"); 4278 key = mg_get_header(conn, "Sec-WebSocket-Key"); 4279 version = mg_get_header(conn, "Sec-WebSocket-Version"); 4280 4281 return host != NULL && upgrade != NULL && connection != NULL && 4282 key != NULL && version != NULL && 4283 mg_strcasestr(upgrade, "websocket") != NULL && 4284 mg_strcasestr(connection, "Upgrade") != NULL; 4285 } 4286 #endif // !USE_WEBSOCKET 4287 4288 static int isbyte(int n) { 4289 return n >= 0 && n <= 255; 4290 } 4291 4292 static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { 4293 int n, a, b, c, d, slash = 32, len = 0; 4294 4295 if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 || 4296 sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && 4297 isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && 4298 slash >= 0 && slash < 33) { 4299 len = n; 4300 *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | d; 4301 *mask = slash ? 0xffffffffU << (32 - slash) : 0; 4302 } 4303 4304 return len; 4305 } 4306 4307 static int set_throttle(const char *spec, uint32_t remote_ip, const char *uri) { 4308 int throttle = 0; 4309 struct vec vec, val; 4310 uint32_t net, mask; 4311 char mult; 4312 double v; 4313 4314 while ((spec = next_option(spec, &vec, &val)) != NULL) { 4315 mult = ','; 4316 if (sscanf(val.ptr, "%lf%c", &v, &mult) < 1 || v < 0 || 4317 (lowercase(&mult) != 'k' && lowercase(&mult) != 'm' && mult != ',')) { 4318 continue; 4319 } 4320 v *= lowercase(&mult) == 'k' ? 1024 : lowercase(&mult) == 'm' ? 1048576 : 1; 4321 if (vec.len == 1 && vec.ptr[0] == '*') { 4322 throttle = (int) v; 4323 } else if (parse_net(vec.ptr, &net, &mask) > 0) { 4324 if ((remote_ip & mask) == net) { 4325 throttle = (int) v; 4326 } 4327 } else if (match_prefix(vec.ptr, vec.len, uri) > 0) { 4328 throttle = (int) v; 4329 } 4330 } 4331 4332 return throttle; 4333 } 4334 4335 static uint32_t get_remote_ip(const struct mg_connection *conn) { 4336 return ntohl(* (uint32_t *) &conn->client.rsa.sin.sin_addr); 4337 } 4338 4339 #ifdef USE_LUA 4340 #include "mod_lua.c" 4341 #endif // USE_LUA 4342 4343 int mg_upload(struct mg_connection *conn, const char *destination_dir) { 4344 const char *content_type_header, *boundary_start; 4345 char buf[MG_BUF_LEN], path[PATH_MAX], fname[1024], boundary[100], *s; 4346 FILE *fp; 4347 int bl, n, i, j, headers_len, boundary_len, eof, 4348 len = 0, num_uploaded_files = 0; 4349 4350 // Request looks like this: 4351 // 4352 // POST /upload HTTP/1.1 4353 // Host: 127.0.0.1:8080 4354 // Content-Length: 244894 4355 // Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryRVr 4356 // 4357 // ------WebKitFormBoundaryRVr 4358 // Content-Disposition: form-data; name="file"; filename="accum.png" 4359 // Content-Type: image/png 4360 // 4361 // <89>PNG 4362 // <PNG DATA> 4363 // ------WebKitFormBoundaryRVr 4364 4365 // Extract boundary string from the Content-Type header 4366 if ((content_type_header = mg_get_header(conn, "Content-Type")) == NULL || 4367 (boundary_start = mg_strcasestr(content_type_header, 4368 "boundary=")) == NULL || 4369 (sscanf(boundary_start, "boundary=\"%99[^\"]\"", boundary) == 0 && 4370 sscanf(boundary_start, "boundary=%99s", boundary) == 0) || 4371 boundary[0] == '\0') { 4372 return num_uploaded_files; 4373 } 4374 4375 boundary_len = strlen(boundary); 4376 bl = boundary_len + 4; // \r\n--<boundary> 4377 for (;;) { 4378 // Pull in headers 4379 assert(len >= 0 && len <= (int) sizeof(buf)); 4380 while ((n = mg_read(conn, buf + len, sizeof(buf) - len)) > 0) { 4381 len += n; 4382 } 4383 if ((headers_len = get_request_len(buf, len)) <= 0) { 4384 break; 4385 } 4386 4387 // Fetch file name. 4388 fname[0] = '\0'; 4389 for (i = j = 0; i < headers_len; i++) { 4390 if (buf[i] == '\r' && buf[i + 1] == '\n') { 4391 buf[i] = buf[i + 1] = '\0'; 4392 // TODO(lsm): don't expect filename to be the 3rd field, 4393 // parse the header properly instead. 4394 sscanf(&buf[j], "Content-Disposition: %*s %*s filename=\"%1023[^\"]", 4395 fname); 4396 j = i + 2; 4397 } 4398 } 4399 4400 // Give up if the headers are not what we expect 4401 if (fname[0] == '\0') { 4402 break; 4403 } 4404 4405 // Move data to the beginning of the buffer 4406 assert(len >= headers_len); 4407 memmove(buf, &buf[headers_len], len - headers_len); 4408 len -= headers_len; 4409 4410 // We open the file with exclusive lock held. This guarantee us 4411 // there is no other thread can save into the same file simultaneously. 4412 fp = NULL; 4413 // Construct destination file name. Do not allow paths to have slashes. 4414 if ((s = strrchr(fname, '/')) == NULL && 4415 (s = strrchr(fname, '\\')) == NULL) { 4416 s = fname; 4417 } 4418 4419 // Open file in binary mode. TODO: set an exclusive lock. 4420 snprintf(path, sizeof(path), "%s/%s", destination_dir, s); 4421 if ((fp = fopen(path, "wb")) == NULL) { 4422 break; 4423 } 4424 4425 // Read POST data, write into file until boundary is found. 4426 eof = n = 0; 4427 do { 4428 len += n; 4429 for (i = 0; i < len - bl; i++) { 4430 if (!memcmp(&buf[i], "\r\n--", 4) && 4431 !memcmp(&buf[i + 4], boundary, boundary_len)) { 4432 // Found boundary, that's the end of file data. 4433 fwrite(buf, 1, i, fp); 4434 eof = 1; 4435 memmove(buf, &buf[i + bl], len - (i + bl)); 4436 len -= i + bl; 4437 break; 4438 } 4439 } 4440 if (!eof && len > bl) { 4441 fwrite(buf, 1, len - bl, fp); 4442 memmove(buf, &buf[len - bl], bl); 4443 len = bl; 4444 } 4445 } while (!eof && (n = mg_read(conn, buf + len, sizeof(buf) - len)) > 0); 4446 fclose(fp); 4447 if (eof) { 4448 num_uploaded_files++; 4449 if (conn->ctx->callbacks.upload != NULL) { 4450 conn->ctx->callbacks.upload(conn, path); 4451 } 4452 } 4453 } 4454 4455 return num_uploaded_files; 4456 } 4457 4458 static int is_put_or_delete_request(const struct mg_connection *conn) { 4459 const char *s = conn->request_info.request_method; 4460 return s != NULL && (!strcmp(s, "PUT") || 4461 !strcmp(s, "DELETE") || 4462 !strcmp(s, "MKCOL")); 4463 } 4464 4465 static int get_first_ssl_listener_index(const struct mg_context *ctx) { 4466 int i, index = -1; 4467 for (i = 0; index == -1 && i < ctx->num_listening_sockets; i++) { 4468 index = ctx->listening_sockets[i].is_ssl ? i : -1; 4469 } 4470 return index; 4471 } 4472 4473 static void redirect_to_https_port(struct mg_connection *conn, int ssl_index) { 4474 char host[1025]; 4475 const char *host_header; 4476 4477 if ((host_header = mg_get_header(conn, "Host")) == NULL || 4478 sscanf(host_header, "%1024[^:]", host) == 0) { 4479 // Cannot get host from the Host: header. Fallback to our IP address. 4480 sockaddr_to_string(host, sizeof(host), &conn->client.lsa); 4481 } 4482 4483 mg_printf(conn, "HTTP/1.1 302 Found\r\nLocation: https://%s:%d%s\r\n\r\n", 4484 host, (int) ntohs(conn->ctx->listening_sockets[ssl_index]. 4485 lsa.sin.sin_port), conn->request_info.uri); 3400 4486 } 3401 4487 … … 3407 4493 struct mg_request_info *ri = &conn->request_info; 3408 4494 char path[PATH_MAX]; 3409 int stat_result, uri_len;3410 struct mgstat st;4495 int uri_len, ssl_index; 4496 struct file file = STRUCT_FILE_INITIALIZER; 3411 4497 3412 4498 if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) { 3413 * conn->request_info.query_string++ = '\0'; 3414 } 3415 uri_len = strlen(ri->uri); 3416 url_decode(ri->uri, (size_t)uri_len, ri->uri, (size_t)(uri_len + 1), 0); 3417 remove_double_dots_and_double_slashes(ri->uri); 3418 stat_result = convert_uri_to_file_name(conn, path, sizeof(path), &st); 4499 * ((char *) conn->request_info.query_string++) = '\0'; 4500 } 4501 uri_len = (int) strlen(ri->uri); 4502 mg_url_decode(ri->uri, uri_len, (char *) ri->uri, uri_len + 1, 0); 4503 remove_double_dots_and_double_slashes((char *) ri->uri); 4504 convert_uri_to_file_name(conn, path, sizeof(path), &file); 4505 conn->throttle = set_throttle(conn->ctx->config[THROTTLE], 4506 get_remote_ip(conn), ri->uri); 3419 4507 3420 4508 DEBUG_TRACE(("%s", ri->uri)); 3421 if (!check_authorization(conn, path)) { 4509 // Perform redirect and auth checks before calling begin_request() handler. 4510 // Otherwise, begin_request() would need to perform auth checks and redirects. 4511 if (!conn->client.is_ssl && conn->client.ssl_redir && 4512 (ssl_index = get_first_ssl_listener_index(conn->ctx)) > -1) { 4513 redirect_to_https_port(conn, ssl_index); 4514 } else if (!is_put_or_delete_request(conn) && 4515 !check_authorization(conn, path)) { 3422 4516 send_authorization_request(conn); 3423 } else if (call_user(conn, MG_NEW_REQUEST) != NULL) { 4517 } else if (conn->ctx->callbacks.begin_request != NULL && 4518 conn->ctx->callbacks.begin_request(conn)) { 3424 4519 // Do nothing, callback has served the request 4520 #if defined(USE_WEBSOCKET) 4521 } else if (is_websocket_request(conn)) { 4522 handle_websocket_request(conn); 4523 #endif 3425 4524 } else if (!strcmp(ri->request_method, "OPTIONS")) { 3426 4525 send_options(conn); 3427 } else if (strstr(path, PASSWORDS_FILE_NAME)) {3428 // Do not allow to view passwords files3429 send_http_error(conn, 403, "Forbidden", "Access Forbidden");3430 4526 } else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) { 3431 4527 send_http_error(conn, 404, "Not Found", "Not Found"); 3432 } else if ((!strcmp(ri->request_method, "PUT") || 3433 !strcmp(ri->request_method, "DELETE")) && 3434 (conn->ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL || 3435 !is_authorized_for_put(conn))) { 4528 } else if (is_put_or_delete_request(conn) && 4529 (is_authorized_for_put(conn) != 1)) { 3436 4530 send_authorization_request(conn); 3437 4531 } else if (!strcmp(ri->request_method, "PUT")) { 3438 4532 put_file(conn, path); 4533 } else if (!strcmp(ri->request_method, "MKCOL")) { 4534 mkcol(conn, path); 3439 4535 } else if (!strcmp(ri->request_method, "DELETE")) { 3440 if (mg_remove(path) == 0) { 3441 send_http_error(conn, 200, "OK", ""); 3442 } else { 3443 send_http_error(conn, 500, http_500_error, "remove(%s): %s", path, 3444 strerror(ERRNO)); 3445 } 3446 } else if (stat_result != 0) { 4536 struct de de; 4537 memset(&de.file, 0, sizeof(de.file)); 4538 if(!mg_stat(conn, path, &de.file)) { 4539 send_http_error(conn, 404, "Not Found", "%s", "File not found"); 4540 } else { 4541 if(de.file.modification_time) { 4542 if(de.file.is_directory) { 4543 remove_directory(conn, path); 4544 send_http_error(conn, 204, "No Content", "%s", ""); 4545 } else if (mg_remove(path) == 0) { 4546 send_http_error(conn, 204, "No Content", "%s", ""); 4547 } else { 4548 send_http_error(conn, 423, "Locked", "remove(%s): %s", path, 4549 strerror(ERRNO)); 4550 } 4551 } 4552 else { 4553 send_http_error(conn, 500, http_500_error, "remove(%s): %s", path, 4554 strerror(ERRNO)); 4555 } 4556 } 4557 } else if ((file.membuf == NULL && file.modification_time == (time_t) 0) || 4558 must_hide_file(conn, path)) { 3447 4559 send_http_error(conn, 404, "Not Found", "%s", "File not found"); 3448 } else if (st.is_directory && ri->uri[uri_len - 1] != '/') { 3449 (void) mg_printf(conn, 3450 "HTTP/1.1 301 Moved Permanently\r\n" 3451 "Location: %s/\r\n\r\n", ri->uri); 4560 } else if (file.is_directory && ri->uri[uri_len - 1] != '/') { 4561 mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n" 4562 "Location: %s/\r\n\r\n", ri->uri); 3452 4563 } else if (!strcmp(ri->request_method, "PROPFIND")) { 3453 handle_propfind(conn, path, & st);3454 } else if ( st.is_directory &&3455 !substitute_index_file(conn, path, sizeof(path), & st)) {4564 handle_propfind(conn, path, &file); 4565 } else if (file.is_directory && 4566 !substitute_index_file(conn, path, sizeof(path), &file)) { 3456 4567 if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")) { 3457 4568 handle_directory_request(conn, path); … … 3460 4571 "Directory listing denied"); 3461 4572 } 4573 #ifdef USE_LUA 4574 } else if (match_prefix("**.lp$", 6, path) > 0) { 4575 handle_lsp_request(conn, path, &file, NULL); 4576 #endif 3462 4577 #if !defined(NO_CGI) 3463 4578 } else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS], … … 3465 4580 path) > 0) { 3466 4581 if (strcmp(ri->request_method, "POST") && 4582 strcmp(ri->request_method, "HEAD") && 3467 4583 strcmp(ri->request_method, "GET")) { 3468 4584 send_http_error(conn, 501, "Not Implemented", … … 3476 4592 path) > 0) { 3477 4593 handle_ssi_file_request(conn, path); 3478 } else if (is_not_modified(conn, & st)) {3479 send_http_error(conn, 304, "Not Modified", " ");4594 } else if (is_not_modified(conn, &file)) { 4595 send_http_error(conn, 304, "Not Modified", "%s", ""); 3480 4596 } else { 3481 handle_file_request(conn, path, & st);4597 handle_file_request(conn, path, &file); 3482 4598 } 3483 4599 } 3484 4600 3485 4601 static void close_all_listening_sockets(struct mg_context *ctx) { 3486 struct socket *sp, *tmp; 3487 for (sp = ctx->listening_sockets; sp != NULL; sp = tmp) { 3488 tmp = sp->next; 3489 (void) closesocket(sp->sock); 3490 free(sp); 3491 } 4602 int i; 4603 for (i = 0; i < ctx->num_listening_sockets; i++) { 4604 closesocket(ctx->listening_sockets[i].sock); 4605 } 4606 free(ctx->listening_sockets); 4607 } 4608 4609 static int is_valid_port(unsigned int port) { 4610 return port > 0 && port < 0xffff; 3492 4611 } 3493 4612 3494 4613 // Valid listening port specification is: [ip_address:]port[s] 3495 // Examples: 80, 443s, 127.0.0.1:3128, 1.2.3.4:8080s4614 // Examples: 80, 443s, 127.0.0.1:3128, 1.2.3.4:8080s 3496 4615 // TODO(lsm): add parsing of the IPv6 address 3497 4616 static int parse_port_string(const struct vec *vec, struct socket *so) { 3498 int a, b, c, d, port, len; 4617 unsigned int a, b, c, d, ch, len, port; 4618 #if defined(USE_IPV6) 4619 char buf[100]; 4620 #endif 3499 4621 3500 4622 // MacOS needs that. If we do not zero it, subsequent bind() will fail. … … 3502 4624 // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). 3503 4625 memset(so, 0, sizeof(*so)); 3504 3505 if (sscanf(vec->ptr, "%d.%d.%d.%d:%d%n", &a, &b, &c, &d, &port, &len) == 5) { 3506 // Bind to a specific IPv4 address 4626 so->lsa.sin.sin_family = AF_INET; 4627 4628 if (sscanf(vec->ptr, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) { 4629 // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 3507 4630 so->lsa.sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d); 3508 } else if (sscanf(vec->ptr, "%d%n", &port, &len) != 1 || 3509 len <= 0 || 3510 len > (int) vec->len || 3511 (vec->ptr[len] && vec->ptr[len] != 's' && vec->ptr[len] != ',')) { 3512 return 0; 3513 } 3514 3515 so->is_ssl = vec->ptr[len] == 's'; 4631 so->lsa.sin.sin_port = htons((uint16_t) port); 3516 4632 #if defined(USE_IPV6) 3517 so->lsa.sin6.sin6_family = AF_INET6; 3518 so->lsa.sin6.sin6_port = htons((uint16_t) port); 3519 #else 3520 so->lsa.sin.sin_family = AF_INET; 3521 so->lsa.sin.sin_port = htons((uint16_t) port); 4633 4634 } else if (sscanf(vec->ptr, "[%49[^]]]:%d%n", buf, &port, &len) == 2 && 4635 inet_pton(AF_INET6, buf, &so->lsa.sin6.sin6_addr)) { 4636 // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080 4637 so->lsa.sin6.sin6_family = AF_INET6; 4638 so->lsa.sin6.sin6_port = htons((uint16_t) port); 3522 4639 #endif 3523 3524 return 1; 4640 } else if (sscanf(vec->ptr, "%u%n", &port, &len) == 1) { 4641 // If only port is specified, bind to IPv4, INADDR_ANY 4642 so->lsa.sin.sin_port = htons((uint16_t) port); 4643 } else { 4644 port = len = 0; // Parsing failure. Make port invalid. 4645 } 4646 4647 ch = vec->ptr[len]; // Next character after the port number 4648 so->is_ssl = ch == 's'; 4649 so->ssl_redir = ch == 'r'; 4650 4651 // Make sure the port is valid and vector ends with 's', 'r' or ',' 4652 return is_valid_port(port) && 4653 (ch == '\0' || ch == 's' || ch == 'r' || ch == ','); 3525 4654 } 3526 4655 … … 3528 4657 const char *list = ctx->config[LISTENING_PORTS]; 3529 4658 int on = 1, success = 1; 3530 SOCKET sock; 4659 #if defined(USE_IPV6) 4660 int off = 0; 4661 #endif 3531 4662 struct vec vec; 3532 struct socket so, * listener;4663 struct socket so, *ptr; 3533 4664 3534 4665 while (success && (list = next_option(list, &vec, NULL)) != NULL) { 3535 4666 if (!parse_port_string(&vec, &so)) { 3536 4667 cry(fc(ctx), "%s: %.*s: invalid port spec. Expecting list of: %s", 3537 __func__, vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|p]");4668 __func__, (int) vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|r]"); 3538 4669 success = 0; 3539 4670 } else if (so.is_ssl && ctx->ssl_ctx == NULL) { 3540 4671 cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?"); 3541 4672 success = 0; 3542 } else if ((so ck = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==4673 } else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) == 3543 4674 INVALID_SOCKET || 3544 #if !defined(_WIN32)3545 4675 // On Windows, SO_REUSEADDR is recommended only for 3546 4676 // broadcast UDP sockets 3547 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, 3548 sizeof(on)) != 0 || 3549 #endif // !_WIN32 3550 // Set TCP keep-alive. This is needed because if HTTP-level 3551 // keep-alive is enabled, and client resets the connection, 3552 // server won't get TCP FIN or RST and will keep the connection 3553 // open forever. With TCP keep-alive, next keep-alive 3554 // handshake will figure out that the client is down and 3555 // will close the server end. 3556 // Thanks to Igor Klopov who suggested the patch. 3557 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &on, 3558 sizeof(on)) != 0 || 3559 bind(sock, &so.lsa.sa, sizeof(so.lsa)) != 0 || 3560 listen(sock, 100) != 0) { 3561 closesocket(sock); 3562 cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__, 3563 vec.len, vec.ptr, strerror(ERRNO)); 4677 setsockopt(so.sock, SOL_SOCKET, SO_REUSEADDR, 4678 (void *) &on, sizeof(on)) != 0 || 4679 #if defined(USE_IPV6) 4680 (so.lsa.sa.sa_family == AF_INET6 && 4681 setsockopt(so.sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &off, 4682 sizeof(off)) != 0) || 4683 #endif 4684 bind(so.sock, &so.lsa.sa, so.lsa.sa.sa_family == AF_INET ? 4685 sizeof(so.lsa.sin) : sizeof(so.lsa)) != 0 || 4686 listen(so.sock, SOMAXCONN) != 0) { 4687 cry(fc(ctx), "%s: cannot bind to %.*s: %d (%s)", __func__, 4688 (int) vec.len, vec.ptr, ERRNO, strerror(errno)); 4689 closesocket(so.sock); 3564 4690 success = 0; 3565 } else if (( listener = (struct socket *)3566 calloc(1, sizeof(*listener))) == NULL) {3567 closesocket(sock);3568 c ry(fc(ctx), "%s: %s", __func__, strerror(ERRNO));4691 } else if ((ptr = (struct socket *) realloc(ctx->listening_sockets, 4692 (ctx->num_listening_sockets + 1) * 4693 sizeof(ctx->listening_sockets[0]))) == NULL) { 4694 closesocket(so.sock); 3569 4695 success = 0; 3570 4696 } else { 3571 *listener = so; 3572 listener->sock = sock; 3573 set_close_on_exec(listener->sock); 3574 listener->next = ctx->listening_sockets; 3575 ctx->listening_sockets = listener; 4697 set_close_on_exec(so.sock); 4698 ctx->listening_sockets = ptr; 4699 ctx->listening_sockets[ctx->num_listening_sockets] = so; 4700 ctx->num_listening_sockets++; 3576 4701 } 3577 4702 } … … 3598 4723 const struct mg_request_info *ri; 3599 4724 FILE *fp; 3600 char date[64], src_addr[ 20];4725 char date[64], src_addr[IP_ADDR_STR_LEN]; 3601 4726 3602 4727 fp = conn->ctx->config[ACCESS_LOG_FILE] == NULL ? NULL : 3603 mg_fopen(conn->ctx->config[ACCESS_LOG_FILE], "a+");4728 fopen(conn->ctx->config[ACCESS_LOG_FILE], "a+"); 3604 4729 3605 4730 if (fp == NULL) … … 3617 4742 ri->request_method ? ri->request_method : "-", 3618 4743 ri->uri ? ri->uri : "-", ri->http_version, 3619 conn-> request_info.status_code, conn->num_bytes_sent);4744 conn->status_code, conn->num_bytes_sent); 3620 4745 log_header(conn, "Referer", fp); 3621 4746 log_header(conn, "User-Agent", fp); … … 3627 4752 } 3628 4753 3629 static int isbyte(int n) {3630 return n >= 0 && n <= 255;3631 }3632 3633 4754 // Verify given socket address against the ACL. 3634 4755 // Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. 3635 static int check_acl(struct mg_context *ctx, const union usa *usa) { 3636 int a, b, c, d, n, mask, allowed; 3637 char flag; 3638 uint32_t acl_subnet, acl_mask, remote_ip; 4756 static int check_acl(struct mg_context *ctx, uint32_t remote_ip) { 4757 int allowed, flag; 4758 uint32_t net, mask; 3639 4759 struct vec vec; 3640 4760 const char *list = ctx->config[ACCESS_CONTROL_LIST]; 3641 4761 3642 if (list == NULL) {3643 return 1;3644 }3645 3646 (void) memcpy(&remote_ip, &usa->sin.sin_addr, sizeof(remote_ip));3647 3648 4762 // If any ACL is set, deny by default 3649 allowed = '-';4763 allowed = list == NULL ? '+' : '-'; 3650 4764 3651 4765 while ((list = next_option(list, &vec, NULL)) != NULL) { 3652 mask = 32;3653 3654 if (sscanf(vec.ptr, "%c%d.%d.%d.%d%n", &flag, &a, &b, &c, &d, &n) != 5) {4766 flag = vec.ptr[0]; 4767 if ((flag != '+' && flag != '-') || 4768 parse_net(&vec.ptr[1], &net, &mask) == 0) { 3655 4769 cry(fc(ctx), "%s: subnet must be [+|-]x.x.x.x[/x]", __func__); 3656 4770 return -1; 3657 } else if (flag != '+' && flag != '-') { 3658 cry(fc(ctx), "%s: flag must be + or -: [%s]", __func__, vec.ptr); 3659 return -1; 3660 } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) { 3661 cry(fc(ctx), "%s: bad ip address: [%s]", __func__, vec.ptr); 3662 return -1; 3663 } else if (sscanf(vec.ptr + n, "/%d", &mask) == 0) { 3664 // Do nothing, no mask specified 3665 } else if (mask < 0 || mask > 32) { 3666 cry(fc(ctx), "%s: bad subnet mask: %d [%s]", __func__, n, vec.ptr); 3667 return -1; 3668 } 3669 3670 acl_subnet = (a << 24) | (b << 16) | (c << 8) | d; 3671 acl_mask = mask ? 0xffffffffU << (32 - mask) : 0; 3672 3673 if (acl_subnet == (ntohl(remote_ip) & acl_mask)) { 4771 } 4772 4773 if (net == (remote_ip & mask)) { 3674 4774 allowed = flag; 3675 4775 } … … 3677 4777 3678 4778 return allowed == '+'; 3679 }3680 3681 static void add_to_set(SOCKET fd, fd_set *set, int *max_fd) {3682 FD_SET(fd, set);3683 if (fd > (SOCKET) *max_fd) {3684 *max_fd = (int) fd;3685 }3686 4779 } 3687 4780 … … 3713 4806 static pthread_mutex_t *ssl_mutexes; 3714 4807 4808 static int sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *)) { 4809 return (conn->ssl = SSL_new(s)) != NULL && 4810 SSL_set_fd(conn->ssl, conn->client.sock) == 1 && 4811 func(conn->ssl) == 1; 4812 } 4813 3715 4814 // Return OpenSSL error message 3716 4815 static const char *ssl_error(void) { … … 3722 4821 static void ssl_locking_callback(int mode, int mutex_num, const char *file, 3723 4822 int line) { 3724 line = 0; // Unused3725 file = NULL; // Unused3726 3727 if (mode & CRYPTO_LOCK) {4823 (void) line; 4824 (void) file; 4825 4826 if (mode & 1) { // 1 is CRYPTO_LOCK 3728 4827 (void) pthread_mutex_lock(&ssl_mutexes[mutex_num]); 3729 4828 } else { … … 3771 4870 // Dynamically load SSL library. Set up ctx->ssl_ctx pointer. 3772 4871 static int set_ssl_option(struct mg_context *ctx) { 3773 struct mg_request_info request_info;3774 SSL_CTX *CTX;3775 4872 int i, size; 3776 const char *pem = ctx->config[SSL_CERTIFICATE]; 3777 const char *chain = ctx->config[SSL_CHAIN_FILE]; 3778 3779 if (pem == NULL) { 4873 const char *pem; 4874 4875 // If PEM file is not specified and the init_ssl callback 4876 // is not specified, skip SSL initialization. 4877 if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL && 4878 ctx->callbacks.init_ssl == NULL) { 3780 4879 return 1; 3781 4880 } … … 3788 4887 #endif // NO_SSL_DL 3789 4888 3790 // Initialize SSL crap4889 // Initialize SSL library 3791 4890 SSL_library_init(); 3792 4891 SSL_load_error_strings(); 3793 4892 3794 if (( CTX= SSL_CTX_new(SSLv23_server_method())) == NULL) {3795 cry(fc(ctx), "SSL_CTX_new error: %s", ssl_error());3796 } else if (ctx->user_callback != NULL) {3797 memset(&request_info, 0, sizeof(request_info));3798 request_info.user_data = ctx->user_data; 3799 ctx->user_callback(MG_INIT_SSL, (struct mg_connection *) CTX,3800 &request_info);3801 }3802 3803 if (CTX != NULL && SSL_CTX_use_certificate_file(CTX, pem,3804 SSL_FILETYPE_PEM) == 0) {4893 if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { 4894 cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error()); 4895 return 0; 4896 } 4897 4898 // If user callback returned non-NULL, that means that user callback has 4899 // set up certificate itself. In this case, skip sertificate setting. 4900 if ((ctx->callbacks.init_ssl == NULL || 4901 !ctx->callbacks.init_ssl(ctx->ssl_ctx, ctx->user_data)) && 4902 (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 || 4903 SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0)) { 3805 4904 cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error()); 3806 4905 return 0; 3807 } else if (CTX != NULL && SSL_CTX_use_PrivateKey_file(CTX, pem, 3808 SSL_FILETYPE_PEM) == 0) { 3809 cry(fc(ctx), "%s: cannot open %s: %s", NULL, pem, ssl_error()); 3810 return 0; 3811 } 3812 3813 if (CTX != NULL && chain != NULL && 3814 SSL_CTX_use_certificate_chain_file(CTX, chain) == 0) { 3815 cry(fc(ctx), "%s: cannot open %s: %s", NULL, chain, ssl_error()); 3816 return 0; 4906 } 4907 4908 if (pem != NULL) { 4909 (void) SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem); 3817 4910 } 3818 4911 … … 3832 4925 CRYPTO_set_id_callback(&ssl_id_callback); 3833 4926 3834 // Done with everything. Save the context.3835 ctx->ssl_ctx = CTX;3836 3837 4927 return 1; 3838 4928 } … … 3852 4942 3853 4943 static int set_gpass_option(struct mg_context *ctx) { 3854 struct mgstat mgstat;4944 struct file file = STRUCT_FILE_INITIALIZER; 3855 4945 const char *path = ctx->config[GLOBAL_PASSWORDS_FILE]; 3856 return path == NULL || mg_stat(path, &mgstat) == 0; 4946 if (path != NULL && !mg_stat(fc(ctx), path, &file)) { 4947 cry(fc(ctx), "Cannot open %s: %s", path, strerror(ERRNO)); 4948 return 0; 4949 } 4950 return 1; 3857 4951 } 3858 4952 3859 4953 static int set_acl_option(struct mg_context *ctx) { 3860 union usa fake; 3861 return check_acl(ctx, &fake) != -1; 4954 return check_acl(ctx, (uint32_t) 0x7f000001UL) != -1; 3862 4955 } 3863 4956 3864 4957 static void reset_per_request_attributes(struct mg_connection *conn) { 3865 struct mg_request_info *ri = &conn->request_info; 3866 3867 // Reset request info attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port 3868 ri->remote_user = ri->request_method = ri->uri = ri->http_version = 3869 conn->path_info = NULL; 3870 ri->num_headers = 0; 3871 ri->status_code = -1; 3872 4958 conn->path_info = NULL; 3873 4959 conn->num_bytes_sent = conn->consumed_content = 0; 3874 conn->content_len = -1; 3875 conn->request_len = conn->data_len = 0; 3876 conn->must_close = 0; 3877 } 3878 3879 static void close_socket_gracefully(SOCKET sock) { 3880 char buf[BUFSIZ]; 4960 conn->status_code = -1; 4961 conn->must_close = conn->request_len = conn->throttle = 0; 4962 } 4963 4964 static void close_socket_gracefully(struct mg_connection *conn) { 4965 #if defined(_WIN32) 4966 char buf[MG_BUF_LEN]; 4967 int n; 4968 #endif 3881 4969 struct linger linger; 3882 int n;3883 4970 3884 4971 // Set linger option to avoid socket hanging out after close. This prevent … … 3886 4973 linger.l_onoff = 1; 3887 4974 linger.l_linger = 1; 3888 setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); 4975 setsockopt(conn->client.sock, SOL_SOCKET, SO_LINGER, 4976 (char *) &linger, sizeof(linger)); 3889 4977 3890 4978 // Send FIN to the client 3891 (void) shutdown(sock, SHUT_WR); 3892 set_non_blocking_mode(sock); 3893 3894 // Read and discard pending data. If we do not do that and close the 4979 shutdown(conn->client.sock, SHUT_WR); 4980 set_non_blocking_mode(conn->client.sock); 4981 4982 #if defined(_WIN32) 4983 // Read and discard pending incoming data. If we do not do that and close the 3895 4984 // socket, the data in the send buffer may be discarded. This 3896 4985 // behaviour is seen on Windows, when client keeps sending data 3897 // when server decide to close the connection; then when client4986 // when server decides to close the connection; then when client 3898 4987 // does recv() it gets no data back. 3899 4988 do { 3900 n = pull(NULL, sock, NULL, buf, sizeof(buf));4989 n = pull(NULL, conn, buf, sizeof(buf)); 3901 4990 } while (n > 0); 4991 #endif 3902 4992 3903 4993 // Now we know that our FIN is ACK-ed, safe to close 3904 (void) closesocket(sock);4994 closesocket(conn->client.sock); 3905 4995 } 3906 4996 3907 4997 static void close_connection(struct mg_connection *conn) { 3908 if (conn->ssl) { 4998 conn->must_close = 1; 4999 5000 #ifndef NO_SSL 5001 if (conn->ssl != NULL) { 5002 // Run SSL_shutdown twice to ensure completly close SSL connection 5003 SSL_shutdown(conn->ssl); 3909 5004 SSL_free(conn->ssl); 3910 5005 conn->ssl = NULL; 3911 5006 } 3912 5007 #endif 3913 5008 if (conn->client.sock != INVALID_SOCKET) { 3914 close_socket_gracefully(conn->client.sock); 3915 } 3916 } 3917 3918 static void discard_current_request_from_buffer(struct mg_connection *conn) { 3919 char *buffered; 3920 int buffered_len, body_len; 3921 3922 buffered = conn->buf + conn->request_len; 3923 buffered_len = conn->data_len - conn->request_len; 3924 assert(buffered_len >= 0); 3925 3926 if (conn->content_len <= 0) { 3927 // Protect from negative Content-Length, too 3928 body_len = 0; 3929 } else if (conn->content_len < (int64_t) buffered_len) { 3930 body_len = (int) conn->content_len; 5009 close_socket_gracefully(conn); 5010 conn->client.sock = INVALID_SOCKET; 5011 } 5012 } 5013 5014 void mg_close_connection(struct mg_connection *conn) { 5015 #ifndef NO_SSL 5016 if (conn->client_ssl_ctx != NULL) { 5017 SSL_CTX_free((SSL_CTX *) conn->client_ssl_ctx); 5018 } 5019 #endif 5020 close_connection(conn); 5021 free(conn); 5022 } 5023 5024 static struct mg_connection *mg_connect(const char *host, int port, int use_ssl, 5025 char *ebuf, size_t ebuf_len) { 5026 static struct mg_context fake_ctx; 5027 struct mg_connection *conn = NULL; 5028 SOCKET sock; 5029 5030 if ((sock = conn2(host, port, use_ssl, ebuf, ebuf_len)) == INVALID_SOCKET) { 5031 } else if ((conn = (struct mg_connection *) 5032 calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) { 5033 snprintf(ebuf, ebuf_len, "calloc(): %s", strerror(ERRNO)); 5034 closesocket(sock); 5035 #ifndef NO_SSL 5036 } else if (use_ssl && (conn->client_ssl_ctx = 5037 SSL_CTX_new(SSLv23_client_method())) == NULL) { 5038 snprintf(ebuf, ebuf_len, "SSL_CTX_new error"); 5039 closesocket(sock); 5040 free(conn); 5041 conn = NULL; 5042 #endif // NO_SSL 3931 5043 } else { 3932 body_len = buffered_len; 3933 } 3934 3935 conn->data_len -= conn->request_len + body_len; 3936 memmove(conn->buf, conn->buf + conn->request_len + body_len, 3937 (size_t) conn->data_len); 5044 socklen_t len = sizeof(struct sockaddr); 5045 conn->buf_size = MAX_REQUEST_SIZE; 5046 conn->buf = (char *) (conn + 1); 5047 conn->ctx = &fake_ctx; 5048 conn->client.sock = sock; 5049 getsockname(sock, &conn->client.rsa.sa, &len); 5050 conn->client.is_ssl = use_ssl; 5051 #ifndef NO_SSL 5052 if (use_ssl) { 5053 // SSL_CTX_set_verify call is needed to switch off server certificate 5054 // checking, which is off by default in OpenSSL and on in yaSSL. 5055 SSL_CTX_set_verify(conn->client_ssl_ctx, 0, 0); 5056 sslize(conn, conn->client_ssl_ctx, SSL_connect); 5057 } 5058 #endif 5059 } 5060 5061 return conn; 3938 5062 } 3939 5063 … … 3944 5068 } 3945 5069 5070 static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len) { 5071 const char *cl; 5072 5073 ebuf[0] = '\0'; 5074 reset_per_request_attributes(conn); 5075 conn->request_len = read_request(NULL, conn, conn->buf, conn->buf_size, 5076 &conn->data_len); 5077 assert(conn->request_len < 0 || conn->data_len >= conn->request_len); 5078 5079 if (conn->request_len == 0 && conn->data_len == conn->buf_size) { 5080 snprintf(ebuf, ebuf_len, "%s", "Request Too Large"); 5081 } else if (conn->request_len <= 0) { 5082 snprintf(ebuf, ebuf_len, "%s", "Client closed connection"); 5083 } else if (parse_http_message(conn->buf, conn->buf_size, 5084 &conn->request_info) <= 0) { 5085 snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf); 5086 } else { 5087 // Request is valid 5088 if ((cl = get_header(&conn->request_info, "Content-Length")) != NULL) { 5089 conn->content_len = strtoll(cl, NULL, 10); 5090 } else if (!mg_strcasecmp(conn->request_info.request_method, "POST") || 5091 !mg_strcasecmp(conn->request_info.request_method, "PUT")) { 5092 conn->content_len = -1; 5093 } else { 5094 conn->content_len = 0; 5095 } 5096 conn->birth_time = time(NULL); 5097 } 5098 return ebuf[0] == '\0'; 5099 } 5100 5101 struct mg_connection *mg_download(const char *host, int port, int use_ssl, 5102 char *ebuf, size_t ebuf_len, 5103 const char *fmt, ...) { 5104 struct mg_connection *conn; 5105 va_list ap; 5106 5107 va_start(ap, fmt); 5108 ebuf[0] = '\0'; 5109 if ((conn = mg_connect(host, port, use_ssl, ebuf, ebuf_len)) == NULL) { 5110 } else if (mg_vprintf(conn, fmt, ap) <= 0) { 5111 snprintf(ebuf, ebuf_len, "%s", "Error sending request"); 5112 } else { 5113 getreq(conn, ebuf, ebuf_len); 5114 } 5115 if (ebuf[0] != '\0' && conn != NULL) { 5116 mg_close_connection(conn); 5117 conn = NULL; 5118 } 5119 5120 return conn; 5121 } 5122 3946 5123 static void process_new_connection(struct mg_connection *conn) { 3947 5124 struct mg_request_info *ri = &conn->request_info; 3948 int keep_alive_enabled ;3949 c onst char *cl;5125 int keep_alive_enabled, keep_alive, discard_len; 5126 char ebuf[100]; 3950 5127 3951 5128 keep_alive_enabled = !strcmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes"); 3952 5129 keep_alive = 0; 5130 5131 // Important: on new connection, reset the receiving buffer. Credit goes 5132 // to crule42. 5133 conn->data_len = 0; 3953 5134 do { 3954 reset_per_request_attributes(conn); 3955 conn->request_len = read_request(NULL, conn->client.sock, conn->ssl, 3956 conn->buf, conn->buf_size, 3957 &conn->data_len); 3958 assert(conn->data_len >= conn->request_len); 3959 if (conn->request_len == 0 && conn->data_len == conn->buf_size) { 3960 send_http_error(conn, 413, "Request Too Large", ""); 3961 return; 3962 } if (conn->request_len <= 0) { 3963 return; // Remote end closed the connection 3964 } 3965 3966 // Nul-terminate the request cause parse_http_request() uses sscanf 3967 conn->buf[conn->request_len - 1] = '\0'; 3968 if (!parse_http_request(conn->buf, ri) || !is_valid_uri(ri->uri)) { 3969 // Do not put garbage in the access log, just send it back to the client 3970 send_http_error(conn, 400, "Bad Request", 3971 "Cannot parse HTTP request: [%.*s]", conn->data_len, conn->buf); 5135 if (!getreq(conn, ebuf, sizeof(ebuf))) { 5136 send_http_error(conn, 500, "Server Error", "%s", ebuf); 5137 conn->must_close = 1; 5138 } else if (!is_valid_uri(conn->request_info.uri)) { 5139 snprintf(ebuf, sizeof(ebuf), "Invalid URI: [%s]", ri->uri); 5140 send_http_error(conn, 400, "Bad Request", "%s", ebuf); 3972 5141 } else if (strcmp(ri->http_version, "1.0") && 3973 5142 strcmp(ri->http_version, "1.1")) { 3974 // Request seems valid, but HTTP version is strange 3975 send_http_error(conn, 505, "HTTP version not supported", ""); 5143 snprintf(ebuf, sizeof(ebuf), "Bad HTTP version: [%s]", ri->http_version); 5144 send_http_error(conn, 505, "Bad HTTP version", "%s", ebuf); 5145 } 5146 5147 if (ebuf[0] == '\0') { 5148 handle_request(conn); 5149 if (conn->ctx->callbacks.end_request != NULL) { 5150 conn->ctx->callbacks.end_request(conn, conn->status_code); 5151 } 3976 5152 log_access(conn); 3977 } else {3978 // Request is valid, handle it3979 cl = get_header(ri, "Content-Length");3980 conn->content_len = cl == NULL ? -1 : strtoll(cl, NULL, 10);3981 conn->birth_time = time(NULL);3982 handle_request(conn);3983 call_user(conn, MG_REQUEST_COMPLETE);3984 log_access(conn);3985 discard_current_request_from_buffer(conn);3986 5153 } 3987 5154 if (ri->remote_user != NULL) { 3988 5155 free((void *) ri->remote_user); 3989 } 3990 } while (conn->ctx->stop_flag == 0 && 3991 keep_alive_enabled && 3992 should_keep_alive(conn)); 5156 // Important! When having connections with and without auth 5157 // would cause double free and then crash 5158 ri->remote_user = NULL; 5159 } 5160 5161 // NOTE(lsm): order is important here. should_keep_alive() call 5162 // is using parsed request, which will be invalid after memmove's below. 5163 // Therefore, memorize should_keep_alive() result now for later use 5164 // in loop exit condition. 5165 keep_alive = conn->ctx->stop_flag == 0 && keep_alive_enabled && 5166 conn->content_len >= 0 && should_keep_alive(conn); 5167 5168 // Discard all buffered data for this request 5169 discard_len = conn->content_len >= 0 && conn->request_len > 0 && 5170 conn->request_len + conn->content_len < (int64_t) conn->data_len ? 5171 (int) (conn->request_len + conn->content_len) : conn->data_len; 5172 assert(discard_len >= 0); 5173 memmove(conn->buf, conn->buf + discard_len, conn->data_len - discard_len); 5174 conn->data_len -= discard_len; 5175 assert(conn->data_len >= 0); 5176 assert(conn->data_len <= conn->buf_size); 5177 } while (keep_alive); 3993 5178 } 3994 5179 … … 4023 5208 } 4024 5209 4025 static void worker_thread(struct mg_context *ctx) { 5210 static void *worker_thread(void *thread_func_param) { 5211 struct mg_context *ctx = (struct mg_context *) thread_func_param; 4026 5212 struct mg_connection *conn; 4027 int buf_size = atoi(ctx->config[MAX_REQUEST_SIZE]); 4028 4029 conn = (struct mg_connection *) calloc(1, sizeof(*conn) + buf_size); 5213 5214 conn = (struct mg_connection *) calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE); 4030 5215 if (conn == NULL) { 4031 5216 cry(fc(ctx), "%s", "Cannot create new connection struct, OOM"); 4032 return; 4033 } 4034 conn->buf_size = buf_size; 4035 conn->buf = (char *) (conn + 1); 4036 4037 // Call consume_socket() even when ctx->stop_flag > 0, to let it signal 4038 // sq_empty condvar to wake up the master waiting in produce_socket() 4039 while (consume_socket(ctx, &conn->client)) { 4040 conn->birth_time = time(NULL); 5217 } else { 5218 conn->buf_size = MAX_REQUEST_SIZE; 5219 conn->buf = (char *) (conn + 1); 4041 5220 conn->ctx = ctx; 4042 4043 // Fill in IP, port info early so even if SSL setup below fails, 4044 // error handler would have the corresponding info. 4045 // Thanks to Johannes Winkelmann for the patch. 4046 // TODO(lsm): Fix IPv6 case 4047 conn->request_info.remote_port = ntohs(conn->client.rsa.sin.sin_port); 4048 memcpy(&conn->request_info.remote_ip, 4049 &conn->client.rsa.sin.sin_addr.s_addr, 4); 4050 conn->request_info.remote_ip = ntohl(conn->request_info.remote_ip); 4051 conn->request_info.is_ssl = conn->client.is_ssl; 4052 4053 if (!conn->client.is_ssl || 4054 (conn->client.is_ssl && sslize(conn, SSL_accept))) { 4055 process_new_connection(conn); 4056 } 4057 4058 close_connection(conn); 4059 } 4060 free(conn); 5221 conn->request_info.user_data = ctx->user_data; 5222 5223 // Call consume_socket() even when ctx->stop_flag > 0, to let it signal 5224 // sq_empty condvar to wake up the master waiting in produce_socket() 5225 while (consume_socket(ctx, &conn->client)) { 5226 conn->birth_time = time(NULL); 5227 5228 // Fill in IP, port info early so even if SSL setup below fails, 5229 // error handler would have the corresponding info. 5230 // Thanks to Johannes Winkelmann for the patch. 5231 // TODO(lsm): Fix IPv6 case 5232 conn->request_info.remote_port = ntohs(conn->client.rsa.sin.sin_port); 5233 memcpy(&conn->request_info.remote_ip, 5234 &conn->client.rsa.sin.sin_addr.s_addr, 4); 5235 conn->request_info.remote_ip = ntohl(conn->request_info.remote_ip); 5236 conn->request_info.is_ssl = conn->client.is_ssl; 5237 5238 if (!conn->client.is_ssl 5239 #ifndef NO_SSL 5240 || sslize(conn, conn->ctx->ssl_ctx, SSL_accept) 5241 #endif 5242 ) { 5243 process_new_connection(conn); 5244 } 5245 5246 close_connection(conn); 5247 } 5248 free(conn); 5249 } 4061 5250 4062 5251 // Signal master that we're done with connection and exiting … … 4068 5257 4069 5258 DEBUG_TRACE(("exiting")); 5259 return NULL; 4070 5260 } 4071 5261 … … 4091 5281 } 4092 5282 5283 static int set_sock_timeout(SOCKET sock, int milliseconds) { 5284 #ifdef _WIN32 5285 DWORD t = milliseconds; 5286 #else 5287 struct timeval t; 5288 t.tv_sec = milliseconds / 1000; 5289 t.tv_usec = (milliseconds * 1000) % 1000000; 5290 #endif 5291 return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *) &t, sizeof(t)) || 5292 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (void *) &t, sizeof(t)); 5293 } 5294 4093 5295 static void accept_new_connection(const struct socket *listener, 4094 5296 struct mg_context *ctx) { 4095 struct socket accepted; 4096 char src_addr[20]; 4097 socklen_t len; 4098 int allowed; 4099 4100 len = sizeof(accepted.rsa); 4101 accepted.lsa = listener->lsa; 4102 accepted.sock = accept(listener->sock, &accepted.rsa.sa, &len); 4103 if (accepted.sock != INVALID_SOCKET) { 4104 allowed = check_acl(ctx, &accepted.rsa); 4105 if (allowed) { 4106 // Put accepted socket structure into the queue 4107 DEBUG_TRACE(("accepted socket %d", accepted.sock)); 4108 accepted.is_ssl = listener->is_ssl; 4109 produce_socket(ctx, &accepted); 4110 } else { 4111 sockaddr_to_string(src_addr, sizeof(src_addr), &accepted.rsa); 4112 cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr); 4113 (void) closesocket(accepted.sock); 4114 } 4115 } 4116 } 4117 4118 static void master_thread(struct mg_context *ctx) { 4119 fd_set read_set; 4120 struct timeval tv; 4121 struct socket *sp; 4122 int max_fd; 5297 struct socket so; 5298 char src_addr[IP_ADDR_STR_LEN]; 5299 socklen_t len = sizeof(so.rsa); 5300 int on = 1; 5301 5302 if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) == INVALID_SOCKET) { 5303 } else if (!check_acl(ctx, ntohl(* (uint32_t *) &so.rsa.sin.sin_addr))) { 5304 sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa); 5305 cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr); 5306 closesocket(so.sock); 5307 } else { 5308 // Put so socket structure into the queue 5309 DEBUG_TRACE(("Accepted socket %d", (int) so.sock)); 5310 set_close_on_exec(so.sock); 5311 so.is_ssl = listener->is_ssl; 5312 so.ssl_redir = listener->ssl_redir; 5313 getsockname(so.sock, &so.lsa.sa, &len); 5314 // Set TCP keep-alive. This is needed because if HTTP-level keep-alive 5315 // is enabled, and client resets the connection, server won't get 5316 // TCP FIN or RST and will keep the connection open forever. With TCP 5317 // keep-alive, next keep-alive handshake will figure out that the client 5318 // is down and will close the server end. 5319 // Thanks to Igor Klopov who suggested the patch. 5320 setsockopt(so.sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &on, sizeof(on)); 5321 set_sock_timeout(so.sock, atoi(ctx->config[REQUEST_TIMEOUT])); 5322 produce_socket(ctx, &so); 5323 } 5324 } 5325 5326 static void *master_thread(void *thread_func_param) { 5327 struct mg_context *ctx = (struct mg_context *) thread_func_param; 5328 struct pollfd *pfd; 5329 int i; 4123 5330 4124 5331 // Increase priority of the master thread … … 4126 5333 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); 4127 5334 #endif 4128 5335 4129 5336 #if defined(ISSUE_317) 4130 5337 struct sched_param sched_param; … … 4133 5340 #endif 4134 5341 4135 while (ctx->stop_flag == 0) { 4136 FD_ZERO(&read_set); 4137 max_fd = -1; 4138 4139 // Add listening sockets to the read set 4140 for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) { 4141 add_to_set(sp->sock, &read_set, &max_fd); 4142 } 4143 4144 tv.tv_sec = 0; 4145 tv.tv_usec = 200 * 1000; 4146 4147 if (select(max_fd + 1, &read_set, NULL, NULL, &tv) < 0) { 4148 #ifdef _WIN32 4149 // On windows, if read_set and write_set are empty, 4150 // select() returns "Invalid parameter" error 4151 // (at least on my Windows XP Pro). So in this case, we sleep here. 4152 mg_sleep(1000); 4153 #endif // _WIN32 4154 } else { 4155 for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) { 4156 if (ctx->stop_flag == 0 && FD_ISSET(sp->sock, &read_set)) { 4157 accept_new_connection(sp, ctx); 5342 pfd = (struct pollfd *) calloc(ctx->num_listening_sockets, sizeof(pfd[0])); 5343 while (pfd != NULL && ctx->stop_flag == 0) { 5344 for (i = 0; i < ctx->num_listening_sockets; i++) { 5345 pfd[i].fd = ctx->listening_sockets[i].sock; 5346 pfd[i].events = POLLIN; 5347 } 5348 5349 if (poll(pfd, ctx->num_listening_sockets, 200) > 0) { 5350 for (i = 0; i < ctx->num_listening_sockets; i++) { 5351 // NOTE(lsm): on QNX, poll() returns POLLRDNORM after the 5352 // successfull poll, and POLLIN is defined as (POLLRDNORM | POLLRDBAND) 5353 // Therefore, we're checking pfd[i].revents & POLLIN, not 5354 // pfd[i].revents == POLLIN. 5355 if (ctx->stop_flag == 0 && (pfd[i].revents & POLLIN)) { 5356 accept_new_connection(&ctx->listening_sockets[i], ctx); 4158 5357 } 4159 5358 } 4160 5359 } 4161 5360 } 5361 free(pfd); 4162 5362 DEBUG_TRACE(("stopping workers")); 4163 5363 … … 4184 5384 uninitialize_ssl(ctx); 4185 5385 #endif 4186 4187 // Signal mg_stop() that we're done 5386 DEBUG_TRACE(("exiting")); 5387 5388 // Signal mg_stop() that we're done. 5389 // WARNING: This must be the very last thing this 5390 // thread does, as ctx becomes invalid after this line. 4188 5391 ctx->stop_flag = 2; 4189 4190 DEBUG_TRACE(("exiting")); 5392 return NULL; 4191 5393 } 4192 5394 … … 4200 5402 } 4201 5403 5404 #ifndef NO_SSL 4202 5405 // Deallocate SSL context 4203 5406 if (ctx->ssl_ctx != NULL) { 4204 5407 SSL_CTX_free(ctx->ssl_ctx); 4205 5408 } 4206 #ifndef NO_SSL4207 5409 if (ssl_mutexes != NULL) { 4208 5410 free(ssl_mutexes); 5411 ssl_mutexes = NULL; 4209 5412 } 4210 5413 #endif // !NO_SSL … … 4219 5422 // Wait until mg_fini() stops 4220 5423 while (ctx->stop_flag != 2) { 4221 mg_sleep(10);5424 (void) mg_sleep(10); 4222 5425 } 4223 5426 free_context(ctx); … … 4228 5431 } 4229 5432 4230 struct mg_context *mg_start(mg_callback_t user_callback, void *user_data, 5433 struct mg_context *mg_start(const struct mg_callbacks *callbacks, 5434 void *user_data, 4231 5435 const char **options) { 4232 5436 struct mg_context *ctx; … … 4242 5446 // Allocate context and initialize reasonable general case defaults. 4243 5447 // TODO(lsm): do proper error handling here. 4244 ctx = (struct mg_context *) calloc(1, sizeof(*ctx)); 4245 ctx->user_callback = user_callback; 5448 if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) { 5449 return NULL; 5450 } 5451 ctx->callbacks = *callbacks; 4246 5452 ctx->user_data = user_data; 4247 5453 … … 4257 5463 } 4258 5464 if (ctx->config[i] != NULL) { 4259 cry(fc(ctx), "%s: duplicate option", name); 5465 cry(fc(ctx), "warning: %s: duplicate option", name); 5466 free(ctx->config[i]); 4260 5467 } 4261 5468 ctx->config[i] = mg_strdup(value); … … 4264 5471 4265 5472 // Set default value if needed 4266 for (i = 0; config_options[i * ENTRIES_PER_CONFIG_OPTION] != NULL; i++) {4267 default_value = config_options[i * ENTRIES_PER_CONFIG_OPTION + 2];5473 for (i = 0; config_options[i * 2] != NULL; i++) { 5474 default_value = config_options[i * 2 + 1]; 4268 5475 if (ctx->config[i] == NULL && default_value != NULL) { 4269 5476 ctx->config[i] = mg_strdup(default_value); 4270 DEBUG_TRACE(("Setting default: [%s] -> [%s]",4271 config_options[i * ENTRIES_PER_CONFIG_OPTION + 1],4272 default_value));4273 5477 } 4274 5478 } … … 4303 5507 4304 5508 // Start master (listening) thread 4305 start_thread(ctx, (mg_thread_func_t)master_thread, ctx);5509 mg_start_thread(master_thread, ctx); 4306 5510 4307 5511 // Start worker threads 4308 5512 for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) { 4309 if ( start_thread(ctx, (mg_thread_func_t)worker_thread, ctx) != 0) {4310 cry(fc(ctx), "Cannot start worker thread: % d",ERRNO);5513 if (mg_start_thread(worker_thread, ctx) != 0) { 5514 cry(fc(ctx), "Cannot start worker thread: %ld", (long) ERRNO); 4311 5515 } else { 4312 5516 ctx->num_threads++; -
cpukit/mghttpd/mongoose.h
r0adc8a7 rb5d2d4a 1 // Copyright (c) 2004-201 1Sergey Lyubka1 // Copyright (c) 2004-2012 Sergey Lyubka 2 2 // 3 3 // Permission is hereby granted, free of charge, to any person obtaining a copy … … 22 22 #define MONGOOSE_HEADER_INCLUDED 23 23 24 #include <stdio.h> 24 25 #include <stddef.h> 25 26 … … 34 35 // This structure contains information about the HTTP request. 35 36 struct mg_request_info { 36 void *user_data; // User-defined pointer passed to mg_start()37 c har *request_method; // "GET", "POST", etc38 c har *uri; // URL-decoded URI39 c har *http_version; // E.g. "1.0", "1.1"40 c har *query_string; // URL part after '?' (not including '?') or NULL41 char *remote_user; // Authenticated user, or NULL if no auth used42 char *log_message; // Mongoose error log message, MG_EVENT_LOG only43 long remote_ip; // Client's IP address44 int remote_port; // Client's port45 int status_code; // HTTP reply status code, e.g. 20046 int is_ssl; // 1 if SSL-ed, 0 if not 47 int num_headers; // Number ofheaders37 const char *request_method; // "GET", "POST", etc 38 const char *uri; // URL-decoded URI 39 const char *http_version; // E.g. "1.0", "1.1" 40 const char *query_string; // URL part after '?', not including '?', or NULL 41 const char *remote_user; // Authenticated user, or NULL if no auth used 42 long remote_ip; // Client's IP address 43 int remote_port; // Client's port 44 int is_ssl; // 1 if SSL-ed, 0 if not 45 void *user_data; // User data pointer passed to mg_start() 46 void *conn_data; // Connection-specific user data 47 48 int num_headers; // Number of HTTP headers 48 49 struct mg_header { 49 c har *name;// HTTP header name50 c har *value;// HTTP header value51 } http_headers[64]; // Maximum 64 headers50 const char *name; // HTTP header name 51 const char *value; // HTTP header value 52 } http_headers[64]; // Maximum 64 headers 52 53 }; 53 54 54 // Various events on which user-defined function is called by Mongoose. 55 enum mg_event { 56 MG_NEW_REQUEST, // New HTTP request has arrived from the client 57 MG_HTTP_ERROR, // HTTP error must be returned to the client 58 MG_EVENT_LOG, // Mongoose logs an event, request_info.log_message 59 MG_INIT_SSL, // Mongoose initializes SSL. Instead of mg_connection *, 60 // SSL context is passed to the callback function. 61 MG_REQUEST_COMPLETE // Mongoose has finished handling the request 55 56 // This structure needs to be passed to mg_start(), to let mongoose know 57 // which callbacks to invoke. For detailed description, see 58 // https://github.com/valenok/mongoose/blob/master/UserManual.md 59 struct mg_callbacks { 60 // Called when mongoose has received new HTTP request. 61 // If callback returns non-zero, 62 // callback must process the request by sending valid HTTP headers and body, 63 // and mongoose will not do any further processing. 64 // If callback returns 0, mongoose processes the request itself. In this case, 65 // callback must not send any data to the client. 66 int (*begin_request)(struct mg_connection *); 67 68 // Called when mongoose has finished processing request. 69 void (*end_request)(const struct mg_connection *, int reply_status_code); 70 71 // Called when mongoose is about to log a message. If callback returns 72 // non-zero, mongoose does not log anything. 73 int (*log_message)(const struct mg_connection *, const char *message); 74 75 // Called when mongoose initializes SSL library. 76 int (*init_ssl)(void *ssl_context, void *user_data); 77 78 // Called when websocket request is received, before websocket handshake. 79 // If callback returns 0, mongoose proceeds with handshake, otherwise 80 // cinnection is closed immediately. 81 int (*websocket_connect)(const struct mg_connection *); 82 83 // Called when websocket handshake is successfully completed, and 84 // connection is ready for data exchange. 85 void (*websocket_ready)(struct mg_connection *); 86 87 // Called when data frame has been received from the client. 88 // Parameters: 89 // bits: first byte of the websocket frame, see websocket RFC at 90 // http://tools.ietf.org/html/rfc6455, section 5.2 91 // data, data_len: payload, with mask (if any) already applied. 92 // Return value: 93 // non-0: keep this websocket connection opened. 94 // 0: close this websocket connection. 95 int (*websocket_data)(struct mg_connection *, int bits, 96 char *data, size_t data_len); 97 98 // Called when mongoose tries to open a file. Used to intercept file open 99 // calls, and serve file data from memory instead. 100 // Parameters: 101 // path: Full path to the file to open. 102 // data_len: Placeholder for the file size, if file is served from memory. 103 // Return value: 104 // NULL: do not serve file from memory, proceed with normal file open. 105 // non-NULL: pointer to the file contents in memory. data_len must be 106 // initilized with the size of the memory block. 107 const char * (*open_file)(const struct mg_connection *, 108 const char *path, size_t *data_len); 109 110 // Called when mongoose is about to serve Lua server page (.lp file), if 111 // Lua support is enabled. 112 // Parameters: 113 // lua_context: "lua_State *" pointer. 114 void (*init_lua)(struct mg_connection *, void *lua_context); 115 116 // Called when mongoose has uploaded a file to a temporary directory as a 117 // result of mg_upload() call. 118 // Parameters: 119 // file_file: full path name to the uploaded file. 120 void (*upload)(struct mg_connection *, const char *file_name); 121 122 // Called when mongoose is about to send HTTP error to the client. 123 // Implementing this callback allows to create custom error pages. 124 // Parameters: 125 // status: HTTP error status code. 126 int (*http_error)(struct mg_connection *, int status); 62 127 }; 63 128 64 // Prototype for the user-defined function. Mongoose calls this function 65 // on every MG_* event. 129 // Start web server. 66 130 // 67 131 // Parameters: 68 // event: which event has been triggered. 69 // conn: opaque connection handler. Could be used to read, write data to the 70 // client, etc. See functions below that have "mg_connection *" arg. 71 // request_info: Information about HTTP request. 72 // 73 // Return: 74 // If handler returns non-NULL, that means that handler has processed the 75 // request by sending appropriate HTTP reply to the client. Mongoose treats 76 // the request as served. 77 // If handler returns NULL, that means that handler has not processed 78 // the request. Handler must not send any data to the client in this case. 79 // Mongoose proceeds with request handling as if nothing happened. 80 typedef void * (*mg_callback_t)(enum mg_event event, 81 struct mg_connection *conn, 82 const struct mg_request_info *request_info); 83 84 85 // Start web server. 86 // 87 // Parameters: 88 // callback: user defined event handling function or NULL. 132 // callbacks: mg_callbacks structure with user-defined callbacks. 89 133 // options: NULL terminated list of option_name, option_value pairs that 90 134 // specify Mongoose configuration parameters. … … 103 147 // struct mg_context *ctx = mg_start(&my_func, NULL, options); 104 148 // 105 // Please refer to http://code.google.com/p/mongoose/wiki/MongooseManual149 // Refer to https://github.com/valenok/mongoose/blob/master/UserManual.md 106 150 // for the list of valid option and their possible values. 107 151 // 108 152 // Return: 109 153 // web server context, or NULL on error. 110 struct mg_context *mg_start(mg_callback_t callback, void *user_data, 111 const char **options); 154 struct mg_context *mg_start(const struct mg_callbacks *callbacks, 155 void *user_data, 156 const char **configuration_options); 112 157 113 158 … … 130 175 131 176 // Return array of strings that represent valid configuration options. 132 // For each option, a short name, long name, and default value is returned. 177 // For each option, option name and default value is returned, i.e. the 178 // number of entries in the array equals to number_of_options x 2. 133 179 // Array is NULL terminated. 134 180 const char **mg_get_valid_option_names(void); … … 152 198 const char *password); 153 199 200 201 // Return information associated with the request. 202 struct mg_request_info *mg_get_request_info(struct mg_connection *); 203 204 154 205 // Send data to the client. 206 // Return: 207 // 0 when the connection has been closed 208 // -1 on error 209 // >0 number of bytes written on success 155 210 int mg_write(struct mg_connection *, const void *buf, size_t len); 156 211 157 212 158 // Send data to the browser using printf() semantics. 159 // 160 // Works exactly like mg_write(), but allows to do message formatting. Note 161 // that mg_printf() uses internal buffer of size BUFSIZ defined in <stdio.h> 162 // (8 KiB on most Linux systems) as temporary message storage for formatting. 163 // Do not print data that is bigger than that, otherwise it will be truncated. 164 int mg_printf(struct mg_connection *, const char *fmt, ...) 213 // Send data to a websocket client wrapped in a websocket frame. 214 // It is unsafe to read/write to this connection from another thread. 215 // This function is available when mongoose is compiled with -DUSE_WEBSOCKET 216 // 217 // Return: 218 // 0 when the connection has been closed 219 // -1 on error 220 // >0 number of bytes written on success 221 int mg_websocket_write(struct mg_connection* conn, int opcode, 222 const char *data, size_t data_len); 223 224 // Opcodes, from http://tools.ietf.org/html/rfc6455 225 enum { 226 WEBSOCKET_OPCODE_CONTINUATION = 0x0, 227 WEBSOCKET_OPCODE_TEXT = 0x1, 228 WEBSOCKET_OPCODE_BINARY = 0x2, 229 WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8, 230 WEBSOCKET_OPCODE_PING = 0x9, 231 WEBSOCKET_OPCODE_PONG = 0xa 232 }; 233 234 235 // Macros for enabling compiler-specific checks for printf-like arguments. 236 #undef PRINTF_FORMAT_STRING 237 #if defined(_MSC_VER) && _MSC_VER >= 1400 238 #include <sal.h> 239 #if defined(_MSC_VER) && _MSC_VER > 1400 240 #define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s 241 #else 242 #define PRINTF_FORMAT_STRING(s) __format_string s 243 #endif 244 #else 245 #define PRINTF_FORMAT_STRING(s) s 246 #endif 247 165 248 #ifdef __GNUC__ 166 __attribute__((format(printf, 2, 3))) 249 #define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y))) 250 #else 251 #define PRINTF_ARGS(x, y) 167 252 #endif 168 ; 253 254 // Send data to the client using printf() semantics. 255 // 256 // Works exactly like mg_write(), but allows to do message formatting. 257 int mg_printf(struct mg_connection *, 258 PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); 169 259 170 260 … … 174 264 175 265 // Read data from the remote end, return number of bytes read. 266 // Return: 267 // 0 connection has been closed by peer. No more data could be read. 268 // < 0 read error. No more data could be read from the connection. 269 // > 0 number of bytes read into the buffer. 176 270 int mg_read(struct mg_connection *, void *buf, size_t len); 177 271 … … 192 286 // data_len: length of the encoded data. 193 287 // var_name: variable name to decode from the buffer 194 // buf: destination buffer for the decoded variable195 // buf_len: length of the destination buffer288 // dst: destination buffer for the decoded variable 289 // dst_len: length of the destination buffer 196 290 // 197 291 // Return: 198 292 // On success, length of the decoded variable. 199 // On error, -1 (variable not found, or destination buffer is too small). 200 // 201 // Destination buffer is guaranteed to be '\0' - terminated. In case of 202 // failure, dst[0] == '\0'. 293 // On error: 294 // -1 (variable not found). 295 // -2 (destination buffer is NULL, zero length or too small to hold the 296 // decoded variable). 297 // 298 // Destination buffer is guaranteed to be '\0' - terminated if it is not 299 // NULL or zero length. 203 300 int mg_get_var(const char *data, size_t data_len, 204 const char *var_name, char * buf, size_t buf_len);301 const char *var_name, char *dst, size_t dst_len); 205 302 206 303 // Fetch value of certain cookie variable into the destination buffer. … … 212 309 // Return: 213 310 // On success, value length. 214 // On error, 0 (either "Cookie:" header is not present at all, or the 215 // requested parameter is not found, or destination buffer is too small 216 // to hold the value). 217 int mg_get_cookie(const struct mg_connection *, 218 const char *cookie_name, char *buf, size_t buf_len); 311 // On error: 312 // -1 (either "Cookie:" header is not present at all or the requested 313 // parameter is not found). 314 // -2 (destination buffer is NULL, zero length or too small to hold the 315 // value). 316 int mg_get_cookie(const char *cookie, const char *var_name, 317 char *buf, size_t buf_len); 318 319 320 // Download data from the remote web server. 321 // host: host name to connect to, e.g. "foo.com", or "10.12.40.1". 322 // port: port number, e.g. 80. 323 // use_ssl: wether to use SSL connection. 324 // error_buffer, error_buffer_size: error message placeholder. 325 // request_fmt,...: HTTP request. 326 // Return: 327 // On success, valid pointer to the new connection, suitable for mg_read(). 328 // On error, NULL. error_buffer contains error message. 329 // Example: 330 // char ebuf[100]; 331 // struct mg_connection *conn; 332 // conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf), 333 // "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n"); 334 struct mg_connection *mg_download(const char *host, int port, int use_ssl, 335 char *error_buffer, size_t error_buffer_size, 336 PRINTF_FORMAT_STRING(const char *request_fmt), 337 ...) PRINTF_ARGS(6, 7); 338 339 340 // Close the connection opened by mg_download(). 341 void mg_close_connection(struct mg_connection *conn); 342 343 344 // File upload functionality. Each uploaded file gets saved into a temporary 345 // file and MG_UPLOAD event is sent. 346 // Return number of uploaded files. 347 int mg_upload(struct mg_connection *conn, const char *destination_dir); 348 349 350 // Convenience function -- create detached thread. 351 // Return: 0 on success, non-0 on error. 352 typedef void * (*mg_thread_func_t)(void *); 353 int mg_start_thread(mg_thread_func_t f, void *p); 354 355 356 // Return builtin mime type for the given file name. 357 // For unrecognized extensions, "text/plain" is returned. 358 const char *mg_get_builtin_mime_type(const char *file_name); 219 359 220 360 … … 222 362 const char *mg_version(void); 223 363 364 // URL-decode input buffer into destination buffer. 365 // 0-terminate the destination buffer. 366 // form-url-encoded data differs from URI encoding in a way that it 367 // uses '+' as character for space, see RFC 1866 section 8.2.1 368 // http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt 369 // Return: length of the decoded data, or -1 if dst buffer is too small. 370 int mg_url_decode(const char *src, int src_len, char *dst, 371 int dst_len, int is_form_url_encoded); 224 372 225 373 // MD5 hash given strings. 226 374 // Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of 227 // asciiz strings. When function returns, buf will contain human-readable375 // ASCIIz strings. When function returns, buf will contain human-readable 228 376 // MD5 hash. Example: 229 377 // char buf[33]; 230 378 // mg_md5(buf, "aa", "bb", NULL); 231 void mg_md5(char *buf, ...);379 char *mg_md5(char buf[33], ...); 232 380 233 381 -
testsuites/libtests/mghttpd01/init.c
r0adc8a7 rb5d2d4a 45 45 "Date: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n" \ 46 46 "Last-Modified: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n" \ 47 "Etag: \"21dae500. a2\"\r\n" \47 "Etag: \"21dae500.162\"\r\n" \ 48 48 "Content-Type: text/html\r\n" \ 49 49 "Content-Length: 162\r\n" \ … … 81 81 } 82 82 83 typedef struct { 84 char *string; 85 int size; 86 } printctx; 87 88 static void *callback(enum mg_event event, 89 struct mg_connection *conn, 90 const struct mg_request_info *request_info) 91 { 92 if (event == MG_NEW_REQUEST) { 93 int cbacktest = strncmp(request_info->uri, CBACKTEST_URI, sizeof(CBACKTEST_URI)); 94 if (cbacktest == 0) 95 { 96 mg_write(conn, CBACKTEST_TXT, sizeof(CBACKTEST_TXT)); 97 98 /* Mark as processed */ 99 return ""; 100 } 83 static int callback(struct mg_connection *conn) 84 { 85 int cbacktest = strncmp(mg_get_request_info(conn)->uri, CBACKTEST_URI, sizeof(CBACKTEST_URI)); 86 if (cbacktest == 0) 87 { 88 mg_write(conn, CBACKTEST_TXT, sizeof(CBACKTEST_TXT)); 89 90 /* Mark as processed */ 91 return 1; 101 92 } 102 return NULL; 93 94 return 0; 103 95 } 104 96 … … 173 165 static void test_mongoose(void) 174 166 { 167 const struct mg_callbacks callbacks = { 168 .begin_request = callback 169 }; 175 170 const char *options[] = { 176 171 "listening_ports", "80", 177 "document_root", "/www /",172 "document_root", "/www", 178 173 "num_threads", "1", 179 "max_request_size", "2048",180 174 "thread_stack_size", "16384", 181 175 "thread_priority", "250", 182 176 "thread_policy", "o", 183 177 NULL}; 178 const struct mg_callbacks callbacks2 = { 179 NULL 180 }; 184 181 const char *options2[] = { 185 182 "listening_ports", "8080", 186 "document_root", "/www2 /",183 "document_root", "/www2", 187 184 "num_threads", "1", 188 185 "thread_stack_size", "16384", 189 "max_request_size", "2048",190 186 NULL}; 191 187 192 struct mg_context *mg1 = mg_start(&callback , NULL, options);193 struct mg_context *mg2 = mg_start( NULL, NULL, options2);188 struct mg_context *mg1 = mg_start(&callbacks, NULL, options); 189 struct mg_context *mg2 = mg_start(&callbacks2, NULL, options2); 194 190 195 191 test_mg_index_html();
Note: See TracChangeset
for help on using the changeset viewer.