summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorStefan Fritsch <sf@sfritsch.de>2011-12-27 19:42:53 +0100
committerStefan Fritsch <sf@sfritsch.de>2011-12-27 19:42:53 +0100
commitdb26b587c04799e75b6dd0fcd4b46aaa168f9161 (patch)
tree127af2f77fd3eddb75604ebecedeeea163325078 /modules
parentd9f98b967bedecc0bffe82682d1ed4e06c9df687 (diff)
downloadapache2-db26b587c04799e75b6dd0fcd4b46aaa168f9161.tar.gz
Upstream tarball 2.2.15upstream/2.2.15
Diffstat (limited to 'modules')
-rw-r--r--modules/aaa/mod_authnz_ldap.c16
-rw-r--r--modules/arch/win32/mod_isapi.c2
-rw-r--r--modules/cache/NWGNUdsk_cach2
-rw-r--r--modules/cache/NWGNUmem_cach2
-rw-r--r--modules/cache/NWGNUmod_cach2
-rw-r--r--modules/cache/cache_storage.c2
-rw-r--r--modules/cache/cache_util.c245
-rw-r--r--modules/cache/mod_cache.c231
-rw-r--r--modules/cache/mod_cache.h68
-rw-r--r--modules/cache/mod_disk_cache.c13
-rw-r--r--modules/cache/mod_mem_cache.c10
-rw-r--r--modules/config5.m42
-rw-r--r--modules/database/mod_dbd.c11
-rw-r--r--modules/dav/fs/NWGNUmakefile2
-rw-r--r--modules/dav/lock/NWGNUmakefile2
-rw-r--r--modules/dav/main/NWGNUmakefile2
-rw-r--r--modules/filters/config.m41
-rw-r--r--modules/filters/mod_charset_lite.c11
-rw-r--r--modules/filters/mod_filter.c4
-rw-r--r--modules/filters/mod_include.c47
-rw-r--r--modules/filters/mod_reqtimeout.c431
-rw-r--r--modules/filters/mod_reqtimeout.dsp111
-rw-r--r--modules/http/byterange_filter.c2
-rw-r--r--modules/http/http_filters.c9
-rw-r--r--modules/http/http_request.c3
-rw-r--r--modules/http/mod_mime.c21
-rw-r--r--modules/ldap/util_ldap.c2
-rw-r--r--modules/ldap/util_ldap_cache_mgr.c62
-rw-r--r--modules/loggers/mod_log_config.c6
-rw-r--r--modules/mappers/mod_negotiation.c3
-rw-r--r--modules/mappers/mod_rewrite.c12
-rw-r--r--modules/proxy/NWGNUmakefile1
-rw-r--r--modules/proxy/NWGNUproxy27
-rw-r--r--modules/proxy/NWGNUproxyajp9
-rw-r--r--modules/proxy/NWGNUproxybalancer10
-rw-r--r--modules/proxy/NWGNUproxycon4
-rw-r--r--modules/proxy/NWGNUproxyftp6
-rw-r--r--modules/proxy/NWGNUproxyhtp7
-rw-r--r--modules/proxy/NWGNUproxyscgi2
-rw-r--r--modules/proxy/mod_proxy.h1
-rw-r--r--modules/proxy/mod_proxy_ajp.c7
-rw-r--r--modules/proxy/mod_proxy_connect.c2
-rw-r--r--modules/proxy/mod_proxy_http.c59
-rw-r--r--modules/proxy/proxy_util.c165
-rw-r--r--modules/ssl/mod_ssl.c2
-rw-r--r--modules/ssl/ssl_engine_config.c15
-rw-r--r--modules/ssl/ssl_engine_init.c19
-rw-r--r--modules/ssl/ssl_engine_io.c52
-rw-r--r--modules/ssl/ssl_engine_kernel.c264
-rw-r--r--modules/ssl/ssl_engine_vars.c136
-rw-r--r--modules/ssl/ssl_private.h22
-rw-r--r--modules/ssl/ssl_toolkit_compat.h2
52 files changed, 1824 insertions, 325 deletions
diff --git a/modules/aaa/mod_authnz_ldap.c b/modules/aaa/mod_authnz_ldap.c
index 9b61022a..bb08d20f 100644
--- a/modules/aaa/mod_authnz_ldap.c
+++ b/modules/aaa/mod_authnz_ldap.c
@@ -63,6 +63,7 @@ typedef struct {
deref_options deref; /* how to handle alias dereferening */
char *binddn; /* DN to bind to server (can be NULL) */
char *bindpw; /* Password to bind to server (can be NULL) */
+ int bind_authoritative; /* If true, will return errors when bind fails */
int user_is_dn; /* If true, connection->user is DN instead of userid */
char *remote_user_attribute; /* If set, connection->user is this attribute instead of userid */
@@ -294,6 +295,7 @@ static void *create_authnz_ldap_dir_config(apr_pool_t *p, char *d)
sec->host = NULL;
sec->binddn = NULL;
sec->bindpw = NULL;
+ sec->bind_authoritative = 1;
sec->deref = always;
sec->group_attrib_is_dn = 1;
sec->auth_authoritative = 1;
@@ -409,7 +411,15 @@ start_over:
/* handle bind failure */
if (result != LDAP_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ if (!sec->bind_authoritative) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "[%" APR_PID_T_FMT "] auth_ldap authenticate: "
+ "user %s authentication failed; URI %s [%s][%s] (not authoritative)",
+ getpid(), user, r->uri, ldc->reason, ldap_err2string(result));
+ return AUTH_USER_NOT_FOUND;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
"[%" APR_PID_T_FMT "] auth_ldap authenticate: "
"user %s authentication failed; URI %s [%s][%s]",
getpid(), user, r->uri, ldc->reason, ldap_err2string(result));
@@ -1065,6 +1075,10 @@ static const command_rec authnz_ldap_cmds[] =
(void *)APR_OFFSETOF(authn_ldap_config_t, bindpw), OR_AUTHCFG,
"Password to use to bind to LDAP server. If not provided, will do an anonymous bind."),
+ AP_INIT_FLAG("AuthLDAPBindAuthoritative", ap_set_flag_slot,
+ (void *)APR_OFFSETOF(authn_ldap_config_t, bind_authoritative), OR_AUTHCFG,
+ "Set to 'on' to return failures when user-specific bind fails - defaults to on."),
+
AP_INIT_FLAG("AuthLDAPRemoteUserIsDN", ap_set_flag_slot,
(void *)APR_OFFSETOF(authn_ldap_config_t, user_is_dn), OR_AUTHCFG,
"Set to 'on' to set the REMOTE_USER environment variable to be the full "
diff --git a/modules/arch/win32/mod_isapi.c b/modules/arch/win32/mod_isapi.c
index bfc26aad..ec0c800d 100644
--- a/modules/arch/win32/mod_isapi.c
+++ b/modules/arch/win32/mod_isapi.c
@@ -1503,7 +1503,6 @@ apr_status_t isapi_handler (request_rec *r)
/* Set up client input */
res = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
if (res) {
- isapi_unload(isa, 0);
return res;
}
@@ -1534,7 +1533,6 @@ apr_status_t isapi_handler (request_rec *r)
}
if (res < 0) {
- isapi_unload(isa, 0);
return HTTP_INTERNAL_SERVER_ERROR;
}
diff --git a/modules/cache/NWGNUdsk_cach b/modules/cache/NWGNUdsk_cach
index b91823dd..a2fb7d06 100644
--- a/modules/cache/NWGNUdsk_cach
+++ b/modules/cache/NWGNUdsk_cach
@@ -26,9 +26,7 @@ XINCDIRS += \
$(APR)/include \
$(APRUTIL)/include \
$(AP_WORK)/include \
- $(AP_WORK)/os/NetWare \
$(AP_WORK)/server/mpm/NetWare \
- $(AP_WORK)/srclib/pcre \
$(NWOS) \
$(EOLIST)
diff --git a/modules/cache/NWGNUmem_cach b/modules/cache/NWGNUmem_cach
index bcf98e7f..19e329a7 100644
--- a/modules/cache/NWGNUmem_cach
+++ b/modules/cache/NWGNUmem_cach
@@ -26,9 +26,7 @@ XINCDIRS += \
$(APR)/include \
$(APRUTIL)/include \
$(AP_WORK)/include \
- $(AP_WORK)/os/NetWare \
$(AP_WORK)/server/mpm/NetWare \
- $(AP_WORK)/srclib/pcre \
$(NWOS) \
$(EOLIST)
diff --git a/modules/cache/NWGNUmod_cach b/modules/cache/NWGNUmod_cach
index ff0a9667..424da522 100644
--- a/modules/cache/NWGNUmod_cach
+++ b/modules/cache/NWGNUmod_cach
@@ -26,9 +26,7 @@ XINCDIRS += \
$(APR)/include \
$(APRUTIL)/include \
$(AP_WORK)/include \
- $(AP_WORK)/os/NetWare \
$(AP_WORK)/server/mpm/NetWare \
- $(AP_WORK)/srclib/pcre \
$(NWOS) \
$(EOLIST)
diff --git a/modules/cache/cache_storage.c b/modules/cache/cache_storage.c
index 1af26d77..2fbadc8b 100644
--- a/modules/cache/cache_storage.c
+++ b/modules/cache/cache_storage.c
@@ -37,7 +37,7 @@ int cache_remove_url(cache_request_rec *cache, apr_pool_t *p)
/* Remove the stale cache entry if present. If not, we're
* being called from outside of a request; remove the
- * non-stalle handle.
+ * non-stale handle.
*/
h = cache->stale_handle ? cache->stale_handle : cache->handle;
if (!h) {
diff --git a/modules/cache/cache_util.c b/modules/cache/cache_util.c
index ec2d749b..cb3d5653 100644
--- a/modules/cache/cache_util.c
+++ b/modules/cache/cache_util.c
@@ -22,6 +22,8 @@
/* -------------------------------------------------------------- */
+extern APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
+
extern module AP_MODULE_DECLARE_DATA cache_module;
/* Determine if "url" matches the hostname, scheme and port and path
@@ -164,9 +166,182 @@ CACHE_DECLARE(apr_int64_t) ap_cache_current_age(cache_info *info,
return apr_time_sec(current_age);
}
+/**
+ * Try obtain a cache wide lock on the given cache key.
+ *
+ * If we return APR_SUCCESS, we obtained the lock, and we are clear to
+ * proceed to the backend. If we return APR_EEXISTS, then the lock is
+ * already locked, someone else has gone to refresh the backend data
+ * already, so we must return stale data with a warning in the mean
+ * time. If we return anything else, then something has gone pear
+ * shaped, and we allow the request through to the backend regardless.
+ *
+ * This lock is created from the request pool, meaning that should
+ * something go wrong and the lock isn't deleted on return of the
+ * request headers from the backend for whatever reason, at worst the
+ * lock will be cleaned up when the request dies or finishes.
+ *
+ * If something goes truly bananas and the lock isn't deleted when the
+ * request dies, the lock will be trashed when its max-age is reached,
+ * or when a request arrives containing a Cache-Control: no-cache. At
+ * no point is it possible for this lock to permanently deny access to
+ * the backend.
+ */
+CACHE_DECLARE(apr_status_t) ap_cache_try_lock(cache_server_conf *conf,
+ request_rec *r, char *key) {
+ apr_status_t status;
+ const char *lockname;
+ const char *path;
+ char dir[5];
+ apr_time_t now = apr_time_now();
+ apr_finfo_t finfo;
+ apr_file_t *lockfile;
+ void *dummy;
+
+ finfo.mtime = 0;
+
+ if (!conf || !conf->lock || !conf->lockpath) {
+ /* no locks configured, leave */
+ return APR_SUCCESS;
+ }
+
+ /* lock already obtained earlier? if so, success */
+ apr_pool_userdata_get(&dummy, CACHE_LOCKFILE_KEY, r->pool);
+ if (dummy) {
+ return APR_SUCCESS;
+ }
+
+ /* create the key if it doesn't exist */
+ if (!key) {
+ cache_generate_key(r, r->pool, &key);
+ }
+
+ /* create a hashed filename from the key, and save it for later */
+ lockname = ap_cache_generate_name(r->pool, 0, 0, key);
+
+ /* lock files represent discrete just-went-stale URLs "in flight", so
+ * we support a simple two level directory structure, more is overkill.
+ */
+ dir[0] = '/';
+ dir[1] = lockname[0];
+ dir[2] = '/';
+ dir[3] = lockname[1];
+ dir[4] = 0;
+
+ /* make the directories */
+ path = apr_pstrcat(r->pool, conf->lockpath, dir, NULL);
+ if (APR_SUCCESS != (status = apr_dir_make_recursive(path,
+ APR_UREAD|APR_UWRITE|APR_UEXECUTE, r->pool))) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "Could not create a cache lock directory: %s",
+ path);
+ return status;
+ }
+ lockname = apr_pstrcat(r->pool, path, "/", lockname, NULL);
+ apr_pool_userdata_set(lockname, CACHE_LOCKNAME_KEY, NULL, r->pool);
+
+ /* is an existing lock file too old? */
+ status = apr_stat(&finfo, lockname,
+ APR_FINFO_MTIME | APR_FINFO_NLINK, r->pool);
+ if (!(APR_STATUS_IS_ENOENT(status)) && APR_SUCCESS != status) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_EEXIST, r->server,
+ "Could not stat a cache lock file: %s",
+ lockname);
+ return status;
+ }
+ if ((status == APR_SUCCESS) && (((now - finfo.mtime) > conf->lockmaxage)
+ || (now < finfo.mtime))) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, status, r->server,
+ "Cache lock file for '%s' too old, removing: %s",
+ r->uri, lockname);
+ apr_file_remove(lockname, r->pool);
+ }
+
+ /* try obtain a lock on the file */
+ if (APR_SUCCESS == (status = apr_file_open(&lockfile, lockname,
+ APR_WRITE | APR_CREATE | APR_EXCL | APR_DELONCLOSE,
+ APR_UREAD | APR_UWRITE, r->pool))) {
+ apr_pool_userdata_set(lockfile, CACHE_LOCKFILE_KEY, NULL, r->pool);
+ }
+ return status;
+
+}
+
+/**
+ * Remove the cache lock, if present.
+ *
+ * First, try to close the file handle, whose delete-on-close should
+ * kill the file. Otherwise, just delete the file by name.
+ *
+ * If no lock name has yet been calculated, do the calculation of the
+ * lock name first before trying to delete the file.
+ *
+ * If an optional bucket brigade is passed, the lock will only be
+ * removed if the bucket brigade contains an EOS bucket.
+ */
+CACHE_DECLARE(apr_status_t) ap_cache_remove_lock(cache_server_conf *conf,
+ request_rec *r, char *key, apr_bucket_brigade *bb) {
+ void *dummy;
+ const char *lockname;
+
+ if (!conf || !conf->lock || !conf->lockpath) {
+ /* no locks configured, leave */
+ return APR_SUCCESS;
+ }
+ if (bb) {
+ apr_bucket *e;
+ int eos_found = 0;
+ for (e = APR_BRIGADE_FIRST(bb);
+ e != APR_BRIGADE_SENTINEL(bb);
+ e = APR_BUCKET_NEXT(e))
+ {
+ if (APR_BUCKET_IS_EOS(e)) {
+ eos_found = 1;
+ break;
+ }
+ }
+ if (!eos_found) {
+ /* no eos found in brigade, don't delete anything just yet,
+ * we are not done.
+ */
+ return APR_SUCCESS;
+ }
+ }
+ apr_pool_userdata_get(&dummy, CACHE_LOCKFILE_KEY, r->pool);
+ if (dummy) {
+ return apr_file_close((apr_file_t *)dummy);
+ }
+ apr_pool_userdata_get(&dummy, CACHE_LOCKNAME_KEY, r->pool);
+ lockname = (const char *)dummy;
+ if (!lockname) {
+ char dir[5];
+
+ /* create the key if it doesn't exist */
+ if (!key) {
+ cache_generate_key(r, r->pool, &key);
+ }
+
+ /* create a hashed filename from the key, and save it for later */
+ lockname = ap_cache_generate_name(r->pool, 0, 0, key);
+
+ /* lock files represent discrete just-went-stale URLs "in flight", so
+ * we support a simple two level directory structure, more is overkill.
+ */
+ dir[0] = '/';
+ dir[1] = lockname[0];
+ dir[2] = '/';
+ dir[3] = lockname[1];
+ dir[4] = 0;
+
+ lockname = apr_pstrcat(r->pool, conf->lockpath, dir, "/", lockname, NULL);
+ }
+ return apr_file_remove(lockname, r->pool);
+}
+
CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
request_rec *r)
{
+ apr_status_t status;
apr_int64_t age, maxage_req, maxage_cresp, maxage, smaxage, maxstale;
apr_int64_t minfresh;
const char *cc_cresp, *cc_req;
@@ -176,6 +351,7 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
char *val;
apr_time_t age_c = 0;
cache_info *info = &(h->cache_obj->info);
+ const char *warn_head;
cache_server_conf *conf =
(cache_server_conf *)ap_get_module_config(r->server->module_config,
&cache_module);
@@ -338,7 +514,6 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
((smaxage == -1) && (maxage == -1) &&
(info->expire != APR_DATE_BAD) &&
(age < (apr_time_sec(info->expire - info->date) + maxstale - minfresh)))) {
- const char *warn_head;
warn_head = apr_table_get(h->resp_hdrs, "Warning");
@@ -361,7 +536,7 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
}
/*
* If none of Expires, Cache-Control: max-age, or Cache-Control:
- * s-maxage appears in the response, and the respose header age
+ * s-maxage appears in the response, and the response header age
* calculated is more than 24 hours add the warning 113
*/
if ((maxage_cresp == -1) && (smaxage == -1) &&
@@ -380,7 +555,71 @@ CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h,
return 1; /* Cache object is fresh (enough) */
}
- return 0; /* Cache object is stale */
+ /*
+ * At this point we are stale, but: if we are under load, we may let
+ * a significant number of stale requests through before the first
+ * stale request successfully revalidates itself, causing a sudden
+ * unexpected thundering herd which in turn brings angst and drama.
+ *
+ * So.
+ *
+ * We want the first stale request to go through as normal. But the
+ * second and subsequent request, we must pretend to be fresh until
+ * the first request comes back with either new content or confirmation
+ * that the stale content is still fresh.
+ *
+ * To achieve this, we create a very simple file based lock based on
+ * the key of the cached object. We attempt to open the lock file with
+ * exclusive write access. If we succeed, woohoo! we're first, and we
+ * follow the stale path to the backend server. If we fail, oh well,
+ * we follow the fresh path, and avoid being a thundering herd.
+ *
+ * The lock lives only as long as the stale request that went on ahead.
+ * If the request succeeds, the lock is deleted. If the request fails,
+ * the lock is deleted, and another request gets to make a new lock
+ * and try again.
+ *
+ * At any time, a request marked "no-cache" will force a refresh,
+ * ignoring the lock, ensuring an extended lockout is impossible.
+ *
+ * A lock that exceeds a maximum age will be deleted, and another
+ * request gets to make a new lock and try again.
+ */
+ status = ap_cache_try_lock(conf, r, (char *)h->cache_obj->key);
+ if (APR_SUCCESS == status) {
+ /* we obtained a lock, follow the stale path */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "Cache lock obtained for stale cached URL, "
+ "revalidating entry: %s",
+ r->unparsed_uri);
+ return 0;
+ }
+ else if (APR_EEXIST == status) {
+ /* lock already exists, return stale data anyway, with a warning */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "Cache already locked for stale cached URL, "
+ "pretend it is fresh: %s",
+ r->unparsed_uri);
+
+ /* make sure we don't stomp on a previous warning */
+ warn_head = apr_table_get(h->resp_hdrs, "Warning");
+ if ((warn_head == NULL) ||
+ ((warn_head != NULL) && (ap_strstr_c(warn_head, "110") == NULL))) {
+ apr_table_merge(h->resp_hdrs, "Warning",
+ "110 Response is stale");
+ }
+
+ return 1;
+ }
+ else {
+ /* some other error occurred, just treat the object as stale */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
+ "Attempt to obtain a cache lock for stale "
+ "cached URL failed, revalidating entry anyway: %s",
+ r->unparsed_uri);
+ return 0;
+ }
+
}
/*
diff --git a/modules/cache/mod_cache.c b/modules/cache/mod_cache.c
index 49a37423..162773f9 100644
--- a/modules/cache/mod_cache.c
+++ b/modules/cache/mod_cache.c
@@ -114,40 +114,56 @@ static int cache_url_handler(request_rec *r, int lookup)
if (rv == DECLINED) {
if (!lookup) {
- /*
- * Add cache_save filter to cache this request. Choose
- * the correct filter by checking if we are a subrequest
- * or not.
+ /* try to obtain a cache lock at this point. if we succeed,
+ * we are the first to try and cache this url. if we fail,
+ * it means someone else is already trying to cache this
+ * url, and we should just let the request through to the
+ * backend without any attempt to cache. this stops
+ * duplicated simultaneous attempts to cache an entity.
*/
- if (r->main) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
- r->server,
- "Adding CACHE_SAVE_SUBREQ filter for %s",
- r->uri);
- ap_add_output_filter_handle(cache_save_subreq_filter_handle,
- NULL, r, r->connection);
+ rv = ap_cache_try_lock(conf, r, NULL);
+ if (APR_SUCCESS == rv) {
+
+ /*
+ * Add cache_save filter to cache this request. Choose
+ * the correct filter by checking if we are a subrequest
+ * or not.
+ */
+ if (r->main) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+ r->server,
+ "Adding CACHE_SAVE_SUBREQ filter for %s",
+ r->uri);
+ ap_add_output_filter_handle(cache_save_subreq_filter_handle,
+ NULL, r, r->connection);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+ r->server, "Adding CACHE_SAVE filter for %s",
+ r->uri);
+ ap_add_output_filter_handle(cache_save_filter_handle,
+ NULL, r, r->connection);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
+ "Adding CACHE_REMOVE_URL filter for %s",
+ r->uri);
+
+ /* Add cache_remove_url filter to this request to remove a
+ * stale cache entry if needed. Also put the current cache
+ * request rec in the filter context, as the request that
+ * is available later during running the filter maybe
+ * different due to an internal redirect.
+ */
+ cache->remove_url_filter =
+ ap_add_output_filter_handle(cache_remove_url_filter_handle,
+ cache, r, r->connection);
}
else {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
- r->server, "Adding CACHE_SAVE filter for %s",
- r->uri);
- ap_add_output_filter_handle(cache_save_filter_handle,
- NULL, r, r->connection);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
+ r->server, "Cache locked for url, not caching "
+ "response: %s", r->uri);
}
-
- ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
- "Adding CACHE_REMOVE_URL filter for %s",
- r->uri);
-
- /* Add cache_remove_url filter to this request to remove a
- * stale cache entry if needed. Also put the current cache
- * request rec in the filter context, as the request that
- * is available later during running the filter maybe
- * different due to an internal redirect.
- */
- cache->remove_url_filter =
- ap_add_output_filter_handle(cache_remove_url_filter_handle,
- cache, r, r->connection);
}
else {
if (cache->stale_headers) {
@@ -166,7 +182,7 @@ static int cache_url_handler(request_rec *r, int lookup)
/* error */
ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
"cache: error returned while checking for cached "
- "file by %s cache", cache->provider_name);
+ "file by '%s' cache", cache->provider_name);
}
return DECLINED;
}
@@ -313,6 +329,10 @@ static int cache_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
* Check to see if we *can* save this particular response.
* If we can, call cache_create_entity() and save the headers and body
* Finally, pass the data to the next filter (the network or whatever)
+ *
+ * After the various failure cases, the cache lock is proactively removed, so
+ * that another request is given the opportunity to attempt to cache without
+ * waiting for a potentially slow client to acknowledge the failure.
*/
static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
@@ -328,6 +348,7 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
cache_info *info = NULL;
char *reason;
apr_pool_t *p;
+ apr_bucket *e;
conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
&cache_module);
@@ -370,7 +391,19 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
"cache: Cache provider's store_body failed!");
ap_remove_output_filter(f);
+
+ /* give someone else the chance to cache the file */
+ ap_cache_remove_lock(conf, r, cache->handle ?
+ (char *)cache->handle->cache_obj->key : NULL, NULL);
}
+ else {
+
+ /* proactively remove the lock as soon as we see the eos bucket */
+ ap_cache_remove_lock(conf, r, cache->handle ?
+ (char *)cache->handle->cache_obj->key : NULL, in);
+
+ }
+
return ap_pass_brigade(f->next, in);
}
@@ -441,17 +474,17 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
* telling us to serve the cached copy.
*/
if (exps != NULL || cc_out != NULL) {
- /* We are also allowed to cache any response given that it has a
- * valid Expires or Cache Control header. If we find a either of
- * those here, we pass request through the rest of the tests. From
+ /* We are also allowed to cache any response given that it has a
+ * valid Expires or Cache Control header. If we find a either of
+ * those here, we pass request through the rest of the tests. From
* the RFC:
*
- * A response received with any other status code (e.g. status
- * codes 302 and 307) MUST NOT be returned in a reply to a
- * subsequent request unless there are cache-control directives or
- * another header(s) that explicitly allow it. For example, these
- * include the following: an Expires header (section 14.21); a
- * "max-age", "s-maxage", "must-revalidate", "proxy-revalidate",
+ * A response received with any other status code (e.g. status
+ * codes 302 and 307) MUST NOT be returned in a reply to a
+ * subsequent request unless there are cache-control directives or
+ * another header(s) that explicitly allow it. For example, these
+ * include the following: an Expires header (section 14.21); a
+ * "max-age", "s-maxage", "must-revalidate", "proxy-revalidate",
* "public" or "private" cache-control directive (section 14.9).
*/
}
@@ -473,7 +506,8 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
reason = "Expires header already expired, not cacheable";
}
else if (!conf->ignorequerystring && r->parsed_uri.query && exps == NULL &&
- !ap_cache_liststr(NULL, cc_out, "max-age", NULL)) {
+ !ap_cache_liststr(NULL, cc_out, "max-age", NULL) &&
+ !ap_cache_liststr(NULL, cc_out, "s-maxage", NULL)) {
/* if a query string is present but no explicit expiration time,
* don't cache it (RFC 2616/13.9 & 13.2.1)
*/
@@ -487,14 +521,17 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
reason = "HTTP Status 304 Not Modified";
}
else if (r->status == HTTP_OK && lastmods == NULL && etag == NULL
- && (exps == NULL) && (conf->no_last_mod_ignore ==0)) {
+ && (exps == NULL) && (conf->no_last_mod_ignore ==0) &&
+ !ap_cache_liststr(NULL, cc_out, "max-age", NULL) &&
+ !ap_cache_liststr(NULL, cc_out, "s-maxage", NULL)) {
/* 200 OK response from HTTP/1.0 and up without Last-Modified,
- * Etag, or Expires headers.
+ * Etag, Expires, Cache-Control:max-age, or Cache-Control:s-maxage
+ * headers.
*/
/* Note: mod-include clears last_modified/expires/etags - this
* is why we have an optional function for a key-gen ;-)
*/
- reason = "No Last-Modified, Etag, or Expires headers";
+ reason = "No Last-Modified, Etag, Expires, Cache-Control:max-age or Cache-Control:s-maxage headers";
}
else if (r->header_only && !cache->stale_handle) {
/* Forbid HEAD requests unless we have it cached already */
@@ -540,7 +577,7 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
"*", NULL)) {
reason = "Vary header contains '*'";
}
- else if (apr_table_get(r->subprocess_env, "no-cache") != NULL) {
+ else if (apr_table_get(r->subprocess_env, "no-cache") != NULL) {
reason = "environment variable 'no-cache' is set";
}
else if (r->no_cache) {
@@ -556,6 +593,10 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
/* remove this filter from the chain */
ap_remove_output_filter(f);
+ /* remove the lock file unconditionally */
+ ap_cache_remove_lock(conf, r, cache->handle ?
+ (char *)cache->handle->cache_obj->key : NULL, NULL);
+
/* ship the data up the stack */
return ap_pass_brigade(f->next, in);
}
@@ -580,7 +621,6 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
/* if we don't get the content-length, see if we have all the
* buckets and use their length to calculate the size
*/
- apr_bucket *e;
int all_buckets_here=0;
int unresolved_length = 0;
size=0;
@@ -657,6 +697,8 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
if (rv != OK) {
/* Caching layer declined the opportunity to cache the response */
ap_remove_output_filter(f);
+ ap_cache_remove_lock(conf, r, cache->handle ?
+ (char *)cache->handle->cache_obj->key : NULL, NULL);
return ap_pass_brigade(f->next, in);
}
@@ -845,16 +887,23 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
"cache: attempt to remove url from cache unsuccessful.");
}
+
}
+ /* let someone else attempt to cache */
+ ap_cache_remove_lock(conf, r, cache->handle ?
+ (char *)cache->handle->cache_obj->key : NULL, NULL);
+
return ap_pass_brigade(f->next, bb);
}
- if(rv != APR_SUCCESS) {
+ if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
"cache: store_headers failed");
- ap_remove_output_filter(f);
+ ap_remove_output_filter(f);
+ ap_cache_remove_lock(conf, r, cache->handle ?
+ (char *)cache->handle->cache_obj->key : NULL, NULL);
return ap_pass_brigade(f->next, in);
}
@@ -863,8 +912,15 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
"cache: store_body failed");
ap_remove_output_filter(f);
+ ap_cache_remove_lock(conf, r, cache->handle ?
+ (char *)cache->handle->cache_obj->key : NULL, NULL);
+ return ap_pass_brigade(f->next, in);
}
+ /* proactively remove the lock as soon as we see the eos bucket */
+ ap_cache_remove_lock(conf, r, cache->handle ?
+ (char *)cache->handle->cache_obj->key : NULL, in);
+
return ap_pass_brigade(f->next, in);
}
@@ -908,6 +964,7 @@ static int cache_remove_url_filter(ap_filter_t *f, apr_bucket_brigade *in)
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, in);
}
+
/* Now remove this cache entry from the cache */
cache_remove_url(cache, r->pool);
@@ -921,6 +978,7 @@ static int cache_remove_url_filter(ap_filter_t *f, apr_bucket_brigade *in)
static void * create_cache_config(apr_pool_t *p, server_rec *s)
{
+ const char *tmppath;
cache_server_conf *ps = apr_pcalloc(p, sizeof(cache_server_conf));
/* array of URL prefixes for which caching is enabled */
@@ -953,6 +1011,13 @@ static void * create_cache_config(apr_pool_t *p, server_rec *s)
/* array of identifiers that should not be used for key calculation */
ps->ignore_session_id = apr_array_make(p, 10, sizeof(char *));
ps->ignore_session_id_set = CACHE_IGNORE_SESSION_ID_UNSET;
+ ps->lock = 0; /* thundering herd lock defaults to off */
+ ps->lock_set = 0;
+ apr_temp_dir_get(&tmppath, p);
+ if (tmppath) {
+ ps->lockpath = apr_pstrcat(p, tmppath, DEFAULT_CACHE_LOCKPATH, NULL);
+ }
+ ps->lockmaxage = apr_time_from_sec(DEFAULT_CACHE_MAXAGE);
return ps;
}
@@ -1006,6 +1071,18 @@ static void * merge_cache_config(apr_pool_t *p, void *basev, void *overridesv)
(overrides->ignore_session_id_set == CACHE_IGNORE_SESSION_ID_UNSET)
? base->ignore_session_id
: overrides->ignore_session_id;
+ ps->lock =
+ (overrides->lock_set == 0)
+ ? base->lock
+ : overrides->lock;
+ ps->lockpath =
+ (overrides->lockpath_set == 0)
+ ? base->lockpath
+ : overrides->lockpath;
+ ps->lockmaxage =
+ (overrides->lockmaxage_set == 0)
+ ? base->lockmaxage
+ : overrides->lockmaxage;
return ps;
}
static const char *set_cache_ignore_no_last_mod(cmd_parms *parms, void *dummy,
@@ -1225,6 +1302,55 @@ static const char *set_cache_ignore_querystring(cmd_parms *parms, void *dummy,
return NULL;
}
+static const char *set_cache_lock(cmd_parms *parms, void *dummy,
+ int flag)
+{
+ cache_server_conf *conf;
+
+ conf =
+ (cache_server_conf *)ap_get_module_config(parms->server->module_config,
+ &cache_module);
+ conf->lock = flag;
+ conf->lock_set = 1;
+ return NULL;
+}
+
+static const char *set_cache_lock_path(cmd_parms *parms, void *dummy,
+ const char *arg)
+{
+ cache_server_conf *conf;
+
+ conf =
+ (cache_server_conf *)ap_get_module_config(parms->server->module_config,
+ &cache_module);
+
+ conf->lockpath = ap_server_root_relative(parms->pool, arg);
+ if (!conf->lockpath) {
+ return apr_pstrcat(parms->pool, "Invalid CacheLockPath path ",
+ arg, NULL);
+ }
+ conf->lockpath_set = 1;
+ return NULL;
+}
+
+static const char *set_cache_lock_maxage(cmd_parms *parms, void *dummy,
+ const char *arg)
+{
+ cache_server_conf *conf;
+ apr_int64_t seconds;
+
+ conf =
+ (cache_server_conf *)ap_get_module_config(parms->server->module_config,
+ &cache_module);
+ seconds = apr_atoi64(arg);
+ if (seconds <= 0) {
+ return "CacheLockMaxAge value must be a non-zero positive integer";
+ }
+ conf->lockmaxage = apr_time_from_sec(seconds);
+ conf->lockmaxage_set = 1;
+ return NULL;
+}
+
static int cache_post_config(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
@@ -1284,6 +1410,15 @@ static const command_rec cache_cmds[] =
AP_INIT_TAKE1("CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF,
"The factor used to estimate Expires date from "
"LastModified date"),
+ AP_INIT_FLAG("CacheLock", set_cache_lock,
+ NULL, RSRC_CONF,
+ "Enable or disable the thundering herd lock."),
+ AP_INIT_TAKE1("CacheLockPath", set_cache_lock_path, NULL, RSRC_CONF,
+ "The thundering herd lock path. Defaults to the '"
+ DEFAULT_CACHE_LOCKPATH "' directory in the system "
+ "temp directory."),
+ AP_INIT_TAKE1("CacheLockMaxAge", set_cache_lock_maxage, NULL, RSRC_CONF,
+ "Maximum age of any thundering herd lock."),
{NULL}
};
diff --git a/modules/cache/mod_cache.h b/modules/cache/mod_cache.h
index 72653ae3..eca42c01 100644
--- a/modules/cache/mod_cache.h
+++ b/modules/cache/mod_cache.h
@@ -24,7 +24,7 @@
*/
#ifndef MOD_CACHE_H
-#define MOD_CACHE_H
+#define MOD_CACHE_H
#define CORE_PRIVATE
@@ -86,9 +86,13 @@
#define DEFAULT_CACHE_MAXEXPIRE MSEC_ONE_DAY
#define DEFAULT_CACHE_EXPIRE MSEC_ONE_HR
#define DEFAULT_CACHE_LMFACTOR (0.1)
+#define DEFAULT_CACHE_MAXAGE 5
+#define DEFAULT_CACHE_LOCKPATH "/mod_cache-lock"
+#define CACHE_LOCKNAME_KEY "mod_cache-lockname"
+#define CACHE_LOCKFILE_KEY "mod_cache-lockfile"
-/* Create a set of PROXY_DECLARE(type), PROXY_DECLARE_NONSTD(type) and
- * PROXY_DECLARE_DATA with appropriate export and import tags for the platform
+/* Create a set of CACHE_DECLARE(type), CACHE_DECLARE_NONSTD(type) and
+ * CACHE_DECLARE_DATA with appropriate export and import tags for the platform
*/
#if !defined(WIN32)
#define CACHE_DECLARE(type) type
@@ -134,7 +138,7 @@ typedef struct {
int factor_set;
/** ignore the last-modified header when deciding to cache this request */
int no_last_mod_ignore_set;
- int no_last_mod_ignore;
+ int no_last_mod_ignore;
/** ignore client's requests for uncached responses */
int ignorecachecontrol;
int ignorecachecontrol_set;
@@ -159,6 +163,13 @@ typedef struct {
#define CACHE_IGNORE_SESSION_ID_SET 1
#define CACHE_IGNORE_SESSION_ID_UNSET 0
int ignore_session_id_set;
+ /* thundering herd lock */
+ int lock;
+ int lock_set;
+ const char *lockpath;
+ int lockpath_set;
+ apr_time_t lockmaxage;
+ int lockmaxage_set;
} cache_server_conf;
/* cache info information */
@@ -173,11 +184,11 @@ struct cache_info {
/* cache handle information */
-/* XXX TODO On the next structure change/MMN bump,
+/* XXX TODO On the next structure change/MMN bump,
* count must become an apr_off_t, representing
* the potential size of disk cached objects.
* Then dig for
- * "XXX Bad Temporary Cast - see cache_object_t notes"
+ * "XXX Bad Temporary Cast - see cache_object_t notes"
*/
typedef struct cache_object cache_object_t;
struct cache_object {
@@ -206,7 +217,7 @@ typedef struct {
apr_status_t (*store_headers)(cache_handle_t *h, request_rec *r, cache_info *i);
apr_status_t (*store_body)(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b);
apr_status_t (*recall_headers) (cache_handle_t *h, request_rec *r);
- apr_status_t (*recall_body) (cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
+ apr_status_t (*recall_body) (cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
int (*create_entity) (cache_handle_t *h, request_rec *r,
const char *urlkey, apr_off_t len);
int (*open_entity) (cache_handle_t *h, request_rec *r,
@@ -260,6 +271,45 @@ CACHE_DECLARE(apr_time_t) ap_cache_current_age(cache_info *info, const apr_time_
CACHE_DECLARE(int) ap_cache_check_freshness(cache_handle_t *h, request_rec *r);
/**
+ * Try obtain a cache wide lock on the given cache key.
+ *
+ * If we return APR_SUCCESS, we obtained the lock, and we are clear to
+ * proceed to the backend. If we return APR_EEXISTS, the the lock is
+ * already locked, someone else has gone to refresh the backend data
+ * already, so we must return stale data with a warning in the mean
+ * time. If we return anything else, then something has gone pear
+ * shaped, and we allow the request through to the backend regardless.
+ *
+ * This lock is created from the request pool, meaning that should
+ * something go wrong and the lock isn't deleted on return of the
+ * request headers from the backend for whatever reason, at worst the
+ * lock will be cleaned up when the request is dies or finishes.
+ *
+ * If something goes truly bananas and the lock isn't deleted when the
+ * request dies, the lock will be trashed when its max-age is reached,
+ * or when a request arrives containing a Cache-Control: no-cache. At
+ * no point is it possible for this lock to permanently deny access to
+ * the backend.
+ */
+CACHE_DECLARE(apr_status_t) ap_cache_try_lock(cache_server_conf *conf,
+ request_rec *r, char *key);
+
+/**
+ * Remove the cache lock, if present.
+ *
+ * First, try to close the file handle, whose delete-on-close should
+ * kill the file. Otherwise, just delete the file by name.
+ *
+ * If no lock name has yet been calculated, do the calculation of the
+ * lock name first before trying to delete the file.
+ *
+ * If an optional bucket brigade is passed, the lock will only be
+ * removed if the bucket brigade contains an EOS bucket.
+ */
+CACHE_DECLARE(apr_status_t) ap_cache_remove_lock(cache_server_conf *conf,
+ request_rec *r, char *key, apr_bucket_brigade *bb);
+
+/**
* Merge in cached headers into the response
* @param h cache_handle_t
* @param r request_rec
@@ -330,8 +380,8 @@ apr_status_t cache_recall_entity_body(cache_handle_t *h, apr_pool_t *p, apr_buck
#define CACHE_DECLARE_DATA __declspec(dllimport)
#endif
-APR_DECLARE_OPTIONAL_FN(apr_status_t,
- ap_cache_generate_key,
+APR_DECLARE_OPTIONAL_FN(apr_status_t,
+ ap_cache_generate_key,
(request_rec *r, apr_pool_t*p, char**key ));
diff --git a/modules/cache/mod_disk_cache.c b/modules/cache/mod_disk_cache.c
index 70a804b8..f13800b3 100644
--- a/modules/cache/mod_disk_cache.c
+++ b/modules/cache/mod_disk_cache.c
@@ -1041,6 +1041,8 @@ static apr_status_t store_body(cache_handle_t *h, request_rec *r,
* sanity checks.
*/
if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
+ const char *cl_header = apr_table_get(r->headers_out, "Content-Length");
+
if (r->connection->aborted || r->no_cache) {
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
"disk_cache: Discarding body for URL %s "
@@ -1059,6 +1061,17 @@ static apr_status_t store_body(cache_handle_t *h, request_rec *r,
file_cache_errorcleanup(dobj, r);
return APR_EGENERAL;
}
+ if (cl_header) {
+ apr_int64_t cl = apr_atoi64(cl_header);
+ if ((errno == 0) && (dobj->file_size != cl)) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "disk_cache: URL %s didn't receive complete response, not caching",
+ h->cache_obj->key);
+ /* Remove the intermediate cache file and return non-APR_SUCCESS */
+ file_cache_errorcleanup(dobj, r);
+ return APR_EGENERAL;
+ }
+ }
/* All checks were fine. Move tempfile to final destination */
/* Link to the perm file, and close the descriptor */
diff --git a/modules/cache/mod_mem_cache.c b/modules/cache/mod_mem_cache.c
index 48bd785c..fb988915 100644
--- a/modules/cache/mod_mem_cache.c
+++ b/modules/cache/mod_mem_cache.c
@@ -745,6 +745,16 @@ static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_bri
apr_size_t len;
if (APR_BUCKET_IS_EOS(e)) {
+ const char *cl_header = apr_table_get(r->headers_out, "Content-Length");
+ if (cl_header) {
+ apr_int64_t cl = apr_atoi64(cl_header);
+ if ((errno == 0) && (obj->count != cl)) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "mem_cache: URL %s didn't receive complete response, not caching",
+ h->cache_obj->key);
+ return APR_EGENERAL;
+ }
+ }
if (mobj->m_len > obj->count) {
/* Caching a streamed response. Reallocate a buffer of the
* correct size and copy the streamed response into that
diff --git a/modules/config5.m4 b/modules/config5.m4
index b8a5868b..7dd021ee 100644
--- a/modules/config5.m4
+++ b/modules/config5.m4
@@ -18,7 +18,7 @@ AC_ARG_WITH(module,
fi
cp $pkg $modpath_current/$modfilec
fi
- module=`echo $pkg | sed -e 's;\(.*/\)*mod_\(.*\).c;\2;'`
+ module=`echo $pkg | sed -e 's;\(.*/\).*mod_\(.*\).c;\2;'`
objects="mod_$module.lo"
# The filename of a convenience library must have a "lib" prefix:
libname="libmod_$module.la"
diff --git a/modules/database/mod_dbd.c b/modules/database/mod_dbd.c
index 9914875c..cb01193f 100644
--- a/modules/database/mod_dbd.c
+++ b/modules/database/mod_dbd.c
@@ -641,6 +641,15 @@ static apr_status_t dbd_setup_init(apr_pool_t *pool, server_rec *s)
return rv;
}
+static void dbd_child_init(apr_pool_t *p, server_rec *s)
+{
+ apr_status_t rv = dbd_setup_init(p, s);
+ if (rv) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+ "DBD: child init failed!");
+ }
+}
+
#if APR_HAS_THREADS
static apr_status_t dbd_setup_lock(server_rec *s, dbd_group_t *group)
{
@@ -895,7 +904,7 @@ static void dbd_hooks(apr_pool_t *pool)
{
ap_hook_pre_config(dbd_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_post_config(dbd_post_config, NULL, NULL, APR_HOOK_MIDDLE);
- ap_hook_child_init((void*)dbd_setup_init, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_child_init(dbd_child_init, NULL, NULL, APR_HOOK_MIDDLE);
APR_REGISTER_OPTIONAL_FN(ap_dbd_prepare);
APR_REGISTER_OPTIONAL_FN(ap_dbd_open);
diff --git a/modules/dav/fs/NWGNUmakefile b/modules/dav/fs/NWGNUmakefile
index e9c27aea..f15b2dd6 100644
--- a/modules/dav/fs/NWGNUmakefile
+++ b/modules/dav/fs/NWGNUmakefile
@@ -27,9 +27,7 @@ XINCDIRS += \
$(APR)/include \
$(APRUTIL)/include \
$(AP_WORK)/include \
- $(AP_WORK)/os/NetWare \
$(AP_WORK)/server/mpm/NetWare \
- $(AP_WORK)/srclib/pcre \
$(AP_WORK)/modules/dav/main \
$(AP_WORK)/modules/arch/netware \
$(NWOS) \
diff --git a/modules/dav/lock/NWGNUmakefile b/modules/dav/lock/NWGNUmakefile
index 66e22eae..e36a5237 100644
--- a/modules/dav/lock/NWGNUmakefile
+++ b/modules/dav/lock/NWGNUmakefile
@@ -27,9 +27,7 @@ XINCDIRS += \
$(APR)/include \
$(APRUTIL)/include \
$(AP_WORK)/include \
- $(AP_WORK)/os/NetWare \
$(AP_WORK)/server/mpm/NetWare \
- $(AP_WORK)/srclib/pcre \
$(AP_WORK)/modules/dav/main \
$(NWOS) \
$(EOLIST)
diff --git a/modules/dav/main/NWGNUmakefile b/modules/dav/main/NWGNUmakefile
index c8626342..812dc9e3 100644
--- a/modules/dav/main/NWGNUmakefile
+++ b/modules/dav/main/NWGNUmakefile
@@ -26,9 +26,7 @@ XINCDIRS += \
$(APR)/include \
$(APRUTIL)/include \
$(AP_WORK)/include \
- $(AP_WORK)/os/NetWare \
$(AP_WORK)/server/mpm/NetWare \
- $(AP_WORK)/srclib/pcre \
$(NWOS) \
$(EOLIST)
diff --git a/modules/filters/config.m4 b/modules/filters/config.m4
index 9c1f6083..247d4f65 100644
--- a/modules/filters/config.m4
+++ b/modules/filters/config.m4
@@ -4,6 +4,7 @@ dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]])
APACHE_MODPATH_INIT(filters)
+APACHE_MODULE(reqtimeout, Limit time waiting for request from client, , , most)
APACHE_MODULE(ext_filter, external filter module, , , most)
APACHE_MODULE(include, Server Side Includes, , , yes)
APACHE_MODULE(filter, Smart Filtering, , , yes)
diff --git a/modules/filters/mod_charset_lite.c b/modules/filters/mod_charset_lite.c
index 2d54ff6f..76bbe551 100644
--- a/modules/filters/mod_charset_lite.c
+++ b/modules/filters/mod_charset_lite.c
@@ -181,7 +181,7 @@ static const char *add_charset_options(cmd_parms *cmd, void *in_dc,
else if (!strcasecmp(flag, "NoImplicitAdd")) {
dc->implicit_add = IA_NOIMPADD;
}
- if (!strcasecmp(flag, "TranslateAllMimeTypes")) {
+ else if (!strcasecmp(flag, "TranslateAllMimeTypes")) {
dc->force_xlate = FX_FORCE;
}
else if (!strcasecmp(flag, "NoTranslateAllMimeTypes")) {
@@ -337,6 +337,15 @@ static void xlate_insert_filter(request_rec *r)
charset_dir_t *dc = ap_get_module_config(r->per_dir_config,
&charset_lite_module);
+ if (dc && (dc->implicit_add == IA_NOIMPADD)) {
+ if (dc->debug >= DBGLVL_GORY) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "xlate output filter not added implicitly because "
+ "CharsetOptions included 'NoImplicitAdd'");
+ }
+ return;
+ }
+
if (reqinfo) {
if (reqinfo->output_ctx && !configured_on_output(r, XLATEOUT_FILTER_NAME)) {
ap_add_output_filter(XLATEOUT_FILTER_NAME, reqinfo->output_ctx, r,
diff --git a/modules/filters/mod_filter.c b/modules/filters/mod_filter.c
index 3ba13c95..73893642 100644
--- a/modules/filters/mod_filter.c
+++ b/modules/filters/mod_filter.c
@@ -201,9 +201,7 @@ static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter)
* Not sure if there's anything better to do with them
*/
if (!str) {
- if (provider->match_type == DEFINED && provider->match.string) {
- match = 0;
- }
+ match = 0;
}
/* we can't check for NULL in provider as that kills integer 0
* so we have to test each string/regexp case in the switch
diff --git a/modules/filters/mod_include.c b/modules/filters/mod_include.c
index 6ec0fa89..59ce8e3e 100644
--- a/modules/filters/mod_include.c
+++ b/modules/filters/mod_include.c
@@ -115,7 +115,9 @@ typedef struct {
const char *default_time_fmt;
const char *undefined_echo;
xbithack_t xbithack;
- const int accessenable;
+ int accessenable;
+ int lastmodified;
+ int etag;
} include_dir_config;
typedef struct {
@@ -461,6 +463,9 @@ static const char lazy_eval_sentinel;
#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
#define DEFAULT_UNDEFINED_ECHO "(none)"
+#define DEFAULT_ACCESSENABLE 0
+#define DEFAULT_LASTMODIFIED 0
+#define DEFAULT_ETAG 0
#ifdef XBITHACK
#define DEFAULT_XBITHACK XBITHACK_FULL
@@ -3529,7 +3534,9 @@ static int includes_setup(ap_filter_t *f)
* We don't know if we are going to be including a file or executing
* a program - in either case a strong ETag header will likely be invalid.
*/
- apr_table_setn(f->r->notes, "no-etag", "");
+ if (!conf->etag) {
+ apr_table_setn(f->r->notes, "no-etag", "");
+ }
return OK;
}
@@ -3618,13 +3625,32 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
* a program which may change the Last-Modified header or make the
* content completely dynamic. Therefore, we can't support these
* headers.
- * Exception: XBitHack full means we *should* set the Last-Modified field.
+ *
+ * Exception: XBitHack full means we *should* set the
+ * Last-Modified field.
+ *
+ * SSILastModified on means we *should* set the Last-Modified field
+ * if not present, or respect an existing value if present.
*/
+ /* Must we respect the last modified header? By default, no */
+ if (conf->lastmodified) {
+
+ /* update the last modified if we have a valid time, and only if
+ * we don't already have a valid last modified.
+ */
+ if (r->finfo.valid & APR_FINFO_MTIME
+ && !apr_table_get(f->r->headers_out, "Last-Modified")) {
+ ap_update_mtime(r, r->finfo.mtime);
+ ap_set_last_modified(r);
+ }
+
+ }
+
/* Assure the platform supports Group protections */
- if ((conf->xbithack == XBITHACK_FULL)
+ else if (((conf->xbithack == XBITHACK_FULL)
&& (r->finfo.valid & APR_FINFO_GPROT)
- && (r->finfo.protection & APR_GEXECUTE)) {
+ && (r->finfo.protection & APR_GEXECUTE))) {
ap_update_mtime(r, r->finfo.mtime);
ap_set_last_modified(r);
}
@@ -3704,6 +3730,9 @@ static void *create_includes_dir_config(apr_pool_t *p, char *dummy)
result->default_time_fmt = DEFAULT_TIME_FORMAT;
result->undefined_echo = DEFAULT_UNDEFINED_ECHO;
result->xbithack = DEFAULT_XBITHACK;
+ result->accessenable = DEFAULT_ACCESSENABLE;
+ result->lastmodified = DEFAULT_LASTMODIFIED;
+ result->etag = DEFAULT_ETAG;
return result;
}
@@ -3856,6 +3885,14 @@ static const command_rec includes_cmds[] =
AP_INIT_FLAG("SSIAccessEnable", ap_set_flag_slot,
(void *)APR_OFFSETOF(include_dir_config, accessenable),
OR_LIMIT, "Whether testing access is enabled. Limited to 'on' or 'off'"),
+ AP_INIT_FLAG("SSILastModified", ap_set_flag_slot,
+ (void *)APR_OFFSETOF(include_dir_config, lastmodified),
+ OR_LIMIT, "Whether to set the last modified header or respect "
+ "an existing header. Limited to 'on' or 'off'"),
+ AP_INIT_FLAG("SSIEtag", ap_set_flag_slot,
+ (void *)APR_OFFSETOF(include_dir_config, etag),
+ OR_LIMIT, "Whether to allow the generation of ETags within the server. "
+ "Existing ETags will be preserved. Limited to 'on' or 'off'"),
{NULL}
};
diff --git a/modules/filters/mod_reqtimeout.c b/modules/filters/mod_reqtimeout.c
new file mode 100644
index 00000000..215a04c0
--- /dev/null
+++ b/modules/filters/mod_reqtimeout.c
@@ -0,0 +1,431 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_connection.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "util_filter.h"
+#define APR_WANT_STRFUNC
+#include "apr_strings.h"
+
+module AP_MODULE_DECLARE_DATA reqtimeout_module;
+
+typedef struct
+{
+ int header_timeout; /* timeout for reading the req hdrs in secs */
+ int header_max_timeout; /* max timeout for req hdrs in secs */
+ int header_min_rate; /* min rate for reading req hdrs in bytes/s */
+ apr_time_t header_rate_factor;
+ int body_timeout; /* timeout for reading the req body in secs */
+ int body_max_timeout; /* max timeout for req body in secs */
+ int body_min_rate; /* timeout for reading the req body in secs */
+ apr_time_t body_rate_factor;
+} reqtimeout_srv_cfg;
+
+typedef struct
+{
+ apr_time_t timeout_at;
+ apr_time_t max_timeout_at;
+ int min_rate;
+ int new_timeout;
+ int new_max_timeout;
+ int in_keep_alive;
+ char *type;
+ apr_time_t rate_factor;
+} reqtimeout_con_cfg;
+
+typedef struct
+{
+ apr_socket_t *socket;
+} reqtimeout_ctx;
+
+static const char *const reqtimeout_filter_name = "reqtimeout";
+
+static void extend_timeout(reqtimeout_con_cfg *ccfg, apr_bucket_brigade *bb)
+{
+ apr_off_t len;
+ apr_time_t new_timeout_at;
+
+ if (apr_brigade_length(bb, 0, &len) != APR_SUCCESS || len <= 0)
+ return;
+
+ new_timeout_at = ccfg->timeout_at + len * ccfg->rate_factor;
+ if (ccfg->max_timeout_at > 0 && new_timeout_at > ccfg->max_timeout_at) {
+ ccfg->timeout_at = ccfg->max_timeout_at;
+ }
+ else {
+ ccfg->timeout_at = new_timeout_at;
+ }
+}
+
+static apr_status_t reqtimeout_filter(ap_filter_t *f,
+ apr_bucket_brigade *bb,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes)
+{
+ reqtimeout_ctx *ctx;
+ apr_time_t time_left;
+ apr_time_t now;
+ apr_status_t rv;
+ apr_interval_time_t saved_sock_timeout = -1;
+ reqtimeout_con_cfg *ccfg;
+
+ ctx = f->ctx;
+ AP_DEBUG_ASSERT(ctx != NULL);
+
+ ccfg = ap_get_module_config(f->c->conn_config, &reqtimeout_module);
+ AP_DEBUG_ASSERT(ccfg != NULL);
+
+ if (ccfg->in_keep_alive) {
+ /* For this read, the normal keep-alive timeout must be used */
+ ccfg->in_keep_alive = 0;
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ now = apr_time_now();
+ if (ccfg->new_timeout > 0) {
+ /* set new timeout */
+ ccfg->timeout_at = now + apr_time_from_sec(ccfg->new_timeout);
+ ccfg->new_timeout = 0;
+ if (ccfg->new_max_timeout > 0) {
+ ccfg->max_timeout_at = now + apr_time_from_sec(ccfg->new_max_timeout);
+ ccfg->new_max_timeout = 0;
+ }
+ }
+ else if (ccfg->timeout_at == 0) {
+ /* no timeout set */
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ time_left = ccfg->timeout_at - now;
+ if (time_left <= 0) {
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c,
+ "Request %s read timeout", ccfg->type);
+ return APR_TIMEUP;
+ }
+
+ if (block == APR_NONBLOCK_READ || mode == AP_MODE_INIT
+ || mode == AP_MODE_EATCRLF) {
+ rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
+ if (ccfg->min_rate > 0 && rv == APR_SUCCESS) {
+ extend_timeout(ccfg, bb);
+ }
+ return rv;
+ }
+
+ if (time_left < apr_time_from_sec(1)) {
+ time_left = apr_time_from_sec(1);
+ }
+
+ rv = apr_socket_timeout_get(ctx->socket, &saved_sock_timeout);
+ AP_DEBUG_ASSERT(rv == APR_SUCCESS);
+
+ if (saved_sock_timeout >= time_left) {
+ rv = apr_socket_timeout_set(ctx->socket, time_left);
+ AP_DEBUG_ASSERT(rv == APR_SUCCESS);
+ }
+ else {
+ saved_sock_timeout = -1;
+ }
+
+ rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
+
+ if (saved_sock_timeout != -1) {
+ apr_socket_timeout_set(ctx->socket, saved_sock_timeout);
+ }
+
+ if (ccfg->min_rate > 0 && rv == APR_SUCCESS) {
+ extend_timeout(ccfg, bb);
+ }
+
+ if (rv == APR_TIMEUP) {
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c,
+ "Request %s read timeout", ccfg->type);
+ }
+ return rv;
+}
+
+static int reqtimeout_pre_conn(conn_rec *c, void *csd)
+{
+ reqtimeout_ctx *ctx;
+ reqtimeout_con_cfg *ccfg;
+ reqtimeout_srv_cfg *cfg;
+
+ cfg = ap_get_module_config(c->base_server->module_config,
+ &reqtimeout_module);
+ AP_DEBUG_ASSERT(cfg != NULL);
+ if (cfg->header_timeout <= 0 && cfg->body_timeout <= 0) {
+ /* not configured for this vhost */
+ return OK;
+ }
+
+ ctx = apr_pcalloc(c->pool, sizeof(reqtimeout_ctx));
+ ctx->socket = csd;
+
+ ccfg = apr_pcalloc(c->pool, sizeof(reqtimeout_con_cfg));
+ ccfg->new_timeout = cfg->header_timeout;
+ ccfg->new_max_timeout = cfg->header_max_timeout;
+ ccfg->type = "header";
+ ccfg->min_rate = cfg->header_min_rate;
+ ccfg->rate_factor = cfg->header_rate_factor;
+ ap_set_module_config(c->conn_config, &reqtimeout_module, ccfg);
+
+ ap_add_input_filter("reqtimeout", ctx, NULL, c);
+ return OK;
+}
+
+static int reqtimeout_after_headers(request_rec *r)
+{
+ reqtimeout_srv_cfg *cfg;
+ reqtimeout_con_cfg *ccfg =
+ ap_get_module_config(r->connection->conn_config, &reqtimeout_module);
+
+ if (ccfg == NULL) {
+ /* not configured for this vhost */
+ return OK;
+ }
+
+ cfg = ap_get_module_config(r->connection->base_server->module_config,
+ &reqtimeout_module);
+ AP_DEBUG_ASSERT(cfg != NULL);
+
+ ccfg->timeout_at = 0;
+ ccfg->max_timeout_at = 0;
+ ccfg->new_timeout = cfg->body_timeout;
+ ccfg->new_max_timeout = cfg->body_max_timeout;
+ ccfg->min_rate = cfg->body_min_rate;
+ ccfg->rate_factor = cfg->body_rate_factor;
+ ccfg->type = "body";
+
+ return OK;
+}
+
+static int reqtimeout_after_body(request_rec *r)
+{
+ reqtimeout_srv_cfg *cfg;
+ reqtimeout_con_cfg *ccfg =
+ ap_get_module_config(r->connection->conn_config, &reqtimeout_module);
+
+ if (ccfg == NULL) {
+ /* not configured for this vhost */
+ return OK;
+ }
+
+ cfg = ap_get_module_config(r->connection->base_server->module_config,
+ &reqtimeout_module);
+ AP_DEBUG_ASSERT(cfg != NULL);
+
+ ccfg->timeout_at = 0;
+ ccfg->max_timeout_at = 0;
+ ccfg->in_keep_alive = 1;
+ ccfg->new_timeout = cfg->header_timeout;
+ ccfg->new_max_timeout = cfg->header_max_timeout;
+ ccfg->min_rate = cfg->header_min_rate;
+ ccfg->rate_factor = cfg->header_rate_factor;
+
+ ccfg->type = "header";
+
+ return OK;
+}
+
+static void *reqtimeout_create_srv_config(apr_pool_t *p, server_rec *s)
+{
+ reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
+
+ cfg->header_timeout = -1;
+ cfg->header_max_timeout = -1;
+ cfg->header_min_rate = -1;
+ cfg->body_timeout = -1;
+ cfg->body_max_timeout = -1;
+ cfg->body_min_rate = -1;
+
+ return cfg;
+}
+
+#define MERGE_INT(cfg, b, a, val) cfg->val = (a->val == -1) ? b->val : a->val;
+static void *reqtimeout_merge_srv_config(apr_pool_t *p, void *base_, void *add_)
+{
+ reqtimeout_srv_cfg *base = base_;
+ reqtimeout_srv_cfg *add = add_;
+ reqtimeout_srv_cfg *cfg = apr_pcalloc(p, sizeof(reqtimeout_srv_cfg));
+
+ MERGE_INT(cfg, base, add, header_timeout);
+ MERGE_INT(cfg, base, add, header_max_timeout);
+ MERGE_INT(cfg, base, add, header_min_rate);
+ MERGE_INT(cfg, base, add, body_timeout);
+ MERGE_INT(cfg, base, add, body_max_timeout);
+ MERGE_INT(cfg, base, add, body_min_rate);
+
+ cfg->header_rate_factor = (cfg->header_min_rate == -1) ? base->header_rate_factor :
+ add->header_rate_factor;
+ cfg->body_rate_factor = (cfg->body_min_rate == -1) ? base->body_rate_factor :
+ add->body_rate_factor;
+
+ return cfg;
+}
+
+static const char *parse_int(apr_pool_t *p, const char *arg, int *val) {
+ char *endptr;
+ *val = strtol(arg, &endptr, 10);
+
+ if (arg == endptr) {
+ return apr_psprintf(p, "Value '%s' not numerical", endptr);
+ }
+ if (*endptr != '\0') {
+ return apr_psprintf(p, "Cannot parse '%s'", endptr);
+ }
+ if (*val < 0) {
+ return "Value must be non-negative";
+ }
+ return NULL;
+}
+
+static const char *set_reqtimeout_param(reqtimeout_srv_cfg *conf,
+ apr_pool_t *p,
+ const char *key,
+ const char *val)
+{
+ const char *ret = NULL;
+ char *rate_str = NULL, *initial_str, *max_str = NULL;
+ int rate = 0, initial = 0, max = 0;
+ enum { PARAM_HEADER, PARAM_BODY } type;
+
+ if (!strcasecmp(key, "header")) {
+ type = PARAM_HEADER;
+ }
+ else if (!strcasecmp(key, "body")) {
+ type = PARAM_BODY;
+ }
+ else {
+ return "Unknown RequestReadTimeout parameter";
+ }
+
+ if ((rate_str = ap_strcasestr(val, ",minrate="))) {
+ initial_str = apr_pstrndup(p, val, rate_str - val);
+ rate_str += strlen(",minrate=");
+ ret = parse_int(p, rate_str, &rate);
+ if (ret)
+ return ret;
+
+ if (rate == 0)
+ return "Minimum data rate must be larger than 0";
+
+ if ((max_str = strchr(initial_str, '-'))) {
+ *max_str++ = '\0';
+ ret = parse_int(p, max_str, &max);
+ if (ret)
+ return ret;
+ }
+
+ ret = parse_int(p, initial_str, &initial);
+ }
+ else {
+ if (ap_strchr_c(val, '-'))
+ return "Must set MinRate option if using timeout range";
+ ret = parse_int(p, val, &initial);
+ }
+
+ if (ret)
+ return ret;
+
+ if (max && initial >= max) {
+ return "Maximum timeout must be larger than initial timeout";
+ }
+
+ if (type == PARAM_HEADER) {
+ conf->header_timeout = initial;
+ conf->header_max_timeout = max;
+ conf->header_min_rate = rate;
+ if (rate)
+ conf->header_rate_factor = apr_time_from_sec(1) / rate;
+ }
+ else {
+ conf->body_timeout = initial;
+ conf->body_max_timeout = max;
+ conf->body_min_rate = rate;
+ if (rate)
+ conf->body_rate_factor = apr_time_from_sec(1) / rate;
+ }
+ return ret;
+}
+
+static const char *set_reqtimeouts(cmd_parms *cmd, void *mconfig,
+ const char *arg)
+{
+ reqtimeout_srv_cfg *conf =
+ ap_get_module_config(cmd->server->module_config,
+ &reqtimeout_module);
+
+ while (*arg) {
+ char *word, *val;
+ const char *err;
+
+ word = ap_getword_conf(cmd->pool, &arg);
+ val = strchr(word, '=');
+ if (!val) {
+ return "Invalid RequestReadTimeout parameter. Parameter must be "
+ "in the form 'key=value'";
+ }
+ else
+ *val++ = '\0';
+
+ err = set_reqtimeout_param(conf, cmd->pool, word, val);
+
+ if (err)
+ return apr_psprintf(cmd->temp_pool, "RequestReadTimeout: %s=%s: %s",
+ word, val, err);
+ }
+
+ return NULL;
+
+}
+
+static void reqtimeout_hooks(apr_pool_t *pool)
+{
+ /*
+ * mod_ssl is AP_FTYPE_CONNECTION + 5 and mod_reqtimeout needs to
+ * be called before mod_ssl. Otherwise repeated reads during the ssl
+ * handshake can prevent the timeout from triggering.
+ */
+ ap_register_input_filter(reqtimeout_filter_name, reqtimeout_filter, NULL,
+ AP_FTYPE_CONNECTION + 8);
+ ap_hook_pre_connection(reqtimeout_pre_conn, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_post_read_request(reqtimeout_after_headers, NULL, NULL,
+ APR_HOOK_MIDDLE);
+ ap_hook_log_transaction(reqtimeout_after_body, NULL, NULL,
+ APR_HOOK_MIDDLE);
+}
+
+static const command_rec reqtimeout_cmds[] = {
+ AP_INIT_RAW_ARGS("RequestReadTimeout", set_reqtimeouts, NULL, RSRC_CONF,
+ "Set various timeout parameters for reading request "
+ "headers and body"),
+ {NULL}
+};
+
+module AP_MODULE_DECLARE_DATA reqtimeout_module = {
+ STANDARD20_MODULE_STUFF,
+ NULL, /* create per-dir config structures */
+ NULL, /* merge per-dir config structures */
+ reqtimeout_create_srv_config, /* create per-server config structures */
+ reqtimeout_merge_srv_config, /* merge per-server config structures */
+ reqtimeout_cmds, /* table of config file commands */
+ reqtimeout_hooks
+};
diff --git a/modules/filters/mod_reqtimeout.dsp b/modules/filters/mod_reqtimeout.dsp
new file mode 100644
index 00000000..0afa4683
--- /dev/null
+++ b/modules/filters/mod_reqtimeout.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_reqtimeout" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_reqtimeout - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "mod_reqtimeout.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "mod_reqtimeout.mak" CFG="mod_reqtimeout - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_reqtimeout - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_reqtimeout - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "mod_reqtimeout - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "AP_RL_DECLARE_EXPORT" /Fd"Release\mod_reqtimeout_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/mod_reqtimeout.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_reqtimeout.so" /d LONG_NAME="reqtimeout_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /out:".\Release\mod_reqtimeout.so" /base:@..\..\os\win32\BaseAddr.ref,mod_reqtimeout.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_reqtimeout.so" /base:@..\..\os\win32\BaseAddr.ref,mod_reqtimeout.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_reqtimeout.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_reqtimeout - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "AP_RL_DECLARE_EXPORT" /Fd"Debug\mod_reqtimeout_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/mod_reqtimeout.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_reqtimeout.so" /d LONG_NAME="reqtimeout_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_reqtimeout.so" /base:@..\..\os\win32\BaseAddr.ref,mod_reqtimeout.so
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_reqtimeout.so" /base:@..\..\os\win32\BaseAddr.ref,mod_reqtimeout.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_reqtimeout.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_reqtimeout - Win32 Release"
+# Name "mod_reqtimeout - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_reqtimeout.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/http/byterange_filter.c b/modules/http/byterange_filter.c
index a25d1e59..e38d366e 100644
--- a/modules/http/byterange_filter.c
+++ b/modules/http/byterange_filter.c
@@ -308,7 +308,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f,
APR_BRIGADE_INSERT_TAIL(bsend, e);
/* we're done with the original content - all of our data is in bsend. */
- apr_brigade_destroy(bb);
+ apr_brigade_cleanup(bb);
/* send our multipart output */
return ap_pass_brigade(f->next, bsend);
diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c
index f7f86df4..7fbc3692 100644
--- a/modules/http/http_filters.c
+++ b/modules/http/http_filters.c
@@ -524,6 +524,11 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
if (ctx->state != BODY_NONE) {
ctx->remaining -= totalread;
+ if (ctx->remaining > 0) {
+ e = APR_BRIGADE_LAST(b);
+ if (APR_BUCKET_IS_EOS(e))
+ return APR_EOF;
+ }
}
/* If we have no more bytes remaining on a C-L request,
@@ -1115,7 +1120,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx));
}
else if (ctx->headers_sent) {
- apr_brigade_destroy(b);
+ apr_brigade_cleanup(b);
return OK;
}
}
@@ -1286,7 +1291,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
ap_pass_brigade(f->next, b2);
if (r->header_only) {
- apr_brigade_destroy(b);
+ apr_brigade_cleanup(b);
ctx->headers_sent = 1;
return OK;
}
diff --git a/modules/http/http_request.c b/modules/http/http_request.c
index fe10a196..3a7e5ef9 100644
--- a/modules/http/http_request.c
+++ b/modules/http/http_request.c
@@ -353,6 +353,8 @@ static request_rec *internal_internal_redirect(const char *new_uri,
new->method_number = r->method_number;
new->allowed_methods = ap_make_method_list(new->pool, 2);
ap_parse_uri(new, new_uri);
+ new->parsed_uri.port_str = r->parsed_uri.port_str;
+ new->parsed_uri.port = r->parsed_uri.port;
new->request_config = ap_create_request_config(r->pool);
@@ -386,7 +388,6 @@ static request_rec *internal_internal_redirect(const char *new_uri,
new->err_headers_out = r->err_headers_out;
new->subprocess_env = rename_original_env(r->pool, r->subprocess_env);
new->notes = apr_table_make(r->pool, 5);
- new->allowed_methods = ap_make_method_list(new->pool, 2);
new->htaccess = r->htaccess;
new->no_cache = r->no_cache;
diff --git a/modules/http/mod_mime.c b/modules/http/mod_mime.c
index d9d8b101..eed6ebd9 100644
--- a/modules/http/mod_mime.c
+++ b/modules/http/mod_mime.c
@@ -274,6 +274,16 @@ static const char *add_extension_info(cmd_parms *cmd, void *m_,
}
/*
+ * As RemoveType should also override the info from TypesConfig, we add an
+ * empty string as type instead of actually removing the type.
+ */
+static const char *remove_extension_type(cmd_parms *cmd, void *m_,
+ const char *ext)
+{
+ return add_extension_info(cmd, m_, "", ext);
+}
+
+/*
* Note handler names are un-added with each per_dir_config merge.
* This keeps the association from being inherited, but not
* from being re-added at a subordinate level.
@@ -312,6 +322,12 @@ static const char *multiviews_match(cmd_parms *cmd, void *m_,
const char *include)
{
mime_dir_config *m = (mime_dir_config *) m_;
+ const char *errmsg;
+
+ errmsg = ap_check_cmd_context(cmd, NOT_IN_LOCATION);
+ if (errmsg != NULL) {
+ return errmsg;
+ }
if (strcasecmp(include, "Any") == 0) {
if (m->multimatch && (m->multimatch & ~MULTIMATCH_ANY)) {
@@ -397,7 +413,7 @@ static const command_rec mime_cmds[] =
AP_INIT_ITERATE("RemoveOutputFilter", remove_extension_info,
(void *)APR_OFFSETOF(extension_info, output_filters), OR_FILEINFO,
"one or more file extensions"),
- AP_INIT_ITERATE("RemoveType", remove_extension_info,
+ AP_INIT_ITERATE("RemoveType", remove_extension_type,
(void *)APR_OFFSETOF(extension_info, forced_type), OR_FILEINFO,
"one or more file extensions"),
AP_INIT_TAKE1("TypesConfig", set_types_config, NULL, RSRC_CONF,
@@ -809,7 +825,8 @@ static int find_ct(request_rec *r)
if (exinfo != NULL) {
- if (exinfo->forced_type) {
+ /* empty string is treated as special case for RemoveType */
+ if (exinfo->forced_type && *exinfo->forced_type) {
ap_set_content_type(r, exinfo->forced_type);
found = 1;
}
diff --git a/modules/ldap/util_ldap.c b/modules/ldap/util_ldap.c
index 5b432696..a894b6ea 100644
--- a/modules/ldap/util_ldap.c
+++ b/modules/ldap/util_ldap.c
@@ -1820,7 +1820,7 @@ static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
#endif
- st->cache_bytes = 100000;
+ st->cache_bytes = 500000;
st->search_cache_ttl = 600000000;
st->search_cache_size = 1024;
st->compare_cache_ttl = 600000000;
diff --git a/modules/ldap/util_ldap_cache_mgr.c b/modules/ldap/util_ldap_cache_mgr.c
index 56b8cbec..6bb066ab 100644
--- a/modules/ldap/util_ldap_cache_mgr.c
+++ b/modules/ldap/util_ldap_cache_mgr.c
@@ -385,6 +385,7 @@ void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload)
void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
{
unsigned long hashval;
+ void *tmp_payload;
util_cache_node_t *node;
/* sanity check */
@@ -397,21 +398,68 @@ void *util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
util_ald_cache_purge(cache);
if (cache->numentries >= cache->maxentries) {
/* if the purge was not effective, we leave now to avoid an overflow */
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ "Purge of LDAP cache failed");
return NULL;
}
}
- /* should be safe to add an entry */
- if ((node = (util_cache_node_t *)util_ald_alloc(cache, sizeof(util_cache_node_t))) == NULL) {
- return NULL;
+ node = (util_cache_node_t *)util_ald_alloc(cache,
+ sizeof(util_cache_node_t));
+ if (node == NULL) {
+ /*
+ * XXX: The cache management should be rewritten to work
+ * properly when LDAPSharedCacheSize is too small.
+ */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
+ "LDAPSharedCacheSize is too small. Increase it or "
+ "reduce LDAPCacheEntries/LDAPOpCacheEntries!");
+ if (cache->numentries < cache->fullmark) {
+ /*
+ * We have not even reached fullmark, trigger a complete purge.
+ * This is still better than not being able to add new entries
+ * at all.
+ */
+ cache->marktime = apr_time_now();
+ }
+ util_ald_cache_purge(cache);
+ node = (util_cache_node_t *)util_ald_alloc(cache,
+ sizeof(util_cache_node_t));
+ if (node == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ "Could not allocate memory for LDAP cache entry");
+ return NULL;
+ }
}
/* Take a copy of the payload before proceeeding. */
- payload = (*cache->copy)(cache, payload);
- if (!payload) {
- util_ald_free(cache, node);
- return NULL;
+ tmp_payload = (*cache->copy)(cache, payload);
+ if (tmp_payload == NULL) {
+ /*
+ * XXX: The cache management should be rewritten to work
+ * properly when LDAPSharedCacheSize is too small.
+ */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
+ "LDAPSharedCacheSize is too small. Increase it or "
+ "reduce LDAPCacheEntries/LDAPOpCacheEntries!");
+ if (cache->numentries < cache->fullmark) {
+ /*
+ * We have not even reached fullmark, trigger a complete purge.
+ * This is still better than not being able to add new entries
+ * at all.
+ */
+ cache->marktime = apr_time_now();
+ }
+ util_ald_cache_purge(cache);
+ tmp_payload = (*cache->copy)(cache, payload);
+ if (tmp_payload == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ "Could not allocate memory for LDAP cache value");
+ util_ald_free(cache, node);
+ return NULL;
+ }
}
+ payload = tmp_payload;
/* populate the entry */
cache->inserts++;
diff --git a/modules/loggers/mod_log_config.c b/modules/loggers/mod_log_config.c
index 60485dd3..8ceadf7c 100644
--- a/modules/loggers/mod_log_config.c
+++ b/modules/loggers/mod_log_config.c
@@ -381,6 +381,11 @@ static const char *log_status(request_rec *r, char *a)
return pfmt(r->pool, r->status);
}
+static const char *log_handler(request_rec *r, char *a)
+{
+ return ap_escape_logitem(r->pool, r->handler);
+}
+
static const char *clf_log_bytes_sent(request_rec *r, char *a)
{
if (!r->sent_bodyct || !r->bytes_sent) {
@@ -1516,6 +1521,7 @@ static int log_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
log_pfn_register(p, "T", log_request_duration, 1);
log_pfn_register(p, "U", log_request_uri, 1);
log_pfn_register(p, "s", log_status, 1);
+ log_pfn_register(p, "R", log_handler, 1);
}
return OK;
diff --git a/modules/mappers/mod_negotiation.c b/modules/mappers/mod_negotiation.c
index a9c6bc9e..d3ae87c6 100644
--- a/modules/mappers/mod_negotiation.c
+++ b/modules/mappers/mod_negotiation.c
@@ -3130,6 +3130,9 @@ static int handle_multi(request_rec *r)
goto return_from_multi;
}
}
+ if (sub_req->args == NULL) {
+ sub_req->args = r->args;
+ }
/* now do a "fast redirect" ... promotes the sub_req into the main req */
ap_internal_fast_redirect(sub_req, r);
diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c
index b9c8a51f..d5bd5679 100644
--- a/modules/mappers/mod_rewrite.c
+++ b/modules/mappers/mod_rewrite.c
@@ -606,6 +606,13 @@ static unsigned is_absolute_uri(char *uri)
return 7;
}
break;
+
+ case 's':
+ case 'S':
+ if (!strncasecmp(uri, "cgi://", 6)) { /* scgi:// */
+ return 7;
+ }
+ break;
}
return 0;
@@ -838,7 +845,10 @@ static void reduce_uri(request_rec *r)
*/
static void fully_qualify_uri(request_rec *r)
{
- if (!is_absolute_uri(r->filename)) {
+ if (r->method_number == M_CONNECT) {
+ return;
+ }
+ else if (!is_absolute_uri(r->filename)) {
const char *thisserver;
char *thisport;
int port;
diff --git a/modules/proxy/NWGNUmakefile b/modules/proxy/NWGNUmakefile
index 212a5be7..311282e2 100644
--- a/modules/proxy/NWGNUmakefile
+++ b/modules/proxy/NWGNUmakefile
@@ -158,6 +158,7 @@ TARGET_nlm = \
$(OBJDIR)/proxyhtp.nlm \
$(OBJDIR)/proxybalancer.nlm \
$(OBJDIR)/proxyajp.nlm \
+ $(OBJDIR)/proxyscgi.nlm \
$(EOLIST)
#
diff --git a/modules/proxy/NWGNUproxy b/modules/proxy/NWGNUproxy
index eede5a5c..4d3beee4 100644
--- a/modules/proxy/NWGNUproxy
+++ b/modules/proxy/NWGNUproxy
@@ -223,16 +223,7 @@ endif
# Any symbols exported to here
#
FILES_nlm_exports = \
- proxy_module \
- proxy_hook_scheme_handler \
- proxy_hook_canon_handler \
- proxy_hook_pre_request \
- proxy_hook_post_request \
- ap_proxy_ssl_enable \
- ap_proxy_ssl_disable \
- ap_proxy_conn_is_https \
- ap_proxy_ssl_val \
- proxy_run_fixups \
+ @$(OBJDIR)/mod_proxy.imp \
$(EOLIST)
#
@@ -248,7 +239,7 @@ FILES_lib_objs = \
libs :: $(OBJDIR) $(TARGET_lib)
-nlms :: libs $(TARGET_nlm)
+nlms :: libs $(OBJDIR)/mod_proxy.imp $(TARGET_nlm)
#
# Updated this target to create necessary directories and copy files to the
@@ -262,6 +253,20 @@ install :: nlms FORCE
vpath %.c ../arch/netware
+$(OBJDIR)/mod_proxy.imp:
+ @echo Creating $@
+ @echo # Exports of mod_proxy > $@
+ @echo proxy_module, >> $@
+ @echo proxy_hook_canon_handler, >> $@
+ @echo proxy_hook_post_request, >> $@
+ @echo proxy_hook_pre_request, >> $@
+ @echo proxy_hook_scheme_handler, >> $@
+ @echo proxy_run_fixups, >> $@
+ @echo ap_proxy_conn_is_https, >> $@
+ @echo ap_proxy_ssl_enable, >> $@
+ @echo ap_proxy_ssl_disable, >> $@
+ @echo ap_proxy_ssl_val >> $@
+
#
# Include the 'tail' makefile that has targets that depend on variables defined
# in this makefile
diff --git a/modules/proxy/NWGNUproxyajp b/modules/proxy/NWGNUproxyajp
index 77942ffc..446549f8 100644
--- a/modules/proxy/NWGNUproxyajp
+++ b/modules/proxy/NWGNUproxyajp
@@ -213,15 +213,8 @@ FILE_nlm_copyright =
FILES_nlm_Ximports = \
@$(APR)/aprlib.imp \
@$(NWOS)/httpd.imp \
+ @$(OBJDIR)/mod_proxy.imp \
@libc.imp \
- proxy_module \
- proxy_hook_scheme_handler \
- proxy_hook_canon_handler \
- proxy_run_fixups \
- ap_proxy_ssl_enable \
- ap_proxy_ssl_disable \
- ap_proxy_conn_is_https \
- ap_proxy_ssl_val \
$(EOLIST)
# Don't link with Winsock if standard sockets are being used
diff --git a/modules/proxy/NWGNUproxybalancer b/modules/proxy/NWGNUproxybalancer
index c50c48f6..7dc04b8e 100644
--- a/modules/proxy/NWGNUproxybalancer
+++ b/modules/proxy/NWGNUproxybalancer
@@ -209,16 +209,8 @@ FILE_nlm_copyright =
FILES_nlm_Ximports = \
@$(APR)/aprlib.imp \
@$(NWOS)/httpd.imp \
+ @$(OBJDIR)/mod_proxy.imp \
@libc.imp \
- proxy_module \
- proxy_hook_scheme_handler \
- proxy_hook_canon_handler \
- proxy_hook_pre_request \
- proxy_hook_post_request \
- proxy_hook_load_lbmethods \
- proxy_run_fixups \
- ap_proxy_ssl_enable \
- ap_proxy_ssl_disable \
$(EOLIST)
# Don't link with Winsock if standard sockets are being used
diff --git a/modules/proxy/NWGNUproxycon b/modules/proxy/NWGNUproxycon
index 6650197b..4ce871cf 100644
--- a/modules/proxy/NWGNUproxycon
+++ b/modules/proxy/NWGNUproxycon
@@ -208,10 +208,8 @@ FILE_nlm_copyright =
FILES_nlm_Ximports = \
@$(APR)/aprlib.imp \
@$(NWOS)/httpd.imp \
+ @$(OBJDIR)/mod_proxy.imp \
@libc.imp \
- proxy_module \
- proxy_hook_scheme_handler \
- proxy_hook_canon_handler \
$(EOLIST)
#
diff --git a/modules/proxy/NWGNUproxyftp b/modules/proxy/NWGNUproxyftp
index 89fef5ac..653d9d7b 100644
--- a/modules/proxy/NWGNUproxyftp
+++ b/modules/proxy/NWGNUproxyftp
@@ -209,12 +209,8 @@ FILE_nlm_copyright =
FILES_nlm_Ximports = \
@$(APR)/aprlib.imp \
@$(NWOS)/httpd.imp \
+ @$(OBJDIR)/mod_proxy.imp \
@libc.imp \
- proxy_module \
- proxy_hook_scheme_handler \
- proxy_hook_canon_handler \
- ap_proxy_ssl_enable \
- ap_proxy_ssl_disable \
$(EOLIST)
# Don't link with Winsock if standard sockets are being used
diff --git a/modules/proxy/NWGNUproxyhtp b/modules/proxy/NWGNUproxyhtp
index 5f5c2ada..21debea1 100644
--- a/modules/proxy/NWGNUproxyhtp
+++ b/modules/proxy/NWGNUproxyhtp
@@ -209,13 +209,8 @@ FILE_nlm_copyright =
FILES_nlm_Ximports = \
@$(APR)/aprlib.imp \
@$(NWOS)/httpd.imp \
+ @$(OBJDIR)/mod_proxy.imp \
@libc.imp \
- proxy_module \
- proxy_hook_scheme_handler \
- proxy_hook_canon_handler \
- proxy_run_fixups \
- ap_proxy_ssl_enable \
- ap_proxy_ssl_disable \
$(EOLIST)
# Don't link with Winsock if standard sockets are being used
diff --git a/modules/proxy/NWGNUproxyscgi b/modules/proxy/NWGNUproxyscgi
index 40d8bb24..4c51621f 100644
--- a/modules/proxy/NWGNUproxyscgi
+++ b/modules/proxy/NWGNUproxyscgi
@@ -94,7 +94,7 @@ endif
# This is used by the link 'name' directive to name the nlm. If left blank
# TARGET_nlm (see below) will be used.
#
-NLM_NAME = proxyscg
+NLM_NAME = proxyscgi
#
# This is used by the link '-desc ' directive.
diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h
index 04b71066..358248f1 100644
--- a/modules/proxy/mod_proxy.h
+++ b/modules/proxy/mod_proxy.h
@@ -247,6 +247,7 @@ typedef struct {
* which the backend currently answers. */
int need_flush;/* Flag to decide whether we need to flush the
* filter chain or not */
+ void *forward; /* opaque forward proxy data */
} proxy_conn_rec;
typedef struct {
diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c
index 66693aa9..fa8c41f8 100644
--- a/modules/proxy/mod_proxy_ajp.c
+++ b/modules/proxy/mod_proxy_ajp.c
@@ -257,7 +257,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: ap_get_brigade failed");
apr_brigade_destroy(input_brigade);
- return HTTP_INTERNAL_SERVER_ERROR;
+ return HTTP_BAD_REQUEST;
}
/* have something */
@@ -469,7 +469,9 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
if (ap_pass_brigade(r->output_filters,
output_brigade) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "proxy: error processing body");
+ "proxy: error processing body.%s",
+ r->connection->aborted ?
+ " Client aborted connection." : "");
output_failed = 1;
}
data_sent = 1;
@@ -507,6 +509,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
conn->close++;
output_failed = 0;
result = CMD_AJP13_END_RESPONSE;
+ request_ended = 1;
}
/*
diff --git a/modules/proxy/mod_proxy_connect.c b/modules/proxy/mod_proxy_connect.c
index 8804359c..d66a50d8 100644
--- a/modules/proxy/mod_proxy_connect.c
+++ b/modules/proxy/mod_proxy_connect.c
@@ -201,7 +201,7 @@ static int proxy_connect_handler(request_rec *r, proxy_worker *worker,
return DECLINED;
}
else {
- return HTTP_BAD_GATEWAY;
+ return HTTP_SERVICE_UNAVAILABLE;
}
}
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
index 34015263..7e4767f9 100644
--- a/modules/proxy/mod_proxy_http.c
+++ b/modules/proxy/mod_proxy_http.c
@@ -1369,6 +1369,10 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
{"Keep-Alive", "Proxy-Authenticate", "TE", "Trailer", "Upgrade", NULL};
int i;
const char *te = NULL;
+ int original_status = r->status;
+ int proxy_status = OK;
+ const char *original_status_line = r->status_line;
+ const char *proxy_status_line = NULL;
bb = apr_brigade_create(p, c->bucket_alloc);
pass_bb = apr_brigade_create(p, c->bucket_alloc);
@@ -1482,7 +1486,7 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
keepchar = buffer[12];
buffer[12] = '\0';
- r->status = atoi(&buffer[9]);
+ proxy_status = atoi(&buffer[9]);
if (keepchar != '\0') {
buffer[12] = keepchar;
@@ -1493,8 +1497,13 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
buffer[12] = ' ';
buffer[13] = '\0';
}
- r->status_line = apr_pstrdup(p, &buffer[9]);
+ proxy_status_line = apr_pstrdup(p, &buffer[9]);
+ /* The status out of the front is the same as the status coming in
+ * from the back, until further notice.
+ */
+ r->status = proxy_status;
+ r->status_line = proxy_status_line;
/* read the headers. */
/* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/
@@ -1570,7 +1579,7 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
ap_set_content_type(r, apr_pstrdup(p, buf));
}
- if (!ap_is_HTTP_INFO(r->status)) {
+ if (!ap_is_HTTP_INFO(proxy_status)) {
ap_proxy_pre_http_request(origin, rp);
}
@@ -1621,7 +1630,7 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
backend->close += 1;
}
- if (ap_is_HTTP_INFO(r->status)) {
+ if (ap_is_HTTP_INFO(proxy_status)) {
interim_response++;
}
else {
@@ -1660,7 +1669,7 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
* ProxyPassReverse/etc from here to ap_proxy_read_headers
*/
- if ((r->status == 401) && (conf->error_override)) {
+ if ((proxy_status == 401) && (conf->error_override)) {
const char *buf;
const char *wa = "WWW-Authenticate";
if ((buf = apr_table_get(r->headers_out, wa))) {
@@ -1700,8 +1709,8 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
/* send body - but only if a body is expected */
if ((!r->header_only) && /* not HEAD request */
!interim_response && /* not any 1xx response */
- (r->status != HTTP_NO_CONTENT) && /* not 204 */
- (r->status != HTTP_NOT_MODIFIED)) { /* not 304 */
+ (proxy_status != HTTP_NO_CONTENT) && /* not 204 */
+ (proxy_status != HTTP_NOT_MODIFIED)) { /* not 304 */
/* We need to copy the output headers and treat them as input
* headers as well. BUT, we need to do this before we remove
@@ -1727,11 +1736,22 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
* if we are overriding the errors, we can't put the content
* of the page into the brigade
*/
- if (!conf->error_override || !ap_is_HTTP_ERROR(r->status)) {
+ if (!conf->error_override || !ap_is_HTTP_ERROR(proxy_status)) {
/* read the body, pass it to the output filters */
apr_read_type_e mode = APR_NONBLOCK_READ;
int finish = FALSE;
+ /* Handle the case where the error document is itself reverse
+ * proxied and was successful. We must maintain any previous
+ * error status so that an underlying error (eg HTTP_NOT_FOUND)
+ * doesn't become an HTTP_OK.
+ */
+ if (conf->error_override && !ap_is_HTTP_ERROR(proxy_status)
+ && ap_is_HTTP_ERROR(original_status)) {
+ r->status = original_status;
+ r->status_line = original_status_line;
+ }
+
do {
apr_off_t readbytes;
apr_status_t rv;
@@ -1848,25 +1868,27 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
if (conf->error_override) {
/* the code above this checks for 'OK' which is what the hook expects */
- if (!ap_is_HTTP_ERROR(r->status))
+ if (!ap_is_HTTP_ERROR(proxy_status)) {
return OK;
+ }
else {
/* clear r->status for override error, otherwise ErrorDocument
* thinks that this is a recursive error, and doesn't find the
* custom error page
*/
- int status = r->status;
r->status = HTTP_OK;
/* Discard body, if one is expected */
if (!r->header_only && /* not HEAD request */
- (status != HTTP_NO_CONTENT) && /* not 204 */
- (status != HTTP_NOT_MODIFIED)) { /* not 304 */
- ap_discard_request_body(rp);
- }
- return status;
+ (proxy_status != HTTP_NO_CONTENT) && /* not 204 */
+ (proxy_status != HTTP_NOT_MODIFIED)) { /* not 304 */
+ ap_discard_request_body(rp);
+ }
+ return proxy_status;
}
- } else
+ }
+ else {
return OK;
+ }
}
static
@@ -1974,10 +1996,7 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
/* Step Two: Make the Connection */
if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) {
- if (r->proxyreq == PROXYREQ_PROXY)
- status = HTTP_NOT_FOUND;
- else
- status = HTTP_SERVICE_UNAVAILABLE;
+ status = HTTP_SERVICE_UNAVAILABLE;
goto cleanup;
}
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
index b835832d..a70a8758 100644
--- a/modules/proxy/proxy_util.c
+++ b/modules/proxy/proxy_util.c
@@ -29,6 +29,18 @@
#define apr_socket_create apr_socket_create_ex
#endif
+/*
+ * Opaque structure containing target server info when
+ * using a forward proxy.
+ * Up to now only used in combination with HTTP CONNECT.
+ */
+typedef struct {
+ int use_http_connect; /* Use SSL Tunneling via HTTP CONNECT */
+ const char *target_host; /* Target hostname */
+ apr_port_t target_port; /* Target port */
+ const char *proxy_auth; /* Proxy authorization */
+} forward_info;
+
/* Global balancer counter */
int PROXY_DECLARE_DATA proxy_lb_workers = 0;
static int lb_workers_limit = 0;
@@ -2085,6 +2097,34 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
if (proxyname) {
conn->hostname = apr_pstrdup(conn->pool, proxyname);
conn->port = proxyport;
+ /*
+ * If we have a forward proxy and the protocol is HTTPS,
+ * then we need to prepend a HTTP CONNECT request before
+ * sending our actual HTTPS requests.
+ * Save our real backend data for using it later during HTTP CONNECT.
+ */
+ if (conn->is_ssl) {
+ const char *proxy_auth;
+
+ forward_info *forward = apr_pcalloc(conn->pool, sizeof(forward_info));
+ conn->forward = forward;
+ forward->use_http_connect = 1;
+ forward->target_host = apr_pstrdup(conn->pool, uri->hostname);
+ forward->target_port = uri->port;
+ /* Do we want to pass Proxy-Authorization along?
+ * If we haven't used it, then YES
+ * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
+ * So let's make it configurable by env.
+ * The logic here is the same used in mod_proxy_http.
+ */
+ proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization");
+ if (proxy_auth != NULL &&
+ proxy_auth[0] != '\0' &&
+ r->user == NULL && /* we haven't yet authenticated */
+ apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
+ forward->proxy_auth = apr_pstrdup(conn->pool, proxy_auth);
+ }
+ }
}
else {
conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
@@ -2224,6 +2264,102 @@ static int is_socket_connected(apr_socket_t *sock)
}
#endif /* USE_ALTERNATE_IS_CONNECTED */
+
+/*
+ * Send a HTTP CONNECT request to a forward proxy.
+ * The proxy is given by "backend", the target server
+ * is contained in the "forward" member of "backend".
+ */
+static apr_status_t send_http_connect(proxy_conn_rec *backend,
+ server_rec *s)
+{
+ int status;
+ apr_size_t nbytes;
+ apr_size_t left;
+ int complete = 0;
+ char buffer[HUGE_STRING_LEN];
+ char drain_buffer[HUGE_STRING_LEN];
+ forward_info *forward = (forward_info *)backend->forward;
+ int len = 0;
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: CONNECT: sending the CONNECT request for %s:%d "
+ "to the remote proxy %pI (%s)",
+ forward->target_host, forward->target_port,
+ backend->addr, backend->hostname);
+ /* Create the CONNECT request */
+ nbytes = apr_snprintf(buffer, sizeof(buffer),
+ "CONNECT %s:%d HTTP/1.0" CRLF,
+ forward->target_host, forward->target_port);
+ /* Add proxy authorization from the initial request if necessary */
+ if (forward->proxy_auth != NULL) {
+ nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes,
+ "Proxy-Authorization: %s" CRLF,
+ forward->proxy_auth);
+ }
+ /* Set a reasonable agent and send everything */
+ nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes,
+ "Proxy-agent: %s" CRLF CRLF,
+ ap_get_server_banner());
+ apr_socket_send(backend->sock, buffer, &nbytes);
+
+ /* Receive the whole CONNECT response */
+ left = sizeof(buffer) - 1;
+ /* Read until we find the end of the headers or run out of buffer */
+ do {
+ nbytes = left;
+ status = apr_socket_recv(backend->sock, buffer + len, &nbytes);
+ len += nbytes;
+ left -= nbytes;
+ buffer[len] = '\0';
+ if (strstr(buffer + len - nbytes, "\r\n\r\n") != NULL) {
+ complete = 1;
+ break;
+ }
+ } while (status == APR_SUCCESS && left > 0);
+ /* Drain what's left */
+ if (!complete) {
+ nbytes = sizeof(drain_buffer) - 1;
+ while (status == APR_SUCCESS && nbytes) {
+ status = apr_socket_recv(backend->sock, drain_buffer, &nbytes);
+ buffer[nbytes] = '\0';
+ nbytes = sizeof(drain_buffer) - 1;
+ if (strstr(drain_buffer, "\r\n\r\n") != NULL) {
+ complete = 1;
+ break;
+ }
+ }
+ }
+
+ /* Check for HTTP_OK response status */
+ if (status == APR_SUCCESS) {
+ int major, minor;
+ /* Only scan for three character status code */
+ char code_str[4];
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "send_http_connect: response from the forward proxy: %s",
+ buffer);
+
+ /* Extract the returned code */
+ if (sscanf(buffer, "HTTP/%u.%u %3s", &major, &minor, code_str) == 3) {
+ status = atoi(code_str);
+ if (status == HTTP_OK) {
+ status = APR_SUCCESS;
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+ "send_http_connect: the forward proxy returned code is '%s'",
+ code_str);
+ status = APR_INCOMPLETE;
+ }
+ }
+ }
+
+ return(status);
+}
+
+
PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
proxy_conn_rec *conn,
proxy_worker *worker,
@@ -2336,7 +2472,33 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
apr_socket_timeout_set(newsock, s->timeout);
}
- conn->sock = newsock;
+ conn->sock = newsock;
+
+ if (conn->forward) {
+ forward_info *forward = (forward_info *)conn->forward;
+ /*
+ * For HTTP CONNECT we need to prepend CONNECT request before
+ * sending our actual HTTPS requests.
+ */
+ if (forward->use_http_connect) {
+ rv = send_http_connect(conn, s);
+ /* If an error occurred, loop round and try again */
+ if (rv != APR_SUCCESS) {
+ conn->sock = NULL;
+ apr_socket_close(newsock);
+ loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
+ ap_log_error(APLOG_MARK, loglevel, rv, s,
+ "proxy: %s: attempt to connect to %s:%d "
+ "via http CONNECT through %pI (%s) failed",
+ proxy_function,
+ forward->target_host, forward->target_port,
+ backend_addr, worker->hostname);
+ backend_addr = backend_addr->next;
+ continue;
+ }
+ }
+ }
+
connected = 1;
}
/*
@@ -2516,4 +2678,3 @@ ap_proxy_buckets_lifetime_transform(request_rec *r, apr_bucket_brigade *from,
}
return rv;
}
-
diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
index c8600e9d..37c13731 100644
--- a/modules/ssl/mod_ssl.c
+++ b/modules/ssl/mod_ssl.c
@@ -143,6 +143,8 @@ static const command_rec ssl_config_cmds[] = {
"(`[+-][SSLv2|SSLv3|TLSv1] ...' - see manual)")
SSL_CMD_SRV(HonorCipherOrder, FLAG,
"Use the server's cipher ordering preference")
+ SSL_CMD_SRV(InsecureRenegotiation, FLAG,
+ "Enable support for insecure renegotiation")
SSL_CMD_ALL(UserName, TAKE1,
"Set user name to SSL variable value")
SSL_CMD_SRV(StrictSNIVHostCheck, FLAG,
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
index bda6fe56..e983f1e4 100644
--- a/modules/ssl/ssl_engine_config.c
+++ b/modules/ssl/ssl_engine_config.c
@@ -169,6 +169,7 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p)
sc->vhost_id_len = 0; /* set during module init */
sc->session_cache_timeout = UNSET;
sc->cipher_server_pref = UNSET;
+ sc->insecure_reneg = UNSET;
sc->proxy_ssl_check_peer_expire = SSL_ENABLED_UNSET;
sc->proxy_ssl_check_peer_cn = SSL_ENABLED_UNSET;
#ifndef OPENSSL_NO_TLSEXT
@@ -262,6 +263,7 @@ void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv)
cfgMergeBool(proxy_enabled);
cfgMergeInt(session_cache_timeout);
cfgMergeBool(cipher_server_pref);
+ cfgMergeBool(insecure_reneg);
cfgMerge(proxy_ssl_check_peer_expire, SSL_ENABLED_UNSET);
cfgMerge(proxy_ssl_check_peer_cn, SSL_ENABLED_UNSET);
#ifndef OPENSSL_NO_TLSEXT
@@ -688,6 +690,19 @@ const char *ssl_cmd_SSLHonorCipherOrder(cmd_parms *cmd, void *dcfg, int flag)
#endif
}
+const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int flag)
+{
+#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+ sc->insecure_reneg = flag?TRUE:FALSE;
+ return NULL;
+#else
+ return "The SSLInsecureRenegotiation directive is not available "
+ "with this SSL library";
+#endif
+}
+
+
static const char *ssl_cmd_check_dir(cmd_parms *parms,
const char **dir)
{
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
index 259254a9..d4f9171d 100644
--- a/modules/ssl/ssl_engine_init.c
+++ b/modules/ssl/ssl_engine_init.c
@@ -394,6 +394,7 @@ static void ssl_init_ctx_protocol(server_rec *s,
MODSSL_SSL_METHOD_CONST SSL_METHOD *method = NULL;
char *cp;
int protocol = mctx->protocol;
+ SSLSrvConfigRec *sc = mySrvConfig(s);
/*
* Create the new per-server SSL context
@@ -444,11 +445,14 @@ static void ssl_init_ctx_protocol(server_rec *s,
}
#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
- {
- SSLSrvConfigRec *sc = mySrvConfig(s);
- if (sc->cipher_server_pref == TRUE) {
- SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
- }
+ if (sc->cipher_server_pref == TRUE) {
+ SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+#endif
+
+#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+ if (sc->insecure_reneg == TRUE) {
+ SSL_CTX_set_options(ctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
}
#endif
@@ -501,10 +505,7 @@ static void ssl_init_ctx_callbacks(server_rec *s,
SSL_CTX_set_tmp_rsa_callback(ctx, ssl_callback_TmpRSA);
SSL_CTX_set_tmp_dh_callback(ctx, ssl_callback_TmpDH);
- if (s->loglevel >= APLOG_DEBUG) {
- /* this callback only logs if LogLevel >= info */
- SSL_CTX_set_info_callback(ctx, ssl_callback_LogTracingState);
- }
+ SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
}
static void ssl_init_ctx_verify(server_rec *s,
diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c
index 6e58c6b3..d26a0c2b 100644
--- a/modules/ssl/ssl_engine_io.c
+++ b/modules/ssl/ssl_engine_io.c
@@ -103,6 +103,7 @@ typedef struct {
ap_filter_t *pInputFilter;
ap_filter_t *pOutputFilter;
int nobuffer; /* non-zero to prevent buffering */
+ SSLConnRec *config;
} ssl_filter_ctx_t;
typedef struct {
@@ -193,7 +194,13 @@ static int bio_filter_out_read(BIO *bio, char *out, int outl)
static int bio_filter_out_write(BIO *bio, const char *in, int inl)
{
bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio->ptr);
-
+
+ /* Abort early if the client has initiated a renegotiation. */
+ if (outctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
+ outctx->rc = APR_ECONNABORTED;
+ return -1;
+ }
+
/* when handshaking we'll have a small number of bytes.
* max size SSL will pass us here is about 16k.
* (16413 bytes to be exact)
@@ -458,7 +465,6 @@ static int bio_filter_in_read(BIO *bio, char *in, int inlen)
apr_size_t inl = inlen;
bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)(bio->ptr);
apr_read_type_e block = inctx->block;
- SSLConnRec *sslconn = myConnConfig(inctx->f->c);
inctx->rc = APR_SUCCESS;
@@ -466,17 +472,25 @@ static int bio_filter_in_read(BIO *bio, char *in, int inlen)
if (!in)
return 0;
- /* XXX: flush here only required for SSLv2;
- * OpenSSL calls BIO_flush() at the appropriate times for
- * the other protocols.
+ /* Abort early if the client has initiated a renegotiation. */
+ if (inctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
+ inctx->rc = APR_ECONNABORTED;
+ return -1;
+ }
+
+ /* In theory, OpenSSL should flush as necessary, but it is known
+ * not to do so correctly in some cases; see PR 46952.
+ *
+ * Historically, this flush call was performed only for an SSLv2
+ * connection or for a proxy connection. Calling _out_flush
+ * should be very cheap in cases where it is unnecessary (and no
+ * output is buffered) so the performance impact of doing it
+ * unconditionally should be minimal.
*/
- if ((SSL_version(inctx->ssl) == SSL2_VERSION) || sslconn->is_proxy) {
- if (bio_filter_out_flush(inctx->bio_out) < 0) {
- bio_filter_out_ctx_t *outctx =
- (bio_filter_out_ctx_t *)(inctx->bio_out->ptr);
- inctx->rc = outctx->rc;
- return -1;
- }
+ if (bio_filter_out_flush(inctx->bio_out) < 0) {
+ bio_filter_out_ctx_t *outctx = inctx->bio_out->ptr;
+ inctx->rc = outctx->rc;
+ return -1;
}
BIO_clear_retry_flags(bio);
@@ -1358,9 +1372,17 @@ static apr_status_t ssl_io_filter_input(ap_filter_t *f,
}
else {
/* We have no idea what you are talking about, so return an error. */
- return APR_ENOTIMPL;
+ status = APR_ENOTIMPL;
}
+ /* It is possible for mod_ssl's BIO to be used outside of the
+ * direct control of mod_ssl's input or output filter -- notably,
+ * when mod_ssl initiates a renegotiation. Switching the BIO mode
+ * back to "blocking" here ensures such operations don't fail with
+ * SSL_ERROR_WANT_READ. */
+ inctx->block = APR_BLOCK_READ;
+
+ /* Handle custom errors. */
if (status != APR_SUCCESS) {
return ssl_io_filter_error(f, bb, status);
}
@@ -1665,7 +1687,7 @@ static apr_status_t ssl_io_filter_buffer(ap_filter_t *f,
}
else {
/* Split a line into the passed-in brigade. */
- rv = apr_brigade_split_line(bb, ctx->bb, mode, bytes);
+ rv = apr_brigade_split_line(bb, ctx->bb, block, bytes);
if (rv) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c,
@@ -1724,6 +1746,8 @@ void ssl_io_filter_init(conn_rec *c, SSL *ssl)
filter_ctx = apr_palloc(c->pool, sizeof(ssl_filter_ctx_t));
+ filter_ctx->config = myConnConfig(c);
+
filter_ctx->nobuffer = 0;
filter_ctx->pOutputFilter = ap_add_output_filter(ssl_io_filter,
filter_ctx, NULL, c);
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
index 15186209..33f97bd9 100644
--- a/modules/ssl/ssl_engine_kernel.c
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -35,6 +35,29 @@ static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn);
static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s);
#endif
+/* Perform a speculative (and non-blocking) read from the connection
+ * filters for the given request, to determine whether there is any
+ * pending data to read. Return non-zero if there is, else zero. */
+static int has_buffered_data(request_rec *r)
+{
+ apr_bucket_brigade *bb;
+ apr_off_t len;
+ apr_status_t rv;
+ int result;
+
+ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
+ rv = ap_get_brigade(r->connection->input_filters, bb, AP_MODE_SPECULATIVE,
+ APR_NONBLOCK_READ, 1);
+ result = rv == APR_SUCCESS
+ && apr_brigade_length(bb, 1, &len) == APR_SUCCESS
+ && len > 0;
+
+ apr_brigade_destroy(bb);
+
+ return result;
+}
+
/*
* Post Read Request Handler
*/
@@ -720,21 +743,50 @@ int ssl_hook_Access(request_rec *r)
else {
request_rec *id = r->main ? r->main : r;
- /* do a full renegotiation */
+ /* Additional mitigation for CVE-2009-3555: At this point,
+ * before renegotiating, an (entire) request has been read
+ * from the connection. An attacker may have sent further
+ * data to "prefix" any subsequent request by the victim's
+ * client after the renegotiation; this data may already
+ * have been read and buffered. Forcing a connection
+ * closure after the response ensures such data will be
+ * discarded. Legimately pipelined HTTP requests will be
+ * retried anyway with this approach. */
+ if (has_buffered_data(r)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "insecure SSL re-negotiation required, but "
+ "a pipelined request is present; keepalive "
+ "disabled");
+ r->connection->keepalive = AP_CONN_CLOSE;
+ }
+
+ /* Perform a full renegotiation. */
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
- "Performing full renegotiation: "
- "complete handshake protocol");
+ "Performing full renegotiation: complete handshake "
+ "protocol (%s support secure renegotiation)",
+#if defined(SSL_get_secure_renegotiation_support)
+ SSL_get_secure_renegotiation_support(ssl) ?
+ "client does" : "client does not"
+#else
+ "server does not"
+#endif
+ );
SSL_set_session_id_context(ssl,
(unsigned char *)&id,
sizeof(id));
+ /* Toggle the renegotiation state to allow the new
+ * handshake to proceed. */
+ sslconn->reneg_state = RENEG_ALLOW;
+
SSL_renegotiate(ssl);
SSL_do_handshake(ssl);
if (SSL_get_state(ssl) != SSL_ST_OK) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Re-negotiation request failed");
+ ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, r->server);
r->connection->aborted = 1;
return HTTP_FORBIDDEN;
@@ -750,6 +802,8 @@ int ssl_hook_Access(request_rec *r)
SSL_set_state(ssl, SSL_ST_ACCEPT);
SSL_do_handshake(ssl);
+ sslconn->reneg_state = RENEG_REJECT;
+
if (SSL_get_state(ssl) != SSL_ST_OK) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"Re-negotiation handshake failed: "
@@ -1021,6 +1075,7 @@ static const char *ssl_hook_Fixup_vars[] = {
"SSL_VERSION_INTERFACE",
"SSL_VERSION_LIBRARY",
"SSL_PROTOCOL",
+ "SSL_SECURE_RENEG",
"SSL_COMPRESS_METHOD",
"SSL_CIPHER",
"SSL_CIPHER_EXPORT",
@@ -1033,33 +1088,7 @@ static const char *ssl_hook_Fixup_vars[] = {
"SSL_CLIENT_V_END",
"SSL_CLIENT_V_REMAIN",
"SSL_CLIENT_S_DN",
- "SSL_CLIENT_S_DN_C",
- "SSL_CLIENT_S_DN_ST",
- "SSL_CLIENT_S_DN_L",
- "SSL_CLIENT_S_DN_O",
- "SSL_CLIENT_S_DN_OU",
- "SSL_CLIENT_S_DN_CN",
- "SSL_CLIENT_S_DN_T",
- "SSL_CLIENT_S_DN_I",
- "SSL_CLIENT_S_DN_G",
- "SSL_CLIENT_S_DN_S",
- "SSL_CLIENT_S_DN_D",
- "SSL_CLIENT_S_DN_UID",
- "SSL_CLIENT_S_DN_Email",
"SSL_CLIENT_I_DN",
- "SSL_CLIENT_I_DN_C",
- "SSL_CLIENT_I_DN_ST",
- "SSL_CLIENT_I_DN_L",
- "SSL_CLIENT_I_DN_O",
- "SSL_CLIENT_I_DN_OU",
- "SSL_CLIENT_I_DN_CN",
- "SSL_CLIENT_I_DN_T",
- "SSL_CLIENT_I_DN_I",
- "SSL_CLIENT_I_DN_G",
- "SSL_CLIENT_I_DN_S",
- "SSL_CLIENT_I_DN_D",
- "SSL_CLIENT_I_DN_UID",
- "SSL_CLIENT_I_DN_Email",
"SSL_CLIENT_A_KEY",
"SSL_CLIENT_A_SIG",
"SSL_SERVER_M_VERSION",
@@ -1067,33 +1096,7 @@ static const char *ssl_hook_Fixup_vars[] = {
"SSL_SERVER_V_START",
"SSL_SERVER_V_END",
"SSL_SERVER_S_DN",
- "SSL_SERVER_S_DN_C",
- "SSL_SERVER_S_DN_ST",
- "SSL_SERVER_S_DN_L",
- "SSL_SERVER_S_DN_O",
- "SSL_SERVER_S_DN_OU",
- "SSL_SERVER_S_DN_CN",
- "SSL_SERVER_S_DN_T",
- "SSL_SERVER_S_DN_I",
- "SSL_SERVER_S_DN_G",
- "SSL_SERVER_S_DN_S",
- "SSL_SERVER_S_DN_D",
- "SSL_SERVER_S_DN_UID",
- "SSL_SERVER_S_DN_Email",
"SSL_SERVER_I_DN",
- "SSL_SERVER_I_DN_C",
- "SSL_SERVER_I_DN_ST",
- "SSL_SERVER_I_DN_L",
- "SSL_SERVER_I_DN_O",
- "SSL_SERVER_I_DN_OU",
- "SSL_SERVER_I_DN_CN",
- "SSL_SERVER_I_DN_T",
- "SSL_SERVER_I_DN_I",
- "SSL_SERVER_I_DN_G",
- "SSL_SERVER_I_DN_S",
- "SSL_SERVER_I_DN_D",
- "SSL_SERVER_I_DN_UID",
- "SSL_SERVER_I_DN_Email",
"SSL_SERVER_A_KEY",
"SSL_SERVER_A_SIG",
"SSL_SESSION_ID",
@@ -1140,6 +1143,8 @@ int ssl_hook_Fixup(request_rec *r)
/* standard SSL environment variables */
if (dc->nOptions & SSL_OPT_STDENVVARS) {
+ modssl_var_extract_dns(env, sslconn->ssl, r->pool);
+
for (i = 0; ssl_hook_Fixup_vars[i]; i++) {
var = (char *)ssl_hook_Fixup_vars[i];
val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
@@ -1175,6 +1180,12 @@ int ssl_hook_Fixup(request_rec *r)
}
}
+
+#ifdef SSL_get_secure_renegotiation_support
+ apr_table_setn(r->notes, "ssl-secure-reneg",
+ SSL_get_secure_renegotiation_support(ssl) ? "1" : "0");
+#endif
+
return DECLINED;
}
@@ -1844,76 +1855,55 @@ void ssl_callback_DelSessionCacheEntry(SSL_CTX *ctx,
return;
}
-/*
- * This callback function is executed while OpenSSL processes the
- * SSL handshake and does SSL record layer stuff. We use it to
- * trace OpenSSL's processing in out SSL logfile.
- */
-void ssl_callback_LogTracingState(MODSSL_INFO_CB_ARG_TYPE ssl, int where, int rc)
+/* Dump debugginfo trace to the log file. */
+static void log_tracing_state(MODSSL_INFO_CB_ARG_TYPE ssl, conn_rec *c,
+ server_rec *s, int where, int rc)
{
- conn_rec *c;
- server_rec *s;
- SSLSrvConfigRec *sc;
-
/*
- * find corresponding server
+ * create the various trace messages
*/
- if (!(c = (conn_rec *)SSL_get_app_data((SSL *)ssl))) {
- return;
+ if (where & SSL_CB_HANDSHAKE_START) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "%s: Handshake: start", SSL_LIBRARY_NAME);
}
-
- s = mySrvFromConn(c);
- if (!(sc = mySrvConfig(s))) {
- return;
+ else if (where & SSL_CB_HANDSHAKE_DONE) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "%s: Handshake: done", SSL_LIBRARY_NAME);
}
-
- /*
- * create the various trace messages
- */
- if (s->loglevel >= APLOG_DEBUG) {
- if (where & SSL_CB_HANDSHAKE_START) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
- "%s: Handshake: start", SSL_LIBRARY_NAME);
- }
- else if (where & SSL_CB_HANDSHAKE_DONE) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
- "%s: Handshake: done", SSL_LIBRARY_NAME);
- }
- else if (where & SSL_CB_LOOP) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
- "%s: Loop: %s",
- SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
- }
- else if (where & SSL_CB_READ) {
+ else if (where & SSL_CB_LOOP) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "%s: Loop: %s",
+ SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ }
+ else if (where & SSL_CB_READ) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "%s: Read: %s",
+ SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ }
+ else if (where & SSL_CB_WRITE) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "%s: Write: %s",
+ SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
+ }
+ else if (where & SSL_CB_ALERT) {
+ char *str = (where & SSL_CB_READ) ? "read" : "write";
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "%s: Alert: %s:%s:%s",
+ SSL_LIBRARY_NAME, str,
+ SSL_alert_type_string_long(rc),
+ SSL_alert_desc_string_long(rc));
+ }
+ else if (where & SSL_CB_EXIT) {
+ if (rc == 0) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
- "%s: Read: %s",
+ "%s: Exit: failed in %s",
SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
}
- else if (where & SSL_CB_WRITE) {
+ else if (rc < 0) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
- "%s: Write: %s",
+ "%s: Exit: error in %s",
SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
}
- else if (where & SSL_CB_ALERT) {
- char *str = (where & SSL_CB_READ) ? "read" : "write";
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
- "%s: Alert: %s:%s:%s",
- SSL_LIBRARY_NAME, str,
- SSL_alert_type_string_long(rc),
- SSL_alert_desc_string_long(rc));
- }
- else if (where & SSL_CB_EXIT) {
- if (rc == 0) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
- "%s: Exit: failed in %s",
- SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
- }
- else if (rc < 0) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
- "%s: Exit: error in %s",
- SSL_LIBRARY_NAME, SSL_state_string_long(ssl));
- }
- }
}
/*
@@ -1933,6 +1923,52 @@ void ssl_callback_LogTracingState(MODSSL_INFO_CB_ARG_TYPE ssl, int where, int rc
}
}
+/*
+ * This callback function is executed while OpenSSL processes the SSL
+ * handshake and does SSL record layer stuff. It's used to trap
+ * client-initiated renegotiations, and for dumping everything to the
+ * log.
+ */
+void ssl_callback_Info(MODSSL_INFO_CB_ARG_TYPE ssl, int where, int rc)
+{
+ conn_rec *c;
+ server_rec *s;
+ SSLConnRec *scr;
+
+ /* Retrieve the conn_rec and the associated SSLConnRec. */
+ if ((c = (conn_rec *)SSL_get_app_data((SSL *)ssl)) == NULL) {
+ return;
+ }
+
+ if ((scr = myConnConfig(c)) == NULL) {
+ return;
+ }
+
+ /* If the reneg state is to reject renegotiations, check the SSL
+ * state machine and move to ABORT if a Client Hello is being
+ * read. */
+ if ((where & SSL_CB_ACCEPT_LOOP) && scr->reneg_state == RENEG_REJECT) {
+ int state = SSL_get_state(ssl);
+
+ if (state == SSL3_ST_SR_CLNT_HELLO_A
+ || state == SSL23_ST_SR_CLNT_HELLO_A) {
+ scr->reneg_state = RENEG_ABORT;
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
+ "rejecting client initiated renegotiation");
+ }
+ }
+ /* If the first handshake is complete, change state to reject any
+ * subsequent client-initated renegotiation. */
+ else if ((where & SSL_CB_HANDSHAKE_DONE) && scr->reneg_state == RENEG_INIT) {
+ scr->reneg_state = RENEG_REJECT;
+ }
+
+ s = mySrvFromConn(c);
+ if (s && s->loglevel >= APLOG_DEBUG) {
+ log_tracing_state(ssl, c, s, where, rc);
+ }
+}
+
#ifndef OPENSSL_NO_TLSEXT
/*
* This callback function is executed when OpenSSL encounters an extended
diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c
index ab99af9e..83cff2c1 100644
--- a/modules/ssl/ssl_engine_vars.c
+++ b/modules/ssl/ssl_engine_vars.c
@@ -326,6 +326,14 @@ static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, char *var)
TLSEXT_NAMETYPE_host_name));
}
#endif
+ else if (ssl != NULL && strcEQ(var, "SECURE_RENEG")) {
+ int flag = 0;
+#ifdef SSL_get_secure_renegotiation_support
+ flag = SSL_get_secure_renegotiation_support(ssl);
+#endif
+ result = apr_pstrdup(p, flag ? "true" : "false");
+ }
+
return result;
}
@@ -402,27 +410,31 @@ static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var)
return result;
}
+/* In this table, .extract is non-zero if RDNs using the NID should be
+ * extracted to for the SSL_{CLIENT,SERVER}_{I,S}_DN_* environment
+ * variables. */
static const struct {
char *name;
int nid;
+ int extract;
} ssl_var_lookup_ssl_cert_dn_rec[] = {
- { "C", NID_countryName },
- { "ST", NID_stateOrProvinceName }, /* officially (RFC2156) */
- { "SP", NID_stateOrProvinceName }, /* compatibility (SSLeay) */
- { "L", NID_localityName },
- { "O", NID_organizationName },
- { "OU", NID_organizationalUnitName },
- { "CN", NID_commonName },
- { "T", NID_title },
- { "I", NID_initials },
- { "G", NID_givenName },
- { "S", NID_surname },
- { "D", NID_description },
+ { "C", NID_countryName, 1 },
+ { "ST", NID_stateOrProvinceName, 1 }, /* officially (RFC2156) */
+ { "SP", NID_stateOrProvinceName, 0 }, /* compatibility (SSLeay) */
+ { "L", NID_localityName, 1 },
+ { "O", NID_organizationName, 1 },
+ { "OU", NID_organizationalUnitName, 1 },
+ { "CN", NID_commonName, 1 },
+ { "T", NID_title, 1 },
+ { "I", NID_initials, 1 },
+ { "G", NID_givenName, 1 },
+ { "S", NID_surname, 1 },
+ { "D", NID_description, 1 },
#ifdef NID_userId
- { "UID", NID_userId },
+ { "UID", NID_x500UniqueIdentifier, 1 },
#endif
- { "Email", NID_pkcs9_emailAddress },
- { NULL, 0 }
+ { "Email", NID_pkcs9_emailAddress, 1 },
+ { NULL, 0, 0 }
};
static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var)
@@ -626,7 +638,7 @@ static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var)
ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize);
if (ssl && strEQ(var, "")) {
- const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
+ MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
result = (cipher != NULL ? (char *)SSL_CIPHER_get_name(cipher) : NULL);
}
else if (strcEQ(var, "_EXPORT"))
@@ -647,7 +659,7 @@ static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var)
static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize)
{
- const SSL_CIPHER *cipher;
+ MODSSL_SSL_CIPHER_CONST SSL_CIPHER *cipher;
*usekeysize = 0;
*algkeysize = 0;
@@ -671,6 +683,96 @@ static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
return NULL;
}
+/* Add each RDN in 'xn' to the table 't' where the NID is present in
+ * 'nids', using key prefix 'pfx'. */
+static void extract_dn(apr_table_t *t, apr_hash_t *nids, const char *pfx,
+ X509_NAME *xn, apr_pool_t *p)
+{
+ STACK_OF(X509_NAME_ENTRY) *ents = X509_NAME_get_entries(xn);
+ X509_NAME_ENTRY *xsne;
+ apr_hash_t *count;
+ int i, nid;
+
+ /* Hash of (int) NID -> (int *) counter to count each time an RDN
+ * with the given NID has been seen. */
+ count = apr_hash_make(p);
+
+ /* For each RDN... */
+ for (i = 0; i < sk_X509_NAME_ENTRY_num(ents); i++) {
+ const char *tag;
+
+ xsne = sk_X509_NAME_ENTRY_value(ents, i);
+
+ /* Retrieve the nid, and check whether this is one of the nids
+ * which are to be extracted. */
+ nid = OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
+
+ tag = apr_hash_get(nids, &nid, sizeof nid);
+ if (tag) {
+ unsigned char *data = X509_NAME_ENTRY_get_data_ptr(xsne);
+ const char *key;
+ int *dup;
+ char *value;
+
+ /* Check whether a variable with this nid was already
+ * been used; if so, use the foo_N=bar syntax. */
+ dup = apr_hash_get(count, &nid, sizeof nid);
+ if (dup) {
+ key = apr_psprintf(p, "%s%s_%d", pfx, tag, ++(*dup));
+ }
+ else {
+ /* Otherwise, use the plain foo=bar syntax. */
+ dup = apr_pcalloc(p, sizeof *dup);
+ apr_hash_set(count, &nid, sizeof nid, dup);
+ key = apr_pstrcat(p, pfx, tag, NULL);
+ }
+
+ /* cast needed from 'unsigned char *' to 'char *' */
+ value = apr_pstrmemdup(p, (char *)data,
+ X509_NAME_ENTRY_get_data_len(xsne));
+#if APR_CHARSET_EBCDIC
+ ap_xlate_proto_from_ascii(value, X509_NAME_ENTRY_get_data_len(xsne));
+#endif /* APR_CHARSET_EBCDIC */
+ apr_table_setn(t, key, value);
+ }
+ }
+}
+
+void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p)
+{
+ apr_hash_t *nids;
+ unsigned n;
+ X509 *xs;
+
+ /* Build up a hash table of (int *)NID->(char *)short-name for all
+ * the tags which are to be extracted: */
+ nids = apr_hash_make(p);
+ for (n = 0; ssl_var_lookup_ssl_cert_dn_rec[n].name; n++) {
+ if (ssl_var_lookup_ssl_cert_dn_rec[n].extract) {
+ apr_hash_set(nids, &ssl_var_lookup_ssl_cert_dn_rec[n].nid,
+ sizeof(ssl_var_lookup_ssl_cert_dn_rec[0].nid),
+ ssl_var_lookup_ssl_cert_dn_rec[n].name);
+ }
+ }
+
+ /* Extract the server cert DNS -- note that the refcount does NOT
+ * increase: */
+ xs = SSL_get_certificate(ssl);
+ if (xs) {
+ extract_dn(t, nids, "SSL_SERVER_S_DN_", X509_get_subject_name(xs), p);
+ extract_dn(t, nids, "SSL_SERVER_I_DN_", X509_get_issuer_name(xs), p);
+ }
+
+ /* Extract the client cert DNs -- note that the refcount DOES
+ * increase: */
+ xs = SSL_get_peer_certificate(ssl);
+ if (xs) {
+ extract_dn(t, nids, "SSL_CLIENT_S_DN_", X509_get_subject_name(xs), p);
+ extract_dn(t, nids, "SSL_CLIENT_I_DN_", X509_get_issuer_name(xs), p);
+ X509_free(xs);
+ }
+}
+
const char *ssl_ext_lookup(apr_pool_t *p, conn_rec *c, int peer,
const char *oidnum)
{
diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h
index 818ed983..0613f0d2 100644
--- a/modules/ssl/ssl_private.h
+++ b/modules/ssl/ssl_private.h
@@ -356,6 +356,20 @@ typedef struct {
int is_proxy;
int disabled;
int non_ssl_request;
+
+ /* Track the handshake/renegotiation state for the connection so
+ * that all client-initiated renegotiations can be rejected, as a
+ * partial fix for CVE-2009-3555. */
+ enum {
+ RENEG_INIT = 0, /* Before initial handshake */
+ RENEG_REJECT, /* After initial handshake; any client-initiated
+ * renegotiation should be rejected */
+ RENEG_ALLOW, /* A server-initated renegotiation is taking
+ * place (as dictated by configuration) */
+ RENEG_ABORT /* Renegotiation initiated by client, abort the
+ * connection */
+ } reneg_state;
+
server_rec *server;
} SSLConnRec;
@@ -457,6 +471,7 @@ struct SSLSrvConfigRec {
int vhost_id_len;
int session_cache_timeout;
BOOL cipher_server_pref;
+ BOOL insecure_reneg;
modssl_ctx_t *server;
modssl_ctx_t *proxy;
ssl_enabled_t proxy_ssl_check_peer_expire;
@@ -531,6 +546,7 @@ const char *ssl_cmd_SSLRequire(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLUserName(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLRenegBufferSize(cmd_parms *cmd, void *dcfg, const char *arg);
const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int flag);
const char *ssl_cmd_SSLProxyEngine(cmd_parms *cmd, void *dcfg, int flag);
const char *ssl_cmd_SSLProxyProtocol(cmd_parms *, void *, const char *);
@@ -574,7 +590,7 @@ int ssl_callback_proxy_cert(SSL *ssl, MODSSL_CLIENT_CERT_CB_ARG_TYPE **
int ssl_callback_NewSessionCacheEntry(SSL *, SSL_SESSION *);
SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *, unsigned char *, int, int *);
void ssl_callback_DelSessionCacheEntry(SSL_CTX *, SSL_SESSION *);
-void ssl_callback_LogTracingState(MODSSL_INFO_CB_ARG_TYPE, int, int);
+void ssl_callback_Info(MODSSL_INFO_CB_ARG_TYPE, int, int);
#ifndef OPENSSL_NO_TLSEXT
int ssl_callback_ServerNameIndication(SSL *, int *, modssl_ctx_t *);
#endif
@@ -681,6 +697,10 @@ extern apr_array_header_t *ssl_extlist_by_oid(request_rec *r, const char *oidstr
void ssl_var_log_config_register(apr_pool_t *p);
+/* Extract SSL_*_DN_* variables into table 't' from SSL object 'ssl',
+ * allocating from 'p': */
+void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p);
+
#define APR_SHM_MAXSIZE (64 * 1024 * 1024)
#endif /* SSL_PRIVATE_H */
diff --git a/modules/ssl/ssl_toolkit_compat.h b/modules/ssl/ssl_toolkit_compat.h
index 06a22669..a841eccd 100644
--- a/modules/ssl/ssl_toolkit_compat.h
+++ b/modules/ssl/ssl_toolkit_compat.h
@@ -93,8 +93,10 @@
/** ...shifting sands of openssl... */
#if (OPENSSL_VERSION_NUMBER >= 0x0090707f)
# define MODSSL_D2I_SSL_SESSION_CONST const
+# define MODSSL_SSL_CIPHER_CONST const
#else
# define MODSSL_D2I_SSL_SESSION_CONST
+# define MODSSL_SSL_CIPHER_CONST
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x00908000)