Merge commit 'origin/VLAN-GUI' into tomato-shibby
[tomato.git] / release / src / router / httpd / httpd.c
blob8d9eb20fc9a1edc9187470134418a73d67c9897c
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)) {
252 if (((p = nvram_get("http_passwd")) == NULL) || (*p == 0)) p = "admin";
253 if (strcmp(pass, p) == 0) {
254 return AUTH_OK;
259 return AUTH_BAD;
262 return AUTH_NONE;
265 static void auth_fail(int clen)
267 if (post) web_eat(clen);
268 eat_garbage();
269 send_authenticate();
272 static int check_wif(int idx, int unit, int subunit, void *param)
274 sta_info_t *sti = param;
275 return (wl_ioctl(nvram_safe_get(wl_nvname("ifname", unit, subunit)), WLC_GET_VAR, sti, sizeof(*sti)) == 0);
278 static int check_wlaccess(void)
280 char mac[32];
281 char ifname[32];
282 sta_info_t sti;
284 if (nvram_match("web_wl_filter", "1")) {
285 if (get_client_info(mac, ifname)) {
286 memset(&sti, 0, sizeof(sti));
287 strcpy((char *)&sti, "sta_info"); // sta_info0<mac>
288 ether_atoe(mac, (char *)&sti + 9);
289 if (foreach_wif(1, &sti, check_wif)) {
290 if (nvram_match("debug_logwlac", "1")) {
291 syslog(LOG_WARNING, "Wireless access from %s blocked.", mac);
293 return 0;
297 return 1;
300 // -----------------------------------------------------------------------------
302 static int match_one(const char* pattern, int patternlen, const char* string)
304 const char* p;
306 for (p = pattern; p - pattern < patternlen; ++p, ++string) {
307 if (*p == '?' && *string != '\0')
308 continue;
309 if (*p == '*') {
310 int i, pl;
311 ++p;
312 if (*p == '*') {
313 /* Double-wildcard matches anything. */
314 ++p;
315 i = strlen(string);
316 } else
317 /* Single-wildcard matches anything but slash. */
318 i = strcspn(string, "/");
319 pl = patternlen - (p - pattern);
320 for (; i >= 0; --i)
321 if (match_one(p, pl, &(string[i])))
322 return 1;
323 return 0;
325 if (*p != *string)
326 return 0;
328 if (*string == '\0')
329 return 1;
330 return 0;
333 // Simple shell-style filename matcher. Only does ? * and **, and multiple
334 // patterns separated by |. Returns 1 or 0.
335 static int match(const char* pattern, const char* string)
337 const char* p;
339 for (;;) {
340 p = strchr(pattern, '|');
341 if (p == NULL) return match_one(pattern, strlen(pattern), string);
342 if (match_one(pattern, p - pattern, string)) return 1;
343 pattern = p + 1;
348 void do_file(char *path)
350 FILE *f;
351 char buf[1024];
352 int nr;
353 if ((f = fopen(path, "r")) != NULL) {
354 while ((nr = fread(buf, 1, sizeof(buf), f)) > 0)
355 web_write(buf, nr);
356 fclose(f);
360 static void handle_request(void)
362 char line[10000], *cur;
363 char *method, *path, *protocol, *authorization, *boundary;
364 char *cp;
365 char *file;
366 const struct mime_handler *handler;
367 int cl = 0;
368 auth_t auth;
370 user_agent = "";
371 header_sent = 0;
372 authorization = boundary = NULL;
373 bzero(line, sizeof(line));
375 // Parse the first line of the request.
376 if (!web_getline(line, sizeof(line))) {
377 send_error(400, NULL, NULL);
378 return;
381 _dprintf("%s\n", line);
383 method = path = line;
384 strsep(&path, " ");
385 if (!path) { // Avoid http server crash, added by honor 2003-12-08
386 send_error(400, NULL, NULL);
387 return;
389 while (*path == ' ') path++;
391 if ((strcasecmp(method, "GET") != 0) && (strcasecmp(method, "POST") != 0)) {
392 send_error(501, NULL, NULL);
393 return;
396 protocol = path;
397 strsep(&protocol, " ");
398 if (!protocol) { // Avoid http server crash, added by honor 2003-12-08
399 send_error(400, NULL, NULL);
400 return;
402 while (*protocol == ' ') protocol++;
404 if (path[0] != '/') {
405 send_error(400, NULL, NULL);
406 return;
408 file = path + 1;
410 #if 0
411 const char *hid;
412 int n;
414 hid = nvram_safe_get("http_id");
415 n = strlen(hid);
416 cprintf("file=%s hid=%s n=%d +n=%s\n", file, hid, n, path + n + 1);
417 if ((strncmp(file, hid, n) == 0) && (path[n + 1] == '/')) {
418 path += n + 1;
419 file = path + 1;
420 hidok = 1;
422 cprintf("OK path=%s file=%s\n", path, file);
424 #endif
426 if ((cp = strchr(file, '?')) != NULL) {
427 *cp = 0;
428 setenv("QUERY_STRING", cp + 1, 1);
429 webcgi_init(cp + 1);
432 if ((file[0] == '/') || (strncmp(file, "..", 2) == 0) || (strstr(file, "/../") != NULL) ||
433 (strcmp(file + (strlen(file) - 3), "/..") == 0)) {
434 send_error(400, NULL, NULL);
435 return;
438 if ((file[0] == 0) || (strcmp(file, "index.asp") == 0)) {
439 file = "status-overview.asp";
441 else if ((strcmp(file, "ext/") == 0) || (strcmp(file, "ext") == 0)) {
442 file = "ext/index.asp";
445 cp = protocol;
446 strsep(&cp, " ");
447 cur = protocol + strlen(protocol) + 1;
449 while (web_getline(cur, line + sizeof(line) - cur)) {
450 if ((strcmp(cur, "\n") == 0) || (strcmp(cur, "\r\n") == 0)) {
451 break;
454 if (strncasecmp(cur, "Authorization:", 14) == 0) {
455 cp = &cur[14];
456 cp += strspn(cp, " \t");
457 authorization = cp;
458 cur = cp + strlen(cp) + 1;
460 else if (strncasecmp(cur, "Content-Length:", 15) == 0) {
461 cp = &cur[15];
462 cp += strspn(cp, " \t");
463 cl = strtoul(cp, NULL, 0);
464 if ((cl < 0) || (cl >= INT_MAX)) {
465 send_error(400, NULL, NULL);
466 return;
469 else if ((strncasecmp(cur, "Content-Type:", 13) == 0) && ((cp = strstr(cur, "boundary=")))) {
470 boundary = &cp[9];
471 for (cp = cp + 9; *cp && *cp != '\r' && *cp != '\n'; cp++);
472 *cp = '\0';
473 cur = ++cp;
475 else if (strncasecmp(cur, "User-Agent:", 11) == 0) {
476 user_agent = cur + 11;
477 user_agent += strspn(user_agent, " \t");
478 cur = user_agent + strlen(user_agent);
479 *cur++ = 0;
483 post = (strcasecmp(method, "post") == 0);
485 auth = auth_check(authorization);
487 #if 0
488 cprintf("UserAgent: %s\n", user_agent);
489 switch (auth) {
490 case AUTH_NONE:
491 cprintf("AUTH_NONE\n");
492 break;
493 case AUTH_OK:
494 cprintf("AUTH_OK\n");
495 break;
496 case AUTH_BAD:
497 cprintf("AUTH_BAD\n");
498 break;
499 default:
500 cprintf("AUTH_?\n");
501 break;
503 #endif
507 Chrome 1.0.154:
508 - 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
509 - It *always* sends an 'AUTH_NONE request' first. This results in a
510 send_authenticate, then finally sends an 'AUTH_OK request'.
511 - It does not clear the authentication even if AUTH_NONE request succeeds.
512 - If user doesn't enter anything (blank) for credential, it sends the
513 previous credential.
517 if (strcmp(file, "logout") == 0) { // special case
518 wi_generic(file, cl, boundary);
519 eat_garbage();
521 if (strstr(user_agent, "Chrome/") != NULL) {
522 if (auth != AUTH_BAD) {
523 send_authenticate();
524 return;
527 else {
528 if (auth == AUTH_OK) {
529 send_authenticate();
530 return;
533 send_error(404, NULL, "Goodbye");
534 return;
537 if (auth == AUTH_BAD) {
538 auth_fail(cl);
539 return;
542 for (handler = &mime_handlers[0]; handler->pattern; handler++) {
543 if (match(handler->pattern, file)) {
544 if ((handler->auth) && (auth != AUTH_OK)) {
545 auth_fail(cl);
546 return;
549 if (handler->input) handler->input(file, cl, boundary);
550 eat_garbage();
551 if (handler->mime_type != NULL) send_header(200, NULL, handler->mime_type, handler->cache);
552 if (handler->output) handler->output(file);
553 return;
557 if (auth != AUTH_OK) {
558 auth_fail(cl);
559 return;
563 if ((!post) && (strchr(file, '.') == NULL)) {
564 cl = strlen(file);
565 if ((cl > 1) && (cl < 64)) {
566 char alt[128];
568 path = alt + 1;
569 strcpy(path, file);
571 cp = path + cl - 1;
572 if (*cp == '/') *cp = 0;
574 if ((cp = strrchr(path, '/')) != NULL) *cp = '-';
576 strcat(path, ".asp");
577 if (f_exists(path)) {
578 alt[0] = '/';
579 redirect(alt);
580 return;
586 send_error(404, NULL, NULL);
589 #ifdef TCONFIG_HTTPS
591 #define HTTPS_CRT_VER "1"
593 static void save_cert(void)
595 if (eval("tar", "-C", "/", "-czf", "/tmp/cert.tgz", "etc/cert.pem", "etc/key.pem") == 0) {
596 if (nvram_set_file("https_crt_file", "/tmp/cert.tgz", 8192)) {
597 nvram_set("crt_ver", HTTPS_CRT_VER);
598 nvram_commit_x();
601 unlink("/tmp/cert.tgz");
604 static void erase_cert(void)
606 unlink("/etc/cert.pem");
607 unlink("/etc/key.pem");
608 nvram_unset("https_crt_file");
609 nvram_unset("https_crt_gen");
612 static void start_ssl(void)
614 int ok;
615 int save;
616 int retry;
617 unsigned long long sn;
618 char t[32];
620 if (nvram_match("https_crt_gen", "1")) {
621 erase_cert();
624 retry = 1;
625 while (1) {
626 save = nvram_match("https_crt_save", "1");
628 if ((!f_exists("/etc/cert.pem")) || (!f_exists("/etc/key.pem"))) {
629 ok = 0;
630 if (save && nvram_match("crt_ver", HTTPS_CRT_VER)) {
631 if (nvram_get_file("https_crt_file", "/tmp/cert.tgz", 8192)) {
632 if (eval("tar", "-xzf", "/tmp/cert.tgz", "-C", "/", "etc/cert.pem", "etc/key.pem") == 0)
633 ok = 1;
634 unlink("/tmp/cert.tgz");
637 if (!ok) {
638 erase_cert();
639 syslog(LOG_INFO, "Generating SSL certificate...");
641 // browsers seems to like this when the ip address moves... -- zzz
642 f_read("/dev/urandom", &sn, sizeof(sn));
644 sprintf(t, "%llu", sn & 0x7FFFFFFFFFFFFFFFULL);
645 eval("gencert.sh", t);
649 if ((save) && (*nvram_safe_get("https_crt_file")) == 0) {
650 save_cert();
653 if (mssl_init("/etc/cert.pem", "/etc/key.pem")) return;
655 erase_cert();
657 syslog(retry ? LOG_WARNING : LOG_ERR, "Unable to start SSL");
658 if (!retry) exit(1);
659 retry = 0;
662 #endif
664 static void init_id(void)
666 char s[128];
667 unsigned long long n;
669 if (strncmp(nvram_safe_get("http_id"), "TID", 3) != 0) {
670 f_read("/dev/urandom", &n, sizeof(n));
671 sprintf(s, "TID%llx", n);
672 nvram_set("http_id", s);
675 nvram_unset("http_id_warn");
678 void check_id(const char *url)
680 if (!nvram_match("http_id", webcgi_safeget("_http_id", ""))) {
681 #if 0
682 const char *hid = nvram_safe_get("http_id");
683 if (url) {
684 const char *a;
685 int n;
687 // http://xxx/yyy/TID/zzz
688 if (((a = strrchr(url, '/')) != NULL) && (a != url)) {
689 n = strlen(hid);
690 a -= n;
691 if ((a > url) && (*(a - 1) == '/')) {
692 if (strncmp(a, hid, n) == 0) return;
697 #endif
699 char s[72];
700 char *i;
701 char *u;
702 #ifdef TCONFIG_IPV6
703 char a[INET6_ADDRSTRLEN];
704 #else
705 char a[INET_ADDRSTRLEN];
706 #endif
707 void *addr = NULL;
709 if (clientsai.ss_family == AF_INET)
710 addr = &( ((struct sockaddr_in*) &clientsai)->sin_addr );
711 #ifdef TCONFIG_IPV6
712 else if (clientsai.ss_family == AF_INET6)
713 addr = &( ((struct sockaddr_in6*) &clientsai)->sin6_addr );
714 #endif
715 inet_ntop(clientsai.ss_family, addr, a, sizeof(a));
717 sprintf(s, "%s,%ld", a, time(NULL) & 0xFFC0);
718 if (!nvram_match("http_id_warn", s)) {
719 nvram_set("http_id_warn", s);
721 strlcpy(s, webcgi_safeget("_http_id", ""), sizeof(s));
722 i = js_string(s); // quicky scrub
724 strlcpy(s, url, sizeof(s));
725 u = js_string(s);
727 if ((i != NULL) && (u != NULL)) {
728 syslog(LOG_WARNING, "Invalid ID '%s' from %s for /%s", i, a, u);
731 // free(u);
732 // free(i);
735 exit(1);
739 static void add_listen_socket(const char *addr, int server_port, int do_ipv6, int do_ssl)
741 int listenfd, n;
742 struct sockaddr_storage sai_stor;
744 #ifdef TCONFIG_IPV6
745 sa_family_t HTTPD_FAMILY = do_ipv6 ? AF_INET6 : AF_INET;
746 #else
747 #define HTTPD_FAMILY AF_INET
748 #endif
750 if (listeners.count >= HTTP_MAX_LISTENERS) {
751 syslog(LOG_ERR, "number of listeners exceeded the max allowed (%d)", HTTP_MAX_LISTENERS);
752 return;
755 if (server_port <= 0) {
756 IF_TCONFIG_HTTPS(if (do_ssl) server_port = 443; else)
757 server_port = 80;
760 if ((listenfd = socket(HTTPD_FAMILY, SOCK_STREAM, 0)) < 0) {
761 syslog(LOG_ERR, "create listening socket: %m");
762 return;
764 fcntl(listenfd, F_SETFD, FD_CLOEXEC);
766 n = 1;
767 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof(n));
769 #ifdef TCONFIG_IPV6
770 if (do_ipv6) {
771 struct sockaddr_in6 *sai = (struct sockaddr_in6 *) &sai_stor;
772 sai->sin6_family = HTTPD_FAMILY;
773 sai->sin6_port = htons(server_port);
774 if (addr && *addr)
775 inet_pton(HTTPD_FAMILY, addr, &(sai->sin6_addr));
776 else
777 sai->sin6_addr = in6addr_any;
778 n = 1;
779 setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&n, sizeof(n));
780 } else
781 #endif
783 struct sockaddr_in *sai = (struct sockaddr_in *) &sai_stor;
784 sai->sin_family = HTTPD_FAMILY;
785 sai->sin_port = htons(server_port);
786 sai->sin_addr.s_addr = (addr && *addr) ? inet_addr(addr) : INADDR_ANY;
789 if (bind(listenfd, (struct sockaddr *)&sai_stor, sizeof(sai_stor)) < 0) {
790 syslog(LOG_ERR, "bind: [%s]:%d: %m", (addr && *addr) ? addr : (IF_TCONFIG_IPV6(do_ipv6 ? "::" :) ""), server_port);
791 close(listenfd);
792 return;
795 if (listen(listenfd, 64) < 0) {
796 syslog(LOG_ERR, "listen: %m");
797 close(listenfd);
798 return;
801 if (listenfd >= 0) {
802 _dprintf("%s: added IPv%d listener [%d] for %s:%d, ssl=%d\n",
803 __FUNCTION__, do_ipv6 ? 6 : 4, listenfd,
804 (addr && *addr) ? addr : "addr_any", server_port, do_ssl);
805 listeners.listener[listeners.count].listenfd = listenfd;
806 listeners.listener[listeners.count++].ssl = do_ssl;
807 FD_SET(listenfd, &listeners.lfdset);
808 if (maxfd < listenfd) maxfd = listenfd;
812 static void setup_listeners(int do_ipv6)
814 char ipaddr[INET6_ADDRSTRLEN];
815 char ipaddr1[INET6_ADDRSTRLEN];
816 char ipaddr2[INET6_ADDRSTRLEN];
817 char ipaddr3[INET6_ADDRSTRLEN];
818 IF_TCONFIG_IPV6(const char *wanaddr);
819 int wanport, p;
820 IF_TCONFIG_IPV6(int wan6port);
822 wanport = nvram_get_int("http_wanport");
823 #ifdef TCONFIG_IPV6
824 wan6port = wanport;
825 if (do_ipv6) {
826 // get the configured routable IPv6 address from the lan iface.
827 // add_listen_socket() will fall back to in6addr_any
828 // if NULL or empty address is returned
829 strlcpy(ipaddr, getifaddr(nvram_safe_get("lan_ifname"), AF_INET6, 0) ? : "", sizeof(ipaddr));
831 else
832 #endif
833 strlcpy(ipaddr, nvram_safe_get("lan_ipaddr"), sizeof(ipaddr));
834 strlcpy(ipaddr1, nvram_safe_get("lan1_ipaddr"), sizeof(ipaddr1));
835 strlcpy(ipaddr2, nvram_safe_get("lan2_ipaddr"), sizeof(ipaddr2));
836 strlcpy(ipaddr3, nvram_safe_get("lan3_ipaddr"), sizeof(ipaddr3));
838 if (!nvram_match("http_enable", "0")) {
839 p = nvram_get_int("http_lanport");
840 add_listen_socket(ipaddr, p, do_ipv6, 0);
841 if (strcmp(ipaddr1,"")!=0)
842 add_listen_socket(ipaddr1, p, do_ipv6, 0);
843 if (strcmp(ipaddr2,"")!=0)
844 add_listen_socket(ipaddr2, p, do_ipv6, 0);
845 if (strcmp(ipaddr3,"")!=0)
846 add_listen_socket(ipaddr3, p, do_ipv6, 0);
848 IF_TCONFIG_IPV6(if (do_ipv6 && wanport == p) wan6port = 0);
851 #ifdef TCONFIG_HTTPS
852 if (!nvram_match("https_enable", "0")) {
853 do_ssl = 1;
854 p = nvram_get_int("https_lanport");
855 add_listen_socket(ipaddr, p, do_ipv6, 1);
856 if (strcmp(ipaddr1,"")!=0)
857 add_listen_socket(ipaddr1, p, do_ipv6, 1);
858 if (strcmp(ipaddr2,"")!=0)
859 add_listen_socket(ipaddr2, p, do_ipv6, 1);
860 if (strcmp(ipaddr3,"")!=0)
861 add_listen_socket(ipaddr3, p, do_ipv6, 1);
863 IF_TCONFIG_IPV6(if (do_ipv6 && wanport == p) wan6port = 0);
865 #endif
867 if ((wanport) && nvram_match("wk_mode","gateway") && nvram_match("remote_management", "1") && check_wanup()) {
868 IF_TCONFIG_HTTPS(if (nvram_match("remote_mgt_https", "1")) do_ssl = 1);
869 #ifdef TCONFIG_IPV6
870 if (do_ipv6) {
871 if (*ipaddr && wan6port) {
872 add_listen_socket(ipaddr, wan6port, 1, nvram_match("remote_mgt_https", "1"));
874 if (*ipaddr || wan6port) {
875 // get the IPv6 address from wan iface
876 wanaddr = getifaddr((char *)get_wan6face(), AF_INET6, 0);
877 if (wanaddr && *wanaddr && strcmp(wanaddr, ipaddr) != 0) {
878 add_listen_socket(wanaddr, wanport, 1, nvram_match("remote_mgt_https", "1"));
881 } else
882 #endif
884 int i;
885 char *ip;
886 wanface_list_t wanfaces;
888 memcpy(&wanfaces, get_wanfaces(), sizeof(wanfaces));
889 for (i = 0; i < wanfaces.count; ++i) {
890 ip = wanfaces.iface[i].ip;
891 if (!(*ip) || strcmp(ip, "0.0.0.0") == 0)
892 continue;
893 add_listen_socket(ip, wanport, 0, nvram_match("remote_mgt_https", "1"));
899 static void close_listen_sockets(void)
901 int i;
903 for (i = listeners.count - 1; i >= 0; --i) {
904 if (listeners.listener[i].listenfd >= 0)
905 close(listeners.listener[i].listenfd);
907 listeners.count = 0;
908 FD_ZERO(&listeners.lfdset);
909 maxfd = -1;
912 int main(int argc, char **argv)
914 int c;
915 int debug = 0;
916 fd_set rfdset;
917 int i, n;
918 struct sockaddr_storage sai;
919 char bind[128];
920 char *port = NULL;
921 #ifdef TCONFIG_IPV6
922 int ip6 = 0;
923 #else
924 #define ip6 0
925 #endif
927 openlog("httpd", LOG_PID, LOG_DAEMON);
929 do_ssl = 0;
930 listeners.count = 0;
931 FD_ZERO(&listeners.lfdset);
932 memset(bind, 0, sizeof(bind));
934 while ((c = getopt(argc, argv, "hdp:s:")) != -1) {
935 switch (c) {
936 case 'h':
937 printf(
938 "Usage: %s [options]\n"
939 " -d Debug mode / do not demonize\n"
940 , argv[0]);
941 return 1;
942 case 'd':
943 debug = 1;
944 break;
945 case 'p':
946 case 's':
947 // [addr:]port
948 if ((port = strrchr(optarg, ':')) != NULL) {
949 if ((optarg[0] == '[') && (port > optarg) && (port[-1] == ']'))
950 memcpy(bind, optarg + 1, MIN(sizeof(bind), (int)(port - optarg) - 2));
951 else
952 memcpy(bind, optarg, MIN(sizeof(bind), (int)(port - optarg)));
953 port++;
955 else {
956 port = optarg;
959 IF_TCONFIG_HTTPS(if (c == 's') do_ssl = 1);
960 IF_TCONFIG_IPV6(ip6 = (*bind && strchr(bind, ':')));
961 add_listen_socket(bind, atoi(port), ip6, (c == 's'));
963 memset(bind, 0, sizeof(bind));
964 break;
968 setup_listeners(0);
969 IF_TCONFIG_IPV6(if (ipv6_enabled()) setup_listeners(1));
971 if (listeners.count == 0) {
972 syslog(LOG_ERR, "can't bind to any address");
973 return 1;
975 _dprintf("%s: initialized %d listener(s)\n", __FUNCTION__, listeners.count);
977 IF_TCONFIG_HTTPS(if (do_ssl) start_ssl());
979 init_id();
981 if (!debug) {
982 if (daemon(1, 1) == -1) {
983 syslog(LOG_ERR, "daemon: %m");
984 return 0;
987 char s[16];
988 sprintf(s, "%d", getpid());
989 f_write_string("/var/run/httpd.pid", s, 0, 0644);
991 else {
992 printf("DEBUG mode, not daemonizing\n");
995 signal(SIGPIPE, SIG_IGN);
996 signal(SIGALRM, SIG_IGN);
997 signal(SIGHUP, SIG_IGN);
998 signal(SIGCHLD, SIG_IGN);
1000 for (;;) {
1002 /* Do a select() on at least one and possibly many listen fds.
1003 ** If there's only one listen fd then we could skip the select
1004 ** and just do the (blocking) accept(), saving one system call;
1005 ** that's what happened up through version 1.18. However there
1006 ** is one slight drawback to that method: the blocking accept()
1007 ** is not interrupted by a signal call. Since we definitely want
1008 ** signals to interrupt a waiting server, we use select() even
1009 ** if there's only one fd.
1011 rfdset = listeners.lfdset;
1012 _dprintf("%s: calling select(maxfd=%d)...\n", __FUNCTION__, maxfd);
1013 if (select(maxfd + 1, &rfdset, NULL, NULL, NULL) < 0) {
1014 if (errno != EINTR && errno != EAGAIN) sleep(1);
1015 continue;
1018 for (i = listeners.count - 1; i >= 0; --i) {
1019 if (listeners.listener[i].listenfd < 0 ||
1020 !FD_ISSET(listeners.listener[i].listenfd, &rfdset))
1021 continue;
1023 do_ssl = 0;
1024 n = sizeof(sai);
1025 _dprintf("%s: calling accept(listener=%d)...\n", __FUNCTION__, listeners.listener[i].listenfd);
1026 connfd = accept(listeners.listener[i].listenfd,
1027 (struct sockaddr *)&sai, &n);
1028 if (connfd < 0) {
1029 _dprintf("accept: %m");
1030 continue;
1032 _dprintf("%s: connfd = accept(listener=%d) = %d\n", __FUNCTION__, listeners.listener[i].listenfd, connfd);
1034 if (!wait_action_idle(10)) {
1035 _dprintf("router is busy");
1036 continue;
1039 if (fork() == 0) {
1040 IF_TCONFIG_HTTPS(do_ssl = listeners.listener[i].ssl);
1041 close_listen_sockets();
1042 webcgi_init(NULL);
1044 clientsai = sai;
1045 if (!check_wlaccess()) {
1046 exit(0);
1049 struct timeval tv;
1050 tv.tv_sec = 60;
1051 tv.tv_usec = 0;
1052 setsockopt(connfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
1053 setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
1055 n = 1;
1056 setsockopt(connfd, IPPROTO_TCP, TCP_NODELAY, (char *)&n, sizeof(n));
1058 fcntl(connfd, F_SETFD, FD_CLOEXEC);
1060 if (web_open()) handle_request();
1061 web_close();
1062 exit(0);
1064 close(connfd);
1068 close_listen_sockets();
1069 return 0;