1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-16 00:00:07 +01:00
libwebsockets/lwsws/conf.c
Andy Green 7ca7443ac2 lwsws cgi integration
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-09 12:24:36 +08:00

506 lines
11 KiB
C

/*
* libwebsockets web server application
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU 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
* General Public License for more details.
*
* You should have received a copy of the GNU 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 "lwsws.h"
static const char * const paths_global[] = {
"global.uid",
"global.gid",
"global.interface",
"global.count-threads",
"global.init-ssl",
};
enum lejp_global_paths {
LEJPGP_UID,
LEJPGP_GID,
LEJPGP_INTERFACE,
LEJPGP_COUNT_THREADS,
LWJPGP_INIT_SSL,
};
static const char * const paths_vhosts[] = {
"vhosts[]",
"vhosts[].mounts[]",
"vhosts[].name",
"vhosts[].port",
"vhosts[].host-ssl-key",
"vhosts[].host-ssl-cert",
"vhosts[].host-ssl-ca",
"vhosts[].mounts[].mountpoint",
"vhosts[].mounts[].origin",
"vhosts[].mounts[].default",
"vhosts[].mounts[].cgi-env[].*",
"vhosts[].ws-protocols[].*.*",
"vhosts[].ws-protocols[].*",
"vhosts[].ws-protocols[]",
};
enum lejp_vhost_paths {
LEJPVP,
LEJPVP_MOUNTS,
LEJPVP_NAME,
LEJPVP_PORT,
LEJPVP_HOST_SSL_KEY,
LEJPVP_HOST_SSL_CERT,
LEJPVP_HOST_SSL_CA,
LEJPVP_MOUNTPOINT,
LEJPVP_ORIGIN,
LEJPVP_DEFAULT,
LEJPVP_CGI_ENV,
LEJPVP_PROTOCOL_NAME_OPT,
LEJPVP_PROTOCOL_NAME,
LEJPVP_PROTOCOL,
};
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;
char *mountpoint, *origin, *def;
struct lws_protocol_vhost_options *pvo;
struct lws_protocol_vhost_options *mp_cgienv;
};
static void *
lwsws_align(struct jpargs *a)
{
if ((unsigned long)(a->p) & 15)
a->p += 16 - ((unsigned long)(a->p) & 15);
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 < ARRAY_SIZE(on); n++)
if (!strcasecmp(s, on[n]))
return 1;
return 0;
}
static char
lejp_globals_cb(struct lejp_ctx *ctx, char reason)
{
struct jpargs *a = (struct jpargs *)ctx->user;
/* 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 LEJPGP_UID:
a->info->uid = atoi(ctx->buf);
return 0;
case LEJPGP_GID:
a->info->gid = atoi(ctx->buf);
return 0;
case LEJPGP_INTERFACE:
a->info->iface = a->p;
break;
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;
default:
return 0;
}
a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
return 0;
}
static 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;
struct lws_http_mount *m;
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) {
/* set the defaults for this vhost */
a->valid = 1;
a->head = NULL;
a->last = NULL;
a->info->port = 0;
a->info->iface = NULL;
a->info->protocols = a->protocols;
a->info->extensions = a->extensions;
a->info->ssl_cert_filepath = NULL;
a->info->ssl_private_key_filepath = NULL;
a->info->ssl_ca_filepath = NULL;
a->info->timeout_secs = 5;
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->pvo = NULL;
}
if (reason == LEJPCB_OBJECT_START &&
ctx->path_match == LEJPVP_MOUNTS + 1) {
a->mountpoint = NULL;
a->origin = NULL;
a->def = NULL;
a->mp_cgienv = NULL;
}
/* 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, 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_err("adding %s\n", a->p);
a->p += n;
a->pvo->value = a->p;
a->pvo->options = NULL;
a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
*(a->p)++ = '\0';
}
if (reason == LEJPCB_OBJECT_END &&
(ctx->path_match == LEJPVP + 1 || !ctx->path[0]) &&
a->valid) {
//lwsl_notice("%s\n", ctx->path);
if (!a->info->port) {
lwsl_err("Port required (eg, 443)");
return 1;
}
a->valid = 0;
if (!lws_create_vhost(a->context, a->info, a->head)) {
lwsl_err("Failed to create vhost %s\n",
a->info->vhost_name);
return 1;
}
return 0;
}
if (reason == LEJPCB_OBJECT_END &&
ctx->path_match == LEJPVP_MOUNTS + 1) {
if (!a->mountpoint || !a->origin) {
lwsl_err("mountpoint and origin required\n");
return 1;
}
n = lws_write_http_mount(a->last, &m, a->p, a->mountpoint,
a->origin, a->def, a->mp_cgienv);
if (!n)
return 1;
a->p += n;
if (!a->head)
a->head = m;
a->last = m;
}
/* 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_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_MOUNTPOINT:
a->mountpoint = a->p;
break;
case LEJPVP_ORIGIN:
a->origin = a->p;
break;
case LEJPVP_DEFAULT:
a->def = a->p;
break;
case LEJPVP_CGI_ENV:
mp_cgienv = lwsws_align(a);
a->p += sizeof(*a->mp_cgienv);
mp_cgienv->next = a->mp_cgienv;
a->mp_cgienv = mp_cgienv;
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
mp_cgienv->name = a->p;
a->p += n;
mp_cgienv->value = a->p;
mp_cgienv->options = NULL;
a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
*(a->p)++ = '\0';
lwsl_notice(" adding cgi-env '%s' = '%s'\n", mp_cgienv->name,
mp_cgienv->value);
break;
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, 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;
a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
*(a->p)++ = '\0';
break;
default:
return 0;
}
a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
*(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 = open(f, O_RDONLY);
if (fd < 0) {
lwsl_err("Cannot open %s\n", f);
return 1;
}
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\n", f, n, m);
return 2;
}
return 0;
}
#if defined(LWS_USE_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;
uv_loop_t loop;
uv_loop_init(&loop);
if (!uv_fs_scandir(&loop, &req, d, 0, NULL)) {
lwsl_err("Scandir on %s failed\n", d);
return 1;
}
while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
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);
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)
{
#ifndef _WIN32
struct dirent **namelist;
char path[256];
int n, i, ret = 0;
n = scandir(d, &namelist, filter, alphasort);
if (n < 0) {
lwsl_err("Scandir on %d failed\n", d);
}
for (i = 0; i < n; i++) {
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;
}
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;
a.info = info;
a.p = *cs;
a.end = (a.p + *len) - 1;
a.valid = 0;
if (lwsws_get_config(&a, "/etc/lwsws/conf", paths_global,
ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
return 1;
if (lwsws_get_config_d(&a, d, paths_global,
ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
return 1;
*cs = a.p;
*len = 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;
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;
if (lwsws_get_config(&a, "/etc/lwsws/conf", paths_vhosts,
ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
return 1;
if (lwsws_get_config_d(&a, d, paths_vhosts,
ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
return 1;
*cs = a.p;
*len = a.end - a.p;
lws_finalize_startup(context);
return 0;
}