blob: 5a79d9f051bf5a811841d3da21723166d39e6bfb [file] [log] [blame]
Andy Greencd0c6962016-03-28 10:12:37 +08001/*
2 * libwebsockets web server application
3 *
4 * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
5 *
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.
10 *
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.
15 *
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,
19 * MA 02110-1301 USA
20 */
21
22#include "lwsws.h"
23
24static const char * const paths_global[] = {
25 "global.uid",
26 "global.gid",
Andy Greencd0c6962016-03-28 10:12:37 +080027 "global.count-threads",
28 "global.init-ssl",
29};
30
31enum lejp_global_paths {
32 LEJPGP_UID,
33 LEJPGP_GID,
Andy Greencd0c6962016-03-28 10:12:37 +080034 LEJPGP_COUNT_THREADS,
35 LWJPGP_INIT_SSL,
36};
37
38static const char * const paths_vhosts[] = {
39 "vhosts[]",
40 "vhosts[].mounts[]",
41 "vhosts[].name",
42 "vhosts[].port",
Andy Green1042b342016-04-14 11:50:05 +080043 "vhosts[].interface",
Andy Greencd0c6962016-03-28 10:12:37 +080044 "vhosts[].host-ssl-key",
45 "vhosts[].host-ssl-cert",
46 "vhosts[].host-ssl-ca",
47 "vhosts[].mounts[].mountpoint",
48 "vhosts[].mounts[].origin",
Andy Green37098ae2016-04-08 13:25:34 +080049 "vhosts[].mounts[].default",
Andy Greenf5efa742016-04-13 11:42:53 +080050 "vhosts[].mounts[].cgi-timeout",
Andy Greena5e73a12016-04-13 11:49:07 +080051 "vhosts[].mounts[].cgi-env[].*",
Andy Green37098ae2016-04-08 13:25:34 +080052 "vhosts[].ws-protocols[].*.*",
53 "vhosts[].ws-protocols[].*",
Andy Greend459a6f2016-04-08 18:30:45 +080054 "vhosts[].ws-protocols[]",
Andy Greenb46e4a82016-04-12 16:26:03 +080055 "vhosts[].keepalive_timeout",
Andy Greencd0c6962016-03-28 10:12:37 +080056};
57
58enum lejp_vhost_paths {
59 LEJPVP,
60 LEJPVP_MOUNTS,
61 LEJPVP_NAME,
62 LEJPVP_PORT,
Andy Green1042b342016-04-14 11:50:05 +080063 LEJPVP_INTERFACE,
Andy Greencd0c6962016-03-28 10:12:37 +080064 LEJPVP_HOST_SSL_KEY,
65 LEJPVP_HOST_SSL_CERT,
66 LEJPVP_HOST_SSL_CA,
67 LEJPVP_MOUNTPOINT,
68 LEJPVP_ORIGIN,
69 LEJPVP_DEFAULT,
Andy Greenf5efa742016-04-13 11:42:53 +080070 LEJPVP_CGI_TIMEOUT,
Andy Greena5e73a12016-04-13 11:49:07 +080071 LEJPVP_CGI_ENV,
Andy Green37098ae2016-04-08 13:25:34 +080072 LEJPVP_PROTOCOL_NAME_OPT,
73 LEJPVP_PROTOCOL_NAME,
74 LEJPVP_PROTOCOL,
Andy Greenb46e4a82016-04-12 16:26:03 +080075 LEJPVP_KEEPALIVE_TIMEOUT,
Andy Greencd0c6962016-03-28 10:12:37 +080076};
77
78struct jpargs {
79 struct lws_context_creation_info *info;
80 struct lws_context *context;
81 const struct lws_protocols *protocols;
82 const struct lws_extension *extensions;
83 char *p, *end, valid;
84 struct lws_http_mount *head, *last;
85 char *mountpoint, *origin, *def;
Andy Green37098ae2016-04-08 13:25:34 +080086 struct lws_protocol_vhost_options *pvo;
Andy Greena5e73a12016-04-13 11:49:07 +080087 struct lws_protocol_vhost_options *mp_cgienv;
Andy Greenf5efa742016-04-13 11:42:53 +080088 int cgi_timeout;
Andy Greencd0c6962016-03-28 10:12:37 +080089};
90
Andy Green37098ae2016-04-08 13:25:34 +080091static void *
92lwsws_align(struct jpargs *a)
93{
94 if ((unsigned long)(a->p) & 15)
95 a->p += 16 - ((unsigned long)(a->p) & 15);
96
97 return a->p;
98}
99
Andy Greencd0c6962016-03-28 10:12:37 +0800100static int arg_to_bool(const char *s)
101{
102 static const char * const on[] = { "on", "yes", "true" };
103 int n = atoi(s);
104
105 if (n)
106 return 1;
107
108 for (n = 0; n < ARRAY_SIZE(on); n++)
109 if (!strcasecmp(s, on[n]))
110 return 1;
111
112 return 0;
113}
114
115static char
116lejp_globals_cb(struct lejp_ctx *ctx, char reason)
117{
118 struct jpargs *a = (struct jpargs *)ctx->user;
119
120 /* we only match on the prepared path strings */
121 if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
122 return 0;
123
124 switch (ctx->path_match - 1) {
125 case LEJPGP_UID:
126 a->info->uid = atoi(ctx->buf);
127 return 0;
128 case LEJPGP_GID:
129 a->info->gid = atoi(ctx->buf);
130 return 0;
Andy Greencd0c6962016-03-28 10:12:37 +0800131 case LEJPGP_COUNT_THREADS:
132 a->info->count_threads = atoi(ctx->buf);
133 return 0;
134 case LWJPGP_INIT_SSL:
135 if (arg_to_bool(ctx->buf))
136 a->info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
137 return 0;
138
139 default:
140 return 0;
141 }
142
143 a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
144
145 return 0;
146}
147
148static char
149lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
150{
151 struct jpargs *a = (struct jpargs *)ctx->user;
Andy Greena5e73a12016-04-13 11:49:07 +0800152 struct lws_protocol_vhost_options *pvo, *mp_cgienv;
Andy Greencd0c6962016-03-28 10:12:37 +0800153 struct lws_http_mount *m;
154 int n;
155
Andy Greena5e73a12016-04-13 11:49:07 +0800156#if 0
157 lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
158 for (n = 0; n < ctx->wildcount; n++)
159 lwsl_notice(" %d\n", ctx->wild[n]);
160#endif
Andy Green37098ae2016-04-08 13:25:34 +0800161
Andy Greencd0c6962016-03-28 10:12:37 +0800162 if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
Andy Green37098ae2016-04-08 13:25:34 +0800163 /* set the defaults for this vhost */
Andy Greencd0c6962016-03-28 10:12:37 +0800164 a->valid = 1;
165 a->head = NULL;
166 a->last = NULL;
167 a->info->port = 0;
168 a->info->iface = NULL;
169 a->info->protocols = a->protocols;
170 a->info->extensions = a->extensions;
171 a->info->ssl_cert_filepath = NULL;
172 a->info->ssl_private_key_filepath = NULL;
173 a->info->ssl_ca_filepath = NULL;
174 a->info->timeout_secs = 5;
175 a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
176 "ECDHE-RSA-AES256-GCM-SHA384:"
177 "DHE-RSA-AES256-GCM-SHA384:"
178 "ECDHE-RSA-AES256-SHA384:"
179 "HIGH:!aNULL:!eNULL:!EXPORT:"
180 "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
181 "!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
182 "!DHE-RSA-AES128-SHA256:"
183 "!AES128-GCM-SHA256:"
184 "!AES128-SHA256:"
185 "!DHE-RSA-AES256-SHA256:"
186 "!AES256-GCM-SHA384:"
187 "!AES256-SHA256";
Andy Green37098ae2016-04-08 13:25:34 +0800188 a->info->pvo = NULL;
Andy Greenb46e4a82016-04-12 16:26:03 +0800189 a->info->keepalive_timeout = 60;
Andy Greencd0c6962016-03-28 10:12:37 +0800190 }
191
192 if (reason == LEJPCB_OBJECT_START &&
193 ctx->path_match == LEJPVP_MOUNTS + 1) {
194 a->mountpoint = NULL;
195 a->origin = NULL;
196 a->def = NULL;
Andy Greena5e73a12016-04-13 11:49:07 +0800197 a->mp_cgienv = NULL;
Andy Greenf5efa742016-04-13 11:42:53 +0800198 a->cgi_timeout = 0;
Andy Greencd0c6962016-03-28 10:12:37 +0800199 }
200
Andy Green37098ae2016-04-08 13:25:34 +0800201 /* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
202 if (reason == LEJPCB_OBJECT_START &&
203 ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) {
204 a->pvo = lwsws_align(a);
205 a->p += sizeof(*a->pvo);
206
207 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
208 /* ie, enable this protocol, no options yet */
209 a->pvo->next = a->info->pvo;
210 a->info->pvo = a->pvo;
211 a->pvo->name = a->p;
212 lwsl_err("adding %s\n", a->p);
213 a->p += n;
214 a->pvo->value = a->p;
215 a->pvo->options = NULL;
216 a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
217 *(a->p)++ = '\0';
218 }
219
Andy Greencd0c6962016-03-28 10:12:37 +0800220 if (reason == LEJPCB_OBJECT_END &&
221 (ctx->path_match == LEJPVP + 1 || !ctx->path[0]) &&
222 a->valid) {
223
224 //lwsl_notice("%s\n", ctx->path);
225 if (!a->info->port) {
226 lwsl_err("Port required (eg, 443)");
227 return 1;
228 }
229 a->valid = 0;
230
231 if (!lws_create_vhost(a->context, a->info, a->head)) {
232 lwsl_err("Failed to create vhost %s\n",
233 a->info->vhost_name);
234 return 1;
235 }
236
237 return 0;
238 }
239
240 if (reason == LEJPCB_OBJECT_END &&
241 ctx->path_match == LEJPVP_MOUNTS + 1) {
242 if (!a->mountpoint || !a->origin) {
243 lwsl_err("mountpoint and origin required\n");
244 return 1;
245 }
246
247 n = lws_write_http_mount(a->last, &m, a->p, a->mountpoint,
Andy Greenf5efa742016-04-13 11:42:53 +0800248 a->origin, a->def, a->mp_cgienv,
249 a->cgi_timeout);
Andy Greencd0c6962016-03-28 10:12:37 +0800250 if (!n)
251 return 1;
252 a->p += n;
253 if (!a->head)
254 a->head = m;
255
256 a->last = m;
257 }
258
259 /* we only match on the prepared path strings */
260 if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
261 return 0;
262
263 switch (ctx->path_match - 1) {
264 case LEJPVP_NAME:
265 a->info->vhost_name = a->p;
266 break;
267 case LEJPVP_PORT:
268 a->info->port = atoi(ctx->buf);
269 return 0;
Andy Green1042b342016-04-14 11:50:05 +0800270 case LEJPVP_INTERFACE:
271 a->info->iface = a->p;
272 break;
Andy Greencd0c6962016-03-28 10:12:37 +0800273 case LEJPVP_HOST_SSL_KEY:
274 a->info->ssl_private_key_filepath = a->p;
275 break;
276 case LEJPVP_HOST_SSL_CERT:
277 a->info->ssl_cert_filepath = a->p;
278 break;
279 case LEJPVP_HOST_SSL_CA:
280 a->info->ssl_ca_filepath = a->p;
281 break;
282 case LEJPVP_MOUNTPOINT:
283 a->mountpoint = a->p;
284 break;
285 case LEJPVP_ORIGIN:
286 a->origin = a->p;
287 break;
288 case LEJPVP_DEFAULT:
289 a->def = a->p;
290 break;
Andy Greenf5efa742016-04-13 11:42:53 +0800291 case LEJPVP_CGI_TIMEOUT:
292 a->cgi_timeout = atoi(ctx->buf);
293 return 0;
Andy Greenb46e4a82016-04-12 16:26:03 +0800294 case LEJPVP_KEEPALIVE_TIMEOUT:
295 a->info->keepalive_timeout = atoi(ctx->buf);
296 return 0;
Andy Greena5e73a12016-04-13 11:49:07 +0800297 case LEJPVP_CGI_ENV:
298 mp_cgienv = lwsws_align(a);
299 a->p += sizeof(*a->mp_cgienv);
Andy Green37098ae2016-04-08 13:25:34 +0800300
Andy Greena5e73a12016-04-13 11:49:07 +0800301 mp_cgienv->next = a->mp_cgienv;
302 a->mp_cgienv = mp_cgienv;
303
304 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
305 mp_cgienv->name = a->p;
306 a->p += n;
307 mp_cgienv->value = a->p;
308 mp_cgienv->options = NULL;
309 a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
310 *(a->p)++ = '\0';
311
312 lwsl_notice(" adding cgi-env '%s' = '%s'\n", mp_cgienv->name,
313 mp_cgienv->value);
314
315 break;
Andy Green37098ae2016-04-08 13:25:34 +0800316 case LEJPVP_PROTOCOL_NAME_OPT:
317 /* this catches, eg,
318 * vhosts[].ws-protocols[].xxx-protocol.yyy-option
319 * ie, these are options attached to a protocol with { }
320 */
321 pvo = lwsws_align(a);
322 a->p += sizeof(*a->pvo);
323
324 n = lejp_get_wildcard(ctx, 1, a->p, a->end - a->p);
325 /* ie, enable this protocol, no options yet */
326 pvo->next = a->pvo->options;
327 a->pvo->options = pvo;
328 pvo->name = a->p;
329 a->p += n;
330 pvo->value = a->p;
331 pvo->options = NULL;
332 a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
333 *(a->p)++ = '\0';
334 break;
335
Andy Greencd0c6962016-03-28 10:12:37 +0800336 default:
337 return 0;
338 }
339
340 a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
341 *(a->p)++ = '\0';
342
343 return 0;
344}
345
346/*
347 * returns 0 = OK, 1 = can't open, 2 = parsing error
348 */
349
350static int
351lwsws_get_config(void *user, const char *f, const char * const *paths,
352 int count_paths, lejp_callback cb)
353{
354 unsigned char buf[128];
355 struct lejp_ctx ctx;
356 int n, m, fd;
357
358 fd = open(f, O_RDONLY);
359 if (fd < 0) {
360 lwsl_err("Cannot open %s\n", f);
361 return 1;
362 }
363 lwsl_info("%s: %s\n", __func__, f);
364 lejp_construct(&ctx, cb, user, paths, count_paths);
365
366 do {
367 n = read(fd, buf, sizeof(buf));
368 if (!n)
369 break;
370
Andy Green6e7b79b2016-04-09 09:32:01 +0800371 m = (int)(signed char)lejp_parse(&ctx, buf, n);
Andy Greencd0c6962016-03-28 10:12:37 +0800372 } while (m == LEJP_CONTINUE);
373
374 close(fd);
375 n = ctx.line;
376 lejp_destruct(&ctx);
377
378 if (m < 0) {
379 lwsl_err("%s(%u): parsing error %d\n", f, n, m);
380 return 2;
381 }
382
383 return 0;
384}
385
Andy Green0a183542016-04-09 07:22:40 +0800386#if defined(LWS_USE_LIBUV) && UV_VERSION_MAJOR > 0
387
388static int
389lwsws_get_config_d(void *user, const char *d, const char * const *paths,
390 int count_paths, lejp_callback cb)
391{
392 uv_dirent_t dent;
393 uv_fs_t req;
394 char path[256];
395 int ret = 0;
396 uv_loop_t loop;
397
398 uv_loop_init(&loop);
399
400 if (!uv_fs_scandir(&loop, &req, d, 0, NULL)) {
401 lwsl_err("Scandir on %s failed\n", d);
402 return 1;
403 }
404
405 while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
406 snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name);
407 ret = lwsws_get_config(user, path, paths, count_paths, cb);
408 if (ret)
409 goto bail;
410 }
411
412bail:
413 uv_fs_req_cleanup(&req);
414 uv_loop_close(&loop);
415
416 return ret;
417}
418
419#else
420
Andy Greencd0c6962016-03-28 10:12:37 +0800421#ifndef _WIN32
422static int filter(const struct dirent *ent)
423{
424 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
425 return 0;
426
427 return 1;
428}
429#endif
430
431static int
432lwsws_get_config_d(void *user, const char *d, const char * const *paths,
433 int count_paths, lejp_callback cb)
434{
435#ifndef _WIN32
436 struct dirent **namelist;
437 char path[256];
438 int n, i, ret = 0;
439
440 n = scandir(d, &namelist, filter, alphasort);
441 if (n < 0) {
442 lwsl_err("Scandir on %d failed\n", d);
443 }
444
445 for (i = 0; i < n; i++) {
446 snprintf(path, sizeof(path) - 1, "%s/%s", d,
447 namelist[i]->d_name);
448 ret = lwsws_get_config(user, path, paths, count_paths, cb);
449 if (ret) {
450 while (i++ < n)
451 free(namelist[i]);
452 goto bail;
453 }
454 free(namelist[i]);
455 }
456
457bail:
458 free(namelist);
459
460 return ret;
461#else
462 return 0;
463#endif
464}
465
Andy Green0a183542016-04-09 07:22:40 +0800466#endif
467
Andy Greencd0c6962016-03-28 10:12:37 +0800468int
469lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d,
470 char **cs, int *len)
471{
472 struct jpargs a;
473
474 a.info = info;
475 a.p = *cs;
Andy Greena5e73a12016-04-13 11:49:07 +0800476 a.end = (a.p + *len) - 1;
Andy Greencd0c6962016-03-28 10:12:37 +0800477 a.valid = 0;
478
479 if (lwsws_get_config(&a, "/etc/lwsws/conf", paths_global,
480 ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
481 return 1;
482 if (lwsws_get_config_d(&a, d, paths_global,
483 ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
484 return 1;
485
486 *cs = a.p;
487 *len = a.end - a.p;
488
489 return 0;
490}
491
492int
493lwsws_get_config_vhosts(struct lws_context *context,
494 struct lws_context_creation_info *info, const char *d,
495 char **cs, int *len)
496{
497 struct jpargs a;
498
499 a.info = info;
500 a.p = *cs;
501 a.end = a.p + *len;
502 a.valid = 0;
503 a.context = context;
504 a.protocols = info->protocols;
505 a.extensions = info->extensions;
506
507 if (lwsws_get_config(&a, "/etc/lwsws/conf", paths_vhosts,
508 ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
509 return 1;
510 if (lwsws_get_config_d(&a, d, paths_vhosts,
511 ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
512 return 1;
513
514 *cs = a.p;
515 *len = a.end - a.p;
516
517 lws_finalize_startup(context);
518
519 return 0;
520}