diff --git a/README.coding.md b/README.coding.md index aab6d772..5b438397 100644 --- a/README.coding.md +++ b/README.coding.md @@ -534,6 +534,25 @@ There are some new members but mainly it's stuff you used to set at context creation time. +How lws matches hostname or SNI to a vhost +------------------------------------------ + +LWS first strips any trailing :port number. + +Then it tries to find an exact name match for a vhost listening on the correct +port, ie, if SNI or the Host: header provided abc.com:1234, it will match on a +vhost named abc.com that is listening on port 1234. + +If there is no exact match, lws will consider wildcard matches, for example +if cats.abc.com:1234 is provided by the client by SNI or Host: header, it will +accept a vhost "abc.com" listening on port 1234. If there was a better, exact, +match, it will have been chosen in preference to this. + +Connections with SSL will still have the client go on to check the +certificate allows wildcards and error out if not. + + + Using lws v2 mounts on a vhost ------------------------------ diff --git a/doc/html/md_README.coding.html b/doc/html/md_README.coding.html index 997db81f..570e6764 100644 --- a/doc/html/md_README.coding.html +++ b/doc/html/md_README.coding.html @@ -197,6 +197,11 @@ space" as a virtual filesystem that may or may not be backed by a regular filesy
1 LWS_VISIBLE struct lws_vhost *
2 lws_create_vhost(struct lws_context *context,
3  struct lws_context_creation_info *info,
4  struct lws_http_mount *mounts);

lws_create_vhost() uses the same info struct as lws_create_context(), it ignores members related to context and uses the ones meaningful for vhost (marked with VH in libwebsockets.h).

1 struct lws_context_creation_info {
2  int port; /* VH */
3  const char *iface; /* VH */
4  const struct lws_protocols *protocols; /* VH */
5  const struct lws_extension *extensions; /* VH */
6 ...

When you attach the vhost, if the vhost's port already has a listen socket then both vhosts share it and use SNI (is SSL in use) or the Host: header from the client to select the right one. Or if no other vhost already listening the a new listen socket is created.

There are some new members but mainly it's stuff you used to set at context creation time.

+

How lws matches hostname or SNI to a vhost

+

LWS first strips any trailing :port number.

+

Then it tries to find an exact name match for a vhost listening on the correct port, ie, if SNI or the Host: header provided abc.com:1234, it will match on a vhost named abc.com that is listening on port 1234.

+

If there is no exact match, lws will consider wildcard matches, for example if cats.abc.com:1234 is provided by the client by SNI or Host: header, it will accept a vhost "abc.com" listening on port 1234. If there was a better, exact, match, it will have been chosen in preference to this.

+

Connections with SSL will still have the client go on to check the certificate allows wildcards and error out if not.

Using lws v2 mounts on a vhost

The last argument to lws_create_vhost() lets you associate a linked list of lws_http_mount structures with that vhost's URL 'namespace', in a similar way that unix lets you mount filesystems into areas of your / filesystem how you like and deal with the contents transparently.

1 struct lws_http_mount {
2  struct lws_http_mount *mount_next;
3  const char *mountpoint; /* mountpoint in http pathspace, eg, "/" */
4  const char *origin; /* path to be mounted, eg, "/var/www/warmcat.com" */
5  const char *def; /* default target, eg, "index.html" */
6 
7  struct lws_protocol_vhost_options *cgienv;
8 
9  int cgi_timeout;
10  int cache_max_age;
11 
12  unsigned int cache_reusable:1;
13  unsigned int cache_revalidate:1;
14  unsigned int cache_intermediaries:1;
15 
16  unsigned char origin_protocol;
17  unsigned char mountpoint_len;
18 };

The last mount structure should have a NULL mount_next, otherwise it should point to the 'next' mount structure in your list.

diff --git a/lib/server.c b/lib/server.c index 902bf94d..d5272c67 100644 --- a/lib/server.c +++ b/lib/server.c @@ -193,16 +193,48 @@ struct lws_vhost * lws_select_vhost(struct lws_context *context, int port, const char *servername) { struct lws_vhost *vhost = context->vhost_list; + const char *p; + int n, m, colon; + + n = strlen(servername); + colon = n; + p = strchr(servername, ':'); + if (p) + colon = p - servername; + + /* first try exact matches */ while (vhost) { if (port == vhost->listen_port && - !strcmp(vhost->name, servername)) { + !strncmp(vhost->name, servername, colon)) { lwsl_info("SNI: Found: %s\n", servername); return vhost; } vhost = vhost->vhost_next; } + /* + * if no exact matches, try matching *.vhost-name + * unintentional matches are possible but resolve to x.com for *.x.com + * which is reasonable. If exact match exists we already chose it and + * never reach here. SSL will still fail it if the cert doesn't allow + * *.x.com. + */ + + vhost = context->vhost_list; + while (vhost) { + m = strlen(vhost->name); + if (port == vhost->listen_port && + m <= (colon - 2) && + servername[colon - m - 1] == '.' && + !strncmp(vhost->name, servername + colon - m, m)) { + lwsl_info("SNI: Found %s on wildcard: %s\n", + servername, vhost->name); + return vhost; + } + vhost = vhost->vhost_next; + } + return NULL; } @@ -990,7 +1022,8 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len) if (vhost) wsi->vhost = vhost; - } + } else + lwsl_info("no host\n"); wsi->vhost->trans++; if (!wsi->conn_stat_done) {