2 * libwebsockets web server application
4 * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 #include "private-libwebsockets.h"
26 /* this is needed for Travis CI */
30 #define ESC_INSTALL_DATADIR "_lws_ddir_"
32 static const char * const paths_global[] = {
35 "global.count-threads",
37 "global.server-string",
41 enum lejp_global_paths {
50 static const char * const paths_vhosts[] = {
56 "vhosts[].unix-socket",
58 "vhosts[].host-ssl-key",
59 "vhosts[].host-ssl-cert",
60 "vhosts[].host-ssl-ca",
61 "vhosts[].access-log",
62 "vhosts[].mounts[].mountpoint",
63 "vhosts[].mounts[].origin",
64 "vhosts[].mounts[].protocol",
65 "vhosts[].mounts[].default",
66 "vhosts[].mounts[].auth-mask",
67 "vhosts[].mounts[].cgi-timeout",
68 "vhosts[].mounts[].cgi-env[].*",
69 "vhosts[].mounts[].cache-max-age",
70 "vhosts[].mounts[].cache-reuse",
71 "vhosts[].mounts[].cache-revalidate",
72 "vhosts[].mounts[].cache-intermediaries",
73 "vhosts[].mounts[].extra-mimetypes.*",
74 "vhosts[].mounts[].interpret.*",
75 "vhosts[].ws-protocols[].*.*",
76 "vhosts[].ws-protocols[].*",
77 "vhosts[].ws-protocols[]",
78 "vhosts[].keepalive_timeout",
79 "vhosts[].enable-client-ssl",
81 "vhosts[].ecdh-curve",
84 "vhosts[].ssl-option-set",
85 "vhosts[].ssl-option-clear",
86 "vhosts[].mounts[].pmo[].*",
89 enum lejp_vhost_paths {
103 LEJPVP_MOUNT_PROTOCOL,
105 LEJPVP_DEFAULT_AUTH_MASK,
108 LEJPVP_MOUNT_CACHE_MAX_AGE,
109 LEJPVP_MOUNT_CACHE_REUSE,
110 LEJPVP_MOUNT_CACHE_REVALIDATE,
111 LEJPVP_MOUNT_CACHE_INTERMEDIARIES,
112 LEJPVP_MOUNT_EXTRA_MIMETYPES,
113 LEJPVP_MOUNT_INTERPRET,
114 LEJPVP_PROTOCOL_NAME_OPT,
115 LEJPVP_PROTOCOL_NAME,
117 LEJPVP_KEEPALIVE_TIMEOUT,
118 LEJPVP_ENABLE_CLIENT_SSL,
123 LEJPVP_SSL_OPTION_SET,
124 LEJPVP_SSL_OPTION_CLEAR,
128 static const char * const parser_errs[] = {
132 "Expected closing '}'",
135 "Illegal unescaped control char",
136 "Illegal escape format",
137 "Illegal hex number",
139 "Illegal value start",
140 "Digit required after decimal point",
142 "Bad exponent format",
147 "JSON nesting limit exceeded",
148 "Nesting tracking used up",
150 "Comma or block end expected",
152 "Parser callback errored (see earlier error)",
155 #define MAX_PLUGIN_DIRS 10
158 struct lws_context_creation_info *info;
159 struct lws_context *context;
160 const struct lws_protocols *protocols;
161 const struct lws_extension *extensions;
162 char *p, *end, valid;
163 struct lws_http_mount *head, *last;
165 struct lws_protocol_vhost_options *pvo;
166 struct lws_protocol_vhost_options *pvo_em;
167 struct lws_protocol_vhost_options *pvo_int;
168 struct lws_http_mount m;
169 const char **plugin_dirs;
170 int count_plugin_dirs;
172 unsigned int enable_client_ssl:1;
173 unsigned int fresh_mount:1;
174 unsigned int any_vhosts:1;
178 lwsws_align(struct jpargs *a)
180 if ((unsigned long)(a->p) & 15)
181 a->p += 16 - ((unsigned long)(a->p) & 15);
187 arg_to_bool(const char *s)
189 static const char * const on[] = { "on", "yes", "true" };
195 for (n = 0; n < ARRAY_SIZE(on); n++)
196 if (!strcasecmp(s, on[n]))
203 lejp_globals_cb(struct lejp_ctx *ctx, char reason)
205 struct jpargs *a = (struct jpargs *)ctx->user;
207 /* we only match on the prepared path strings */
208 if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
211 switch (ctx->path_match - 1) {
213 a->info->uid = atoi(ctx->buf);
216 a->info->gid = atoi(ctx->buf);
218 case LEJPGP_COUNT_THREADS:
219 a->info->count_threads = atoi(ctx->buf);
221 case LWJPGP_INIT_SSL:
222 if (arg_to_bool(ctx->buf))
223 a->info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
225 case LEJPGP_SERVER_STRING:
226 a->info->server_string = a->p;
228 case LEJPGP_PLUGIN_DIR:
229 if (a->count_plugin_dirs == MAX_PLUGIN_DIRS - 1) {
230 lwsl_err("Too many plugin dirs\n");
233 a->plugin_dirs[a->count_plugin_dirs++] = a->p;
240 a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
246 lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
248 struct jpargs *a = (struct jpargs *)ctx->user;
249 struct lws_protocol_vhost_options *pvo, *mp_cgienv;
250 struct lws_http_mount *m;
255 lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
256 for (n = 0; n < ctx->wildcount; n++)
257 lwsl_notice(" %d\n", ctx->wild[n]);
260 if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
261 /* set the defaults for this vhost */
266 a->info->iface = NULL;
267 a->info->protocols = a->protocols;
268 a->info->extensions = a->extensions;
269 a->info->ssl_cert_filepath = NULL;
270 a->info->ssl_private_key_filepath = NULL;
271 a->info->ssl_ca_filepath = NULL;
272 a->info->timeout_secs = 5;
273 a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
274 "ECDHE-RSA-AES256-GCM-SHA384:"
275 "DHE-RSA-AES256-GCM-SHA384:"
276 "ECDHE-RSA-AES256-SHA384:"
277 "HIGH:!aNULL:!eNULL:!EXPORT:"
278 "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
279 "!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
280 "!DHE-RSA-AES128-SHA256:"
281 "!AES128-GCM-SHA256:"
283 "!DHE-RSA-AES256-SHA256:"
284 "!AES256-GCM-SHA384:"
287 a->info->keepalive_timeout = 60;
288 a->info->log_filepath = NULL;
289 a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK |
290 LWS_SERVER_OPTION_STS);
291 a->enable_client_ssl = 0;
294 if (reason == LEJPCB_OBJECT_START &&
295 ctx->path_match == LEJPVP_MOUNTS + 1) {
297 memset(&a->m, 0, sizeof(a->m));
300 /* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
301 if (reason == LEJPCB_OBJECT_START &&
302 ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) {
303 a->pvo = lwsws_align(a);
304 a->p += sizeof(*a->pvo);
306 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
307 /* ie, enable this protocol, no options yet */
308 a->pvo->next = a->info->pvo;
309 a->info->pvo = a->pvo;
311 lwsl_notice(" adding protocol %s\n", a->p);
313 a->pvo->value = a->p;
314 a->pvo->options = NULL;
318 if (reason == LEJPCB_OBJECT_END &&
319 (ctx->path_match == LEJPVP + 1 || !ctx->path[0]) &&
322 struct lws_vhost *vhost;
324 //lwsl_notice("%s\n", ctx->path);
325 if (!a->info->port) {
326 lwsl_err("Port required (eg, 443)");
330 a->info->mounts = a->head;
332 vhost = lws_create_vhost(a->context, a->info);
334 lwsl_err("Failed to create vhost %s\n",
335 a->info->vhost_name);
340 if (a->enable_client_ssl) {
341 memset(a->info, 0, sizeof(*a->info));
342 a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
343 lws_init_vhost_client_ssl(a->info, vhost);
349 if (reason == LEJPCB_OBJECT_END &&
350 ctx->path_match == LEJPVP_MOUNTS + 1) {
351 static const char * const mount_protocols[] = {
364 if (!a->m.mountpoint || !a->m.origin) {
365 lwsl_err("mountpoint and origin required\n");
368 lwsl_debug("adding mount %s\n", a->m.mountpoint);
370 memcpy(m, &a->m, sizeof(*m));
372 a->last->mount_next = m;
374 for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
375 if (!strncmp(a->m.origin, mount_protocols[n],
376 strlen(mount_protocols[n]))) {
377 lwsl_err("----%s\n", a->m.origin);
378 m->origin_protocol = n;
379 m->origin = a->m.origin +
380 strlen(mount_protocols[n]);
384 if (n == ARRAY_SIZE(mount_protocols)) {
385 lwsl_err("unsupported protocol:// %s\n", a->m.origin);
397 /* we only match on the prepared path strings */
398 if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
401 switch (ctx->path_match - 1) {
403 a->info->vhost_name = a->p;
406 a->info->port = atoi(ctx->buf);
408 case LEJPVP_INTERFACE:
409 a->info->iface = a->p;
412 if (arg_to_bool(ctx->buf))
413 a->info->options |= LWS_SERVER_OPTION_UNIX_SOCK;
415 a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK);
418 if (arg_to_bool(ctx->buf))
419 a->info->options |= LWS_SERVER_OPTION_STS;
421 a->info->options &= ~(LWS_SERVER_OPTION_STS);
423 case LEJPVP_HOST_SSL_KEY:
424 a->info->ssl_private_key_filepath = a->p;
426 case LEJPVP_HOST_SSL_CERT:
427 a->info->ssl_cert_filepath = a->p;
429 case LEJPVP_HOST_SSL_CA:
430 a->info->ssl_ca_filepath = a->p;
432 case LEJPVP_ACCESS_LOG:
433 a->info->log_filepath = a->p;
435 case LEJPVP_MOUNTPOINT:
436 a->m.mountpoint = a->p;
437 a->m.mountpoint_len = (unsigned char)strlen(ctx->buf);
440 if (!strncmp(ctx->buf, "callback://", 11))
441 a->m.protocol = a->p + 11;
449 case LEJPVP_DEFAULT_AUTH_MASK:
450 a->m.auth_mask = atoi(ctx->buf);
452 case LEJPVP_MOUNT_CACHE_MAX_AGE:
453 a->m.cache_max_age = atoi(ctx->buf);
455 case LEJPVP_MOUNT_CACHE_REUSE:
456 a->m.cache_reusable = arg_to_bool(ctx->buf);
458 case LEJPVP_MOUNT_CACHE_REVALIDATE:
459 a->m.cache_revalidate = arg_to_bool(ctx->buf);
461 case LEJPVP_MOUNT_CACHE_INTERMEDIARIES:
462 a->m.cache_intermediaries = arg_to_bool(ctx->buf);;
464 case LEJPVP_CGI_TIMEOUT:
465 a->m.cgi_timeout = atoi(ctx->buf);
467 case LEJPVP_KEEPALIVE_TIMEOUT:
468 a->info->keepalive_timeout = atoi(ctx->buf);
471 a->info->ssl_cipher_list = a->p;
473 case LEJPVP_ECDH_CURVE:
474 a->info->ecdh_curve = a->p;
478 mp_cgienv = lwsws_align(a);
479 a->p += sizeof(*a->m.cgienv);
481 mp_cgienv->next = a->m.cgienv;
482 a->m.cgienv = mp_cgienv;
484 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
485 mp_cgienv->name = a->p;
487 mp_cgienv->value = a->p;
488 mp_cgienv->options = NULL;
489 lwsl_notice(" adding pmo / cgi-env '%s' = '%s'\n", mp_cgienv->name,
493 case LEJPVP_PROTOCOL_NAME_OPT:
495 * vhosts[].ws-protocols[].xxx-protocol.yyy-option
496 * ie, these are options attached to a protocol with { }
498 pvo = lwsws_align(a);
499 a->p += sizeof(*a->pvo);
501 n = lejp_get_wildcard(ctx, 1, a->p, a->end - a->p);
502 /* ie, enable this protocol, no options yet */
503 pvo->next = a->pvo->options;
504 a->pvo->options = pvo;
511 case LEJPVP_MOUNT_EXTRA_MIMETYPES:
512 a->pvo_em = lwsws_align(a);
513 a->p += sizeof(*a->pvo_em);
515 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
516 /* ie, enable this protocol, no options yet */
517 a->pvo_em->next = a->m.extra_mimetypes;
518 a->m.extra_mimetypes = a->pvo_em;
519 a->pvo_em->name = a->p;
520 lwsl_notice(" adding extra-mimetypes %s -> %s\n", a->p, ctx->buf);
522 a->pvo_em->value = a->p;
523 a->pvo_em->options = NULL;
526 case LEJPVP_MOUNT_INTERPRET:
527 a->pvo_int = lwsws_align(a);
528 a->p += sizeof(*a->pvo_int);
530 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
531 /* ie, enable this protocol, no options yet */
532 a->pvo_int->next = a->m.interpret;
533 a->m.interpret = a->pvo_int;
534 a->pvo_int->name = a->p;
535 lwsl_notice(" adding interpret %s -> %s\n", a->p,
538 a->pvo_int->value = a->p;
539 a->pvo_int->options = NULL;
542 case LEJPVP_ENABLE_CLIENT_SSL:
543 a->enable_client_ssl = arg_to_bool(ctx->buf);
547 if (arg_to_bool(ctx->buf))
548 a->info->options |= LWS_SERVER_OPTION_DISABLE_IPV6;
550 a->info->options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6);
553 case LEJPVP_IPV6ONLY:
554 a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY;
555 if (arg_to_bool(ctx->buf))
556 a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE;
558 a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
561 case LEJPVP_SSL_OPTION_SET:
562 a->info->ssl_options_set |= atol(ctx->buf);
564 case LEJPVP_SSL_OPTION_CLEAR:
565 a->info->ssl_options_clear |= atol(ctx->buf);
574 p1 = strstr(p, ESC_INSTALL_DATADIR);
577 if (n > a->end - a->p)
581 a->p += snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR);
582 p += n + strlen(ESC_INSTALL_DATADIR);
585 a->p += snprintf(a->p, a->end - a->p, "%s", p);
592 * returns 0 = OK, 1 = can't open, 2 = parsing error
596 lwsws_get_config(void *user, const char *f, const char * const *paths,
597 int count_paths, lejp_callback cb)
599 unsigned char buf[128];
603 fd = open(f, O_RDONLY);
605 lwsl_err("Cannot open %s\n", f);
608 lwsl_info("%s: %s\n", __func__, f);
609 lejp_construct(&ctx, cb, user, paths, count_paths);
612 n = read(fd, buf, sizeof(buf));
616 m = (int)(signed char)lejp_parse(&ctx, buf, n);
617 } while (m == LEJP_CONTINUE);
624 lwsl_err("%s(%u): parsing error %d: %s\n", f, n, m,
632 #if defined(LWS_USE_LIBUV) && UV_VERSION_MAJOR > 0
635 lwsws_get_config_d(void *user, const char *d, const char * const *paths,
636 int count_paths, lejp_callback cb)
646 if (!uv_fs_scandir(&loop, &req, d, 0, NULL)) {
647 lwsl_err("Scandir on %s failed\n", d);
651 while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
652 snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name);
653 ret = lwsws_get_config(user, path, paths, count_paths, cb);
659 uv_fs_req_cleanup(&req);
660 uv_loop_close(&loop);
668 static int filter(const struct dirent *ent)
670 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
678 lwsws_get_config_d(void *user, const char *d, const char * const *paths,
679 int count_paths, lejp_callback cb)
682 struct dirent **namelist;
686 n = scandir(d, &namelist, filter, alphasort);
688 lwsl_err("Scandir on %d failed\n", d);
691 for (i = 0; i < n; i++) {
692 snprintf(path, sizeof(path) - 1, "%s/%s", d,
693 namelist[i]->d_name);
694 ret = lwsws_get_config(user, path, paths, count_paths, cb);
715 lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d,
719 const char * const *old = info->plugin_dirs;
722 memset(&a, 0, sizeof(a));
726 a.end = (a.p + *len) - 1;
730 info->plugin_dirs = (void *)a.p;
731 a.plugin_dirs = (void *)a.p; /* writeable version */
732 a.p += MAX_PLUGIN_DIRS * sizeof(void *);
734 /* copy any default paths */
736 while (old && *old) {
737 a.plugin_dirs[a.count_plugin_dirs++] = *old;
741 snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
742 if (lwsws_get_config(&a, dd, paths_global,
743 ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
745 snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
746 if (lwsws_get_config_d(&a, dd, paths_global,
747 ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
750 a.plugin_dirs[a.count_plugin_dirs] = NULL;
759 lwsws_get_config_vhosts(struct lws_context *context,
760 struct lws_context_creation_info *info, const char *d,
766 memset(&a, 0, sizeof(a));
773 a.protocols = info->protocols;
774 a.extensions = info->extensions;
776 snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
777 if (lwsws_get_config(&a, dd, paths_vhosts,
778 ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
780 snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
781 if (lwsws_get_config_d(&a, dd, paths_vhosts,
782 ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
789 lwsl_err("Need at least one vhost\n");
793 lws_finalize_startup(context);