PR 51714. [Stefan Fritsch, Jim Jagielski, Ruediger Pluem, Eric Covener,
<lowprio20 gmail.com>]
+ *) 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.
* 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" */
#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
#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
*/
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
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
*/
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;
}
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)
{
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 *);
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)
{
}
/* 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,
#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
}
}
+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
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 */