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)) {
253 if ((nvram_match("http_root", "0")) && (strcmp(buf
, "root") == 0)) {
256 if (((p
= nvram_get("http_passwd")) == NULL
) || (*p
== 0)) p
= "admin";
257 if (strcmp(pass
, p
) == 0) {
270 static void auth_fail(int clen
)
272 if (post
) web_eat(clen
);
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)
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
);
305 // -----------------------------------------------------------------------------
307 static int match_one(const char* pattern
, int patternlen
, const char* string
)
311 for (p
= pattern
; p
- pattern
< patternlen
; ++p
, ++string
) {
312 if (*p
== '?' && *string
!= '\0')
318 /* Double-wildcard matches anything. */
322 /* Single-wildcard matches anything but slash. */
323 i
= strcspn(string
, "/");
324 pl
= patternlen
- (p
- pattern
);
326 if (match_one(p
, pl
, &(string
[i
])))
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
)
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;
353 void do_file(char *path
)
358 if ((f
= fopen(path
, "r")) != NULL
) {
359 while ((nr
= fread(buf
, 1, sizeof(buf
), f
)) > 0)
365 static void handle_request(void)
367 char line
[10000], *cur
;
368 char *method
, *path
, *protocol
, *authorization
, *boundary
;
371 const struct mime_handler
*handler
;
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
);
386 _dprintf("%s\n", line
);
388 method
= path
= line
;
390 if (!path
) { // Avoid http server crash, added by honor 2003-12-08
391 send_error(400, NULL
, NULL
);
394 while (*path
== ' ') path
++;
396 if ((strcasecmp(method
, "GET") != 0) && (strcasecmp(method
, "POST") != 0)) {
397 send_error(501, NULL
, NULL
);
402 strsep(&protocol
, " ");
403 if (!protocol
) { // Avoid http server crash, added by honor 2003-12-08
404 send_error(400, NULL
, NULL
);
407 while (*protocol
== ' ') protocol
++;
409 if (path
[0] != '/') {
410 send_error(400, NULL
, NULL
);
419 hid
= nvram_safe_get("http_id");
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] == '/')) {
427 cprintf("OK path=%s file=%s\n", path
, file
);
431 if ((cp
= strchr(file
, '?')) != NULL
) {
433 setenv("QUERY_STRING", cp
+ 1, 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
);
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";
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)) {
459 if (strncasecmp(cur
, "Authorization:", 14) == 0) {
461 cp
+= strspn(cp
, " \t");
463 cur
= cp
+ strlen(cp
) + 1;
465 else if (strncasecmp(cur
, "Content-Length:", 15) == 0) {
467 cp
+= strspn(cp
, " \t");
468 cl
= strtoul(cp
, NULL
, 0);
469 if ((cl
< 0) || (cl
>= INT_MAX
)) {
470 send_error(400, NULL
, NULL
);
474 else if ((strncasecmp(cur
, "Content-Type:", 13) == 0) && ((cp
= strstr(cur
, "boundary=")))) {
476 for (cp
= cp
+ 9; *cp
&& *cp
!= '\r' && *cp
!= '\n'; 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
);
488 post
= (strcasecmp(method
, "post") == 0);
490 auth
= auth_check(authorization
);
493 cprintf("UserAgent: %s\n", user_agent
);
496 cprintf("AUTH_NONE\n");
499 cprintf("AUTH_OK\n");
502 cprintf("AUTH_BAD\n");
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
522 if (strcmp(file
, "logout") == 0) { // special case
523 wi_generic(file
, cl
, boundary
);
526 if (strstr(user_agent
, "Chrome/") != NULL
) {
527 if (auth
!= AUTH_BAD
) {
533 if (auth
== AUTH_OK
) {
538 send_error(404, NULL
, "Goodbye");
542 if (auth
== AUTH_BAD
) {
547 for (handler
= &mime_handlers
[0]; handler
->pattern
; handler
++) {
548 if (match(handler
->pattern
, file
)) {
549 if ((handler
->auth
) && (auth
!= AUTH_OK
)) {
554 if (handler
->input
) handler
->input(file
, cl
, boundary
);
556 if (handler
->mime_type
!= NULL
) send_header(200, NULL
, handler
->mime_type
, handler
->cache
);
557 if (handler
->output
) handler
->output(file
);
562 if (auth
!= AUTH_OK
) {
568 if ((!post) && (strchr(file, '.') == NULL)) {
570 if ((cl > 1) && (cl < 64)) {
577 if (*cp == '/') *cp = 0;
579 if ((cp = strrchr(path, '/')) != NULL) *cp = '-';
581 strcat(path, ".asp");
582 if (f_exists(path)) {
591 send_error(404, NULL
, NULL
);
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
);
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)
622 unsigned long long sn
;
625 if (nvram_match("https_crt_gen", "1")) {
631 save
= nvram_match("https_crt_save", "1");
633 if ((!f_exists("/etc/cert.pem")) || (!f_exists("/etc/key.pem"))) {
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)
639 unlink("/tmp/cert.tgz");
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) {
658 if (mssl_init("/etc/cert.pem", "/etc/key.pem")) return;
662 syslog(retry
? LOG_WARNING
: LOG_ERR
, "Unable to start SSL");
669 static void init_id(void)
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", ""))) {
687 const char *hid
= nvram_safe_get("http_id");
692 // http://xxx/yyy/TID/zzz
693 if (((a
= strrchr(url
, '/')) != NULL
) && (a
!= url
)) {
696 if ((a
> url
) && (*(a
- 1) == '/')) {
697 if (strncmp(a
, hid
, n
) == 0) return;
708 char a
[INET6_ADDRSTRLEN
];
710 char a
[INET_ADDRSTRLEN
];
714 if (clientsai
.ss_family
== AF_INET
)
715 addr
= &( ((struct sockaddr_in
*) &clientsai
)->sin_addr
);
717 else if (clientsai
.ss_family
== AF_INET6
)
718 addr
= &( ((struct sockaddr_in6
*) &clientsai
)->sin6_addr
);
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
));
732 if ((i
!= NULL
) && (u
!= NULL
)) {
733 syslog(LOG_WARNING
, "Invalid ID '%s' from %s for /%s", i
, a
, u
);
744 static void add_listen_socket(const char *addr
, int server_port
, int do_ipv6
, int do_ssl
)
747 struct sockaddr_storage sai_stor
;
750 sa_family_t HTTPD_FAMILY
= do_ipv6
? AF_INET6
: AF_INET
;
752 #define HTTPD_FAMILY AF_INET
755 if (listeners
.count
>= HTTP_MAX_LISTENERS
) {
756 syslog(LOG_ERR
, "number of listeners exceeded the max allowed (%d)", HTTP_MAX_LISTENERS
);
760 if (server_port
<= 0) {
761 IF_TCONFIG_HTTPS(if (do_ssl
) server_port
= 443; else)
765 if ((listenfd
= socket(HTTPD_FAMILY
, SOCK_STREAM
, 0)) < 0) {
766 syslog(LOG_ERR
, "create listening socket: %m");
769 fcntl(listenfd
, F_SETFD
, FD_CLOEXEC
);
772 setsockopt(listenfd
, SOL_SOCKET
, SO_REUSEADDR
, (char*)&n
, sizeof(n
));
776 struct sockaddr_in6
*sai
= (struct sockaddr_in6
*) &sai_stor
;
777 sai
->sin6_family
= HTTPD_FAMILY
;
778 sai
->sin6_port
= htons(server_port
);
780 inet_pton(HTTPD_FAMILY
, addr
, &(sai
->sin6_addr
));
782 sai
->sin6_addr
= in6addr_any
;
784 setsockopt(listenfd
, IPPROTO_IPV6
, IPV6_V6ONLY
, (char*)&n
, sizeof(n
));
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
);
800 if (listen(listenfd
, 64) < 0) {
801 syslog(LOG_ERR
, "listen: %m");
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
);
825 IF_TCONFIG_IPV6(int wan6port
);
827 wanport
= nvram_get_int("http_wanport");
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
));
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);
857 if (!nvram_match("https_enable", "0")) {
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);
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);
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"));
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)
898 add_listen_socket(ip
, wanport
, 0, nvram_match("remote_mgt_https", "1"));
904 static void close_listen_sockets(void)
908 for (i
= listeners
.count
- 1; i
>= 0; --i
) {
909 if (listeners
.listener
[i
].listenfd
>= 0)
910 close(listeners
.listener
[i
].listenfd
);
913 FD_ZERO(&listeners
.lfdset
);
917 int main(int argc
, char **argv
)
923 struct sockaddr_storage sai
;
932 openlog("httpd", LOG_PID
, LOG_DAEMON
);
936 FD_ZERO(&listeners
.lfdset
);
937 memset(bind
, 0, sizeof(bind
));
939 while ((c
= getopt(argc
, argv
, "hdp:s:")) != -1) {
943 "Usage: %s [options]\n"
944 " -d Debug mode / do not demonize\n"
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));
957 memcpy(bind
, optarg
, MIN(sizeof(bind
), (int)(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
));
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");
980 _dprintf("%s: initialized %d listener(s)\n", __FUNCTION__
, listeners
.count
);
982 IF_TCONFIG_HTTPS(if (do_ssl
) start_ssl());
987 if (daemon(1, 1) == -1) {
988 syslog(LOG_ERR
, "daemon: %m");
993 sprintf(s
, "%d", getpid());
994 f_write_string("/var/run/httpd.pid", s
, 0, 0644);
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
);
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);
1023 for (i
= listeners
.count
- 1; i
>= 0; --i
) {
1024 if (listeners
.listener
[i
].listenfd
< 0 ||
1025 !FD_ISSET(listeners
.listener
[i
].listenfd
, &rfdset
))
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
);
1034 _dprintf("accept: %m");
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");
1045 IF_TCONFIG_HTTPS(do_ssl
= listeners
.listener
[i
].ssl
);
1046 close_listen_sockets();
1050 if (!check_wlaccess()) {
1057 setsockopt(connfd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof(tv
));
1058 setsockopt(connfd
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof(tv
));
1061 setsockopt(connfd
, IPPROTO_TCP
, TCP_NODELAY
, (char *)&n
, sizeof(n
));
1063 fcntl(connfd
, F_SETFD
, FD_CLOEXEC
);
1065 if (web_open()) handle_request();
1073 close_listen_sockets();