usbmodeswitch: Updated to v.1.2.6 from shibby's branch.
[tomato.git] / release / src / router / httpd / httpd.c
blobb224bbfd879cee7e2e3139e5a886ea25aaf24e20
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 12
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 #ifdef TCONFIG_VLAN
816 char ipaddr1[INET6_ADDRSTRLEN];
817 char ipaddr2[INET6_ADDRSTRLEN];
818 char ipaddr3[INET6_ADDRSTRLEN];
819 #endif
820 IF_TCONFIG_IPV6(const char *wanaddr);
821 int wanport, p;
822 IF_TCONFIG_IPV6(int wan6port);
824 wanport = nvram_get_int("http_wanport");
825 #ifdef TCONFIG_IPV6
826 wan6port = wanport;
827 if (do_ipv6) {
828 // get the configured routable IPv6 address from the lan iface.
829 // add_listen_socket() will fall back to in6addr_any
830 // if NULL or empty address is returned
831 strlcpy(ipaddr, getifaddr(nvram_safe_get("lan_ifname"), AF_INET6, 0) ? : "", sizeof(ipaddr));
833 else
834 #endif
835 strlcpy(ipaddr, nvram_safe_get("lan_ipaddr"), sizeof(ipaddr));
836 #ifdef TCONFIG_VLAN
837 strlcpy(ipaddr1, nvram_safe_get("lan1_ipaddr"), sizeof(ipaddr));
838 strlcpy(ipaddr2, nvram_safe_get("lan2_ipaddr"), sizeof(ipaddr));
839 strlcpy(ipaddr3, nvram_safe_get("lan3_ipaddr"), sizeof(ipaddr));
840 #endif
842 if (!nvram_match("http_enable", "0")) {
843 p = nvram_get_int("http_lanport");
844 add_listen_socket(ipaddr, p, do_ipv6, 0);
845 #ifdef TCONFIG_VLAN
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);
852 #endif
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 #ifdef TCONFIG_VLAN
862 if (strcmp(ipaddr1,"")!=0)
863 add_listen_socket(ipaddr1, p, do_ipv6, 1);
864 if (strcmp(ipaddr2,"")!=0)
865 add_listen_socket(ipaddr2, p, do_ipv6, 1);
866 if (strcmp(ipaddr3,"")!=0)
867 add_listen_socket(ipaddr3, p, do_ipv6, 1);
868 #endif
869 IF_TCONFIG_IPV6(if (do_ipv6 && wanport == p) wan6port = 0);
871 #endif
873 if ((wanport) && nvram_match("wk_mode","gateway") && nvram_match("remote_management", "1") && check_wanup()) {
874 IF_TCONFIG_HTTPS(if (nvram_match("remote_mgt_https", "1")) do_ssl = 1);
875 #ifdef TCONFIG_IPV6
876 if (do_ipv6) {
877 if (*ipaddr && wan6port) {
878 add_listen_socket(ipaddr, wan6port, 1, nvram_match("remote_mgt_https", "1"));
880 if (*ipaddr || wan6port) {
881 // get the IPv6 address from wan iface
882 wanaddr = getifaddr((char *)get_wan6face(), AF_INET6, 0);
883 if (wanaddr && *wanaddr && strcmp(wanaddr, ipaddr) != 0) {
884 add_listen_socket(wanaddr, wanport, 1, nvram_match("remote_mgt_https", "1"));
887 } else
888 #endif
890 int i;
891 char *ip;
892 wanface_list_t wanfaces;
894 memcpy(&wanfaces, get_wanfaces(), sizeof(wanfaces));
895 for (i = 0; i < wanfaces.count; ++i) {
896 ip = wanfaces.iface[i].ip;
897 if (!(*ip) || strcmp(ip, "0.0.0.0") == 0)
898 continue;
899 add_listen_socket(ip, wanport, 0, nvram_match("remote_mgt_https", "1"));
905 static void close_listen_sockets(void)
907 int i;
909 for (i = listeners.count - 1; i >= 0; --i) {
910 if (listeners.listener[i].listenfd >= 0)
911 close(listeners.listener[i].listenfd);
913 listeners.count = 0;
914 FD_ZERO(&listeners.lfdset);
915 maxfd = -1;
918 int main(int argc, char **argv)
920 int c;
921 int debug = 0;
922 fd_set rfdset;
923 int i, n;
924 struct sockaddr_storage sai;
925 char bind[128];
926 char *port = NULL;
927 #ifdef TCONFIG_IPV6
928 int ip6 = 0;
929 #else
930 #define ip6 0
931 #endif
933 openlog("httpd", LOG_PID, LOG_DAEMON);
935 do_ssl = 0;
936 listeners.count = 0;
937 FD_ZERO(&listeners.lfdset);
938 memset(bind, 0, sizeof(bind));
940 while ((c = getopt(argc, argv, "hdp:s:")) != -1) {
941 switch (c) {
942 case 'h':
943 printf(
944 "Usage: %s [options]\n"
945 " -d Debug mode / do not demonize\n"
946 , argv[0]);
947 return 1;
948 case 'd':
949 debug = 1;
950 break;
951 case 'p':
952 case 's':
953 // [addr:]port
954 if ((port = strrchr(optarg, ':')) != NULL) {
955 if ((optarg[0] == '[') && (port > optarg) && (port[-1] == ']'))
956 memcpy(bind, optarg + 1, MIN(sizeof(bind), (int)(port - optarg) - 2));
957 else
958 memcpy(bind, optarg, MIN(sizeof(bind), (int)(port - optarg)));
959 port++;
961 else {
962 port = optarg;
965 IF_TCONFIG_HTTPS(if (c == 's') do_ssl = 1);
966 IF_TCONFIG_IPV6(ip6 = (*bind && strchr(bind, ':')));
967 add_listen_socket(bind, atoi(port), ip6, (c == 's'));
969 memset(bind, 0, sizeof(bind));
970 break;
974 setup_listeners(0);
975 IF_TCONFIG_IPV6(if (ipv6_enabled()) setup_listeners(1));
977 if (listeners.count == 0) {
978 syslog(LOG_ERR, "can't bind to any address");
979 return 1;
981 _dprintf("%s: initialized %d listener(s)\n", __FUNCTION__, listeners.count);
983 IF_TCONFIG_HTTPS(if (do_ssl) start_ssl());
985 init_id();
987 if (!debug) {
988 if (daemon(1, 1) == -1) {
989 syslog(LOG_ERR, "daemon: %m");
990 return 0;
993 char s[16];
994 sprintf(s, "%d", getpid());
995 f_write_string("/var/run/httpd.pid", s, 0, 0644);
997 else {
998 printf("DEBUG mode, not daemonizing\n");
1001 signal(SIGPIPE, SIG_IGN);
1002 signal(SIGALRM, SIG_IGN);
1003 signal(SIGHUP, SIG_IGN);
1004 signal(SIGCHLD, SIG_IGN);
1006 for (;;) {
1008 /* Do a select() on at least one and possibly many listen fds.
1009 ** If there's only one listen fd then we could skip the select
1010 ** and just do the (blocking) accept(), saving one system call;
1011 ** that's what happened up through version 1.18. However there
1012 ** is one slight drawback to that method: the blocking accept()
1013 ** is not interrupted by a signal call. Since we definitely want
1014 ** signals to interrupt a waiting server, we use select() even
1015 ** if there's only one fd.
1017 rfdset = listeners.lfdset;
1018 _dprintf("%s: calling select(maxfd=%d)...\n", __FUNCTION__, maxfd);
1019 if (select(maxfd + 1, &rfdset, NULL, NULL, NULL) < 0) {
1020 if (errno != EINTR && errno != EAGAIN) sleep(1);
1021 continue;
1024 for (i = listeners.count - 1; i >= 0; --i) {
1025 if (listeners.listener[i].listenfd < 0 ||
1026 !FD_ISSET(listeners.listener[i].listenfd, &rfdset))
1027 continue;
1029 do_ssl = 0;
1030 n = sizeof(sai);
1031 _dprintf("%s: calling accept(listener=%d)...\n", __FUNCTION__, listeners.listener[i].listenfd);
1032 connfd = accept(listeners.listener[i].listenfd,
1033 (struct sockaddr *)&sai, &n);
1034 if (connfd < 0) {
1035 _dprintf("accept: %m");
1036 continue;
1038 _dprintf("%s: connfd = accept(listener=%d) = %d\n", __FUNCTION__, listeners.listener[i].listenfd, connfd);
1040 if (!wait_action_idle(10)) {
1041 _dprintf("router is busy");
1042 continue;
1045 if (fork() == 0) {
1046 IF_TCONFIG_HTTPS(do_ssl = listeners.listener[i].ssl);
1047 close_listen_sockets();
1048 webcgi_init(NULL);
1050 clientsai = sai;
1051 if (!check_wlaccess()) {
1052 exit(0);
1055 struct timeval tv;
1056 tv.tv_sec = 60;
1057 tv.tv_usec = 0;
1058 setsockopt(connfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
1059 setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
1061 n = 1;
1062 setsockopt(connfd, IPPROTO_TCP, TCP_NODELAY, (char *)&n, sizeof(n));
1064 fcntl(connfd, F_SETFD, FD_CLOEXEC);
1066 if (web_open()) handle_request();
1067 web_close();
1068 exit(0);
1070 close(connfd);
1074 close_listen_sockets();
1075 return 0;