diff --git a/lib/client-handshake.c b/lib/client-handshake.c index f4018b18..eed9b5ef 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -19,28 +19,28 @@ lws_client_connect_2(struct lws *wsi) /* proxy? */ - if (context->http_proxy_port) { + if (wsi->vhost->http_proxy_port) { plen = sprintf((char *)pt->serv_buf, "CONNECT %s:%u HTTP/1.0\x0d\x0a" "User-agent: libwebsockets\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS), wsi->u.hdr.c_port); - if (context->proxy_basic_auth_token[0]) + if (wsi->vhost->proxy_basic_auth_token[0]) plen += sprintf((char *)pt->serv_buf + plen, "Proxy-authorization: basic %s\x0d\x0a", - context->proxy_basic_auth_token); + wsi->vhost->proxy_basic_auth_token); plen += sprintf((char *)pt->serv_buf + plen, "\x0d\x0a"); - ads = context->http_proxy_address; + ads = wsi->vhost->http_proxy_address; #ifdef LWS_USE_IPV6 if (LWS_IPV6_ENABLED(context)) { memset(&server_addr6, 0, sizeof(struct sockaddr_in6)); - server_addr6.sin6_port = htons(context->http_proxy_port); + server_addr6.sin6_port = htons(wsi->vhost->http_proxy_port); } else #endif - server_addr4.sin_port = htons(context->http_proxy_port); + server_addr4.sin_port = htons(wsi->vhost->http_proxy_port); } else { ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); @@ -151,7 +151,7 @@ lws_client_connect_2(struct lws *wsi) goto oom4; } - if (lws_plat_set_socket_options(context, wsi->sock)) { + if (lws_plat_set_socket_options(wsi->vhost, wsi->sock)) { lwsl_err("Failed to set wsi socket options\n"); compatible_close(wsi->sock); goto oom4; @@ -176,7 +176,7 @@ lws_client_connect_2(struct lws *wsi) lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, AWAITING_TIMEOUT); - n = lws_socket_bind(context, wsi->sock, 0, context->iface); + n = lws_socket_bind(context, wsi->sock, 0, wsi->vhost->iface); if (n < 0) goto failed; } @@ -222,7 +222,7 @@ lws_client_connect_2(struct lws *wsi) /* we are connected to server, or proxy */ - if (context->http_proxy_port) { + if (wsi->vhost->http_proxy_port) { /* * OK from now on we talk via the proxy, so connect to that @@ -231,9 +231,9 @@ lws_client_connect_2(struct lws *wsi) * leaving old string/frag there but unreferenced) */ if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, - context->http_proxy_address)) + wsi->vhost->http_proxy_address)) goto failed; - wsi->u.hdr.c_port = context->http_proxy_port; + wsi->u.hdr.c_port = wsi->vhost->http_proxy_port; n = send(wsi->sock, (char *)pt->serv_buf, plen, MSG_NOSIGNAL); @@ -486,8 +486,11 @@ lws_client_connect_via_info(struct lws_client_connect_info *i) wsi->pending_timeout = NO_PENDING_TIMEOUT; wsi->position_in_fds_table = -1; wsi->u.hdr.c_port = i->port; + wsi->vhost = i->vhost; + if (!wsi->vhost) + wsi->vhost = i->context->vhost_list; - wsi->protocol = &i->context->protocols[0]; + wsi->protocol = &wsi->vhost->protocols[0]; if (wsi && !wsi->user_space && i->userdata) { wsi->user_space_externally_allocated = 1; wsi->user_space = i->userdata; diff --git a/lib/client.c b/lib/client.c index b8262f81..d55d7f51 100644 --- a/lib/client.c +++ b/lib/client.c @@ -154,7 +154,7 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi, _WSI_TOKEN_CLIENT_HOST); #endif - wsi->ssl = SSL_new(context->ssl_client_ctx); + wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx); #ifndef USE_WOLFSSL SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); @@ -541,12 +541,12 @@ int lws_client_interpret_server_handshake(struct lws *wsi) { int n, len, okay = 0, isErrorCodeReceived = 0, port = 0, ssl = 0; - struct lws_context *context = wsi->context; int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR; const char *pc, *prot, *ads = NULL, *path; struct allocated_headers *ah; char *p; #ifndef LWS_NO_EXTENSIONS + struct lws_context *context = wsi->context; struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; char *sb = (char *)&pt->serv_buf[0]; const struct lws_ext_options *opts; @@ -739,7 +739,7 @@ lws_client_interpret_server_handshake(struct lws *wsi) * no protocol name to work from, * default to first protocol */ - wsi->protocol = &context->protocols[0]; + wsi->protocol = &wsi->vhost->protocols[0]; goto check_extensions; } @@ -768,9 +768,9 @@ lws_client_interpret_server_handshake(struct lws *wsi) */ n = 0; wsi->protocol = NULL; - while (context->protocols[n].callback && !wsi->protocol) { - if (strcmp(p, context->protocols[n].name) == 0) { - wsi->protocol = &context->protocols[n]; + while (wsi->vhost->protocols[n].callback && !wsi->protocol) { + if (strcmp(p, wsi->vhost->protocols[n].name) == 0) { + wsi->protocol = &wsi->vhost->protocols[n]; break; } n++; @@ -838,7 +838,7 @@ check_extensions: lwsl_notice("checking client ext %s\n", ext_name); n = 0; - ext = lws_get_context(wsi)->extensions; + ext = wsi->vhost->extensions; while (ext && ext->callback) { if (strcmp(ext_name, ext->name)) { ext++; @@ -987,7 +987,7 @@ check_accept: * inform all extensions, not just active ones since they * already know */ - ext = context->extensions; + ext = wsi->vhost->extensions; while (ext && ext->callback) { v = NULL; @@ -1103,7 +1103,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt) /* tell the server what extensions we could support */ #ifndef LWS_NO_EXTENSIONS - ext = context->extensions; + ext = wsi->vhost->extensions; while (ext && ext->callback) { n = lws_ext_cb_all_exts(context, wsi, LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION, @@ -1113,7 +1113,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt) ext++; continue; } - n = context->protocols[0].callback(wsi, + n = wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED, wsi->user_space, (char *)ext->name, 0); @@ -1162,7 +1162,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt) /* give userland a chance to append, eg, cookies */ - context->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, NULL, &p, (pkt + LWS_MAX_SOCKET_IO_BUF) - p - 12); p += sprintf(p, "\x0d\x0a"); diff --git a/lib/context.c b/lib/context.c index 884e123b..b8d031b0 100644 --- a/lib/context.c +++ b/lib/context.c @@ -40,6 +40,160 @@ lws_get_library_version(void) return library_version; } +static const char * const mount_protocols[] = { + "http://", + "https://", + "file://", + "cgi://" +}; + +LWS_VISIBLE LWS_EXTERN int +lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res, + void *store, const char *mountpoint, const char *origin, + const char *def) +{ + struct lws_http_mount *m; + void *orig = store; + unsigned long l = (unsigned long)store; + int n; + + if (l & 15) + l += 16 - (l & 15); + + store = (void *)l; + m = (struct lws_http_mount *)store; + *res = m; + + m->def = def; + m->mountpoint = mountpoint; + m->mountpoint_len = (unsigned char)strlen(mountpoint); + m->mount_next = NULL; + if (next) + next->mount_next = m; + for (n = 0; n < ARRAY_SIZE(mount_protocols); n++) + if (!strncmp(origin, mount_protocols[n], + strlen(mount_protocols[n]))) { + m->origin_protocol = n; + m->origin = origin + strlen(mount_protocols[n]); + break; + } + + if (n == ARRAY_SIZE(mount_protocols)) { + lwsl_err("unsupported protocol://\n"); + return 0; /* ie, fail */ + } + + return ((char *)store + sizeof(*m)) - (char *)orig; +} + +LWS_VISIBLE struct lws_vhost * +lws_create_vhost(struct lws_context *context, + struct lws_context_creation_info *info, + struct lws_http_mount *mounts) +{ + struct lws_vhost *vh = lws_zalloc(sizeof(*vh)), + **vh1 = &context->vhost_list; + struct lws wsi; + char *p; + int n; + + if (!vh) + return NULL; + + vh->context = context; + if (!info->vhost_name) + vh->name = "default"; + else + vh->name = info->vhost_name; + + vh->iface = info->iface; + vh->protocols = info->protocols; + for (vh->count_protocols = 0; + info->protocols[vh->count_protocols].callback; + vh->count_protocols++) + ; + + vh->mount_list = mounts; + + lwsl_notice("Creating Vhost '%s' port %d, %d protocols\n", + vh->name, info->port, vh->count_protocols); + + while (mounts) { + lwsl_notice(" mounting %s%s to %s\n", + mount_protocols[mounts->origin_protocol], + mounts->origin, mounts->mountpoint); + mounts = mounts->mount_next; + } + +#ifndef LWS_NO_EXTENSIONS + vh->extensions = info->extensions; +#endif + + vh->listen_port = info->port; + vh->http_proxy_port = 0; + vh->http_proxy_address[0] = '\0'; + + /* either use proxy from info, or try get it from env var */ + + if (info->http_proxy_address) { + /* override for backwards compatibility */ + if (info->http_proxy_port) + vh->http_proxy_port = info->http_proxy_port; + lws_set_proxy(vh, info->http_proxy_address); + } else { +#ifdef LWS_HAVE_GETENV + p = getenv("http_proxy"); + if (p) + lws_set_proxy(vh, p); +#endif + } + + memset(&wsi, 0, sizeof(wsi)); + wsi.context = context; + wsi.vhost = vh; + + /* initialize supported protocols */ + + for (n = 0; n < vh->count_protocols; n++) + /* + * inform all the protocols that they are doing their one-time + * initialization if they want to. + * + * NOTE the wsi is all zeros except for the context & vh ptrs + * so lws_get_context(wsi) can work in the callback. + */ + info->protocols[n].callback(&wsi, + LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0); + + vh->ka_time = info->ka_time; + vh->ka_interval = info->ka_interval; + vh->ka_probes = info->ka_probes; + + if (lws_context_init_server_ssl(info, vh)) + goto bail; + + if (lws_context_init_client_ssl(info, vh)) + goto bail; + + if (lws_context_init_server(info, vh)) + goto bail; + + while (1) { + if (!(*vh1)) { + *vh1 = vh; + break; + } + vh1 = &(*vh1)->vhost_next; + }; + + return vh; + +bail: + lws_free(vh); + + return NULL; +} + /** * lws_create_context() - Create the websocket handler * @info: pointer to struct with parameters @@ -77,7 +231,6 @@ lws_create_context(struct lws_context_creation_info *info) #ifndef LWS_NO_DAEMONIZE int pid_daemon = get_daemonize_pid(); #endif - char *p; int n, m; lwsl_notice("Initial logging level %d\n", log_level); @@ -92,6 +245,7 @@ lws_create_context(struct lws_context_creation_info *info) lwsl_notice("IPV6 not compiled in\n"); #endif lws_feature_status_libev(info); + lws_feature_status_libuv(info); #endif lwsl_info(" LWS_DEF_HEADER_LEN : %u\n", LWS_DEF_HEADER_LEN); lwsl_info(" LWS_MAX_PROTOCOLS : %u\n", LWS_MAX_PROTOCOLS); @@ -125,16 +279,10 @@ lws_create_context(struct lws_context_creation_info *info) if (context->count_threads > LWS_MAX_SMP) context->count_threads = LWS_MAX_SMP; - context->protocols = info->protocols; context->token_limits = info->token_limits; - context->listen_port = info->port; - context->http_proxy_port = 0; - context->http_proxy_address[0] = '\0'; + context->options = info->options; - context->iface = info->iface; - context->ka_time = info->ka_time; - context->ka_interval = info->ka_interval; - context->ka_probes = info->ka_probes; + if (info->timeout_secs) context->timeout_secs = info->timeout_secs; else @@ -253,6 +401,18 @@ lws_create_context(struct lws_context_creation_info *info) if (lws_plat_init(context, info)) goto bail; + lws_context_init_ssl_library(info); + + /* + * if he's not saying he'll make his own vhosts later then act + * compatibly and make a default vhost using the data in the info + */ + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + if (!lws_create_vhost(context, info, NULL)) { + lwsl_err("Failed to create default vhost\n"); + return NULL; + } + lws_context_init_extensions(info, context); context->user_space = info->user; @@ -263,51 +423,16 @@ lws_create_context(struct lws_context_creation_info *info) strcpy(context->canonical_hostname, "unknown"); lws_server_get_canonical_hostname(context, info); - /* either use proxy from info, or try get it from env var */ - - if (info->http_proxy_address) { - /* override for backwards compatibility */ - if (info->http_proxy_port) - context->http_proxy_port = info->http_proxy_port; - lws_set_proxy(context, info->http_proxy_address); - } else { -#ifdef LWS_HAVE_GETENV - p = getenv("http_proxy"); - if (p) - lws_set_proxy(context, p); -#endif - } - - if (lws_context_init_server_ssl(info, context)) - goto bail; - - if (lws_context_init_client_ssl(info, context)) - goto bail; - - if (lws_context_init_server(info, context)) - goto bail; + context->uid = info->uid; + context->gid = info->gid; /* * 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++) - /* - * inform all the protocols that they are doing their one-time - * initialization if they want to. - * - * NOTE the wsi is all zeros except for the context pointer - * so lws_get_context(wsi) can work in the callback. - */ - info->protocols[context->count_protocols].callback(&wsi, - LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0); + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + lws_plat_drop_app_privileges(info); /* * give all extensions a chance to create any per-context @@ -342,6 +467,7 @@ lws_context_destroy(struct lws_context *context) { const struct lws_protocols *protocol = NULL; struct lws_context_per_thread *pt; + struct lws_vhost *vh; struct lws wsi; int n, m; @@ -390,13 +516,18 @@ lws_context_destroy(struct lws_context *context) * inform all the protocols that they are done and will have no more * callbacks */ - protocol = context->protocols; - if (protocol) - while (protocol->callback) { - protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY, - NULL, NULL, 0); - protocol++; - } + vh = context->vhost_list; + while (vh) { + protocol = vh->protocols; + if (protocol) + while (protocol->callback) { + protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY, + NULL, NULL, 0); + protocol++; + } + lws_ssl_SSL_CTX_destroy(vh); + vh = vh->vhost_next; + } for (n = 0; n < context->count_threads; n++) { pt = &context->pt[n]; @@ -412,6 +543,7 @@ lws_context_destroy(struct lws_context *context) } lws_plat_context_early_destroy(context); lws_ssl_context_destroy(context); + if (context->pt[0].fds) lws_free_set_NULL(context->pt[0].fds); diff --git a/lib/extension-permessage-deflate.c b/lib/extension-permessage-deflate.c index 6ae348a6..322f4688 100644 --- a/lib/extension-permessage-deflate.c +++ b/lib/extension-permessage-deflate.c @@ -275,7 +275,7 @@ lws_extension_callback_pm_deflate(struct lws_context *context, if (deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL], Z_DEFLATED, -priv->args[PMD_CLIENT_MAX_WINDOW_BITS + - !wsi->context->listen_port], + !wsi->vhost->listen_port], priv->args[PMD_MEM_LEVEL], Z_DEFAULT_STRATEGY) != Z_OK) { lwsl_ext("inflateInit2 failed\n"); diff --git a/lib/extension.c b/lib/extension.c index f093850a..df24dd55 100644 --- a/lib/extension.c +++ b/lib/extension.c @@ -6,7 +6,6 @@ LWS_VISIBLE void lws_context_init_extensions(struct lws_context_creation_info *info, struct lws_context *context) { - context->extensions = info->extensions; lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE); } @@ -185,7 +184,12 @@ int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi, int reason, void *arg, int len) { int n = 0, m, handled = 0; - const struct lws_extension *ext = context->extensions; + const struct lws_extension *ext; + + if (!wsi || !wsi->vhost) + return 0; + + ext = wsi->vhost->extensions; while (ext && ext->callback && !handled) { m = ext->callback(context, ext, wsi, reason, diff --git a/lib/http2.c b/lib/http2.c index a2aa3f92..224668bd 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -52,10 +52,10 @@ lws_http2_wsi_from_id(struct lws *wsi, unsigned int sid) } struct lws * -lws_create_server_child_wsi(struct lws_context *context, struct lws *parent_wsi, +lws_create_server_child_wsi(struct lws_vhost *vhost, struct lws *parent_wsi, unsigned int sid) { - struct lws *wsi = lws_create_new_server_wsi(context); + struct lws *wsi = lws_create_new_server_wsi(vhost); if (!wsi) return NULL; @@ -82,7 +82,7 @@ lws_create_server_child_wsi(struct lws_context *context, struct lws *parent_wsi, wsi->state = LWSS_HTTP2_ESTABLISHED; wsi->mode = parent_wsi->mode; - wsi->protocol = &context->protocols[0]; + wsi->protocol = &vhost->protocols[0]; lws_ensure_user_space(wsi); lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__, @@ -198,7 +198,6 @@ static const char * https_client_preface = int lws_http2_parser(struct lws *wsi, unsigned char c) { - struct lws_context *context = wsi->context; struct lws *swsi; int n; @@ -378,7 +377,8 @@ lws_http2_parser(struct lws *wsi, unsigned char c) if (!wsi->u.http2.stream_id) return 1; if (!wsi->u.http2.stream_wsi) - wsi->u.http2.stream_wsi = lws_create_server_child_wsi(context, wsi, wsi->u.http2.stream_id); + wsi->u.http2.stream_wsi = + lws_create_server_child_wsi(wsi->vhost, wsi, wsi->u.http2.stream_id); /* END_STREAM means after servicing this, close the stream */ wsi->u.http2.END_STREAM = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_STREAM); @@ -474,7 +474,8 @@ int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi) */ lwsl_info("%s: setting up sid 1\n", __func__); - swsi = wsi->u.http2.stream_wsi = lws_create_server_child_wsi(context, wsi, 1); + swsi = wsi->u.http2.stream_wsi = + lws_create_server_child_wsi(wsi->vhost, wsi, 1); /* pass on the initial headers to SID 1 */ swsi->u.http.ah = wsi->u.http.ah; wsi->u.http.ah = NULL; diff --git a/lib/libev.c b/lib/libev.c index 17430936..cb8c3c51 100644 --- a/lib/libev.c +++ b/lib/libev.c @@ -78,6 +78,7 @@ lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi) { struct ev_signal *w_sigint = &context->pt[tsi].w_sigint.ev_watcher; struct ev_io *w_accept = &context->pt[tsi].w_accept.ev_watcher; + struct lws_vhost *vh = context->vhost_list; const char * backend_name; int status = 0; int backend; @@ -90,10 +91,17 @@ lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi) context->pt[tsi].io_loop_ev = loop; /* - * Initialize the accept w_accept with the listening socket + * Initialize the accept w_accept with all the listening sockets * and register a callback for read operations */ - ev_io_init(w_accept, lws_accept_cb, context->pt[tsi].lserv_fd, EV_READ); + while (vh) { + if (vh->lserv_wsi) { + vh->lserv_wsi->w_read.context = context; + ev_io_init(w_accept, lws_accept_cb, vh->lserv_wsi->sock, + EV_READ); + } + vh = vh->vhost_next; + } ev_io_start(context->pt[tsi].io_loop_ev, w_accept); /* Register the signal watcher unless the user says not to */ diff --git a/lib/libuv.c b/lib/libuv.c index 92d44eea..e74e81db 100644 --- a/lib/libuv.c +++ b/lib/libuv.c @@ -102,7 +102,7 @@ LWS_VISIBLE int lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi) { struct lws_context_per_thread *pt = &context->pt[tsi]; - struct lws *wsi = wsi_from_fd(context, pt->lserv_fd); + struct lws_vhost *vh = context->vhost_list; int status = 0, n; if (!loop) { @@ -119,23 +119,28 @@ lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi) for (n = 0; n < ARRAY_SIZE(sigs); n++) { uv_signal_init(loop, &pt->signals[n]); pt->signals[n].data = pt->context; - uv_signal_start(&pt->signals[n], context->lws_uv_sigint_cb, sigs[n]); + uv_signal_start(&pt->signals[n], + context->lws_uv_sigint_cb, sigs[n]); } } /* - * Initialize the accept wsi read watcher with the listening socket + * Initialize the accept wsi read watcher with all the listening sockets * and register a callback for read operations * * We have to do it here because the uv loop(s) are not * initialized until after context creation. */ - if (wsi) { - wsi->w_read.context = context; - uv_poll_init(pt->io_loop_uv, &wsi->w_read.uv_watcher, - pt->lserv_fd); - uv_poll_start(&wsi->w_read.uv_watcher, UV_READABLE, - lws_io_cb); + while (vh) { + if (vh->lserv_wsi) { + vh->lserv_wsi->w_read.context = context; + uv_poll_init(pt->io_loop_uv, + &vh->lserv_wsi->w_read.uv_watcher, + vh->lserv_wsi->sock); + uv_poll_start(&vh->lserv_wsi->w_read.uv_watcher, + UV_READABLE, lws_io_cb); + } + vh = vh->vhost_next; } uv_timer_init(pt->io_loop_uv, &pt->uv_timeout_watcher); diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 5e08b998..b83b3143 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -194,8 +194,8 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason) wsi->u.http.fd != LWS_INVALID_FILE) { lws_plat_file_close(wsi, wsi->u.http.fd); wsi->u.http.fd = LWS_INVALID_FILE; - context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP, - wsi->user_space, NULL, 0); + wsi->vhost->protocols[0].callback(wsi, + LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0); } if (wsi->socket_is_permanently_unusable || reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY || @@ -234,10 +234,10 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason) goto just_kill_connection; if (wsi->mode == LWSCM_HTTP_SERVING) - context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP, + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0); if (wsi->mode == LWSCM_HTTP_CLIENT) - context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP, + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP, wsi->user_space, NULL, 0); /* @@ -468,12 +468,12 @@ just_kill_connection: wsi->user_space, NULL, 0); } else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) { lwsl_debug("calling back CLOSED_HTTP\n"); - context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP, + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0 ); } else if (wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY || wsi->mode == LWSCM_WSCL_WAITING_CONNECT) { lwsl_debug("Connection closed before server reply\n"); - context->protocols[0].callback(wsi, + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_CONNECTION_ERROR, wsi->user_space, NULL, 0); } else @@ -525,7 +525,7 @@ lws_close_free_wsi_final(struct lws *wsi) } /* outermost destroy notification for wsi (user_space still intact) */ - wsi->context->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, + wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, wsi->user_space, NULL, 0); lws_free_wsi(wsi); @@ -903,7 +903,7 @@ int user_callback_handle_rxflow(lws_callback_function callback_function, */ LWS_VISIBLE int -lws_set_proxy(struct lws_context *context, const char *proxy) +lws_set_proxy(struct lws_vhost *vhost, const char *proxy) { char *p; char authstring[96]; @@ -920,35 +920,35 @@ lws_set_proxy(struct lws_context *context, const char *proxy) strncpy(authstring, proxy, p - proxy); // null termination not needed on input if (lws_b64_encode_string(authstring, (p - proxy), - context->proxy_basic_auth_token, - sizeof context->proxy_basic_auth_token) < 0) + vhost->proxy_basic_auth_token, + sizeof vhost->proxy_basic_auth_token) < 0) goto auth_too_long; lwsl_notice(" Proxy auth in use\n"); proxy = p + 1; } else - context->proxy_basic_auth_token[0] = '\0'; + vhost->proxy_basic_auth_token[0] = '\0'; - strncpy(context->http_proxy_address, proxy, - sizeof(context->http_proxy_address) - 1); - context->http_proxy_address[ - sizeof(context->http_proxy_address) - 1] = '\0'; + strncpy(vhost->http_proxy_address, proxy, + sizeof(vhost->http_proxy_address) - 1); + vhost->http_proxy_address[ + sizeof(vhost->http_proxy_address) - 1] = '\0'; - p = strchr(context->http_proxy_address, ':'); - if (!p && !context->http_proxy_port) { + p = strchr(vhost->http_proxy_address, ':'); + if (!p && !vhost->http_proxy_port) { lwsl_err("http_proxy needs to be ads:port\n"); return -1; } else { if (p) { *p = '\0'; - context->http_proxy_port = atoi(p + 1); + vhost->http_proxy_port = atoi(p + 1); } } - lwsl_notice(" Proxy %s:%u\n", context->http_proxy_address, - context->http_proxy_port); + lwsl_notice(" Proxy %s:%u\n", vhost->http_proxy_address, + vhost->http_proxy_port); return 0; @@ -1469,8 +1469,8 @@ lws_socket_bind(struct lws_context *context, int sockfd, int port, n = bind(sockfd, v, n); if (n < 0) { - lwsl_err("ERROR on binding to port %d (%d %d)\n", - port, n, LWS_ERRNO); + lwsl_err("ERROR on binding fd %d to port %d (%d %d)\n", + sockfd, port, n, LWS_ERRNO); return -1; } @@ -1512,6 +1512,21 @@ lws_urlencode(const char *in, int inlen, char *out, int outlen) return out - start; } +LWS_VISIBLE LWS_EXTERN int +lws_finalize_startup(struct lws_context *context) +{ + struct lws_context_creation_info info; + + info.uid = context->uid; + info.gid = context->gid; + + if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + lws_plat_drop_app_privileges(&info); + + return 0; +} + + LWS_VISIBLE LWS_EXTERN int lws_is_cgi(struct lws *wsi) { #ifdef LWS_WITH_CGI @@ -1555,10 +1570,10 @@ lws_create_basic_wsi(struct lws_context *context, int tsi) /* * these can only be set once the protocol is known * we set an unestablished connection's protocol pointer - * to the start of the supported list, so it can look + * to the start of the defauly vhost supported list, so it can look * for matching ones during the handshake */ - new_wsi->protocol = context->protocols; + new_wsi->protocol = context->vhost_list->protocols; new_wsi->user_space = NULL; new_wsi->ietf_spec_revision = 0; new_wsi->sock = LWS_SOCK_INVALID; diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index c6359ed2..939843e2 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -307,6 +307,7 @@ enum lws_context_options { (1 << 3) | (1 << 12), LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT = (1 << 12), + LWS_SERVER_OPTION_EXPLICIT_VHOSTS = (1 << 13), /****** add new things just above ---^ ******/ }; @@ -1292,6 +1293,13 @@ extern int lws_extension_callback_pm_deflate( /** * struct lws_context_creation_info - parameters to create context with * + * This is also used to create vhosts.... if LWS_SERVER_OPTION_EXPLICIT_VHOSTS + * is not given, then for backwards compatibility one vhost is created at + * context-creation time using the info from this struct. + * + * If LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, then no vhosts are created + * at the same time as the context, they are expected to be created afterwards. + * * @port: Port to listen on... you can use CONTEXT_PORT_NO_LISTEN to * suppress listening on any port, that's what you want if you are * not running a websocket server at all but just using it as a @@ -1359,22 +1367,22 @@ extern int lws_extension_callback_pm_deflate( */ struct lws_context_creation_info { - int port; - const char *iface; - const struct lws_protocols *protocols; - const struct lws_extension *extensions; + int port; /* VH */ + const char *iface; /* VH */ + const struct lws_protocols *protocols; /* VH */ + const struct lws_extension *extensions; /* VH */ const struct lws_token_limits *token_limits; - const char *ssl_private_key_password; - const char *ssl_cert_filepath; - const char *ssl_private_key_filepath; - const char *ssl_ca_filepath; - const char *ssl_cipher_list; - const char *http_proxy_address; - unsigned int http_proxy_port; - int gid; - int uid; - unsigned int options; - void *user; + const char *ssl_private_key_password; /* VH */ + const char *ssl_cert_filepath; /* VH */ + const char *ssl_private_key_filepath; /* VH */ + const char *ssl_ca_filepath; /* VH */ + const char *ssl_cipher_list; /* VH */ + const char *http_proxy_address; /* VH */ + unsigned int http_proxy_port; /* VH */ + int gid; /* context */ + int uid; /* context */ + unsigned int options; /* context */ + void *user; /* context */ int ka_time; int ka_probes; int ka_interval; @@ -1387,10 +1395,11 @@ struct lws_context_creation_info { short max_http_header_data; short max_http_header_pool; - unsigned int count_threads; - unsigned int fd_limit_per_thread; - unsigned int timeout_secs; - const char *ecdh_curve; + unsigned int count_threads; /* context */ + unsigned int fd_limit_per_thread; /* context */ + unsigned int timeout_secs; /* VH */ + const char *ecdh_curve; /* VH */ + const char *vhost_name; /* VH */ /* Add new things just above here ---^ * This is part of the ABI, don't needlessly break compatibility @@ -1426,6 +1435,7 @@ struct lws_context_creation_info { * @uri_replace_from: if non-NULL, when this string is found in URIs in * text/html content-encoding, it's replaced with @uri_replace_to * @uri_replace_to: see above + * @vhost: vhost to bind to (used to determine related SSL_CTX) */ struct lws_client_connect_info { @@ -1444,6 +1454,7 @@ struct lws_client_connect_info { struct lws *parent_wsi; const char *uri_replace_from; const char *uri_replace_to; + struct lws_vhost *vhost; /* Add new things just above here ---^ * This is part of the ABI, don't needlessly break compatibility @@ -1456,6 +1467,13 @@ struct lws_client_connect_info { void *_unused[4]; }; +struct lws_http_mount; + +LWS_VISIBLE LWS_EXTERN int +lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res, + void *store, const char *mountpoint, const char *origin, + const char *def); + LWS_VISIBLE LWS_EXTERN void lws_set_log_level(int level, void (*log_emit_function)(int level, const char *line)); @@ -1466,8 +1484,18 @@ lwsl_emit_syslog(int level, const char *line); LWS_VISIBLE LWS_EXTERN struct lws_context * lws_create_context(struct lws_context_creation_info *info); +struct lws_vhost; + +LWS_VISIBLE struct lws_vhost * +lws_create_vhost(struct lws_context *context, + struct lws_context_creation_info *info, + struct lws_http_mount *mounts); + LWS_VISIBLE LWS_EXTERN int -lws_set_proxy(struct lws_context *context, const char *proxy); +lws_finalize_startup(struct lws_context *context); + +LWS_VISIBLE LWS_EXTERN int +lws_set_proxy(struct lws_vhost *vhost, const char *proxy); LWS_VISIBLE LWS_EXTERN void lws_context_destroy(struct lws_context *context); diff --git a/lib/lws-plat-unix.c b/lib/lws-plat-unix.c index 48a89703..a9e8c88e 100644 --- a/lib/lws-plat-unix.c +++ b/lib/lws-plat-unix.c @@ -127,7 +127,7 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) /* stay dead once we are dead */ - if (!context) + if (!context || !context->vhost_list) return 1; lws_libev_run(context, tsi); @@ -139,7 +139,7 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) memset(&_lws, 0, sizeof(_lws)); _lws.context = context; - context->service_tid_detected = context->protocols[0].callback( + context->service_tid_detected = context->vhost_list->protocols[0].callback( &_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); } context->service_tid = context->service_tid_detected; @@ -200,7 +200,7 @@ lws_plat_service(struct lws_context *context, int timeout_ms) } LWS_VISIBLE int -lws_plat_set_socket_options(struct lws_context *context, int fd) +lws_plat_set_socket_options(struct lws_vhost *vhost, int fd) { int optval = 1; socklen_t optlen = sizeof(optval); @@ -212,7 +212,7 @@ lws_plat_set_socket_options(struct lws_context *context, int fd) struct protoent *tcp_proto; #endif - if (context->ka_time) { + if (vhost->ka_time) { /* enable keepalive on this socket */ optval = 1; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, @@ -230,17 +230,17 @@ lws_plat_set_socket_options(struct lws_context *context, int fd) */ #else /* set the keepalive conditions we want on it too */ - optval = context->ka_time; + optval = vhost->ka_time; if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (const void *)&optval, optlen) < 0) return 1; - optval = context->ka_interval; + optval = vhost->ka_interval; if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, (const void *)&optval, optlen) < 0) return 1; - optval = context->ka_probes; + optval = vhost->ka_probes; if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, (const void *)&optval, optlen) < 0) return 1; @@ -283,7 +283,7 @@ lws_plat_drop_app_privileges(struct lws_context_creation_info *info) if (setuid(info->uid)) lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO)); else - lwsl_notice(" Set privs to user '%s'\n", p->pw_name); + lwsl_notice("Set privs to user '%s'\n", p->pw_name); } else lwsl_warn("getpwuid: unable to find uid %d", info->uid); } diff --git a/lib/lws-plat-win.c b/lib/lws-plat-win.c index 428ae7cd..751771c4 100644 --- a/lib/lws-plat-win.c +++ b/lib/lws-plat-win.c @@ -171,28 +171,30 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) memset(&_lws, 0, sizeof(_lws)); _lws.context = context; - context->service_tid_detected = context->protocols[0].callback( - &_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + context->service_tid_detected = context->vhost_list-> + protocols[0].callback(&_lws, LWS_CALLBACK_GET_THREAD_ID, + NULL, NULL, 0); } context->service_tid = context->service_tid_detected; for (i = 0; i < pt->fds_count; ++i) { pfd = &pt->fds[i]; - if (pfd->fd == pt->lserv_fd) + + if (!(pfd->events & LWS_POLLOUT)) continue; - if (pfd->events & LWS_POLLOUT) { - wsi = wsi_from_fd(context, pfd->fd); - if (!wsi || wsi->sock_send_blocking) - continue; - pfd->revents = LWS_POLLOUT; - n = lws_service_fd(context, pfd); - if (n < 0) - return -1; - /* if something closed, retry this slot */ - if (n) - i--; - } + wsi = wsi_from_fd(context, pfd->fd); + if (wsi->listener) + continue; + if (!wsi || wsi->sock_send_blocking) + continue; + pfd->revents = LWS_POLLOUT; + n = lws_service_fd(context, pfd); + if (n < 0) + return -1; + /* if something closed, retry this slot */ + if (n) + i--; } /* if we know something needs service already, don't wait in poll */ @@ -264,7 +266,7 @@ lws_plat_service(struct lws_context *context, int timeout_ms) } LWS_VISIBLE int -lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd) +lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd) { int optval = 1; int optlen = sizeof(optval); @@ -276,7 +278,7 @@ lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd) struct protoent *tcp_proto; #endif - if (context->ka_time) { + if (vhost->ka_time) { /* enable keepalive on this socket */ optval = 1; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, @@ -284,8 +286,8 @@ lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd) return 1; alive.onoff = TRUE; - alive.keepalivetime = context->ka_time; - alive.keepaliveinterval = context->ka_interval; + alive.keepalivetime = vhost->ka_time; + alive.keepaliveinterval = vhost->ka_interval; if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), NULL, 0, &dwBytesRet, NULL, NULL)) diff --git a/lib/pollfd.c b/lib/pollfd.c index 65a28e41..1d359bda 100644 --- a/lib/pollfd.c +++ b/lib/pollfd.c @@ -43,7 +43,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa) pa->prev_events = pfd->events; pa->events = pfd->events = (pfd->events & ~_and) | _or; - if (context->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD, + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD, wsi->user_space, (void *)pa, 0)) { ret = -1; goto bail; @@ -86,7 +86,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa) sampled_tid = context->service_tid; if (sampled_tid) { - tid = context->protocols[0].callback(wsi, + tid = wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); if (tid == -1) { ret = -1; @@ -114,7 +114,8 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi) __func__, wsi, wsi->tsi, wsi->sock, pt->fds_count); if ((unsigned int)pt->fds_count >= context->fd_limit_per_thread) { - lwsl_err("Too many fds (%d)\n", context->max_fds); + lwsl_err("Too many fds (%d vs %d)\n", context->max_fds, + context->fd_limit_per_thread ); return 1; } @@ -127,9 +128,10 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi) #endif assert(wsi); + assert(wsi->vhost); assert(lws_socket_is_valid(wsi->sock)); - if (context->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, wsi->user_space, (void *) &pa, 1)) return -1; @@ -144,7 +146,7 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi) lws_plat_insert_socket_into_fds(context, wsi); /* external POLL support via protocol 0 */ - if (context->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD, + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD, wsi->user_space, (void *) &pa, 0)) ret = -1; #ifndef LWS_NO_SERVER @@ -154,7 +156,7 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi) #endif lws_pt_unlock(pt); - if (context->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, wsi->user_space, (void *)&pa, 1)) ret = -1; @@ -180,7 +182,7 @@ remove_wsi_socket_from_fds(struct lws *wsi) } #endif - if (context->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, wsi->user_space, (void *)&pa, 1)) return -1; @@ -213,7 +215,7 @@ remove_wsi_socket_from_fds(struct lws *wsi) /* remove also from external POLL support via protocol 0 */ if (lws_socket_is_valid(wsi->sock)) - if (context->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD, + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD, wsi->user_space, (void *) &pa, 0)) ret = -1; #ifndef LWS_NO_SERVER @@ -224,7 +226,7 @@ remove_wsi_socket_from_fds(struct lws *wsi) #endif lws_pt_unlock(pt); - if (context->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, wsi->user_space, (void *) &pa, 1)) ret = -1; @@ -246,7 +248,7 @@ lws_change_pollfd(struct lws *wsi, int _and, int _or) if (!context) return 1; - if (context->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, wsi->user_space, (void *) &pa, 0)) return -1; @@ -255,7 +257,7 @@ lws_change_pollfd(struct lws *wsi, int _and, int _or) lws_pt_lock(pt); ret = _lws_change_pollfd(wsi, _and, _or, &pa); lws_pt_unlock(pt); - if (context->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, + if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, wsi->user_space, (void *) &pa, 0)) ret = -1; diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 58e8b707..125f8927 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -581,7 +581,6 @@ struct lws_context_per_thread { struct lws_signal_watcher w_sigint; unsigned char ev_loop_foreign:1; #endif - lws_sockfd_type lserv_fd; unsigned long count_conns; /* @@ -601,13 +600,71 @@ struct lws_context_per_thread { unsigned char tid; }; +struct lws_http_mount { + struct lws_http_mount *mount_next; + const char *mountpoint; /* mountpoint in http pathspace, eg, "/" */ + const char *origin; /* path to be mounted, eg, "/var/www/warmcat.com" */ + const char *def; /* default target, eg, "index.html" */ + + unsigned char origin_protocol; + unsigned char mountpoint_len; +}; + +/* + * virtual host -related context information + * vhostwide SSL context + * vhostwide proxy + * + * heirarchy: + * + * context -> vhost -> wsi + * + * incoming connection non-SSL vhost binding: + * + * listen socket -> wsi -> select vhost after first headers + * + * incoming connection SSL vhost binding: + * + * SSL SNI -> wsi -> bind after SSL negotiation + */ + +struct lws_vhost { + char http_proxy_address[128]; + char proxy_basic_auth_token[128]; + struct lws_context *context; + struct lws_vhost *vhost_next; + struct lws_http_mount *mount_list; + struct lws *lserv_wsi; + const char *name; + const char *iface; + const struct lws_protocols *protocols; +#ifdef LWS_OPENSSL_SUPPORT + SSL_CTX *ssl_ctx; + SSL_CTX *ssl_client_ctx; +#endif +#ifndef LWS_NO_EXTENSIONS + const struct lws_extension *extensions; +#endif + + int listen_port; + unsigned int http_proxy_port; + int count_protocols; + int ka_time; + int ka_probes; + int ka_interval; + +#ifdef LWS_OPENSSL_SUPPORT + int use_ssl; + int allow_non_ssl_on_ssl_port; + unsigned int user_supplied_ssl_ctx:1; +#endif +}; + /* * the rest is managed per-context, that includes * * - processwide single fd -> wsi lookup * - contextwide headers pool - * - contextwide ssl context - * - contextwide proxy */ struct lws_context { @@ -620,27 +677,16 @@ struct lws_context { #else struct lws **lws_lookup; /* fd to wsi */ #endif - const char *iface; + struct lws_vhost *vhost_list; const struct lws_token_limits *token_limits; void *user_space; - const struct lws_protocols *protocols; - -#ifdef LWS_OPENSSL_SUPPORT - SSL_CTX *ssl_ctx; - SSL_CTX *ssl_client_ctx; -#endif -#ifndef LWS_NO_EXTENSIONS - const struct lws_extension *extensions; -#endif #if defined(LWS_USE_LIBEV) lws_ev_signal_cb_t * lws_ev_sigint_cb; #endif #if defined(LWS_USE_LIBUV) uv_signal_cb lws_uv_sigint_cb; #endif - char http_proxy_address[128]; - char proxy_basic_auth_token[128]; char canonical_hostname[128]; #ifdef LWS_LATENCY unsigned long worst_latency; @@ -648,16 +694,25 @@ struct lws_context { #endif int max_fds; - int listen_port; #if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV) int use_ev_sigint; #endif int started_with_parent; + int uid, gid; int fd_random; - int lserv_mod; +#ifdef LWS_OPENSSL_SUPPORT +#define lws_ssl_anybody_has_buffered_read(w) \ + (w->vhost->use_ssl && \ + w->context->pt[(int)w->tsi].pending_read_list) +#define lws_ssl_anybody_has_buffered_read_tsi(c, t) \ + (/*c->use_ssl && */ \ + c->pt[(int)t].pending_read_list) +#else +#define lws_ssl_anybody_has_buffered_read(ctx) (0) +#define lws_ssl_anybody_has_buffered_read_tsi(ctx, t) (0) +#endif int count_wsi_allocated; - unsigned int http_proxy_port; unsigned int options; unsigned int fd_limit_per_thread; unsigned int timeout_secs; @@ -671,26 +726,6 @@ struct lws_context { volatile int service_tid; int service_tid_detected; - int count_protocols; - int ka_time; - int ka_probes; - int ka_interval; - -#ifdef LWS_OPENSSL_SUPPORT - int use_ssl; - int allow_non_ssl_on_ssl_port; - unsigned int user_supplied_ssl_ctx:1; -#define lws_ssl_anybody_has_buffered_read(w) \ - (w->context->use_ssl && \ - w->context->pt[(int)w->tsi].pending_read_list) -#define lws_ssl_anybody_has_buffered_read_tsi(c, t) \ - (c->use_ssl && \ - c->pt[(int)t].pending_read_list) -#else -#define lws_ssl_anybody_has_buffered_read(ctx) (0) -#define lws_ssl_anybody_has_buffered_read_tsi(ctx, t) (0) -#endif - short max_http_header_data; short max_http_header_pool; short count_threads; @@ -699,6 +734,9 @@ struct lws_context { unsigned int requested_kill:1; }; +#define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x] +#define lws_get_vh_protocol(vh, x) vh->protocols[x] + LWS_EXTERN void lws_close_free_wsi_final(struct lws *wsi); LWS_EXTERN void @@ -1101,6 +1139,7 @@ struct lws { /* pointers */ struct lws_context *context; + struct lws_vhost *vhost; struct lws *parent; /* points to parent, if any */ struct lws *child_list; /* points to first child */ struct lws *sibling_list; /* subsequent children at same level */ @@ -1146,6 +1185,7 @@ struct lws { #endif unsigned int hdr_parsing_completed:1; + unsigned int listener:1; unsigned int user_space_externally_allocated:1; unsigned int socket_is_permanently_unusable:1; unsigned int rxflow_change_to:2; @@ -1282,7 +1322,7 @@ lws_client_reset(struct lws *wsi, int ssl, const char *address, int port, const char *path, const char *host); LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT -lws_create_new_server_wsi(struct lws_context *context); +lws_create_new_server_wsi(struct lws_vhost *vhost); LWS_EXTERN char * LWS_WARN_UNUSED_RESULT lws_generate_client_handshake(struct lws *wsi, char *pkt); @@ -1377,7 +1417,7 @@ void lws_http2_configure_if_upgraded(struct lws *wsi); #endif LWS_EXTERN int -lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd); +lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd); LWS_EXTERN int LWS_WARN_UNUSED_RESULT lws_header_table_attach(struct lws *wsi, int autoservice); @@ -1402,7 +1442,9 @@ lws_change_pollfd(struct lws *wsi, int _and, int _or); #ifndef LWS_NO_SERVER int lws_context_init_server(struct lws_context_creation_info *info, - struct lws_context *context); + struct lws_vhost *vhost); +LWS_EXTERN struct lws_vhost * +lws_select_vhost(struct lws_context *context, int port, const char *servername); LWS_EXTERN int handshake_0405(struct lws_context *context, struct lws *wsi); LWS_EXTERN int LWS_WARN_UNUSED_RESULT @@ -1445,7 +1487,9 @@ enum lws_ssl_capable_status { #define lws_server_socket_service_ssl(_b, _c) (0) #define lws_ssl_close(_a) (0) #define lws_ssl_context_destroy(_a) +#define lws_ssl_SSL_CTX_destroy(_a) #define lws_ssl_remove_wsi_from_buffered_list(_a) +#define lws_context_init_ssl_library(_a) #else #define LWS_SSL_ENABLED(context) (context->use_ssl) LWS_EXTERN int openssl_websocket_private_data_index; @@ -1455,23 +1499,27 @@ LWS_EXTERN int LWS_WARN_UNUSED_RESULT lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len); LWS_EXTERN int LWS_WARN_UNUSED_RESULT lws_ssl_pending(struct lws *wsi); +LWS_EXTERN int +lws_context_init_ssl_library(struct lws_context_creation_info *info); LWS_EXTERN int LWS_WARN_UNUSED_RESULT lws_server_socket_service_ssl(struct lws *new_wsi, lws_sockfd_type accept_fd); LWS_EXTERN int lws_ssl_close(struct lws *wsi); LWS_EXTERN void +lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost); +LWS_EXTERN void lws_ssl_context_destroy(struct lws_context *context); LWS_VISIBLE void lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi); #ifndef LWS_NO_SERVER LWS_EXTERN int lws_context_init_server_ssl(struct lws_context_creation_info *info, - struct lws_context *context); + struct lws_vhost *vhost); #else #define lws_context_init_server_ssl(_a, _b) (0) #endif LWS_EXTERN void -lws_ssl_destroy(struct lws_context *context); +lws_ssl_destroy(struct lws_vhost *vhost); /* HTTP2-related */ @@ -1549,7 +1597,7 @@ lws_http_transaction_completed_client(struct lws *wsi); #ifdef LWS_OPENSSL_SUPPORT LWS_EXTERN int lws_context_init_client_ssl(struct lws_context_creation_info *info, - struct lws_context *context); + struct lws_vhost *vhost); #else #define lws_context_init_client_ssl(_a, _b) (0) #endif diff --git a/lib/server-handshake.c b/lib/server-handshake.c index 5f525852..bab9c359 100644 --- a/lib/server-handshake.c +++ b/lib/server-handshake.c @@ -84,7 +84,7 @@ lws_extension_server_handshake(struct lws *wsi, char **p) /* check a client's extension against our support */ - ext = lws_get_context(wsi)->extensions; + ext = wsi->vhost->extensions; while (ext && ext->callback) { @@ -106,7 +106,7 @@ lws_extension_server_handshake(struct lws *wsi, char **p) * ask user code if it's OK to apply it on this * particular connection + protocol */ - m = lws_get_context(wsi)->protocols[0].callback(wsi, + m = wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CONFIRM_EXTENSION_OKAY, wsi->user_space, ext_name, 0); diff --git a/lib/server.c b/lib/server.c index 48cbc314..4b7078a2 100644 --- a/lib/server.c +++ b/lib/server.c @@ -24,12 +24,13 @@ int lws_context_init_server(struct lws_context_creation_info *info, - struct lws_context *context) + struct lws_vhost *vhost) { #ifdef LWS_POSIX int n, opt = 1, limit = 1; #endif lws_sockfd_type sockfd; + struct lws_vhost *vh; struct lws *wsi; int m = 0; @@ -38,9 +39,25 @@ lws_context_init_server(struct lws_context_creation_info *info, if (info->port == CONTEXT_PORT_NO_LISTEN) return 0; + vh = vhost->context->vhost_list; + while (vh) { + if (vh->listen_port == info->port) { + if ((!info->iface && !vh->iface) || + (info->iface && vh->iface && + !strcmp(info->iface, vh->iface))) { + vhost->listen_port = info->port; + vhost->iface = info->iface; + lwsl_notice(" using listen skt from vhost %s\n", + vh->name); + return 0; + } + } + vh = vh->vhost_next; + } + #if LWS_POSIX #if defined(__linux__) - limit = context->count_threads; + limit = vhost->context->count_threads; #endif for (m = 0; m < limit; m++) { @@ -70,7 +87,7 @@ lws_context_init_server(struct lws_context_creation_info *info, return 1; } #if defined(__linux__) && defined(SO_REUSEPORT) && LWS_MAX_SMP > 1 - if (context->count_threads > 1) + if (vhost->context->count_threads > 1) if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const void *)&opt, sizeof(opt)) < 0) { compatible_close(sockfd); @@ -78,33 +95,36 @@ lws_context_init_server(struct lws_context_creation_info *info, } #endif #endif - lws_plat_set_socket_options(context, sockfd); + lws_plat_set_socket_options(vhost, sockfd); #if LWS_POSIX - n = lws_socket_bind(context, sockfd, info->port, info->iface); + n = lws_socket_bind(vhost->context, sockfd, info->port, info->iface); if (n < 0) goto bail; info->port = n; #endif - context->listen_port = info->port; + vhost->listen_port = info->port; + vhost->iface = info->iface; wsi = lws_zalloc(sizeof(struct lws)); if (wsi == NULL) { lwsl_err("Out of mem\n"); goto bail; } - wsi->context = context; + wsi->context = vhost->context; wsi->sock = sockfd; wsi->mode = LWSCM_SERVER_LISTENER; - wsi->protocol = context->protocols; + wsi->protocol = vhost->protocols; wsi->tsi = m; + wsi->vhost = vhost; + wsi->listener = 1; - context->pt[m].wsi_listening = wsi; - if (insert_wsi_socket_into_fds(context, wsi)) + vhost->context->pt[m].wsi_listening = wsi; + if (insert_wsi_socket_into_fds(vhost->context, wsi)) goto bail; - context->count_wsi_allocated++; - context->pt[m].lserv_fd = sockfd; + vhost->context->count_wsi_allocated++; + vhost->lserv_wsi = wsi; #if LWS_POSIX listen(wsi->sock, LWS_SOMAXCONN); @@ -112,7 +132,8 @@ lws_context_init_server(struct lws_context_creation_info *info, #else mbed3_tcp_stream_bind(wsi->sock, info->port, wsi); #endif - lwsl_notice(" Listening on port %d\n", info->port); + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) + lwsl_notice(" Listening on port %d\n", info->port); return 0; @@ -143,6 +164,69 @@ _lws_server_listen_accept_flow_control(struct lws *twsi, int on) return n; } +struct lws_vhost * +lws_select_vhost(struct lws_context *context, int port, const char *servername) +{ + struct lws_vhost *vhost = context->vhost_list; + + while (vhost) { + if (port == vhost->listen_port && + !strcmp(vhost->name, servername)) { + lwsl_info("SNI: Found: %s\n", servername); + return vhost; + } + vhost = vhost->vhost_next; + } + + return NULL; +} + +static const char * get_mimetype(const char *file) +{ + int n = strlen(file); + + if (n < 5) + return NULL; + + if (!strcmp(&file[n - 4], ".ico")) + return "image/x-icon"; + + if (!strcmp(&file[n - 4], ".png")) + return "image/png"; + + if (!strcmp(&file[n - 5], ".html")) + return "text/html"; + + if (!strcmp(&file[n - 4], ".css")) + return "text/css"; + + return NULL; +} + +int lws_http_serve(struct lws *wsi, char *uri, const char *origin) +{ + const char *mimetype; + char path[256]; + int n; + + lwsl_notice("%s: %s %s\n", __func__, uri, origin); + snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri); + + mimetype = get_mimetype(path); + if (!mimetype) { + lwsl_err("unknown mimetype for %s", path); + lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL); + return -1; + } + + n = lws_serve_http_file(wsi, path, mimetype, NULL, 0); + if (n < 0) + if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi))) + return -1; /* error or can't reuse connection: close the socket */ + + return 0; +} + int lws_http_action(struct lws *wsi) { @@ -152,6 +236,7 @@ lws_http_action(struct lws *wsi) enum http_connection_type connection_type; enum http_version request_version; char content_length_str[32]; + struct lws_http_mount *hm; unsigned int n, count = 0; char http_version_str[10]; char http_conn_str[20]; @@ -280,7 +365,7 @@ lws_http_action(struct lws *wsi) goto bail_nuke_ah; if (lws_add_http_header_status(wsi, 301, &p, end)) goto bail_nuke_ah; - n = sprintf((char *)end, "https://%s/", + n = sprintf((char *)end, "htt struct lws_http_mount *hm;ps://%s/", lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION, end, n, &p, end)) @@ -295,7 +380,28 @@ lws_http_action(struct lws *wsi) } #endif - n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, + /* can we serve it from the mount list? */ + + hm = wsi->vhost->mount_list; + while (hm) { + char *s = uri_ptr + hm->mountpoint_len; + + if (s[0] == '\0') + s = (char *)hm->def; + + if (!s) + s = "index.html"; + + if (uri_len >= hm->mountpoint_len && + !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len)) { + n = lws_http_serve(wsi, s, hm->origin); + break; + } + hm = hm->mount_next; + } + + if (!hm) + n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP, wsi->user_space, uri_ptr, uri_len); if (n) { lwsl_info("LWS_CALLBACK_HTTP closing\n"); @@ -387,13 +493,25 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len) lwsl_info("No upgrade\n"); ah = wsi->u.hdr.ah; + /* select vhost */ + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { + struct lws_vhost *vhost = lws_select_vhost( + context, wsi->vhost->listen_port, + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); + + if (vhost) + wsi->vhost = vhost; + } + lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED); wsi->state = LWSS_HTTP; wsi->u.http.fd = LWS_INVALID_FILE; /* expose it at the same offset as u.hdr */ wsi->u.http.ah = ah; - lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi, (void *)wsi->u.hdr.ah); + lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi, + (void *)wsi->u.hdr.ah); n = lws_http_action(wsi); @@ -485,12 +603,12 @@ upgrade_ws: lwsl_info("checking %s\n", protocol_name); n = 0; - while (context->protocols[n].callback) { - if (context->protocols[n].name && - !strcmp(context->protocols[n].name, + while (wsi->vhost->protocols[n].callback) { + if (wsi->vhost->protocols[n].name && + !strcmp(wsi->vhost->protocols[n].name, protocol_name)) { lwsl_info("prot match %d\n", n); - wsi->protocol = &context->protocols[n]; + wsi->protocol = &wsi->vhost->protocols[n]; hit = 1; break; } @@ -513,7 +631,7 @@ upgrade_ws: * allow it and match to protocol 0 */ lwsl_info("defaulting to prot 0 handler\n"); - wsi->protocol = &context->protocols[0]; + wsi->protocol = &wsi->vhost->protocols[0]; } /* allocate wsi->user storage */ @@ -643,10 +761,10 @@ lws_get_idlest_tsi(struct lws_context *context) } struct lws * -lws_create_new_server_wsi(struct lws_context *context) +lws_create_new_server_wsi(struct lws_vhost *vhost) { struct lws *new_wsi; - int n = lws_get_idlest_tsi(context); + int n = lws_get_idlest_tsi(vhost->context); if (n < 0) { lwsl_err("no space for new conn\n"); @@ -662,7 +780,8 @@ lws_create_new_server_wsi(struct lws_context *context) new_wsi->tsi = n; lwsl_info("Accepted %p to tsi %d\n", new_wsi, new_wsi->tsi); - new_wsi->context = context; + new_wsi->vhost = vhost; + new_wsi->context = vhost->context; new_wsi->pending_timeout = NO_PENDING_TIMEOUT; new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; @@ -673,7 +792,7 @@ lws_create_new_server_wsi(struct lws_context *context) new_wsi->hdr_parsing_completed = 0; #ifdef LWS_OPENSSL_SUPPORT - new_wsi->use_ssl = LWS_SSL_ENABLED(context); + new_wsi->use_ssl = LWS_SSL_ENABLED(vhost); #endif /* @@ -682,17 +801,17 @@ lws_create_new_server_wsi(struct lws_context *context) * to the start of the supported list, so it can look * for matching ones during the handshake */ - new_wsi->protocol = context->protocols; + new_wsi->protocol = vhost->protocols; new_wsi->user_space = NULL; new_wsi->ietf_spec_revision = 0; new_wsi->sock = LWS_SOCK_INVALID; - context->count_wsi_allocated++; + vhost->context->count_wsi_allocated++; /* * outermost create notification for wsi * no user_space because no protocol selection */ - context->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE, + vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE, NULL, NULL, 0); return new_wsi; @@ -772,7 +891,7 @@ lws_http_transaction_completed(struct lws *wsi) LWS_VISIBLE struct lws * lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) { - struct lws *new_wsi = lws_create_new_server_wsi(context); + struct lws *new_wsi = lws_create_new_server_wsi(context->vhost_list); if (!new_wsi) { compatible_close(accept_fd); @@ -796,7 +915,7 @@ lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) * set properties of the newly created wsi. There's no protocol * selected yet so we issue this to protocols[0] */ - if ((context->protocols[0].callback)(new_wsi, + if ((context->vhost_list->protocols[0].callback)(new_wsi, LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0)) { compatible_close(new_wsi->sock); lws_free(new_wsi); @@ -806,7 +925,7 @@ lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) lws_libev_accept(new_wsi, new_wsi->sock); lws_libuv_accept(new_wsi, new_wsi->sock); - if (!LWS_SSL_ENABLED(context)) { + if (!LWS_SSL_ENABLED(new_wsi->vhost)) { if (insert_wsi_socket_into_fds(context, new_wsi)) goto fail; } else { @@ -1113,7 +1232,7 @@ try_pollout: break; } - lws_plat_set_socket_options(context, accept_fd); + lws_plat_set_socket_options(wsi->vhost, accept_fd); lwsl_debug("accepted new conn port %u on fd=%d\n", ntohs(cli_addr.sin_port), accept_fd); @@ -1127,7 +1246,7 @@ try_pollout: * to reject based on client IP. There's no protocol selected * yet so we issue this to protocols[0] */ - if ((context->protocols[0].callback)(wsi, + if ((wsi->vhost->protocols[0].callback)(wsi, LWS_CALLBACK_FILTER_NETWORK_CONNECTION, NULL, (void *)(long)accept_fd, 0)) { lwsl_debug("Callback denied network connection\n"); diff --git a/lib/ssl.c b/lib/ssl.c index 7c7ad6e7..f00524c3 100644 --- a/lib/ssl.c +++ b/lib/ssl.c @@ -28,7 +28,8 @@ #include #endif -int openssl_websocket_private_data_index; +int openssl_websocket_private_data_index, + openssl_SSL_CTX_private_data_index; static int lws_context_init_ssl_pem_passwd_cb(char * buf, int size, int rwflag, void *userdata) @@ -55,13 +56,47 @@ static void lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creatio SSL_CTX_set_default_passwd_cb(ssl_ctx, lws_context_init_ssl_pem_passwd_cb); } +int +lws_context_init_ssl_library(struct lws_context_creation_info *info) +{ +#ifdef USE_WOLFSSL +#ifdef USE_OLD_CYASSL + lwsl_notice(" Compiled with CyaSSL support\n"); +#else + lwsl_notice(" Compiled with wolfSSL support\n"); +#endif +#else + lwsl_notice(" Compiled with OpenSSL support\n"); +#endif + + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { + lwsl_notice(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); + return 0; + } + + /* 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, "lws", NULL, NULL, NULL); + + openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0, + NULL, NULL, NULL, NULL); + + return 0; +} + #ifndef LWS_NO_SERVER static int OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { SSL *ssl; int n; - struct lws_context *context; + struct lws_vhost *vh; struct lws wsi; ssl = X509_STORE_CTX_get_ex_data(x509_ctx, @@ -71,16 +106,17 @@ OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) * !!! nasty openssl requires the index to come as a library-scope * static */ - context = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); + vh = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); /* * give him a fake wsi with context set, so he can use lws_get_context() * in the callback */ memset(&wsi, 0, sizeof(wsi)); - wsi.context = context; + wsi.vhost = vh; + wsi.context = vh->context; - n = context->protocols[0].callback(&wsi, + n = vh->protocols[0].callback(&wsi, LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION, x509_ctx, ssl, preverify_ok); @@ -89,7 +125,7 @@ OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) } static int -lws_context_ssl_init_ecdh(struct lws_context *context) +lws_context_ssl_init_ecdh(struct lws_vhost *vhost) { #ifdef LWS_SSL_SERVER_WITH_ECDH_CERT EC_KEY *EC_key = NULL; @@ -97,13 +133,13 @@ lws_context_ssl_init_ecdh(struct lws_context *context) int KeyType; X509 *x; - if (!lws_check_opt(context->options, LWS_SERVER_OPTION_SSL_ECDH)) + if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH)) return 0; lwsl_notice(" Using ECDH certificate support\n"); /* Get X509 certificate from ssl context */ - x = sk_X509_value(context->ssl_ctx->extra_certs, 0); + x = sk_X509_value(vhost->ssl_ctx->extra_certs, 0); if (!x) { lwsl_err("%s: x is NULL\n", __func__); return 1; @@ -129,7 +165,7 @@ lws_context_ssl_init_ecdh(struct lws_context *context) lwsl_err("%s: ECDH key is NULL \n", __func__); return 1; } - SSL_CTX_set_tmp_ecdh(context->ssl_ctx, EC_key); + SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, EC_key); EC_KEY_free(EC_key); #endif return 0; @@ -137,7 +173,7 @@ lws_context_ssl_init_ecdh(struct lws_context *context) static int lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info, - struct lws_context *context) + struct lws_vhost *vhost) { #ifdef LWS_HAVE_OPENSSL_ECDH_H EC_KEY *ecdh; @@ -158,10 +194,10 @@ lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info, lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve); return 1; } - SSL_CTX_set_tmp_ecdh(context->ssl_ctx, ecdh); + SSL_CTX_set_tmp_ecdh(vhost->ssl_ctx, ecdh); EC_KEY_free(ecdh); - SSL_CTX_set_options(context->ssl_ctx, SSL_OP_SINGLE_ECDH_USE); + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_ECDH_USE); lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve); #else @@ -175,18 +211,43 @@ static int lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) { struct lws_context *context; + struct lws_vhost *vhost, *vh; const char *servername; + int port; if (!ssl) return SSL_TLSEXT_ERR_NOACK; context = (struct lws_context *)SSL_CTX_get_ex_data( - SSL_get_SSL_CTX(ssl), 0); + SSL_get_SSL_CTX(ssl), + openssl_SSL_CTX_private_data_index); + + /* + * We can only get ssl accepted connections by using a vhost's ssl_ctx + * find out which listening one took us and only match vhosts on the + * same port. + */ + vh = context->vhost_list; + while (vh) { + if (vh->ssl_ctx == SSL_get_SSL_CTX(ssl)) + break; + vh = vh->vhost_next; + } + + assert(vh); /* we cannot get an ssl without using a vhost ssl_ctx */ + port = vh->listen_port; servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - lwsl_err("ServerName: %s, context = %p\n", servername, context); - //SSL_set_SSL_CTX(ssl, sslctx); + if (servername) { + vhost = lws_select_vhost(context, port, servername); + if (vhost) { + lwsl_info("SNI: Found: %s\n", servername); + SSL_set_SSL_CTX(ssl, vhost->ssl_ctx); + return SSL_TLSEXT_ERR_OK; + } + lwsl_err("SNI: Unknown ServerName: %s\n", servername); + } return SSL_TLSEXT_ERR_OK; } @@ -194,57 +255,39 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) LWS_VISIBLE int lws_context_init_server_ssl(struct lws_context_creation_info *info, - struct lws_context *context) + struct lws_vhost *vhost) { SSL_METHOD *method; + struct lws_context *context = vhost->context; struct lws wsi; int error; int n; -#ifdef USE_WOLFSSL -#ifdef USE_OLD_CYASSL - lwsl_notice(" Compiled with CyaSSL support\n"); -#else - lwsl_notice(" Compiled with wolfSSL support\n"); -#endif -#else - lwsl_notice(" Compiled with OpenSSL support\n"); -#endif - if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { - lwsl_notice(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); + vhost->use_ssl = 0; return 0; } if (info->port != CONTEXT_PORT_NO_LISTEN) { - context->use_ssl = info->ssl_cert_filepath != NULL; + vhost->use_ssl = info->ssl_cert_filepath != NULL; - if (info->ssl_cipher_list) + if (vhost->use_ssl && info->ssl_cipher_list) lwsl_notice(" SSL ciphers: '%s'\n", info->ssl_cipher_list); - if (context->use_ssl) + if (vhost->use_ssl) lwsl_notice(" Using SSL mode\n"); else lwsl_notice(" Using non-SSL mode\n"); } /* - * give him a fake wsi with context set, so he can use + * give him a fake wsi with context + vhost set, so he can use * lws_get_context() in the callback */ memset(&wsi, 0, sizeof(wsi)); - wsi.context = context; - - /* 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); + wsi.vhost = vhost; + wsi.context = vhost->context; /* * Firefox insists on SSLv23 not SSLv3 @@ -263,8 +306,8 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info, (char *)context->pt[0].serv_buf)); return 1; } - context->ssl_ctx = SSL_CTX_new(method); /* create context */ - if (!context->ssl_ctx) { + vhost->ssl_ctx = SSL_CTX_new(method); /* create context */ + if (!vhost->ssl_ctx) { error = ERR_get_error(); lwsl_err("problem creating ssl context %lu: %s\n", error, ERR_error_string(error, @@ -273,22 +316,19 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info, } /* associate the lws context with the SSL_CTX */ - n = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); - if (n) { - lwsl_err("cannot register arg0 on SSL_CTX %d\n", n); - return 1; - } - SSL_CTX_set_ex_data(context->ssl_ctx, 0, context); + + SSL_CTX_set_ex_data(vhost->ssl_ctx, + openssl_SSL_CTX_private_data_index, vhost->context); /* Disable SSLv2 and SSLv3 */ - SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); #ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_COMPRESSION); + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_COMPRESSION); #endif - SSL_CTX_set_options(context->ssl_ctx, SSL_OP_SINGLE_DH_USE); - SSL_CTX_set_options(context->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); if (info->ssl_cipher_list) - SSL_CTX_set_cipher_list(context->ssl_ctx, + SSL_CTX_set_cipher_list(vhost->ssl_ctx, info->ssl_cipher_list); /* as a server, are we requiring clients to identify themselves? */ @@ -299,17 +339,17 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info, if (!lws_check_opt(info->options, LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED)) verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; - SSL_CTX_set_session_id_context(context->ssl_ctx, + SSL_CTX_set_session_id_context(vhost->ssl_ctx, (unsigned char *)context, sizeof(void *)); /* absolutely require the client cert */ - SSL_CTX_set_verify(context->ssl_ctx, + SSL_CTX_set_verify(vhost->ssl_ctx, verify_options, OpenSSL_verify_callback); } #ifndef OPENSSL_NO_TLSEXT - SSL_CTX_set_tlsext_servername_callback(context->ssl_ctx, + SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx, lws_ssl_server_name_cb); #endif @@ -319,27 +359,29 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info, */ if (info->ssl_ca_filepath && - !SSL_CTX_load_verify_locations(context->ssl_ctx, + !SSL_CTX_load_verify_locations(vhost->ssl_ctx, info->ssl_ca_filepath, NULL)) { - lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n"); + lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", __func__); } - if (lws_context_ssl_init_ecdh_curve(info, context)) - return -1; + if (vhost->use_ssl) { + if (lws_context_ssl_init_ecdh_curve(info, vhost)) + return -1; - context->protocols[0].callback(&wsi, + vhost->protocols[0].callback(&wsi, LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, - context->ssl_ctx, NULL, 0); + vhost->ssl_ctx, NULL, 0); + } if (lws_check_opt(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; + vhost->allow_non_ssl_on_ssl_port = 1; - if (context->use_ssl) { + if (vhost->use_ssl) { /* openssl init for server sockets */ /* set the local certificate from CertFile */ - n = SSL_CTX_use_certificate_chain_file(context->ssl_ctx, + n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx, info->ssl_cert_filepath); if (n != 1) { error = ERR_get_error(); @@ -350,11 +392,11 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info, (char *)context->pt[0].serv_buf)); return 1; } - lws_ssl_bind_passphrase(context->ssl_ctx, info); + lws_ssl_bind_passphrase(vhost->ssl_ctx, info); if (info->ssl_private_key_filepath != NULL) { /* set the private key from KeyFile */ - if (SSL_CTX_use_PrivateKey_file(context->ssl_ctx, + if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx, info->ssl_private_key_filepath, SSL_FILETYPE_PEM) != 1) { error = ERR_get_error(); @@ -365,21 +407,21 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info, return 1; } } else - if (context->protocols[0].callback(&wsi, + if (vhost->protocols[0].callback(&wsi, LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, - context->ssl_ctx, NULL, 0)) { + vhost->ssl_ctx, NULL, 0)) { lwsl_err("ssl private key not set\n"); return 1; } /* verify private key */ - if (!SSL_CTX_check_private_key(context->ssl_ctx)) { + if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) { lwsl_err("Private SSL key doesn't match cert\n"); return 1; } - if (lws_context_ssl_init_ecdh(context)) + if (lws_context_ssl_init_ecdh(vhost)) return 1; /* @@ -395,15 +437,15 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info, #endif LWS_VISIBLE void -lws_ssl_destroy(struct lws_context *context) +lws_ssl_destroy(struct lws_vhost *vhost) { - if (!lws_check_opt(context->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) + if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) return; - if (context->ssl_ctx) - SSL_CTX_free(context->ssl_ctx); - if (!context->user_supplied_ssl_ctx && context->ssl_client_ctx) - SSL_CTX_free(context->ssl_client_ctx); + if (vhost->ssl_ctx) + SSL_CTX_free(vhost->ssl_ctx); + if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx) + SSL_CTX_free(vhost->ssl_client_ctx); #if (OPENSSL_VERSION_NUMBER < 0x01000000) || defined(USE_WOLFSSL) ERR_remove_state(0); @@ -430,7 +472,7 @@ lws_decode_ssl_error(void) #ifndef LWS_NO_CLIENT int lws_context_init_client_ssl(struct lws_context_creation_info *info, - struct lws_context *context) + struct lws_vhost *vhost) { int error; int n; @@ -442,9 +484,9 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info, if (info->provided_client_ssl_ctx) { /* use the provided OpenSSL context if given one */ - context->ssl_client_ctx = info->provided_client_ssl_ctx; + vhost->ssl_client_ctx = info->provided_client_ssl_ctx; /* nothing for lib to delete */ - context->user_supplied_ssl_ctx = 1; + vhost->user_supplied_ssl_ctx = 1; return 0; } @@ -463,39 +505,39 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info, error = ERR_get_error(); lwsl_err("problem creating ssl method %lu: %s\n", error, ERR_error_string(error, - (char *)context->pt[0].serv_buf)); + (char *)vhost->context->pt[0].serv_buf)); return 1; } /* create context */ - context->ssl_client_ctx = SSL_CTX_new(method); - if (!context->ssl_client_ctx) { + vhost->ssl_client_ctx = SSL_CTX_new(method); + if (!vhost->ssl_client_ctx) { error = ERR_get_error(); lwsl_err("problem creating ssl context %lu: %s\n", error, ERR_error_string(error, - (char *)context->pt[0].serv_buf)); + (char *)vhost->context->pt[0].serv_buf)); return 1; } #ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(context->ssl_client_ctx, + SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_NO_COMPRESSION); #endif - SSL_CTX_set_options(context->ssl_client_ctx, + SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); if (info->ssl_cipher_list) - SSL_CTX_set_cipher_list(context->ssl_client_ctx, + SSL_CTX_set_cipher_list(vhost->ssl_client_ctx, info->ssl_cipher_list); #ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS)) /* loads OS default CA certs */ - SSL_CTX_set_default_verify_paths(context->ssl_client_ctx); + SSL_CTX_set_default_verify_paths(vhost->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, + vhost->ssl_client_ctx, NULL, LWS_OPENSSL_CLIENT_CERTS)) lwsl_err( "Unable to load SSL Client certs from %s " @@ -504,7 +546,7 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info, "going to work", LWS_OPENSSL_CLIENT_CERTS); } else if (!SSL_CTX_load_verify_locations( - context->ssl_client_ctx, info->ssl_ca_filepath, + vhost->ssl_client_ctx, info->ssl_ca_filepath, NULL)) lwsl_err( "Unable to load SSL Client certs " @@ -520,32 +562,32 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info, /* support for client-side certificate authentication */ if (info->ssl_cert_filepath) { - n = SSL_CTX_use_certificate_chain_file(context->ssl_client_ctx, + n = SSL_CTX_use_certificate_chain_file(vhost->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->pt[0].serv_buf)); + (char *)vhost->context->pt[0].serv_buf)); return 1; } } if (info->ssl_private_key_filepath) { - lws_ssl_bind_passphrase(context->ssl_client_ctx, info); + lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info); /* set the private key from KeyFile */ - if (SSL_CTX_use_PrivateKey_file(context->ssl_client_ctx, + if (SSL_CTX_use_PrivateKey_file(vhost->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->pt[0].serv_buf)); + (char *)vhost->context->pt[0].serv_buf)); return 1; } /* verify private key */ - if (!SSL_CTX_check_private_key(context->ssl_client_ctx)) { + if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) { lwsl_err("Private SSL key doesn't match cert\n"); return 1; } @@ -556,11 +598,12 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info, * lws_get_context() in the callback */ memset(&wsi, 0, sizeof(wsi)); - wsi.context = context; + wsi.vhost = vhost; + wsi.context = vhost->context; - context->protocols[0].callback(&wsi, + vhost->protocols[0].callback(&wsi, LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS, - context->ssl_client_ctx, NULL, 0); + vhost->ssl_client_ctx, NULL, 0); return 0; } @@ -712,13 +755,13 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) BIO *bio; #endif - if (!LWS_SSL_ENABLED(context)) + if (!LWS_SSL_ENABLED(wsi->vhost)) return 0; switch (wsi->mode) { case LWSCM_SSL_INIT: - wsi->ssl = SSL_new(context->ssl_ctx); + wsi->ssl = SSL_new(wsi->vhost->ssl_ctx); if (wsi->ssl == NULL) { lwsl_err("SSL_new failed: %s\n", ERR_error_string(SSL_get_error(wsi->ssl, 0), NULL)); @@ -787,7 +830,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) * it disabled unless you know it's not a problem for you */ - if (context->allow_non_ssl_on_ssl_port) { + if (wsi->vhost->allow_non_ssl_on_ssl_port) { if (n >= 1 && pt->serv_buf[0] >= ' ') { /* * TLS content-type for Handshake is 0x16, and @@ -876,14 +919,18 @@ fail: return 1; } -LWS_VISIBLE void +void +lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost) +{ + if (vhost->ssl_ctx) + SSL_CTX_free(vhost->ssl_ctx); + if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx) + SSL_CTX_free(vhost->ssl_client_ctx); +} + +void lws_ssl_context_destroy(struct lws_context *context) { - if (context->ssl_ctx) - SSL_CTX_free(context->ssl_ctx); - if (!context->user_supplied_ssl_ctx && context->ssl_client_ctx) - SSL_CTX_free(context->ssl_client_ctx); - #if (OPENSSL_VERSION_NUMBER < 0x01000000) || defined(USE_WOLFSSL) ERR_remove_state(0); #else diff --git a/test-server/test-server.c b/test-server/test-server.c index 58f3d7ca..b285cda5 100644 --- a/test-server/test-server.c +++ b/test-server/test-server.c @@ -1,5 +1,5 @@ /* - * libwebsockets-test-servet - libwebsockets test implementation + * libwebsockets-test-server - libwebsockets test implementation * * Copyright (C) 2010-2016 Andy Green *