• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/uhttpd/main.c

  1 /*
  2  * uhttpd - Tiny single-threaded httpd
  3  *
  4  *   Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org>
  5  *   Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
  6  *
  7  * Permission to use, copy, modify, and/or distribute this software for any
  8  * purpose with or without fee is hereby granted, provided that the above
  9  * copyright notice and this permission notice appear in all copies.
 10  *
 11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 18  */
 19 
 20 #ifndef _DEFAULT_SOURCE
 21 # define _DEFAULT_SOURCE
 22 #endif
 23 
 24 #define _BSD_SOURCE
 25 #define _GNU_SOURCE
 26 #define _XOPEN_SOURCE   700
 27 #include <sys/types.h>
 28 #include <sys/socket.h>
 29 #include <netinet/in.h>
 30 
 31 #include <getopt.h>
 32 #include <errno.h>
 33 #include <netdb.h>
 34 #include <signal.h>
 35 
 36 #include <libubox/usock.h>
 37 #include <libubox/utils.h>
 38 
 39 #include "uhttpd.h"
 40 #include "tls.h"
 41 
 42 char uh_buf[4096];
 43 
 44 static int run_server(void)
 45 {
 46         uloop_init();
 47         uh_setup_listeners();
 48         uh_plugin_post_init();
 49         uloop_run();
 50 
 51         return 0;
 52 }
 53 
 54 static void uh_config_parse(void)
 55 {
 56         const char *path = conf.file;
 57         FILE *c;
 58         char line[512];
 59         char *col1;
 60         char *col2;
 61         char *eol;
 62 
 63         if (!path)
 64                 path = "/etc/httpd.conf";
 65 
 66         c = fopen(path, "r");
 67         if (!c)
 68                 return;
 69 
 70         memset(line, 0, sizeof(line));
 71 
 72         while (fgets(line, sizeof(line) - 1, c)) {
 73                 if ((line[0] == '/') && (strchr(line, ':') != NULL)) {
 74                         if (!(col1 = strchr(line, ':')) || (*col1++ = 0) ||
 75                                 !(col2 = strchr(col1, ':')) || (*col2++ = 0) ||
 76                                 !(eol = strchr(col2, '\n')) || (*eol++  = 0))
 77                                 continue;
 78 
 79                         uh_auth_add(line, col1, col2);
 80                 } else if (!strncmp(line, "I:", 2)) {
 81                         if (!(col1 = strchr(line, ':')) || (*col1++ = 0) ||
 82                                 !(eol = strchr(col1, '\n')) || (*eol++  = 0))
 83                                 continue;
 84 
 85                         uh_index_add(strdup(col1));
 86                 } else if (!strncmp(line, "E404:", 5)) {
 87                         if (!(col1 = strchr(line, ':')) || (*col1++ = 0) ||
 88                                 !(eol = strchr(col1, '\n')) || (*eol++  = 0))
 89                                 continue;
 90 
 91                         conf.error_handler = strdup(col1);
 92                 }
 93                 else if ((line[0] == '*') && (strchr(line, ':') != NULL)) {
 94                         if (!(col1 = strchr(line, '*')) || (*col1++ = 0) ||
 95                                 !(col2 = strchr(col1, ':')) || (*col2++ = 0) ||
 96                                 !(eol = strchr(col2, '\n')) || (*eol++  = 0))
 97                                 continue;
 98 
 99                         uh_interpreter_add(col1, col2);
100                 }
101         }
102 
103         fclose(c);
104 }
105 
106 static int add_listener_arg(char *arg, bool tls)
107 {
108         char *host = NULL;
109         char *port = arg;
110         char *s;
111         int l;
112 
113         s = strrchr(arg, ':');
114         if (s) {
115                 host = arg;
116                 port = s + 1;
117                 *s = 0;
118         }
119 
120         if (host && *host == '[') {
121                 l = strlen(host);
122                 if (l >= 2) {
123                         host[l-1] = 0;
124                         host++;
125                 }
126         }
127 
128         return uh_socket_bind(host, port, tls);
129 }
130 
131 static int usage(const char *name)
132 {
133         fprintf(stderr,
134                 "Usage: %s -p [addr:]port -h docroot\n"
135                 "       -f              Do not fork to background\n"
136                 "       -c file         Configuration file, default is '/etc/httpd.conf'\n"
137                 "       -p [addr:]port  Bind to specified address and port, multiple allowed\n"
138 #ifdef HAVE_TLS
139                 "       -s [addr:]port  Like -p but provide HTTPS on this port\n"
140                 "       -C file         ASN.1 server certificate file\n"
141                 "       -K file         ASN.1 server private key file\n"
142                 "       -P ciphers      Colon separated list of allowed TLS ciphers\n"
143                 "\t-q              Redirect all HTTP requests to HTTPS\n"
144                 "\t-Q host=url     Redirect requests by SNI hostname to URL, -Q may be repeated\n"
145 #endif
146                 "       -h directory    Specify the document root, default is '.'\n"
147                 "       -E string       Use given virtual URL as 404 error handler\n"
148                 "       -b string       Use given charset for directory listings, default to UTF-8\n"
149                 "       -I string       Use given filename as index for directories, multiple allowed\n"
150                 "       -S              Do not follow symbolic links outside of the docroot\n"
151                 "       -D              Do not allow directory listings, send 403 instead\n"
152                 "       -R              Enable RFC1918 filter\n"
153                 "       -n count        Maximum allowed number of concurrent script requests\n"
154                 "       -N count        Maximum allowed number of concurrent connections\n"
155 #ifdef HAVE_LUA
156                 "       -l string       URL prefix for Lua handler\n"
157                 "       -L file         Path to Lua handler script, -l and -L may be repeated in pairs\n"
158 #endif
159 #ifdef HAVE_UCODE
160                 "       -o string       URL prefix for ucode handler\n"
161                 "       -O file         Path to ucode handler script, -o and -O may be repeated in pairs\n"
162 #endif
163 #ifdef HAVE_UBUS
164                 "       -u string       URL prefix for UBUS via JSON-RPC handler\n"
165                 "       -U file         Override ubus socket path\n"
166                 "       -a              Do not authenticate JSON-RPC requests against UBUS session api\n"
167                 "       -X              Enable CORS HTTP headers on JSON-RPC api\n"
168                 "       -e              Events subscription reconnection time (retry value)\n"
169 #endif
170                 "       -x string       URL prefix for CGI handler, default is '/cgi-bin'\n"
171                 "       -y alias[=path] URL alias handle\n"
172                 "       -i .ext=path    Use interpreter at path for files with the given extension\n"
173                 "       -t seconds      CGI, Lua and UBUS script timeout in seconds, default is 60\n"
174                 "       -T seconds      Network timeout in seconds, default is 30\n"
175                 "       -k seconds      HTTP keepalive timeout\n"
176                 "       -A seconds      TCP keepalive timeout, default is unset\n"
177                 "       -d string       URL decode given string\n"
178                 "       -r string       Specify basic auth realm\n"
179                 "       -m string       MD5 crypt given string\n"
180                 "\n", name
181         );
182         return 1;
183 }
184 
185 static void init_defaults_pre(void)
186 {
187         conf.script_timeout = 60;
188         conf.network_timeout = 30;
189         conf.http_keepalive = 20;
190         conf.max_script_requests = 3;
191         conf.max_connections = 100;
192         conf.realm = "Protected Area";
193         conf.cgi_prefix = "/cgi-bin";
194         conf.cgi_path = "/sbin:/usr/sbin:/bin:/usr/bin";
195         INIT_LIST_HEAD(&conf.cgi_alias);
196         INIT_LIST_HEAD(&conf.lua_prefix);
197 #if HAVE_UCODE
198         INIT_LIST_HEAD(&conf.ucode_prefix);
199 #endif
200         INIT_LIST_HEAD(&conf.sni_redirect);
201 }
202 
203 static void init_defaults_post(void)
204 {
205         uh_index_add("index.html");
206         uh_index_add("index.htm");
207         uh_index_add("default.html");
208         uh_index_add("default.htm");
209 
210         if (conf.cgi_prefix) {
211                 char *str = malloc(strlen(conf.docroot) + strlen(conf.cgi_prefix) + 1);
212 
213                 strcpy(str, conf.docroot);
214                 strcat(str, conf.cgi_prefix);
215                 conf.cgi_docroot_path = str;
216                 conf.cgi_prefix_len = strlen(conf.cgi_prefix);
217         };
218 }
219 
220 static void fixup_prefix(char *str)
221 {
222         int len;
223 
224         if (!str || !str[0])
225                 return;
226 
227         len = strlen(str) - 1;
228 
229         while (len >= 0 && str[len] == '/')
230                 len--;
231 
232         str[len + 1] = 0;
233 }
234 
235 #ifdef HAVE_LUA
236 static void add_lua_prefix(const char *prefix, const char *handler) {
237         struct lua_prefix *p;
238         char *pprefix, *phandler;
239 
240         p = calloc_a(sizeof(*p),
241                      &pprefix, strlen(prefix) + 1,
242                      &phandler, strlen(handler) + 1);
243 
244         if (!p)
245                 return;
246 
247         p->prefix = strcpy(pprefix, prefix);
248         p->handler = strcpy(phandler, handler);
249 
250         list_add_tail(&p->list, &conf.lua_prefix);
251 }
252 #endif
253 
254 #ifdef HAVE_UCODE
255 static void add_ucode_prefix(const char *prefix, const char *handler) {
256         struct ucode_prefix *p;
257         char *pprefix, *phandler;
258 
259         p = calloc_a(sizeof(*p),
260                      &pprefix, strlen(prefix) + 1,
261                      &phandler, strlen(handler) + 1);
262 
263         if (!p)
264                 return;
265 
266         p->prefix = strcpy(pprefix, prefix);
267         p->handler = strcpy(phandler, handler);
268 
269         list_add_tail(&p->list, &conf.ucode_prefix);
270 }
271 #endif
272 
273 int main(int argc, char **argv)
274 {
275         struct alias *alias;
276         bool nofork = false;
277         char *port;
278         int opt, ch;
279         int cur_fd;
280         int bound = 0;
281 #ifdef HAVE_TLS
282         int n_tls = 0;
283         const char *tls_key = NULL, *tls_crt = NULL, *tls_ciphers = NULL;
284 #endif
285 #ifdef HAVE_LUA
286         const char *lua_prefix = NULL, *lua_handler = NULL;
287 #endif
288 #ifdef HAVE_UCODE
289         const char *ucode_prefix = NULL, *ucode_handler = NULL;
290 #endif
291 
292         BUILD_BUG_ON(sizeof(uh_buf) < PATH_MAX);
293 
294         uh_dispatch_add(&cgi_dispatch);
295         init_defaults_pre();
296         signal(SIGPIPE, SIG_IGN);
297 
298         while ((ch = getopt(argc, argv, "A:ab:C:c:Dd:E:e:fh:H:I:i:K:k:L:l:m:N:n:O:o:P:p:qQ:Rr:Ss:T:t:U:u:Xx:y:")) != -1) {
299                 switch(ch) {
300 #ifdef HAVE_TLS
301                 case 'C':
302                         tls_crt = optarg;
303                         break;
304 
305                 case 'K':
306                         tls_key = optarg;
307                         break;
308 
309                 case 'P':
310                         tls_ciphers = optarg;
311                         break;
312 
313                 case 'q':
314                         conf.tls_redirect = 1;
315                         break;
316 
317                 case 'Q':
318                         uh_sni_redirect_add(optarg);
319                         break;
320 
321                 case 's':
322                         n_tls++;
323                         /* fall through */
324 #else
325                 case 'C':
326                 case 'K':
327                 case 'P':
328                 case 'q':
329                 case 'Q':
330                 case 's':
331                         fprintf(stderr, "uhttpd: TLS support not compiled, "
332                                         "ignoring -%c\n", ch);
333                         break;
334 #endif
335                 case 'p':
336                         optarg = strdup(optarg);
337                         bound += add_listener_arg(optarg, (ch == 's'));
338                         free(optarg);
339                         break;
340 
341                 case 'h':
342                         if (!realpath(optarg, uh_buf)) {
343                                 fprintf(stderr, "Error: Invalid directory %s: %s\n",
344                                                 optarg, strerror(errno));
345                                 exit(1);
346                         }
347                         conf.docroot = strdup(uh_buf);
348                         break;
349 
350                 case 'H':
351                         if (uh_handler_add(optarg)) {
352                                 fprintf(stderr, "Error: Failed to load handler script %s\n",
353                                         optarg);
354                                 exit(1);
355                         }
356                         break;
357 
358                 case 'E':
359                         if (optarg[0] != '/') {
360                                 fprintf(stderr, "Error: Invalid error handler: %s\n",
361                                                 optarg);
362                                 exit(1);
363                         }
364                         conf.error_handler = optarg;
365                         break;
366 
367                 case 'I':
368                         if (optarg[0] == '/') {
369                                 fprintf(stderr, "Error: Invalid index page: %s\n",
370                                                 optarg);
371                                 exit(1);
372                         }
373                         uh_index_add(optarg);
374                         break;
375 
376                 case 'b':
377                         conf.dirlist_charset = optarg;
378                         break;
379 
380                 case 'S':
381                         conf.no_symlinks = 1;
382                         break;
383 
384                 case 'D':
385                         conf.no_dirlists = 1;
386                         break;
387 
388                 case 'R':
389                         conf.rfc1918_filter = 1;
390                         break;
391 
392                 case 'n':
393                         conf.max_script_requests = atoi(optarg);
394                         break;
395 
396                 case 'N':
397                         conf.max_connections = atoi(optarg);
398                         break;
399 
400                 case 'x':
401                         fixup_prefix(optarg);
402                         conf.cgi_prefix = optarg;
403                         break;
404 
405                 case 'y':
406                         alias = calloc(1, sizeof(*alias));
407                         if (!alias) {
408                                 fprintf(stderr, "Error: failed to allocate alias\n");
409                                 exit(1);
410                         }
411                         alias->alias = strdup(optarg);
412                         alias->path = strchr(alias->alias, '=');
413                         if (alias->path)
414                                 *alias->path++ = 0;
415                         list_add(&alias->list, &conf.cgi_alias);
416                         break;
417 
418                 case 'i':
419                         optarg = strdup(optarg);
420                         port = strchr(optarg, '=');
421                         if (optarg[0] != '.' || !port) {
422                                 fprintf(stderr, "Error: Invalid interpreter: %s\n",
423                                                 optarg);
424                                 exit(1);
425                         }
426 
427                         *port++ = 0;
428                         uh_interpreter_add(optarg, port);
429                         break;
430 
431                 case 't':
432                         conf.script_timeout = atoi(optarg);
433                         break;
434 
435                 case 'T':
436                         conf.network_timeout = atoi(optarg);
437                         break;
438 
439                 case 'k':
440                         conf.http_keepalive = atoi(optarg);
441                         break;
442 
443                 case 'A':
444                         conf.tcp_keepalive = atoi(optarg);
445                         break;
446 
447                 case 'f':
448                         nofork = 1;
449                         break;
450 
451                 case 'd':
452                         optarg = strdup(optarg);
453                         port = alloca(strlen(optarg) + 1);
454                         if (!port)
455                                 return -1;
456 
457                         /* "decode" plus to space to retain compat */
458                         for (opt = 0; optarg[opt]; opt++)
459                                 if (optarg[opt] == '+')
460                                         optarg[opt] = ' ';
461 
462                         /* opt now contains strlen(optarg) -- no need to re-scan */
463                         if (uh_urldecode(port, opt + 1, optarg, opt) < 0) {
464                                 fprintf(stderr, "uhttpd: invalid encoding\n");
465                                 return -1;
466                         }
467 
468                         printf("%s", port);
469                         return 0;
470                         break;
471 
472                 /* basic auth realm */
473                 case 'r':
474                         conf.realm = optarg;
475                         break;
476 
477                 /* md5 crypt */
478                 case 'm':
479                         printf("%s\n", crypt(optarg, "$1$"));
480                         return 0;
481                         break;
482 
483                 /* config file */
484                 case 'c':
485                         conf.file = optarg;
486                         break;
487 
488 #ifdef HAVE_LUA
489                 case 'l':
490                 case 'L':
491                         if (ch == 'l') {
492                                 if (lua_prefix)
493                                         fprintf(stderr, "uhttpd: Ignoring previous -%c %s\n",
494                                                 ch, lua_prefix);
495 
496                                 lua_prefix = optarg;
497                         }
498                         else {
499                                 if (lua_handler)
500                                         fprintf(stderr, "uhttpd: Ignoring previous -%c %s\n",
501                                                 ch, lua_handler);
502 
503                                 lua_handler = optarg;
504                         }
505 
506                         if (lua_prefix && lua_handler) {
507                                 add_lua_prefix(lua_prefix, lua_handler);
508                                 lua_prefix = NULL;
509                                 lua_handler = NULL;
510                         }
511 
512                         break;
513 #else
514                 case 'l':
515                 case 'L':
516                         fprintf(stderr, "uhttpd: Lua support not compiled, "
517                                         "ignoring -%c\n", ch);
518                         break;
519 #endif
520 #ifdef HAVE_UCODE
521                 case 'o':
522                 case 'O':
523                         if (ch == 'o') {
524                                 if (ucode_prefix)
525                                         fprintf(stderr, "uhttpd: Ignoring previous -%c %s\n",
526                                                 ch, ucode_prefix);
527 
528                                 ucode_prefix = optarg;
529                         }
530                         else {
531                                 if (ucode_handler)
532                                         fprintf(stderr, "uhttpd: Ignoring previous -%c %s\n",
533                                                 ch, ucode_handler);
534 
535                                 ucode_handler = optarg;
536                         }
537 
538                         if (ucode_prefix && ucode_handler) {
539                                 add_ucode_prefix(ucode_prefix, ucode_handler);
540                                 ucode_prefix = NULL;
541                                 ucode_handler = NULL;
542                         }
543 
544                         break;
545 #else
546                 case 'o':
547                 case 'O':
548                         fprintf(stderr, "uhttpd: ucode support not compiled, "
549                                         "ignoring -%c\n", ch);
550                         break;
551 #endif
552 #ifdef HAVE_UBUS
553                 case 'a':
554                         conf.ubus_noauth = 1;
555                         break;
556 
557                 case 'u':
558                         conf.ubus_prefix = optarg;
559                         break;
560 
561                 case 'U':
562                         conf.ubus_socket = optarg;
563                         break;
564 
565                 case 'X':
566                         conf.ubus_cors = 1;
567                         break;
568 
569                 case 'e':
570                         conf.events_retry = atoi(optarg);
571                         break;
572 #else
573                 case 'a':
574                 case 'u':
575                 case 'U':
576                 case 'X':
577                 case 'e':
578                         fprintf(stderr, "uhttpd: UBUS support not compiled, "
579                                         "ignoring -%c\n", ch);
580                         break;
581 #endif
582                 default:
583                         return usage(argv[0]);
584                 }
585         }
586 
587         uh_config_parse();
588 
589         if (!conf.docroot) {
590                 if (!realpath(".", uh_buf)) {
591                         fprintf(stderr, "Error: Unable to determine work dir\n");
592                         return 1;
593                 }
594                 conf.docroot = strdup(uh_buf);
595         }
596 
597         init_defaults_post();
598 
599         if (!bound) {
600                 fprintf(stderr, "Error: No sockets bound, unable to continue\n");
601                 return 1;
602         }
603 
604 #ifdef HAVE_TLS
605         if (n_tls) {
606                 if (!tls_crt || !tls_key) {
607                         fprintf(stderr, "Please specify a certificate and "
608                                         "a key file to enable SSL support\n");
609                         return 1;
610                 }
611 
612                 if (uh_tls_init(tls_key, tls_crt, tls_ciphers))
613                     return 1;
614         }
615 #endif
616 
617 #ifdef HAVE_LUA
618         if (lua_handler || lua_prefix) {
619                 fprintf(stderr, "Need handler and prefix to enable Lua support\n");
620                 return 1;
621         }
622 
623         if (!list_empty(&conf.lua_prefix) && uh_plugin_init("uhttpd_lua.so"))
624                 return 1;
625 #endif
626 #ifdef HAVE_UCODE
627         if (ucode_handler || ucode_prefix) {
628                 fprintf(stderr, "Need handler and prefix to enable ucode support\n");
629                 return 1;
630         }
631 
632         if (!list_empty(&conf.ucode_prefix) && uh_plugin_init("uhttpd_ucode.so"))
633                 return 1;
634 #endif
635 #ifdef HAVE_UBUS
636         if (conf.ubus_prefix && uh_plugin_init("uhttpd_ubus.so"))
637                 return 1;
638 #endif
639 
640         /* fork (if not disabled) */
641         if (!nofork) {
642                 switch (fork()) {
643                 case -1:
644                         perror("fork()");
645                         exit(1);
646 
647                 case 0:
648                         /* daemon setup */
649                         if (chdir("/"))
650                                 perror("chdir()");
651 
652                         cur_fd = open("/dev/null", O_RDWR);
653                         if (cur_fd >= 0) {
654                                 dup2(cur_fd, 0);
655                                 dup2(cur_fd, 1);
656                                 dup2(cur_fd, 2);
657                                 if (cur_fd > 2)
658                                         close(cur_fd);
659                         }
660 
661                         break;
662 
663                 default:
664                         exit(0);
665                 }
666         }
667 
668         return run_server();
669 }
670 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt