aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2026-01-22 01:58:46 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2026-01-22 01:58:46 +0100
commit3f36fe9c2f93d3c1409cc831a7dfb49b0d1998fd (patch)
tree93755da9a2b9da86ffd98930c1da8bd87f2e683d
parent5ac9d0a865d0d9f8c0281d6111e352492b2cd6d2 (diff)
downloadbusybox-w32-3f36fe9c2f93d3c1409cc831a7dfb49b0d1998fd.tar.gz
busybox-w32-3f36fe9c2f93d3c1409cc831a7dfb49b0d1998fd.tar.bz2
busybox-w32-3f36fe9c2f93d3c1409cc831a7dfb49b0d1998fd.zip
httpd: add -M MAXCONN - do not accept unlimited number of connections
We were lacking even basic rate-limiting. function old new delta httpd_main 648 915 +267 handle_incoming_and_exit 2235 2264 +29 packed_usage 35868 35894 +26 cgi_io_loop_and_exit 537 552 +15 send_headers 704 712 +8 get_line 106 108 +2 .rodata 106829 106830 +1 send_file_and_exit 890 887 -3 mini_httpd 161 - -161 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 7/1 up/down: 348/-164) Total: 184 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--networking/httpd.c105
1 files changed, 86 insertions, 19 deletions
diff --git a/networking/httpd.c b/networking/httpd.c
index 49f60d967..39a7046f5 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -267,6 +267,7 @@
267//usage: "[-ifv[v]]" 267//usage: "[-ifv[v]]"
268//usage: " [-c CONFFILE]" 268//usage: " [-c CONFFILE]"
269//usage: " [-p [IP:]PORT]" 269//usage: " [-p [IP:]PORT]"
270//usage: " [-M MAXCONN]"
270//usage: IF_FEATURE_HTTPD_SETUID(" [-u USER[:GRP]]") 271//usage: IF_FEATURE_HTTPD_SETUID(" [-u USER[:GRP]]")
271//usage: IF_FEATURE_HTTPD_BASIC_AUTH(" [-r REALM]") 272//usage: IF_FEATURE_HTTPD_BASIC_AUTH(" [-r REALM]")
272//usage: " [-h HOME]\n" 273//usage: " [-h HOME]\n"
@@ -277,6 +278,7 @@
277//usage: "\n -f Run in foreground" 278//usage: "\n -f Run in foreground"
278//usage: "\n -v[v] Verbose" 279//usage: "\n -v[v] Verbose"
279//usage: "\n -p [IP:]PORT Bind to IP:PORT (default *:"STR(CONFIG_FEATURE_HTTPD_PORT_DEFAULT)")" 280//usage: "\n -p [IP:]PORT Bind to IP:PORT (default *:"STR(CONFIG_FEATURE_HTTPD_PORT_DEFAULT)")"
281//usage: "\n -M NUM Pause if NUM connections are open (default 256)"
280//usage: IF_FEATURE_HTTPD_SETUID( 282//usage: IF_FEATURE_HTTPD_SETUID(
281//usage: "\n -u USER[:GRP] Set uid/gid after binding to port") 283//usage: "\n -u USER[:GRP] Set uid/gid after binding to port")
282//usage: IF_FEATURE_HTTPD_BASIC_AUTH( 284//usage: IF_FEATURE_HTTPD_BASIC_AUTH(
@@ -479,6 +481,8 @@ struct globals {
479 IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;) 481 IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
480 482
481 pid_t parent_pid; 483 pid_t parent_pid;
484 int children_fd;
485 int conn_limit;
482 486
483 off_t file_size; /* -1 - unknown */ 487 off_t file_size; /* -1 - unknown */
484#if ENABLE_FEATURE_HTTPD_RANGES 488#if ENABLE_FEATURE_HTTPD_RANGES
@@ -494,7 +498,6 @@ struct globals {
494#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 498#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
495 Htaccess *script_i; /* config script interpreters */ 499 Htaccess *script_i; /* config script interpreters */
496#endif 500#endif
497 char *iobuf; /* [IOBUF_SIZE] */
498#define hdr_buf bb_common_bufsiz1 501#define hdr_buf bb_common_bufsiz1
499#define sizeof_hdr_buf COMMON_BUFSIZE 502#define sizeof_hdr_buf COMMON_BUFSIZE
500 char *hdr_ptr; 503 char *hdr_ptr;
@@ -508,6 +511,7 @@ struct globals {
508#if ENABLE_FEATURE_HTTPD_PROXY 511#if ENABLE_FEATURE_HTTPD_PROXY
509 Htaccess_Proxy *proxy; 512 Htaccess_Proxy *proxy;
510#endif 513#endif
514 char iobuf[IOBUF_SIZE];
511}; 515};
512#define G (*ptr_to_globals) 516#define G (*ptr_to_globals)
513#define verbose (G.verbose ) 517#define verbose (G.verbose )
@@ -543,11 +547,11 @@ enum {
543#define g_auth (G.g_auth ) 547#define g_auth (G.g_auth )
544#define mime_a (G.mime_a ) 548#define mime_a (G.mime_a )
545#define script_i (G.script_i ) 549#define script_i (G.script_i )
546#define iobuf (G.iobuf )
547#define hdr_ptr (G.hdr_ptr ) 550#define hdr_ptr (G.hdr_ptr )
548#define hdr_cnt (G.hdr_cnt ) 551#define hdr_cnt (G.hdr_cnt )
549#define http_error_page (G.http_error_page ) 552#define http_error_page (G.http_error_page )
550#define proxy (G.proxy ) 553#define proxy (G.proxy )
554#define iobuf (G.iobuf )
551#define INIT_G() do { \ 555#define INIT_G() do { \
552 setup_common_bufsiz(); \ 556 setup_common_bufsiz(); \
553 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 557 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
@@ -1045,7 +1049,8 @@ static void decodeBase64(char *data)
1045 */ 1049 */
1046static int openServer(void) 1050static int openServer(void)
1047{ 1051{
1048 unsigned n = bb_strtou(bind_addr_or_port, NULL, 10); 1052 unsigned n;
1053 n = bb_strtou(bind_addr_or_port, NULL, 10);
1049 if (!errno && n && n <= 0xffff) 1054 if (!errno && n && n <= 0xffff)
1050 n = create_and_bind_stream_or_die(NULL, n); 1055 n = create_and_bind_stream_or_die(NULL, n);
1051 else 1056 else
@@ -2227,10 +2232,6 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2227#endif 2232#endif
2228 char *HTTP_slash; 2233 char *HTTP_slash;
2229 2234
2230 /* Allocation of iobuf is postponed until now
2231 * (IOW, server process doesn't need to waste 8k) */
2232 iobuf = xmalloc(IOBUF_SIZE);
2233
2234 if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) { 2235 if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) {
2235 /* NB: can be NULL (user runs httpd -i by hand?) */ 2236 /* NB: can be NULL (user runs httpd -i by hand?) */
2236 rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->u.sa); 2237 rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->u.sa);
@@ -2643,6 +2644,57 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2643 ); 2644 );
2644} 2645}
2645 2646
2647static int count_children(void)
2648{
2649 int count;
2650 int sz = pread(G.children_fd, iobuf, IOBUF_SIZE - 1, 0);
2651 if (sz < 0)
2652 return -1;
2653 /* The actual format is "NUM NUM ", but future-proof for lack of last space, and for '\n' */
2654 count = 0;
2655 if (sz > 0) {
2656 char *p = iobuf;
2657 iobuf[sz] = '\n';
2658 do {
2659 if (*++p == ' ')
2660 count++, p++;
2661 } while (*p != '\n');
2662 if (p[-1] != ' ') /* it was "NUM NUM\n" (not "NUM NUM \n")? */
2663 count++; /* there were (NUMSPACES + 1) pids */
2664 }
2665 return count;
2666}
2667
2668static int throttle_if_conn_limit(void)
2669{
2670 unsigned usec = 0xffff; /* 0.065535 seconds */
2671 int countdown, c;
2672 for (;;) {
2673 countdown = G.conn_limit;
2674 c = count_children();
2675 if (c < 0)
2676 break; /* can't count them */
2677 countdown -= c;
2678 if (countdown <= 0) { /* we are at MAXCONN, pause until we are below */
2679 bb_error_msg("pausing, children:%u", c);
2680 //bb_error_msg("pausing %ums, children:%u", usec/1000, c);
2681 usleep(usec);
2682 usec = ((usec << 1) | 1) & 0x1fffff; /* x2, up to 2.097151 seconds */
2683 continue; /* loop: count them again */
2684 }
2685 if (VERBOSE_3) /* -vvv periodically shows # of children */
2686 bb_error_msg("children:%u", c ? c - 1 : c);
2687// "Why minus one?!" you ask. We _just now_ forked (see the caller)
2688// and immediately checked the # of children.
2689// Of course, the just-forked child did not have time to complete.
2690// Without "minus one" hack, this causes above statement to always show
2691// at least one child, gives wrong impression to the log's reader
2692// (looks like one child is stuck).
2693 break;
2694 }
2695 return countdown;
2696}
2697
2646/* 2698/*
2647 * The main http server function. 2699 * The main http server function.
2648 * Given a socket, listen for new connections and farm out 2700 * Given a socket, listen for new connections and farm out
@@ -2653,16 +2705,22 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2653static void mini_httpd(int server_socket) NORETURN; 2705static void mini_httpd(int server_socket) NORETURN;
2654static void mini_httpd(int server_socket) 2706static void mini_httpd(int server_socket)
2655{ 2707{
2708 int countdown;
2709
2656 xmove_fd(server_socket, 0); 2710 xmove_fd(server_socket, 0);
2657 /* NB: it's best to not use xfuncs in this loop before fork(). 2711 /* NB: it's best to not use xfuncs in this loop before fork().
2658 * Otherwise server may die on transient errors (temporary 2712 * Otherwise server may die on transient errors (temporary
2659 * out-of-memory condition, etc), which is Bad(tm). 2713 * out-of-memory condition, etc), which is Bad(tm).
2660 * Try to do any dangerous calls after fork. 2714 * Try to do any dangerous calls after fork.
2661 */ 2715 */
2716 countdown = G.conn_limit;
2662 while (1) { 2717 while (1) {
2663 int n; 2718 int n;
2664 len_and_sockaddr fromAddr; 2719 len_and_sockaddr fromAddr;
2665 2720
2721 if (G.children_fd > 0 && --countdown < 0)
2722 countdown = throttle_if_conn_limit();
2723
2666 /* Wait for connections... */ 2724 /* Wait for connections... */
2667 fromAddr.len = LSA_SIZEOF_SA; 2725 fromAddr.len = LSA_SIZEOF_SA;
2668 n = accept(0, &fromAddr.u.sa, &fromAddr.len); 2726 n = accept(0, &fromAddr.u.sa, &fromAddr.len);
@@ -2698,6 +2756,7 @@ static void mini_httpd(int server_socket)
2698static void mini_httpd_nommu(int server_socket, int argc, char **argv) NORETURN; 2756static void mini_httpd_nommu(int server_socket, int argc, char **argv) NORETURN;
2699static void mini_httpd_nommu(int server_socket, int argc, char **argv) 2757static void mini_httpd_nommu(int server_socket, int argc, char **argv)
2700{ 2758{
2759 int countdown;
2701 char *argv_copy[argc + 2]; 2760 char *argv_copy[argc + 2];
2702 2761
2703 argv_copy[0] = argv[0]; 2762 argv_copy[0] = argv[0];
@@ -2710,9 +2769,13 @@ static void mini_httpd_nommu(int server_socket, int argc, char **argv)
2710 * out-of-memory condition, etc), which is Bad(tm). 2769 * out-of-memory condition, etc), which is Bad(tm).
2711 * Try to do any dangerous calls after fork. 2770 * Try to do any dangerous calls after fork.
2712 */ 2771 */
2772 countdown = G.conn_limit;
2713 while (1) { 2773 while (1) {
2714 int n; 2774 int n;
2715 2775
2776 if (G.children_fd > 0 && --countdown < 0)
2777 countdown = throttle_if_conn_limit();
2778
2716 /* Wait for connections... */ 2779 /* Wait for connections... */
2717 n = accept(0, NULL, NULL); 2780 n = accept(0, NULL, NULL);
2718 if (n < 0) 2781 if (n < 0)
@@ -2774,9 +2837,10 @@ enum {
2774 IF_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,) 2837 IF_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,)
2775 IF_FEATURE_HTTPD_SETUID( u_opt_setuid ,) 2838 IF_FEATURE_HTTPD_SETUID( u_opt_setuid ,)
2776 p_opt_port , 2839 p_opt_port ,
2777 p_opt_inetd , 2840 M_opt_maxconn ,
2778 p_opt_foreground, 2841 i_opt_inetd ,
2779 p_opt_verbose , 2842 f_opt_foreground,
2843 v_opt_verbose ,
2780 OPT_CONFIG_FILE = 1 << c_opt_config_file, 2844 OPT_CONFIG_FILE = 1 << c_opt_config_file,
2781 OPT_DECODE_URL = 1 << d_opt_decode_url, 2845 OPT_DECODE_URL = 1 << d_opt_decode_url,
2782 OPT_HOME_HTTPD = 1 << h_opt_home_httpd, 2846 OPT_HOME_HTTPD = 1 << h_opt_home_httpd,
@@ -2785,9 +2849,9 @@ enum {
2785 OPT_MD5 = IF_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0, 2849 OPT_MD5 = IF_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0,
2786 OPT_SETUID = IF_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0, 2850 OPT_SETUID = IF_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0,
2787 OPT_PORT = 1 << p_opt_port, 2851 OPT_PORT = 1 << p_opt_port,
2788 OPT_INETD = 1 << p_opt_inetd, 2852 OPT_INETD = 1 << i_opt_inetd,
2789 OPT_FOREGROUND = 1 << p_opt_foreground, 2853 OPT_FOREGROUND = 1 << f_opt_foreground,
2790 OPT_VERBOSE = 1 << p_opt_verbose, 2854 OPT_VERBOSE = 1 << v_opt_verbose,
2791}; 2855};
2792 2856
2793 2857
@@ -2809,6 +2873,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2809 setlocale(LC_TIME, "C"); 2873 setlocale(LC_TIME, "C");
2810#endif 2874#endif
2811 2875
2876 G.conn_limit = 256;
2812 home_httpd = xrealloc_getcwd_or_warn(NULL); 2877 home_httpd = xrealloc_getcwd_or_warn(NULL);
2813 /* We do not "absolutize" path given by -h (home) opt. 2878 /* We do not "absolutize" path given by -h (home) opt.
2814 * If user gives relative path in -h, 2879 * If user gives relative path in -h,
@@ -2819,7 +2884,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2819 IF_FEATURE_HTTPD_BASIC_AUTH("r:") 2884 IF_FEATURE_HTTPD_BASIC_AUTH("r:")
2820 IF_FEATURE_HTTPD_AUTH_MD5("m:") 2885 IF_FEATURE_HTTPD_AUTH_MD5("m:")
2821 IF_FEATURE_HTTPD_SETUID("u:") 2886 IF_FEATURE_HTTPD_SETUID("u:")
2822 "p:ifv" 2887 "p:M:+ifv"
2823 "\0" 2888 "\0"
2824 /* -v counts, -i implies -f */ 2889 /* -v counts, -i implies -f */
2825 "vv:if", 2890 "vv:if",
@@ -2829,6 +2894,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2829 IF_FEATURE_HTTPD_AUTH_MD5(, &pass) 2894 IF_FEATURE_HTTPD_AUTH_MD5(, &pass)
2830 IF_FEATURE_HTTPD_SETUID(, &s_ugid) 2895 IF_FEATURE_HTTPD_SETUID(, &s_ugid)
2831 , &bind_addr_or_port 2896 , &bind_addr_or_port
2897 , &G.conn_limit
2832 , &verbose 2898 , &verbose
2833 ); 2899 );
2834 if (opt & OPT_DECODE_URL) { 2900 if (opt & OPT_DECODE_URL) {
@@ -2871,6 +2937,10 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2871 xchdir(home_httpd); 2937 xchdir(home_httpd);
2872 2938
2873 if (!(opt & OPT_INETD)) { 2939 if (!(opt & OPT_INETD)) {
2940 G.parent_pid = getpid();
2941 sprintf(iobuf, "/proc/self/task/%u/children", (unsigned)G.parent_pid);
2942 G.children_fd = open(iobuf, O_RDONLY | O_CLOEXEC);
2943
2874 /* Make it unnecessary to wait for children */ 2944 /* Make it unnecessary to wait for children */
2875 signal(SIGCHLD, SIG_IGN); 2945 signal(SIGCHLD, SIG_IGN);
2876 2946
@@ -2905,16 +2975,13 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2905// setenv_long("SERVER_PORT", ???); 2975// setenv_long("SERVER_PORT", ???);
2906 } 2976 }
2907#endif 2977#endif
2908
2909 parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE); 2978 parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE);
2910 if (!(opt & OPT_INETD)) {
2911 G.parent_pid = getpid();
2912 signal(SIGHUP, sighup_handler);
2913 }
2914 2979
2915 xfunc_error_retval = 0; 2980 xfunc_error_retval = 0;
2916 if (opt & OPT_INETD) 2981 if (opt & OPT_INETD)
2917 mini_httpd_inetd(); /* never returns */ 2982 mini_httpd_inetd(); /* never returns */
2983
2984 signal(SIGHUP, sighup_handler);
2918#if BB_MMU 2985#if BB_MMU
2919 if (!(opt & OPT_FOREGROUND)) 2986 if (!(opt & OPT_FOREGROUND))
2920 bb_daemonize(0); /* don't change current directory */ 2987 bb_daemonize(0); /* don't change current directory */