miniupnpd 1.9 (20160113)
[tomato.git] / release / src / router / httpd / httpd.c
blobf60cfc17ce53502105a6727488f2a5605cc10105
1 /*
3 micro_httpd/mini_httpd
5 Copyright © 1999,2000 by Jef Poskanzer <jef@acme.com>.
6 All rights reserved.
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 SUCH DAMAGE.
32 Copyright 2003, CyberTAN Inc. All Rights Reserved
34 This is UNPUBLISHED PROPRIETARY SOURCE CODE of CyberTAN Inc.
35 the contents of this file may not be disclosed to third parties,
36 copied or duplicated in any form without the prior written
37 permission of CyberTAN Inc.
39 This software should be used as a reference only, and it not
40 intended for production use!
42 THIS SOFTWARE IS OFFERED "AS IS", AND CYBERTAN GRANTS NO WARRANTIES OF ANY
43 KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. CYBERTAN
44 SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
45 FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE
50 Modified for Tomato Firmware
51 Portions, Copyright (C) 2006-2009 Jonathan Zarate
55 #include "tomato.h"
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <time.h>
59 #include <netinet/in.h>
60 #include <sys/types.h>
61 #include <sys/socket.h>
62 #include <arpa/inet.h>
63 #include <getopt.h>
64 #include <stdarg.h>
65 #include <sys/wait.h>
66 #include <error.h>
67 #include <sys/signal.h>
68 #include <netinet/tcp.h>
69 #include <sys/stat.h>
71 #include <wlutils.h>
74 #include "../mssl/mssl.h"
75 int do_ssl;
77 #define HTTP_MAX_LISTENERS 8
79 typedef struct {
80 int count;
81 fd_set lfdset;
82 struct {
83 int listenfd;
84 int ssl;
85 } listener[HTTP_MAX_LISTENERS];
86 } listeners_t;
87 static listeners_t listeners;
88 static int maxfd = -1;
90 int post;
91 int connfd = -1;
92 FILE *connfp = NULL;
93 struct sockaddr_storage clientsai;
95 int header_sent;
96 char *user_agent;
97 // int hidok = 0;
99 const char mime_html[] = "text/html; charset=utf-8";
100 const char mime_plain[] = "text/plain";
101 const char mime_javascript[] = "text/javascript";
102 const char mime_binary[] = "application/tomato-binary-file"; // instead of "application/octet-stream" to make browser just "save as" and prevent automatic detection weirdness -- zzz
103 const char mime_octetstream[] = "application/octet-stream";
105 static int match(const char* pattern, const char* string);
106 static int match_one(const char* pattern, int patternlen, const char* string);
107 static void handle_request(void);
110 // --------------------------------------------------------------------------------------------------------------------
113 static const char *http_status_desc(int status)
115 switch (status) {
116 case 200:
117 return "OK";
118 case 302:
119 return "Found";
120 case 400:
121 return "Invalid Request";
122 case 401:
123 return "Unauthorized";
124 case 404:
125 return "Not Found";
126 case 501:
127 return "Not Implemented";
129 return "Unknown";
132 void send_header(int status, const char* header, const char* mime, int cache)
134 time_t now;
135 char tms[128];
137 now = time(NULL);
138 if (now < Y2K) now += Y2K;
139 strftime(tms, sizeof(tms), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); // RFC 1123
140 web_printf("HTTP/1.0 %d %s\r\n"
141 "Date: %s\r\n",
142 status, http_status_desc(status),
143 tms);
145 if (mime) web_printf("Content-Type: %s\r\n", mime);
146 if (cache > 0) {
147 web_printf("Cache-Control: max-age=%d\r\n", cache * 60);
149 else {
150 web_puts("Cache-Control: no-cache, no-store, must-revalidate, private\r\n"
151 "Expires: Thu, 31 Dec 1970 00:00:00 GMT\r\n"
152 "Pragma: no-cache\r\n");
154 if (header) web_printf("%s\r\n", header);
155 web_puts("Connection: close\r\n\r\n");
157 header_sent = 1;
160 void send_error(int status, const char *header, const char *text)
162 const char *s = http_status_desc(status);
163 send_header(status, header, mime_html, 0);
164 web_printf(
165 "<html>"
166 "<head><title>Error</title></head>"
167 "<body>"
168 "<h2>%d %s</h2> %s"
169 "</body></html>",
170 status, s, text ? text : s);
173 void redirect(const char *path)
175 char s[512];
177 snprintf(s, sizeof(s), "Location: %s", path);
178 send_header(302, s, mime_html, 0);
179 web_puts(s);
181 _dprintf("Redirect: %s\n", path);
184 int skip_header(int *len)
186 char buf[2048];
188 while (*len > 0) {
189 if (!web_getline(buf, MIN(*len, sizeof(buf)))) {
190 break;
192 *len -= strlen(buf);
193 if ((strcmp(buf, "\n") == 0) || (strcmp(buf, "\r\n") == 0)) {
194 return 1;
197 return 0;
201 // -----------------------------------------------------------------------------
203 static void eat_garbage(void)
205 int i;
206 int flags;
208 // eat garbage \r\n (IE6, ...) or browser ends up with a tcp reset error message
209 if ((!do_ssl) && (post)) {
210 if (((flags = fcntl(connfd, F_GETFL)) != -1) && (fcntl(connfd, F_SETFL, flags | O_NONBLOCK) != -1)) {
211 // if (fgetc(connfp) != EOF) fgetc(connfp);
212 for (i = 0; i < 1024; ++i) {
213 if (fgetc(connfp) == EOF) break;
215 fcntl(connfd, F_SETFL, flags);
220 // -----------------------------------------------------------------------------
223 static void send_authenticate(void)
225 char header[128];
226 const char *realm;
228 realm = nvram_get("router_name");
229 if ((realm == NULL) || (*realm == 0) || (strlen(realm) > 64)) realm = "unknown";
231 sprintf(header, "WWW-Authenticate: Basic realm=\"%s\"", realm);
232 send_error(401, header, NULL);
235 typedef enum { AUTH_NONE, AUTH_OK, AUTH_BAD } auth_t;
237 static auth_t auth_check(const char *authorization)
239 char buf[512];
240 const char *u, *p;
241 char* pass;
242 int len;
244 if ((authorization != NULL) && (strncasecmp(authorization, "Basic ", 6) == 0)) {
245 if (base64_decoded_len(strlen(authorization + 6)) <= sizeof(buf)) {
246 len = base64_decode(authorization + 6, buf, strlen(authorization) - 6);
247 buf[len] = 0;
248 if ((pass = strchr(buf, ':')) != NULL) {
249 *pass++ = 0;
250 if (((u = nvram_get("http_username")) == NULL) || (*u == 0)) u = "admin";
251 if ((strcmp(buf, "root") == 0) || (strcmp(buf, u) == 0)) {
253 if ((nvram_match("http_root", "0")) && (strcmp(buf, "root") == 0)) {
254 return AUTH_BAD;
255 } else {
256 if (((p = nvram_get("http_passwd")) == NULL) || (*p == 0)) p = "admin";
257 if (strcmp(pass, p) == 0) {
258 return AUTH_OK;
264 return AUTH_BAD;
267 return AUTH_NONE;
270 static void auth_fail(int clen)
272 if (post) web_eat(clen);
273 eat_garbage();
274 send_authenticate();
277 static int check_wif(int idx, int unit, int subunit, void *param)
279 sta_info_t *sti = param;
280 return (wl_ioctl(nvram_safe_get(wl_nvname("ifname", unit, subunit)), WLC_GET_VAR, sti, sizeof(*sti)) == 0);
283 static int check_wlaccess(void)
285 char mac[32];
286 char ifname[32];
287 sta_info_t sti;
289 if (nvram_match("web_wl_filter", "1")) {
290 if (get_client_info(mac, ifname)) {
291 memset(&sti, 0, sizeof(sti));
292 strcpy((char *)&sti, "sta_info"); // sta_info0<mac>
293 ether_atoe(mac, (char *)&sti + 9);
294 if (foreach_wif(1, &sti, check_wif)) {
295 if (nvram_match("debug_logwlac", "1")) {
296 syslog(LOG_WARNING, "Wireless access from %s blocked.", mac);
298 return 0;
302 return 1;
305 // -----------------------------------------------------------------------------
307 static int match_one(const char* pattern, int patternlen, const char* string)
309 const char* p;
311 for (p = pattern; p - pattern < patternlen; ++p, ++string) {
312 if (*p == '?' && *string != '\0')
313 continue;
314 if (*p == '*') {
315 int i, pl;
316 ++p;
317 if (*p == '*') {
318 /* Double-wildcard matches anything. */
319 ++p;
320 i = strlen(string);
321 } else
322 /* Single-wildcard matches anything but slash. */
323 i = strcspn(string, "/");
324 pl = patternlen - (p - pattern);
325 for (; i >= 0; --i)
326 if (match_one(p, pl, &(string[i])))
327 return 1;
328 return 0;
330 if (*p != *string)
331 return 0;
333 if (*string == '\0')
334 return 1;
335 return 0;
338 // Simple shell-style filename matcher. Only does ? * and **, and multiple
339 // patterns separated by |. Returns 1 or 0.
340 static int match(const char* pattern, const char* string)
342 const char* p;
344 for (;;) {
345 p = strchr(pattern, '|');
346 if (p == NULL) return match_one(pattern, strlen(pattern), string);
347 if (match_one(pattern, p - pattern, string)) return 1;
348 pattern = p + 1;
353 void do_file(char *path)
355 FILE *f;
356 char buf[1024];
357 int nr;
358 if ((f = fopen(path, "r")) != NULL) {
359 while ((nr = fread(buf, 1, sizeof(buf), f)) > 0)
360 web_write(buf, nr);
361 fclose(f);
365 static void handle_request(void)
367 char line[10000], *cur;
368 char *method, *path, *protocol, *authorization, *boundary;
369 char *cp;
370 char *file;
371 const struct mime_handler *handler;
372 int cl = 0;
373 auth_t auth;
375 user_agent = "";
376 header_sent = 0;
377 authorization = boundary = NULL;
378 bzero(line, sizeof(line));
380 // Parse the first line of the request.
381 if (!web_getline(line, sizeof(line))) {
382 send_error(400, NULL, NULL);
383 return;
386 _dprintf("%s\n", line);
388 method = path = line;
389 strsep(&path, " ");
390 if (!path) { // Avoid http server crash, added by honor 2003-12-08
391 send_error(400, NULL, NULL);
392 return;
394 while (*path == ' ') path++;
396 if ((strcasecmp(method, "GET") != 0) && (strcasecmp(method, "POST") != 0)) {
397 send_error(501, NULL, NULL);
398 return;
401 protocol = path;
402 strsep(&protocol, " ");
403 if (!protocol) { // Avoid http server crash, added by honor 2003-12-08
404 send_error(400, NULL, NULL);
405 return;
407 while (*protocol == ' ') protocol++;
409 if (path[0] != '/') {
410 send_error(400, NULL, NULL);
411 return;
413 file = path + 1;
415 #if 0
416 const char *hid;
417 int n;
419 hid = nvram_safe_get("http_id");
420 n = strlen(hid);
421 cprintf("file=%s hid=%s n=%d +n=%s\n", file, hid, n, path + n + 1);
422 if ((strncmp(file, hid, n) == 0) && (path[n + 1] == '/')) {
423 path += n + 1;
424 file = path + 1;
425 hidok = 1;
427 cprintf("OK path=%s file=%s\n", path, file);
429 #endif
431 if ((cp = strchr(file, '?')) != NULL) {
432 *cp = 0;
433 setenv("QUERY_STRING", cp + 1, 1);
434 webcgi_init(cp + 1);
437 if ((file[0] == '/') || (strncmp(file, "..", 2) == 0) || (strstr(file, "/../") != NULL) ||
438 (strcmp(file + (strlen(file) - 3), "/..") == 0)) {
439 send_error(400, NULL, NULL);
440 return;
443 if ((file[0] == 0) || (strcmp(file, "index.asp") == 0)) {
444 file = "status-overview.asp";
446 else if ((strcmp(file, "ext/") == 0) || (strcmp(file, "ext") == 0)) {
447 file = "ext/index.asp";
450 cp = protocol;
451 strsep(&cp, " ");
452 cur = protocol + strlen(protocol) + 1;
454 while (web_getline(cur, line + sizeof(line) - cur)) {
455 if ((strcmp(cur, "\n") == 0) || (strcmp(cur, "\r\n") == 0)) {
456 break;
459 if (strncasecmp(cur, "Authorization:", 14) == 0) {
460 cp = &cur[14];
461 cp += strspn(cp, " \t");
462 authorization = cp;
463 cur = cp + strlen(cp) + 1;
465 else if (strncasecmp(cur, "Content-Length:", 15) == 0) {
466 cp = &cur[15];
467 cp += strspn(cp, " \t");
468 cl = strtoul(cp, NULL, 0);
469 if ((cl < 0) || (cl >= INT_MAX)) {
470 send_error(400, NULL, NULL);
471 return;
474 else if ((strncasecmp(cur, "Content-Type:", 13) == 0) && ((cp = strstr(cur, "boundary=")))) {
475 boundary = &cp[9];
476 for (cp = cp + 9; *cp && *cp != '\r' && *cp != '\n'; cp++);
477 *cp = '\0';
478 cur = ++cp;
480 else if (strncasecmp(cur, "User-Agent:", 11) == 0) {
481 user_agent = cur + 11;
482 user_agent += strspn(user_agent, " \t");
483 cur = user_agent + strlen(user_agent);
484 *cur++ = 0;
488 post = (strcasecmp(method, "post") == 0);
490 auth = auth_check(authorization);
492 #if 0
493 cprintf("UserAgent: %s\n", user_agent);
494 switch (auth) {
495 case AUTH_NONE:
496 cprintf("AUTH_NONE\n");
497 break;
498 case AUTH_OK:
499 cprintf("AUTH_OK\n");
500 break;
501 case AUTH_BAD:
502 cprintf("AUTH_BAD\n");
503 break;
504 default:
505 cprintf("AUTH_?\n");
506 break;
508 #endif
512 Chrome 1.0.154:
513 - User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.53 Safari/525.19
514 - It *always* sends an 'AUTH_NONE request' first. This results in a
515 send_authenticate, then finally sends an 'AUTH_OK request'.
516 - It does not clear the authentication even if AUTH_NONE request succeeds.
517 - If user doesn't enter anything (blank) for credential, it sends the
518 previous credential.
522 if (strcmp(file, "logout") == 0) { // special case
523 wi_generic(file, cl, boundary);
524 eat_garbage();
526 if (strstr(user_agent, "Chrome/") != NULL) {
527 if (auth != AUTH_BAD) {
528 send_authenticate();
529 return;
532 else {
533 if (auth == AUTH_OK) {
534 send_authenticate();
535 return;
538 send_error(404, NULL, "Goodbye");
539 return;
542 if (auth == AUTH_BAD) {
543 auth_fail(cl);
544 return;
547 for (handler = &mime_handlers[0]; handler->pattern; handler++) {
548 if (match(handler->pattern, file)) {
549 if ((handler->auth) && (auth != AUTH_OK)) {
550 auth_fail(cl);
551 return;
554 if (handler->input) handler->input(file, cl, boundary);
555 eat_garbage();
556 if (handler->mime_type != NULL) send_header(200, NULL, handler->mime_type, handler->cache);
557 if (handler->output) handler->output(file);
558 return;
562 if (auth != AUTH_OK) {
563 auth_fail(cl);
564 return;
568 if ((!post) && (strchr(file, '.') == NULL)) {
569 cl = strlen(file);
570 if ((cl > 1) && (cl < 64)) {
571 char alt[128];
573 path = alt + 1;
574 strcpy(path, file);
576 cp = path + cl - 1;
577 if (*cp == '/') *cp = 0;
579 if ((cp = strrchr(path, '/')) != NULL) *cp = '-';
581 strcat(path, ".asp");
582 if (f_exists(path)) {
583 alt[0] = '/';
584 redirect(alt);
585 return;
591 send_error(404, NULL, NULL);
594 #ifdef TCONFIG_HTTPS
596 #define HTTPS_CRT_VER "1"
598 static void save_cert(void)
600 if (eval("tar", "-C", "/", "-czf", "/tmp/cert.tgz", "etc/cert.pem", "etc/key.pem") == 0) {
601 if (nvram_set_file("https_crt_file", "/tmp/cert.tgz", 8192)) {
602 nvram_set("crt_ver", HTTPS_CRT_VER);
603 nvram_commit_x();
606 unlink("/tmp/cert.tgz");
609 static void erase_cert(void)
611 unlink("/etc/cert.pem");
612 unlink("/etc/key.pem");
613 nvram_unset("https_crt_file");
614 nvram_unset("https_crt_gen");
617 static void start_ssl(void)
619 int ok;
620 int save;
621 int retry;
622 unsigned long long sn;
623 char t[32];
625 if (nvram_match("https_crt_gen", "1")) {
626 erase_cert();
629 retry = 1;
630 while (1) {
631 save = nvram_match("https_crt_save", "1");
633 if ((!f_exists("/etc/cert.pem")) || (!f_exists("/etc/key.pem"))) {
634 ok = 0;
635 if (save && nvram_match("crt_ver", HTTPS_CRT_VER)) {
636 if (nvram_get_file("https_crt_file", "/tmp/cert.tgz", 8192)) {
637 if (eval("tar", "-xzf", "/tmp/cert.tgz", "-C", "/", "etc/cert.pem", "etc/key.pem") == 0)
638 ok = 1;
639 unlink("/tmp/cert.tgz");
642 if (!ok) {
643 erase_cert();
644 syslog(LOG_INFO, "Generating SSL certificate...");
646 // browsers seems to like this when the ip address moves... -- zzz
647 f_read("/dev/urandom", &sn, sizeof(sn));
649 sprintf(t, "%llu", sn & 0x7FFFFFFFFFFFFFFFULL);
650 eval("gencert.sh", t);
654 if ((save) && (*nvram_safe_get("https_crt_file")) == 0) {
655 save_cert();
658 if (mssl_init("/etc/cert.pem", "/etc/key.pem")) return;
660 erase_cert();
662 syslog(retry ? LOG_WARNING : LOG_ERR, "Unable to start SSL");
663 if (!retry) exit(1);
664 retry = 0;
667 #endif
669 static void init_id(void)
671 char s[128];
672 unsigned long long n;
674 if (strncmp(nvram_safe_get("http_id"), "TID", 3) != 0) {
675 f_read("/dev/urandom", &n, sizeof(n));
676 sprintf(s, "TID%llx", n);
677 nvram_set("http_id", s);
680 nvram_unset("http_id_warn");
683 void check_id(const char *url)
685 if (!nvram_match("http_id", webcgi_safeget("_http_id", ""))) {
686 #if 0
687 const char *hid = nvram_safe_get("http_id");
688 if (url) {
689 const char *a;
690 int n;
692 // http://xxx/yyy/TID/zzz
693 if (((a = strrchr(url, '/')) != NULL) && (a != url)) {
694 n = strlen(hid);
695 a -= n;
696 if ((a > url) && (*(a - 1) == '/')) {
697 if (strncmp(a, hid, n) == 0) return;
702 #endif
704 char s[72];
705 char *i;
706 char *u;
707 #ifdef TCONFIG_IPV6
708 char a[INET6_ADDRSTRLEN];
709 #else
710 char a[INET_ADDRSTRLEN];
711 #endif
712 void *addr = NULL;
714 if (clientsai.ss_family == AF_INET)
715 addr = &( ((struct sockaddr_in*) &clientsai)->sin_addr );
716 #ifdef TCONFIG_IPV6
717 else if (clientsai.ss_family == AF_INET6)
718 addr = &( ((struct sockaddr_in6*) &clientsai)->sin6_addr );
719 #endif
720 inet_ntop(clientsai.ss_family, addr, a, sizeof(a));
722 sprintf(s, "%s,%ld", a, time(NULL) & 0xFFC0);
723 if (!nvram_match("http_id_warn", s)) {
724 nvram_set("http_id_warn", s);
726 strlcpy(s, webcgi_safeget("_http_id", ""), sizeof(s));
727 i = js_string(s); // quicky scrub
729 strlcpy(s, url, sizeof(s));
730 u = js_string(s);
732 if ((i != NULL) && (u != NULL)) {
733 syslog(LOG_WARNING, "Invalid ID '%s' from %s for /%s", i, a, u);
736 // free(u);
737 // free(i);
740 exit(1);
744 static void add_listen_socket(const char *addr, int server_port, int do_ipv6, int do_ssl)
746 int listenfd, n;
747 struct sockaddr_storage sai_stor;
749 #ifdef TCONFIG_IPV6
750 sa_family_t HTTPD_FAMILY = do_ipv6 ? AF_INET6 : AF_INET;
751 #else
752 #define HTTPD_FAMILY AF_INET
753 #endif
755 if (listeners.count >= HTTP_MAX_LISTENERS) {
756 syslog(LOG_ERR, "number of listeners exceeded the max allowed (%d)", HTTP_MAX_LISTENERS);
757 return;
760 if (server_port <= 0) {
761 IF_TCONFIG_HTTPS(if (do_ssl) server_port = 443; else)
762 server_port = 80;
765 if ((listenfd = socket(HTTPD_FAMILY, SOCK_STREAM, 0)) < 0) {
766 syslog(LOG_ERR, "create listening socket: %m");
767 return;
769 fcntl(listenfd, F_SETFD, FD_CLOEXEC);
771 n = 1;
772 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof(n));
774 #ifdef TCONFIG_IPV6
775 if (do_ipv6) {
776 struct sockaddr_in6 *sai = (struct sockaddr_in6 *) &sai_stor;
777 sai->sin6_family = HTTPD_FAMILY;
778 sai->sin6_port = htons(server_port);
779 if (addr && *addr)
780 inet_pton(HTTPD_FAMILY, addr, &(sai->sin6_addr));
781 else
782 sai->sin6_addr = in6addr_any;
783 n = 1;
784 setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&n, sizeof(n));
785 } else
786 #endif
788 struct sockaddr_in *sai = (struct sockaddr_in *) &sai_stor;
789 sai->sin_family = HTTPD_FAMILY;
790 sai->sin_port = htons(server_port);
791 sai->sin_addr.s_addr = (addr && *addr) ? inet_addr(addr) : INADDR_ANY;
794 if (bind(listenfd, (struct sockaddr *)&sai_stor, sizeof(sai_stor)) < 0) {
795 syslog(LOG_ERR, "bind: [%s]:%d: %m", (addr && *addr) ? addr : (IF_TCONFIG_IPV6(do_ipv6 ? "::" :) ""), server_port);
796 close(listenfd);
797 return;
800 if (listen(listenfd, 64) < 0) {
801 syslog(LOG_ERR, "listen: %m");
802 close(listenfd);
803 return;
806 if (listenfd >= 0) {
807 _dprintf("%s: added IPv%d listener [%d] for %s:%d, ssl=%d\n",
808 __FUNCTION__, do_ipv6 ? 6 : 4, listenfd,
809 (addr && *addr) ? addr : "addr_any", server_port, do_ssl);
810 listeners.listener[listeners.count].listenfd = listenfd;
811 listeners.listener[listeners.count++].ssl = do_ssl;
812 FD_SET(listenfd, &listeners.lfdset);
813 if (maxfd < listenfd) maxfd = listenfd;
817 static void setup_listeners(int do_ipv6)
819 char ipaddr[INET6_ADDRSTRLEN];
820 char ipaddr1[INET6_ADDRSTRLEN];
821 char ipaddr2[INET6_ADDRSTRLEN];
822 char ipaddr3[INET6_ADDRSTRLEN];
823 IF_TCONFIG_IPV6(const char *wanaddr);
824 int wanport, p;
825 IF_TCONFIG_IPV6(int wan6port);
827 wanport = nvram_get_int("http_wanport");
828 #ifdef TCONFIG_IPV6
829 wan6port = wanport;
830 if (do_ipv6) {
831 // get the configured routable IPv6 address from the lan iface.
832 // add_listen_socket() will fall back to in6addr_any
833 // if NULL or empty address is returned
834 strlcpy(ipaddr, getifaddr(nvram_safe_get("lan_ifname"), AF_INET6, 0) ? : "", sizeof(ipaddr));
836 else
837 #endif
838 strlcpy(ipaddr, nvram_safe_get("lan_ipaddr"), sizeof(ipaddr));
839 strlcpy(ipaddr1, nvram_safe_get("lan1_ipaddr"), sizeof(ipaddr));
840 strlcpy(ipaddr2, nvram_safe_get("lan2_ipaddr"), sizeof(ipaddr));
841 strlcpy(ipaddr3, nvram_safe_get("lan3_ipaddr"), sizeof(ipaddr));
843 if (!nvram_match("http_enable", "0")) {
844 p = nvram_get_int("http_lanport");
845 add_listen_socket(ipaddr, p, do_ipv6, 0);
846 if (strcmp(ipaddr1,"")!=0)
847 add_listen_socket(ipaddr1, p, do_ipv6, 0);
848 if (strcmp(ipaddr2,"")!=0)
849 add_listen_socket(ipaddr2, p, do_ipv6, 0);
850 if (strcmp(ipaddr3,"")!=0)
851 add_listen_socket(ipaddr3, p, do_ipv6, 0);
853 IF_TCONFIG_IPV6(if (do_ipv6 && wanport == p) wan6port = 0);
856 #ifdef TCONFIG_HTTPS
857 if (!nvram_match("https_enable", "0")) {
858 do_ssl = 1;
859 p = nvram_get_int("https_lanport");
860 add_listen_socket(ipaddr, p, do_ipv6, 1);
861 if (strcmp(ipaddr1,"")!=0)
862 add_listen_socket(ipaddr1, p, do_ipv6, 1);
863 if (strcmp(ipaddr2,"")!=0)
864 add_listen_socket(ipaddr2, p, do_ipv6, 1);
865 if (strcmp(ipaddr3,"")!=0)
866 add_listen_socket(ipaddr3, p, do_ipv6, 1);
868 IF_TCONFIG_IPV6(if (do_ipv6 && wanport == p) wan6port = 0);
870 #endif
872 if ((wanport) && nvram_match("wk_mode","gateway") && nvram_match("remote_management", "1") && check_wanup()) {
873 IF_TCONFIG_HTTPS(if (nvram_match("remote_mgt_https", "1")) do_ssl = 1);
874 #ifdef TCONFIG_IPV6
875 if (do_ipv6) {
876 if (*ipaddr && wan6port) {
877 add_listen_socket(ipaddr, wan6port, 1, nvram_match("remote_mgt_https", "1"));
879 if (*ipaddr || wan6port) {
880 // get the IPv6 address from wan iface
881 wanaddr = getifaddr((char *)get_wan6face(), AF_INET6, 0);
882 if (wanaddr && *wanaddr && strcmp(wanaddr, ipaddr) != 0) {
883 add_listen_socket(wanaddr, wanport, 1, nvram_match("remote_mgt_https", "1"));
886 } else
887 #endif
889 int i;
890 char *ip;
891 wanface_list_t wanfaces;
893 memcpy(&wanfaces, get_wanfaces(), sizeof(wanfaces));
894 for (i = 0; i < wanfaces.count; ++i) {
895 ip = wanfaces.iface[i].ip;
896 if (!(*ip) || strcmp(ip, "0.0.0.0") == 0)
897 continue;
898 add_listen_socket(ip, wanport, 0, nvram_match("remote_mgt_https", "1"));
904 static void close_listen_sockets(void)
906 int i;
908 for (i = listeners.count - 1; i >= 0; --i) {
909 if (listeners.listener[i].listenfd >= 0)
910 close(listeners.listener[i].listenfd);
912 listeners.count = 0;
913 FD_ZERO(&listeners.lfdset);
914 maxfd = -1;
917 int main(int argc, char **argv)
919 int c;
920 int debug = 0;
921 fd_set rfdset;
922 int i, n;
923 struct sockaddr_storage sai;
924 char bind[128];
925 char *port = NULL;
926 #ifdef TCONFIG_IPV6
927 int ip6 = 0;
928 #else
929 #define ip6 0
930 #endif
932 openlog("httpd", LOG_PID, LOG_DAEMON);
934 do_ssl = 0;
935 listeners.count = 0;
936 FD_ZERO(&listeners.lfdset);
937 memset(bind, 0, sizeof(bind));
939 while ((c = getopt(argc, argv, "hdp:s:")) != -1) {
940 switch (c) {
941 case 'h':
942 printf(
943 "Usage: %s [options]\n"
944 " -d Debug mode / do not demonize\n"
945 , argv[0]);
946 return 1;
947 case 'd':
948 debug = 1;
949 break;
950 case 'p':
951 case 's':
952 // [addr:]port
953 if ((port = strrchr(optarg, ':')) != NULL) {
954 if ((optarg[0] == '[') && (port > optarg) && (port[-1] == ']'))
955 memcpy(bind, optarg + 1, MIN(sizeof(bind), (int)(port - optarg) - 2));
956 else
957 memcpy(bind, optarg, MIN(sizeof(bind), (int)(port - optarg)));
958 port++;
960 else {
961 port = optarg;
964 IF_TCONFIG_HTTPS(if (c == 's') do_ssl = 1);
965 IF_TCONFIG_IPV6(ip6 = (*bind && strchr(bind, ':')));
966 add_listen_socket(bind, atoi(port), ip6, (c == 's'));
968 memset(bind, 0, sizeof(bind));
969 break;
973 setup_listeners(0);
974 IF_TCONFIG_IPV6(if (ipv6_enabled()) setup_listeners(1));
976 if (listeners.count == 0) {
977 syslog(LOG_ERR, "can't bind to any address");
978 return 1;
980 _dprintf("%s: initialized %d listener(s)\n", __FUNCTION__, listeners.count);
982 IF_TCONFIG_HTTPS(if (do_ssl) start_ssl());
984 init_id();
986 if (!debug) {
987 if (daemon(1, 1) == -1) {
988 syslog(LOG_ERR, "daemon: %m");
989 return 0;
992 char s[16];
993 sprintf(s, "%d", getpid());
994 f_write_string("/var/run/httpd.pid", s, 0, 0644);
996 else {
997 printf("DEBUG mode, not daemonizing\n");
1000 signal(SIGPIPE, SIG_IGN);
1001 signal(SIGALRM, SIG_IGN);
1002 signal(SIGHUP, SIG_IGN);
1003 signal(SIGCHLD, SIG_IGN);
1005 for (;;) {
1007 /* Do a select() on at least one and possibly many listen fds.
1008 ** If there's only one listen fd then we could skip the select
1009 ** and just do the (blocking) accept(), saving one system call;
1010 ** that's what happened up through version 1.18. However there
1011 ** is one slight drawback to that method: the blocking accept()
1012 ** is not interrupted by a signal call. Since we definitely want
1013 ** signals to interrupt a waiting server, we use select() even
1014 ** if there's only one fd.
1016 rfdset = listeners.lfdset;
1017 _dprintf("%s: calling select(maxfd=%d)...\n", __FUNCTION__, maxfd);
1018 if (select(maxfd + 1, &rfdset, NULL, NULL, NULL) < 0) {
1019 if (errno != EINTR && errno != EAGAIN) sleep(1);
1020 continue;
1023 for (i = listeners.count - 1; i >= 0; --i) {
1024 if (listeners.listener[i].listenfd < 0 ||
1025 !FD_ISSET(listeners.listener[i].listenfd, &rfdset))
1026 continue;
1028 do_ssl = 0;
1029 n = sizeof(sai);
1030 _dprintf("%s: calling accept(listener=%d)...\n", __FUNCTION__, listeners.listener[i].listenfd);
1031 connfd = accept(listeners.listener[i].listenfd,
1032 (struct sockaddr *)&sai, &n);
1033 if (connfd < 0) {
1034 _dprintf("accept: %m");
1035 continue;
1037 _dprintf("%s: connfd = accept(listener=%d) = %d\n", __FUNCTION__, listeners.listener[i].listenfd, connfd);
1039 if (!wait_action_idle(10)) {
1040 _dprintf("router is busy");
1041 continue;
1044 if (fork() == 0) {
1045 IF_TCONFIG_HTTPS(do_ssl = listeners.listener[i].ssl);
1046 close_listen_sockets();
1047 webcgi_init(NULL);
1049 clientsai = sai;
1050 if (!check_wlaccess()) {
1051 exit(0);
1054 struct timeval tv;
1055 tv.tv_sec = 60;
1056 tv.tv_usec = 0;
1057 setsockopt(connfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
1058 setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
1060 n = 1;
1061 setsockopt(connfd, IPPROTO_TCP, TCP_NODELAY, (char *)&n, sizeof(n));
1063 fcntl(connfd, F_SETFD, FD_CLOEXEC);
1065 if (web_open()) handle_request();
1066 web_close();
1067 exit(0);
1069 close(connfd);
1073 close_listen_sockets();
1074 return 0;