From cb2c421fdb815fbf619614e2792f6260541b789e Mon Sep 17 00:00:00 2001 From: Torsten Dreyer Date: Fri, 29 May 2015 11:33:06 +0200 Subject: [PATCH] Pull in latest mongoose HEAD --- 3rdparty/mongoose/mongoose.c | 557 ++++++++++++++++++++++++----------- 3rdparty/mongoose/mongoose.h | 28 +- 2 files changed, 405 insertions(+), 180 deletions(-) diff --git a/3rdparty/mongoose/mongoose.c b/3rdparty/mongoose/mongoose.c index 84c2d6094..600c6fedd 100644 --- a/3rdparty/mongoose/mongoose.c +++ b/3rdparty/mongoose/mongoose.c @@ -14,8 +14,6 @@ // // Alternatively, you can license this library under a commercial // license, as set out in . -// -// $Date: 2014-09-16 06:47:40 UTC $ #ifdef NOEMBED_NET_SKELETON #include "net_skeleton.h" @@ -36,8 +34,6 @@ // // Alternatively, you can license this software under a commercial // license, as set out in . -// -// $Date: 2014-09-28 05:04:41 UTC $ #ifndef NS_SKELETON_HEADER_INCLUDED #define NS_SKELETON_HEADER_INCLUDED @@ -48,9 +44,13 @@ #undef _UNICODE // Use multibyte encoding on Windows #define _MBCS // Use multibyte encoding on Windows #define _INTEGRAL_MAX_BITS 64 // Enable _stati64() on Windows +#ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005+ +#endif #undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h +#ifdef __Linux__ #define _XOPEN_SOURCE 600 // For flockfile() on Linux +#endif #define __STDC_FORMAT_MACROS // wants this for C++ #define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX #ifndef _LARGEFILE_SOURCE @@ -63,6 +63,18 @@ #pragma warning (disable : 4204) // missing c99 support #endif +#if defined(_WIN32) && !defined(MONGOOSE_NO_CGI) +#define MONGOOSE_ENABLE_THREADS /* Windows uses stdio threads for CGI */ +#endif + +#ifndef MONGOOSE_ENABLE_THREADS +#define NS_DISABLE_THREADS +#endif + +#ifdef __OS2__ +#define _MMAP_DECLARED // Prevent dummy mmap() declaration in stdio.h +#endif + #include #include #include @@ -80,7 +92,14 @@ #ifdef _WIN32 #ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") // Linking with winsock library +#include +typedef SSIZE_T ssize_t; +#endif +#ifndef FD_SETSIZE +#define FD_SETSIZE 1024 #endif +#include +#include #include #include #ifndef EINPROGRESS @@ -124,7 +143,12 @@ typedef struct _stati64 ns_stat_t; #include #include #define closesocket(x) close(x) +#ifndef __OS2__ #define __cdecl +#else +#include +typedef int socklen_t; +#endif #define INVALID_SOCKET (-1) #define to64(x) strtoll(x, NULL, 10) typedef int sock_t; @@ -214,6 +238,7 @@ struct ns_connection { sock_t sock; // Socket union socket_address sa; // Peer address + size_t recv_iobuf_limit; /* Max size of recv buffer */ struct iobuf recv_iobuf; // Received data struct iobuf send_iobuf; // Data scheduled for sending SSL *ssl; @@ -255,7 +280,7 @@ struct ns_connection *ns_bind(struct ns_mgr *, const char *, struct ns_connection *ns_connect(struct ns_mgr *, const char *, ns_callback_t, void *); -int ns_send(struct ns_connection *, const void *buf, int len); +int ns_send(struct ns_connection *, const void *buf, size_t len); int ns_printf(struct ns_connection *, const char *fmt, ...); int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap); @@ -305,12 +330,18 @@ int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); #define NS_FREE free #endif +#ifndef NS_CALLOC +#define NS_CALLOC calloc +#endif + +#define NS_CTL_MSG_MESSAGE_SIZE (8 * 1024) +#define NS_READ_BUFFER_SIZE 2048 #define NS_UDP_RECEIVE_BUFFER_SIZE 2000 #define NS_VPRINTF_BUFFER_SIZE 500 struct ctl_msg { ns_callback_t callback; - char message[1024 * 8]; + char message[NS_CTL_MSG_MESSAGE_SIZE]; }; void iobuf_resize(struct iobuf *io, size_t new_size) { @@ -341,6 +372,11 @@ size_t iobuf_append(struct iobuf *io, const void *buf, size_t len) { assert(io != NULL); assert(io->len <= io->size); + /* check overflow */ + if (len > ~(size_t)0 - (size_t)(io->buf + io->len)) { + return 0; + } + if (len <= 0) { } else if (io->len + len <= io->size) { memcpy(io->buf + io->len, buf, len); @@ -366,7 +402,8 @@ void iobuf_remove(struct iobuf *io, size_t n) { static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) { if (nc->flags & NSF_UDP) { - long n = sendto(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); + long n = sendto(nc->sock, (const char *) buf, len, 0, &nc->sa.sa, + sizeof(nc->sa.sin)); DBG(("%p %d send %ld (%d %s)", nc, nc->sock, n, errno, strerror(errno))); return n < 0 ? 0 : n; } else { @@ -427,7 +464,7 @@ int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) { // succeed or out of memory. *buf = NULL; while (len < 0) { - if (*buf) free(*buf); + if (*buf) NS_FREE(*buf); size *= 2; if ((*buf = (char *) NS_MALLOC(size)) == NULL) break; va_copy(ap_copy, ap); @@ -456,7 +493,7 @@ int ns_vprintf(struct ns_connection *nc, const char *fmt, va_list ap) { ns_out(nc, buf, len); } if (buf != mem && buf != NULL) { - free(buf); + NS_FREE(buf); } return len; @@ -490,7 +527,7 @@ static void hexdump(struct ns_connection *nc, const char *path, ns_hexdump(io->buf + (ev == NS_SEND ? 0 : io->len) - (ev == NS_SEND ? 0 : num_bytes), num_bytes, buf, buf_size); fprintf(fp, "%s", buf); - free(buf); + NS_FREE(buf); } fclose(fp); } @@ -589,6 +626,28 @@ int ns_socketpair(sock_t sp[2]) { // TODO(lsm): use non-blocking resolver static int ns_resolve2(const char *host, struct in_addr *ina) { +#ifdef NS_ENABLE_GETADDRINFO + int rv = 0; + struct addrinfo hints, *servinfo, *p; + struct sockaddr_in *h = NULL; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + if((rv = getaddrinfo(host, NULL , NULL, &servinfo)) != 0) { + DBG(("getaddrinfo(%s) failed: %s", host, strerror(errno))); + return 0; + } + + for(p = servinfo; p != NULL; p = p->ai_next) { + memcpy(&h, &p->ai_addr, sizeof(struct sockaddr_in *)); + memcpy(ina, &h->sin_addr, sizeof(ina)); + } + + freeaddrinfo(servinfo); + return 1; +#else struct hostent *he; if ((he = gethostbyname(host)) == NULL) { DBG(("gethostbyname(%s) failed: %s", host, strerror(errno))); @@ -597,6 +656,7 @@ static int ns_resolve2(const char *host, struct in_addr *ina) { return 1; } return 0; +#endif } // Resolve FDQN "host", store IP address in the "ip". @@ -655,8 +715,8 @@ static int ns_parse_address(const char *str, union socket_address *sa, sa->sin.sin_port = htons((uint16_t) port); } - if (*use_ssl && (sscanf(str + len, ":%99[^:]:%99[^:]%n", cert, ca, &n) == 2 || - sscanf(str + len, ":%99[^:]%n", cert, &n) == 1)) { + if (*use_ssl && (sscanf(str + len, ":%99[^:,]:%99[^:,]%n", cert, ca, &n) == 2 || + sscanf(str + len, ":%99[^:,]%n", cert, &n) == 1)) { len += n; } @@ -868,7 +928,7 @@ static int ns_ssl_err(struct ns_connection *conn, int res) { #endif static void ns_read_from_socket(struct ns_connection *conn) { - char buf[2048]; + char buf[NS_READ_BUFFER_SIZE]; int n = 0; if (conn->flags & NSF_CONNECTING) { @@ -889,6 +949,7 @@ static void ns_read_from_socket(struct ns_connection *conn) { } else { ok = 1; } + conn->flags &= ~(NSF_WANT_READ | NSF_WANT_WRITE); } #endif conn->flags &= ~NSF_CONNECTING; @@ -917,6 +978,7 @@ static void ns_read_from_socket(struct ns_connection *conn) { int ssl_err = ns_ssl_err(conn, res); if (res == 1) { conn->flags |= NSF_SSL_HANDSHAKE_DONE; + conn->flags &= ~(NSF_WANT_READ | NSF_WANT_WRITE); } else if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) { return; // Call us again @@ -954,6 +1016,8 @@ static void ns_write_to_socket(struct ns_connection *conn) { } else { conn->flags |= NSF_CLOSE_IMMEDIATELY; } + } else { + conn->flags &= ~(NSF_WANT_READ | NSF_WANT_WRITE); } } else #endif @@ -969,14 +1033,14 @@ static void ns_write_to_socket(struct ns_connection *conn) { } } -int ns_send(struct ns_connection *conn, const void *buf, int len) { +int ns_send(struct ns_connection *conn, const void *buf, size_t len) { return (int) ns_out(conn, buf, len); } static void ns_handle_udp(struct ns_connection *ls) { struct ns_connection nc; char buf[NS_UDP_RECEIVE_BUFFER_SIZE]; - int n; + ssize_t n; socklen_t s_len = sizeof(nc.sa); memset(&nc, 0, sizeof(nc)); @@ -1000,7 +1064,7 @@ static void ns_handle_udp(struct ns_connection *ls) { } static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { - if (sock != INVALID_SOCKET) { + if ( (sock != INVALID_SOCKET) && (sock < FD_SETSIZE) ) { FD_SET(sock, set); if (*max_fd == INVALID_SOCKET || sock > *max_fd) { *max_fd = sock; @@ -1024,18 +1088,19 @@ time_t ns_mgr_poll(struct ns_mgr *mgr, int milli) { if (!(conn->flags & (NSF_LISTENING | NSF_CONNECTING))) { ns_call(conn, NS_POLL, ¤t_time); } - if (!(conn->flags & NSF_WANT_WRITE)) { - //DBG(("%p read_set", conn)); - ns_add_to_set(conn->sock, &read_set, &max_fd); - } - if (((conn->flags & NSF_CONNECTING) && !(conn->flags & NSF_WANT_READ)) || - (conn->send_iobuf.len > 0 && !(conn->flags & NSF_CONNECTING) && - !(conn->flags & NSF_BUFFER_BUT_DONT_SEND))) { - //DBG(("%p write_set", conn)); - ns_add_to_set(conn->sock, &write_set, &max_fd); - } if (conn->flags & NSF_CLOSE_IMMEDIATELY) { ns_close_conn(conn); + } else { + if (!(conn->flags & NSF_WANT_WRITE)) { + //DBG(("%p read_set", conn)); + ns_add_to_set(conn->sock, &read_set, &max_fd); + } + if (((conn->flags & NSF_CONNECTING) && !(conn->flags & NSF_WANT_READ)) || + (conn->send_iobuf.len > 0 && !(conn->flags & NSF_CONNECTING) && + !(conn->flags & NSF_BUFFER_BUT_DONT_SEND))) { + //DBG(("%p write_set", conn)); + ns_add_to_set(conn->sock, &write_set, &max_fd); + } } } @@ -1247,21 +1312,21 @@ void ns_mgr_free(struct ns_mgr *s) { #define fopen(x, y) mg_fopen((x), (y)) #define open(x, y, z) mg_open((x), (y), (z)) #define close(x) _close(x) +#define fileno(x) _fileno(x) #define lseek(x, y, z) _lseeki64((x), (y), (z)) +#define read(x, y, z) _read((x), (y), (z)) +#define write(x, y, z) _write((x), (y), (z)) #define popen(x, y) _popen((x), (y)) #define pclose(x) _pclose(x) #define mkdir(x, y) _mkdir(x) +#define rmdir(x) _rmdir(x) +#define strdup(x) _strdup(x) #ifndef __func__ #define STRX(x) #x #define STR(x) STRX(x) #define __func__ __FILE__ ":" STR(__LINE__) #endif -/* MINGW has adopted the MSVC formatting for 64-bit ints as of gcc 4.4 till 4.8*/ -#if (defined(__MINGW32__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4 && __GNUC_MINOR__ < 8))) || defined(_MSC_VER) #define INT64_FMT "I64d" -#else -#define INT64_FMT "lld" -#endif #define flockfile(x) ((void) (x)) #define funlockfile(x) ((void) (x)) typedef struct _stati64 file_stat_t; @@ -1269,11 +1334,18 @@ typedef HANDLE process_id_t; #else ////////////// UNIX specific defines and includes +#if !defined(MONGOOSE_NO_FILESYSTEM) &&\ + (!defined(MONGOOSE_NO_DAV) || !defined(MONGOOSE_NO_DIRECTORY_LISTING)) #include +#endif +#if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_DL) #include +#endif #include #include +#if !defined(O_BINARY) #define O_BINARY 0 +#endif #define INT64_FMT PRId64 typedef struct stat file_stat_t; typedef pid_t process_id_t; @@ -1307,13 +1379,15 @@ typedef pid_t process_id_t; #define MONGOOSE_IDLE_TIMEOUT_SECONDS 300 #endif -#ifdef NS_DISABLE_SOCKETPAIR +#if defined(NS_DISABLE_SOCKETPAIR) && !defined(MONGOOSE_NO_CGI) #define MONGOOSE_NO_CGI #endif #ifdef MONGOOSE_NO_FILESYSTEM #define MONGOOSE_NO_AUTH +#if !defined(MONGOOSE_NO_CGI) #define MONGOOSE_NO_CGI +#endif #define MONGOOSE_NO_DAV #define MONGOOSE_NO_DIRECTORY_LISTING #define MONGOOSE_NO_LOGGING @@ -1323,7 +1397,7 @@ typedef pid_t process_id_t; struct vec { const char *ptr; - int len; + size_t len; }; // For directory listing and WevDAV support @@ -1333,7 +1407,7 @@ struct dir_entry { file_stat_t st; }; -// NOTE(lsm): this enum shoulds be in sync with the config_options. +// NOTE(lsm): this enum should be in sync with the config_options. enum { ACCESS_CONTROL_LIST, #ifndef MONGOOSE_NO_FILESYSTEM @@ -1346,6 +1420,7 @@ enum { CGI_PATTERN, #endif DAV_AUTH_FILE, + DAV_ROOT, DOCUMENT_ROOT, #ifndef MONGOOSE_NO_DIRECTORY_LISTING ENABLE_DIRECTORY_LISTING, @@ -1384,6 +1459,7 @@ static const char *static_config_options[] = { "cgi_pattern", DEFAULT_CGI_PATTERN, #endif "dav_auth_file", NULL, + "dav_root", NULL, "document_root", NULL, #ifndef MONGOOSE_NO_DIRECTORY_LISTING "enable_directory_listing", "yes", @@ -1428,7 +1504,7 @@ enum endpoint_type { }; #define MG_HEADERS_SENT NSF_USER_1 -#define MG_LONG_RUNNING NSF_USER_2 +#define MG_USING_CHUNKED_API NSF_USER_2 #define MG_CGI_CONN NSF_USER_3 #define MG_PROXY_CONN NSF_USER_4 #define MG_PROXY_DONT_PARSE NSF_USER_5 @@ -1443,7 +1519,7 @@ struct connection { char *request; int64_t num_bytes_recv; // Total number of bytes received int64_t cl; // Reply content length, for Range support - int request_len; // Request length, including last \r\n after last header + ssize_t request_len; // Request length, including last \r\n after last header }; #define MG_CONN_2_CONN(c) ((struct connection *) ((char *) (c) - \ @@ -1463,7 +1539,7 @@ static const struct { {".shtm", 5, "text/html"}, {".shtml", 6, "text/html"}, {".css", 4, "text/css"}, - {".js", 3, "application/x-javascript"}, + {".js", 3, "application/javascript"}, {".ico", 4, "image/x-icon"}, {".gif", 4, "image/gif"}, {".jpg", 4, "image/jpeg"}, @@ -1508,11 +1584,11 @@ static const struct { {NULL, 0, NULL} }; -#ifndef MONGOOSE_NO_THREADS +#ifdef MONGOOSE_ENABLE_THREADS void *mg_start_thread(void *(*f)(void *), void *p) { return ns_start_thread(f, p); } -#endif // MONGOOSE_NO_THREADS +#endif // MONGOOSE_ENABLE_THREADS #ifndef MONGOOSE_NO_MMAP #ifdef _WIN32 @@ -1528,6 +1604,37 @@ static void *mmap(void *addr, int64_t len, int prot, int flags, int fd, #define MAP_FAILED NULL #define MAP_PRIVATE 0 #define PROT_READ 0 +#elif defined(__OS2__) +static void *mmap(void *addr, int64_t len, int prot, int flags, int fd, + int offset) { + void *p; + + int pos = lseek( fd, 0, SEEK_CUR ); /* Get a current position */ + + if (pos == -1) + return NULL; + + /* Seek to offset offset */ + if (lseek( fd, offset, SEEK_SET) == -1) + return NULL; + + p = malloc(len); + + /* Read in a file */ + if (!p || read(fd, p, len) == -1) { + free(p); + p = NULL; + } + + /* Restore the position */ + lseek(fd, pos, SEEK_SET); + + return p; +} +#define munmap(x, y) free(x) +#define MAP_FAILED NULL +#define MAP_PRIVATE 0 +#define PROT_READ 0 #else #include #endif @@ -1654,9 +1761,9 @@ static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) { // -1 if request is malformed // 0 if request is not yet fully buffered // >0 actual request length, including last \r\n\r\n -static int get_request_len(const char *s, int buf_len) { +static int get_request_len(const char *s, size_t buf_len) { const unsigned char *buf = (unsigned char *) s; - int i; + size_t i; for (i = 0; i < buf_len; i++) { // Control characters are not allowed but >=128 are. @@ -1844,13 +1951,21 @@ static void write_chunk(struct connection *conn, const char *buf, int len) { } size_t mg_printf(struct mg_connection *conn, const char *fmt, ...) { - struct connection *c = MG_CONN_2_CONN(conn); va_list ap; + int ret; va_start(ap, fmt); - ns_vprintf(c->ns_conn, fmt, ap); + ret = mg_vprintf(conn, fmt, ap); va_end(ap); + return ret; +} + +size_t mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) { + struct connection *c = MG_CONN_2_CONN(conn); + + ns_vprintf(c->ns_conn, fmt, ap); + return c->ns_conn->send_iobuf.len; } @@ -1869,6 +1984,8 @@ struct threadparam { static int wait_until_ready(sock_t sock, int for_read) { fd_set set; + if ( (sock == INVALID_SOCKET) || (sock >= FD_SETSIZE) ) + return 0; FD_ZERO(&set); FD_SET(sock, &set); select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0); @@ -1888,9 +2005,9 @@ static void *push_to_stdin(void *arg) { if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0)) stop = 1; } } - DBG(("%s", "FORWARED EVERYTHING TO CGI")); + DBG(("%s", "FORWARDED EVERYTHING TO CGI")); CloseHandle(tp->hPipe); - free(tp); + NS_FREE(tp); _endthread(); return NULL; } @@ -1911,14 +2028,14 @@ static void *pull_from_stdout(void *arg) { CloseHandle(tp->hPipe); shutdown(tp->s, 2); // Without this, IO thread may get truncated data closesocket(tp->s); - free(tp); + NS_FREE(tp); _endthread(); return NULL; } static void spawn_stdio_thread(sock_t sock, HANDLE hPipe, void *(*func)(void *)) { - struct threadparam *tp = (struct threadparam *)malloc(sizeof(*tp)); + struct threadparam *tp = (struct threadparam *)NS_MALLOC(sizeof(*tp)); if (tp != NULL) { tp->s = sock; tp->hPipe = hPipe; @@ -2247,7 +2364,7 @@ static void on_cgi_data(struct ns_connection *nc) { // If reply has not been parsed yet, parse it if (conn->ns_conn->flags & NSF_BUFFER_BUT_DONT_SEND) { struct iobuf *io = &conn->ns_conn->send_iobuf; - int s_len = sizeof(cgi_status) - 1; + size_t s_len = sizeof(cgi_status) - 1; int len = get_request_len(io->buf + s_len, io->len - s_len); char buf[MAX_REQUEST_SIZE], *s = buf; @@ -2277,7 +2394,7 @@ static void on_cgi_data(struct ns_connection *nc) { #endif // !MONGOOSE_NO_CGI static char *mg_strdup(const char *str) { - char *copy = (char *) malloc(strlen(str) + 1); + char *copy = (char *) NS_MALLOC(strlen(str) + 1); if (copy != NULL) { strcpy(copy, str); } @@ -2339,7 +2456,9 @@ static void remove_double_dots_and_double_slashes(char *s) { // Skip all following slashes, backslashes and double-dots while (s[0] != '\0') { if (s[0] == '/' || s[0] == '\\') { s++; } - else if (s[0] == '.' && s[1] == '.') { s += 2; } + else if (s[0] == '.' && (s[1] == '/' || s[1] == '\\')) { s += 2; } + else if (s[0] == '.' && s[1] == '.' && s[2] == '\0') { s += 2; } + else if (s[0] == '.' && s[1] == '.' && (s[2] == '/' || s[2] == '\\')) { s += 3; } else { break; } } } @@ -2347,13 +2466,14 @@ static void remove_double_dots_and_double_slashes(char *s) { *p = '\0'; } -int mg_url_decode(const char *src, int src_len, char *dst, - int dst_len, int is_form_url_encoded) { - int i, j, a, b; -#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') +int mg_url_decode(const char *src, size_t src_len, char *dst, + size_t dst_len, int is_form_url_encoded) { + size_t i, j = 0; + int a, b; +#define HEXTOI(x) (isdigit(x) ? (x) - '0' : (x) - 'W') for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { - if (src[i] == '%' && i < src_len - 2 && + if (src[i] == '%' && i + 2 < src_len && isxdigit(* (const unsigned char *) (src + i + 1)) && isxdigit(* (const unsigned char *) (src + i + 2))) { a = tolower(* (const unsigned char *) (src + i + 1)); @@ -2375,14 +2495,16 @@ int mg_url_decode(const char *src, int src_len, char *dst, static int is_valid_http_method(const char *s) { return !strcmp(s, "GET") || !strcmp(s, "POST") || !strcmp(s, "HEAD") || !strcmp(s, "CONNECT") || !strcmp(s, "PUT") || !strcmp(s, "DELETE") || - !strcmp(s, "OPTIONS") || !strcmp(s, "PROPFIND") || !strcmp(s, "MKCOL"); + !strcmp(s, "OPTIONS") || !strcmp(s, "PROPFIND") || !strcmp(s, "MKCOL") || + !strcmp(s, "PATCH"); } // Parse HTTP request, fill in mg_request structure. // This function modifies the buffer by NUL-terminating // HTTP request components, header names and header values. // Note that len must point to the last \n of HTTP headers. -static int parse_http_message(char *buf, int len, struct mg_connection *ri) { +static size_t parse_http_message(char *buf, size_t len, + struct mg_connection *ri) { int is_request, n; // Reset the connection. Make sure that we don't touch fields that are @@ -2390,9 +2512,11 @@ static int parse_http_message(char *buf, int len, struct mg_connection *ri) { ri->request_method = ri->uri = ri->http_version = ri->query_string = NULL; ri->num_headers = ri->status_code = ri->is_websocket = ri->content_len = 0; + if (len < 1) return ~0; + buf[len - 1] = '\0'; - // RFC says that all initial whitespaces should be ingored + // RFC says that all initial whitespaces should be ignored while (*buf != '\0' && isspace(* (unsigned char *) buf)) { buf++; } @@ -2405,7 +2529,7 @@ static int parse_http_message(char *buf, int len, struct mg_connection *ri) { is_request = is_valid_http_method(ri->request_method); if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) || (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) { - len = -1; + len = ~0; } else { if (is_request) { ri->http_version += 5; @@ -2464,7 +2588,7 @@ const char *mg_get_header(const struct mg_connection *ri, const char *s) { } // Perform case-insensitive match of string against pattern -int mg_match_prefix(const char *pattern, int pattern_len, const char *str) { +int mg_match_prefix(const char *pattern, ssize_t pattern_len, const char *str) { const char *or_str; int len, res, i = 0, j = 0; @@ -2536,6 +2660,12 @@ void mg_template(struct mg_connection *conn, const char *s, } #ifndef MONGOOSE_NO_FILESYSTEM +static int is_dav_request(const struct connection *conn) { + const char *s = conn->mg_conn.request_method; + return !strcmp(s, "PUT") || !strcmp(s, "DELETE") || + !strcmp(s, "MKCOL") || !strcmp(s, "PROPFIND"); +} + static int must_hide_file(struct connection *conn, const char *path) { const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$"; const char *pattern = conn->server->config_options[HIDE_FILES_PATTERN]; @@ -2548,19 +2678,25 @@ static int convert_uri_to_file_name(struct connection *conn, char *buf, size_t buf_len, file_stat_t *st) { struct vec a, b; const char *rewrites = conn->server->config_options[URL_REWRITES]; - const char *root = conn->server->config_options[DOCUMENT_ROOT]; + const char *root = +#ifndef MONGOOSE_NO_DAV + is_dav_request(conn) && conn->server->config_options[DAV_ROOT] != NULL ? + conn->server->config_options[DAV_ROOT] : +#endif + conn->server->config_options[DOCUMENT_ROOT]; #ifndef MONGOOSE_NO_CGI const char *cgi_pat = conn->server->config_options[CGI_PATTERN]; char *p; #endif const char *uri = conn->mg_conn.uri; const char *domain = mg_get_header(&conn->mg_conn, "Host"); + // Important: match_len has to be declared as int, unless rewrites break. int match_len, root_len = root == NULL ? 0 : strlen(root); // Perform virtual hosting rewrites if (rewrites != NULL && domain != NULL) { const char *colon = strchr(domain, ':'); - int domain_len = colon == NULL ? (int) strlen(domain) : colon - domain; + size_t domain_len = colon == NULL ? strlen(domain) : colon - domain; while ((rewrites = next_option(rewrites, &a, &b)) != NULL) { if (a.len > 1 && a.ptr[0] == '@' && a.len == domain_len + 1 && @@ -2620,25 +2756,29 @@ static int should_keep_alive(const struct mg_connection *conn) { (header == NULL && http_version && !strcmp(http_version, "1.1"))); } -size_t mg_write(struct mg_connection *c, const void *buf, int len) { +size_t mg_write(struct mg_connection *c, const void *buf, size_t len) { struct connection *conn = MG_CONN_2_CONN(c); ns_send(conn->ns_conn, buf, len); return conn->ns_conn->send_iobuf.len; } void mg_send_status(struct mg_connection *c, int status) { + struct connection *conn = MG_CONN_2_CONN(c); if (c->status_code == 0) { c->status_code = status; mg_printf(c, "HTTP/1.1 %d %s\r\n", status, status_code_to_str(status)); } + conn->ns_conn->flags |= MG_USING_CHUNKED_API; } void mg_send_header(struct mg_connection *c, const char *name, const char *v) { + struct connection *conn = MG_CONN_2_CONN(c); if (c->status_code == 0) { c->status_code = 200; mg_printf(c, "HTTP/1.1 %d %s\r\n", 200, status_code_to_str(200)); } mg_printf(c, "%s: %s\r\n", name, v); + conn->ns_conn->flags |= MG_USING_CHUNKED_API; } static void terminate_headers(struct mg_connection *c) { @@ -2658,22 +2798,30 @@ size_t mg_send_data(struct mg_connection *c, const void *data, int data_len) { } size_t mg_printf_data(struct mg_connection *c, const char *fmt, ...) { - struct connection *conn = MG_CONN_2_CONN(c); va_list ap; + int ret; + + va_start(ap, fmt); + ret = mg_vprintf_data(c, fmt, ap); + va_end(ap); + + return ret; +} + +size_t mg_vprintf_data(struct mg_connection *c, const char *fmt, va_list ap) { + struct connection *conn = MG_CONN_2_CONN(c); int len; char mem[IOBUF_SIZE], *buf = mem; terminate_headers(c); - va_start(ap, fmt); len = ns_avprintf(&buf, sizeof(mem), fmt, ap); - va_end(ap); if (len >= 0) { write_chunk((struct connection *) conn, buf, len); } if (buf != mem && buf != NULL) { - free(buf); + NS_FREE(buf); } return conn->ns_conn->send_iobuf.len; } @@ -2780,8 +2928,8 @@ static void SHA1Init(SHA1_CTX *context) { } static void SHA1Update(SHA1_CTX *context, const unsigned char *data, - uint32_t len) { - uint32_t i, j; + size_t len) { + size_t i, j; j = context->count[0]; if ((context->count[0] += len << 3) < j) @@ -2869,10 +3017,10 @@ static void send_websocket_handshake(struct mg_connection *conn, mg_write(conn, buf, strlen(buf)); } -static int deliver_websocket_frame(struct connection *conn) { +static size_t deliver_websocket_frame(struct connection *conn) { // Having buf unsigned char * is important, as it is used below in arithmetic unsigned char *buf = (unsigned char *) conn->ns_conn->recv_iobuf.buf; - int i, len, buf_len = conn->ns_conn->recv_iobuf.len, frame_len = 0, + size_t i, len, buf_len = conn->ns_conn->recv_iobuf.len, frame_len = 0, mask_len = 0, header_len = 0, data_len = 0, buffered = 0; if (buf_len >= 2) { @@ -2883,10 +3031,10 @@ static int deliver_websocket_frame(struct connection *conn) { header_len = 2 + mask_len; } else if (len == 126 && buf_len >= 4 + mask_len) { header_len = 4 + mask_len; - data_len = ((((int) buf[2]) << 8) + buf[3]); + data_len = ((((size_t) buf[2]) << 8) + buf[3]); } else if (buf_len >= 10 + mask_len) { header_len = 10 + mask_len; - data_len = (int) (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) + + data_len = (size_t) (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) + htonl(* (uint32_t *) &buf[6]); } } @@ -2907,7 +3055,8 @@ static int deliver_websocket_frame(struct connection *conn) { } // Call the handler and remove frame from the iobuf - if (call_user(conn, MG_REQUEST) == MG_FALSE) { + if (call_user(conn, MG_REQUEST) == MG_FALSE || + (buf[0] & 0x0f) == WEBSOCKET_OPCODE_CONNECTION_CLOSE) { conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA; } iobuf_remove(&conn->ns_conn->recv_iobuf, frame_len); @@ -2917,12 +3066,17 @@ static int deliver_websocket_frame(struct connection *conn) { } size_t mg_websocket_write(struct mg_connection *conn, int opcode, - const char *data, size_t data_len) { + const char *data, size_t data_len) { unsigned char mem[4192], *copy = mem; size_t copy_len = 0; + /* Check overflow */ + if (data_len > ~(size_t)0 - (size_t)10) { + return 0; + } + if (data_len + 10 > sizeof(mem) && - (copy = (unsigned char *) malloc(data_len + 10)) == NULL) { + (copy = (unsigned char *) NS_MALLOC(data_len + 10)) == NULL) { return 0; } @@ -2942,10 +3096,11 @@ size_t mg_websocket_write(struct mg_connection *conn, int opcode, copy_len = 4 + data_len; } else { // 64-bit length field + const uint32_t hi = htonl((uint32_t) ((uint64_t) data_len >> 32)); + const uint32_t lo = htonl(data_len & 0xffffffff); copy[1] = 127; - * (uint32_t *) (copy + 2) = (uint32_t) - htonl((uint32_t) ((uint64_t) data_len >> 32)); - * (uint32_t *) (copy + 6) = (uint32_t) htonl(data_len & 0xffffffff); + memcpy(copy+2,&hi,sizeof(hi)); + memcpy(copy+6,&lo,sizeof(lo)); memcpy(copy + 10, data, data_len); copy_len = 10 + data_len; } @@ -2954,7 +3109,7 @@ size_t mg_websocket_write(struct mg_connection *conn, int opcode, mg_write(conn, copy, copy_len); } if (copy != mem) { - free(copy); + NS_FREE(copy); } // If we send closing frame, schedule a connection to be closed after @@ -2979,7 +3134,7 @@ size_t mg_websocket_printf(struct mg_connection *conn, int opcode, va_end(ap); if (buf != mem && buf != NULL) { - free(buf); + NS_FREE(buf); } return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len; @@ -3014,7 +3169,8 @@ static int call_request_handler(struct connection *conn) { int result; conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf; if ((result = call_user(conn, MG_REQUEST)) == MG_TRUE) { - if (conn->ns_conn->flags & MG_HEADERS_SENT) { + if (conn->ns_conn->flags & MG_USING_CHUNKED_API) { + terminate_headers(&conn->mg_conn); write_terminating_chunk(conn); } close_local_endpoint(conn); @@ -3141,7 +3297,8 @@ static int find_index_file(struct connection *conn, char *path, const char *list = conn->server->config_options[INDEX_FILES]; file_stat_t st; struct vec filename_vec; - size_t n = strlen(path), found = 0; + size_t n = strlen(path); + int found = 0; // The 'path' given to us points to the directory. Remove all trailing // directory separator characters from the end of the path, and @@ -3155,8 +3312,12 @@ static int find_index_file(struct connection *conn, char *path, // path and see if the file exists. If it exists, break the loop while ((list = next_option(list, &filename_vec, NULL)) != NULL) { + if (path_len <= n + 2) { + continue; + } + // Ignore too long entries that may overflow path buffer - if (filename_vec.len > (int) (path_len - (n + 2))) + if (filename_vec.len > (path_len - (n + 2))) continue; // Prepare full path to the index file @@ -3176,7 +3337,8 @@ static int find_index_file(struct connection *conn, char *path, // If no index file exists, restore directory path if (!found) { - path[n] = '\0'; + path[n] = '/'; + path[n + 1] = '\0'; } return found; @@ -3194,7 +3356,7 @@ static void open_file_endpoint(struct connection *conn, const char *path, file_stat_t *st, const char *extra_headers) { char date[64], lm[64], etag[64], range[64], headers[1000]; const char *msg = "OK", *hdr; - time_t curtime = time(NULL); + time_t t, curtime = time(NULL); int64_t r1, r2; struct vec mime_vec; int n; @@ -3224,7 +3386,8 @@ static void open_file_endpoint(struct connection *conn, const char *path, // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 gmt_time_string(date, sizeof(date), &curtime); - gmt_time_string(lm, sizeof(lm), &st->st_mtime); + t = st->st_mtime; // store in local variable for NDK compile + gmt_time_string(lm, sizeof(lm), &t); construct_etag(etag, sizeof(etag), st); n = mg_snprintf(headers, sizeof(headers), @@ -3292,7 +3455,7 @@ static DIR *opendir(const char *name) { if (name == NULL) { SetLastError(ERROR_BAD_ARGUMENTS); - } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) { + } else if ((dir = (DIR *) NS_MALLOC(sizeof(*dir))) == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); } else { to_wchar(name, wpath, ARRAY_SIZE(wpath)); @@ -3303,7 +3466,7 @@ static DIR *opendir(const char *name) { dir->handle = FindFirstFileW(wpath, &dir->info); dir->result.d_name[0] = '\0'; } else { - free(dir); + NS_FREE(dir); dir = NULL; } } @@ -3318,7 +3481,7 @@ static int closedir(DIR *dir) { if (dir->handle != INVALID_HANDLE_VALUE) result = FindClose(dir->handle) ? 0 : -1; - free(dir); + NS_FREE(dir); } else { result = -1; SetLastError(ERROR_BAD_ARGUMENTS); @@ -3373,11 +3536,11 @@ static int scan_directory(struct connection *conn, const char *dir, } mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); - // Resize the array if nesessary + // Resize the array if necessary if (arr_ind >= arr_size) { if ((p = (struct dir_entry *) - realloc(*arr, (inc + arr_size) * sizeof(**arr))) != NULL) { - // Memset new chunk to zero, otherwize st_mtime will have garbage which + NS_REALLOC(*arr, (inc + arr_size) * sizeof(**arr))) != NULL) { + // Memset new chunk to zero, otherwise st_mtime will have garbage which // can make strftime() segfault, see // http://code.google.com/p/mongoose/issues/detail?id=79 memset(p + arr_size, 0, sizeof(**arr) * inc); @@ -3399,7 +3562,7 @@ static int scan_directory(struct connection *conn, const char *dir, return arr_ind; } -int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len) { +size_t mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len) { static const char *dont_escape = "._-$,;~()"; static const char *hex = "0123456789abcdef"; size_t i = 0, j = 0; @@ -3428,6 +3591,7 @@ static void print_dir_entry(const struct dir_entry *de) { int64_t fsize = de->st.st_size; int is_dir = S_ISDIR(de->st.st_mode); const char *slash = is_dir ? "/" : ""; + time_t t; if (is_dir) { mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]"); @@ -3444,7 +3608,8 @@ static void print_dir_entry(const struct dir_entry *de) { mg_snprintf(size, sizeof(size), "%.1fG", (double) fsize / 1073741824); } } - strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&de->st.st_mtime)); + t = de->st.st_mtime; // store in local variable for NDK compile + strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&t)); mg_url_encode(de->file_name, strlen(de->file_name), href, sizeof(href)); mg_printf_data(&de->conn->mg_conn, "%s%s" @@ -3499,12 +3664,14 @@ static void send_directory_listing(struct connection *conn, const char *dir) { sort_direction, sort_direction, sort_direction); num_entries = scan_directory(conn, dir, &arr); - qsort(arr, num_entries, sizeof(arr[0]), compare_dir_entries); - for (i = 0; i < num_entries; i++) { - print_dir_entry(&arr[i]); - free(arr[i].file_name); + if (arr) { + qsort(arr, num_entries, sizeof(arr[0]), compare_dir_entries); + for (i = 0; i < num_entries; i++) { + print_dir_entry(&arr[i]); + NS_FREE(arr[i].file_name); + } + NS_FREE(arr); } - free(arr); write_terminating_chunk(conn); close_local_endpoint(conn); @@ -3515,8 +3682,8 @@ static void send_directory_listing(struct connection *conn, const char *dir) { static void print_props(struct connection *conn, const char *uri, file_stat_t *stp) { char mtime[64]; - - gmt_time_string(mtime, sizeof(mtime), &stp->st_mtime); + time_t t = stp->st_mtime; // store in local variable for NDK compile + gmt_time_string(mtime, sizeof(mtime), &t); mg_printf(&conn->mg_conn, "" "%s" @@ -3572,9 +3739,9 @@ static void handle_propfind(struct connection *conn, const char *path, struct dir_entry *de = &arr[i]; mg_url_encode(de->file_name, strlen(de->file_name), buf, sizeof(buf)); print_props(conn, buf, &de->st); - free(de->file_name); + NS_FREE(de->file_name); } - free(arr); + NS_FREE(arr); } ns_send(conn->ns_conn, footer, sizeof(footer) - 1); } @@ -3696,7 +3863,7 @@ static void handle_put(struct connection *conn, const char *path) { static void forward_put_data(struct connection *conn) { struct iobuf *io = &conn->ns_conn->recv_iobuf; size_t k = conn->cl < (int64_t) io->len ? conn->cl : (int64_t) io->len; // To write - int n = write(conn->endpoint.fd, io->buf, k); // Write them! + size_t n = write(conn->endpoint.fd, io->buf, k); // Write them! if (n > 0) { iobuf_remove(io, n); conn->cl -= n; @@ -3721,6 +3888,7 @@ void mg_send_digest_auth_request(struct mg_connection *c) { c->status_code = 401; mg_printf(c, "HTTP/1.1 401 Unauthorized\r\n" + "Content-Length: 0\r\n" "WWW-Authenticate: Digest qop=\"auth\", " "realm=\"%s\", nonce=\"%lu\"\r\n\r\n", conn->server->config_options[AUTH_DOMAIN], @@ -3754,6 +3922,23 @@ static FILE *open_auth_file(struct connection *conn, const char *path, } #if !defined(HAVE_MD5) && !defined(MONGOOSE_NO_AUTH) +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + typedef struct MD5Context { uint32_t buf[4]; uint32_t bits[2]; @@ -4058,33 +4243,28 @@ static int is_authorized_for_dav(struct connection *conn) { return authorized; } - -static int is_dav_request(const struct connection *conn) { - const char *s = conn->mg_conn.request_method; - return !strcmp(s, "PUT") || !strcmp(s, "DELETE") || - !strcmp(s, "MKCOL") || !strcmp(s, "PROPFIND"); -} #endif // MONGOOSE_NO_AUTH -static int parse_header(const char *str, int str_len, const char *var_name, +static int parse_header(const char *str, size_t str_len, const char *var_name, char *buf, size_t buf_size) { - int ch = ' ', len = 0, n = strlen(var_name); + int ch = ' ', ch1 = ',', len = 0; + size_t n = strlen(var_name); const char *p, *end = str + str_len, *s = NULL; if (buf != NULL && buf_size > 0) buf[0] = '\0'; // Find where variable starts for (s = str; s != NULL && s + n < end; s++) { - if ((s == str || s[-1] == ' ' || s[-1] == ',') && s[n] == '=' && + if ((s == str || s[-1] == ch || s[-1] == ch1) && s[n] == '=' && !memcmp(s, var_name, n)) break; } if (s != NULL && &s[n + 1] < end) { s += n + 1; - if (*s == '"' || *s == '\'') ch = *s++; + if (*s == '"' || *s == '\'') ch = ch1 = *s++; p = s; - while (p < end && p[0] != ch && p[0] != ',' && len < (int) buf_size) { - if (p[0] == '\\' && p[1] == ch) p++; + while (p < end && p[0] != ch && p[0] != ch1 && len < (int) buf_size) { + if (ch == ch1 && p[0] == '\\' && p[1] == ch) p++; buf[len++] = *p++; } if (len >= (int) buf_size || (ch != ' ' && *p != ch)) { @@ -4109,7 +4289,7 @@ static void send_ssi_file(struct mg_connection *, const char *, FILE *, int); static void send_file_data(struct mg_connection *conn, FILE *fp) { char buf[IOBUF_SIZE]; - int n; + size_t n; while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) { mg_write(conn, buf, n); } @@ -4133,7 +4313,7 @@ static void do_ssi_include(struct mg_connection *conn, const char *ssi, mg_snprintf(path, sizeof(path), "%s", file_name); } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 || sscanf(tag, " \"%[^\"]\"", file_name) == 1) { - // File name is relative to the currect document + // File name is relative to the current document mg_snprintf(path, sizeof(path), "%s", ssi); if ((p = strrchr(path, '/')) != NULL) { p[1] = '\0'; @@ -4544,7 +4724,11 @@ static void try_parse(struct connection *conn) { // If request is buffered in, remove it from the iobuf. This is because // iobuf could be reallocated, and pointers in parsed request could // become invalid. - conn->request = (char *) malloc(conn->request_len); + conn->request = (char *) NS_MALLOC(conn->request_len); + if (conn->request == NULL) { + conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY; + return; + } memcpy(conn->request, io->buf, conn->request_len); //DBG(("%p [%.*s]", conn, conn->request_len, conn->request)); iobuf_remove(io, conn->request_len); @@ -4638,7 +4822,7 @@ static void call_http_client_handler(struct connection *conn) { iobuf_remove(&conn->ns_conn->recv_iobuf, conn->mg_conn.content_len); conn->mg_conn.status_code = 0; conn->cl = conn->num_bytes_recv = conn->request_len = 0; - free(conn->request); + NS_FREE(conn->request); conn->request = NULL; } @@ -4662,7 +4846,7 @@ struct mg_connection *mg_connect(struct mg_server *server, const char *addr) { nsconn = ns_connect(&server->ns_mgr, addr, mg_ev_handler, NULL); if (nsconn == NULL) return 0; - if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) { + if ((conn = (struct connection *) NS_CALLOC(1, sizeof(*conn))) == NULL) { nsconn->flags |= NSF_CLOSE_IMMEDIATELY; return 0; } @@ -4754,8 +4938,8 @@ static void close_local_endpoint(struct connection *conn) { // Gobble possible POST data sent to the URI handler iobuf_free(&conn->ns_conn->recv_iobuf); - free(conn->request); - free(conn->path_info); + NS_FREE(conn->request); + NS_FREE(conn->path_info); conn->endpoint.nc = NULL; conn->request = conn->path_info = NULL; @@ -4763,7 +4947,7 @@ static void close_local_endpoint(struct connection *conn) { conn->cl = conn->num_bytes_recv = conn->request_len = 0; conn->ns_conn->flags &= ~(NSF_FINISHED_SENDING_DATA | NSF_BUFFER_BUT_DONT_SEND | NSF_CLOSE_IMMEDIATELY | - MG_HEADERS_SENT | MG_LONG_RUNNING); + MG_HEADERS_SENT | MG_USING_CHUNKED_API); // Do not memset() the whole structure, as some of the fields // (IP addresses & ports, server_param) must survive. Nullify the rest. @@ -4781,7 +4965,7 @@ static void close_local_endpoint(struct connection *conn) { static void transfer_file_data(struct connection *conn) { char buf[IOBUF_SIZE]; - int n; + size_t n; // If output buffer is too big, don't send anything. Wait until // mongoose drains already buffered data to the client. @@ -4802,7 +4986,7 @@ static void transfer_file_data(struct connection *conn) { } } -int mg_poll_server(struct mg_server *server, int milliseconds) { +time_t mg_poll_server(struct mg_server *server, int milliseconds) { return ns_mgr_poll(&server->ns_mgr, milliseconds); } @@ -4813,9 +4997,9 @@ void mg_destroy_server(struct mg_server **server) { ns_mgr_free(&s->ns_mgr); for (i = 0; i < (int) ARRAY_SIZE(s->config_options); i++) { - free(s->config_options[i]); // It is OK to free(NULL) + NS_FREE(s->config_options[i]); // It is OK to free(NULL) } - free(s); + NS_FREE(s); *server = NULL; } } @@ -4831,20 +5015,17 @@ struct mg_connection *mg_next(struct mg_server *s, struct mg_connection *c) { } static int get_var(const char *data, size_t data_len, const char *name, - char *dst, size_t dst_len) { - const char *p, *e, *s; + char *dst, size_t dst_len, int n) { + const char *p, *e = data + data_len, *s; size_t name_len; - int len; + int i = 0, len = -1; if (dst == NULL || dst_len == 0) { len = -2; } else if (data == NULL || name == NULL || data_len == 0) { - len = -1; dst[0] = '\0'; } else { name_len = strlen(name); - e = data + data_len; - len = -1; dst[0] = '\0'; // data is "var1=val1&var2=val2...". Find variable first @@ -4852,6 +5033,8 @@ static int get_var(const char *data, size_t data_len, const char *name, if ((p == data || p[-1] == '&') && p[name_len] == '=' && !mg_strncasecmp(name, p, name_len)) { + if (n != i++) continue; + // Point p to variable value p += name_len + 1; @@ -4877,16 +5060,21 @@ static int get_var(const char *data, size_t data_len, const char *name, return len; } -int mg_get_var(const struct mg_connection *conn, const char *name, - char *dst, size_t dst_len) { +int mg_get_var_n(const struct mg_connection *conn, const char *name, + char *dst, size_t dst_len, int n) { int len = get_var(conn->query_string, conn->query_string == NULL ? 0 : - strlen(conn->query_string), name, dst, dst_len); - if (len < 0) { - len = get_var(conn->content, conn->content_len, name, dst, dst_len); + strlen(conn->query_string), name, dst, dst_len, n); + if (len == -1) { + len = get_var(conn->content, conn->content_len, name, dst, dst_len, n); } return len; } +int mg_get_var(const struct mg_connection *conn, const char *name, + char *dst, size_t dst_len) { + return mg_get_var_n(conn, name, dst, dst_len, 0); +} + static int get_line_len(const char *buf, int buf_len) { int len = 0; while (len < buf_len && buf[len] != '\n') len++; @@ -4941,8 +5129,16 @@ void mg_copy_listeners(struct mg_server *s, struct mg_server *to) { for (c = ns_next(&s->ns_mgr, NULL); c != NULL; c = ns_next(&s->ns_mgr, c)) { struct ns_connection *tmp; if ((c->flags & NSF_LISTENING) && - (tmp = (struct ns_connection *) malloc(sizeof(*tmp))) != NULL) { + (tmp = (struct ns_connection *) NS_MALLOC(sizeof(*tmp))) != NULL) { memcpy(tmp, c, sizeof(*tmp)); + +#if defined(NS_ENABLE_SSL) && defined(HEADER_SSL_H) + /* OpenSSL only. See https://github.com/cesanta/mongoose/issues/441 */ + if (tmp->ssl_ctx != NULL) { + tmp->ssl_ctx->references++; + } +#endif + tmp->mgr = &to->ns_mgr; ns_add_conn(tmp->mgr, tmp); } @@ -4988,7 +5184,7 @@ const char *mg_set_option(struct mg_server *server, const char *name, } if (*v != NULL) { - free(*v); + NS_FREE(*v); *v = NULL; } @@ -4998,25 +5194,44 @@ const char *mg_set_option(struct mg_server *server, const char *name, DBG(("%s [%s]", name, *v)); if (ind == LISTENING_PORT) { + char buf[500] = ""; + size_t n = 0; struct vec vec; + + /* + * Ports can be specified as 0, meaning that OS has to choose any + * free port that is available. In order to pass chosen port number to + * the user, we rewrite all 0 port to chosen values. + */ while ((value = next_option(value, &vec, NULL)) != NULL) { struct ns_connection *c = ns_bind(&server->ns_mgr, vec.ptr, mg_ev_handler, NULL); - if (c== NULL) { + if (c == NULL) { error_msg = "Cannot bind to port"; break; } else { - char buf[100]; - ns_sock_to_str(c->sock, buf, sizeof(buf), 2); - free(*v); - *v = mg_strdup(buf); + char buf2[50], cert[100], ca[100]; + union socket_address sa; + int proto, use_ssl; + + ns_parse_address(vec.ptr, &sa, &proto, &use_ssl, cert, ca); + ns_sock_to_str(c->sock, buf2, sizeof(buf2), + memchr(vec.ptr, ':', vec.len) == NULL ? 2 : 3); + + n += snprintf(buf + n, sizeof(buf) - n, "%s%s%s%s%s%s%s", + n > 0 ? "," : "", + use_ssl ? "ssl://" : "", + buf2, cert[0] ? ":" : "", cert, ca[0] ? ":" : "", ca); } } + buf[sizeof(buf) - 1] = '\0'; + NS_FREE(*v); + *v = mg_strdup(buf); #ifndef MONGOOSE_NO_FILESYSTEM } else if (ind == HEXDUMP_FILE) { server->ns_mgr.hexdump_file = *v; #endif -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(MONGOOSE_NO_USER) } else if (ind == RUN_AS_USER) { struct passwd *pw; if ((pw = getpwnam(value)) == NULL) { @@ -5050,7 +5265,7 @@ static void on_accept(struct ns_connection *nc, union socket_address *sa) { if (!check_acl(server->config_options[ACCESS_CONTROL_LIST], ntohl(* (uint32_t *) &sa->sin.sin_addr)) || - (conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) { + (conn = (struct connection *) NS_CALLOC(1, sizeof(*conn))) == NULL) { nc->flags |= NSF_CLOSE_IMMEDIATELY; } else { // Circularly link two connection structures @@ -5077,31 +5292,32 @@ static void process_udp(struct ns_connection *nc) { //ns_printf(nc, "%s", "HTTP/1.0 200 OK\r\n\r\n"); } -static void mg_ev_handler(struct ns_connection *nc, int ev, void *p) { - struct connection *conn = (struct connection *) nc->user_data; - - // Send NS event to the handler. Note that call_user won't send an event - // if conn == NULL. Therefore, repeat this for NS_ACCEPT event as well. #ifdef MONGOOSE_SEND_NS_EVENTS - { - struct connection *conn = (struct connection *) nc->user_data; +static void send_ns_event(struct ns_connection *nc, int ev, void *p) { + struct connection *conn = (struct connection *) nc->user_data; + if (conn != NULL) { void *param[2] = { nc, p }; - if (conn != NULL) conn->mg_conn.callback_param = param; + conn->mg_conn.callback_param = param; call_user(conn, (enum mg_event) ev); } +} +#else +static void send_ns_event(struct ns_connection *nc, int ev, void *p) { + (void) nc; (void) p; (void) ev; +} #endif +static void mg_ev_handler(struct ns_connection *nc, int ev, void *p) { + struct connection *conn = (struct connection *) nc->user_data; + + // Send NS event to the handler. Note that call_user won't send an event + // if conn == NULL. Therefore, repeat this for NS_ACCEPT event as well. + send_ns_event(nc, ev, p); + switch (ev) { case NS_ACCEPT: on_accept(nc, (union socket_address *) p); -#ifdef MONGOOSE_SEND_NS_EVENTS - { - struct connection *conn = (struct connection *) nc->user_data; - void *param[2] = { nc, p }; - if (conn != NULL) conn->mg_conn.callback_param = param; - call_user(conn, (enum mg_event) ev); - } -#endif + send_ns_event(nc, ev, p); break; case NS_CONNECT: @@ -5162,7 +5378,7 @@ static void mg_ev_handler(struct ns_connection *nc, int ev, void *p) { call_user(conn, MG_CLOSE); close_local_endpoint(conn); conn->ns_conn = NULL; - free(conn); + NS_FREE(conn); } break; @@ -5173,6 +5389,11 @@ static void mg_ev_handler(struct ns_connection *nc, int ev, void *p) { write_terminating_chunk(conn); } close_local_endpoint(conn); + /* + * MG_POLL callback returned MG_TRUE, + * i.e. data is sent, set corresponding flag + */ + conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA; } if (conn->endpoint_type == EP_FILE) { @@ -5209,7 +5430,7 @@ static void iter2(struct ns_connection *nc, int ev, void *param) { (void) ev; //DBG(("%p [%s]", conn, msg)); - if (sscanf(msg, "%p %n", &func, &n) && func != NULL) { + if (sscanf(msg, "%p %n", &func, &n) && func != NULL && conn != NULL) { conn->mg_conn.callback_param = (void *) (msg + n); func(&conn->mg_conn, MG_POLL); } @@ -5242,7 +5463,7 @@ const char *mg_get_option(const struct mg_server *server, const char *name) { } struct mg_server *mg_create_server(void *server_data, mg_handler_t handler) { - struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server)); + struct mg_server *server = (struct mg_server *) NS_CALLOC(1, sizeof(*server)); ns_mgr_init(&server->ns_mgr, server_data); set_default_option_values(server->config_options); server->event_handler = handler; diff --git a/3rdparty/mongoose/mongoose.h b/3rdparty/mongoose/mongoose.h index 96c8c90b1..f2c47a5ce 100644 --- a/3rdparty/mongoose/mongoose.h +++ b/3rdparty/mongoose/mongoose.h @@ -2,28 +2,27 @@ // Copyright (c) 2013-2014 Cesanta Software Limited // All rights reserved // -// This library is dual-licensed: you can redistribute it and/or modify +// This software 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 . // -// You are free to use this library under the terms of the GNU General +// You are free to use this software 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 +// Alternatively, you can license this software under a commercial // license, as set out in . -// -// $Date: 2014-09-09 17:07:55 UTC $ #ifndef MONGOOSE_HEADER_INCLUDED #define MONGOOSE_HEADER_INCLUDED -#define MONGOOSE_VERSION "5.5" +#define MONGOOSE_VERSION "5.6" #include // required for FILE #include // required for size_t +#include // required for time_t #ifdef __cplusplus extern "C" { @@ -53,7 +52,7 @@ struct mg_connection { int is_websocket; // Connection is a websocket connection int status_code; // HTTP status code for HTTP error handler int wsbits; // First byte of the websocket frame - void *server_param; // Parameter passed to mg_add_uri_handler() + void *server_param; // Parameter passed to mg_create_server() void *connection_param; // Placeholder for connection-specific data void *callback_param; }; @@ -61,7 +60,8 @@ struct mg_connection { struct mg_server; // Opaque structure describing server instance enum mg_result { MG_FALSE, MG_TRUE, MG_MORE }; enum mg_event { - MG_POLL = 100, // Callback return value is ignored + MG_POLL = 100, // If callback returns MG_TRUE connection closes + // after all of data is sent MG_CONNECT, // If callback returns MG_FALSE, connect fails MG_AUTH, // If callback returns MG_FALSE, authentication fails MG_REQUEST, // If callback returns MG_FALSE, Mongoose continues with req @@ -90,7 +90,7 @@ enum { struct mg_server *mg_create_server(void *server_param, mg_handler_t handler); void mg_destroy_server(struct mg_server **); const char *mg_set_option(struct mg_server *, const char *opt, const char *val); -int mg_poll_server(struct mg_server *, int milliseconds); +time_t mg_poll_server(struct mg_server *, int milliseconds); const char **mg_get_valid_option_names(void); const char *mg_get_option(const struct mg_server *server, const char *name); void mg_copy_listeners(struct mg_server *from, struct mg_server *to); @@ -104,8 +104,10 @@ void mg_send_status(struct mg_connection *, int status_code); void mg_send_header(struct mg_connection *, const char *name, const char *val); size_t mg_send_data(struct mg_connection *, const void *data, int data_len); size_t mg_printf_data(struct mg_connection *, const char *format, ...); -size_t mg_write(struct mg_connection *, const void *buf, int len); +size_t mg_vprintf_data(struct mg_connection *, const char *format, va_list ap); +size_t mg_write(struct mg_connection *, const void *buf, size_t len); size_t mg_printf(struct mg_connection *conn, const char *fmt, ...); +size_t mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap); size_t mg_websocket_write(struct mg_connection *, int opcode, const char *data, size_t data_len); @@ -119,6 +121,8 @@ const char *mg_get_header(const struct mg_connection *, const char *name); const char *mg_get_mime_type(const char *name, const char *default_mime_type); int mg_get_var(const struct mg_connection *conn, const char *var_name, char *buf, size_t buf_len); +int mg_get_var_n(const struct mg_connection *conn, const char *var_name, + char *buf, size_t buf_len, int n); int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t); int mg_parse_multipart(const char *buf, int buf_len, char *var_name, int var_name_len, @@ -130,8 +134,8 @@ int mg_parse_multipart(const char *buf, int buf_len, void *mg_start_thread(void *(*func)(void *), void *param); char *mg_md5(char buf[33], ...); int mg_authorize_digest(struct mg_connection *c, FILE *fp); -int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len); -int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int); +size_t mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len); +int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len, int); int mg_terminate_ssl(struct mg_connection *c, const char *cert); int mg_forward(struct mg_connection *c, const char *addr); void *mg_mmap(FILE *fp, size_t size); -- 2.39.5