diff options
Diffstat (limited to 'modules/proxy')
-rw-r--r-- | modules/proxy/mod_proxy.c | 34 | ||||
-rw-r--r-- | modules/proxy/mod_proxy.h | 8 | ||||
-rw-r--r-- | modules/proxy/proxy_util.c | 146 |
3 files changed, 129 insertions, 59 deletions
diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index c9cef7c..ad0c031 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -1245,6 +1245,7 @@ static int proxy_fixup(request_rec *r) return OK; /* otherwise; we've done the best we can */ } + /* Send a redirection if the request contains a hostname which is not */ /* fully qualified, i.e. doesn't have a domain name appended. Some proxy */ /* servers like Netscape's allow this and access hosts from the local */ @@ -1298,7 +1299,7 @@ static int proxy_handler(request_rec *r) ap_get_module_config(sconf, &proxy_module); apr_array_header_t *proxies = conf->proxies; struct proxy_remote *ents = (struct proxy_remote *) proxies->elts; - int i, rc, access_status; + int rc = DECLINED, access_status, i; int direct_connect = 0; const char *str; apr_int64_t maxfwd; @@ -1313,19 +1314,28 @@ static int proxy_handler(request_rec *r) return DECLINED; } - if (!r->proxyreq) { - /* We may have forced the proxy handler via config or .htaccess */ - if (r->handler && - strncmp(r->handler, "proxy:", 6) == 0 && - strncmp(r->filename, "proxy:", 6) != 0) { - r->proxyreq = PROXYREQ_REVERSE; - r->filename = apr_pstrcat(r->pool, r->handler, r->filename, NULL); + /* We may have forced the proxy handler via config or .htaccess */ + if (!r->proxyreq && r->handler && strncmp(r->handler, "proxy:", 6) == 0) { + char *old_filename = r->filename; + + r->proxyreq = PROXYREQ_REVERSE; + r->filename = apr_pstrcat(r->pool, r->handler, r->filename, NULL); + + /* Still need to fixup/canonicalize r->filename */ + rc = ap_proxy_fixup_uds_filename(r); + if (rc <= OK) { + rc = proxy_fixup(r); } - else { - return DECLINED; + if (rc != OK) { + r->filename = old_filename; + r->proxyreq = 0; } - } else if (strncmp(r->filename, "proxy:", 6) != 0) { - return DECLINED; + } + else if (r->proxyreq && strncmp(r->filename, "proxy:", 6) == 0) { + rc = OK; + } + if (rc != OK) { + return rc; } /* handle max-forwards / OPTIONS / TRACE */ diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 51a55f8..59572bf 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -1003,6 +1003,14 @@ PROXY_DECLARE(proxy_balancer_shared *) ap_proxy_find_balancershm(ap_slotmem_prov proxy_balancer *balancer, unsigned int *index); +/* + * Strip the UDS part of r->filename if any, and put the UDS path in + * r->notes ("uds_path") + * @param r current request + * @return OK if fixed up, DECLINED if not UDS, or an HTTP_XXX error + */ +PROXY_DECLARE(int) ap_proxy_fixup_uds_filename(request_rec *r); + /** * Get the most suitable worker and/or balancer for the request * @param worker worker used for processing request diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index a54a4fa..e71cbd8 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -1512,8 +1512,9 @@ static void socket_cleanup(proxy_conn_rec *conn) apr_pool_clear(conn->scpool); } -static void address_cleanup(proxy_conn_rec *conn) +static void conn_cleanup(proxy_conn_rec *conn) { + socket_cleanup(conn); conn->address = NULL; conn->addr = NULL; conn->hostname = NULL; @@ -1522,9 +1523,6 @@ static void address_cleanup(proxy_conn_rec *conn) if (conn->uds_pool) { apr_pool_clear(conn->uds_pool); } - if (conn->sock) { - socket_cleanup(conn); - } } static apr_status_t conn_pool_cleanup(void *theworker) @@ -2431,7 +2429,7 @@ static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worke * were passed a UDS url (eg: from mod_proxy) and adjust uds_path * as required. */ -static int fix_uds_filename(request_rec *r, char **url) +PROXY_DECLARE(int) ap_proxy_fixup_uds_filename(request_rec *r) { char *uds_url = r->filename + 6, *origin_url; @@ -2439,7 +2437,6 @@ static int fix_uds_filename(request_rec *r, char **url) !ap_cstr_casecmpn(uds_url, "unix:", 5) && (origin_url = ap_strchr(uds_url + 5, '|'))) { char *uds_path = NULL; - apr_size_t url_len; apr_uri_t urisock; apr_status_t rv; @@ -2454,20 +2451,20 @@ static int fix_uds_filename(request_rec *r, char **url) if (!uds_path) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10292) "Invalid proxy UDS filename (%s)", r->filename); - return 0; + return HTTP_BAD_REQUEST; } apr_table_setn(r->notes, "uds_path", uds_path); - /* Remove the UDS path from *url and r->filename */ - url_len = strlen(origin_url); - *url = apr_pstrmemdup(r->pool, origin_url, url_len); - memcpy(uds_url, *url, url_len + 1); - ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, - "*: rewrite of url due to UDS(%s): %s (%s)", - uds_path, *url, r->filename); + "*: fixup UDS from %s: %s (%s)", + r->filename, origin_url, uds_path); + + /* Overwrite the UDS part in place */ + memmove(uds_url, origin_url, strlen(origin_url) + 1); + return OK; } - return 1; + + return DECLINED; } PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, @@ -2486,9 +2483,6 @@ PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "%s: found worker %s for %s", (*worker)->s->scheme, (*worker)->s->name_ex, *url); - if (!forward && !fix_uds_filename(r, url)) { - return HTTP_INTERNAL_SERVER_ERROR; - } access_status = OK; } else if (forward) { @@ -2518,9 +2512,6 @@ PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, * regarding the Connection header in the request. */ apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1"); - if (!fix_uds_filename(r, url)) { - return HTTP_INTERNAL_SERVER_ERROR; - } } } } @@ -2530,6 +2521,20 @@ PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, "all workers are busy. Unable to serve %s", *url); access_status = HTTP_SERVICE_UNAVAILABLE; } + + if (access_status == OK && r->proxyreq == PROXYREQ_REVERSE) { + int rc = ap_proxy_fixup_uds_filename(r); + if (ap_is_HTTP_ERROR(rc)) { + return rc; + } + /* If the URL has changed in r->filename, take everything after + * the "proxy:" prefix. + */ + if (rc == OK) { + *url = apr_pstrdup(r->pool, r->filename + 6); + } + } + return access_status; } @@ -2784,8 +2789,8 @@ static apr_status_t worker_address_resolve(proxy_worker *worker, apr_sockaddr_t *addr = *paddr; for (; addr; addr = addr->next) { addrs = apr_psprintf(pool, "%s%s%pI", - addrs ? ", " : "", addrs ? addrs : "", + addrs ? ", " : "", addr); } if (r) { @@ -2959,15 +2964,16 @@ PROXY_DECLARE(apr_status_t) ap_proxy_determine_address(const char *proxy_functio */ address->expiry = apr_atomic_read32(&worker->s->address_expiry); if (address->expiry <= now) { - apr_uint32_t new_expiry = address->expiry + ttl; - while (new_expiry <= now) { - new_expiry += ttl; - } - new_expiry = apr_atomic_cas32(&worker->s->address_expiry, - new_expiry, address->expiry); - /* race lost? well the expiry should grow anyway.. */ - AP_DEBUG_ASSERT(new_expiry > now); - address->expiry = new_expiry; + apr_uint32_t prev, next = (now + ttl) - (now % ttl); + do { + prev = apr_atomic_cas32(&worker->s->address_expiry, + next, address->expiry); + if (prev == address->expiry) { + address->expiry = next; + break; + } + address->expiry = prev; + } while (prev <= now); } } else { @@ -3008,13 +3014,40 @@ PROXY_DECLARE(apr_status_t) ap_proxy_determine_address(const char *proxy_functio PROXY_THREAD_UNLOCK(worker); - /* Kill any socket using the old address */ - if (conn->sock) { - if (r ? APLOGrdebug(r) : APLOGdebug(s)) { - /* XXX: this requires the old conn->addr[ess] to still - * be alive since it's not copied by apr_socket_connect() - * in ap_proxy_connect_backend(). - */ + /* Release the old conn address */ + if (conn->address) { + /* On Windows and OS/2, apr_socket_connect() called from + * ap_proxy_connect_backend() does a simple pointer copy of + * its given conn->addr[->next] into conn->sock->remote_addr. + * Thus conn->addr cannot be freed if the conn->sock should be + * kept alive (same new and old addresses) and the old address + * is still in conn->sock->remote_addr. In this case we rather + * delay the release of the old address by moving the cleanup + * to conn->scpool such that it runs when the socket is closed. + * In any other case, including other platforms, just release + * the old address now since conn->sock->remote_addr is either + * obsolete (socket forcibly closed) or a copy on conn->scpool + * already (not a dangling pointer). + */ + int keep_addr_alive = 0, + keep_conn_alive = (conn->sock && conn->addr && + proxy_addrs_equal(conn->addr, + address->addr)); + if (keep_conn_alive) { +#if defined(WIN32) || defined(OS2) + apr_sockaddr_t *remote_addr = NULL; + apr_socket_addr_get(&remote_addr, APR_REMOTE, conn->sock); + for (addr = conn->addr; addr; addr = addr->next) { + if (addr == remote_addr) { + keep_addr_alive = 1; + break; + } + } +#else + /* Nothing to do, keep_addr_alive = 0 */ +#endif + } + else if (conn->sock && (r ? APLOGrdebug(r) : APLOGdebug(s))) { apr_sockaddr_t *local_addr = NULL; apr_sockaddr_t *remote_addr = NULL; apr_socket_addr_get(&local_addr, APR_LOCAL, conn->sock); @@ -3032,18 +3065,26 @@ PROXY_DECLARE(apr_status_t) ap_proxy_determine_address(const char *proxy_functio local_addr, remote_addr); } } - socket_cleanup(conn); + if (keep_addr_alive) { + apr_pool_cleanup_kill(conn->pool, conn->address, + proxy_address_cleanup); + apr_pool_cleanup_register(conn->scpool, conn->address, + proxy_address_cleanup, + apr_pool_cleanup_null); + } + else { + apr_pool_cleanup_run(conn->pool, conn->address, + proxy_address_cleanup); + if (!keep_conn_alive) { + conn_cleanup(conn); + } + } } - /* Kill the old address (if any) and use the new one */ - if (conn->address) { - apr_pool_cleanup_run(conn->pool, conn->address, - proxy_address_cleanup); - } + /* Use the new address */ apr_pool_cleanup_register(conn->pool, address, proxy_address_cleanup, apr_pool_cleanup_null); - address_cleanup(conn); conn->address = address; conn->hostname = address->hostname; conn->port = address->hostport; @@ -3079,6 +3120,13 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, apr_pstrcat(p,"URI cannot be parsed: ", *url, NULL)); } + + if (!uri->hostname) { + return ap_proxyerror(r, HTTP_BAD_REQUEST, + apr_pstrcat(p,"URI has no hostname: ", *url, + NULL)); + } + if (!uri->port) { uri->port = ap_proxy_port_of_scheme(uri->scheme); } @@ -3125,7 +3173,7 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, if (!conn->uds_path || strcmp(conn->uds_path, uds_path) != 0) { apr_pool_t *pool = conn->pool; if (conn->uds_path) { - address_cleanup(conn); + conn_cleanup(conn); if (!conn->uds_pool) { apr_pool_create(&conn->uds_pool, worker->cp->dns_pool); } @@ -3226,7 +3274,7 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, if (conn->hostname && (conn->port != hostport || ap_cstr_casecmp(conn->hostname, hostname) != 0)) { - address_cleanup(conn); + conn_cleanup(conn); } /* Resolve the connection address with the determined hostname/port */ @@ -4462,6 +4510,10 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p, /* Compute Host header */ if (dconf->preserve_host == 0) { + if (!uri->hostname) { + rc = HTTP_BAD_REQUEST; + goto cleanup; + } if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */ if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { host = apr_pstrcat(r->pool, "[", uri->hostname, "]:", |