2 * libwebsockets web server application
4 * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser 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",
39 "global.ws-pingpong-secs",
40 "global.reject-service-keywords[].*",
41 "global.reject-service-keywords[]",
44 enum lejp_global_paths {
52 LWJPGP_REJECT_SERVICE_KEYWORDS_NAME,
53 LWJPGP_REJECT_SERVICE_KEYWORDS
56 static const char * const paths_vhosts[] = {
62 "vhosts[].unix-socket",
64 "vhosts[].host-ssl-key",
65 "vhosts[].host-ssl-cert",
66 "vhosts[].host-ssl-ca",
67 "vhosts[].access-log",
68 "vhosts[].mounts[].mountpoint",
69 "vhosts[].mounts[].origin",
70 "vhosts[].mounts[].protocol",
71 "vhosts[].mounts[].default",
72 "vhosts[].mounts[].auth-mask",
73 "vhosts[].mounts[].cgi-timeout",
74 "vhosts[].mounts[].cgi-env[].*",
75 "vhosts[].mounts[].cache-max-age",
76 "vhosts[].mounts[].cache-reuse",
77 "vhosts[].mounts[].cache-revalidate",
78 "vhosts[].mounts[].basic-auth",
79 "vhosts[].mounts[].cache-intermediaries",
80 "vhosts[].mounts[].extra-mimetypes.*",
81 "vhosts[].mounts[].interpret.*",
82 "vhosts[].ws-protocols[].*.*",
83 "vhosts[].ws-protocols[].*",
84 "vhosts[].ws-protocols[]",
85 "vhosts[].keepalive_timeout",
86 "vhosts[].enable-client-ssl",
88 "vhosts[].ecdh-curve",
91 "vhosts[].ssl-option-set",
92 "vhosts[].ssl-option-clear",
93 "vhosts[].mounts[].pmo[].*",
94 "vhosts[].headers[].*",
98 enum lejp_vhost_paths {
107 LEJPVP_HOST_SSL_CERT,
112 LEJPVP_MOUNT_PROTOCOL,
114 LEJPVP_DEFAULT_AUTH_MASK,
117 LEJPVP_MOUNT_CACHE_MAX_AGE,
118 LEJPVP_MOUNT_CACHE_REUSE,
119 LEJPVP_MOUNT_CACHE_REVALIDATE,
120 LEJPVP_MOUNT_BASIC_AUTH,
121 LEJPVP_MOUNT_CACHE_INTERMEDIARIES,
122 LEJPVP_MOUNT_EXTRA_MIMETYPES,
123 LEJPVP_MOUNT_INTERPRET,
124 LEJPVP_PROTOCOL_NAME_OPT,
125 LEJPVP_PROTOCOL_NAME,
127 LEJPVP_KEEPALIVE_TIMEOUT,
128 LEJPVP_ENABLE_CLIENT_SSL,
133 LEJPVP_SSL_OPTION_SET,
134 LEJPVP_SSL_OPTION_CLEAR,
140 static const char * const parser_errs[] = {
144 "Expected closing '}'",
147 "Illegal unescaped control char",
148 "Illegal escape format",
149 "Illegal hex number",
151 "Illegal value start",
152 "Digit required after decimal point",
154 "Bad exponent format",
159 "JSON nesting limit exceeded",
160 "Nesting tracking used up",
162 "Comma or block end expected",
164 "Parser callback errored (see earlier error)",
167 #define MAX_PLUGIN_DIRS 10
170 struct lws_context_creation_info *info;
171 struct lws_context *context;
172 const struct lws_protocols *protocols;
173 const struct lws_extension *extensions;
174 char *p, *end, valid;
175 struct lws_http_mount *head, *last;
177 struct lws_protocol_vhost_options *pvo;
178 struct lws_protocol_vhost_options *pvo_em;
179 struct lws_protocol_vhost_options *pvo_int;
180 struct lws_http_mount m;
181 const char **plugin_dirs;
182 int count_plugin_dirs;
184 unsigned int enable_client_ssl:1;
185 unsigned int fresh_mount:1;
186 unsigned int any_vhosts:1;
190 lwsws_align(struct jpargs *a)
192 if ((unsigned long)(a->p) & 15)
193 a->p += 16 - ((unsigned long)(a->p) & 15);
199 arg_to_bool(const char *s)
201 static const char * const on[] = { "on", "yes", "true" };
207 for (n = 0; n < ARRAY_SIZE(on); n++)
208 if (!strcasecmp(s, on[n]))
215 lejp_globals_cb(struct lejp_ctx *ctx, char reason)
217 struct jpargs *a = (struct jpargs *)ctx->user;
218 struct lws_protocol_vhost_options *rej;
221 /* we only match on the prepared path strings */
222 if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
225 /* this catches, eg, vhosts[].headers[].xxx */
226 if (reason == LEJPCB_VAL_STR_END &&
227 ctx->path_match == LWJPGP_REJECT_SERVICE_KEYWORDS_NAME + 1) {
228 rej = lwsws_align(a);
229 a->p += sizeof(*rej);
231 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
232 rej->next = a->info->reject_service_keywords;
233 a->info->reject_service_keywords = rej;
235 lwsl_notice(" adding rej %s=%s\n", a->p, ctx->buf);
243 switch (ctx->path_match - 1) {
245 a->info->uid = atoi(ctx->buf);
248 a->info->gid = atoi(ctx->buf);
250 case LEJPGP_COUNT_THREADS:
251 a->info->count_threads = atoi(ctx->buf);
253 case LWJPGP_INIT_SSL:
254 if (arg_to_bool(ctx->buf))
255 a->info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
257 case LEJPGP_SERVER_STRING:
258 a->info->server_string = a->p;
260 case LEJPGP_PLUGIN_DIR:
261 if (a->count_plugin_dirs == MAX_PLUGIN_DIRS - 1) {
262 lwsl_err("Too many plugin dirs\n");
265 a->plugin_dirs[a->count_plugin_dirs++] = a->p;
268 case LWJPGP_PINGPONG_SECS:
269 a->info->ws_ping_pong_interval = atoi(ctx->buf);
277 a->p += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf);
284 lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
286 struct jpargs *a = (struct jpargs *)ctx->user;
287 struct lws_protocol_vhost_options *pvo, *mp_cgienv, *headers;
288 struct lws_http_mount *m;
293 lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
294 for (n = 0; n < ctx->wildcount; n++)
295 lwsl_notice(" %d\n", ctx->wild[n]);
298 if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
299 /* set the defaults for this vhost */
304 a->info->iface = NULL;
305 a->info->protocols = a->protocols;
306 a->info->extensions = a->extensions;
307 a->info->ssl_cert_filepath = NULL;
308 a->info->ssl_private_key_filepath = NULL;
309 a->info->ssl_ca_filepath = NULL;
310 a->info->timeout_secs = 5;
311 a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
312 "ECDHE-RSA-AES256-GCM-SHA384:"
313 "DHE-RSA-AES256-GCM-SHA384:"
314 "ECDHE-RSA-AES256-SHA384:"
315 "HIGH:!aNULL:!eNULL:!EXPORT:"
316 "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
317 "!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
318 "!DHE-RSA-AES128-SHA256:"
319 "!AES128-GCM-SHA256:"
321 "!DHE-RSA-AES256-SHA256:"
322 "!AES256-GCM-SHA384:"
325 a->info->headers = NULL;
326 a->info->keepalive_timeout = 5;
327 a->info->log_filepath = NULL;
328 a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK |
329 LWS_SERVER_OPTION_STS);
330 a->enable_client_ssl = 0;
333 if (reason == LEJPCB_OBJECT_START &&
334 ctx->path_match == LEJPVP_MOUNTS + 1) {
336 memset(&a->m, 0, sizeof(a->m));
339 /* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
340 if (reason == LEJPCB_OBJECT_START &&
341 ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) {
342 a->pvo = lwsws_align(a);
343 a->p += sizeof(*a->pvo);
345 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
346 /* ie, enable this protocol, no options yet */
347 a->pvo->next = a->info->pvo;
348 a->info->pvo = a->pvo;
350 lwsl_notice(" adding protocol %s\n", a->p);
352 a->pvo->value = a->p;
353 a->pvo->options = NULL;
357 /* this catches, eg, vhosts[].headers[].xxx */
358 if (reason == LEJPCB_VAL_STR_END &&
359 ctx->path_match == LEJPVP_HEADERS_NAME + 1) {
360 headers = lwsws_align(a);
361 a->p += sizeof(*headers);
363 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
364 /* ie, enable this protocol, no options yet */
365 headers->next = a->info->headers;
366 a->info->headers = headers;
367 headers->name = a->p;
368 // lwsl_notice(" adding header %s=%s\n", a->p, ctx->buf);
375 headers->value = a->p;
376 headers->options = NULL;
380 if (reason == LEJPCB_OBJECT_END &&
381 (ctx->path_match == LEJPVP + 1 || !ctx->path[0]) &&
384 struct lws_vhost *vhost;
386 //lwsl_notice("%s\n", ctx->path);
387 if (!a->info->port) {
388 lwsl_err("Port required (eg, 443)");
392 a->info->mounts = a->head;
394 vhost = lws_create_vhost(a->context, a->info);
396 lwsl_err("Failed to create vhost %s\n",
397 a->info->vhost_name);
402 if (a->enable_client_ssl) {
403 memset(a->info, 0, sizeof(*a->info));
404 a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
405 lws_init_vhost_client_ssl(a->info, vhost);
411 if (reason == LEJPCB_OBJECT_END &&
412 ctx->path_match == LEJPVP_MOUNTS + 1) {
413 static const char * const mount_protocols[] = {
426 if (!a->m.mountpoint || !a->m.origin) {
427 lwsl_err("mountpoint and origin required\n");
430 lwsl_debug("adding mount %s\n", a->m.mountpoint);
432 memcpy(m, &a->m, sizeof(*m));
434 a->last->mount_next = m;
436 for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
437 if (!strncmp(a->m.origin, mount_protocols[n],
438 strlen(mount_protocols[n]))) {
439 lwsl_err("----%s\n", a->m.origin);
440 m->origin_protocol = n;
441 m->origin = a->m.origin +
442 strlen(mount_protocols[n]);
446 if (n == ARRAY_SIZE(mount_protocols)) {
447 lwsl_err("unsupported protocol:// %s\n", a->m.origin);
459 /* we only match on the prepared path strings */
460 if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
463 switch (ctx->path_match - 1) {
465 a->info->vhost_name = a->p;
468 a->info->port = atoi(ctx->buf);
470 case LEJPVP_INTERFACE:
471 a->info->iface = a->p;
474 if (arg_to_bool(ctx->buf))
475 a->info->options |= LWS_SERVER_OPTION_UNIX_SOCK;
477 a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK);
480 if (arg_to_bool(ctx->buf))
481 a->info->options |= LWS_SERVER_OPTION_STS;
483 a->info->options &= ~(LWS_SERVER_OPTION_STS);
485 case LEJPVP_HOST_SSL_KEY:
486 a->info->ssl_private_key_filepath = a->p;
488 case LEJPVP_HOST_SSL_CERT:
489 a->info->ssl_cert_filepath = a->p;
491 case LEJPVP_HOST_SSL_CA:
492 a->info->ssl_ca_filepath = a->p;
494 case LEJPVP_ACCESS_LOG:
495 a->info->log_filepath = a->p;
497 case LEJPVP_MOUNTPOINT:
498 a->m.mountpoint = a->p;
499 a->m.mountpoint_len = (unsigned char)strlen(ctx->buf);
502 if (!strncmp(ctx->buf, "callback://", 11))
503 a->m.protocol = a->p + 11;
511 case LEJPVP_DEFAULT_AUTH_MASK:
512 a->m.auth_mask = atoi(ctx->buf);
514 case LEJPVP_MOUNT_CACHE_MAX_AGE:
515 a->m.cache_max_age = atoi(ctx->buf);
517 case LEJPVP_MOUNT_CACHE_REUSE:
518 a->m.cache_reusable = arg_to_bool(ctx->buf);
520 case LEJPVP_MOUNT_CACHE_REVALIDATE:
521 a->m.cache_revalidate = arg_to_bool(ctx->buf);
523 case LEJPVP_MOUNT_CACHE_INTERMEDIARIES:
524 a->m.cache_intermediaries = arg_to_bool(ctx->buf);;
526 case LEJPVP_MOUNT_BASIC_AUTH:
527 a->m.basic_auth_login_file = a->p;
529 case LEJPVP_CGI_TIMEOUT:
530 a->m.cgi_timeout = atoi(ctx->buf);
532 case LEJPVP_KEEPALIVE_TIMEOUT:
533 a->info->keepalive_timeout = atoi(ctx->buf);
536 a->info->ssl_cipher_list = a->p;
538 case LEJPVP_ECDH_CURVE:
539 a->info->ecdh_curve = a->p;
543 mp_cgienv = lwsws_align(a);
544 a->p += sizeof(*a->m.cgienv);
546 mp_cgienv->next = a->m.cgienv;
547 a->m.cgienv = mp_cgienv;
549 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
550 mp_cgienv->name = a->p;
552 mp_cgienv->value = a->p;
553 mp_cgienv->options = NULL;
554 //lwsl_notice(" adding pmo / cgi-env '%s' = '%s'\n", mp_cgienv->name,
555 // mp_cgienv->value);
558 case LEJPVP_PROTOCOL_NAME_OPT:
560 * vhosts[].ws-protocols[].xxx-protocol.yyy-option
561 * ie, these are options attached to a protocol with { }
563 pvo = lwsws_align(a);
564 a->p += sizeof(*a->pvo);
566 n = lejp_get_wildcard(ctx, 1, a->p, a->end - a->p);
567 /* ie, enable this protocol, no options yet */
568 pvo->next = a->pvo->options;
569 a->pvo->options = pvo;
576 case LEJPVP_MOUNT_EXTRA_MIMETYPES:
577 a->pvo_em = lwsws_align(a);
578 a->p += sizeof(*a->pvo_em);
580 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
581 /* ie, enable this protocol, no options yet */
582 a->pvo_em->next = a->m.extra_mimetypes;
583 a->m.extra_mimetypes = a->pvo_em;
584 a->pvo_em->name = a->p;
585 lwsl_notice(" adding extra-mimetypes %s -> %s\n", a->p, ctx->buf);
587 a->pvo_em->value = a->p;
588 a->pvo_em->options = NULL;
591 case LEJPVP_MOUNT_INTERPRET:
592 a->pvo_int = lwsws_align(a);
593 a->p += sizeof(*a->pvo_int);
595 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
596 /* ie, enable this protocol, no options yet */
597 a->pvo_int->next = a->m.interpret;
598 a->m.interpret = a->pvo_int;
599 a->pvo_int->name = a->p;
600 lwsl_notice(" adding interpret %s -> %s\n", a->p,
603 a->pvo_int->value = a->p;
604 a->pvo_int->options = NULL;
607 case LEJPVP_ENABLE_CLIENT_SSL:
608 a->enable_client_ssl = arg_to_bool(ctx->buf);
612 if (arg_to_bool(ctx->buf))
613 a->info->options |= LWS_SERVER_OPTION_DISABLE_IPV6;
615 a->info->options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6);
618 case LEJPVP_IPV6ONLY:
619 a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY;
620 if (arg_to_bool(ctx->buf))
621 a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE;
623 a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
626 case LEJPVP_SSL_OPTION_SET:
627 a->info->ssl_options_set |= atol(ctx->buf);
629 case LEJPVP_SSL_OPTION_CLEAR:
630 a->info->ssl_options_clear |= atol(ctx->buf);
639 p1 = strstr(p, ESC_INSTALL_DATADIR);
642 if (n > a->end - a->p)
646 a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR);
647 p += n + strlen(ESC_INSTALL_DATADIR);
650 a->p += lws_snprintf(a->p, a->end - a->p, "%s", p);
657 * returns 0 = OK, 1 = can't open, 2 = parsing error
661 lwsws_get_config(void *user, const char *f, const char * const *paths,
662 int count_paths, lejp_callback cb)
664 unsigned char buf[128];
668 fd = open(f, O_RDONLY);
670 lwsl_err("Cannot open %s\n", f);
673 lwsl_info("%s: %s\n", __func__, f);
674 lejp_construct(&ctx, cb, user, paths, count_paths);
677 n = read(fd, buf, sizeof(buf));
681 m = (int)(signed char)lejp_parse(&ctx, buf, n);
682 } while (m == LEJP_CONTINUE);
689 lwsl_err("%s(%u): parsing error %d: %s\n", f, n, m,
697 #if defined(LWS_USE_LIBUV) && UV_VERSION_MAJOR > 0
700 lwsws_get_config_d(void *user, const char *d, const char * const *paths,
701 int count_paths, lejp_callback cb)
709 ir = uv_loop_init(&loop);
711 lwsl_err("%s: loop init failed %d\n", __func__, ir);
714 if (!uv_fs_scandir(&loop, &req, d, 0, NULL)) {
715 lwsl_err("Scandir on %s failed\n", d);
719 while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
720 lws_snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name);
721 ret = lwsws_get_config(user, path, paths, count_paths, cb);
727 uv_fs_req_cleanup(&req);
728 uv_loop_close(&loop);
736 static int filter(const struct dirent *ent)
738 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
746 lwsws_get_config_d(void *user, const char *d, const char * const *paths,
747 int count_paths, lejp_callback cb)
750 struct dirent **namelist;
754 n = scandir(d, &namelist, filter, alphasort);
756 lwsl_err("Scandir on %d failed\n", d);
759 for (i = 0; i < n; i++) {
760 lws_snprintf(path, sizeof(path) - 1, "%s/%s", d,
761 namelist[i]->d_name);
762 ret = lwsws_get_config(user, path, paths, count_paths, cb);
783 lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d,
787 const char * const *old = info->plugin_dirs;
790 memset(&a, 0, sizeof(a));
794 a.end = (a.p + *len) - 1;
798 info->plugin_dirs = (void *)a.p;
799 a.plugin_dirs = (void *)a.p; /* writeable version */
800 a.p += MAX_PLUGIN_DIRS * sizeof(void *);
802 /* copy any default paths */
804 while (old && *old) {
805 a.plugin_dirs[a.count_plugin_dirs++] = *old;
809 lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
810 if (lwsws_get_config(&a, dd, paths_global,
811 ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
813 lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
814 if (lwsws_get_config_d(&a, dd, paths_global,
815 ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
818 a.plugin_dirs[a.count_plugin_dirs] = NULL;
827 lwsws_get_config_vhosts(struct lws_context *context,
828 struct lws_context_creation_info *info, const char *d,
834 memset(&a, 0, sizeof(a));
841 a.protocols = info->protocols;
842 a.extensions = info->extensions;
844 lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
845 if (lwsws_get_config(&a, dd, paths_vhosts,
846 ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
848 lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
849 if (lwsws_get_config_d(&a, dd, paths_vhosts,
850 ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
857 lwsl_err("Need at least one vhost\n");
861 // lws_finalize_startup(context);