From 2d4e23d88b52302bada92e581bd48e9f993e2df6 Mon Sep 17 00:00:00 2001 From: Stefan Fritsch Date: Thu, 15 Sep 2011 19:53:59 +0000 Subject: [PATCH] Create wrapper API for apr_random; use in mod_lbmethod_heartbeat and mod_serf to - replace some needles use of apr_generate_random_bytes - remove code duplication git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1171247 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 2 + include/ap_mmn.h | 3 +- include/httpd.h | 20 +++++ include/mod_core.h | 3 + modules/http/http_core.c | 6 ++ .../proxy/balancers/mod_lbmethod_heartbeat.c | 39 +-------- modules/proxy/mod_serf.c | 32 +------ server/core.c | 85 +++++++++++++++++++ 8 files changed, 120 insertions(+), 70 deletions(-) diff --git a/CHANGES b/CHANGES index 735f597a73..688981faf0 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,8 @@ Changes with Apache 2.3.15 PR 51714. [Stefan Fritsch, Jim Jagielski, Ruediger Pluem, Eric Covener, ] + *) core: Add convenience API for apr_random. [Stefan Fritsch] + *) core: Add MaxRangeOverlaps and MaxRangeReversals directives to control the number of overlapping and reversing ranges (respectively) permitted before returning the entire resource, with a default limit of 20. diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 2e9b3e45fd..ffec22da17 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -351,6 +351,7 @@ * 20110724.4 (2.3.15-dev) add max_ranges to core_dir_config * 20110724.5 (2.3.15-dev) add ap_set_accept_ranges() * 20110724.6 (2.3.15-dev) add max_overlaps and max_reversals to core_dir_config + * 20110724.7 (2.3.15-dev) add ap_random_insecure_bytes(), ap_random_pick() */ #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */ @@ -358,7 +359,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20110724 #endif -#define MODULE_MAGIC_NUMBER_MINOR 6 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 7 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/httpd.h b/include/httpd.h index a9b82a2f6c..d009e68d31 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -2067,6 +2067,26 @@ AP_DECLARE(const char *) ap_strstr_c(const char *s, const char *c); #endif +/** + * Generate pseudo random bytes. + * This is a convenience interface to apr_random. It is cheaper but less + * secure than apr_generate_random_bytes(). + * @param buf where to store the bytes + * @param size number of bytes to generate + * @note ap_random_insecure_bytes() is thread-safe, it uses a mutex on + * threaded MPMs. + */ +APR_DECLARE(void) ap_random_insecure_bytes(void *buf, apr_size_t size); + +/** + * Get a pseudo random number in a range. + * @param min low end of range + * @param max high end of range + * @return a number in the range + */ +APR_DECLARE(apr_uint32_t) ap_random_pick(apr_uint32_t min, apr_uint32_t max); + + #define AP_NORESTART APR_OS_START_USEERR + 1 #ifdef __cplusplus diff --git a/include/mod_core.h b/include/mod_core.h index 714064c7bb..aa93de2f63 100644 --- a/include/mod_core.h +++ b/include/mod_core.h @@ -87,6 +87,9 @@ AP_DECLARE_NONSTD(int) ap_send_http_trace(request_rec *r); */ AP_DECLARE(int) ap_send_http_options(request_rec *r); +/* Used for multipart/byteranges boundary string */ +extern AP_DECLARE_DATA const char *ap_multipart_boundary; + #ifdef __cplusplus } #endif diff --git a/modules/http/http_core.c b/modules/http/http_core.c index fe26de70cd..8db3ad7a83 100644 --- a/modules/http/http_core.c +++ b/modules/http/http_core.c @@ -41,6 +41,8 @@ AP_DECLARE_DATA ap_filter_rec_t *ap_chunk_filter_handle; AP_DECLARE_DATA ap_filter_rec_t *ap_http_outerror_filter_handle; AP_DECLARE_DATA ap_filter_rec_t *ap_byterange_filter_handle; +AP_DECLARE_DATA const char *ap_multipart_boundary; + /* If we are using an MPM That Supports Async Connections, * use a different processing function */ @@ -255,9 +257,13 @@ static int http_send_options(request_rec *r) static int http_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { + apr_uint64_t val; if (ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm) != APR_SUCCESS) { async_mpm = 0; } + ap_random_insecure_bytes(&val, sizeof(val)); + ap_multipart_boundary = apr_psprintf(p, "%0" APR_UINT64_T_HEX_FMT, val); + return OK; } diff --git a/modules/proxy/balancers/mod_lbmethod_heartbeat.c b/modules/proxy/balancers/mod_lbmethod_heartbeat.c index e8dbbc122a..65f7575e53 100644 --- a/modules/proxy/balancers/mod_lbmethod_heartbeat.c +++ b/modules/proxy/balancers/mod_lbmethod_heartbeat.c @@ -253,36 +253,6 @@ static apr_status_t read_heartbeats(const char *path, apr_hash_t *servers, return rv; } -/* - * Finding a random number in a range. - * n' = a + n(b-a+1)/(M+1) - * where: - * n' = random number in range - * a = low end of range - * b = high end of range - * n = random number of 0..M - * M = maxint - * Algorithm 'borrowed' from PHP's rand() function. - */ -#define RAND_RANGE(__n, __min, __max, __tmax) \ -(__n) = (__min) + (long) ((double) ((__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0))) - -static apr_status_t random_pick(apr_uint32_t *number, - apr_uint32_t min, - apr_uint32_t max) -{ - apr_status_t rv = - apr_generate_random_bytes((void*)number, sizeof(apr_uint32_t)); - - if (rv) { - return rv; - } - - RAND_RANGE(*number, min, max, APR_UINT32_MAX); - - return APR_SUCCESS; -} - static proxy_worker *find_best_hb(proxy_balancer *balancer, request_rec *r) { @@ -343,14 +313,7 @@ static proxy_worker *find_best_hb(proxy_balancer *balancer, apr_uint32_t c = 0; apr_uint32_t pick = 0; - rv = random_pick(&pick, 0, openslots); - - if (rv) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, - "lb_heartbeat: failed picking a random number. how random."); - apr_pool_destroy(tpool); - return NULL; - } + pick = ap_random_pick(0, openslots); for (i = 0; i < up_servers->nelts; i++) { server = APR_ARRAY_IDX(up_servers, i, hb_server_t *); diff --git a/modules/proxy/mod_serf.c b/modules/proxy/mod_serf.c index cc00eceed5..408179b238 100644 --- a/modules/proxy/mod_serf.c +++ b/modules/proxy/mod_serf.c @@ -399,35 +399,6 @@ static apr_status_t setup_request(serf_request_t *request, return APR_SUCCESS; } -/* - * Finding a random number in a range. - * n' = a + n(b-a+1)/(M+1) - * where: - * n' = random number in range - * a = low end of range - * b = high end of range - * n = random number of 0..M - * M = maxint - * Algorithm 'borrowed' from PHP's rand() function. (See mod_lbmethod_heartbeat.c). - */ -#define RAND_RANGE(__n, __min, __max, __tmax) \ -(__n) = (__min) + (long) ((double) ((__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0))) - -static apr_status_t random_pick(apr_uint32_t *number, - apr_uint32_t min, - apr_uint32_t max) -{ - apr_status_t rv = - apr_generate_random_bytes((void*)number, sizeof(apr_uint32_t)); - - if (rv) { - return rv; - } - - RAND_RANGE(*number, min, max, APR_UINT32_MAX); - - return APR_SUCCESS; -} /* TOOD: rewrite drive_serf to make it async */ static int drive_serf(request_rec *r, serf_config_t *conf) { @@ -499,8 +470,7 @@ static int drive_serf(request_rec *r, serf_config_t *conf) } /* TOOD: restructure try all servers in the array !! */ - if (random_pick(&pick, 0, servers->nelts-1) != APR_SUCCESS) - pick = 0; + pick = ap_random_pick(0, servers->nelts-1); choice = APR_ARRAY_IDX(servers, pick, ap_serf_server_t *); rv = apr_sockaddr_info_get(&address, choice->ip, diff --git a/server/core.c b/server/core.c index ef4cdd051a..418db70a95 100644 --- a/server/core.c +++ b/server/core.c @@ -20,6 +20,7 @@ #include "apr_fnmatch.h" #include "apr_hash.h" #include "apr_thread_proc.h" /* for RLIMIT stuff */ +#include "apr_random.h" #define APR_WANT_IOVEC #define APR_WANT_STRFUNC @@ -4593,12 +4594,93 @@ AP_DECLARE(int) ap_state_query(int query) } } +static apr_random_t *rng = NULL; +#if APR_HAS_THREADS +static apr_thread_mutex_t *rng_mutex = NULL; + +static void create_rng_mutex(apr_pool_t *pchild, server_rec *s) +{ + int threaded_mpm; + if (ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm) != APR_SUCCESS) + return; + if (threaded_mpm) + apr_thread_mutex_create(&rng_mutex, APR_THREAD_MUTEX_DEFAULT, pchild); +} +#endif + +static void rng_init(apr_pool_t *p) +{ + unsigned char seed[8]; + apr_status_t rv; + rng = apr_random_standard_new(p); + do { + rv = apr_generate_random_bytes(seed, sizeof(seed)); + if (rv != APR_SUCCESS) + goto error; + apr_random_add_entropy(rng, seed, sizeof(seed)); + rv = apr_random_insecure_ready(rng); + } while (rv == APR_ENOTENOUGHENTROPY); + if (rv == APR_SUCCESS) + return; +error: + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, + "Could not initialize random number generator"); + exit(1); +} + +APR_DECLARE(void) ap_random_insecure_bytes(void *buf, apr_size_t size) +{ +#if APR_HAS_THREADS + if (rng_mutex) + apr_thread_mutex_lock(rng_mutex); +#endif + /* apr_random_insecure_bytes can only fail with APR_ENOTENOUGHENTROPY, + * and we have ruled that out during initialization. Therefore we don't + * need to check the return code. + */ + apr_random_insecure_bytes(rng, buf, size); +#if APR_HAS_THREADS + if (rng_mutex) + apr_thread_mutex_unlock(rng_mutex); +#endif +} + +/* + * Finding a random number in a range. + * n' = a + n(b-a+1)/(M+1) + * where: + * n' = random number in range + * a = low end of range + * b = high end of range + * n = random number of 0..M + * M = maxint + * Algorithm 'borrowed' from PHP's rand() function. + */ +#define RAND_RANGE(__n, __min, __max, __tmax) \ +(__n) = (__min) + (long) ((double) ((__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0))) +APR_DECLARE(apr_uint32_t) ap_random_pick(apr_uint32_t min, apr_uint32_t max) +{ + apr_uint32_t number; + if (max < 16384) { + apr_uint16_t num16; + ap_random_insecure_bytes(&num16, sizeof(num16)); + RAND_RANGE(num16, min, max, APR_UINT16_MAX); + number = num16; + } + else { + ap_random_insecure_bytes(&number, sizeof(number)); + RAND_RANGE(number, min, max, APR_UINT32_MAX); + } + return number; +} + static void register_hooks(apr_pool_t *p) { errorlog_hash = apr_hash_make(p); ap_register_log_hooks(p); ap_register_config_hooks(p); ap_expr_init(p); + rng_init(p); /* create_connection and pre_connection should always be hooked * APR_HOOK_REALLY_LAST by core to give other modules the opportunity @@ -4615,6 +4697,9 @@ static void register_hooks(apr_pool_t *p) ap_hook_translate_name(ap_core_translate,NULL,NULL,APR_HOOK_REALLY_LAST); ap_hook_map_to_storage(core_map_to_storage,NULL,NULL,APR_HOOK_REALLY_LAST); ap_hook_open_logs(ap_open_logs,NULL,NULL,APR_HOOK_REALLY_FIRST); +#if APR_HAS_THREADS + ap_hook_child_init(create_rng_mutex,NULL,NULL,APR_HOOK_REALLY_FIRST); +#endif ap_hook_child_init(ap_logs_child_init,NULL,NULL,APR_HOOK_MIDDLE); ap_hook_handler(default_handler,NULL,NULL,APR_HOOK_REALLY_LAST); /* FIXME: I suspect we can eliminate the need for these do_nothings - Ben */ -- 2.40.0