[package] uhttpd: various fixes
[openwrt.git] / package / uhttpd / src / uhttpd.c
1 /*
2  * uhttpd - Tiny single-threaded httpd - Main component
3  *
4  *   Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18
19 #define _XOPEN_SOURCE 500       /* crypt() */
20
21 #include "uhttpd.h"
22 #include "uhttpd-utils.h"
23 #include "uhttpd-file.h"
24
25 #ifdef HAVE_CGI
26 #include "uhttpd-cgi.h"
27 #endif
28
29 #ifdef HAVE_LUA
30 #include "uhttpd-lua.h"
31 #endif
32
33 #ifdef HAVE_TLS
34 #include "uhttpd-tls.h"
35 #endif
36
37
38 static int run = 1;
39
40 static void uh_sigterm(int sig)
41 {
42         run = 0;
43 }
44
45 static void uh_config_parse(struct config *conf)
46 {
47         FILE *c;
48         char line[512];
49         char *col1 = NULL;
50         char *col2 = NULL;
51         char *eol  = NULL;
52
53         const char *path = conf->file ? conf->file : "/etc/httpd.conf";
54
55
56         if ((c = fopen(path, "r")) != NULL)
57         {
58                 memset(line, 0, sizeof(line));
59
60                 while (fgets(line, sizeof(line) - 1, c))
61                 {
62                         if ((line[0] == '/') && (strchr(line, ':') != NULL))
63                         {
64                                 if (!(col1 = strchr(line, ':')) || (*col1++ = 0) ||
65                                     !(col2 = strchr(col1, ':')) || (*col2++ = 0) ||
66                                         !(eol = strchr(col2, '\n')) || (*eol++  = 0))
67                                 {
68                                         continue;
69                                 }
70
71                                 if (!uh_auth_add(line, col1, col2))
72                                 {
73                                         fprintf(stderr,
74                                                         "Notice: No password set for user %s, ignoring "
75                                                         "authentication on %s\n", col1, line
76                                         );
77                                 }
78                         }
79                         else if (!strncmp(line, "I:", 2))
80                         {
81                                 if (!(col1 = strchr(line, ':')) || (*col1++ = 0) ||
82                                     !(eol = strchr(col1, '\n')) || (*eol++  = 0))
83                                 {
84                                         continue;
85                                 }
86
87                                 conf->index_file = strdup(col1);
88                         }
89                         else if (!strncmp(line, "E404:", 5))
90                         {
91                                 if (!(col1 = strchr(line, ':')) || (*col1++ = 0) ||
92                                     !(eol = strchr(col1, '\n')) || (*eol++  = 0))
93                                 {
94                                         continue;
95                                 }
96
97                                 conf->error_handler = strdup(col1);
98                         }
99 #ifdef HAVE_CGI
100                         else if ((line[0] == '*') && (strchr(line, ':') != NULL))
101                         {
102                                 if (!(col1 = strchr(line, '*')) || (*col1++ = 0) ||
103                                     !(col2 = strchr(col1, ':')) || (*col2++ = 0) ||
104                                     !(eol = strchr(col2, '\n')) || (*eol++  = 0))
105                                 {
106                                         continue;
107                                 }
108
109                                 if (!uh_interpreter_add(col1, col2))
110                                 {
111                                         fprintf(stderr,
112                                                         "Unable to add interpreter %s for extension %s: "
113                                                         "Out of memory\n", col2, col1
114                                         );
115                                 }
116                         }
117 #endif
118                 }
119
120                 fclose(c);
121         }
122 }
123
124 static void uh_listener_cb(struct uloop_fd *u, unsigned int events);
125
126 static int uh_socket_bind(fd_set *serv_fds, int *max_fd,
127                                                   const char *host, const char *port,
128                                                   struct addrinfo *hints, int do_tls,
129                                                   struct config *conf)
130 {
131         int sock = -1;
132         int yes = 1;
133         int status;
134         int bound = 0;
135
136         int tcp_ka_idl, tcp_ka_int, tcp_ka_cnt;
137
138         struct listener *l = NULL;
139         struct addrinfo *addrs = NULL, *p = NULL;
140
141         if ((status = getaddrinfo(host, port, hints, &addrs)) != 0)
142         {
143                 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(status));
144         }
145
146         /* try to bind a new socket to each found address */
147         for (p = addrs; p; p = p->ai_next)
148         {
149                 /* get the socket */
150                 if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
151                 {
152                         perror("socket()");
153                         goto error;
154                 }
155
156                 /* "address already in use" */
157                 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
158                 {
159                         perror("setsockopt()");
160                         goto error;
161                 }
162
163                 /* TCP keep-alive */
164                 if (conf->tcp_keepalive > 0)
165                 {
166                         tcp_ka_idl = 1;
167                         tcp_ka_cnt = 3;
168                         tcp_ka_int = conf->tcp_keepalive;
169
170                         if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) ||
171                             setsockopt(sock, SOL_TCP, TCP_KEEPIDLE,  &tcp_ka_idl, sizeof(tcp_ka_idl)) ||
172                             setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &tcp_ka_int, sizeof(tcp_ka_int)) ||
173                             setsockopt(sock, SOL_TCP, TCP_KEEPCNT,   &tcp_ka_cnt, sizeof(tcp_ka_cnt)))
174                         {
175                             fprintf(stderr, "Notice: Unable to enable TCP keep-alive: %s\n",
176                                 strerror(errno));
177                         }
178                 }
179
180                 /* required to get parallel v4 + v6 working */
181                 if (p->ai_family == AF_INET6)
182                 {
183                         if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) == -1)
184                         {
185                                 perror("setsockopt()");
186                                 goto error;
187                         }
188                 }
189
190                 /* bind */
191                 if (bind(sock, p->ai_addr, p->ai_addrlen) == -1)
192                 {
193                         perror("bind()");
194                         goto error;
195                 }
196
197                 /* listen */
198                 if (listen(sock, UH_LIMIT_CLIENTS) == -1)
199                 {
200                         perror("listen()");
201                         goto error;
202                 }
203
204                 /* add listener to global list */
205                 if (!(l = uh_listener_add(sock, conf)))
206                 {
207                         fprintf(stderr, "uh_listener_add(): Failed to allocate memory\n");
208                         goto error;
209                 }
210
211 #ifdef HAVE_TLS
212                 /* init TLS */
213                 l->tls = do_tls ? conf->tls : NULL;
214 #endif
215
216                 /* add socket to server fd set */
217                 FD_SET(sock, serv_fds);
218                 fd_cloexec(sock);
219                 *max_fd = max(*max_fd, sock);
220
221                 uh_ufd_add(&l->fd, uh_listener_cb, ULOOP_READ);
222
223                 bound++;
224                 continue;
225
226                 error:
227                 if (sock > 0)
228                         close(sock);
229         }
230
231         freeaddrinfo(addrs);
232
233         return bound;
234 }
235
236 static struct http_request * uh_http_header_parse(struct client *cl,
237                                                                                                   char *buffer, int buflen)
238 {
239         char *method  = buffer;
240         char *path    = NULL;
241         char *version = NULL;
242
243         char *headers = NULL;
244         char *hdrname = NULL;
245         char *hdrdata = NULL;
246
247         int i;
248         int hdrcount = 0;
249
250         struct http_request *req = &cl->request;
251
252
253         /* terminate initial header line */
254         if ((headers = strfind(buffer, buflen, "\r\n", 2)) != NULL)
255         {
256                 buffer[buflen-1] = 0;
257
258                 *headers++ = 0;
259                 *headers++ = 0;
260
261                 /* find request path */
262                 if ((path = strchr(buffer, ' ')) != NULL)
263                         *path++ = 0;
264
265                 /* find http version */
266                 if ((path != NULL) && ((version = strchr(path, ' ')) != NULL))
267                         *version++ = 0;
268
269
270                 /* check method */
271                 if (strcmp(method, "GET") && strcmp(method, "HEAD") && strcmp(method, "POST"))
272                 {
273                         /* invalid method */
274                         uh_http_response(cl, 405, "Method Not Allowed");
275                         return NULL;
276                 }
277                 else
278                 {
279                         switch(method[0])
280                         {
281                                 case 'G':
282                                         req->method = UH_HTTP_MSG_GET;
283                                         break;
284
285                                 case 'H':
286                                         req->method = UH_HTTP_MSG_HEAD;
287                                         break;
288
289                                 case 'P':
290                                         req->method = UH_HTTP_MSG_POST;
291                                         break;
292                         }
293                 }
294
295                 /* check path */
296                 if (!path || !strlen(path))
297                 {
298                         /* malformed request */
299                         uh_http_response(cl, 400, "Bad Request");
300                         return NULL;
301                 }
302                 else
303                 {
304                         req->url = path;
305                 }
306
307                 /* check version */
308                 if ((version == NULL) || (strcmp(version, "HTTP/0.9") &&
309                     strcmp(version, "HTTP/1.0") && strcmp(version, "HTTP/1.1")))
310                 {
311                         /* unsupported version */
312                         uh_http_response(cl, 400, "Bad Request");
313                         return NULL;
314                 }
315                 else
316                 {
317                         req->version = strtof(&version[5], NULL);
318                 }
319
320                 D("SRV: %s %s HTTP/%.1f\n",
321                   (req->method == UH_HTTP_MSG_POST) ? "POST" :
322                         (req->method == UH_HTTP_MSG_GET) ? "GET" : "HEAD",
323                   req->url, req->version);
324
325                 /* process header fields */
326                 for (i = (int)(headers - buffer); i < buflen; i++)
327                 {
328                         /* found eol and have name + value, push out header tuple */
329                         if (hdrname && hdrdata && (buffer[i] == '\r' || buffer[i] == '\n'))
330                         {
331                                 buffer[i] = 0;
332
333                                 /* store */
334                                 if ((hdrcount + 1) < array_size(req->headers))
335                                 {
336                                         D("SRV: HTTP: %s: %s\n", hdrname, hdrdata);
337
338                                         req->headers[hdrcount++] = hdrname;
339                                         req->headers[hdrcount++] = hdrdata;
340
341                                         hdrname = hdrdata = NULL;
342                                 }
343
344                                 /* too large */
345                                 else
346                                 {
347                                         D("SRV: HTTP: header too big (too many headers)\n");
348                                         uh_http_response(cl, 413, "Request Entity Too Large");
349                                         return NULL;
350                                 }
351                         }
352
353                         /* have name but no value and found a colon, start of value */
354                         else if (hdrname && !hdrdata &&
355                                          ((i+1) < buflen) && (buffer[i] == ':'))
356                         {
357                                 buffer[i] = 0;
358                                 hdrdata = &buffer[i+1];
359
360                                 while ((hdrdata + 1) < (buffer + buflen) && *hdrdata == ' ')
361                                         hdrdata++;
362                         }
363
364                         /* have no name and found [A-Za-z], start of name */
365                         else if (!hdrname && isalpha(buffer[i]))
366                         {
367                                 hdrname = &buffer[i];
368                         }
369                 }
370
371                 /* valid enough */
372                 req->redirect_status = 200;
373                 return req;
374         }
375
376         /* Malformed request */
377         uh_http_response(cl, 400, "Bad Request");
378         return NULL;
379 }
380
381
382 static struct http_request * uh_http_header_recv(struct client *cl)
383 {
384         char *bufptr = cl->httpbuf.buf;
385         char *idxptr = NULL;
386
387         ssize_t blen = sizeof(cl->httpbuf.buf)-1;
388         ssize_t rlen = 0;
389
390         memset(bufptr, 0, sizeof(cl->httpbuf.buf));
391
392         while (blen > 0)
393         {
394                 /* receive data */
395                 ensure_out(rlen = uh_tcp_recv(cl, bufptr, blen));
396                 D("SRV: Client(%d) peek(%d) = %d\n", cl->fd.fd, blen, rlen);
397
398                 if (rlen <= 0)
399                 {
400                         D("SRV: Client(%d) dead [%s]\n", cl->fd.fd, strerror(errno));
401                         return NULL;
402                 }
403
404                 blen -= rlen;
405                 bufptr += rlen;
406
407                 if ((idxptr = strfind(cl->httpbuf.buf, sizeof(cl->httpbuf.buf),
408                                                           "\r\n\r\n", 4)))
409                 {
410                         /* header read complete ... */
411                         cl->httpbuf.ptr = idxptr + 4;
412                         cl->httpbuf.len = bufptr - cl->httpbuf.ptr;
413
414                         return uh_http_header_parse(cl, cl->httpbuf.buf,
415                                                                                 (cl->httpbuf.ptr - cl->httpbuf.buf));
416                 }
417         }
418
419         /* request entity too large */
420         D("SRV: HTTP: header too big (buffer exceeded)\n");
421         uh_http_response(cl, 413, "Request Entity Too Large");
422
423 out:
424         return NULL;
425 }
426
427 #if defined(HAVE_LUA) || defined(HAVE_CGI)
428 static int uh_path_match(const char *prefix, const char *url)
429 {
430         if ((strstr(url, prefix) == url) &&
431                 ((prefix[strlen(prefix)-1] == '/') ||
432                  (strlen(url) == strlen(prefix))   ||
433                  (url[strlen(prefix)] == '/')))
434         {
435                 return 1;
436         }
437
438         return 0;
439 }
440 #endif
441
442 static bool uh_dispatch_request(struct client *cl, struct http_request *req)
443 {
444         struct path_info *pin;
445         struct interpreter *ipr = NULL;
446         struct config *conf = cl->server->conf;
447
448 #ifdef HAVE_LUA
449         /* Lua request? */
450         if (conf->lua_state &&
451                 uh_path_match(conf->lua_prefix, req->url))
452         {
453                 return conf->lua_request(cl, conf->lua_state);
454         }
455         else
456 #endif
457
458 #ifdef HAVE_UBUS
459         /* ubus request? */
460         if (conf->ubus_state &&
461                 uh_path_match(conf->ubus_prefix, req->url))
462         {
463                 return conf->ubus_request(cl, conf->ubus_state);
464         }
465         else
466 #endif
467
468         /* dispatch request */
469         if ((pin = uh_path_lookup(cl, req->url)) != NULL)
470         {
471                 /* auth ok? */
472                 if (!pin->redirected && uh_auth_check(cl, req, pin))
473                 {
474 #ifdef HAVE_CGI
475                         if (uh_path_match(conf->cgi_prefix, pin->name) ||
476                                 (ipr = uh_interpreter_lookup(pin->phys)) != NULL)
477                         {
478                                 return uh_cgi_request(cl, pin, ipr);
479                         }
480 #endif
481                         return uh_file_request(cl, pin);
482                 }
483         }
484
485         /* 404 - pass 1 */
486         else
487         {
488                 /* Try to invoke an error handler */
489                 if ((pin = uh_path_lookup(cl, conf->error_handler)) != NULL)
490                 {
491                         /* auth ok? */
492                         if (uh_auth_check(cl, req, pin))
493                         {
494                                 req->redirect_status = 404;
495 #ifdef HAVE_CGI
496                                 if (uh_path_match(conf->cgi_prefix, pin->name) ||
497                                         (ipr = uh_interpreter_lookup(pin->phys)) != NULL)
498                                 {
499                                         return uh_cgi_request(cl, pin, ipr);
500                                 }
501 #endif
502                                 return uh_file_request(cl, pin);
503                         }
504                 }
505
506                 /* 404 - pass 2 */
507                 else
508                 {
509                         uh_http_sendhf(cl, 404, "Not Found", "No such file or directory");
510                 }
511         }
512
513         return false;
514 }
515
516 static void uh_socket_cb(struct uloop_fd *u, unsigned int events);
517
518 static void uh_listener_cb(struct uloop_fd *u, unsigned int events)
519 {
520         int new_fd;
521         struct listener *serv;
522         struct client *cl;
523         struct config *conf;
524
525         serv = container_of(u, struct listener, fd);
526         conf = serv->conf;
527
528         /* defer client if maximum number of requests is exceeded */
529         if (serv->n_clients >= conf->max_requests)
530                 return;
531
532         /* handle new connections */
533         if ((new_fd = accept(u->fd, NULL, 0)) != -1)
534         {
535                 D("SRV: Server(%d) accept => Client(%d)\n", u->fd, new_fd);
536
537                 /* add to global client list */
538                 if ((cl = uh_client_add(new_fd, serv)) != NULL)
539                 {
540                         /* add client socket to global fdset */
541                         uh_ufd_add(&cl->fd, uh_socket_cb, ULOOP_READ);
542                         fd_cloexec(cl->fd.fd);
543
544 #ifdef HAVE_TLS
545                         /* setup client tls context */
546                         if (conf->tls)
547                         {
548                                 if (conf->tls_accept(cl) < 1)
549                                 {
550                                         D("SRV: Client(%d) SSL handshake failed, drop\n", new_fd);
551
552                                         /* remove from global client list */
553                                         uh_client_remove(cl);
554                                         return;
555                                 }
556                         }
557 #endif
558                 }
559
560                 /* insufficient resources */
561                 else
562                 {
563                         fprintf(stderr, "uh_client_add(): Cannot allocate memory\n");
564                         close(new_fd);
565                 }
566         }
567 }
568
569 static void uh_client_cb(struct client *cl, unsigned int events);
570
571 static void uh_rpipe_cb(struct uloop_fd *u, unsigned int events)
572 {
573         struct client *cl = container_of(u, struct client, rpipe);
574
575         D("SRV: Client(%d) rpipe readable\n", cl->fd.fd);
576
577         uh_client_cb(cl, ULOOP_WRITE);
578 }
579
580 static void uh_socket_cb(struct uloop_fd *u, unsigned int events)
581 {
582         struct client *cl = container_of(u, struct client, fd);
583
584         D("SRV: Client(%d) socket readable\n", cl->fd.fd);
585
586         uh_client_cb(cl, ULOOP_READ);
587 }
588
589 static void uh_child_cb(struct uloop_process *p, int rv)
590 {
591         struct client *cl = container_of(p, struct client, proc);
592
593         D("SRV: Client(%d) child(%d) dead\n", cl->fd.fd, cl->proc.pid);
594
595         uh_client_cb(cl, ULOOP_READ | ULOOP_WRITE);
596 }
597
598 static void uh_kill9_cb(struct uloop_timeout *t)
599 {
600         struct client *cl = container_of(t, struct client, timeout);
601
602         if (!kill(cl->proc.pid, 0))
603         {
604                 D("SRV: Client(%d) child(%d) kill(SIGKILL)...\n",
605                   cl->fd.fd, cl->proc.pid);
606
607                 kill(cl->proc.pid, SIGKILL);
608         }
609 }
610
611 static void uh_timeout_cb(struct uloop_timeout *t)
612 {
613         struct client *cl = container_of(t, struct client, timeout);
614
615         D("SRV: Client(%d) child(%d) timed out\n", cl->fd.fd, cl->proc.pid);
616
617         if (!kill(cl->proc.pid, 0))
618         {
619                 D("SRV: Client(%d) child(%d) kill(SIGTERM)...\n",
620                   cl->fd.fd, cl->proc.pid);
621
622                 kill(cl->proc.pid, SIGTERM);
623
624                 cl->timeout.cb = uh_kill9_cb;
625                 uloop_timeout_set(&cl->timeout, 1000);
626         }
627 }
628
629 static void uh_client_cb(struct client *cl, unsigned int events)
630 {
631         int i;
632         struct config *conf;
633         struct http_request *req;
634
635         conf = cl->server->conf;
636
637         D("SRV: Client(%d) enter callback\n", cl->fd.fd);
638
639         /* undispatched yet */
640         if (!cl->dispatched)
641         {
642                 /* we have no headers yet and this was a write event, ignore... */
643                 if (!(events & ULOOP_READ))
644                 {
645                         D("SRV: Client(%d) ignoring write event before headers\n", cl->fd.fd);
646                         return;
647                 }
648
649                 /* attempt to receive and parse headers */
650                 if (!(req = uh_http_header_recv(cl)))
651                 {
652                         D("SRV: Client(%d) failed to receive header\n", cl->fd.fd);
653                         uh_client_shutdown(cl);
654                         return;
655                 }
656
657                 /* process expect headers */
658                 foreach_header(i, req->headers)
659                 {
660                         if (strcasecmp(req->headers[i], "Expect"))
661                                 continue;
662
663                         if (strcasecmp(req->headers[i+1], "100-continue"))
664                         {
665                                 D("SRV: Client(%d) unknown expect header (%s)\n",
666                                   cl->fd.fd, req->headers[i+1]);
667
668                                 uh_http_response(cl, 417, "Precondition Failed");
669                                 uh_client_shutdown(cl);
670                                 return;
671                         }
672                         else
673                         {
674                                 D("SRV: Client(%d) sending HTTP/1.1 100 Continue\n", cl->fd.fd);
675
676                                 uh_http_sendf(cl, NULL, "HTTP/1.1 100 Continue\r\n\r\n");
677                                 cl->httpbuf.len = 0; /* client will re-send the body */
678                                 break;
679                         }
680                 }
681
682                 /* RFC1918 filtering */
683                 if (conf->rfc1918_filter &&
684                         sa_rfc1918(&cl->peeraddr) && !sa_rfc1918(&cl->servaddr))
685                 {
686                         uh_http_sendhf(cl, 403, "Forbidden",
687                                                    "Rejected request from RFC1918 IP "
688                                                    "to public server address");
689
690                         uh_client_shutdown(cl);
691                         return;
692                 }
693
694                 /* dispatch request */
695                 if (!uh_dispatch_request(cl, req))
696                 {
697                         D("SRV: Client(%d) failed to dispach request\n", cl->fd.fd);
698                         uh_client_shutdown(cl);
699                         return;
700                 }
701
702                 /* request handler spawned a pipe, register handler */
703                 if (cl->rpipe.fd > -1)
704                 {
705                         D("SRV: Client(%d) pipe(%d) spawned\n", cl->fd.fd, cl->rpipe.fd);
706
707                         uh_ufd_add(&cl->rpipe, uh_rpipe_cb, ULOOP_READ);
708                 }
709
710                 /* request handler spawned a child, register handler */
711                 if (cl->proc.pid)
712                 {
713                         D("SRV: Client(%d) child(%d) spawned\n", cl->fd.fd, cl->proc.pid);
714
715                         cl->proc.cb = uh_child_cb;
716                         uloop_process_add(&cl->proc);
717
718                         cl->timeout.cb = uh_timeout_cb;
719                         uloop_timeout_set(&cl->timeout, conf->script_timeout * 1000);
720                 }
721
722                 /* header processing complete */
723                 D("SRV: Client(%d) dispatched\n", cl->fd.fd);
724                 cl->dispatched = true;
725         }
726
727         if (!cl->cb(cl))
728         {
729                 D("SRV: Client(%d) response callback signalized EOF\n", cl->fd.fd);
730                 uh_client_shutdown(cl);
731                 return;
732         }
733 }
734
735 #ifdef HAVE_TLS
736 static inline int uh_inittls(struct config *conf)
737 {
738         /* library handle */
739         void *lib;
740
741         /* already loaded */
742         if (conf->tls != NULL)
743                 return 0;
744
745         /* load TLS plugin */
746         if (!(lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL)))
747         {
748                 fprintf(stderr,
749                                 "Notice: Unable to load TLS plugin - disabling SSL support! "
750                                 "(Reason: %s)\n", dlerror()
751                 );
752
753                 return 1;
754         }
755         else
756         {
757                 /* resolve functions */
758                 if (!(conf->tls_init   = dlsym(lib, "uh_tls_ctx_init"))      ||
759                     !(conf->tls_cert   = dlsym(lib, "uh_tls_ctx_cert"))      ||
760                     !(conf->tls_key    = dlsym(lib, "uh_tls_ctx_key"))       ||
761                     !(conf->tls_free   = dlsym(lib, "uh_tls_ctx_free"))      ||
762                     !(conf->tls_accept = dlsym(lib, "uh_tls_client_accept")) ||
763                     !(conf->tls_close  = dlsym(lib, "uh_tls_client_close"))  ||
764                     !(conf->tls_recv   = dlsym(lib, "uh_tls_client_recv"))   ||
765                     !(conf->tls_send   = dlsym(lib, "uh_tls_client_send")))
766                 {
767                         fprintf(stderr,
768                                         "Error: Failed to lookup required symbols "
769                                         "in TLS plugin: %s\n", dlerror()
770                         );
771                         exit(1);
772                 }
773
774                 /* init SSL context */
775                 if (!(conf->tls = conf->tls_init()))
776                 {
777                         fprintf(stderr, "Error: Failed to initalize SSL context\n");
778                         exit(1);
779                 }
780         }
781
782         return 0;
783 }
784 #endif
785
786 int main (int argc, char **argv)
787 {
788         /* master file descriptor list */
789         fd_set serv_fds;
790
791         /* working structs */
792         struct addrinfo hints;
793         struct sigaction sa;
794         struct config conf;
795
796         /* maximum file descriptor number */
797         int cur_fd, max_fd = 0;
798
799 #ifdef HAVE_TLS
800         int tls = 0;
801         int keys = 0;
802 #endif
803
804         int bound = 0;
805         int nofork = 0;
806
807         /* args */
808         int opt;
809         char bind[128];
810         char *port = NULL;
811
812 #ifdef HAVE_LUA
813         /* library handle */
814         void *lib;
815 #endif
816
817         FD_ZERO(&serv_fds);
818
819         /* handle SIGPIPE, SIGINT, SIGTERM */
820         sa.sa_flags = 0;
821         sigemptyset(&sa.sa_mask);
822
823         sa.sa_handler = SIG_IGN;
824         sigaction(SIGPIPE, &sa, NULL);
825
826         sa.sa_handler = uh_sigterm;
827         sigaction(SIGINT,  &sa, NULL);
828         sigaction(SIGTERM, &sa, NULL);
829
830         /* prepare addrinfo hints */
831         memset(&hints, 0, sizeof(hints));
832         hints.ai_family   = AF_UNSPEC;
833         hints.ai_socktype = SOCK_STREAM;
834         hints.ai_flags    = AI_PASSIVE;
835
836         /* parse args */
837         memset(&conf, 0, sizeof(conf));
838         memset(bind, 0, sizeof(bind));
839
840         uloop_init();
841
842         while ((opt = getopt(argc, argv,
843                                                  "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:n:x:i:t:T:A:u:U:")) > 0)
844         {
845                 switch(opt)
846                 {
847                         /* [addr:]port */
848                         case 'p':
849                         case 's':
850                                 if ((port = strrchr(optarg, ':')) != NULL)
851                                 {
852                                         if ((optarg[0] == '[') && (port > optarg) && (port[-1] == ']'))
853                                                 memcpy(bind, optarg + 1,
854                                                         min(sizeof(bind), (int)(port - optarg) - 2));
855                                         else
856                                                 memcpy(bind, optarg,
857                                                         min(sizeof(bind), (int)(port - optarg)));
858
859                                         port++;
860                                 }
861                                 else
862                                 {
863                                         port = optarg;
864                                 }
865
866 #ifdef HAVE_TLS
867                                 if (opt == 's')
868                                 {
869                                         if (uh_inittls(&conf))
870                                         {
871                                                 fprintf(stderr,
872                                                         "Notice: TLS support is disabled, "
873                                                         "ignoring '-s %s'\n", optarg
874                                                 );
875                                                 continue;
876                                         }
877
878                                         tls = 1;
879                                 }
880 #endif
881
882                                 /* bind sockets */
883                                 bound += uh_socket_bind(&serv_fds, &max_fd,
884                                                                                 bind[0] ? bind : NULL,
885                                                                                 port, &hints, (opt == 's'), &conf);
886
887                                 memset(bind, 0, sizeof(bind));
888                                 break;
889
890 #ifdef HAVE_TLS
891                         /* certificate */
892                         case 'C':
893                                 if (!uh_inittls(&conf))
894                                 {
895                                         if (conf.tls_cert(conf.tls, optarg) < 1)
896                                         {
897                                                 fprintf(stderr,
898                                                                 "Error: Invalid certificate file given\n");
899                                                 exit(1);
900                                         }
901
902                                         keys++;
903                                 }
904
905                                 break;
906
907                         /* key */
908                         case 'K':
909                                 if (!uh_inittls(&conf))
910                                 {
911                                         if (conf.tls_key(conf.tls, optarg) < 1)
912                                         {
913                                                 fprintf(stderr,
914                                                                 "Error: Invalid private key file given\n");
915                                                 exit(1);
916                                         }
917
918                                         keys++;
919                                 }
920
921                                 break;
922 #endif
923
924                         /* docroot */
925                         case 'h':
926                                 if (! realpath(optarg, conf.docroot))
927                                 {
928                                         fprintf(stderr, "Error: Invalid directory %s: %s\n",
929                                                         optarg, strerror(errno));
930                                         exit(1);
931                                 }
932                                 break;
933
934                         /* error handler */
935                         case 'E':
936                                 if ((strlen(optarg) == 0) || (optarg[0] != '/'))
937                                 {
938                                         fprintf(stderr, "Error: Invalid error handler: %s\n",
939                                                         optarg);
940                                         exit(1);
941                                 }
942                                 conf.error_handler = optarg;
943                                 break;
944
945                         /* index file */
946                         case 'I':
947                                 if ((strlen(optarg) == 0) || (optarg[0] == '/'))
948                                 {
949                                         fprintf(stderr, "Error: Invalid index page: %s\n",
950                                                         optarg);
951                                         exit(1);
952                                 }
953                                 conf.index_file = optarg;
954                                 break;
955
956                         /* don't follow symlinks */
957                         case 'S':
958                                 conf.no_symlinks = 1;
959                                 break;
960
961                         /* don't list directories */
962                         case 'D':
963                                 conf.no_dirlists = 1;
964                                 break;
965
966                         case 'R':
967                                 conf.rfc1918_filter = 1;
968                                 break;
969
970                         case 'n':
971                                 conf.max_requests = atoi(optarg);
972                                 break;
973
974 #ifdef HAVE_CGI
975                         /* cgi prefix */
976                         case 'x':
977                                 conf.cgi_prefix = optarg;
978                                 break;
979
980                         /* interpreter */
981                         case 'i':
982                                 if ((optarg[0] == '.') && (port = strchr(optarg, '=')))
983                                 {
984                                         *port++ = 0;
985                                         uh_interpreter_add(optarg, port);
986                                 }
987                                 else
988                                 {
989                                         fprintf(stderr, "Error: Invalid interpreter: %s\n",
990                                                         optarg);
991                                         exit(1);
992                                 }
993                                 break;
994 #endif
995
996 #ifdef HAVE_LUA
997                         /* lua prefix */
998                         case 'l':
999                                 conf.lua_prefix = optarg;
1000                                 break;
1001
1002                         /* lua handler */
1003                         case 'L':
1004                                 conf.lua_handler = optarg;
1005                                 break;
1006 #endif
1007
1008 #ifdef HAVE_UBUS
1009                         /* ubus prefix */
1010                         case 'u':
1011                                 conf.ubus_prefix = optarg;
1012                                 break;
1013
1014                         /* ubus socket */
1015                         case 'U':
1016                                 conf.ubus_socket = optarg;
1017                                 break;
1018 #endif
1019
1020 #if defined(HAVE_CGI) || defined(HAVE_LUA)
1021                         /* script timeout */
1022                         case 't':
1023                                 conf.script_timeout = atoi(optarg);
1024                                 break;
1025 #endif
1026
1027                         /* network timeout */
1028                         case 'T':
1029                                 conf.network_timeout = atoi(optarg);
1030                                 break;
1031
1032                         /* tcp keep-alive */
1033                         case 'A':
1034                                 conf.tcp_keepalive = atoi(optarg);
1035                                 break;
1036
1037                         /* no fork */
1038                         case 'f':
1039                                 nofork = 1;
1040                                 break;
1041
1042                         /* urldecode */
1043                         case 'd':
1044                                 if ((port = malloc(strlen(optarg)+1)) != NULL)
1045                                 {
1046                                         /* "decode" plus to space to retain compat */
1047                                         for (opt = 0; optarg[opt]; opt++)
1048                                                 if (optarg[opt] == '+')
1049                                                         optarg[opt] = ' ';
1050                                         /* opt now contains strlen(optarg) -- no need to re-scan */
1051                                         memset(port, 0, opt+1);
1052                                         if (uh_urldecode(port, opt, optarg, opt) < 0)
1053                                             fprintf(stderr, "uhttpd: invalid encoding\n");
1054
1055                                         printf("%s", port);
1056                                         free(port);
1057                                         exit(0);
1058                                 }
1059                                 break;
1060
1061                         /* basic auth realm */
1062                         case 'r':
1063                                 conf.realm = optarg;
1064                                 break;
1065
1066                         /* md5 crypt */
1067                         case 'm':
1068                                 printf("%s\n", crypt(optarg, "$1$"));
1069                                 exit(0);
1070                                 break;
1071
1072                         /* config file */
1073                         case 'c':
1074                                 conf.file = optarg;
1075                                 break;
1076
1077                         default:
1078                                 fprintf(stderr,
1079                                         "Usage: %s -p [addr:]port [-h docroot]\n"
1080                                         "       -f              Do not fork to background\n"
1081                                         "       -c file         Configuration file, default is '/etc/httpd.conf'\n"
1082                                         "       -p [addr:]port  Bind to specified address and port, multiple allowed\n"
1083 #ifdef HAVE_TLS
1084                                         "       -s [addr:]port  Like -p but provide HTTPS on this port\n"
1085                                         "       -C file         ASN.1 server certificate file\n"
1086                                         "       -K file         ASN.1 server private key file\n"
1087 #endif
1088                                         "       -h directory    Specify the document root, default is '.'\n"
1089                                         "       -E string       Use given virtual URL as 404 error handler\n"
1090                                         "       -I string       Use given filename as index page for directories\n"
1091                                         "       -S              Do not follow symbolic links outside of the docroot\n"
1092                                         "       -D              Do not allow directory listings, send 403 instead\n"
1093                                         "       -R              Enable RFC1918 filter\n"
1094                                         "       -n count        Maximum allowed number of concurrent requests\n"
1095 #ifdef HAVE_LUA
1096                                         "       -l string       URL prefix for Lua handler, default is '/lua'\n"
1097                                         "       -L file         Lua handler script, omit to disable Lua\n"
1098 #endif
1099 #ifdef HAVE_UBUS
1100                                         "       -u string       URL prefix for HTTP/JSON handler\n"
1101                                         "       -U file         Override ubus socket path\n"
1102 #endif
1103 #ifdef HAVE_CGI
1104                                         "       -x string       URL prefix for CGI handler, default is '/cgi-bin'\n"
1105                                         "       -i .ext=path    Use interpreter at path for files with the given extension\n"
1106 #endif
1107 #if defined(HAVE_CGI) || defined(HAVE_LUA) || defined(HAVE_UBUS)
1108                                         "       -t seconds      CGI, Lua and UBUS script timeout in seconds, default is 60\n"
1109 #endif
1110                                         "       -T seconds      Network timeout in seconds, default is 30\n"
1111                                         "       -d string       URL decode given string\n"
1112                                         "       -r string       Specify basic auth realm\n"
1113                                         "       -m string       MD5 crypt given string\n"
1114                                         "\n", argv[0]
1115                                 );
1116
1117                                 exit(1);
1118                 }
1119         }
1120
1121 #ifdef HAVE_TLS
1122         if ((tls == 1) && (keys < 2))
1123         {
1124                 fprintf(stderr, "Error: Missing private key or certificate file\n");
1125                 exit(1);
1126         }
1127 #endif
1128
1129         if (bound < 1)
1130         {
1131                 fprintf(stderr, "Error: No sockets bound, unable to continue\n");
1132                 exit(1);
1133         }
1134
1135         /* default docroot */
1136         if (!conf.docroot[0] && !realpath(".", conf.docroot))
1137         {
1138                 fprintf(stderr, "Error: Can not determine default document root: %s\n",
1139                         strerror(errno));
1140                 exit(1);
1141         }
1142
1143         /* default realm */
1144         if (!conf.realm)
1145                 conf.realm = "Protected Area";
1146
1147         /* config file */
1148         uh_config_parse(&conf);
1149
1150         /* default max requests */
1151         if (conf.max_requests <= 0)
1152                 conf.max_requests = 3;
1153
1154         /* default network timeout */
1155         if (conf.network_timeout <= 0)
1156                 conf.network_timeout = 30;
1157
1158 #if defined(HAVE_CGI) || defined(HAVE_LUA) || defined(HAVE_UBUS)
1159         /* default script timeout */
1160         if (conf.script_timeout <= 0)
1161                 conf.script_timeout = 60;
1162 #endif
1163
1164 #ifdef HAVE_CGI
1165         /* default cgi prefix */
1166         if (!conf.cgi_prefix)
1167                 conf.cgi_prefix = "/cgi-bin";
1168 #endif
1169
1170 #ifdef HAVE_LUA
1171         /* load Lua plugin */
1172         if (!(lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)))
1173         {
1174                 fprintf(stderr,
1175                                 "Notice: Unable to load Lua plugin - disabling Lua support! "
1176                                 "(Reason: %s)\n", dlerror());
1177         }
1178         else
1179         {
1180                 /* resolve functions */
1181                 if (!(conf.lua_init    = dlsym(lib, "uh_lua_init"))    ||
1182                     !(conf.lua_close   = dlsym(lib, "uh_lua_close"))   ||
1183                     !(conf.lua_request = dlsym(lib, "uh_lua_request")))
1184                 {
1185                         fprintf(stderr,
1186                                         "Error: Failed to lookup required symbols "
1187                                         "in Lua plugin: %s\n", dlerror()
1188                         );
1189                         exit(1);
1190                 }
1191
1192                 /* init Lua runtime if handler is specified */
1193                 if (conf.lua_handler)
1194                 {
1195                         /* default lua prefix */
1196                         if (!conf.lua_prefix)
1197                                 conf.lua_prefix = "/lua";
1198
1199                         conf.lua_state = conf.lua_init(&conf);
1200                 }
1201         }
1202 #endif
1203
1204 #ifdef HAVE_UBUS
1205         /* load ubus plugin */
1206         if (!(lib = dlopen("uhttpd_ubus.so", RTLD_LAZY | RTLD_GLOBAL)))
1207         {
1208                 fprintf(stderr,
1209                                 "Notice: Unable to load ubus plugin - disabling ubus support! "
1210                                 "(Reason: %s)\n", dlerror());
1211         }
1212         else if (conf.ubus_prefix)
1213         {
1214                 /* resolve functions */
1215                 if (!(conf.ubus_init    = dlsym(lib, "uh_ubus_init"))    ||
1216                     !(conf.ubus_close   = dlsym(lib, "uh_ubus_close"))   ||
1217                     !(conf.ubus_request = dlsym(lib, "uh_ubus_request")))
1218                 {
1219                         fprintf(stderr,
1220                                         "Error: Failed to lookup required symbols "
1221                                         "in ubus plugin: %s\n", dlerror()
1222                         );
1223                         exit(1);
1224                 }
1225
1226                 /* initialize ubus */
1227                 conf.ubus_state = conf.ubus_init(&conf);
1228         }
1229 #endif
1230
1231         /* fork (if not disabled) */
1232         if (!nofork)
1233         {
1234                 switch (fork())
1235                 {
1236                         case -1:
1237                                 perror("fork()");
1238                                 exit(1);
1239
1240                         case 0:
1241                                 /* daemon setup */
1242                                 if (chdir("/"))
1243                                         perror("chdir()");
1244
1245                                 if ((cur_fd = open("/dev/null", O_WRONLY)) > -1)
1246                                         dup2(cur_fd, 0);
1247
1248                                 if ((cur_fd = open("/dev/null", O_RDONLY)) > -1)
1249                                         dup2(cur_fd, 1);
1250
1251                                 if ((cur_fd = open("/dev/null", O_RDONLY)) > -1)
1252                                         dup2(cur_fd, 2);
1253
1254                                 break;
1255
1256                         default:
1257                                 exit(0);
1258                 }
1259         }
1260
1261         /* server main loop */
1262         uloop_run();
1263
1264 #ifdef HAVE_LUA
1265         /* destroy the Lua state */
1266         if (conf.lua_state != NULL)
1267                 conf.lua_close(conf.lua_state);
1268 #endif
1269
1270 #ifdef HAVE_UBUS
1271         /* destroy the ubus state */
1272         if (conf.ubus_state != NULL)
1273                 conf.ubus_close(conf.ubus_state);
1274 #endif
1275
1276         return 0;
1277 }