lwsws cgi integration
[platform/upstream/libwebsockets.git] / lwsws / conf.c
1 /*
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
24 static const char * const paths_global[] = {
25         "global.uid",
26         "global.gid",
27         "global.interface",
28         "global.count-threads",
29         "global.init-ssl",
30 };
31
32 enum lejp_global_paths {
33         LEJPGP_UID,
34         LEJPGP_GID,
35         LEJPGP_INTERFACE,
36         LEJPGP_COUNT_THREADS,
37         LWJPGP_INIT_SSL,
38 };
39
40 static const char * const paths_vhosts[] = {
41         "vhosts[]",
42         "vhosts[].mounts[]",
43         "vhosts[].name",
44         "vhosts[].port",
45         "vhosts[].host-ssl-key",
46         "vhosts[].host-ssl-cert",
47         "vhosts[].host-ssl-ca",
48         "vhosts[].mounts[].mountpoint",
49         "vhosts[].mounts[].origin",
50         "vhosts[].mounts[].default",
51         "vhosts[].mounts[].cgi-env[].*",
52         "vhosts[].ws-protocols[].*.*",
53         "vhosts[].ws-protocols[].*",
54         "vhosts[].ws-protocols[]",
55 };
56
57 enum lejp_vhost_paths {
58         LEJPVP,
59         LEJPVP_MOUNTS,
60         LEJPVP_NAME,
61         LEJPVP_PORT,
62         LEJPVP_HOST_SSL_KEY,
63         LEJPVP_HOST_SSL_CERT,
64         LEJPVP_HOST_SSL_CA,
65         LEJPVP_MOUNTPOINT,
66         LEJPVP_ORIGIN,
67         LEJPVP_DEFAULT,
68         LEJPVP_CGI_ENV,
69         LEJPVP_PROTOCOL_NAME_OPT,
70         LEJPVP_PROTOCOL_NAME,
71         LEJPVP_PROTOCOL,
72 };
73
74 struct jpargs {
75         struct lws_context_creation_info *info;
76         struct lws_context *context;
77         const struct lws_protocols *protocols;
78         const struct lws_extension *extensions;
79         char *p, *end, valid;
80         struct lws_http_mount *head, *last;
81         char *mountpoint, *origin, *def;
82         struct lws_protocol_vhost_options *pvo;
83         struct lws_protocol_vhost_options *mp_cgienv;
84 };
85
86 static void *
87 lwsws_align(struct jpargs *a)
88 {
89         if ((unsigned long)(a->p) & 15)
90                 a->p += 16 - ((unsigned long)(a->p) & 15);
91
92         return a->p;
93 }
94
95 static int arg_to_bool(const char *s)
96 {
97         static const char * const on[] = { "on", "yes", "true" };
98         int n = atoi(s);
99
100         if (n)
101                 return 1;
102
103         for (n = 0; n < ARRAY_SIZE(on); n++)
104                 if (!strcasecmp(s, on[n]))
105                         return 1;
106
107         return 0;
108 }
109
110 static char
111 lejp_globals_cb(struct lejp_ctx *ctx, char reason)
112 {
113         struct jpargs *a = (struct jpargs *)ctx->user;
114
115         /* we only match on the prepared path strings */
116         if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
117                 return 0;
118
119         switch (ctx->path_match - 1) {
120         case LEJPGP_UID:
121                 a->info->uid = atoi(ctx->buf);
122                 return 0;
123         case LEJPGP_GID:
124                 a->info->gid = atoi(ctx->buf);
125                 return 0;
126         case LEJPGP_INTERFACE:
127                 a->info->iface = a->p;
128                 break;
129         case LEJPGP_COUNT_THREADS:
130                 a->info->count_threads = atoi(ctx->buf);
131                 return 0;
132         case LWJPGP_INIT_SSL:
133                 if (arg_to_bool(ctx->buf))
134                         a->info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
135                 return 0;
136
137         default:
138                 return 0;
139         }
140
141         a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
142
143         return 0;
144 }
145
146 static char
147 lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
148 {
149         struct jpargs *a = (struct jpargs *)ctx->user;
150         struct lws_protocol_vhost_options *pvo, *mp_cgienv;
151         struct lws_http_mount *m;
152         int n;
153
154 #if 0
155         lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
156         for (n = 0; n < ctx->wildcount; n++)
157                 lwsl_notice("    %d\n", ctx->wild[n]);
158 #endif
159
160         if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
161                 /* set the defaults for this vhost */
162                 a->valid = 1;
163                 a->head = NULL;
164                 a->last = NULL;
165                 a->info->port = 0;
166                 a->info->iface = NULL;
167                 a->info->protocols = a->protocols;
168                 a->info->extensions = a->extensions;
169                 a->info->ssl_cert_filepath = NULL;
170                 a->info->ssl_private_key_filepath = NULL;
171                 a->info->ssl_ca_filepath = NULL;
172                 a->info->timeout_secs = 5;
173                 a->info->ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
174                                        "ECDHE-RSA-AES256-GCM-SHA384:"
175                                        "DHE-RSA-AES256-GCM-SHA384:"
176                                        "ECDHE-RSA-AES256-SHA384:"
177                                        "HIGH:!aNULL:!eNULL:!EXPORT:"
178                                        "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
179                                        "!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
180                                        "!DHE-RSA-AES128-SHA256:"
181                                        "!AES128-GCM-SHA256:"
182                                        "!AES128-SHA256:"
183                                        "!DHE-RSA-AES256-SHA256:"
184                                        "!AES256-GCM-SHA384:"
185                                        "!AES256-SHA256";
186                 a->info->pvo = NULL;
187         }
188
189         if (reason == LEJPCB_OBJECT_START &&
190             ctx->path_match == LEJPVP_MOUNTS + 1) {
191                 a->mountpoint = NULL;
192                 a->origin = NULL;
193                 a->def = NULL;
194                 a->mp_cgienv = NULL;
195         }
196
197         /* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
198         if (reason == LEJPCB_OBJECT_START &&
199             ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) {
200                 a->pvo = lwsws_align(a);
201                 a->p += sizeof(*a->pvo);
202
203                 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
204                 /* ie, enable this protocol, no options yet */
205                 a->pvo->next = a->info->pvo;
206                 a->info->pvo = a->pvo;
207                 a->pvo->name = a->p;
208                 lwsl_err("adding %s\n", a->p);
209                 a->p += n;
210                 a->pvo->value = a->p;
211                 a->pvo->options = NULL;
212                 a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
213                 *(a->p)++ = '\0';
214         }
215
216         if (reason == LEJPCB_OBJECT_END &&
217             (ctx->path_match == LEJPVP + 1 || !ctx->path[0]) &&
218             a->valid) {
219
220                 //lwsl_notice("%s\n", ctx->path);
221                 if (!a->info->port) {
222                         lwsl_err("Port required (eg, 443)");
223                         return 1;
224                 }
225                 a->valid = 0;
226
227                 if (!lws_create_vhost(a->context, a->info, a->head)) {
228                         lwsl_err("Failed to create vhost %s\n",
229                                  a->info->vhost_name);
230                         return 1;
231                 }
232
233                 return 0;
234         }
235
236         if (reason == LEJPCB_OBJECT_END &&
237             ctx->path_match == LEJPVP_MOUNTS + 1) {
238                 if (!a->mountpoint || !a->origin) {
239                         lwsl_err("mountpoint and origin required\n");
240                         return 1;
241                 }
242
243                 n = lws_write_http_mount(a->last, &m, a->p, a->mountpoint,
244                                          a->origin, a->def, a->mp_cgienv);
245                 if (!n)
246                         return 1;
247                 a->p += n;
248                 if (!a->head)
249                         a->head = m;
250
251                 a->last = m;
252         }
253
254         /* we only match on the prepared path strings */
255         if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
256                 return 0;
257
258         switch (ctx->path_match - 1) {
259         case LEJPVP_NAME:
260                 a->info->vhost_name = a->p;
261                 break;
262         case LEJPVP_PORT:
263                 a->info->port = atoi(ctx->buf);
264                 return 0;
265         case LEJPVP_HOST_SSL_KEY:
266                 a->info->ssl_private_key_filepath = a->p;
267                 break;
268         case LEJPVP_HOST_SSL_CERT:
269                 a->info->ssl_cert_filepath = a->p;
270                 break;
271         case LEJPVP_HOST_SSL_CA:
272                 a->info->ssl_ca_filepath = a->p;
273                 break;
274         case LEJPVP_MOUNTPOINT:
275                 a->mountpoint = a->p;
276                 break;
277         case LEJPVP_ORIGIN:
278                 a->origin = a->p;
279                 break;
280         case LEJPVP_DEFAULT:
281                 a->def = a->p;
282                 break;
283         case LEJPVP_CGI_ENV:
284                 mp_cgienv = lwsws_align(a);
285                 a->p += sizeof(*a->mp_cgienv);
286
287                 mp_cgienv->next = a->mp_cgienv;
288                 a->mp_cgienv = mp_cgienv;
289
290                 n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
291                 mp_cgienv->name = a->p;
292                 a->p += n;
293                 mp_cgienv->value = a->p;
294                 mp_cgienv->options = NULL;
295                 a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
296                 *(a->p)++ = '\0';
297
298                 lwsl_notice("    adding cgi-env '%s' = '%s'\n", mp_cgienv->name,
299                                 mp_cgienv->value);
300
301                 break;
302         case LEJPVP_PROTOCOL_NAME_OPT:
303                 /* this catches, eg,
304                  * vhosts[].ws-protocols[].xxx-protocol.yyy-option
305                  * ie, these are options attached to a protocol with { }
306                  */
307                 pvo = lwsws_align(a);
308                 a->p += sizeof(*a->pvo);
309
310                 n = lejp_get_wildcard(ctx, 1, a->p, a->end - a->p);
311                 /* ie, enable this protocol, no options yet */
312                 pvo->next = a->pvo->options;
313                 a->pvo->options = pvo;
314                 pvo->name = a->p;
315                 a->p += n;
316                 pvo->value = a->p;
317                 pvo->options = NULL;
318                 a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
319                 *(a->p)++ = '\0';
320                 break;
321
322         default:
323                 return 0;
324         }
325
326         a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
327         *(a->p)++ = '\0';
328
329         return 0;
330 }
331
332 /*
333  * returns 0 = OK, 1 = can't open, 2 = parsing error
334  */
335
336 static int
337 lwsws_get_config(void *user, const char *f, const char * const *paths,
338                  int count_paths, lejp_callback cb)
339 {
340         unsigned char buf[128];
341         struct lejp_ctx ctx;
342         int n, m, fd;
343
344         fd = open(f, O_RDONLY);
345         if (fd < 0) {
346                 lwsl_err("Cannot open %s\n", f);
347                 return 1;
348         }
349         lwsl_info("%s: %s\n", __func__, f);
350         lejp_construct(&ctx, cb, user, paths, count_paths);
351
352         do {
353                 n = read(fd, buf, sizeof(buf));
354                 if (!n)
355                         break;
356
357                 m = (int)(signed char)lejp_parse(&ctx, buf, n);
358         } while (m == LEJP_CONTINUE);
359
360         close(fd);
361         n = ctx.line;
362         lejp_destruct(&ctx);
363
364         if (m < 0) {
365                 lwsl_err("%s(%u): parsing error %d\n", f, n, m);
366                 return 2;
367         }
368
369         return 0;
370 }
371
372 #if defined(LWS_USE_LIBUV) && UV_VERSION_MAJOR > 0
373
374 static int
375 lwsws_get_config_d(void *user, const char *d, const char * const *paths,
376                    int count_paths, lejp_callback cb)
377 {
378         uv_dirent_t dent;
379         uv_fs_t req;
380         char path[256];
381         int ret = 0;
382         uv_loop_t loop;
383
384         uv_loop_init(&loop);
385
386         if (!uv_fs_scandir(&loop, &req, d, 0, NULL)) {
387                 lwsl_err("Scandir on %s failed\n", d);
388                 return 1;
389         }
390
391         while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
392                 snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name);
393                 ret = lwsws_get_config(user, path, paths, count_paths, cb);
394                 if (ret)
395                         goto bail;
396         }
397
398 bail:
399         uv_fs_req_cleanup(&req);
400         uv_loop_close(&loop);
401
402         return ret;
403 }
404
405 #else
406
407 #ifndef _WIN32
408 static int filter(const struct dirent *ent)
409 {
410         if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
411                 return 0;
412
413         return 1;
414 }
415 #endif
416
417 static int
418 lwsws_get_config_d(void *user, const char *d, const char * const *paths,
419                    int count_paths, lejp_callback cb)
420 {
421 #ifndef _WIN32
422         struct dirent **namelist;
423         char path[256];
424         int n, i, ret = 0;
425
426         n = scandir(d, &namelist, filter, alphasort);
427         if (n < 0) {
428                 lwsl_err("Scandir on %d failed\n", d);
429         }
430
431         for (i = 0; i < n; i++) {
432                 snprintf(path, sizeof(path) - 1, "%s/%s", d,
433                          namelist[i]->d_name);
434                 ret = lwsws_get_config(user, path, paths, count_paths, cb);
435                 if (ret) {
436                         while (i++ < n)
437                                 free(namelist[i]);
438                         goto bail;
439                 }
440                 free(namelist[i]);
441         }
442
443 bail:
444         free(namelist);
445
446         return ret;
447 #else
448         return 0;
449 #endif
450 }
451
452 #endif
453
454 int
455 lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d,
456                          char **cs, int *len)
457 {
458         struct jpargs a;
459
460         a.info = info;
461         a.p = *cs;
462         a.end = (a.p + *len) - 1;
463         a.valid = 0;
464
465         if (lwsws_get_config(&a, "/etc/lwsws/conf", paths_global,
466                              ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
467                 return 1;
468         if (lwsws_get_config_d(&a, d, paths_global,
469                                ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
470                 return 1;
471
472         *cs = a.p;
473         *len = a.end - a.p;
474
475         return 0;
476 }
477
478 int
479 lwsws_get_config_vhosts(struct lws_context *context,
480                         struct lws_context_creation_info *info, const char *d,
481                         char **cs, int *len)
482 {
483         struct jpargs a;
484
485         a.info = info;
486         a.p = *cs;
487         a.end = a.p + *len;
488         a.valid = 0;
489         a.context = context;
490         a.protocols = info->protocols;
491         a.extensions = info->extensions;
492
493         if (lwsws_get_config(&a, "/etc/lwsws/conf", paths_vhosts,
494                              ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
495                 return 1;
496         if (lwsws_get_config_d(&a, d, paths_vhosts,
497                                ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
498                 return 1;
499
500         *cs = a.p;
501         *len = a.end - a.p;
502
503         lws_finalize_startup(context);
504
505         return 0;
506 }