• 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                 "       -q              Redirect all HTTP requests to HTTPS\n"
144 #endif
145                 "       -h directory    Specify the document root, default is '.'\n"
146                 "       -E string       Use given virtual URL as 404 error handler\n"
147                 "       -b string       Use given charset for directory listings, default to UTF-8\n"
148                 "       -I string       Use given filename as index for directories, multiple allowed\n"
149                 "       -S              Do not follow symbolic links outside of the docroot\n"
150                 "       -D              Do not allow directory listings, send 403 instead\n"
151                 "       -R              Enable RFC1918 filter\n"
152                 "       -n count        Maximum allowed number of concurrent script requests\n"
153                 "       -N count        Maximum allowed number of concurrent connections\n"
154 #ifdef HAVE_LUA
155                 "       -l string       URL prefix for Lua handler\n"
156                 "       -L file         Path to Lua handler script, -l and -L may be repeated in pairs\n"
157 #endif
158 #ifdef HAVE_UCODE
159                 "       -o string       URL prefix for ucode handler\n"
160                 "       -O file         Path to ucode handler script, -o and -O may be repeated in pairs\n"
161 #endif
162 #ifdef HAVE_UBUS
163                 "       -u string       URL prefix for UBUS via JSON-RPC handler\n"
164                 "       -U file         Override ubus socket path\n"
165                 "       -a              Do not authenticate JSON-RPC requests against UBUS session api\n"
166                 "       -X              Enable CORS HTTP headers on JSON-RPC api\n"
167                 "       -e              Events subscription reconnection time (retry value)\n"
168 #endif
169                 "       -x string       URL prefix for CGI handler, default is '/cgi-bin'\n"
170                 "       -y alias[=path] URL alias handle\n"
171                 "       -i .ext=path    Use interpreter at path for files with the given extension\n"
172                 "       -t seconds      CGI, Lua and UBUS script timeout in seconds, default is 60\n"
173                 "       -T seconds      Network timeout in seconds, default is 30\n"
174                 "       -k seconds      HTTP keepalive timeout\n"
175                 "       -A seconds      TCP keepalive timeout, default is unset\n"
176                 "       -d string       URL decode given string\n"
177                 "       -r string       Specify basic auth realm\n"
178                 "       -m string       MD5 crypt given string\n"
179                 "\n", name
180         );
181         return 1;
182 }
183 
184 static void init_defaults_pre(void)
185 {
186         conf.script_timeout = 60;
187         conf.network_timeout = 30;
188         conf.http_keepalive = 20;
189         conf.max_script_requests = 3;
190         conf.max_connections = 100;
191         conf.realm = "Protected Area";
192         conf.cgi_prefix = "/cgi-bin";
193         conf.cgi_path = "/sbin:/usr/sbin:/bin:/usr/bin";
194         INIT_LIST_HEAD(&conf.cgi_alias);
195         INIT_LIST_HEAD(&conf.lua_prefix);
196 #if HAVE_UCODE
197         INIT_LIST_HEAD(&conf.ucode_prefix);
198 #endif
199 }
200 
201 static void init_defaults_post(void)
202 {
203         uh_index_add("index.html");
204         uh_index_add("index.htm");
205         uh_index_add("default.html");
206         uh_index_add("default.htm");
207 
208         if (conf.cgi_prefix) {
209                 char *str = malloc(strlen(conf.docroot) + strlen(conf.cgi_prefix) + 1);
210 
211                 strcpy(str, conf.docroot);
212                 strcat(str, conf.cgi_prefix);
213                 conf.cgi_docroot_path = str;
214                 conf.cgi_prefix_len = strlen(conf.cgi_prefix);
215         };
216 }
217 
218 static void fixup_prefix(char *str)
219 {
220         int len;
221 
222         if (!str || !str[0])
223                 return;
224 
225         len = strlen(str) - 1;
226 
227         while (len >= 0 && str[len] == '/')
228                 len--;
229 
230         str[len + 1] = 0;
231 }
232 
233 #ifdef HAVE_LUA
234 static void add_lua_prefix(const char *prefix, const char *handler) {
235         struct lua_prefix *p;
236         char *pprefix, *phandler;
237 
238         p = calloc_a(sizeof(*p),
239                      &pprefix, strlen(prefix) + 1,
240                      &phandler, strlen(handler) + 1);
241 
242         if (!p)
243                 return;
244 
245         p->prefix = strcpy(pprefix, prefix);
246         p->handler = strcpy(phandler, handler);
247 
248         list_add_tail(&p->list, &conf.lua_prefix);
249 }
250 #endif
251 
252 #ifdef HAVE_UCODE
253 static void add_ucode_prefix(const char *prefix, const char *handler) {
254         struct ucode_prefix *p;
255         char *pprefix, *phandler;
256 
257         p = calloc_a(sizeof(*p),
258                      &pprefix, strlen(prefix) + 1,
259                      &phandler, strlen(handler) + 1);
260 
261         if (!p)
262                 return;
263 
264         p->prefix = strcpy(pprefix, prefix);
265         p->handler = strcpy(phandler, handler);
266 
267         list_add_tail(&p->list, &conf.ucode_prefix);
268 }
269 #endif
270 
271 int main(int argc, char **argv)
272 {
273         struct alias *alias;
274         bool nofork = false;
275         char *port;
276         int opt, ch;
277         int cur_fd;
278         int bound = 0;
279 #ifdef HAVE_TLS
280         int n_tls = 0;
281         const char *tls_key = NULL, *tls_crt = NULL, *tls_ciphers = NULL;
282 #endif
283 #ifdef HAVE_LUA
284         const char *lua_prefix = NULL, *lua_handler = NULL;
285 #endif
286 #ifdef HAVE_UCODE
287         const char *ucode_prefix = NULL, *ucode_handler = NULL;
288 #endif
289 
290         BUILD_BUG_ON(sizeof(uh_buf) < PATH_MAX);
291 
292         uh_dispatch_add(&cgi_dispatch);
293         init_defaults_pre();
294         signal(SIGPIPE, SIG_IGN);
295 
296         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:qRr:Ss:T:t:U:u:Xx:y:")) != -1) {
297                 switch(ch) {
298 #ifdef HAVE_TLS
299                 case 'C':
300                         tls_crt = optarg;
301                         break;
302 
303                 case 'K':
304                         tls_key = optarg;
305                         break;
306 
307                 case 'P':
308                         tls_ciphers = optarg;
309                         break;
310 
311                 case 'q':
312                         conf.tls_redirect = 1;
313                         break;
314 
315                 case 's':
316                         n_tls++;
317                         /* fall through */
318 #else
319                 case 'C':
320                 case 'K':
321                 case 'P':
322                 case 'q':
323                 case 's':
324                         fprintf(stderr, "uhttpd: TLS support not compiled, "
325                                         "ignoring -%c\n", ch);
326                         break;
327 #endif
328                 case 'p':
329                         optarg = strdup(optarg);
330                         bound += add_listener_arg(optarg, (ch == 's'));
331                         free(optarg);
332                         break;
333 
334                 case 'h':
335                         if (!realpath(optarg, uh_buf)) {
336                                 fprintf(stderr, "Error: Invalid directory %s: %s\n",
337                                                 optarg, strerror(errno));
338                                 exit(1);
339                         }
340                         conf.docroot = strdup(uh_buf);
341                         break;
342 
343                 case 'H':
344                         if (uh_handler_add(optarg)) {
345                                 fprintf(stderr, "Error: Failed to load handler script %s\n",
346                                         optarg);
347                                 exit(1);
348                         }
349                         break;
350 
351                 case 'E':
352                         if (optarg[0] != '/') {
353                                 fprintf(stderr, "Error: Invalid error handler: %s\n",
354                                                 optarg);
355                                 exit(1);
356                         }
357                         conf.error_handler = optarg;
358                         break;
359 
360                 case 'I':
361                         if (optarg[0] == '/') {
362                                 fprintf(stderr, "Error: Invalid index page: %s\n",
363                                                 optarg);
364                                 exit(1);
365                         }
366                         uh_index_add(optarg);
367                         break;
368 
369                 case 'b':
370                         conf.dirlist_charset = optarg;
371                         break;
372 
373                 case 'S':
374                         conf.no_symlinks = 1;
375                         break;
376 
377                 case 'D':
378                         conf.no_dirlists = 1;
379                         break;
380 
381                 case 'R':
382                         conf.rfc1918_filter = 1;
383                         break;
384 
385                 case 'n':
386                         conf.max_script_requests = atoi(optarg);
387                         break;
388 
389                 case 'N':
390                         conf.max_connections = atoi(optarg);
391                         break;
392 
393                 case 'x':
394                         fixup_prefix(optarg);
395                         conf.cgi_prefix = optarg;
396                         break;
397 
398                 case 'y':
399                         alias = calloc(1, sizeof(*alias));
400                         if (!alias) {
401                                 fprintf(stderr, "Error: failed to allocate alias\n");
402                                 exit(1);
403                         }
404                         alias->alias = strdup(optarg);
405                         alias->path = strchr(alias->alias, '=');
406                         if (alias->path)
407                                 *alias->path++ = 0;
408                         list_add(&alias->list, &conf.cgi_alias);
409                         break;
410 
411                 case 'i':
412                         optarg = strdup(optarg);
413                         port = strchr(optarg, '=');
414                         if (optarg[0] != '.' || !port) {
415                                 fprintf(stderr, "Error: Invalid interpreter: %s\n",
416                                                 optarg);
417                                 exit(1);
418                         }
419 
420                         *port++ = 0;
421                         uh_interpreter_add(optarg, port);
422                         break;
423 
424                 case 't':
425                         conf.script_timeout = atoi(optarg);
426                         break;
427 
428                 case 'T':
429                         conf.network_timeout = atoi(optarg);
430                         break;
431 
432                 case 'k':
433                         conf.http_keepalive = atoi(optarg);
434                         break;
435 
436                 case 'A':
437                         conf.tcp_keepalive = atoi(optarg);
438                         break;
439 
440                 case 'f':
441                         nofork = 1;
442                         break;
443 
444                 case 'd':
445                         optarg = strdup(optarg);
446                         port = alloca(strlen(optarg) + 1);
447                         if (!port)
448                                 return -1;
449 
450                         /* "decode" plus to space to retain compat */
451                         for (opt = 0; optarg[opt]; opt++)
452                                 if (optarg[opt] == '+')
453                                         optarg[opt] = ' ';
454 
455                         /* opt now contains strlen(optarg) -- no need to re-scan */
456                         if (uh_urldecode(port, opt, optarg, opt) < 0) {
457                                 fprintf(stderr, "uhttpd: invalid encoding\n");
458                                 return -1;
459                         }
460 
461                         printf("%s", port);
462                         return 0;
463                         break;
464 
465                 /* basic auth realm */
466                 case 'r':
467                         conf.realm = optarg;
468                         break;
469 
470                 /* md5 crypt */
471                 case 'm':
472                         printf("%s\n", crypt(optarg, "$1$"));
473                         return 0;
474                         break;
475 
476                 /* config file */
477                 case 'c':
478                         conf.file = optarg;
479                         break;
480 
481 #ifdef HAVE_LUA
482                 case 'l':
483                 case 'L':
484                         if (ch == 'l') {
485                                 if (lua_prefix)
486                                         fprintf(stderr, "uhttpd: Ignoring previous -%c %s\n",
487                                                 ch, lua_prefix);
488 
489                                 lua_prefix = optarg;
490                         }
491                         else {
492                                 if (lua_handler)
493                                         fprintf(stderr, "uhttpd: Ignoring previous -%c %s\n",
494                                                 ch, lua_handler);
495 
496                                 lua_handler = optarg;
497                         }
498 
499                         if (lua_prefix && lua_handler) {
500                                 add_lua_prefix(lua_prefix, lua_handler);
501                                 lua_prefix = NULL;
502                                 lua_handler = NULL;
503                         }
504 
505                         break;
506 #else
507                 case 'l':
508                 case 'L':
509                         fprintf(stderr, "uhttpd: Lua support not compiled, "
510                                         "ignoring -%c\n", ch);
511                         break;
512 #endif
513 #ifdef HAVE_UCODE
514                 case 'o':
515                 case 'O':
516                         if (ch == 'o') {
517                                 if (ucode_prefix)
518                                         fprintf(stderr, "uhttpd: Ignoring previous -%c %s\n",
519                                                 ch, ucode_prefix);
520 
521                                 ucode_prefix = optarg;
522                         }
523                         else {
524                                 if (ucode_handler)
525                                         fprintf(stderr, "uhttpd: Ignoring previous -%c %s\n",
526                                                 ch, ucode_handler);
527 
528                                 ucode_handler = optarg;
529                         }
530 
531                         if (ucode_prefix && ucode_handler) {
532                                 add_ucode_prefix(ucode_prefix, ucode_handler);
533                                 ucode_prefix = NULL;
534                                 ucode_handler = NULL;
535                         }
536 
537                         break;
538 #else
539                 case 'o':
540                 case 'O':
541                         fprintf(stderr, "uhttpd: ucode support not compiled, "
542                                         "ignoring -%c\n", ch);
543                         break;
544 #endif
545 #ifdef HAVE_UBUS
546                 case 'a':
547                         conf.ubus_noauth = 1;
548                         break;
549 
550                 case 'u':
551                         conf.ubus_prefix = optarg;
552                         break;
553 
554                 case 'U':
555                         conf.ubus_socket = optarg;
556                         break;
557 
558                 case 'X':
559                         conf.ubus_cors = 1;
560                         break;
561 
562                 case 'e':
563                         conf.events_retry = atoi(optarg);
564                         break;
565 #else
566                 case 'a':
567                 case 'u':
568                 case 'U':
569                 case 'X':
570                 case 'e':
571                         fprintf(stderr, "uhttpd: UBUS support not compiled, "
572                                         "ignoring -%c\n", ch);
573                         break;
574 #endif
575                 default:
576                         return usage(argv[0]);
577                 }
578         }
579 
580         uh_config_parse();
581 
582         if (!conf.docroot) {
583                 if (!realpath(".", uh_buf)) {
584                         fprintf(stderr, "Error: Unable to determine work dir\n");
585                         return 1;
586                 }
587                 conf.docroot = strdup(uh_buf);
588         }
589 
590         init_defaults_post();
591 
592         if (!bound) {
593                 fprintf(stderr, "Error: No sockets bound, unable to continue\n");
594                 return 1;
595         }
596 
597 #ifdef HAVE_TLS
598         if (n_tls) {
599                 if (!tls_crt || !tls_key) {
600                         fprintf(stderr, "Please specify a certificate and "
601                                         "a key file to enable SSL support\n");
602                         return 1;
603                 }
604 
605                 if (uh_tls_init(tls_key, tls_crt, tls_ciphers))
606                     return 1;
607         }
608 #endif
609 
610 #ifdef HAVE_LUA
611         if (lua_handler || lua_prefix) {
612                 fprintf(stderr, "Need handler and prefix to enable Lua support\n");
613                 return 1;
614         }
615 
616         if (!list_empty(&conf.lua_prefix) && uh_plugin_init("uhttpd_lua.so"))
617                 return 1;
618 #endif
619 #ifdef HAVE_UCODE
620         if (ucode_handler || ucode_prefix) {
621                 fprintf(stderr, "Need handler and prefix to enable ucode support\n");
622                 return 1;
623         }
624 
625         if (!list_empty(&conf.ucode_prefix) && uh_plugin_init("uhttpd_ucode.so"))
626                 return 1;
627 #endif
628 #ifdef HAVE_UBUS
629         if (conf.ubus_prefix && uh_plugin_init("uhttpd_ubus.so"))
630                 return 1;
631 #endif
632 
633         /* fork (if not disabled) */
634         if (!nofork) {
635                 switch (fork()) {
636                 case -1:
637                         perror("fork()");
638                         exit(1);
639 
640                 case 0:
641                         /* daemon setup */
642                         if (chdir("/"))
643                                 perror("chdir()");
644 
645                         cur_fd = open("/dev/null", O_WRONLY);
646                         if (cur_fd > 0) {
647                                 dup2(cur_fd, 0);
648                                 dup2(cur_fd, 1);
649                                 dup2(cur_fd, 2);
650                         }
651 
652                         break;
653 
654                 default:
655                         exit(0);
656                 }
657         }
658 
659         return run_server();
660 }
661 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt