5 Copyright © 1999,2000 by Jef Poskanzer <jef@acme.com>.
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
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
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
59 #include <netinet/in.h>
60 #include <sys/types.h>
61 #include <sys/socket.h>
62 #include <arpa/inet.h>
67 #include <sys/signal.h>
68 #include <netinet/tcp.h>
74 #include "../mssl/mssl.h"
77 #define HTTP_MAX_LISTENERS 8
85 } listener
[HTTP_MAX_LISTENERS
];
87 static listeners_t listeners
;
88 static int maxfd
= -1;
93 struct sockaddr_storage clientsai
;
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
)
121 return "Invalid Request";
123 return "Unauthorized";
127 return "Not Implemented";
132 void send_header(int status
, const char* header
, const char* mime
, int cache
)
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"
142 status
, http_status_desc(status
),
145 if (mime
) web_printf("Content-Type: %s\r\n", mime
);
147 web_printf("Cache-Control: max-age=%d\r\n", cache
* 60);
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");
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);
166 "<head><title>Error</title></head>"
170 status
, s
, text
? text
: s
);
173 void redirect(const char *path
)
177 snprintf(s
, sizeof(s
), "Location: %s", path
);
178 send_header(302, s
, mime_html
, 0);
181 _dprintf("Redirect: %s\n", path
);
184 int skip_header(int *len
)
189 if (!web_getline(buf
, MIN(*len
, sizeof(buf
)))) {
193 if ((strcmp(buf
, "\n") == 0) || (strcmp(buf
, "\r\n") == 0)) {
201 // -----------------------------------------------------------------------------
203 static void eat_garbage(void)
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)
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
)
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);
248 if ((pass
= strchr(buf
, ':')) != NULL
) {
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) {
265 static void auth_fail(int clen
)
267 if (post
) web_eat(clen
);
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)
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
);
300 // -----------------------------------------------------------------------------
302 static int match_one(const char* pattern
, int patternlen
, const char* string
)
306 for (p
= pattern
; p
- pattern
< patternlen
; ++p
, ++string
) {
307 if (*p
== '?' && *string
!= '\0')
313 /* Double-wildcard matches anything. */
317 /* Single-wildcard matches anything but slash. */
318 i
= strcspn(string
, "/");
319 pl
= patternlen
- (p
- pattern
);
321 if (match_one(p
, pl
, &(string
[i
])))
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
)
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;
348 void do_file(char *path
)
353 if ((f
= fopen(path
, "r")) != NULL
) {
354 while ((nr
= fread(buf
, 1, sizeof(buf
), f
)) > 0)
360 static void handle_request(void)
362 char line
[10000], *cur
;
363 char *method
, *path
, *protocol
, *authorization
, *boundary
;
366 const struct mime_handler
*handler
;
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
);
381 _dprintf("%s\n", line
);
383 method
= path
= line
;
385 if (!path
) { // Avoid http server crash, added by honor 2003-12-08
386 send_error(400, NULL
, NULL
);
389 while (*path
== ' ') path
++;
391 if ((strcasecmp(method
, "GET") != 0) && (strcasecmp(method
, "POST") != 0)) {
392 send_error(501, NULL
, NULL
);
397 strsep(&protocol
, " ");
398 if (!protocol
) { // Avoid http server crash, added by honor 2003-12-08
399 send_error(400, NULL
, NULL
);
402 while (*protocol
== ' ') protocol
++;
404 if (path
[0] != '/') {
405 send_error(400, NULL
, NULL
);
414 hid
= nvram_safe_get("http_id");
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] == '/')) {
422 cprintf("OK path=%s file=%s\n", path
, file
);
426 if ((cp
= strchr(file
, '?')) != NULL
) {
428 setenv("QUERY_STRING", cp
+ 1, 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
);
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";
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)) {
454 if (strncasecmp(cur
, "Authorization:", 14) == 0) {
456 cp
+= strspn(cp
, " \t");
458 cur
= cp
+ strlen(cp
) + 1;
460 else if (strncasecmp(cur
, "Content-Length:", 15) == 0) {
462 cp
+= strspn(cp
, " \t");
463 cl
= strtoul(cp
, NULL
, 0);
464 if ((cl
< 0) || (cl
>= INT_MAX
)) {
465 send_error(400, NULL
, NULL
);
469 else if ((strncasecmp(cur
, "Content-Type:", 13) == 0) && ((cp
= strstr(cur
, "boundary=")))) {
471 for (cp
= cp
+ 9; *cp
&& *cp
!= '\r' && *cp
!= '\n'; 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
);
483 post
= (strcasecmp(method
, "post") == 0);
485 auth
= auth_check(authorization
);
488 cprintf("UserAgent: %s\n", user_agent
);
491 cprintf("AUTH_NONE\n");
494 cprintf("AUTH_OK\n");
497 cprintf("AUTH_BAD\n");
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
517 if (strcmp(file
, "logout") == 0) { // special case
518 wi_generic(file
, cl
, boundary
);
521 if (strstr(user_agent
, "Chrome/") != NULL
) {
522 if (auth
!= AUTH_BAD
) {
528 if (auth
== AUTH_OK
) {
533 send_error(404, NULL
, "Goodbye");
537 if (auth
== AUTH_BAD
) {
542 for (handler
= &mime_handlers
[0]; handler
->pattern
; handler
++) {
543 if (match(handler
->pattern
, file
)) {
544 if ((handler
->auth
) && (auth
!= AUTH_OK
)) {
549 if (handler
->input
) handler
->input(file
, cl
, boundary
);
551 if (handler
->mime_type
!= NULL
) send_header(200, NULL
, handler
->mime_type
, handler
->cache
);
552 if (handler
->output
) handler
->output(file
);
557 if (auth
!= AUTH_OK
) {
563 if ((!post) && (strchr(file, '.') == NULL)) {
565 if ((cl > 1) && (cl < 64)) {
572 if (*cp == '/') *cp = 0;
574 if ((cp = strrchr(path, '/')) != NULL) *cp = '-';
576 strcat(path, ".asp");
577 if (f_exists(path)) {
586 send_error(404, NULL
, NULL
);
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
);
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)
617 unsigned long long sn
;
620 if (nvram_match("https_crt_gen", "1")) {
626 save
= nvram_match("https_crt_save", "1");
628 if ((!f_exists("/etc/cert.pem")) || (!f_exists("/etc/key.pem"))) {
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)
634 unlink("/tmp/cert.tgz");
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) {
653 if (mssl_init("/etc/cert.pem", "/etc/key.pem")) return;
657 syslog(retry
? LOG_WARNING
: LOG_ERR
, "Unable to start SSL");
664 static void init_id(void)
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", ""))) {
682 const char *hid
= nvram_safe_get("http_id");
687 // http://xxx/yyy/TID/zzz
688 if (((a
= strrchr(url
, '/')) != NULL
) && (a
!= url
)) {
691 if ((a
> url
) && (*(a
- 1) == '/')) {
692 if (strncmp(a
, hid
, n
) == 0) return;
703 char a
[INET6_ADDRSTRLEN
];
705 char a
[INET_ADDRSTRLEN
];
709 if (clientsai
.ss_family
== AF_INET
)
710 addr
= &( ((struct sockaddr_in
*) &clientsai
)->sin_addr
);
712 else if (clientsai
.ss_family
== AF_INET6
)
713 addr
= &( ((struct sockaddr_in6
*) &clientsai
)->sin6_addr
);
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
));
727 if ((i
!= NULL
) && (u
!= NULL
)) {
728 syslog(LOG_WARNING
, "Invalid ID '%s' from %s for /%s", i
, a
, u
);
739 static void add_listen_socket(const char *addr
, int server_port
, int do_ipv6
, int do_ssl
)
742 struct sockaddr_storage sai_stor
;
745 sa_family_t HTTPD_FAMILY
= do_ipv6
? AF_INET6
: AF_INET
;
747 #define HTTPD_FAMILY AF_INET
750 if (listeners
.count
>= HTTP_MAX_LISTENERS
) {
751 syslog(LOG_ERR
, "number of listeners exceeded the max allowed (%d)", HTTP_MAX_LISTENERS
);
755 if (server_port
<= 0) {
756 IF_TCONFIG_HTTPS(if (do_ssl
) server_port
= 443; else)
760 if ((listenfd
= socket(HTTPD_FAMILY
, SOCK_STREAM
, 0)) < 0) {
761 syslog(LOG_ERR
, "create listening socket: %m");
764 fcntl(listenfd
, F_SETFD
, FD_CLOEXEC
);
767 setsockopt(listenfd
, SOL_SOCKET
, SO_REUSEADDR
, (char*)&n
, sizeof(n
));
771 struct sockaddr_in6
*sai
= (struct sockaddr_in6
*) &sai_stor
;
772 sai
->sin6_family
= HTTPD_FAMILY
;
773 sai
->sin6_port
= htons(server_port
);
775 inet_pton(HTTPD_FAMILY
, addr
, &(sai
->sin6_addr
));
777 sai
->sin6_addr
= in6addr_any
;
779 setsockopt(listenfd
, IPPROTO_IPV6
, IPV6_V6ONLY
, (char*)&n
, sizeof(n
));
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
);
795 if (listen(listenfd
, 64) < 0) {
796 syslog(LOG_ERR
, "listen: %m");
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
);
820 IF_TCONFIG_IPV6(int wan6port
);
822 wanport
= nvram_get_int("http_wanport");
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
));
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);
852 if (!nvram_match("https_enable", "0")) {
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);
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);
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"));
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)
893 add_listen_socket(ip
, wanport
, 0, nvram_match("remote_mgt_https", "1"));
899 static void close_listen_sockets(void)
903 for (i
= listeners
.count
- 1; i
>= 0; --i
) {
904 if (listeners
.listener
[i
].listenfd
>= 0)
905 close(listeners
.listener
[i
].listenfd
);
908 FD_ZERO(&listeners
.lfdset
);
912 int main(int argc
, char **argv
)
918 struct sockaddr_storage sai
;
927 openlog("httpd", LOG_PID
, LOG_DAEMON
);
931 FD_ZERO(&listeners
.lfdset
);
932 memset(bind
, 0, sizeof(bind
));
934 while ((c
= getopt(argc
, argv
, "hdp:s:")) != -1) {
938 "Usage: %s [options]\n"
939 " -d Debug mode / do not demonize\n"
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));
952 memcpy(bind
, optarg
, MIN(sizeof(bind
), (int)(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
));
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");
975 _dprintf("%s: initialized %d listener(s)\n", __FUNCTION__
, listeners
.count
);
977 IF_TCONFIG_HTTPS(if (do_ssl
) start_ssl());
982 if (daemon(1, 1) == -1) {
983 syslog(LOG_ERR
, "daemon: %m");
988 sprintf(s
, "%d", getpid());
989 f_write_string("/var/run/httpd.pid", s
, 0, 0644);
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
);
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);
1018 for (i
= listeners
.count
- 1; i
>= 0; --i
) {
1019 if (listeners
.listener
[i
].listenfd
< 0 ||
1020 !FD_ISSET(listeners
.listener
[i
].listenfd
, &rfdset
))
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
);
1029 _dprintf("accept: %m");
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");
1040 IF_TCONFIG_HTTPS(do_ssl
= listeners
.listener
[i
].ssl
);
1041 close_listen_sockets();
1045 if (!check_wlaccess()) {
1052 setsockopt(connfd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof(tv
));
1053 setsockopt(connfd
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof(tv
));
1056 setsockopt(connfd
, IPPROTO_TCP
, TCP_NODELAY
, (char *)&n
, sizeof(n
));
1058 fcntl(connfd
, F_SETFD
, FD_CLOEXEC
);
1060 if (web_open()) handle_request();
1068 close_listen_sockets();