diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2026-01-22 01:58:46 +0100 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2026-01-22 01:58:46 +0100 |
| commit | 3f36fe9c2f93d3c1409cc831a7dfb49b0d1998fd (patch) | |
| tree | 93755da9a2b9da86ffd98930c1da8bd87f2e683d | |
| parent | 5ac9d0a865d0d9f8c0281d6111e352492b2cd6d2 (diff) | |
| download | busybox-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.c | 105 |
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 | */ |
| 1046 | static int openServer(void) | 1050 | static 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 | ||
| 2647 | static 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 | |||
| 2668 | static 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) | |||
| 2653 | static void mini_httpd(int server_socket) NORETURN; | 2705 | static void mini_httpd(int server_socket) NORETURN; |
| 2654 | static void mini_httpd(int server_socket) | 2706 | static 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) | |||
| 2698 | static void mini_httpd_nommu(int server_socket, int argc, char **argv) NORETURN; | 2756 | static void mini_httpd_nommu(int server_socket, int argc, char **argv) NORETURN; |
| 2699 | static void mini_httpd_nommu(int server_socket, int argc, char **argv) | 2757 | static 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 */ |
