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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt