diff --git a/CMakeLists.txt b/CMakeLists.txt index 4df0dfbd..d68410dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -262,6 +262,7 @@ set(SOURCES lib/libwebsockets.c lib/output.c lib/parsers.c + lib/context.c lib/sha-1.c ) diff --git a/lib/context.c b/lib/context.c new file mode 100644 index 00000000..34bcb323 --- /dev/null +++ b/lib/context.c @@ -0,0 +1,757 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2014 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +#ifdef LWS_OPENSSL_SUPPORT +int openssl_websocket_private_data_index; +#endif + +#ifndef LWS_BUILD_HASH +#define LWS_BUILD_HASH "unknown-build-hash" +#endif + +static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH; + +/** + * lws_get_library_version: get version and git hash library built from + * + * returns a const char * to a string like "1.1 178d78c" + * representing the library version followed by the git head hash it + * was built from + */ + +LWS_VISIBLE const char * +lws_get_library_version(void) +{ + return library_version; +} + +#ifdef LWS_OPENSSL_SUPPORT +static int +OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + + SSL *ssl; + int n; + struct libwebsocket_context *context; + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + + /* + * !!! nasty openssl requires the index to come as a library-scope + * static + */ + context = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); + + n = context->protocols[0].callback(NULL, NULL, + LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION, + x509_ctx, ssl, preverify_ok); + + /* convert return code from 0 = OK to 1 = OK */ + + if (!n) + n = 1; + else + n = 0; + + return n; +} +#endif + +/** + * libwebsocket_create_context() - Create the websocket handler + * @info: pointer to struct with parameters + * + * This function creates the listening socket (if serving) and takes care + * of all initialization in one step. + * + * After initialization, it returns a struct libwebsocket_context * that + * represents this server. After calling, user code needs to take care + * of calling libwebsocket_service() with the context pointer to get the + * server's sockets serviced. This can be done in the same process context + * or a forked process, or another thread, + * + * The protocol callback functions are called for a handful of events + * including http requests coming in, websocket connections becoming + * established, and data arriving; it's also called periodically to allow + * async transmission. + * + * HTTP requests are sent always to the FIRST protocol in @protocol, since + * at that time websocket protocol has not been negotiated. Other + * protocols after the first one never see any HTTP callack activity. + * + * The server created is a simple http server by default; part of the + * websocket standard is upgrading this http connection to a websocket one. + * + * This allows the same server to provide files like scripts and favicon / + * images or whatever over http and dynamic data over websockets all in + * one place; they're all handled in the user callback. + */ + +LWS_VISIBLE struct libwebsocket_context * +libwebsocket_create_context(struct lws_context_creation_info *info) +{ + struct libwebsocket_context *context = NULL; + char *p; + int n; +#ifndef LWS_NO_SERVER + int opt = 1; + struct libwebsocket *wsi; +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 serv_addr6; +#endif + struct sockaddr_in serv_addr4; + struct sockaddr *v; +#endif + +#ifdef LWS_OPENSSL_SUPPORT + SSL_METHOD *method; +#endif + +#ifndef LWS_NO_DAEMONIZE + int pid_daemon = get_daemonize_pid(); +#endif + + lwsl_notice("Initial logging level %d\n", log_level); + lwsl_notice("Library version: %s\n", library_version); +#ifdef LWS_USE_IPV6 + if (!(info->options & LWS_SERVER_OPTION_DISABLE_IPV6)) + lwsl_notice("IPV6 compiled in and enabled\n"); + else + lwsl_notice("IPV6 compiled in but disabled\n"); +#else + lwsl_notice("IPV6 not compiled in\n"); +#endif +#ifdef LWS_USE_LIBEV + if (info->options & LWS_SERVER_OPTION_LIBEV) + lwsl_notice("libev support compiled in and enabled\n"); + else + lwsl_notice("libev support compiled in but disabled\n"); +#else + lwsl_notice("libev support not compiled in\n"); +#endif + lwsl_info(" LWS_MAX_HEADER_LEN: %u\n", LWS_MAX_HEADER_LEN); + lwsl_info(" LWS_MAX_PROTOCOLS: %u\n", LWS_MAX_PROTOCOLS); +#ifndef LWS_NO_EXTENSIONS + lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", + LWS_MAX_EXTENSIONS_ACTIVE); +#else + lwsl_notice(" Configured without extension support\n"); +#endif + lwsl_info(" SPEC_LATEST_SUPPORTED: %u\n", SPEC_LATEST_SUPPORTED); + lwsl_info(" AWAITING_TIMEOUT: %u\n", AWAITING_TIMEOUT); + if (info->ssl_cipher_list) + lwsl_info(" SSL ciphers: '%s'\n", info->ssl_cipher_list); + lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH); + lwsl_info(" LWS_MAX_ZLIB_CONN_BUFFER: %u\n", LWS_MAX_ZLIB_CONN_BUFFER); + + if (lws_plat_context_early_init()) + return NULL; + + context = (struct libwebsocket_context *) + malloc(sizeof(struct libwebsocket_context)); + if (!context) { + lwsl_err("No memory for websocket context\n"); + return NULL; + } + memset(context, 0, sizeof(*context)); +#ifndef LWS_NO_DAEMONIZE + context->started_with_parent = pid_daemon; + lwsl_notice(" Started with daemon pid %d\n", pid_daemon); +#endif + + context->listen_service_extraseen = 0; + context->protocols = info->protocols; + context->listen_port = info->port; + context->http_proxy_port = 0; + context->http_proxy_address[0] = '\0'; + context->options = info->options; + context->iface = info->iface; + /* to reduce this allocation, */ + context->max_fds = getdtablesize(); + lwsl_notice(" static allocation: %u + (%u x %u fds) = %u bytes\n", + sizeof(struct libwebsocket_context), + sizeof(struct libwebsocket_pollfd) + + sizeof(struct libwebsocket *), + context->max_fds, + sizeof(struct libwebsocket_context) + + ((sizeof(struct libwebsocket_pollfd) + + sizeof(struct libwebsocket *)) * + context->max_fds)); + + context->fds = (struct libwebsocket_pollfd *) + malloc(sizeof(struct libwebsocket_pollfd) * + context->max_fds); + if (context->fds == NULL) { + lwsl_err("Unable to allocate fds array for %d connections\n", + context->max_fds); + free(context); + return NULL; + } + + context->lws_lookup = (struct libwebsocket **) + malloc(sizeof(struct libwebsocket *) * context->max_fds); + if (context->lws_lookup == NULL) { + lwsl_err( + "Unable to allocate lws_lookup array for %d connections\n", + context->max_fds); + free(context->fds); + free(context); + return NULL; + } + memset(context->lws_lookup, 0, sizeof(struct libwebsocket *) * + context->max_fds); + + if (lws_plat_init_fd_tables(context)) { + free(context->lws_lookup); + free(context->fds); + free(context); + return NULL; + } + +#ifndef LWS_NO_EXTENSIONS + context->extensions = info->extensions; +#endif + context->last_timeout_check_s = 0; + context->user_space = info->user; + +#ifdef LWS_OPENSSL_SUPPORT + context->use_ssl = 0; + context->allow_non_ssl_on_ssl_port = 0; + context->ssl_ctx = NULL; + context->ssl_client_ctx = NULL; + openssl_websocket_private_data_index = 0; +#endif + + strcpy(context->canonical_hostname, "unknown"); + +#ifndef LWS_NO_SERVER + if (!(info->options & LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME)) { + /* find canonical hostname */ + gethostname((char *)context->canonical_hostname, + sizeof(context->canonical_hostname) - 1); + + lwsl_notice(" canonical_hostname = %s\n", + context->canonical_hostname); + } +#endif + + /* split the proxy ads:port if given */ + + if (info->http_proxy_address) { + strncpy(context->http_proxy_address, info->http_proxy_address, + sizeof(context->http_proxy_address) - 1); + context->http_proxy_address[ + sizeof(context->http_proxy_address) - 1] = '\0'; + context->http_proxy_port = info->http_proxy_port; + } else { +#ifdef HAVE_GETENV + p = getenv("http_proxy"); + if (p) { + strncpy(context->http_proxy_address, p, + sizeof(context->http_proxy_address) - 1); + context->http_proxy_address[ + sizeof(context->http_proxy_address) - 1] = '\0'; + + p = strchr(context->http_proxy_address, ':'); + if (p == NULL) { + lwsl_err("http_proxy needs to be ads:port\n"); + goto bail; + } + *p = '\0'; + context->http_proxy_port = atoi(p + 1); + } +#endif + } + + if (context->http_proxy_address[0]) { + lwsl_notice(" Proxy %s:%u\n", + context->http_proxy_address, + context->http_proxy_port); + } + +#ifndef LWS_NO_SERVER + if (info->port != CONTEXT_PORT_NO_LISTEN) { + +#ifdef LWS_OPENSSL_SUPPORT + context->use_ssl = info->ssl_cert_filepath != NULL && + info->ssl_private_key_filepath != NULL; +#ifdef USE_CYASSL + lwsl_notice(" Compiled with CYASSL support\n"); +#else + lwsl_notice(" Compiled with OpenSSL support\n"); +#endif + if (context->use_ssl) + lwsl_notice(" Using SSL mode\n"); + else + lwsl_notice(" Using non-SSL mode\n"); + +#else + if (info->ssl_cert_filepath != NULL && + info->ssl_private_key_filepath != NULL) { + lwsl_notice(" Not compiled for OpenSSl support!\n"); + goto bail; + } + lwsl_notice(" Compiled without SSL support\n"); +#endif + + lwsl_notice( + " per-conn mem: %u + %u headers + protocol rx buf\n", + sizeof(struct libwebsocket), + sizeof(struct allocated_headers)); + } +#endif + +#ifdef LWS_OPENSSL_SUPPORT + + /* basic openssl init */ + + SSL_library_init(); + + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + + openssl_websocket_private_data_index = + SSL_get_ex_new_index(0, "libwebsockets", NULL, NULL, NULL); + + /* + * Firefox insists on SSLv23 not SSLv3 + * Konq disables SSLv2 by default now, SSLv23 works + */ + + method = (SSL_METHOD *)SSLv23_server_method(); + if (!method) { + int error = ERR_get_error(); + lwsl_err("problem creating ssl method %lu: %s\n", + error, + ERR_error_string(error, + (char *)context->service_buffer)); + goto bail; + } + context->ssl_ctx = SSL_CTX_new(method); /* create context */ + if (!context->ssl_ctx) { + int error = ERR_get_error(); + lwsl_err("problem creating ssl context %lu: %s\n", + error, + ERR_error_string(error, + (char *)context->service_buffer)); + goto bail; + } + +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_COMPRESSION); +#endif + SSL_CTX_set_options(context->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + if (info->ssl_cipher_list) + SSL_CTX_set_cipher_list(context->ssl_ctx, + info->ssl_cipher_list); + +#ifndef LWS_NO_CLIENT + + /* client context */ + + if (info->port == CONTEXT_PORT_NO_LISTEN) { + method = (SSL_METHOD *)SSLv23_client_method(); + if (!method) { + int error = ERR_get_error(); + lwsl_err("problem creating ssl method %lu: %s\n", + error, + ERR_error_string(error, + (char *)context->service_buffer)); + goto bail; + } + /* create context */ + context->ssl_client_ctx = SSL_CTX_new(method); + if (!context->ssl_client_ctx) { + int error = ERR_get_error(); + lwsl_err("problem creating ssl context %lu: %s\n", + error, + ERR_error_string(error, + (char *)context->service_buffer)); + goto bail; + } + +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(context->ssl_client_ctx, + SSL_OP_NO_COMPRESSION); +#endif + SSL_CTX_set_options(context->ssl_client_ctx, + SSL_OP_CIPHER_SERVER_PREFERENCE); + if (info->ssl_cipher_list) + SSL_CTX_set_cipher_list(context->ssl_client_ctx, + info->ssl_cipher_list); + +#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS + /* loads OS default CA certs */ + SSL_CTX_set_default_verify_paths(context->ssl_client_ctx); +#endif + + /* openssl init for cert verification (for client sockets) */ + if (!info->ssl_ca_filepath) { + if (!SSL_CTX_load_verify_locations( + context->ssl_client_ctx, NULL, + LWS_OPENSSL_CLIENT_CERTS)) + lwsl_err( + "Unable to load SSL Client certs from %s " + "(set by --with-client-cert-dir= " + "in configure) -- client ssl isn't " + "going to work", LWS_OPENSSL_CLIENT_CERTS); + } else + if (!SSL_CTX_load_verify_locations( + context->ssl_client_ctx, info->ssl_ca_filepath, + NULL)) + lwsl_err( + "Unable to load SSL Client certs " + "file from %s -- client ssl isn't " + "going to work", info->ssl_ca_filepath); + + /* + * callback allowing user code to load extra verification certs + * helping the client to verify server identity + */ + + /* support for client-side certificate authentication */ + if (info->ssl_cert_filepath) { + n = SSL_CTX_use_certificate_chain_file( + context->ssl_client_ctx, + info->ssl_cert_filepath); + if (n != 1) { + lwsl_err("problem getting cert '%s' %lu: %s\n", + info->ssl_cert_filepath, + ERR_get_error(), + ERR_error_string(ERR_get_error(), + (char *)context->service_buffer)); + goto bail; + } + } + if (info->ssl_private_key_filepath) { + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(context->ssl_client_ctx, + info->ssl_private_key_filepath, + SSL_FILETYPE_PEM) != 1) { + lwsl_err("use_PrivateKey_file '%s' %lu: %s\n", + info->ssl_private_key_filepath, + ERR_get_error(), + ERR_error_string(ERR_get_error(), + (char *)context->service_buffer)); + goto bail; + } + + /* verify private key */ + if (!SSL_CTX_check_private_key( + context->ssl_client_ctx)) { + lwsl_err("Private SSL key doesn't match cert\n"); + goto bail; + } + } + + context->protocols[0].callback(context, NULL, + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS, + context->ssl_client_ctx, NULL, 0); + } +#endif + + /* as a server, are we requiring clients to identify themselves? */ + + if (info->options & + LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT) { + + /* absolutely require the client cert */ + + SSL_CTX_set_verify(context->ssl_ctx, + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + OpenSSL_verify_callback); + + /* + * give user code a chance to load certs into the server + * allowing it to verify incoming client certs + */ + + context->protocols[0].callback(context, NULL, + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, + context->ssl_ctx, NULL, 0); + } + + if (info->options & LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT) { + /* Normally SSL listener rejects non-ssl, optionally allow */ + context->allow_non_ssl_on_ssl_port = 1; + } + + if (context->use_ssl) { + + /* openssl init for server sockets */ + + /* set the local certificate from CertFile */ + n = SSL_CTX_use_certificate_chain_file(context->ssl_ctx, + info->ssl_cert_filepath); + if (n != 1) { + int error = ERR_get_error(); + lwsl_err("problem getting cert '%s' %lu: %s\n", + info->ssl_cert_filepath, + error, + ERR_error_string(error, + (char *)context->service_buffer)); + goto bail; + } + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(context->ssl_ctx, + info->ssl_private_key_filepath, + SSL_FILETYPE_PEM) != 1) { + int error = ERR_get_error(); + lwsl_err("ssl problem getting key '%s' %lu: %s\n", + info->ssl_private_key_filepath, + error, + ERR_error_string(error, + (char *)context->service_buffer)); + goto bail; + } + /* verify private key */ + if (!SSL_CTX_check_private_key(context->ssl_ctx)) { + lwsl_err("Private SSL key doesn't match cert\n"); + goto bail; + } + + /* SSL is happy and has a cert it's content with */ + } +#endif + +#ifndef LWS_NO_SERVER + /* set up our external listening socket we serve on */ + + if (info->port != CONTEXT_PORT_NO_LISTEN) { + int sockfd; + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(context)) + sockfd = socket(AF_INET6, SOCK_STREAM, 0); + else +#endif + sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (sockfd < 0) { + lwsl_err("ERROR opening socket\n"); + goto bail; + } + + /* + * allow us to restart even if old sockets in TIME_WAIT + */ + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, + (const void *)&opt, sizeof(opt)); + + lws_plat_set_socket_options(context, sockfd); + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(context)) { + v = (struct sockaddr *)&serv_addr6; + n = sizeof(struct sockaddr_in6); + bzero((char *) &serv_addr6, sizeof(serv_addr6)); + serv_addr6.sin6_addr = in6addr_any; + serv_addr6.sin6_family = AF_INET6; + serv_addr6.sin6_port = htons(info->port); + } else +#endif + { + v = (struct sockaddr *)&serv_addr4; + n = sizeof(serv_addr4); + bzero((char *) &serv_addr4, sizeof(serv_addr4)); + serv_addr4.sin_addr.s_addr = INADDR_ANY; + serv_addr4.sin_family = AF_INET; + serv_addr4.sin_port = htons(info->port); + + if (info->iface) { + if (interface_to_sa(context, info->iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find interface %s\n", + info->iface); + compatible_close(sockfd); + goto bail; + } + } + } /* ipv4 */ + + n = bind(sockfd, v, n); + if (n < 0) { + lwsl_err("ERROR on binding to port %d (%d %d)\n", + info->port, n, LWS_ERRNO); + compatible_close(sockfd); + goto bail; + } + + if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) + lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO)); + else + info->port = ntohs(sin.sin_port); + + context->listen_port = info->port; + + wsi = (struct libwebsocket *)malloc( + sizeof(struct libwebsocket)); + if (wsi == NULL) { + lwsl_err("Out of mem\n"); + compatible_close(sockfd); + goto bail; + } + memset(wsi, 0, sizeof(struct libwebsocket)); + wsi->sock = sockfd; + wsi->mode = LWS_CONNMODE_SERVER_LISTENER; + + insert_wsi_socket_into_fds(context, wsi); + + context->listen_service_modulo = LWS_LISTEN_SERVICE_MODULO; + context->listen_service_count = 0; + context->listen_service_fd = sockfd; + + listen(sockfd, LWS_SOMAXCONN); + lwsl_notice(" Listening on port %d\n", info->port); + } +#endif + + /* + * drop any root privs for this process + * to listen on port < 1023 we would have needed root, but now we are + * listening, we don't want the power for anything else + */ + lws_plat_drop_app_privileges(info); + + /* initialize supported protocols */ + + for (context->count_protocols = 0; + info->protocols[context->count_protocols].callback; + context->count_protocols++) { + + lwsl_parser(" Protocol: %s\n", + info->protocols[context->count_protocols].name); + + info->protocols[context->count_protocols].owning_server = + context; + info->protocols[context->count_protocols].protocol_index = + context->count_protocols; + + /* + * inform all the protocols that they are doing their one-time + * initialization if they want to + */ + info->protocols[context->count_protocols].callback(context, + NULL, LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0); + } + + /* + * give all extensions a chance to create any per-context + * allocations they need + */ + + if (info->port != CONTEXT_PORT_NO_LISTEN) { + if (lws_ext_callback_for_each_extension_type(context, NULL, + LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT, + NULL, 0) < 0) + goto bail; + } else + if (lws_ext_callback_for_each_extension_type(context, NULL, + LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT, + NULL, 0) < 0) + goto bail; + + return context; + +bail: + libwebsocket_context_destroy(context); + return NULL; +} + +/** + * libwebsocket_context_destroy() - Destroy the websocket context + * @context: Websocket context + * + * This function closes any active connections and then frees the + * context. After calling this, any further use of the context is + * undefined. + */ +LWS_VISIBLE void +libwebsocket_context_destroy(struct libwebsocket_context *context) +{ + int n; + struct libwebsocket_protocols *protocol = context->protocols; + +#ifdef LWS_LATENCY + if (context->worst_latency_info[0]) + lwsl_notice("Worst latency: %s\n", context->worst_latency_info); +#endif + + for (n = 0; n < context->fds_count; n++) { + struct libwebsocket *wsi = + context->lws_lookup[context->fds[n].fd]; + if (!wsi) + continue; + libwebsocket_close_and_free_session(context, + wsi, LWS_CLOSE_STATUS_NOSTATUS /* no protocol close */); + n--; + } + + /* + * give all extensions a chance to clean up any per-context + * allocations they might have made + */ + if (context->listen_port) { + if (lws_ext_callback_for_each_extension_type(context, NULL, LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT, NULL, 0) < 0) + return; + } else + if (lws_ext_callback_for_each_extension_type(context, NULL, LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT, NULL, 0) < 0) + return; + + /* + * inform all the protocols that they are done and will have no more + * callbacks + */ + + while (protocol->callback) { + protocol->callback(context, NULL, LWS_CALLBACK_PROTOCOL_DESTROY, + NULL, NULL, 0); + protocol++; + } + + lws_plat_context_early_destroy(context); + +#ifdef LWS_OPENSSL_SUPPORT + if (context->ssl_ctx) + SSL_CTX_free(context->ssl_ctx); + if (context->ssl_client_ctx) + SSL_CTX_free(context->ssl_client_ctx); + + ERR_remove_state(0); + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#endif + + if (context->fds) + free(context->fds); + if (context->lws_lookup) + free(context->lws_lookup); + + free(context); + + lws_plat_context_late_destroy(context); +} diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index c773cf95..ac7d4099 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -21,20 +21,10 @@ #include "private-libwebsockets.h" -#ifdef LWS_OPENSSL_SUPPORT -int openssl_websocket_private_data_index; -#endif - -#ifndef LWS_BUILD_HASH -#define LWS_BUILD_HASH "unknown-build-hash" -#endif - -static int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE; +int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE; static void lwsl_emit_stderr(int level, const char *line); static void (*lwsl_emit)(int level, const char *line) = lwsl_emit_stderr; -static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH; - static const char * const log_level_names[] = { "ERR", "WARN", @@ -48,20 +38,6 @@ static const char * const log_level_names[] = { "LATENCY", }; -/** - * lws_get_library_version: get version and git hash library built from - * - * returns a const char * to a string like "1.1 178d78c" - * representing the library version followed by the git head hash it - * was built from - */ - -LWS_VISIBLE const char * -lws_get_library_version(void) -{ - return library_version; -} - int insert_wsi_socket_into_fds(struct libwebsocket_context *context, struct libwebsocket *wsi) @@ -1000,80 +976,6 @@ handled: return n; } -/** - * libwebsocket_context_destroy() - Destroy the websocket context - * @context: Websocket context - * - * This function closes any active connections and then frees the - * context. After calling this, any further use of the context is - * undefined. - */ -LWS_VISIBLE void -libwebsocket_context_destroy(struct libwebsocket_context *context) -{ - int n; - struct libwebsocket_protocols *protocol = context->protocols; - -#ifdef LWS_LATENCY - if (context->worst_latency_info[0]) - lwsl_notice("Worst latency: %s\n", context->worst_latency_info); -#endif - - for (n = 0; n < context->fds_count; n++) { - struct libwebsocket *wsi = - context->lws_lookup[context->fds[n].fd]; - if (!wsi) - continue; - libwebsocket_close_and_free_session(context, - wsi, LWS_CLOSE_STATUS_NOSTATUS /* no protocol close */); - n--; - } - - /* - * give all extensions a chance to clean up any per-context - * allocations they might have made - */ - if (context->listen_port) { - if (lws_ext_callback_for_each_extension_type(context, NULL, LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT, NULL, 0) < 0) - return; - } else - if (lws_ext_callback_for_each_extension_type(context, NULL, LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT, NULL, 0) < 0) - return; - - /* - * inform all the protocols that they are done and will have no more - * callbacks - */ - - while (protocol->callback) { - protocol->callback(context, NULL, LWS_CALLBACK_PROTOCOL_DESTROY, - NULL, NULL, 0); - protocol++; - } - - lws_plat_context_early_destroy(context); - -#ifdef LWS_OPENSSL_SUPPORT - if (context->ssl_ctx) - SSL_CTX_free(context->ssl_ctx); - if (context->ssl_client_ctx) - SSL_CTX_free(context->ssl_client_ctx); - - ERR_remove_state(0); - ERR_free_strings(); - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); -#endif - - if (context->fds) - free(context->fds); - if (context->lws_lookup) - free(context->lws_lookup); - - free(context); - - lws_plat_context_late_destroy(context); -} /** * libwebsocket_context_user() - get the user data associated with the context @@ -1456,39 +1358,6 @@ libwebsocket_canonical_hostname(struct libwebsocket_context *context) return (const char *)context->canonical_hostname; } -#ifdef LWS_OPENSSL_SUPPORT -static int -OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) -{ - - SSL *ssl; - int n; - struct libwebsocket_context *context; - - ssl = X509_STORE_CTX_get_ex_data(x509_ctx, - SSL_get_ex_data_X509_STORE_CTX_idx()); - - /* - * !!! nasty openssl requires the index to come as a library-scope - * static - */ - context = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); - - n = context->protocols[0].callback(NULL, NULL, - LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION, - x509_ctx, ssl, preverify_ok); - - /* convert return code from 0 = OK to 1 = OK */ - - if (!n) - n = 1; - else - n = 0; - - return n; -} -#endif - int user_callback_handle_rxflow(callback_function callback_function, struct libwebsocket_context *context, struct libwebsocket *wsi, @@ -1504,608 +1373,6 @@ int user_callback_handle_rxflow(callback_function callback_function, return n; } -/** - * libwebsocket_create_context() - Create the websocket handler - * @info: pointer to struct with parameters - * - * This function creates the listening socket (if serving) and takes care - * of all initialization in one step. - * - * After initialization, it returns a struct libwebsocket_context * that - * represents this server. After calling, user code needs to take care - * of calling libwebsocket_service() with the context pointer to get the - * server's sockets serviced. This can be done in the same process context - * or a forked process, or another thread, - * - * The protocol callback functions are called for a handful of events - * including http requests coming in, websocket connections becoming - * established, and data arriving; it's also called periodically to allow - * async transmission. - * - * HTTP requests are sent always to the FIRST protocol in @protocol, since - * at that time websocket protocol has not been negotiated. Other - * protocols after the first one never see any HTTP callack activity. - * - * The server created is a simple http server by default; part of the - * websocket standard is upgrading this http connection to a websocket one. - * - * This allows the same server to provide files like scripts and favicon / - * images or whatever over http and dynamic data over websockets all in - * one place; they're all handled in the user callback. - */ - -LWS_VISIBLE struct libwebsocket_context * -libwebsocket_create_context(struct lws_context_creation_info *info) -{ - struct libwebsocket_context *context = NULL; - char *p; - int n; -#ifndef LWS_NO_SERVER - int opt = 1; - struct libwebsocket *wsi; -#ifdef LWS_USE_IPV6 - struct sockaddr_in6 serv_addr6; -#endif - struct sockaddr_in serv_addr4; - struct sockaddr *v; -#endif - -#ifdef LWS_OPENSSL_SUPPORT - SSL_METHOD *method; -#endif - -#ifndef LWS_NO_DAEMONIZE - int pid_daemon = get_daemonize_pid(); -#endif - - lwsl_notice("Initial logging level %d\n", log_level); - lwsl_notice("Library version: %s\n", library_version); -#ifdef LWS_USE_IPV6 - if (!(info->options & LWS_SERVER_OPTION_DISABLE_IPV6)) - lwsl_notice("IPV6 compiled in and enabled\n"); - else - lwsl_notice("IPV6 compiled in but disabled\n"); -#else - lwsl_notice("IPV6 not compiled in\n"); -#endif -#ifdef LWS_USE_LIBEV - if (info->options & LWS_SERVER_OPTION_LIBEV) - lwsl_notice("libev support compiled in and enabled\n"); - else - lwsl_notice("libev support compiled in but disabled\n"); -#else - lwsl_notice("libev support not compiled in\n"); -#endif - lwsl_info(" LWS_MAX_HEADER_LEN: %u\n", LWS_MAX_HEADER_LEN); - lwsl_info(" LWS_MAX_PROTOCOLS: %u\n", LWS_MAX_PROTOCOLS); -#ifndef LWS_NO_EXTENSIONS - lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", - LWS_MAX_EXTENSIONS_ACTIVE); -#else - lwsl_notice(" Configured without extension support\n"); -#endif - lwsl_info(" SPEC_LATEST_SUPPORTED: %u\n", SPEC_LATEST_SUPPORTED); - lwsl_info(" AWAITING_TIMEOUT: %u\n", AWAITING_TIMEOUT); - if (info->ssl_cipher_list) - lwsl_info(" SSL ciphers: '%s'\n", info->ssl_cipher_list); - lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH); - lwsl_info(" LWS_MAX_ZLIB_CONN_BUFFER: %u\n", LWS_MAX_ZLIB_CONN_BUFFER); - - if (lws_plat_context_early_init()) - return NULL; - - context = (struct libwebsocket_context *) - malloc(sizeof(struct libwebsocket_context)); - if (!context) { - lwsl_err("No memory for websocket context\n"); - return NULL; - } - memset(context, 0, sizeof(*context)); -#ifndef LWS_NO_DAEMONIZE - context->started_with_parent = pid_daemon; - lwsl_notice(" Started with daemon pid %d\n", pid_daemon); -#endif - - context->listen_service_extraseen = 0; - context->protocols = info->protocols; - context->listen_port = info->port; - context->http_proxy_port = 0; - context->http_proxy_address[0] = '\0'; - context->options = info->options; - context->iface = info->iface; - /* to reduce this allocation, */ - context->max_fds = getdtablesize(); - lwsl_notice(" static allocation: %u + (%u x %u fds) = %u bytes\n", - sizeof(struct libwebsocket_context), - sizeof(struct libwebsocket_pollfd) + - sizeof(struct libwebsocket *), - context->max_fds, - sizeof(struct libwebsocket_context) + - ((sizeof(struct libwebsocket_pollfd) + - sizeof(struct libwebsocket *)) * - context->max_fds)); - - context->fds = (struct libwebsocket_pollfd *) - malloc(sizeof(struct libwebsocket_pollfd) * - context->max_fds); - if (context->fds == NULL) { - lwsl_err("Unable to allocate fds array for %d connections\n", - context->max_fds); - free(context); - return NULL; - } - - context->lws_lookup = (struct libwebsocket **) - malloc(sizeof(struct libwebsocket *) * context->max_fds); - if (context->lws_lookup == NULL) { - lwsl_err( - "Unable to allocate lws_lookup array for %d connections\n", - context->max_fds); - free(context->fds); - free(context); - return NULL; - } - memset(context->lws_lookup, 0, sizeof(struct libwebsocket *) * - context->max_fds); - - if (lws_plat_init_fd_tables(context)) { - free(context->lws_lookup); - free(context->fds); - free(context); - return NULL; - } - -#ifndef LWS_NO_EXTENSIONS - context->extensions = info->extensions; -#endif - context->last_timeout_check_s = 0; - context->user_space = info->user; - -#ifdef LWS_OPENSSL_SUPPORT - context->use_ssl = 0; - context->allow_non_ssl_on_ssl_port = 0; - context->ssl_ctx = NULL; - context->ssl_client_ctx = NULL; - openssl_websocket_private_data_index = 0; -#endif - - strcpy(context->canonical_hostname, "unknown"); - -#ifndef LWS_NO_SERVER - if (!(info->options & LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME)) { - /* find canonical hostname */ - gethostname((char *)context->canonical_hostname, - sizeof(context->canonical_hostname) - 1); - - lwsl_notice(" canonical_hostname = %s\n", - context->canonical_hostname); - } -#endif - - /* split the proxy ads:port if given */ - - if (info->http_proxy_address) { - strncpy(context->http_proxy_address, info->http_proxy_address, - sizeof(context->http_proxy_address) - 1); - context->http_proxy_address[ - sizeof(context->http_proxy_address) - 1] = '\0'; - context->http_proxy_port = info->http_proxy_port; - } else { -#ifdef HAVE_GETENV - p = getenv("http_proxy"); - if (p) { - strncpy(context->http_proxy_address, p, - sizeof(context->http_proxy_address) - 1); - context->http_proxy_address[ - sizeof(context->http_proxy_address) - 1] = '\0'; - - p = strchr(context->http_proxy_address, ':'); - if (p == NULL) { - lwsl_err("http_proxy needs to be ads:port\n"); - goto bail; - } - *p = '\0'; - context->http_proxy_port = atoi(p + 1); - } -#endif - } - - if (context->http_proxy_address[0]) { - lwsl_notice(" Proxy %s:%u\n", - context->http_proxy_address, - context->http_proxy_port); - } - -#ifndef LWS_NO_SERVER - if (info->port != CONTEXT_PORT_NO_LISTEN) { - -#ifdef LWS_OPENSSL_SUPPORT - context->use_ssl = info->ssl_cert_filepath != NULL && - info->ssl_private_key_filepath != NULL; -#ifdef USE_CYASSL - lwsl_notice(" Compiled with CYASSL support\n"); -#else - lwsl_notice(" Compiled with OpenSSL support\n"); -#endif - if (context->use_ssl) - lwsl_notice(" Using SSL mode\n"); - else - lwsl_notice(" Using non-SSL mode\n"); - -#else - if (info->ssl_cert_filepath != NULL && - info->ssl_private_key_filepath != NULL) { - lwsl_notice(" Not compiled for OpenSSl support!\n"); - goto bail; - } - lwsl_notice(" Compiled without SSL support\n"); -#endif - - lwsl_notice( - " per-conn mem: %u + %u headers + protocol rx buf\n", - sizeof(struct libwebsocket), - sizeof(struct allocated_headers)); - } -#endif - -#ifdef LWS_OPENSSL_SUPPORT - - /* basic openssl init */ - - SSL_library_init(); - - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - - openssl_websocket_private_data_index = - SSL_get_ex_new_index(0, "libwebsockets", NULL, NULL, NULL); - - /* - * Firefox insists on SSLv23 not SSLv3 - * Konq disables SSLv2 by default now, SSLv23 works - */ - - method = (SSL_METHOD *)SSLv23_server_method(); - if (!method) { - int error = ERR_get_error(); - lwsl_err("problem creating ssl method %lu: %s\n", - error, - ERR_error_string(error, - (char *)context->service_buffer)); - goto bail; - } - context->ssl_ctx = SSL_CTX_new(method); /* create context */ - if (!context->ssl_ctx) { - int error = ERR_get_error(); - lwsl_err("problem creating ssl context %lu: %s\n", - error, - ERR_error_string(error, - (char *)context->service_buffer)); - goto bail; - } - -#ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_COMPRESSION); -#endif - SSL_CTX_set_options(context->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - if (info->ssl_cipher_list) - SSL_CTX_set_cipher_list(context->ssl_ctx, - info->ssl_cipher_list); - -#ifndef LWS_NO_CLIENT - - /* client context */ - - if (info->port == CONTEXT_PORT_NO_LISTEN) { - method = (SSL_METHOD *)SSLv23_client_method(); - if (!method) { - int error = ERR_get_error(); - lwsl_err("problem creating ssl method %lu: %s\n", - error, - ERR_error_string(error, - (char *)context->service_buffer)); - goto bail; - } - /* create context */ - context->ssl_client_ctx = SSL_CTX_new(method); - if (!context->ssl_client_ctx) { - int error = ERR_get_error(); - lwsl_err("problem creating ssl context %lu: %s\n", - error, - ERR_error_string(error, - (char *)context->service_buffer)); - goto bail; - } - -#ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(context->ssl_client_ctx, - SSL_OP_NO_COMPRESSION); -#endif - SSL_CTX_set_options(context->ssl_client_ctx, - SSL_OP_CIPHER_SERVER_PREFERENCE); - if (info->ssl_cipher_list) - SSL_CTX_set_cipher_list(context->ssl_client_ctx, - info->ssl_cipher_list); - -#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS - /* loads OS default CA certs */ - SSL_CTX_set_default_verify_paths(context->ssl_client_ctx); -#endif - - /* openssl init for cert verification (for client sockets) */ - if (!info->ssl_ca_filepath) { - if (!SSL_CTX_load_verify_locations( - context->ssl_client_ctx, NULL, - LWS_OPENSSL_CLIENT_CERTS)) - lwsl_err( - "Unable to load SSL Client certs from %s " - "(set by --with-client-cert-dir= " - "in configure) -- client ssl isn't " - "going to work", LWS_OPENSSL_CLIENT_CERTS); - } else - if (!SSL_CTX_load_verify_locations( - context->ssl_client_ctx, info->ssl_ca_filepath, - NULL)) - lwsl_err( - "Unable to load SSL Client certs " - "file from %s -- client ssl isn't " - "going to work", info->ssl_ca_filepath); - - /* - * callback allowing user code to load extra verification certs - * helping the client to verify server identity - */ - - /* support for client-side certificate authentication */ - if (info->ssl_cert_filepath) { - n = SSL_CTX_use_certificate_chain_file( - context->ssl_client_ctx, - info->ssl_cert_filepath); - if (n != 1) { - lwsl_err("problem getting cert '%s' %lu: %s\n", - info->ssl_cert_filepath, - ERR_get_error(), - ERR_error_string(ERR_get_error(), - (char *)context->service_buffer)); - goto bail; - } - } - if (info->ssl_private_key_filepath) { - /* set the private key from KeyFile */ - if (SSL_CTX_use_PrivateKey_file(context->ssl_client_ctx, - info->ssl_private_key_filepath, - SSL_FILETYPE_PEM) != 1) { - lwsl_err("use_PrivateKey_file '%s' %lu: %s\n", - info->ssl_private_key_filepath, - ERR_get_error(), - ERR_error_string(ERR_get_error(), - (char *)context->service_buffer)); - goto bail; - } - - /* verify private key */ - if (!SSL_CTX_check_private_key( - context->ssl_client_ctx)) { - lwsl_err("Private SSL key doesn't match cert\n"); - goto bail; - } - } - - context->protocols[0].callback(context, NULL, - LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS, - context->ssl_client_ctx, NULL, 0); - } -#endif - - /* as a server, are we requiring clients to identify themselves? */ - - if (info->options & - LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT) { - - /* absolutely require the client cert */ - - SSL_CTX_set_verify(context->ssl_ctx, - SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - OpenSSL_verify_callback); - - /* - * give user code a chance to load certs into the server - * allowing it to verify incoming client certs - */ - - context->protocols[0].callback(context, NULL, - LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, - context->ssl_ctx, NULL, 0); - } - - if (info->options & LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT) { - /* Normally SSL listener rejects non-ssl, optionally allow */ - context->allow_non_ssl_on_ssl_port = 1; - } - - if (context->use_ssl) { - - /* openssl init for server sockets */ - - /* set the local certificate from CertFile */ - n = SSL_CTX_use_certificate_chain_file(context->ssl_ctx, - info->ssl_cert_filepath); - if (n != 1) { - int error = ERR_get_error(); - lwsl_err("problem getting cert '%s' %lu: %s\n", - info->ssl_cert_filepath, - error, - ERR_error_string(error, - (char *)context->service_buffer)); - goto bail; - } - /* set the private key from KeyFile */ - if (SSL_CTX_use_PrivateKey_file(context->ssl_ctx, - info->ssl_private_key_filepath, - SSL_FILETYPE_PEM) != 1) { - int error = ERR_get_error(); - lwsl_err("ssl problem getting key '%s' %lu: %s\n", - info->ssl_private_key_filepath, - error, - ERR_error_string(error, - (char *)context->service_buffer)); - goto bail; - } - /* verify private key */ - if (!SSL_CTX_check_private_key(context->ssl_ctx)) { - lwsl_err("Private SSL key doesn't match cert\n"); - goto bail; - } - - /* SSL is happy and has a cert it's content with */ - } -#endif - -#ifndef LWS_NO_SERVER - /* set up our external listening socket we serve on */ - - if (info->port != CONTEXT_PORT_NO_LISTEN) { - int sockfd; - struct sockaddr_in sin; - socklen_t len = sizeof(sin); - -#ifdef LWS_USE_IPV6 - if (LWS_IPV6_ENABLED(context)) - sockfd = socket(AF_INET6, SOCK_STREAM, 0); - else -#endif - sockfd = socket(AF_INET, SOCK_STREAM, 0); - - if (sockfd < 0) { - lwsl_err("ERROR opening socket\n"); - goto bail; - } - - /* - * allow us to restart even if old sockets in TIME_WAIT - */ - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, - (const void *)&opt, sizeof(opt)); - - lws_plat_set_socket_options(context, sockfd); - -#ifdef LWS_USE_IPV6 - if (LWS_IPV6_ENABLED(context)) { - v = (struct sockaddr *)&serv_addr6; - n = sizeof(struct sockaddr_in6); - bzero((char *) &serv_addr6, sizeof(serv_addr6)); - serv_addr6.sin6_addr = in6addr_any; - serv_addr6.sin6_family = AF_INET6; - serv_addr6.sin6_port = htons(info->port); - } else -#endif - { - v = (struct sockaddr *)&serv_addr4; - n = sizeof(serv_addr4); - bzero((char *) &serv_addr4, sizeof(serv_addr4)); - serv_addr4.sin_addr.s_addr = INADDR_ANY; - serv_addr4.sin_family = AF_INET; - serv_addr4.sin_port = htons(info->port); - - if (info->iface) { - if (interface_to_sa(context, info->iface, - (struct sockaddr_in *)v, n) < 0) { - lwsl_err("Unable to find interface %s\n", - info->iface); - compatible_close(sockfd); - goto bail; - } - } - } /* ipv4 */ - - n = bind(sockfd, v, n); - if (n < 0) { - lwsl_err("ERROR on binding to port %d (%d %d)\n", - info->port, n, LWS_ERRNO); - compatible_close(sockfd); - goto bail; - } - - if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) - lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO)); - else - info->port = ntohs(sin.sin_port); - - context->listen_port = info->port; - - wsi = (struct libwebsocket *)malloc( - sizeof(struct libwebsocket)); - if (wsi == NULL) { - lwsl_err("Out of mem\n"); - compatible_close(sockfd); - goto bail; - } - memset(wsi, 0, sizeof(struct libwebsocket)); - wsi->sock = sockfd; - wsi->mode = LWS_CONNMODE_SERVER_LISTENER; - - insert_wsi_socket_into_fds(context, wsi); - - context->listen_service_modulo = LWS_LISTEN_SERVICE_MODULO; - context->listen_service_count = 0; - context->listen_service_fd = sockfd; - - listen(sockfd, LWS_SOMAXCONN); - lwsl_notice(" Listening on port %d\n", info->port); - } -#endif - - /* - * drop any root privs for this process - * to listen on port < 1023 we would have needed root, but now we are - * listening, we don't want the power for anything else - */ - lws_plat_drop_app_privileges(info); - - /* initialize supported protocols */ - - for (context->count_protocols = 0; - info->protocols[context->count_protocols].callback; - context->count_protocols++) { - - lwsl_parser(" Protocol: %s\n", - info->protocols[context->count_protocols].name); - - info->protocols[context->count_protocols].owning_server = - context; - info->protocols[context->count_protocols].protocol_index = - context->count_protocols; - - /* - * inform all the protocols that they are doing their one-time - * initialization if they want to - */ - info->protocols[context->count_protocols].callback(context, - NULL, LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0); - } - - /* - * give all extensions a chance to create any per-context - * allocations they need - */ - - if (info->port != CONTEXT_PORT_NO_LISTEN) { - if (lws_ext_callback_for_each_extension_type(context, NULL, - LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT, - NULL, 0) < 0) - goto bail; - } else - if (lws_ext_callback_for_each_extension_type(context, NULL, - LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT, - NULL, 0) < 0) - goto bail; - - return context; - -bail: - libwebsocket_context_destroy(context); - return NULL; -} /** * libwebsocket_set_proxy() - Setups proxy to libwebsocket_context. diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index bd5e76e6..a5b44768 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -584,6 +584,8 @@ struct libwebsocket { #endif }; +LWS_EXTERN int log_level; + LWS_EXTERN void libwebsocket_close_and_free_session(struct libwebsocket_context *context, struct libwebsocket *wsi, enum lws_close_status);