From cafaecfc562f70a7e38f5133491f54848e8cd37b Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Thu, 3 Oct 2013 19:09:35 +0100 Subject: [PATCH] Moved HTTP client, ssl and some util code into separate files --- build/Makefile | 7 +- build/src/http_client.c | 93 ++++++ build/src/internal.h | 1 + build/src/mg_printf.c | 67 ++++ build/src/mongoose.c | 345 -------------------- build/src/ssl.c | 179 +++++++++++ build/src/util.c | 11 + mongoose.c | 692 ++++++++++++++++++++-------------------- 8 files changed, 702 insertions(+), 693 deletions(-) create mode 100644 build/src/http_client.c create mode 100644 build/src/mg_printf.c create mode 100644 build/src/ssl.c create mode 100644 build/src/util.c diff --git a/build/Makefile b/build/Makefile index eafe3a55..a55461c7 100644 --- a/build/Makefile +++ b/build/Makefile @@ -26,9 +26,10 @@ VERSION = $(shell perl -lne \ 'print $$1 if /define\s+MONGOOSE_VERSION\s+"(\S+)"/' ../mongoose.c) # The order in which files are listed is important -SOURCES = src/internal.h src/string.c src/parse_date.c src/options.c \ - src/crypto.c src/auth.c src/win32.c src/unix.c src/mongoose.c \ - src/lua.c +SOURCES = src/internal.h src/util.c src/string.c src/parse_date.c \ + src/options.c src/crypto.c src/auth.c src/win32.c src/unix.c \ + src/mg_printf.c src/ssl.c src/http_client.c \ + src/mongoose.c src/lua.c TINY_SOURCES = ../mongoose.c main.c LUA_SOURCES = $(TINY_SOURCES) sqlite3.c lsqlite3.c lua_5.2.1.c diff --git a/build/src/http_client.c b/build/src/http_client.c new file mode 100644 index 00000000..db06b206 --- /dev/null +++ b/build/src/http_client.c @@ -0,0 +1,93 @@ +#include "internal.h" + +static SOCKET conn2(const char *host, int port, int use_ssl, + char *ebuf, size_t ebuf_len) { + struct sockaddr_in sin; + struct hostent *he; + SOCKET sock = INVALID_SOCKET; + + if (host == NULL) { + snprintf(ebuf, ebuf_len, "%s", "NULL host"); + } else if (use_ssl && SSLv23_client_method == NULL) { + snprintf(ebuf, ebuf_len, "%s", "SSL is not initialized"); + // TODO(lsm): use something threadsafe instead of gethostbyname() + } else if ((he = gethostbyname(host)) == NULL) { + snprintf(ebuf, ebuf_len, "gethostbyname(%s): %s", host, strerror(ERRNO)); + } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { + snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO)); + } else { + set_close_on_exec(sock); + sin.sin_family = AF_INET; + sin.sin_port = htons((uint16_t) port); + sin.sin_addr = * (struct in_addr *) he->h_addr_list[0]; + if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) { + snprintf(ebuf, ebuf_len, "connect(%s:%d): %s", + host, port, strerror(ERRNO)); + closesocket(sock); + sock = INVALID_SOCKET; + } + } + return sock; +} + +struct mg_connection *mg_connect(const char *host, int port, int use_ssl, + char *ebuf, size_t ebuf_len) { + static struct mg_context fake_ctx; + struct mg_connection *conn = NULL; + SOCKET sock; + + if ((sock = conn2(host, port, use_ssl, ebuf, ebuf_len)) == INVALID_SOCKET) { + } else if ((conn = (struct mg_connection *) + calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) { + snprintf(ebuf, ebuf_len, "calloc(): %s", strerror(ERRNO)); + closesocket(sock); +#ifndef NO_SSL + } else if (use_ssl && (conn->client_ssl_ctx = + SSL_CTX_new(SSLv23_client_method())) == NULL) { + snprintf(ebuf, ebuf_len, "SSL_CTX_new error"); + closesocket(sock); + free(conn); + conn = NULL; +#endif // NO_SSL + } else { + socklen_t len = sizeof(struct sockaddr); + conn->buf_size = MAX_REQUEST_SIZE; + conn->buf = (char *) (conn + 1); + conn->ctx = &fake_ctx; + conn->client.sock = sock; + getsockname(sock, &conn->client.rsa.sa, &len); + conn->client.is_ssl = use_ssl; +#ifndef NO_SSL + if (use_ssl) { + // SSL_CTX_set_verify call is needed to switch off server certificate + // checking, which is off by default in OpenSSL and on in yaSSL. + SSL_CTX_set_verify(conn->client_ssl_ctx, 0, 0); + sslize(conn, conn->client_ssl_ctx, SSL_connect); + } +#endif + } + + return conn; +} + +struct mg_connection *mg_download(const char *host, int port, int use_ssl, + char *ebuf, size_t ebuf_len, + const char *fmt, ...) { + struct mg_connection *conn; + va_list ap; + + va_start(ap, fmt); + ebuf[0] = '\0'; + if ((conn = mg_connect(host, port, use_ssl, ebuf, ebuf_len)) == NULL) { + } else if (mg_vprintf(conn, fmt, ap) <= 0) { + snprintf(ebuf, ebuf_len, "%s", "Error sending request"); + } else { + getreq(conn, ebuf, ebuf_len); + } + if (ebuf[0] != '\0' && conn != NULL) { + mg_close_connection(conn); + conn = NULL; + } + + return conn; +} diff --git a/build/src/internal.h b/build/src/internal.h index b84def02..2eb7816b 100644 --- a/build/src/internal.h +++ b/build/src/internal.h @@ -465,6 +465,7 @@ static void send_http_error(struct mg_connection *, int, const char *, PRINTF_ARGS(4, 5); static void cry(struct mg_connection *conn, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); +static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len); #ifdef USE_LUA #include "lua_5.2.1.h" diff --git a/build/src/mg_printf.c b/build/src/mg_printf.c new file mode 100644 index 00000000..90bb3f71 --- /dev/null +++ b/build/src/mg_printf.c @@ -0,0 +1,67 @@ +#include "internal.h" + +// Print message to buffer. If buffer is large enough to hold the message, +// return buffer. If buffer is to small, allocate large enough buffer on heap, +// and return allocated buffer. +static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) { + va_list ap_copy; + int len; + + // Windows is not standard-compliant, and vsnprintf() returns -1 if + // buffer is too small. Also, older versions of msvcrt.dll do not have + // _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. + // Therefore, we make two passes: on first pass, get required message length. + // On second pass, actually print the message. + va_copy(ap_copy, ap); + len = vsnprintf(NULL, 0, fmt, ap_copy); + + if (len > (int) size && + (size = len + 1) > 0 && + (*buf = (char *) malloc(size)) == NULL) { + len = -1; // Allocation failed, mark failure + } else { + va_copy(ap_copy, ap); + vsnprintf(*buf, size, fmt, ap_copy); + } + + return len; +} + +int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) { + char mem[MG_BUF_LEN], *buf = mem; + int len; + + if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + len = mg_write(conn, buf, (size_t) len); + } + if (buf != mem && buf != NULL) { + free(buf); + } + + return len; +} + +int mg_printf(struct mg_connection *conn, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + return mg_vprintf(conn, fmt, ap); +} + +static int mg_chunked_printf(struct mg_connection *conn, const char *fmt, ...) { + char mem[MG_BUF_LEN], *buf = mem; + int len; + + va_list ap; + va_start(ap, fmt); + if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + len = mg_printf(conn, "%X\r\n%s\r\n", len, buf); + } + + if (buf != mem && buf != NULL) { + free(buf); + } + + return len; +} + + diff --git a/build/src/mongoose.c b/build/src/mongoose.c index 5f86022e..ffa0d521 100644 --- a/build/src/mongoose.c +++ b/build/src/mongoose.c @@ -82,16 +82,6 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) { } } -// Return fake connection structure. Used for logging, if connection -// is not applicable at the moment of logging. -static struct mg_connection *fc(struct mg_context *ctx) { - static struct mg_connection fake_connection; - fake_connection.ctx = ctx; - // See https://github.com/cesanta/mongoose/issues/236 - fake_connection.event.user_data = ctx->user_data; - return &fake_connection; -} - const char *mg_version(void) { return MONGOOSE_VERSION; } @@ -297,70 +287,6 @@ int mg_write(struct mg_connection *conn, const void *buf, int len) { return (int) total; } -// Print message to buffer. If buffer is large enough to hold the message, -// return buffer. If buffer is to small, allocate large enough buffer on heap, -// and return allocated buffer. -static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) { - va_list ap_copy; - int len; - - // Windows is not standard-compliant, and vsnprintf() returns -1 if - // buffer is too small. Also, older versions of msvcrt.dll do not have - // _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. - // Therefore, we make two passes: on first pass, get required message length. - // On second pass, actually print the message. - va_copy(ap_copy, ap); - len = vsnprintf(NULL, 0, fmt, ap_copy); - - if (len > (int) size && - (size = len + 1) > 0 && - (*buf = (char *) malloc(size)) == NULL) { - len = -1; // Allocation failed, mark failure - } else { - va_copy(ap_copy, ap); - vsnprintf(*buf, size, fmt, ap_copy); - } - - return len; -} - -int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) { - char mem[MG_BUF_LEN], *buf = mem; - int len; - - if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { - len = mg_write(conn, buf, (size_t) len); - } - if (buf != mem && buf != NULL) { - free(buf); - } - - return len; -} - -int mg_printf(struct mg_connection *conn, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - return mg_vprintf(conn, fmt, ap); -} - -static int mg_chunked_printf(struct mg_connection *conn, const char *fmt, ...) { - char mem[MG_BUF_LEN], *buf = mem; - int len; - - va_list ap; - va_start(ap, fmt); - if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { - len = mg_printf(conn, "%X\r\n%s\r\n", len, buf); - } - - if (buf != mem && buf != NULL) { - free(buf); - } - - return len; -} - 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; @@ -693,38 +619,6 @@ static void get_mime_type(struct mg_context *ctx, const char *path, vec->len = strlen(vec->ptr); } -static SOCKET conn2(const char *host, int port, int use_ssl, - char *ebuf, size_t ebuf_len) { - struct sockaddr_in sin; - struct hostent *he; - SOCKET sock = INVALID_SOCKET; - - if (host == NULL) { - snprintf(ebuf, ebuf_len, "%s", "NULL host"); - } else if (use_ssl && SSLv23_client_method == NULL) { - snprintf(ebuf, ebuf_len, "%s", "SSL is not initialized"); - // TODO(lsm): use something threadsafe instead of gethostbyname() - } else if ((he = gethostbyname(host)) == NULL) { - snprintf(ebuf, ebuf_len, "gethostbyname(%s): %s", host, strerror(ERRNO)); - } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { - snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO)); - } else { - set_close_on_exec(sock); - sin.sin_family = AF_INET; - sin.sin_port = htons((uint16_t) port); - sin.sin_addr = * (struct in_addr *) he->h_addr_list[0]; - if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) { - snprintf(ebuf, ebuf_len, "connect(%s:%d): %s", - host, port, strerror(ERRNO)); - closesocket(sock); - sock = INVALID_SOCKET; - } - } - return sock; -} - - - void mg_url_encode(const char *src, char *dst, size_t dst_len) { static const char *dont_escape = "._-$,;~()"; static const char *hex = "0123456789abcdef"; @@ -2811,183 +2705,6 @@ static int set_uid_option(struct mg_context *ctx) { } #endif // !_WIN32 -#if !defined(NO_SSL) -// set_ssl_option() function updates this array. -// It loads SSL library dynamically and changes NULLs to the actual addresses -// of respective functions. The macros above (like SSL_connect()) are really -// just calling these functions indirectly via the pointer. -static struct ssl_func ssl_sw[] = { - {"SSL_free", NULL}, - {"SSL_accept", NULL}, - {"SSL_connect", NULL}, - {"SSL_read", NULL}, - {"SSL_write", NULL}, - {"SSL_get_error", NULL}, - {"SSL_set_fd", NULL}, - {"SSL_new", NULL}, - {"SSL_CTX_new", NULL}, - {"SSLv23_server_method", NULL}, - {"SSL_library_init", NULL}, - {"SSL_CTX_use_PrivateKey_file", NULL}, - {"SSL_CTX_use_certificate_file",NULL}, - {"SSL_CTX_set_default_passwd_cb",NULL}, - {"SSL_CTX_free", NULL}, - {"SSL_load_error_strings", NULL}, - {"SSL_CTX_use_certificate_chain_file", NULL}, - {"SSLv23_client_method", NULL}, - {"SSL_pending", NULL}, - {"SSL_CTX_set_verify", NULL}, - {"SSL_shutdown", NULL}, - {NULL, NULL} -}; - -// Similar array as ssl_sw. These functions could be located in different lib. -static struct ssl_func crypto_sw[] = { - {"CRYPTO_num_locks", NULL}, - {"CRYPTO_set_locking_callback", NULL}, - {"CRYPTO_set_id_callback", NULL}, - {"ERR_get_error", NULL}, - {"ERR_error_string", NULL}, - {NULL, NULL} -}; - -static pthread_mutex_t *ssl_mutexes; - -static int sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *)) { - return (conn->ssl = SSL_new(s)) != NULL && - SSL_set_fd(conn->ssl, conn->client.sock) == 1 && - func(conn->ssl) == 1; -} - -// Return OpenSSL error message -static const char *ssl_error(void) { - unsigned long err; - err = ERR_get_error(); - return err == 0 ? "" : ERR_error_string(err, NULL); -} - -static void ssl_locking_callback(int mode, int mutex_num, const char *file, - int line) { - (void) line; - (void) file; - - if (mode & 1) { // 1 is CRYPTO_LOCK - (void) pthread_mutex_lock(&ssl_mutexes[mutex_num]); - } else { - (void) pthread_mutex_unlock(&ssl_mutexes[mutex_num]); - } -} - -static unsigned long ssl_id_callback(void) { - return (unsigned long) pthread_self(); -} - -#if !defined(NO_SSL_DL) -static int load_dll(struct mg_context *ctx, const char *dll_name, - struct ssl_func *sw) { - union {void *p; void (*fp)(void);} u; - void *dll_handle; - struct ssl_func *fp; - - if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) { - cry(fc(ctx), "%s: cannot load %s", __func__, dll_name); - return 0; - } - - for (fp = sw; fp->name != NULL; fp++) { -#ifdef _WIN32 - // GetProcAddress() returns pointer to function - u.fp = (void (*)(void)) dlsym(dll_handle, fp->name); -#else - // dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to - // function pointers. We need to use a union to make a cast. - u.p = dlsym(dll_handle, fp->name); -#endif // _WIN32 - if (u.fp == NULL) { - cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name); - return 0; - } else { - fp->ptr = u.fp; - } - } - - return 1; -} -#endif // NO_SSL_DL - -// Dynamically load SSL library. Set up ctx->ssl_ctx pointer. -static int set_ssl_option(struct mg_context *ctx) { - int i, size; - const char *pem; - - // If PEM file is not specified and the init_ssl callback - // is not specified, skip SSL initialization. - if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL) { - // MG_INIT_SSL - // ctx->callbacks.init_ssl == NULL) { - return 1; - } - -#if !defined(NO_SSL_DL) - if (!load_dll(ctx, SSL_LIB, ssl_sw) || - !load_dll(ctx, CRYPTO_LIB, crypto_sw)) { - return 0; - } -#endif // NO_SSL_DL - - // Initialize SSL library - SSL_library_init(); - SSL_load_error_strings(); - - if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { - cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error()); - return 0; - } - - // If user callback returned non-NULL, that means that user callback has - // set up certificate itself. In this case, skip sertificate setting. - // MG_INIT_SSL - if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 || - SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) { - cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error()); - return 0; - } - - if (pem != NULL) { - (void) SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem); - } - - // Initialize locking callbacks, needed for thread safety. - // http://www.openssl.org/support/faq.html#PROG1 - size = sizeof(pthread_mutex_t) * CRYPTO_num_locks(); - if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) { - cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error()); - return 0; - } - - for (i = 0; i < CRYPTO_num_locks(); i++) { - pthread_mutex_init(&ssl_mutexes[i], NULL); - } - - CRYPTO_set_locking_callback(&ssl_locking_callback); - CRYPTO_set_id_callback(&ssl_id_callback); - - return 1; -} - -static void uninitialize_ssl(struct mg_context *ctx) { - int i; - if (ctx->ssl_ctx != NULL) { - CRYPTO_set_locking_callback(NULL); - for (i = 0; i < CRYPTO_num_locks(); i++) { - pthread_mutex_destroy(&ssl_mutexes[i]); - } - CRYPTO_set_locking_callback(NULL); - CRYPTO_set_id_callback(NULL); - } -} -#endif // !NO_SSL - static int set_gpass_option(struct mg_context *ctx) { struct file file = STRUCT_FILE_INITIALIZER; const char *path = ctx->config[GLOBAL_PASSWORDS_FILE]; @@ -3069,46 +2786,6 @@ void mg_close_connection(struct mg_connection *conn) { free(conn); } -struct mg_connection *mg_connect(const char *host, int port, int use_ssl, - char *ebuf, size_t ebuf_len) { - static struct mg_context fake_ctx; - struct mg_connection *conn = NULL; - SOCKET sock; - - if ((sock = conn2(host, port, use_ssl, ebuf, ebuf_len)) == INVALID_SOCKET) { - } else if ((conn = (struct mg_connection *) - calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) { - snprintf(ebuf, ebuf_len, "calloc(): %s", strerror(ERRNO)); - closesocket(sock); -#ifndef NO_SSL - } else if (use_ssl && (conn->client_ssl_ctx = - SSL_CTX_new(SSLv23_client_method())) == NULL) { - snprintf(ebuf, ebuf_len, "SSL_CTX_new error"); - closesocket(sock); - free(conn); - conn = NULL; -#endif // NO_SSL - } else { - socklen_t len = sizeof(struct sockaddr); - conn->buf_size = MAX_REQUEST_SIZE; - conn->buf = (char *) (conn + 1); - conn->ctx = &fake_ctx; - conn->client.sock = sock; - getsockname(sock, &conn->client.rsa.sa, &len); - conn->client.is_ssl = use_ssl; -#ifndef NO_SSL - if (use_ssl) { - // SSL_CTX_set_verify call is needed to switch off server certificate - // checking, which is off by default in OpenSSL and on in yaSSL. - SSL_CTX_set_verify(conn->client_ssl_ctx, 0, 0); - sslize(conn, conn->client_ssl_ctx, SSL_connect); - } -#endif - } - - return conn; -} - static int is_valid_uri(const char *uri) { // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 // URI can be an asterisk (*) or should start with slash. @@ -3152,28 +2829,6 @@ static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len) { return ebuf[0] == '\0'; } -struct mg_connection *mg_download(const char *host, int port, int use_ssl, - char *ebuf, size_t ebuf_len, - const char *fmt, ...) { - struct mg_connection *conn; - va_list ap; - - va_start(ap, fmt); - ebuf[0] = '\0'; - if ((conn = mg_connect(host, port, use_ssl, ebuf, ebuf_len)) == NULL) { - } else if (mg_vprintf(conn, fmt, ap) <= 0) { - snprintf(ebuf, ebuf_len, "%s", "Error sending request"); - } else { - getreq(conn, ebuf, ebuf_len); - } - if (ebuf[0] != '\0' && conn != NULL) { - mg_close_connection(conn); - conn = NULL; - } - - return conn; -} - static void process_new_connection(struct mg_connection *conn) { struct mg_request_info *ri = &conn->request_info; int keep_alive_enabled, keep_alive, discard_len; diff --git a/build/src/ssl.c b/build/src/ssl.c new file mode 100644 index 00000000..6da584b5 --- /dev/null +++ b/build/src/ssl.c @@ -0,0 +1,179 @@ +#include "internal.h" + +#if !defined(NO_SSL) +// set_ssl_option() function updates this array. +// It loads SSL library dynamically and changes NULLs to the actual addresses +// of respective functions. The macros above (like SSL_connect()) are really +// just calling these functions indirectly via the pointer. +static struct ssl_func ssl_sw[] = { + {"SSL_free", NULL}, + {"SSL_accept", NULL}, + {"SSL_connect", NULL}, + {"SSL_read", NULL}, + {"SSL_write", NULL}, + {"SSL_get_error", NULL}, + {"SSL_set_fd", NULL}, + {"SSL_new", NULL}, + {"SSL_CTX_new", NULL}, + {"SSLv23_server_method", NULL}, + {"SSL_library_init", NULL}, + {"SSL_CTX_use_PrivateKey_file", NULL}, + {"SSL_CTX_use_certificate_file",NULL}, + {"SSL_CTX_set_default_passwd_cb",NULL}, + {"SSL_CTX_free", NULL}, + {"SSL_load_error_strings", NULL}, + {"SSL_CTX_use_certificate_chain_file", NULL}, + {"SSLv23_client_method", NULL}, + {"SSL_pending", NULL}, + {"SSL_CTX_set_verify", NULL}, + {"SSL_shutdown", NULL}, + {NULL, NULL} +}; + +// Similar array as ssl_sw. These functions could be located in different lib. +static struct ssl_func crypto_sw[] = { + {"CRYPTO_num_locks", NULL}, + {"CRYPTO_set_locking_callback", NULL}, + {"CRYPTO_set_id_callback", NULL}, + {"ERR_get_error", NULL}, + {"ERR_error_string", NULL}, + {NULL, NULL} +}; + +static pthread_mutex_t *ssl_mutexes; + +static int sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *)) { + return (conn->ssl = SSL_new(s)) != NULL && + SSL_set_fd(conn->ssl, conn->client.sock) == 1 && + func(conn->ssl) == 1; +} + +// Return OpenSSL error message +static const char *ssl_error(void) { + unsigned long err; + err = ERR_get_error(); + return err == 0 ? "" : ERR_error_string(err, NULL); +} + +static void ssl_locking_callback(int mode, int mutex_num, const char *file, + int line) { + (void) line; + (void) file; + + if (mode & 1) { // 1 is CRYPTO_LOCK + (void) pthread_mutex_lock(&ssl_mutexes[mutex_num]); + } else { + (void) pthread_mutex_unlock(&ssl_mutexes[mutex_num]); + } +} + +static unsigned long ssl_id_callback(void) { + return (unsigned long) pthread_self(); +} + +#if !defined(NO_SSL_DL) +static int load_dll(struct mg_context *ctx, const char *dll_name, + struct ssl_func *sw) { + union {void *p; void (*fp)(void);} u; + void *dll_handle; + struct ssl_func *fp; + + if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) { + cry(fc(ctx), "%s: cannot load %s", __func__, dll_name); + return 0; + } + + for (fp = sw; fp->name != NULL; fp++) { +#ifdef _WIN32 + // GetProcAddress() returns pointer to function + u.fp = (void (*)(void)) dlsym(dll_handle, fp->name); +#else + // dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to + // function pointers. We need to use a union to make a cast. + u.p = dlsym(dll_handle, fp->name); +#endif // _WIN32 + if (u.fp == NULL) { + cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name); + return 0; + } else { + fp->ptr = u.fp; + } + } + + return 1; +} +#endif // NO_SSL_DL + +// Dynamically load SSL library. Set up ctx->ssl_ctx pointer. +static int set_ssl_option(struct mg_context *ctx) { + int i, size; + const char *pem; + + // If PEM file is not specified and the init_ssl callback + // is not specified, skip SSL initialization. + if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL) { + // MG_INIT_SSL + // ctx->callbacks.init_ssl == NULL) { + return 1; + } + +#if !defined(NO_SSL_DL) + if (!load_dll(ctx, SSL_LIB, ssl_sw) || + !load_dll(ctx, CRYPTO_LIB, crypto_sw)) { + return 0; + } +#endif // NO_SSL_DL + + // Initialize SSL library + SSL_library_init(); + SSL_load_error_strings(); + + if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { + cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error()); + return 0; + } + + // If user callback returned non-NULL, that means that user callback has + // set up certificate itself. In this case, skip sertificate setting. + // MG_INIT_SSL + if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 || + SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) { + cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error()); + return 0; + } + + if (pem != NULL) { + (void) SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem); + } + + // Initialize locking callbacks, needed for thread safety. + // http://www.openssl.org/support/faq.html#PROG1 + size = sizeof(pthread_mutex_t) * CRYPTO_num_locks(); + if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) { + cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error()); + return 0; + } + + for (i = 0; i < CRYPTO_num_locks(); i++) { + pthread_mutex_init(&ssl_mutexes[i], NULL); + } + + CRYPTO_set_locking_callback(&ssl_locking_callback); + CRYPTO_set_id_callback(&ssl_id_callback); + + return 1; +} + +static void uninitialize_ssl(struct mg_context *ctx) { + int i; + if (ctx->ssl_ctx != NULL) { + CRYPTO_set_locking_callback(NULL); + for (i = 0; i < CRYPTO_num_locks(); i++) { + pthread_mutex_destroy(&ssl_mutexes[i]); + } + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + } +} +#endif // !NO_SSL + diff --git a/build/src/util.c b/build/src/util.c new file mode 100644 index 00000000..0d0a05ed --- /dev/null +++ b/build/src/util.c @@ -0,0 +1,11 @@ +#include "internal.h" + +// Return fake connection structure. Used for logging, if connection +// is not applicable at the moment of logging. +static struct mg_connection *fc(struct mg_context *ctx) { + static struct mg_connection fake_connection; + fake_connection.ctx = ctx; + // See https://github.com/cesanta/mongoose/issues/236 + fake_connection.event.user_data = ctx->user_data; + return &fake_connection; +} diff --git a/mongoose.c b/mongoose.c index 5bd2d8bc..084af584 100644 --- a/mongoose.c +++ b/mongoose.c @@ -465,6 +465,7 @@ static void send_http_error(struct mg_connection *, int, const char *, PRINTF_ARGS(4, 5); static void cry(struct mg_connection *conn, PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); +static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len); #ifdef USE_LUA #include "lua_5.2.1.h" @@ -472,6 +473,16 @@ static int handle_lsp_request(struct mg_connection *, const char *, struct file *, struct lua_State *); #endif +// Return fake connection structure. Used for logging, if connection +// is not applicable at the moment of logging. +static struct mg_connection *fc(struct mg_context *ctx) { + static struct mg_connection fake_connection; + fake_connection.ctx = ctx; + // See https://github.com/cesanta/mongoose/issues/236 + fake_connection.event.user_data = ctx->user_data; + return &fake_connection; +} + static void mg_strlcpy(register char *dst, register const char *src, size_t n) { for (; *src != '\0' && n > 1; n--) { *dst++ = *src++; @@ -1820,6 +1831,342 @@ static int set_non_blocking_mode(SOCKET sock) { #endif // _WIN32 +// Print message to buffer. If buffer is large enough to hold the message, +// return buffer. If buffer is to small, allocate large enough buffer on heap, +// and return allocated buffer. +static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) { + va_list ap_copy; + int len; + + // Windows is not standard-compliant, and vsnprintf() returns -1 if + // buffer is too small. Also, older versions of msvcrt.dll do not have + // _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. + // Therefore, we make two passes: on first pass, get required message length. + // On second pass, actually print the message. + va_copy(ap_copy, ap); + len = vsnprintf(NULL, 0, fmt, ap_copy); + + if (len > (int) size && + (size = len + 1) > 0 && + (*buf = (char *) malloc(size)) == NULL) { + len = -1; // Allocation failed, mark failure + } else { + va_copy(ap_copy, ap); + vsnprintf(*buf, size, fmt, ap_copy); + } + + return len; +} + +int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) { + char mem[MG_BUF_LEN], *buf = mem; + int len; + + if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + len = mg_write(conn, buf, (size_t) len); + } + if (buf != mem && buf != NULL) { + free(buf); + } + + return len; +} + +int mg_printf(struct mg_connection *conn, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + return mg_vprintf(conn, fmt, ap); +} + +static int mg_chunked_printf(struct mg_connection *conn, const char *fmt, ...) { + char mem[MG_BUF_LEN], *buf = mem; + int len; + + va_list ap; + va_start(ap, fmt); + if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + len = mg_printf(conn, "%X\r\n%s\r\n", len, buf); + } + + if (buf != mem && buf != NULL) { + free(buf); + } + + return len; +} + + + +#if !defined(NO_SSL) +// set_ssl_option() function updates this array. +// It loads SSL library dynamically and changes NULLs to the actual addresses +// of respective functions. The macros above (like SSL_connect()) are really +// just calling these functions indirectly via the pointer. +static struct ssl_func ssl_sw[] = { + {"SSL_free", NULL}, + {"SSL_accept", NULL}, + {"SSL_connect", NULL}, + {"SSL_read", NULL}, + {"SSL_write", NULL}, + {"SSL_get_error", NULL}, + {"SSL_set_fd", NULL}, + {"SSL_new", NULL}, + {"SSL_CTX_new", NULL}, + {"SSLv23_server_method", NULL}, + {"SSL_library_init", NULL}, + {"SSL_CTX_use_PrivateKey_file", NULL}, + {"SSL_CTX_use_certificate_file",NULL}, + {"SSL_CTX_set_default_passwd_cb",NULL}, + {"SSL_CTX_free", NULL}, + {"SSL_load_error_strings", NULL}, + {"SSL_CTX_use_certificate_chain_file", NULL}, + {"SSLv23_client_method", NULL}, + {"SSL_pending", NULL}, + {"SSL_CTX_set_verify", NULL}, + {"SSL_shutdown", NULL}, + {NULL, NULL} +}; + +// Similar array as ssl_sw. These functions could be located in different lib. +static struct ssl_func crypto_sw[] = { + {"CRYPTO_num_locks", NULL}, + {"CRYPTO_set_locking_callback", NULL}, + {"CRYPTO_set_id_callback", NULL}, + {"ERR_get_error", NULL}, + {"ERR_error_string", NULL}, + {NULL, NULL} +}; + +static pthread_mutex_t *ssl_mutexes; + +static int sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *)) { + return (conn->ssl = SSL_new(s)) != NULL && + SSL_set_fd(conn->ssl, conn->client.sock) == 1 && + func(conn->ssl) == 1; +} + +// Return OpenSSL error message +static const char *ssl_error(void) { + unsigned long err; + err = ERR_get_error(); + return err == 0 ? "" : ERR_error_string(err, NULL); +} + +static void ssl_locking_callback(int mode, int mutex_num, const char *file, + int line) { + (void) line; + (void) file; + + if (mode & 1) { // 1 is CRYPTO_LOCK + (void) pthread_mutex_lock(&ssl_mutexes[mutex_num]); + } else { + (void) pthread_mutex_unlock(&ssl_mutexes[mutex_num]); + } +} + +static unsigned long ssl_id_callback(void) { + return (unsigned long) pthread_self(); +} + +#if !defined(NO_SSL_DL) +static int load_dll(struct mg_context *ctx, const char *dll_name, + struct ssl_func *sw) { + union {void *p; void (*fp)(void);} u; + void *dll_handle; + struct ssl_func *fp; + + if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) { + cry(fc(ctx), "%s: cannot load %s", __func__, dll_name); + return 0; + } + + for (fp = sw; fp->name != NULL; fp++) { +#ifdef _WIN32 + // GetProcAddress() returns pointer to function + u.fp = (void (*)(void)) dlsym(dll_handle, fp->name); +#else + // dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to + // function pointers. We need to use a union to make a cast. + u.p = dlsym(dll_handle, fp->name); +#endif // _WIN32 + if (u.fp == NULL) { + cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name); + return 0; + } else { + fp->ptr = u.fp; + } + } + + return 1; +} +#endif // NO_SSL_DL + +// Dynamically load SSL library. Set up ctx->ssl_ctx pointer. +static int set_ssl_option(struct mg_context *ctx) { + int i, size; + const char *pem; + + // If PEM file is not specified and the init_ssl callback + // is not specified, skip SSL initialization. + if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL) { + // MG_INIT_SSL + // ctx->callbacks.init_ssl == NULL) { + return 1; + } + +#if !defined(NO_SSL_DL) + if (!load_dll(ctx, SSL_LIB, ssl_sw) || + !load_dll(ctx, CRYPTO_LIB, crypto_sw)) { + return 0; + } +#endif // NO_SSL_DL + + // Initialize SSL library + SSL_library_init(); + SSL_load_error_strings(); + + if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { + cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error()); + return 0; + } + + // If user callback returned non-NULL, that means that user callback has + // set up certificate itself. In this case, skip sertificate setting. + // MG_INIT_SSL + if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 || + SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) { + cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error()); + return 0; + } + + if (pem != NULL) { + (void) SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem); + } + + // Initialize locking callbacks, needed for thread safety. + // http://www.openssl.org/support/faq.html#PROG1 + size = sizeof(pthread_mutex_t) * CRYPTO_num_locks(); + if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) { + cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error()); + return 0; + } + + for (i = 0; i < CRYPTO_num_locks(); i++) { + pthread_mutex_init(&ssl_mutexes[i], NULL); + } + + CRYPTO_set_locking_callback(&ssl_locking_callback); + CRYPTO_set_id_callback(&ssl_id_callback); + + return 1; +} + +static void uninitialize_ssl(struct mg_context *ctx) { + int i; + if (ctx->ssl_ctx != NULL) { + CRYPTO_set_locking_callback(NULL); + for (i = 0; i < CRYPTO_num_locks(); i++) { + pthread_mutex_destroy(&ssl_mutexes[i]); + } + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + } +} +#endif // !NO_SSL + + +static SOCKET conn2(const char *host, int port, int use_ssl, + char *ebuf, size_t ebuf_len) { + struct sockaddr_in sin; + struct hostent *he; + SOCKET sock = INVALID_SOCKET; + + if (host == NULL) { + snprintf(ebuf, ebuf_len, "%s", "NULL host"); + } else if (use_ssl && SSLv23_client_method == NULL) { + snprintf(ebuf, ebuf_len, "%s", "SSL is not initialized"); + // TODO(lsm): use something threadsafe instead of gethostbyname() + } else if ((he = gethostbyname(host)) == NULL) { + snprintf(ebuf, ebuf_len, "gethostbyname(%s): %s", host, strerror(ERRNO)); + } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { + snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO)); + } else { + set_close_on_exec(sock); + sin.sin_family = AF_INET; + sin.sin_port = htons((uint16_t) port); + sin.sin_addr = * (struct in_addr *) he->h_addr_list[0]; + if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) { + snprintf(ebuf, ebuf_len, "connect(%s:%d): %s", + host, port, strerror(ERRNO)); + closesocket(sock); + sock = INVALID_SOCKET; + } + } + return sock; +} + +struct mg_connection *mg_connect(const char *host, int port, int use_ssl, + char *ebuf, size_t ebuf_len) { + static struct mg_context fake_ctx; + struct mg_connection *conn = NULL; + SOCKET sock; + + if ((sock = conn2(host, port, use_ssl, ebuf, ebuf_len)) == INVALID_SOCKET) { + } else if ((conn = (struct mg_connection *) + calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) { + snprintf(ebuf, ebuf_len, "calloc(): %s", strerror(ERRNO)); + closesocket(sock); +#ifndef NO_SSL + } else if (use_ssl && (conn->client_ssl_ctx = + SSL_CTX_new(SSLv23_client_method())) == NULL) { + snprintf(ebuf, ebuf_len, "SSL_CTX_new error"); + closesocket(sock); + free(conn); + conn = NULL; +#endif // NO_SSL + } else { + socklen_t len = sizeof(struct sockaddr); + conn->buf_size = MAX_REQUEST_SIZE; + conn->buf = (char *) (conn + 1); + conn->ctx = &fake_ctx; + conn->client.sock = sock; + getsockname(sock, &conn->client.rsa.sa, &len); + conn->client.is_ssl = use_ssl; +#ifndef NO_SSL + if (use_ssl) { + // SSL_CTX_set_verify call is needed to switch off server certificate + // checking, which is off by default in OpenSSL and on in yaSSL. + SSL_CTX_set_verify(conn->client_ssl_ctx, 0, 0); + sslize(conn, conn->client_ssl_ctx, SSL_connect); + } +#endif + } + + return conn; +} + +struct mg_connection *mg_download(const char *host, int port, int use_ssl, + char *ebuf, size_t ebuf_len, + const char *fmt, ...) { + struct mg_connection *conn; + va_list ap; + + va_start(ap, fmt); + ebuf[0] = '\0'; + if ((conn = mg_connect(host, port, use_ssl, ebuf, ebuf_len)) == NULL) { + } else if (mg_vprintf(conn, fmt, ap) <= 0) { + snprintf(ebuf, ebuf_len, "%s", "Error sending request"); + } else { + getreq(conn, ebuf, ebuf_len); + } + if (ebuf[0] != '\0' && conn != NULL) { + mg_close_connection(conn); + conn = NULL; + } + + return conn; +} + // Return number of bytes left to read for this connection static int64_t left_to_read(const struct mg_connection *conn) { return conn->content_len + conn->request_len - conn->num_bytes_read; @@ -1902,16 +2249,6 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) { } } -// Return fake connection structure. Used for logging, if connection -// is not applicable at the moment of logging. -static struct mg_connection *fc(struct mg_context *ctx) { - static struct mg_connection fake_connection; - fake_connection.ctx = ctx; - // See https://github.com/cesanta/mongoose/issues/236 - fake_connection.event.user_data = ctx->user_data; - return &fake_connection; -} - const char *mg_version(void) { return MONGOOSE_VERSION; } @@ -2117,70 +2454,6 @@ int mg_write(struct mg_connection *conn, const void *buf, int len) { return (int) total; } -// Print message to buffer. If buffer is large enough to hold the message, -// return buffer. If buffer is to small, allocate large enough buffer on heap, -// and return allocated buffer. -static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) { - va_list ap_copy; - int len; - - // Windows is not standard-compliant, and vsnprintf() returns -1 if - // buffer is too small. Also, older versions of msvcrt.dll do not have - // _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. - // Therefore, we make two passes: on first pass, get required message length. - // On second pass, actually print the message. - va_copy(ap_copy, ap); - len = vsnprintf(NULL, 0, fmt, ap_copy); - - if (len > (int) size && - (size = len + 1) > 0 && - (*buf = (char *) malloc(size)) == NULL) { - len = -1; // Allocation failed, mark failure - } else { - va_copy(ap_copy, ap); - vsnprintf(*buf, size, fmt, ap_copy); - } - - return len; -} - -int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) { - char mem[MG_BUF_LEN], *buf = mem; - int len; - - if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { - len = mg_write(conn, buf, (size_t) len); - } - if (buf != mem && buf != NULL) { - free(buf); - } - - return len; -} - -int mg_printf(struct mg_connection *conn, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - return mg_vprintf(conn, fmt, ap); -} - -static int mg_chunked_printf(struct mg_connection *conn, const char *fmt, ...) { - char mem[MG_BUF_LEN], *buf = mem; - int len; - - va_list ap; - va_start(ap, fmt); - if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) { - len = mg_printf(conn, "%X\r\n%s\r\n", len, buf); - } - - if (buf != mem && buf != NULL) { - free(buf); - } - - return len; -} - 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; @@ -2513,38 +2786,6 @@ static void get_mime_type(struct mg_context *ctx, const char *path, vec->len = strlen(vec->ptr); } -static SOCKET conn2(const char *host, int port, int use_ssl, - char *ebuf, size_t ebuf_len) { - struct sockaddr_in sin; - struct hostent *he; - SOCKET sock = INVALID_SOCKET; - - if (host == NULL) { - snprintf(ebuf, ebuf_len, "%s", "NULL host"); - } else if (use_ssl && SSLv23_client_method == NULL) { - snprintf(ebuf, ebuf_len, "%s", "SSL is not initialized"); - // TODO(lsm): use something threadsafe instead of gethostbyname() - } else if ((he = gethostbyname(host)) == NULL) { - snprintf(ebuf, ebuf_len, "gethostbyname(%s): %s", host, strerror(ERRNO)); - } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { - snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO)); - } else { - set_close_on_exec(sock); - sin.sin_family = AF_INET; - sin.sin_port = htons((uint16_t) port); - sin.sin_addr = * (struct in_addr *) he->h_addr_list[0]; - if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) { - snprintf(ebuf, ebuf_len, "connect(%s:%d): %s", - host, port, strerror(ERRNO)); - closesocket(sock); - sock = INVALID_SOCKET; - } - } - return sock; -} - - - void mg_url_encode(const char *src, char *dst, size_t dst_len) { static const char *dont_escape = "._-$,;~()"; static const char *hex = "0123456789abcdef"; @@ -4631,183 +4872,6 @@ static int set_uid_option(struct mg_context *ctx) { } #endif // !_WIN32 -#if !defined(NO_SSL) -// set_ssl_option() function updates this array. -// It loads SSL library dynamically and changes NULLs to the actual addresses -// of respective functions. The macros above (like SSL_connect()) are really -// just calling these functions indirectly via the pointer. -static struct ssl_func ssl_sw[] = { - {"SSL_free", NULL}, - {"SSL_accept", NULL}, - {"SSL_connect", NULL}, - {"SSL_read", NULL}, - {"SSL_write", NULL}, - {"SSL_get_error", NULL}, - {"SSL_set_fd", NULL}, - {"SSL_new", NULL}, - {"SSL_CTX_new", NULL}, - {"SSLv23_server_method", NULL}, - {"SSL_library_init", NULL}, - {"SSL_CTX_use_PrivateKey_file", NULL}, - {"SSL_CTX_use_certificate_file",NULL}, - {"SSL_CTX_set_default_passwd_cb",NULL}, - {"SSL_CTX_free", NULL}, - {"SSL_load_error_strings", NULL}, - {"SSL_CTX_use_certificate_chain_file", NULL}, - {"SSLv23_client_method", NULL}, - {"SSL_pending", NULL}, - {"SSL_CTX_set_verify", NULL}, - {"SSL_shutdown", NULL}, - {NULL, NULL} -}; - -// Similar array as ssl_sw. These functions could be located in different lib. -static struct ssl_func crypto_sw[] = { - {"CRYPTO_num_locks", NULL}, - {"CRYPTO_set_locking_callback", NULL}, - {"CRYPTO_set_id_callback", NULL}, - {"ERR_get_error", NULL}, - {"ERR_error_string", NULL}, - {NULL, NULL} -}; - -static pthread_mutex_t *ssl_mutexes; - -static int sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *)) { - return (conn->ssl = SSL_new(s)) != NULL && - SSL_set_fd(conn->ssl, conn->client.sock) == 1 && - func(conn->ssl) == 1; -} - -// Return OpenSSL error message -static const char *ssl_error(void) { - unsigned long err; - err = ERR_get_error(); - return err == 0 ? "" : ERR_error_string(err, NULL); -} - -static void ssl_locking_callback(int mode, int mutex_num, const char *file, - int line) { - (void) line; - (void) file; - - if (mode & 1) { // 1 is CRYPTO_LOCK - (void) pthread_mutex_lock(&ssl_mutexes[mutex_num]); - } else { - (void) pthread_mutex_unlock(&ssl_mutexes[mutex_num]); - } -} - -static unsigned long ssl_id_callback(void) { - return (unsigned long) pthread_self(); -} - -#if !defined(NO_SSL_DL) -static int load_dll(struct mg_context *ctx, const char *dll_name, - struct ssl_func *sw) { - union {void *p; void (*fp)(void);} u; - void *dll_handle; - struct ssl_func *fp; - - if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) { - cry(fc(ctx), "%s: cannot load %s", __func__, dll_name); - return 0; - } - - for (fp = sw; fp->name != NULL; fp++) { -#ifdef _WIN32 - // GetProcAddress() returns pointer to function - u.fp = (void (*)(void)) dlsym(dll_handle, fp->name); -#else - // dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to - // function pointers. We need to use a union to make a cast. - u.p = dlsym(dll_handle, fp->name); -#endif // _WIN32 - if (u.fp == NULL) { - cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name); - return 0; - } else { - fp->ptr = u.fp; - } - } - - return 1; -} -#endif // NO_SSL_DL - -// Dynamically load SSL library. Set up ctx->ssl_ctx pointer. -static int set_ssl_option(struct mg_context *ctx) { - int i, size; - const char *pem; - - // If PEM file is not specified and the init_ssl callback - // is not specified, skip SSL initialization. - if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL) { - // MG_INIT_SSL - // ctx->callbacks.init_ssl == NULL) { - return 1; - } - -#if !defined(NO_SSL_DL) - if (!load_dll(ctx, SSL_LIB, ssl_sw) || - !load_dll(ctx, CRYPTO_LIB, crypto_sw)) { - return 0; - } -#endif // NO_SSL_DL - - // Initialize SSL library - SSL_library_init(); - SSL_load_error_strings(); - - if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { - cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error()); - return 0; - } - - // If user callback returned non-NULL, that means that user callback has - // set up certificate itself. In this case, skip sertificate setting. - // MG_INIT_SSL - if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 || - SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) { - cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error()); - return 0; - } - - if (pem != NULL) { - (void) SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem); - } - - // Initialize locking callbacks, needed for thread safety. - // http://www.openssl.org/support/faq.html#PROG1 - size = sizeof(pthread_mutex_t) * CRYPTO_num_locks(); - if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) { - cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error()); - return 0; - } - - for (i = 0; i < CRYPTO_num_locks(); i++) { - pthread_mutex_init(&ssl_mutexes[i], NULL); - } - - CRYPTO_set_locking_callback(&ssl_locking_callback); - CRYPTO_set_id_callback(&ssl_id_callback); - - return 1; -} - -static void uninitialize_ssl(struct mg_context *ctx) { - int i; - if (ctx->ssl_ctx != NULL) { - CRYPTO_set_locking_callback(NULL); - for (i = 0; i < CRYPTO_num_locks(); i++) { - pthread_mutex_destroy(&ssl_mutexes[i]); - } - CRYPTO_set_locking_callback(NULL); - CRYPTO_set_id_callback(NULL); - } -} -#endif // !NO_SSL - static int set_gpass_option(struct mg_context *ctx) { struct file file = STRUCT_FILE_INITIALIZER; const char *path = ctx->config[GLOBAL_PASSWORDS_FILE]; @@ -4889,46 +4953,6 @@ void mg_close_connection(struct mg_connection *conn) { free(conn); } -struct mg_connection *mg_connect(const char *host, int port, int use_ssl, - char *ebuf, size_t ebuf_len) { - static struct mg_context fake_ctx; - struct mg_connection *conn = NULL; - SOCKET sock; - - if ((sock = conn2(host, port, use_ssl, ebuf, ebuf_len)) == INVALID_SOCKET) { - } else if ((conn = (struct mg_connection *) - calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) { - snprintf(ebuf, ebuf_len, "calloc(): %s", strerror(ERRNO)); - closesocket(sock); -#ifndef NO_SSL - } else if (use_ssl && (conn->client_ssl_ctx = - SSL_CTX_new(SSLv23_client_method())) == NULL) { - snprintf(ebuf, ebuf_len, "SSL_CTX_new error"); - closesocket(sock); - free(conn); - conn = NULL; -#endif // NO_SSL - } else { - socklen_t len = sizeof(struct sockaddr); - conn->buf_size = MAX_REQUEST_SIZE; - conn->buf = (char *) (conn + 1); - conn->ctx = &fake_ctx; - conn->client.sock = sock; - getsockname(sock, &conn->client.rsa.sa, &len); - conn->client.is_ssl = use_ssl; -#ifndef NO_SSL - if (use_ssl) { - // SSL_CTX_set_verify call is needed to switch off server certificate - // checking, which is off by default in OpenSSL and on in yaSSL. - SSL_CTX_set_verify(conn->client_ssl_ctx, 0, 0); - sslize(conn, conn->client_ssl_ctx, SSL_connect); - } -#endif - } - - return conn; -} - static int is_valid_uri(const char *uri) { // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 // URI can be an asterisk (*) or should start with slash. @@ -4972,28 +4996,6 @@ static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len) { return ebuf[0] == '\0'; } -struct mg_connection *mg_download(const char *host, int port, int use_ssl, - char *ebuf, size_t ebuf_len, - const char *fmt, ...) { - struct mg_connection *conn; - va_list ap; - - va_start(ap, fmt); - ebuf[0] = '\0'; - if ((conn = mg_connect(host, port, use_ssl, ebuf, ebuf_len)) == NULL) { - } else if (mg_vprintf(conn, fmt, ap) <= 0) { - snprintf(ebuf, ebuf_len, "%s", "Error sending request"); - } else { - getreq(conn, ebuf, ebuf_len); - } - if (ebuf[0] != '\0' && conn != NULL) { - mg_close_connection(conn); - conn = NULL; - } - - return conn; -} - static void process_new_connection(struct mg_connection *conn) { struct mg_request_info *ri = &conn->request_info; int keep_alive_enabled, keep_alive, discard_len;