blob: 2050238213d2c4ad4078caa56897426986d97663 [file] [log] [blame]
/*
* libwebsockets web server application
*
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "core/private.h"
#ifndef _WIN32
/* this is needed for Travis CI */
#include <dirent.h>
#endif
#define ESC_INSTALL_DATADIR "_lws_ddir_"
static const char * const paths_global[] = {
"global.uid",
"global.gid",
"global.count-threads",
"global.init-ssl",
"global.server-string",
"global.plugin-dir",
"global.ws-pingpong-secs",
"global.timeout-secs",
"global.reject-service-keywords[].*",
"global.reject-service-keywords[]",
"global.default-alpn",
};
enum lejp_global_paths {
LEJPGP_UID,
LEJPGP_GID,
LEJPGP_COUNT_THREADS,
LWJPGP_INIT_SSL,
LEJPGP_SERVER_STRING,
LEJPGP_PLUGIN_DIR,
LWJPGP_PINGPONG_SECS,
LWJPGP_TIMEOUT_SECS,
LWJPGP_REJECT_SERVICE_KEYWORDS_NAME,
LWJPGP_REJECT_SERVICE_KEYWORDS,
LWJPGP_DEFAULT_ALPN,
};
static const char * const paths_vhosts[] = {
"vhosts[]",
"vhosts[].mounts[]",
"vhosts[].name",
"vhosts[].port",
"vhosts[].interface",
"vhosts[].unix-socket",
"vhosts[].sts",
"vhosts[].host-ssl-key",
"vhosts[].host-ssl-cert",
"vhosts[].host-ssl-ca",
"vhosts[].access-log",
"vhosts[].mounts[].mountpoint",
"vhosts[].mounts[].origin",
"vhosts[].mounts[].protocol",
"vhosts[].mounts[].default",
"vhosts[].mounts[].auth-mask",
"vhosts[].mounts[].cgi-timeout",
"vhosts[].mounts[].cgi-env[].*",
"vhosts[].mounts[].cache-max-age",
"vhosts[].mounts[].cache-reuse",
"vhosts[].mounts[].cache-revalidate",
"vhosts[].mounts[].basic-auth",
"vhosts[].mounts[].cache-intermediaries",
"vhosts[].mounts[].extra-mimetypes.*",
"vhosts[].mounts[].interpret.*",
"vhosts[].ws-protocols[].*.*",
"vhosts[].ws-protocols[].*",
"vhosts[].ws-protocols[]",
"vhosts[].keepalive_timeout",
"vhosts[].enable-client-ssl",
"vhosts[].ciphers",
"vhosts[].ecdh-curve",
"vhosts[].noipv6",
"vhosts[].ipv6only",
"vhosts[].ssl-option-set",
"vhosts[].ssl-option-clear",
"vhosts[].mounts[].pmo[].*",
"vhosts[].headers[].*",
"vhosts[].headers[]",
"vhosts[].client-ssl-key",
"vhosts[].client-ssl-cert",
"vhosts[].client-ssl-ca",
"vhosts[].client-ssl-ciphers",
"vhosts[].onlyraw",
"vhosts[].client-cert-required",
"vhosts[].ignore-missing-cert",
"vhosts[].error-document-404",
"vhosts[].alpn",
"vhosts[].ssl-client-option-set",
"vhosts[].ssl-client-option-clear",
"vhosts[].tls13-ciphers",
"vhosts[].client-tls13-ciphers",
"vhosts[].strict-host-check",
};
enum lejp_vhost_paths {
LEJPVP,
LEJPVP_MOUNTS,
LEJPVP_NAME,
LEJPVP_PORT,
LEJPVP_INTERFACE,
LEJPVP_UNIXSKT,
LEJPVP_STS,
LEJPVP_HOST_SSL_KEY,
LEJPVP_HOST_SSL_CERT,
LEJPVP_HOST_SSL_CA,
LEJPVP_ACCESS_LOG,
LEJPVP_MOUNTPOINT,
LEJPVP_ORIGIN,
LEJPVP_MOUNT_PROTOCOL,
LEJPVP_DEFAULT,
LEJPVP_DEFAULT_AUTH_MASK,
LEJPVP_CGI_TIMEOUT,
LEJPVP_CGI_ENV,
LEJPVP_MOUNT_CACHE_MAX_AGE,
LEJPVP_MOUNT_CACHE_REUSE,
LEJPVP_MOUNT_CACHE_REVALIDATE,
LEJPVP_MOUNT_BASIC_AUTH,
LEJPVP_MOUNT_CACHE_INTERMEDIARIES,
LEJPVP_MOUNT_EXTRA_MIMETYPES,
LEJPVP_MOUNT_INTERPRET,
LEJPVP_PROTOCOL_NAME_OPT,
LEJPVP_PROTOCOL_NAME,
LEJPVP_PROTOCOL,
LEJPVP_KEEPALIVE_TIMEOUT,
LEJPVP_ENABLE_CLIENT_SSL,
LEJPVP_CIPHERS,
LEJPVP_ECDH_CURVE,
LEJPVP_NOIPV6,
LEJPVP_IPV6ONLY,
LEJPVP_SSL_OPTION_SET,
LEJPVP_SSL_OPTION_CLEAR,
LEJPVP_PMO,
LEJPVP_HEADERS_NAME,
LEJPVP_HEADERS,
LEJPVP_CLIENT_SSL_KEY,
LEJPVP_CLIENT_SSL_CERT,
LEJPVP_CLIENT_SSL_CA,
LEJPVP_CLIENT_CIPHERS,
LEJPVP_FLAG_ONLYRAW,
LEJPVP_FLAG_CLIENT_CERT_REQUIRED,
LEJPVP_IGNORE_MISSING_CERT,
LEJPVP_ERROR_DOCUMENT_404,
LEJPVP_ALPN,
LEJPVP_SSL_CLIENT_OPTION_SET,
LEJPVP_SSL_CLIENT_OPTION_CLEAR,
LEJPVP_TLS13_CIPHERS,
LEJPVP_CLIENT_TLS13_CIPHERS,
LEJPVP_FLAG_STRICT_HOST_CHECK,
};
static const char * const parser_errs[] = {
"",
"",
"No opening '{'",
"Expected closing '}'",
"Expected '\"'",
"String underrun",
"Illegal unescaped control char",
"Illegal escape format",
"Illegal hex number",
"Expected ':'",
"Illegal value start",
"Digit required after decimal point",
"Bad number format",
"Bad exponent format",
"Unknown token",
"Too many ']'",
"Mismatched ']'",
"Expected ']'",
"JSON nesting limit exceeded",
"Nesting tracking used up",
"Number too long",
"Comma or block end expected",
"Unknown",
"Parser callback errored (see earlier error)",
};
#define MAX_PLUGIN_DIRS 10
struct jpargs {
struct lws_context_creation_info *info;
struct lws_context *context;
const struct lws_protocols *protocols;
const struct lws_extension *extensions;
char *p, *end, valid;
struct lws_http_mount *head, *last;
struct lws_protocol_vhost_options *pvo;
struct lws_protocol_vhost_options *pvo_em;
struct lws_protocol_vhost_options *pvo_int;
struct lws_http_mount m;
const char **plugin_dirs;
int count_plugin_dirs;
unsigned int enable_client_ssl:1;
unsigned int fresh_mount:1;
unsigned int any_vhosts:1;
unsigned int chunk:1;
};
static void *
lwsws_align(struct jpargs *a)
{
if ((lws_intptr_t)(a->p) & 15)
a->p += 16 - ((lws_intptr_t)(a->p) & 15);
a->chunk = 0;
return a->p;
}
static int
arg_to_bool(const char *s)
{
static const char * const on[] = { "on", "yes", "true" };
int n = atoi(s);
if (n)
return 1;
for (n = 0; n < (int)LWS_ARRAY_SIZE(on); n++)
if (!strcasecmp(s, on[n]))
return 1;
return 0;
}
static signed char
lejp_globals_cb(struct lejp_ctx *ctx, char reason)
{
struct jpargs *a = (struct jpargs *)ctx->user;
struct lws_protocol_vhost_options *rej;
int n;
/* we only match on the prepared path strings */
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
/* this catches, eg, vhosts[].headers[].xxx */
if (reason == LEJPCB_VAL_STR_END &&
ctx->path_match == LWJPGP_REJECT_SERVICE_KEYWORDS_NAME + 1) {
rej = lwsws_align(a);
a->p += sizeof(*rej);
n = lejp_get_wildcard(ctx, 0, a->p, lws_ptr_diff(a->end, a->p));
rej->next = a->info->reject_service_keywords;
a->info->reject_service_keywords = rej;
rej->name = a->p;
lwsl_notice(" adding rej %s=%s\n", a->p, ctx->buf);
a->p += n - 1;
*(a->p++) = '\0';
rej->value = a->p;
rej->options = NULL;
goto dostring;
}
switch (ctx->path_match - 1) {
case LEJPGP_UID:
a->info->uid = atoi(ctx->buf);
return 0;
case LEJPGP_GID:
a->info->gid = atoi(ctx->buf);
return 0;
case LEJPGP_COUNT_THREADS:
a->info->count_threads = atoi(ctx->buf);
return 0;
case LWJPGP_INIT_SSL:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
return 0;
case LEJPGP_SERVER_STRING:
a->info->server_string = a->p;
break;
case LEJPGP_PLUGIN_DIR:
if (a->count_plugin_dirs == MAX_PLUGIN_DIRS - 1) {
lwsl_err("Too many plugin dirs\n");
return -1;
}
a->plugin_dirs[a->count_plugin_dirs++] = a->p;
break;
case LWJPGP_PINGPONG_SECS:
a->info->ws_ping_pong_interval = atoi(ctx->buf);
return 0;
case LWJPGP_TIMEOUT_SECS:
a->info->timeout_secs = atoi(ctx->buf);
return 0;
case LWJPGP_DEFAULT_ALPN:
a->info->alpn = a->p;
break;
default:
return 0;
}
dostring:
a->p += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf);
*(a->p)++ = '\0';
return 0;
}
static signed char
lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
{
struct jpargs *a = (struct jpargs *)ctx->user;
struct lws_protocol_vhost_options *pvo, *mp_cgienv, *headers;
struct lws_http_mount *m;
char *p, *p1;
int n;
#if 0
lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
for (n = 0; n < ctx->wildcount; n++)
lwsl_notice(" %d\n", ctx->wild[n]);
#endif
if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
uint32_t i[4];
const char *ss;
/* set the defaults for this vhost */
a->valid = 1;
a->head = NULL;
a->last = NULL;
i[0] = a->info->count_threads;
i[1] = a->info->options & (
LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME |
LWS_SERVER_OPTION_LIBUV |
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN |
LWS_SERVER_OPTION_LIBEVENT |
LWS_SERVER_OPTION_LIBEV
);
ss = a->info->server_string;
i[2] = a->info->ws_ping_pong_interval;
i[3] = a->info->timeout_secs;
memset(a->info, 0, sizeof(*a->info));
a->info->count_threads = i[0];
a->info->options = i[1];
a->info->server_string = ss;
a->info->ws_ping_pong_interval = i[2];
a->info->timeout_secs = i[3];
a->info->protocols = a->protocols;
a->info->extensions = a->extensions;
#if defined(LWS_WITH_TLS)
a->info->client_ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-GCM-SHA384:"
"DHE-RSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-SHA384:"
"HIGH:!aNULL:!eNULL:!EXPORT:"
"!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
"!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
"!DHE-RSA-AES128-SHA256:"
"!AES128-GCM-SHA256:"
"!AES128-SHA256:"
"!DHE-RSA-AES256-SHA256:"
"!AES256-GCM-SHA384:"
"!AES256-SHA256";
#endif
a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-GCM-SHA384:"
"DHE-RSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-SHA384:"
"HIGH:!aNULL:!eNULL:!EXPORT:"
"!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
"!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
"!DHE-RSA-AES128-SHA256:"
"!AES128-GCM-SHA256:"
"!AES128-SHA256:"
"!DHE-RSA-AES256-SHA256:"
"!AES256-GCM-SHA384:"
"!AES256-SHA256";
a->info->keepalive_timeout = 5;
}
if (reason == LEJPCB_OBJECT_START &&
ctx->path_match == LEJPVP_MOUNTS + 1) {
a->fresh_mount = 1;
memset(&a->m, 0, sizeof(a->m));
}
/* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
if (reason == LEJPCB_OBJECT_START &&
ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) {
a->pvo = lwsws_align(a);
a->p += sizeof(*a->pvo);
n = lejp_get_wildcard(ctx, 0, a->p, lws_ptr_diff(a->end, a->p));
/* ie, enable this protocol, no options yet */
a->pvo->next = a->info->pvo;
a->info->pvo = a->pvo;
a->pvo->name = a->p;
lwsl_info(" adding protocol %s\n", a->p);
a->p += n;
a->pvo->value = a->p;
a->pvo->options = NULL;
goto dostring;
}
/* this catches, eg, vhosts[].headers[].xxx */
if ((reason == LEJPCB_VAL_STR_END || reason == LEJPCB_VAL_STR_CHUNK) &&
ctx->path_match == LEJPVP_HEADERS_NAME + 1) {
if (!a->chunk) {
headers = lwsws_align(a);
a->p += sizeof(*headers);
n = lejp_get_wildcard(ctx, 0, a->p,
lws_ptr_diff(a->end, a->p));
/* ie, add this header */
headers->next = a->info->headers;
a->info->headers = headers;
headers->name = a->p;
lwsl_notice(" adding header %s=%s\n", a->p, ctx->buf);
a->p += n - 1;
*(a->p++) = ':';
if (a->p < a->end)
*(a->p++) = '\0';
else
*(a->p - 1) = '\0';
headers->value = a->p;
headers->options = NULL;
}
a->chunk = reason == LEJPCB_VAL_STR_CHUNK;
goto dostring;
}
if (reason == LEJPCB_OBJECT_END &&
(ctx->path_match == LEJPVP + 1 || !ctx->path[0]) &&
a->valid) {
struct lws_vhost *vhost;
//lwsl_notice("%s\n", ctx->path);
if (!a->info->port &&
!(a->info->options & LWS_SERVER_OPTION_UNIX_SOCK)) {
lwsl_err("Port required (eg, 443)\n");
return 1;
}
a->valid = 0;
a->info->mounts = a->head;
vhost = lws_create_vhost(a->context, a->info);
if (!vhost) {
lwsl_err("Failed to create vhost %s\n",
a->info->vhost_name);
return 1;
}
a->any_vhosts = 1;
#if defined(LWS_WITH_TLS)
if (a->enable_client_ssl) {
const char *cert_filepath = a->info->client_ssl_cert_filepath;
const char *private_key_filepath = a->info->client_ssl_private_key_filepath;
const char *ca_filepath = a->info->client_ssl_ca_filepath;
const char *cipher_list = a->info->client_ssl_cipher_list;
memset(a->info, 0, sizeof(*a->info));
a->info->client_ssl_cert_filepath = cert_filepath;
a->info->client_ssl_private_key_filepath = private_key_filepath;
a->info->client_ssl_ca_filepath = ca_filepath;
a->info->client_ssl_cipher_list = cipher_list;
a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
lws_init_vhost_client_ssl(a->info, vhost);
}
#endif
return 0;
}
if (reason == LEJPCB_OBJECT_END &&
ctx->path_match == LEJPVP_MOUNTS + 1) {
static const char * const mount_protocols[] = {
"http://",
"https://",
"file://",
"cgi://",
">http://",
">https://",
"callback://",
"gzip://",
};
if (!a->fresh_mount)
return 0;
if (!a->m.mountpoint || !a->m.origin) {
lwsl_err("mountpoint and origin required\n");
return 1;
}
lwsl_debug("adding mount %s\n", a->m.mountpoint);
m = lwsws_align(a);
memcpy(m, &a->m, sizeof(*m));
if (a->last)
a->last->mount_next = m;
for (n = 0; n < (int)LWS_ARRAY_SIZE(mount_protocols); n++)
if (!strncmp(a->m.origin, mount_protocols[n],
strlen(mount_protocols[n]))) {
lwsl_info("----%s\n", a->m.origin);
m->origin_protocol = n;
m->origin = a->m.origin +
strlen(mount_protocols[n]);
break;
}
if (n == (int)LWS_ARRAY_SIZE(mount_protocols)) {
lwsl_err("unsupported protocol:// %s\n", a->m.origin);
return 1;
}
a->p += sizeof(*m);
if (!a->head)
a->head = m;
a->last = m;
a->fresh_mount = 0;
}
/* we only match on the prepared path strings */
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
switch (ctx->path_match - 1) {
case LEJPVP_NAME:
a->info->vhost_name = a->p;
break;
case LEJPVP_PORT:
a->info->port = atoi(ctx->buf);
return 0;
case LEJPVP_INTERFACE:
a->info->iface = a->p;
break;
case LEJPVP_UNIXSKT:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_UNIX_SOCK;
else
a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK);
return 0;
case LEJPVP_STS:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_STS;
else
a->info->options &= ~(LWS_SERVER_OPTION_STS);
return 0;
case LEJPVP_HOST_SSL_KEY:
a->info->ssl_private_key_filepath = a->p;
break;
case LEJPVP_HOST_SSL_CERT:
a->info->ssl_cert_filepath = a->p;
break;
case LEJPVP_HOST_SSL_CA:
a->info->ssl_ca_filepath = a->p;
break;
case LEJPVP_ACCESS_LOG:
a->info->log_filepath = a->p;
break;
case LEJPVP_MOUNTPOINT:
a->m.mountpoint = a->p;
a->m.mountpoint_len = (unsigned char)strlen(ctx->buf);
break;
case LEJPVP_ORIGIN:
if (!strncmp(ctx->buf, "callback://", 11))
a->m.protocol = a->p + 11;
if (!a->m.origin)
a->m.origin = a->p;
break;
case LEJPVP_DEFAULT:
a->m.def = a->p;
break;
case LEJPVP_DEFAULT_AUTH_MASK:
a->m.auth_mask = atoi(ctx->buf);
return 0;
case LEJPVP_MOUNT_CACHE_MAX_AGE:
a->m.cache_max_age = atoi(ctx->buf);
return 0;
case LEJPVP_MOUNT_CACHE_REUSE:
a->m.cache_reusable = arg_to_bool(ctx->buf);
return 0;
case LEJPVP_MOUNT_CACHE_REVALIDATE:
a->m.cache_revalidate = arg_to_bool(ctx->buf);
return 0;
case LEJPVP_MOUNT_CACHE_INTERMEDIARIES:
a->m.cache_intermediaries = arg_to_bool(ctx->buf);;
return 0;
case LEJPVP_MOUNT_BASIC_AUTH:
a->m.basic_auth_login_file = a->p;
break;
case LEJPVP_CGI_TIMEOUT:
a->m.cgi_timeout = atoi(ctx->buf);
return 0;
case LEJPVP_KEEPALIVE_TIMEOUT:
a->info->keepalive_timeout = atoi(ctx->buf);
return 0;
#if defined(LWS_WITH_TLS)
case LEJPVP_CLIENT_CIPHERS:
a->info->client_ssl_cipher_list = a->p;
break;
#endif
case LEJPVP_CIPHERS:
a->info->ssl_cipher_list = a->p;
break;
case LEJPVP_TLS13_CIPHERS:
a->info->tls1_3_plus_cipher_list = a->p;
break;
case LEJPVP_CLIENT_TLS13_CIPHERS:
a->info->client_tls_1_3_plus_cipher_list = a->p;
break;
case LEJPVP_ECDH_CURVE:
a->info->ecdh_curve = a->p;
break;
case LEJPVP_PMO:
case LEJPVP_CGI_ENV:
mp_cgienv = lwsws_align(a);
a->p += sizeof(*a->m.cgienv);
mp_cgienv->next = a->m.cgienv;
a->m.cgienv = mp_cgienv;
n = lejp_get_wildcard(ctx, 0, a->p, lws_ptr_diff(a->end, a->p));
mp_cgienv->name = a->p;
a->p += n;
mp_cgienv->value = a->p;
mp_cgienv->options = NULL;
//lwsl_notice(" adding pmo / cgi-env '%s' = '%s'\n", mp_cgienv->name,
// mp_cgienv->value);
goto dostring;
case LEJPVP_PROTOCOL_NAME_OPT:
/* this catches, eg,
* vhosts[].ws-protocols[].xxx-protocol.yyy-option
* ie, these are options attached to a protocol with { }
*/
pvo = lwsws_align(a);
a->p += sizeof(*a->pvo);
n = lejp_get_wildcard(ctx, 1, a->p, lws_ptr_diff(a->end, a->p));
/* ie, enable this protocol, no options yet */
pvo->next = a->pvo->options;
a->pvo->options = pvo;
pvo->name = a->p;
a->p += n;
pvo->value = a->p;
pvo->options = NULL;
break;
case LEJPVP_MOUNT_EXTRA_MIMETYPES:
a->pvo_em = lwsws_align(a);
a->p += sizeof(*a->pvo_em);
n = lejp_get_wildcard(ctx, 0, a->p, lws_ptr_diff(a->end, a->p));
/* ie, enable this protocol, no options yet */
a->pvo_em->next = a->m.extra_mimetypes;
a->m.extra_mimetypes = a->pvo_em;
a->pvo_em->name = a->p;
lwsl_notice(" adding extra-mimetypes %s -> %s\n", a->p, ctx->buf);
a->p += n;
a->pvo_em->value = a->p;
a->pvo_em->options = NULL;
break;
case LEJPVP_MOUNT_INTERPRET:
a->pvo_int = lwsws_align(a);
a->p += sizeof(*a->pvo_int);
n = lejp_get_wildcard(ctx, 0, a->p, lws_ptr_diff(a->end, a->p));
/* ie, enable this protocol, no options yet */
a->pvo_int->next = a->m.interpret;
a->m.interpret = a->pvo_int;
a->pvo_int->name = a->p;
lwsl_notice(" adding interpret %s -> %s\n", a->p,
ctx->buf);
a->p += n;
a->pvo_int->value = a->p;
a->pvo_int->options = NULL;
break;
case LEJPVP_ENABLE_CLIENT_SSL:
a->enable_client_ssl = arg_to_bool(ctx->buf);
return 0;
#if defined(LWS_WITH_TLS)
case LEJPVP_CLIENT_SSL_KEY:
a->info->client_ssl_private_key_filepath = a->p;
break;
case LEJPVP_CLIENT_SSL_CERT:
a->info->client_ssl_cert_filepath = a->p;
break;
case LEJPVP_CLIENT_SSL_CA:
a->info->client_ssl_ca_filepath = a->p;
break;
#endif
case LEJPVP_NOIPV6:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_DISABLE_IPV6;
else
a->info->options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6);
return 0;
case LEJPVP_FLAG_ONLYRAW:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_ONLY_RAW;
else
a->info->options &= ~(LWS_SERVER_OPTION_ONLY_RAW);
return 0;
case LEJPVP_IPV6ONLY:
a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY;
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE;
else
a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
return 0;
case LEJPVP_FLAG_CLIENT_CERT_REQUIRED:
if (arg_to_bool(ctx->buf))
a->info->options |=
LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
return 0;
case LEJPVP_IGNORE_MISSING_CERT:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_IGNORE_MISSING_CERT;
else
a->info->options &= ~(LWS_SERVER_OPTION_IGNORE_MISSING_CERT);
return 0;
case LEJPVP_FLAG_STRICT_HOST_CHECK:
if (arg_to_bool(ctx->buf))
a->info->options |=
LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK;
else
a->info->options &=
~(LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK);
return 0;
case LEJPVP_ERROR_DOCUMENT_404:
a->info->error_document_404 = a->p;
break;
case LEJPVP_SSL_OPTION_SET:
a->info->ssl_options_set |= atol(ctx->buf);
return 0;
case LEJPVP_SSL_OPTION_CLEAR:
a->info->ssl_options_clear |= atol(ctx->buf);
return 0;
case LEJPVP_SSL_CLIENT_OPTION_SET:
a->info->ssl_client_options_set |= atol(ctx->buf);
return 0;
case LEJPVP_SSL_CLIENT_OPTION_CLEAR:
a->info->ssl_client_options_clear |= atol(ctx->buf);
return 0;
case LEJPVP_ALPN:
a->info->alpn = a->p;
break;
default:
return 0;
}
dostring:
p = ctx->buf;
p[LEJP_STRING_CHUNK] = '\0';
p1 = strstr(p, ESC_INSTALL_DATADIR);
if (p1) {
n = lws_ptr_diff(p1, p);
if (n > a->end - a->p)
n = lws_ptr_diff(a->end, a->p);
lws_strncpy(a->p, p, n + 1);
a->p += n;
a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR);
p += n + strlen(ESC_INSTALL_DATADIR);
}
a->p += lws_snprintf(a->p, a->end - a->p, "%s", p);
if (reason == LEJPCB_VAL_STR_END)
*(a->p)++ = '\0';
return 0;
}
/*
* returns 0 = OK, 1 = can't open, 2 = parsing error
*/
static int
lwsws_get_config(void *user, const char *f, const char * const *paths,
int count_paths, lejp_callback cb)
{
unsigned char buf[128];
struct lejp_ctx ctx;
int n, m, fd;
fd = lws_open(f, O_RDONLY);
if (fd < 0) {
lwsl_err("Cannot open %s\n", f);
return 2;
}
lwsl_info("%s: %s\n", __func__, f);
lejp_construct(&ctx, cb, user, paths, count_paths);
do {
n = read(fd, buf, sizeof(buf));
if (!n)
break;
m = (int)(signed char)lejp_parse(&ctx, buf, n);
} while (m == LEJP_CONTINUE);
close(fd);
n = ctx.line;
lejp_destruct(&ctx);
if (m < 0) {
lwsl_err("%s(%u): parsing error %d: %s\n", f, n, m,
parser_errs[-m]);
return 2;
}
return 0;
}
#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 0
static int
lwsws_get_config_d(void *user, const char *d, const char * const *paths,
int count_paths, lejp_callback cb)
{
uv_dirent_t dent;
uv_fs_t req;
char path[256];
int ret = 0, ir;
uv_loop_t loop;
ir = uv_loop_init(&loop);
if (ir) {
lwsl_err("%s: loop init failed %d\n", __func__, ir);
}
if (!uv_fs_scandir(&loop, &req, d, 0, NULL)) {
lwsl_err("Scandir on %s failed\n", d);
return 2;
}
while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
lws_snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name);
ret = lwsws_get_config(user, path, paths, count_paths, cb);
if (ret)
goto bail;
}
bail:
uv_fs_req_cleanup(&req);
while (uv_loop_close(&loop))
;
return ret;
}
#else
#ifndef _WIN32
static int filter(const struct dirent *ent)
{
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
return 0;
return 1;
}
#endif
static int
lwsws_get_config_d(void *user, const char *d, const char * const *paths,
int count_paths, lejp_callback cb)
{
#if !defined(_WIN32) && !defined(LWS_WITH_ESP32)
struct dirent **namelist;
char path[256];
int n, i, ret = 0;
n = scandir(d, &namelist, filter, alphasort);
if (n < 0) {
lwsl_err("Scandir on %s failed\n", d);
return 1;
}
for (i = 0; i < n; i++) {
if (strchr(namelist[i]->d_name, '~'))
goto skip;
lws_snprintf(path, sizeof(path) - 1, "%s/%s", d,
namelist[i]->d_name);
ret = lwsws_get_config(user, path, paths, count_paths, cb);
if (ret) {
while (i++ < n)
free(namelist[i]);
goto bail;
}
skip:
free(namelist[i]);
}
bail:
free(namelist);
return ret;
#else
return 0;
#endif
}
#endif
int
lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d,
char **cs, int *len)
{
struct jpargs a;
const char * const *old = info->plugin_dirs;
char dd[128];
memset(&a, 0, sizeof(a));
a.info = info;
a.p = *cs;
a.end = (a.p + *len) - 1;
a.valid = 0;
lwsws_align(&a);
info->plugin_dirs = (void *)a.p;
a.plugin_dirs = (void *)a.p; /* writeable version */
a.p += MAX_PLUGIN_DIRS * sizeof(void *);
/* copy any default paths */
while (old && *old) {
a.plugin_dirs[a.count_plugin_dirs++] = *old;
old++;
}
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
if (lwsws_get_config(&a, dd, paths_global,
LWS_ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
return 1;
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
if (lwsws_get_config_d(&a, dd, paths_global,
LWS_ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
return 1;
a.plugin_dirs[a.count_plugin_dirs] = NULL;
*cs = a.p;
*len = lws_ptr_diff(a.end, a.p);
return 0;
}
int
lwsws_get_config_vhosts(struct lws_context *context,
struct lws_context_creation_info *info, const char *d,
char **cs, int *len)
{
struct jpargs a;
char dd[128];
memset(&a, 0, sizeof(a));
a.info = info;
a.p = *cs;
a.end = a.p + *len;
a.valid = 0;
a.context = context;
a.protocols = info->protocols;
a.extensions = info->extensions;
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
if (lwsws_get_config(&a, dd, paths_vhosts,
LWS_ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
return 1;
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
if (lwsws_get_config_d(&a, dd, paths_vhosts,
LWS_ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
return 1;
*cs = a.p;
*len = lws_ptr_diff(a.end, a.p);
if (!a.any_vhosts) {
lwsl_err("Need at least one vhost\n");
return 1;
}
// lws_finalize_startup(context);
return 0;
}