introduce vhosts

This patch splits out some lws_context members into a new lws_vhost struct.

 - ssl state and options per vhost
 - SSL_CTX for serving and client per vhost
 - protocols[] per vhost
 - extensions[] per vhost

lws_context maintains a linked list of lws_vhosts.

The same lws_context_creation_info struct is used to regulate both the
context creation and to create vhosts: for backward compatibility if you
didn't provide the new LWS_SERVER_OPTION_EXPLICIT_VHOSTS option, then
a default vhost is created at context creation time using the same info
data as the context itself.

If you will have multiple vhosts though, you should give the
LWS_SERVER_OPTION_EXPLICIT_VHOSTS option at context creation time,
create the context first and then the vhosts afterwards using

  lws_create_vhost(contest, &info);

Although there is a lot of housekeeping to implement this change, there
is almost no additional overhead if you don't use multiple vhosts and
very little api impact (no changes to test apps).

Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
Andy Green 2016-03-28 10:10:43 +08:00
parent e2cf3e1cc0
commit d526c50c22
18 changed files with 784 additions and 370 deletions

View file

@ -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;

View file

@ -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");

View file

@ -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);

View file

@ -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");

View file

@ -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,

View file

@ -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;

View file

@ -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 */

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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);
}

View file

@ -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))

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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");

267
lib/ssl.c
View file

@ -28,7 +28,8 @@
#include <openssl/ecdh.h>
#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

View file

@ -1,5 +1,5 @@
/*
* libwebsockets-test-servet - libwebsockets test implementation
* libwebsockets-test-server - libwebsockets test implementation
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*