mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-16 10:39:49 +00:00
459 lines
13 KiB
C
459 lines
13 KiB
C
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
|
// Copyright (c) 2013 Cesanta Software Limited
|
|
// All rights reserved
|
|
//
|
|
// This library is dual-licensed: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License version 2 as
|
|
// published by the Free Software Foundation. For the terms of this
|
|
// license, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
// You are free to use this library under the terms of the GNU General
|
|
// Public License, but WITHOUT ANY WARRANTY; without even the implied
|
|
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the GNU General Public License for more details.
|
|
//
|
|
// Alternatively, you can license this library under a commercial
|
|
// license, as set out in <http://cesanta.com/products.html>.
|
|
|
|
#if defined(_WIN32)
|
|
#undef _UNICODE
|
|
#define _MBCS
|
|
#if !defined(_CRT_SECURE_NO_WARNINGS)
|
|
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
|
|
#endif
|
|
#else
|
|
#ifdef __linux__
|
|
#define _XOPEN_SOURCE 600 // For flockfile() on Linux
|
|
#endif
|
|
#if !defined(_LARGEFILE_SOURCE)
|
|
#define _LARGEFILE_SOURCE // Enable 64-bit file offsets
|
|
#endif
|
|
#define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++
|
|
#define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX
|
|
#endif
|
|
|
|
#if defined (_MSC_VER)
|
|
// conditional expression is constant: introduced by FD_SET(..)
|
|
#pragma warning (disable : 4127)
|
|
// non-constant aggregate initializer: issued due to missing C99 support
|
|
#pragma warning (disable : 4204)
|
|
#endif
|
|
|
|
// Disable WIN32_LEAN_AND_MEAN.
|
|
// This makes windows.h always include winsock2.h
|
|
#ifdef WIN32_LEAN_AND_MEAN
|
|
#undef WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
|
|
#ifndef _WIN32_WCE // Some ANSI #includes are not available on Windows CE
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#endif // !_WIN32_WCE
|
|
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
|
|
#if defined(_WIN32) // Windows specific
|
|
#undef _WIN32_WINNT
|
|
#define _WIN32_WINNT 0x0400 // To make it link in VS2005
|
|
#include <windows.h>
|
|
|
|
#ifndef PATH_MAX
|
|
#define PATH_MAX MAX_PATH
|
|
#endif
|
|
|
|
#ifndef _WIN32_WCE
|
|
#include <process.h>
|
|
#include <direct.h>
|
|
#include <io.h>
|
|
#else // _WIN32_WCE
|
|
#define NO_CGI // WinCE has no pipes
|
|
|
|
typedef long off_t;
|
|
|
|
#define errno GetLastError()
|
|
#define strerror(x) _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10)
|
|
#endif // _WIN32_WCE
|
|
|
|
#define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \
|
|
((uint64_t)((uint32_t)(hi))) << 32))
|
|
#define RATE_DIFF 10000000 // 100 nsecs
|
|
#define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de)
|
|
#define SYS2UNIX_TIME(lo, hi) \
|
|
(time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)
|
|
|
|
// Visual Studio 6 does not know __func__ or __FUNCTION__
|
|
// The rest of MS compilers use __FUNCTION__, not C99 __func__
|
|
// Also use _strtoui64 on modern M$ compilers
|
|
#if defined(_MSC_VER) && _MSC_VER < 1300
|
|
#define STRX(x) #x
|
|
#define STR(x) STRX(x)
|
|
#define __func__ __FILE__ ":" STR(__LINE__)
|
|
#define strtoull(x, y, z) (unsigned __int64) _atoi64(x)
|
|
#define strtoll(x, y, z) _atoi64(x)
|
|
#else
|
|
#define __func__ __FUNCTION__
|
|
#define strtoull(x, y, z) _strtoui64(x, y, z)
|
|
#define strtoll(x, y, z) _strtoi64(x, y, z)
|
|
#endif // _MSC_VER
|
|
|
|
#define ERRNO GetLastError()
|
|
#define NO_SOCKLEN_T
|
|
#define SSL_LIB "ssleay32.dll"
|
|
#define CRYPTO_LIB "libeay32.dll"
|
|
#define O_NONBLOCK 0
|
|
#if !defined(EWOULDBLOCK)
|
|
#define EWOULDBLOCK WSAEWOULDBLOCK
|
|
#endif // !EWOULDBLOCK
|
|
#define _POSIX_
|
|
#define INT64_FMT "I64d"
|
|
|
|
#define WINCDECL __cdecl
|
|
#define SHUT_WR 1
|
|
#define snprintf _snprintf
|
|
#define vsnprintf _vsnprintf
|
|
#define mg_sleep(x) Sleep(x)
|
|
|
|
#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
|
|
#ifndef popen
|
|
#define popen(x, y) _popen(x, y)
|
|
#endif
|
|
#ifndef pclose
|
|
#define pclose(x) _pclose(x)
|
|
#endif
|
|
#define close(x) _close(x)
|
|
#define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y))
|
|
#define RTLD_LAZY 0
|
|
#define fseeko(x, y, z) _lseeki64(_fileno(x), (y), (z))
|
|
#define fdopen(x, y) _fdopen((x), (y))
|
|
#define write(x, y, z) _write((x), (y), (unsigned) z)
|
|
#define read(x, y, z) _read((x), (y), (unsigned) z)
|
|
#define flockfile(x)
|
|
#define funlockfile(x)
|
|
#define sleep(x) Sleep((x) * 1000)
|
|
#define rmdir(x) _rmdir(x)
|
|
|
|
#if !defined(va_copy)
|
|
#define va_copy(x, y) x = y
|
|
#endif // !va_copy MINGW #defines va_copy
|
|
|
|
#if !defined(fileno)
|
|
#define fileno(x) _fileno(x)
|
|
#endif // !fileno MINGW #defines fileno
|
|
|
|
typedef HANDLE pthread_mutex_t;
|
|
typedef DWORD pthread_t;
|
|
#define pid_t HANDLE // MINGW typedefs pid_t to int. Using #define here.
|
|
|
|
static int pthread_mutex_lock(pthread_mutex_t *);
|
|
static int pthread_mutex_unlock(pthread_mutex_t *);
|
|
static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len);
|
|
|
|
#if defined(HAVE_STDINT)
|
|
#include <stdint.h>
|
|
#else
|
|
typedef unsigned int uint32_t;
|
|
typedef unsigned short uint16_t;
|
|
typedef unsigned __int64 uint64_t;
|
|
typedef __int64 int64_t;
|
|
#define INT64_MAX 9223372036854775807
|
|
#endif // HAVE_STDINT
|
|
|
|
// POSIX dirent interface
|
|
struct dirent {
|
|
char d_name[PATH_MAX];
|
|
};
|
|
|
|
typedef struct DIR {
|
|
HANDLE handle;
|
|
WIN32_FIND_DATAW info;
|
|
struct dirent result;
|
|
} DIR;
|
|
|
|
struct pollfd {
|
|
SOCKET fd;
|
|
short events;
|
|
short revents;
|
|
};
|
|
#define POLLIN 1
|
|
|
|
#ifdef HAVE_POLL
|
|
#define poll(x, y, z) WSAPoll((x), (y), (z))
|
|
#endif
|
|
|
|
|
|
// Mark required libraries
|
|
#ifdef _MSC_VER
|
|
#pragma comment(lib, "Ws2_32.lib")
|
|
#endif
|
|
|
|
#else // UNIX specific
|
|
#include <sys/wait.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/poll.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/time.h>
|
|
#include <stdint.h>
|
|
#include <inttypes.h>
|
|
#include <netdb.h>
|
|
|
|
#include <pwd.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#if !defined(NO_SSL_DL) && !defined(NO_SSL)
|
|
#include <dlfcn.h>
|
|
#endif
|
|
#include <pthread.h>
|
|
#if defined(__MACH__)
|
|
#define SSL_LIB "libssl.dylib"
|
|
#define CRYPTO_LIB "libcrypto.dylib"
|
|
#else
|
|
#if !defined(SSL_LIB)
|
|
#define SSL_LIB "libssl.so"
|
|
#endif
|
|
#if !defined(CRYPTO_LIB)
|
|
#define CRYPTO_LIB "libcrypto.so"
|
|
#endif
|
|
#endif
|
|
#ifndef O_BINARY
|
|
#define O_BINARY 0
|
|
#endif // O_BINARY
|
|
#define closesocket(a) close(a)
|
|
#define mg_mkdir(x, y) mkdir(x, y)
|
|
#define mg_remove(x) remove(x)
|
|
#define mg_sleep(x) usleep((x) * 1000)
|
|
#define ERRNO errno
|
|
#define INVALID_SOCKET (-1)
|
|
#define INT64_FMT PRId64
|
|
typedef int SOCKET;
|
|
#define WINCDECL
|
|
|
|
#endif // End of Windows and UNIX specific includes
|
|
|
|
#include "mongoose.h"
|
|
|
|
#define MONGOOSE_VERSION "5.0"
|
|
#define PASSWORDS_FILE_NAME ".htpasswd"
|
|
#define CGI_ENVIRONMENT_SIZE 4096
|
|
#define MAX_CGI_ENVIR_VARS 64
|
|
#define MG_BUF_LEN 8192
|
|
#define MAX_REQUEST_SIZE 16384
|
|
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
|
|
|
|
typedef SOCKET sock_t;
|
|
|
|
#ifdef DEBUG_TRACE
|
|
#undef DEBUG_TRACE
|
|
#define DEBUG_TRACE(x)
|
|
#else
|
|
#if defined(DEBUG)
|
|
#define DEBUG_TRACE(x) do { \
|
|
flockfile(stdout); \
|
|
printf("*** %lu.%p.%s.%d: ", \
|
|
(unsigned long) time(NULL), (void *) pthread_self(), \
|
|
__func__, __LINE__); \
|
|
printf x; \
|
|
putchar('\n'); \
|
|
fflush(stdout); \
|
|
funlockfile(stdout); \
|
|
} while (0)
|
|
#else
|
|
#define DEBUG_TRACE(x)
|
|
#endif // DEBUG
|
|
#endif // DEBUG_TRACE
|
|
|
|
// Darwin prior to 7.0 and Win32 do not have socklen_t
|
|
#ifdef NO_SOCKLEN_T
|
|
typedef int socklen_t;
|
|
#endif // NO_SOCKLEN_T
|
|
#define _DARWIN_UNLIMITED_SELECT
|
|
|
|
#define IP_ADDR_STR_LEN 50 // IPv6 hex string is 46 chars
|
|
|
|
#if !defined(MSG_NOSIGNAL)
|
|
#define MSG_NOSIGNAL 0
|
|
#endif
|
|
|
|
#if !defined(SOMAXCONN)
|
|
#define SOMAXCONN 100
|
|
#endif
|
|
|
|
#if !defined(PATH_MAX)
|
|
#define PATH_MAX 4096
|
|
#endif
|
|
|
|
// Size of the accepted socket queue
|
|
#if !defined(MGSQLEN)
|
|
#define MGSQLEN 20
|
|
#endif
|
|
|
|
// Extra HTTP headers to send in every static file reply
|
|
#if !defined(EXTRA_HTTP_HEADERS)
|
|
#define EXTRA_HTTP_HEADERS ""
|
|
#endif
|
|
|
|
static const char *http_500_error = "Internal Server Error";
|
|
|
|
#if defined(NO_SSL_DL)
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/err.h>
|
|
#else
|
|
// SSL loaded dynamically from DLL.
|
|
// I put the prototypes here to be independent from OpenSSL source installation.
|
|
typedef struct ssl_st SSL;
|
|
typedef struct ssl_method_st SSL_METHOD;
|
|
typedef struct ssl_ctx_st SSL_CTX;
|
|
|
|
struct ssl_func {
|
|
const char *name; // SSL function name
|
|
void (*ptr)(void); // Function pointer
|
|
};
|
|
|
|
#define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
|
|
#define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
|
|
#define SSL_connect (* (int (*)(SSL *)) ssl_sw[2].ptr)
|
|
#define SSL_read (* (int (*)(SSL *, void *, int)) ssl_sw[3].ptr)
|
|
#define SSL_write (* (int (*)(SSL *, const void *,int)) ssl_sw[4].ptr)
|
|
#define SSL_get_error (* (int (*)(SSL *, int)) ssl_sw[5].ptr)
|
|
#define SSL_set_fd (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr)
|
|
#define SSL_new (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)
|
|
#define SSL_CTX_new (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)
|
|
#define SSLv23_server_method (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr)
|
|
#define SSL_library_init (* (int (*)(void)) ssl_sw[10].ptr)
|
|
#define SSL_CTX_use_PrivateKey_file (* (int (*)(SSL_CTX *, \
|
|
const char *, int)) ssl_sw[11].ptr)
|
|
#define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \
|
|
const char *, int)) ssl_sw[12].ptr)
|
|
#define SSL_CTX_set_default_passwd_cb \
|
|
(* (void (*)(SSL_CTX *, mg_event_handler_t)) ssl_sw[13].ptr)
|
|
#define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)
|
|
#define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr)
|
|
#define SSL_CTX_use_certificate_chain_file \
|
|
(* (int (*)(SSL_CTX *, const char *)) ssl_sw[16].ptr)
|
|
#define SSLv23_client_method (* (SSL_METHOD * (*)(void)) ssl_sw[17].ptr)
|
|
#define SSL_pending (* (int (*)(SSL *)) ssl_sw[18].ptr)
|
|
#define SSL_CTX_set_verify (* (void (*)(SSL_CTX *, int, int)) ssl_sw[19].ptr)
|
|
#define SSL_shutdown (* (int (*)(SSL *)) ssl_sw[20].ptr)
|
|
|
|
#define CRYPTO_num_locks (* (int (*)(void)) crypto_sw[0].ptr)
|
|
#define CRYPTO_set_locking_callback \
|
|
(* (void (*)(void (*)(int, int, const char *, int))) crypto_sw[1].ptr)
|
|
#define CRYPTO_set_id_callback \
|
|
(* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr)
|
|
#define ERR_get_error (* (unsigned long (*)(void)) crypto_sw[3].ptr)
|
|
#define ERR_error_string (* (char * (*)(unsigned long,char *)) crypto_sw[4].ptr)
|
|
#endif // NO_SSL_DL
|
|
|
|
// Unified socket address. For IPv6 support, add IPv6 address structure
|
|
// in the union u.
|
|
union usa {
|
|
struct sockaddr sa;
|
|
struct sockaddr_in sin;
|
|
#if defined(USE_IPV6)
|
|
struct sockaddr_in6 sin6;
|
|
#endif
|
|
};
|
|
|
|
// Describes a string (chunk of memory).
|
|
struct vec {
|
|
const char *ptr;
|
|
size_t len;
|
|
};
|
|
|
|
struct file {
|
|
int is_directory;
|
|
time_t modification_time;
|
|
int64_t size;
|
|
// set to 1 if the content is gzipped
|
|
// in which case we need a content-encoding: gzip header
|
|
int gzipped;
|
|
};
|
|
#define STRUCT_FILE_INITIALIZER { 0, 0, 0, 0 }
|
|
|
|
// Describes listening socket, or socket which was accept()-ed by the master
|
|
// thread and queued for future handling by the worker thread.
|
|
struct socket {
|
|
SOCKET sock; // Listening socket
|
|
union usa lsa; // Local socket address
|
|
union usa rsa; // Remote socket address
|
|
unsigned is_ssl:1; // Is port SSL-ed
|
|
unsigned ssl_redir:1; // Is port supposed to redirect everything to SSL port
|
|
};
|
|
|
|
// NOTE(lsm): this enum shoulds be in sync with the config_options.
|
|
enum {
|
|
CGI_EXTENSIONS, CGI_ENVIRONMENT, PUT_DELETE_PASSWORDS_FILE, CGI_INTERPRETER,
|
|
PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS,
|
|
ACCESS_LOG_FILE, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE,
|
|
GLOBAL_PASSWORDS_FILE, INDEX_FILES, ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST,
|
|
EXTRA_MIME_TYPES, LISTENING_PORTS, DOCUMENT_ROOT, SSL_CERTIFICATE,
|
|
NUM_THREADS, RUN_AS_USER, REWRITE, HIDE_FILES, REQUEST_TIMEOUT,
|
|
NUM_OPTIONS
|
|
};
|
|
|
|
struct mg_context {
|
|
volatile int stop_flag; // Should we stop event loop
|
|
SSL_CTX *ssl_ctx; // SSL context
|
|
char *config[NUM_OPTIONS]; // Mongoose configuration parameters
|
|
mg_event_handler_t event_handler; // User-defined callback function
|
|
void *user_data; // User-defined data
|
|
struct socket *listening_sockets;
|
|
int num_listening_sockets;
|
|
int num_threads; // Number of threads
|
|
sock_t ctl[2]; // Socket pair for inter-thread communication
|
|
|
|
};
|
|
|
|
struct mg_connection {
|
|
struct mg_request_info request_info;
|
|
struct mg_event event;
|
|
struct mg_context *ctx;
|
|
SSL *ssl; // SSL descriptor
|
|
SSL_CTX *client_ssl_ctx; // SSL context for client connections
|
|
struct socket client; // Connected client
|
|
time_t birth_time; // Time when request was received
|
|
int64_t num_bytes_sent; // Total bytes sent to client
|
|
int64_t content_len; // Content-Length header value
|
|
int64_t num_bytes_read; // Bytes read from a remote socket
|
|
char *buf; // Buffer for received data
|
|
char *path_info; // PATH_INFO part of the URL
|
|
int must_close; // 1 if connection must be closed
|
|
int buf_size; // Buffer size
|
|
int request_len; // Size of the request + headers in a buffer
|
|
int data_len; // Total size of data in a buffer
|
|
int status_code; // HTTP reply status code, e.g. 200
|
|
};
|
|
|
|
// Directory entry
|
|
struct de {
|
|
struct mg_connection *conn;
|
|
char *file_name;
|
|
struct file file;
|
|
};
|
|
|
|
static FILE *mg_fopen(const char *path, const char *mode);
|
|
static int mg_stat(const char *path, struct file *filep);
|
|
static void send_http_error(struct mg_connection *, int, const char *,
|
|
PRINTF_FORMAT_STRING(const char *fmt), ...)
|
|
PRINTF_ARGS(4, 5);
|
|
static void cry(struct mg_connection *conn,
|
|
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
|
|
static const char *getreq(struct mg_connection *conn);
|
|
|
|
#ifdef USE_LUA
|
|
#include "lua_5.2.1.h"
|
|
static int handle_lsp_request(struct mg_connection *, const char *,
|
|
struct file *, struct lua_State *);
|
|
#endif
|