From: Chuck Murcko Date: Fri, 18 Jan 2002 20:26:58 +0000 (+0000) Subject: The rest of the proxy http 1.1 switch X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4ec1ec243ecfb6f987b50108b1e147e5680c3cb2;p=thirdparty%2Fapache%2Fhttpd.git The rest of the proxy http 1.1 switch git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x@92911 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/modules/proxy/mod_proxy.c b/src/modules/proxy/mod_proxy.c index 5e44bcd4ffd..ae2497cd119 100644 --- a/src/modules/proxy/mod_proxy.c +++ b/src/modules/proxy/mod_proxy.c @@ -76,7 +76,7 @@ static struct proxy_services defports[] = {"wais", DEFAULT_WAIS_PORT}, {"snews", DEFAULT_SNEWS_PORT}, {"prospero", DEFAULT_PROSPERO_PORT}, - {NULL, -1} /* unknown port */ + {NULL, -1} /* unknown port */ }; /* @@ -98,29 +98,29 @@ static int alias_match(const char *uri, const char *alias_fakename) const char *aliasp = alias_fakename, *urip = uri; while (aliasp < end_fakename) { - if (*aliasp == '/') { - /* any number of '/' in the alias matches any number in - * the supplied URI, but there must be at least one... - */ - if (*urip != '/') - return 0; - - while (*aliasp == '/') - ++aliasp; - while (*urip == '/') - ++urip; - } - else { - /* Other characters are compared literally */ - if (*urip++ != *aliasp++) - return 0; - } + if (*aliasp == '/') { + /* any number of '/' in the alias matches any number in + * the supplied URI, but there must be at least one... + */ + if (*urip != '/') + return 0; + + while (*aliasp == '/') + ++aliasp; + while (*urip == '/') + ++urip; + } + else { + /* Other characters are compared literally */ + if (*urip++ != *aliasp++) + return 0; + } } /* Check last alias path component matched all the way */ if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') - return 0; + return 0; /* Return number of characters from URI which matched (may be * greater than length of alias, since we may have matched @@ -149,25 +149,25 @@ static int proxy_detect(request_rec *r) conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); if (conf->req && r->parsed_uri.scheme) { - /* but it might be something vhosted */ + /* but it might be something vhosted */ if (!(r->parsed_uri.hostname - && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r)) - && ap_matches_request_vhost(r, r->parsed_uri.hostname, + && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r)) + && ap_matches_request_vhost(r, r->parsed_uri.hostname, r->parsed_uri.port_str ? r->parsed_uri.port : ap_default_port(r)))) { - r->proxyreq = STD_PROXY; - r->uri = r->unparsed_uri; - r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL); - r->handler = "proxy-server"; + r->proxyreq = STD_PROXY; + r->uri = r->unparsed_uri; + r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL); + r->handler = "proxy-server"; } } /* We need special treatment for CONNECT proxying: it has no scheme part */ else if (conf->req && r->method_number == M_CONNECT - && r->parsed_uri.hostname - && r->parsed_uri.port_str) { - r->proxyreq = STD_PROXY; - r->uri = r->unparsed_uri; - r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL); - r->handler = "proxy-server"; + && r->parsed_uri.hostname + && r->parsed_uri.port_str) { + r->proxyreq = STD_PROXY; + r->uri = r->unparsed_uri; + r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL); + r->handler = "proxy-server"; } return DECLINED; } @@ -181,10 +181,10 @@ static int proxy_trans(request_rec *r) struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts; if (r->proxyreq != NOT_PROXY) { - /* someone has already set up the proxy, it was possibly ourselves - * in proxy_detect - */ - return OK; + /* someone has already set up the proxy, it was possibly ourselves + * in proxy_detect + */ + return OK; } /* XXX: since r->uri has been manipulated already we're not really @@ -194,14 +194,14 @@ static int proxy_trans(request_rec *r) for (i = 0; i < conf->aliases->nelts; i++) { len = alias_match(r->uri, ent[i].fake); - + if (len > 0) { r->filename = ap_pstrcat(r->pool, "proxy:", ent[i].real, r->uri + len, NULL); r->handler = "proxy-server"; r->proxyreq = PROXY_PASS; return OK; - } + } } return DECLINED; } @@ -217,21 +217,21 @@ static int proxy_fixup(request_rec *r) char *url, *p; if (r->proxyreq == NOT_PROXY || strncmp(r->filename, "proxy:", 6) != 0) - return DECLINED; + return DECLINED; url = &r->filename[6]; /* canonicalise each specific scheme */ if (strncasecmp(url, "http:", 5) == 0) - return ap_proxy_http_canon(r, url + 5, "http", DEFAULT_HTTP_PORT); + return ap_proxy_http_canon(r, url + 5, "http", DEFAULT_HTTP_PORT); else if (strncasecmp(url, "ftp:", 4) == 0) - return ap_proxy_ftp_canon(r, url + 4); + return ap_proxy_ftp_canon(r, url + 4); p = strchr(url, ':'); if (p == NULL || p == url) - return HTTP_BAD_REQUEST; + return HTTP_BAD_REQUEST; - return OK; /* otherwise; we've done the best we can */ + return OK; /* otherwise; we've done the best we can */ } static void proxy_init(server_rec *r, pool *p) @@ -254,29 +254,29 @@ static int proxy_needsdomain(request_rec *r, const char *url, const char *domain /* We only want to worry about GETs */ if (r->proxyreq == NOT_PROXY || r->method_number != M_GET || !r->parsed_uri.hostname) - return DECLINED; + return DECLINED; /* If host does contain a dot already, or it is "localhost", decline */ if (strchr(r->parsed_uri.hostname, '.') != NULL || strcasecmp(r->parsed_uri.hostname, "localhost") == 0) - return DECLINED; /* host name has a dot already */ + return DECLINED; /* host name has a dot already */ ref = ap_table_get(r->headers_in, "Referer"); /* Reassemble the request, but insert the domain after the host name */ /* Note that the domain name always starts with a dot */ r->parsed_uri.hostname = ap_pstrcat(r->pool, r->parsed_uri.hostname, - domain, NULL); + domain, NULL); nuri = ap_unparse_uri_components(r->pool, - &r->parsed_uri, - UNP_REVEALPASSWORD); + &r->parsed_uri, + UNP_REVEALPASSWORD); ap_table_set(r->headers_out, "Location", nuri); ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r, - "Domain missing: %s sent to %s%s%s", r->uri, - ap_unparse_uri_components(r->pool, &r->parsed_uri, - UNP_OMITUSERINFO), - ref ? " from " : "", ref ? ref : ""); + "Domain missing: %s sent to %s%s%s", r->uri, + ap_unparse_uri_components(r->pool, &r->parsed_uri, + UNP_OMITUSERINFO), + ref ? " from " : "", ref ? ref : ""); return HTTP_MOVED_PERMANENTLY; } @@ -298,41 +298,42 @@ static int proxy_handler(request_rec *r) const char *maxfwd_str; if (r->proxyreq == NOT_PROXY || strncmp(r->filename, "proxy:", 6) != 0) - return DECLINED; + return DECLINED; if (r->method_number == M_TRACE && - (maxfwd_str = ap_table_get(r->headers_in, "Max-Forwards")) != NULL) { - long maxfwd = strtol(maxfwd_str, NULL, 10); - if (maxfwd < 1) { - int access_status; - r->proxyreq = NOT_PROXY; - if ((access_status = ap_send_http_trace(r))) - ap_die(access_status, r); - else - ap_finalize_request_protocol(r); - return OK; - } - ap_table_setn(r->headers_in, "Max-Forwards", - ap_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd-1 : 0)); + (maxfwd_str = ap_table_get(r->headers_in, "Max-Forwards")) != NULL) { + long maxfwd = strtol(maxfwd_str, NULL, 10); + if (maxfwd < 1) { + int access_status; + r->proxyreq = NOT_PROXY; + if ((access_status = ap_send_http_trace(r))) + ap_die(access_status, r); + else + ap_finalize_request_protocol(r); + return OK; + } + ap_table_setn(r->headers_in, "Max-Forwards", + ap_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd-1 : 0)); } if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) - return rc; + return rc; url = r->filename + 6; p = strchr(url, ':'); if (p == NULL) - return HTTP_BAD_REQUEST; + return HTTP_BAD_REQUEST; + /* Try serve the request from the cache. If we suceed, we leave. */ rc = ap_proxy_cache_check(r, url, &conf->cache, &cr); if (rc != DECLINED) - return rc; + return rc; /* If the host doesn't have a domain name, add one and redirect. */ if (conf->domain != NULL) { - rc = proxy_needsdomain(r, url, conf->domain); - if (ap_is_HTTP_REDIRECT(rc)) - return HTTP_MOVED_PERMANENTLY; + rc = proxy_needsdomain(r, url, conf->domain); + if (ap_is_HTTP_REDIRECT(rc)) + return HTTP_MOVED_PERMANENTLY; } *p = '\0'; @@ -344,47 +345,47 @@ static int proxy_handler(request_rec *r) /* we only know how to handle communication to a proxy via http */ /*if (strcasecmp(scheme, "http") == 0) */ { - int ii; - struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts; + int ii; + struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts; - for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) { - direct_connect = list[ii].matcher(&list[ii], r); - } + for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) { + direct_connect = list[ii].matcher(&list[ii], r); + } #if DEBUGGING - ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r, - (direct_connect) ? "NoProxy for %s" : "UseProxy for %s", - r->uri); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r, + (direct_connect) ? "NoProxy for %s" : "UseProxy for %s", + r->uri); #endif } /* firstly, try a proxy, unless a NoProxy directive is active */ if (!direct_connect) - for (i = 0; i < proxies->nelts; i++) { - p = strchr(ents[i].scheme, ':'); /* is it a partial URL? */ - if (strcmp(ents[i].scheme, "*") == 0 || - (p == NULL && strcasecmp(scheme, ents[i].scheme) == 0) || - (p != NULL && - strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) { - /* CONNECT is a special method that bypasses the normal - * proxy code. - */ - if (r->method_number == M_CONNECT) - rc = ap_proxy_connect_handler(r, cr, url, ents[i].hostname, - ents[i].port); + for (i = 0; i < proxies->nelts; i++) { + p = strchr(ents[i].scheme, ':'); /* is it a partial URL? */ + if (strcmp(ents[i].scheme, "*") == 0 || + (p == NULL && strcasecmp(scheme, ents[i].scheme) == 0) || + (p != NULL && + strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) { + /* CONNECT is a special method that bypasses the normal + * proxy code. + */ + if (r->method_number == M_CONNECT) + rc = ap_proxy_connect_handler(r, cr, url, ents[i].hostname, + ents[i].port); /* we only know how to handle communication to a proxy via http */ - else if (strcasecmp(ents[i].protocol, "http") == 0) - rc = ap_proxy_http_handler(r, cr, url, ents[i].hostname, - ents[i].port); - else - rc = DECLINED; - - /* an error or success */ - if (rc != DECLINED && rc != HTTP_BAD_GATEWAY) - return rc; - /* we failed to talk to the upstream proxy */ - } - } + else if (strcasecmp(ents[i].protocol, "http") == 0) + rc = ap_proxy_http_handler(r, cr, url, ents[i].hostname, + ents[i].port); + else + rc = DECLINED; + + /* an error or success */ + if (rc != DECLINED && rc != HTTP_BAD_GATEWAY) + return rc; + /* we failed to talk to the upstream proxy */ + } + } /* otherwise, try it direct */ /* N.B. what if we're behind a firewall, where we must use a proxy or @@ -392,13 +393,13 @@ static int proxy_handler(request_rec *r) */ /* handle the scheme */ if (r->method_number == M_CONNECT) - return ap_proxy_connect_handler(r, cr, url, NULL, 0); + return ap_proxy_connect_handler(r, cr, url, NULL, 0); if (strcasecmp(scheme, "http") == 0) - return ap_proxy_http_handler(r, cr, url, NULL, 0); + return ap_proxy_http_handler(r, cr, url, NULL, 0); if (strcasecmp(scheme, "ftp") == 0) - return ap_proxy_ftp_handler(r, cr, url); + return ap_proxy_ftp_handler(r, cr, url); else - return HTTP_FORBIDDEN; + return HTTP_FORBIDDEN; } /* -------------------------------------------------------------- */ @@ -493,26 +494,26 @@ static const char * p = strchr(r, ':'); if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') - return "ProxyRemote: Bad syntax for a remote proxy server"; + return "ProxyRemote: Bad syntax for a remote proxy server"; q = strchr(p + 3, ':'); if (q != NULL) { - if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) - return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)"; - *q = '\0'; + if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) + return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)"; + *q = '\0'; } else - port = -1; + port = -1; *p = '\0'; if (strchr(f, ':') == NULL) - ap_str_tolower(f); /* lowercase scheme */ - ap_str_tolower(p + 3); /* lowercase hostname */ + ap_str_tolower(f); /* lowercase scheme */ + ap_str_tolower(p + 3); /* lowercase hostname */ if (port == -1) { - int i; - for (i = 0; defports[i].scheme != NULL; i++) - if (strcasecmp(defports[i].scheme, r) == 0) - break; - port = defports[i].port; + int i; + for (i = 0; defports[i].scheme != NULL; i++) + if (strcasecmp(defports[i].scheme, r) == 0) + break; + port = defports[i].port; } new = ap_push_array(conf->proxies); @@ -566,19 +567,19 @@ static const char * /* Don't duplicate entries */ for (i = 0; i < conf->noproxies->nelts; i++) { - if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */ - found = 1; + if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */ + found = 1; } if (!found) { - new = ap_push_array(conf->noproxies); - new->name = arg; - /* Don't do name lookups on things that aren't dotted */ - if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL) - /*@@@FIXME: This copies only the first of (possibly many) IP addrs */ - memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr)); - else - new->addr.s_addr = 0; + new = ap_push_array(conf->noproxies); + new->name = arg; + /* Don't do name lookups on things that aren't dotted */ + if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL) + /*@@@FIXME: This copies only the first of (possibly many) IP addrs */ + memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr)); + else + new->addr.s_addr = 0; } return NULL; } @@ -595,7 +596,7 @@ static const char * int *New; if (!ap_isdigit(arg[0])) - return "AllowCONNECT: port number must be numeric"; + return "AllowCONNECT: port number must be numeric"; New = ap_push_array(conf->allowed_connect_ports); *New = atoi(arg); @@ -618,39 +619,39 @@ static const char * /* Don't duplicate entries */ for (i = 0; i < conf->dirconn->nelts; i++) { - if (strcasecmp(arg, list[i].name) == 0) - found = 1; + if (strcasecmp(arg, list[i].name) == 0) + found = 1; } if (!found) { - New = ap_push_array(conf->dirconn); - New->name = arg; - New->hostentry = NULL; + New = ap_push_array(conf->dirconn); + New->name = arg; + New->hostentry = NULL; - if (ap_proxy_is_ipaddr(New, parms->pool)) { + if (ap_proxy_is_ipaddr(New, parms->pool)) { #if DEBUGGING - fprintf(stderr, "Parsed addr %s\n", inet_ntoa(New->addr)); - fprintf(stderr, "Parsed mask %s\n", inet_ntoa(New->mask)); + fprintf(stderr, "Parsed addr %s\n", inet_ntoa(New->addr)); + fprintf(stderr, "Parsed mask %s\n", inet_ntoa(New->mask)); #endif - } - else if (ap_proxy_is_domainname(New, parms->pool)) { - ap_str_tolower(New->name); + } + else if (ap_proxy_is_domainname(New, parms->pool)) { + ap_str_tolower(New->name); #if DEBUGGING - fprintf(stderr, "Parsed domain %s\n", New->name); + fprintf(stderr, "Parsed domain %s\n", New->name); #endif - } - else if (ap_proxy_is_hostname(New, parms->pool)) { - ap_str_tolower(New->name); + } + else if (ap_proxy_is_hostname(New, parms->pool)) { + ap_str_tolower(New->name); #if DEBUGGING - fprintf(stderr, "Parsed host %s\n", New->name); + fprintf(stderr, "Parsed host %s\n", New->name); #endif - } - else { - ap_proxy_is_word(New, parms->pool); + } + else { + ap_proxy_is_word(New, parms->pool); #if DEBUGGING - fprintf(stderr, "Parsed word %s\n", New->name); + fprintf(stderr, "Parsed word %s\n", New->name); #endif - } + } } return NULL; } @@ -662,7 +663,7 @@ static const char * ap_get_module_config(parms->server->module_config, &proxy_module); if (arg[0] != '.') - return "ProxyDomain: domain name must start with a dot."; + return "ProxyDomain: domain name must start with a dot."; psf->domain = arg; return NULL; @@ -688,7 +689,7 @@ static const char * int val; if (sscanf(arg, "%d", &val) != 1) - return "CacheSize value must be an integer (kBytes)"; + return "CacheSize value must be an integer (kBytes)"; psf->cache.space = val; psf->cache.space_set = 1; return NULL; @@ -713,7 +714,7 @@ static const char * double val; if (sscanf(arg, "%lg", &val) != 1) - return "CacheLastModifiedFactor value must be a float"; + return "CacheLastModifiedFactor value must be a float"; psf->cache.lmfactor = val; psf->cache.lmfactor_set = 1; @@ -728,7 +729,7 @@ static const char * double val; if (sscanf(arg, "%lg", &val) != 1) - return "CacheMaxExpire value must be a float"; + return "CacheMaxExpire value must be a float"; psf->cache.maxexpire = (int) (val * (double) SEC_ONE_HR); psf->cache.maxexpire_set = 1; return NULL; @@ -742,7 +743,7 @@ static const char * double val; if (sscanf(arg, "%lg", &val) != 1) - return "CacheDefaultExpire value must be a float"; + return "CacheDefaultExpire value must be a float"; psf->cache.defaultexpire = (int) (val * (double) SEC_ONE_HR); psf->cache.defaultexpire_set = 1; return NULL; @@ -756,7 +757,7 @@ static const char * double val; if (sscanf(arg, "%lg", &val) != 1) - return "CacheGcInterval value must be a float"; + return "CacheGcInterval value must be a float"; psf->cache.gcinterval = (int) (val * (double) SEC_ONE_HR); psf->cache.gcinterval_set = 1; return NULL; @@ -771,9 +772,9 @@ static const char * val = atoi(arg); if (val < 1) - return "CacheDirLevels value must be an integer greater than 0"; + return "CacheDirLevels value must be an integer greater than 0"; if (val * psf->cache.dirlength > CACHEFILE_LEN) - return "CacheDirLevels*CacheDirLength value must not be higher than 20"; + return "CacheDirLevels*CacheDirLength value must not be higher than 20"; psf->cache.dirlevels = val; psf->cache.dirlevels_set = 1; return NULL; @@ -788,9 +789,9 @@ static const char * val = atoi(arg); if (val < 1) - return "CacheDirLength value must be an integer greater than 0"; + return "CacheDirLength value must be an integer greater than 0"; if (val * psf->cache.dirlevels > CACHEFILE_LEN) - return "CacheDirLevels*CacheDirLength value must not be higher than 20"; + return "CacheDirLevels*CacheDirLength value must not be higher than 20"; psf->cache.dirlength = val; psf->cache.dirlength_set = 1; return NULL; @@ -810,19 +811,19 @@ static const char * /* Don't duplicate entries */ for (i = 0; i < conf->nocaches->nelts; i++) { - if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */ - found = 1; + if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */ + found = 1; } if (!found) { - new = ap_push_array(conf->nocaches); - new->name = arg; - /* Don't do name lookups on things that aren't dotted */ - if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL) - /*@@@FIXME: This copies only the first of (possibly many) IP addrs */ - memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr)); - else - new->addr.s_addr = 0; + new = ap_push_array(conf->nocaches); + new->name = arg; + /* Don't do name lookups on things that aren't dotted */ + if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL) + /*@@@FIXME: This copies only the first of (possibly many) IP addrs */ + memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr)); + else + new->addr.s_addr = 0; } return NULL; } @@ -834,7 +835,7 @@ static const char * ap_get_module_config(parms->server->module_config, &proxy_module); int s = atoi(arg); if (s < 512 && s != 0) { - return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default."; + return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default."; } psf->recv_buffer_size = s; @@ -849,7 +850,7 @@ static const char* ap_get_module_config(parms->server->module_config, &proxy_module); int s = atoi(arg); if (s > 100 || s < 0) { - return "CacheForceCompletion must be <= 100 percent, " + return "CacheForceCompletion must be <= 100 percent, " "or 0 for system default."; } @@ -875,7 +876,7 @@ static const char* else if (strcasecmp(arg, "Full") == 0) psf->viaopt = via_full; else { - return "ProxyVia must be one of: " + return "ProxyVia must be one of: " "off | on | full | block"; } @@ -937,24 +938,24 @@ static const command_rec proxy_cmds[] = module MODULE_VAR_EXPORT proxy_module = { STANDARD_MODULE_STUFF, - proxy_init, /* initializer */ - NULL, /* create per-directory config structure */ - NULL, /* merge per-directory config structures */ - create_proxy_config, /* create per-server config structure */ - merge_proxy_config, /* merge per-server config structures */ - proxy_cmds, /* command table */ - proxy_handlers, /* handlers */ - proxy_trans, /* translate_handler */ - NULL, /* check_user_id */ - NULL, /* check auth */ - NULL, /* check access */ - NULL, /* type_checker */ - proxy_fixup, /* pre-run fixups */ - NULL, /* logger */ - NULL, /* header parser */ - NULL, /* child_init */ - NULL, /* child_exit */ - proxy_detect /* post read-request */ + proxy_init, /* initializer */ + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + create_proxy_config, /* create per-server config structure */ + merge_proxy_config, /* merge per-server config structures */ + proxy_cmds, /* command table */ + proxy_handlers, /* handlers */ + proxy_trans, /* translate_handler */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + proxy_fixup, /* pre-run fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + proxy_detect /* post read-request */ }; diff --git a/src/modules/proxy/mod_proxy.h b/src/modules/proxy/mod_proxy.h index 3c45e0cecbb..3874191eb6c 100644 --- a/src/modules/proxy/mod_proxy.h +++ b/src/modules/proxy/mod_proxy.h @@ -70,39 +70,9 @@ If TESTING is set, then garbage collection doesn't delete ... probably a good idea when hacking. - This code is still experimental! - - Things to do: - - 1. Make it garbage collect in the background, not while someone is waiting for - a response! - - 2. Check the logic thoroughly. - - 3. Empty directories are only removed the next time round (but this does avoid - two passes). Consider doing them the first time round. - - Ben Laurie 30 Mar 96 - - More things to do: - - 0. Code cleanup (ongoing) - - 1. add 230 response output for ftp now that it works - - 2. Make the ftp proxy transparent, also same with (future) gopher & wais - - 3. Use protocol handler struct a la Apache module handlers (Dirk van Gulik) - - 4. Use a cache expiry database for more efficient GC (Jeremy Wohl) - - 5. Bulletproof GC against SIGALRM - - Chuck Murcko 15 April 1997 - */ -#define TESTING 0 +#define TESTING 0 #include "httpd.h" #include "http_config.h" @@ -118,26 +88,26 @@ enum enctype { enc_path, enc_search, enc_user, enc_fpath, enc_parm }; -#define HDR_APP (0) /* append header, for proxy_add_header() */ -#define HDR_REP (1) /* replace header, for proxy_add_header() */ +#define HDR_APP (0) /* append header, for proxy_add_header() */ +#define HDR_REP (1) /* replace header, for proxy_add_header() */ /* number of characters in the hash */ #define HASH_LEN (22*2) /* maximum 'CacheDirLevels*CacheDirLength' value */ -#define CACHEFILE_LEN 20 /* must be less than HASH_LEN/2 */ +#define CACHEFILE_LEN 20 /* must be less than HASH_LEN/2 */ -#define SEC_ONE_DAY 86400 /* one day, in seconds */ -#define SEC_ONE_HR 3600 /* one hour, in seconds */ +#define SEC_ONE_DAY 86400 /* one day, in seconds */ +#define SEC_ONE_HR 3600 /* one hour, in seconds */ -#define DEFAULT_FTP_DATA_PORT 20 -#define DEFAULT_FTP_PORT 21 -#define DEFAULT_GOPHER_PORT 70 -#define DEFAULT_NNTP_PORT 119 -#define DEFAULT_WAIS_PORT 210 -#define DEFAULT_HTTPS_PORT 443 -#define DEFAULT_SNEWS_PORT 563 -#define DEFAULT_PROSPERO_PORT 1525 /* WARNING: conflict w/Oracle */ +#define DEFAULT_FTP_DATA_PORT 20 +#define DEFAULT_FTP_PORT 21 +#define DEFAULT_GOPHER_PORT 70 +#define DEFAULT_NNTP_PORT 119 +#define DEFAULT_WAIS_PORT 210 +#define DEFAULT_HTTPS_PORT 443 +#define DEFAULT_SNEWS_PORT 563 +#define DEFAULT_PROSPERO_PORT 1525 /* WARNING: conflict w/Oracle */ /* Some WWW schemes and their default ports; this is basically /etc/services */ struct proxy_services { @@ -147,10 +117,10 @@ struct proxy_services { /* static information about a remote proxy */ struct proxy_remote { - const char *scheme; /* the schemes handled by this proxy, or '*' */ - const char *protocol; /* the scheme used to talk to this proxy */ - const char *hostname; /* the hostname of this proxy */ - int port; /* the port for this proxy */ + const char *scheme; /* the schemes handled by this proxy, or '*' */ + const char *protocol; /* the scheme used to talk to this proxy */ + const char *hostname; /* the hostname of this proxy */ + int port; /* the port for this proxy */ }; struct proxy_alias { @@ -182,29 +152,36 @@ struct nocache_entry { #define DEFAULT_CACHE_COMPLETION (0.9) #define DEFAULT_CACHE_GCINTERVAL SEC_ONE_HR +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + /* static information about the local cache */ struct cache_conf { - const char *root; /* the location of the cache directory */ - off_t space; /* Maximum cache size (in 1024 bytes) */ + const char *root; /* the location of the cache directory */ + off_t space; /* Maximum cache size (in 1024 bytes) */ char space_set; - time_t maxexpire; /* Maximum time to keep cached files in secs */ + time_t maxexpire; /* Maximum time to keep cached files in secs */ char maxexpire_set; - time_t defaultexpire; /* default time to keep cached file in secs */ + time_t defaultexpire; /* default time to keep cached file in secs */ char defaultexpire_set; - double lmfactor; /* factor for estimating expires date */ + double lmfactor; /* factor for estimating expires date */ char lmfactor_set; - time_t gcinterval; /* garbage collection interval, in seconds */ + time_t gcinterval; /* garbage collection interval, in seconds */ char gcinterval_set; - int dirlevels; /* Number of levels of subdirectories */ + int dirlevels; /* Number of levels of subdirectories */ char dirlevels_set; - int dirlength; /* Length of subdirectory names */ + int dirlength; /* Length of subdirectory names */ char dirlength_set; - float cache_completion; /* Force cache completion after this point */ + float cache_completion; /* Force cache completion after this point */ char cache_completion_set; }; typedef struct { - struct cache_conf cache; /* cache configuration */ + struct cache_conf cache; /* cache configuration */ array_header *proxies; array_header *aliases; array_header *raliases; @@ -212,8 +189,8 @@ typedef struct { array_header *dirconn; array_header *nocaches; array_header *allowed_connect_ports; - char *domain; /* domain name to use in absence of a domain name in the request */ - int req; /* true if proxy requests are enabled */ + char *domain; /* domain name to use in absence of a domain name in the request */ + int req; /* true if proxy requests are enabled */ char req_set; enum { via_off, @@ -233,33 +210,48 @@ struct hdr_entry { /* caching information about a request */ typedef struct { - request_rec *req; /* the request */ - char *url; /* the URL requested */ - char *filename; /* name of the cache file, or NULL if no cache */ - char *tempfile; /* name of the temporary file, of NULL if not caching */ - time_t ims; /* if-modified-since date of request; -1 if no header */ - BUFF *fp; /* the cache file descriptor if the file is cached - and may be returned, or NULL if the file is - not cached (or must be reloaded) */ - time_t expire; /* calculated expire date of cached entity */ - time_t lmod; /* last-modified date of cached entity */ - time_t date; /* the date the cached file was last touched */ - int version; /* update count of the file */ - off_t len; /* content length */ - char *protocol; /* Protocol, and major/minor number, e.g. HTTP/1.1 */ - int status; /* the status of the cached file */ - unsigned int written; /* total *content* bytes written to cache */ - float cache_completion; /* specific to this request */ - char *resp_line; /* the whole status like (protocol, code + message) */ - table *hdrs; /* the HTTP headers of the file */ + request_rec *req; /* the request */ + char *url; /* the URL requested */ + char *filename; /* name of the cache file, + or NULL if no cache */ + char *tempfile; /* name of the temporary file, + or NULL if not caching */ + time_t ims; /* if-Modified-Since date of request, + -1 if no header */ + time_t ius; /* if-Unmodified-Since date of request, + -1 if no header */ + const char *im; /* if-Match etag of request, + NULL if no header */ + const char *inm; /* if-None-Match etag of request, + NULL if no header */ + BUFF *fp; /* the cache file descriptor if the file + is cached and may be returned, + or NULL if the file is not cached + (or must be reloaded) */ + BUFF *origfp; /* the old cache file descriptor if the file has + been revalidated and is being rewritten to + disk */ + time_t expire; /* calculated expire date of cached entity */ + time_t lmod; /* last-modified date of cached entity */ + time_t date; /* the date the cached file was last touched */ + time_t req_time; /* the time the request started */ + time_t resp_time; /* the time the response was received */ + int version; /* update count of the file */ + off_t len; /* content length */ + char *protocol; /* Protocol, and major/minor number, + e.g. HTTP/1.1 */ + int status; /* the status of the cached file */ + unsigned int written; /* total *content* bytes written to cache */ + float cache_completion; /* specific to this request */ + char *resp_line; /* the whole status line + (protocol, code + message) */ + table *req_hdrs; /* the original request headers */ + table *hdrs; /* the original HTTP response headers + of the file */ + char *xcache; /* the X-Cache header value + to be sent to client */ } cache_req; -/* Additional information passed to the function called by ap_table_do() */ -struct tbl_do_args { - request_rec *req; - cache_req *cache; -}; - struct per_thread_data { struct hostent hpbuf; u_long ipaddr; @@ -271,15 +263,15 @@ struct per_thread_data { void ap_proxy_cache_tidy(cache_req *c); int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf, - cache_req **cr); + cache_req **cr); int ap_proxy_cache_update(cache_req *c, table *resp_hdrs, - const int is_HTTP1, int nocache); + const int is_HTTP1, int nocache); void ap_proxy_garbage_coll(request_rec *r); /* proxy_connect.c */ int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, - const char *proxyhost, int proxyport); + const char *proxyhost, int proxyport); /* proxy_ftp.c */ @@ -289,23 +281,23 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url); /* proxy_http.c */ int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme, - int def_port); + int def_port); int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, - const char *proxyhost, int proxyport); + const char *proxyhost, int proxyport); /* proxy_util.c */ int ap_proxy_hex2c(const char *x); void ap_proxy_c2hex(int ch, char *x); char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t, - enum proxyreqtype isenc); + enum proxyreqtype isenc); char *ap_proxy_canon_netloc(pool *p, char **const urlp, char **userp, - char **passwordp, char **hostp, int *port); + char **passwordp, char **hostp, int *port); const char *ap_proxy_date_canon(pool *p, const char *x); table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f); -long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c); -void ap_proxy_send_headers(request_rec *r, const char *respline, table *hdrs); -int ap_proxy_liststr(const char *list, const char *val); +long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c, off_t len, int nowrite); +void ap_proxy_write_headers(cache_req *c, const char *respline, table *t); +int ap_proxy_liststr(const char *list, const char *key, char **val); void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength); int ap_proxy_hex2sec(const char *x); void ap_proxy_sec2hex(int t, char *y); @@ -321,5 +313,30 @@ int ap_proxy_garbage_init(server_rec *, pool *); /* This function is called by ap_table_do() for all header lines */ int ap_proxy_send_hdr_line(void *p, const char *key, const char *value); unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache); +time_t ap_proxy_current_age(cache_req *c, const time_t age_value); +BUFF *ap_proxy_open_cachefile(request_rec *r, char *filename); +BUFF *ap_proxy_create_cachefile(request_rec *r, char *filename); +void ap_proxy_clear_connection(pool *p, table *headers); +int ap_proxy_table_replace(table *base, table *overlay); + +/* WARNING - PRIVATE DEFINITION BELOW */ + +/* XXX: if you tweak this you should look at is_empty_table() and table_elts() + * in ap_alloc.h + * + * NOTE: this private definition is a duplicate of the one in alloc.c + * It's here for ap_proxy_table_replace() to avoid breaking binary compat + */ +struct table { + /* This has to be first to promote backwards compatibility with + * older modules which cast a table * to an array_header *... + * they should use the table_elts() function for most of the + * cases they do this for. + */ + array_header a; +#ifdef MAKE_TABLE_PROFILE + void *creator; +#endif +}; #endif /*MOD_PROXY_H*/ diff --git a/src/modules/proxy/proxy_cache.c b/src/modules/proxy/proxy_cache.c index a70e5c8635c..10663038e47 100644 --- a/src/modules/proxy/proxy_cache.c +++ b/src/modules/proxy/proxy_cache.c @@ -62,6 +62,7 @@ #include "http_conf_globals.h" #include "http_log.h" #include "http_main.h" +#include "http_core.h" #include "util_date.h" #ifdef WIN32 #include @@ -86,7 +87,7 @@ struct gc_ent { /* Poor man's 61 bit arithmetic */ typedef struct { - long lower; /* lower 30 bits of result */ + long lower; /* lower 30 bits of result */ long upper; /* upper 31 bits of result */ } long61_t; @@ -106,7 +107,7 @@ typedef struct { */ #define ROUNDUP2BLOCKS(_bytes) (((_bytes)+block_size-1) & ~(block_size-1)) -static long block_size = 512; /* this must be a power of 2 */ +static long block_size = 512; /* this must be a power of 2 */ static long61_t curbytes, cachesize; static time_t garbage_now, garbage_expire; static mutex *garbage_mutex = NULL; @@ -115,14 +116,14 @@ static mutex *garbage_mutex = NULL; int ap_proxy_garbage_init(server_rec *r, pool *p) { if (!garbage_mutex) - garbage_mutex = ap_create_mutex(NULL); + garbage_mutex = ap_create_mutex(NULL); return (0); } static int sub_garbage_coll(request_rec *r, array_header *files, - const char *cachedir, const char *cachesubdir); + const char *cachedir, const char *cachesubdir); static void help_proxy_garbage_coll(request_rec *r); static int should_proxy_garbage_coll(request_rec *r); #if !defined(WIN32) && !defined(MPE) && !defined(OS2) && !defined(NETWARE) && !defined(TPF) @@ -136,14 +137,14 @@ void ap_proxy_garbage_coll(request_rec *r) (void) ap_acquire_mutex(garbage_mutex); if (inside == 1) { - (void) ap_release_mutex(garbage_mutex); - return; + (void) ap_release_mutex(garbage_mutex); + return; } else - inside = 1; + inside = 1; (void) ap_release_mutex(garbage_mutex); - ap_block_alarms(); /* avoid SIGALRM on big cache cleanup */ + ap_block_alarms(); /* avoid SIGALRM on big cache cleanup */ if (should_proxy_garbage_coll(r)) #if !defined(WIN32) && !defined(MPE) && !defined(OS2) && !defined(NETWARE) && !defined(TPF) detached_proxy_garbage_coll(r); @@ -188,7 +189,7 @@ static long cmp_long61 (long61_t *left, long61_t *right) { return (left->upper == right->upper) ? (left->lower - right->lower) - : (left->upper - right->upper); + : (left->upper - right->upper); } /* Compare two gc_ent's, sort them by expiration date */ @@ -198,11 +199,11 @@ static int gcdiff(const void *ap, const void *bp) const struct gc_ent *b = (const struct gc_ent *) bp; if (a->expire > b->expire) - return 1; + return 1; else if (a->expire < b->expire) - return -1; + return -1; else - return 0; + return 0; } #if !defined(WIN32) && !defined(MPE) && !defined(OS2) && !defined(NETWARE) && !defined(TPF) @@ -214,68 +215,68 @@ static void detached_proxy_garbage_coll(request_rec *r) #if 0 ap_log_error(APLOG_MARK, APLOG_DEBUG, r->server, - "proxy: Guess what; we fork() again..."); + "proxy: Guess what; we fork() again..."); #endif switch (pid = fork()) { - case -1: - ap_log_error(APLOG_MARK, APLOG_ERR, r->server, - "proxy: fork() for cache cleanup failed"); - return; + case -1: + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "proxy: fork() for cache cleanup failed"); + return; - case 0: /* Child */ + case 0: /* Child */ - /* close all sorts of things, including the socket fd */ - ap_cleanup_for_exec(); + /* close all sorts of things, including the socket fd */ + ap_cleanup_for_exec(); - /* Fork twice to disassociate from the child */ - switch (pid = fork()) { - case -1: - ap_log_error(APLOG_MARK, APLOG_ERR, r->server, - "proxy: fork(2nd) for cache cleanup failed"); - exit(1); + /* Fork twice to disassociate from the child */ + switch (pid = fork()) { + case -1: + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "proxy: fork(2nd) for cache cleanup failed"); + exit(1); - case 0: /* Child */ - /* The setpgrp() stuff was snarfed from http_main.c */ + case 0: /* Child */ + /* The setpgrp() stuff was snarfed from http_main.c */ #ifndef NO_SETSID - if ((pgrp = setsid()) == -1) { - perror("setsid"); - fprintf(stderr, "%s: setsid failed\n", - ap_server_argv0); - exit(1); - } + if ((pgrp = setsid()) == -1) { + perror("setsid"); + fprintf(stderr, "%s: setsid failed\n", + ap_server_argv0); + exit(1); + } #elif defined(NEXT) || defined(NEWSOS) - if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) { - perror("setpgrp"); - fprintf(stderr, "%S: setpgrp or getpgrp failed\n", - ap_server_argv0); - exit(1); - } + if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) { + perror("setpgrp"); + fprintf(stderr, "%S: setpgrp or getpgrp failed\n", + ap_server_argv0); + exit(1); + } #else - if ((pgrp = setpgrp(getpid(), 0)) == -1) { - perror("setpgrp"); - fprintf(stderr, "%s: setpgrp failed\n", - ap_server_argv0); - exit(1); - } + if ((pgrp = setpgrp(getpid(), 0)) == -1) { + perror("setpgrp"); + fprintf(stderr, "%s: setpgrp failed\n", + ap_server_argv0); + exit(1); + } #endif - help_proxy_garbage_coll(r); - exit(0); - - default: /* Father */ - /* After grandson has been forked off, */ - /* there's nothing else to do. */ - exit(0); - } - default: - /* Wait until grandson has been forked off */ - /* (without wait we'd leave a zombie) */ - waitpid(pid, &status, 0); - return; + help_proxy_garbage_coll(r); + exit(0); + + default: /* Father */ + /* After grandson has been forked off, */ + /* there's nothing else to do. */ + exit(0); + } + default: + /* Wait until grandson has been forked off */ + /* (without wait we'd leave a zombie) */ + waitpid(pid, &status, 0); + return; } } #endif /* ndef WIN32 */ -#define DOT_TIME "/.time" /* marker */ +#define DOT_TIME "/.time" /* marker */ static int should_proxy_garbage_coll(request_rec *r) { @@ -333,7 +334,7 @@ static int should_proxy_garbage_coll(request_rec *r) close(timefd); } else { - lastcheck = buf.st_mtime; /* save the time */ + lastcheck = buf.st_mtime; /* save the time */ if (garbage_now < lastcheck + every) { return 0; } @@ -363,7 +364,7 @@ static void help_proxy_garbage_coll(request_rec *r) cachesize.lower = cachesize.upper = 0; add_long61(&cachesize, conf->space << 10); - ap_block_alarms(); /* avoid SIGALRM on big cache cleanup */ + ap_block_alarms(); /* avoid SIGALRM on big cache cleanup */ files = ap_make_array(r->pool, 100, sizeof(struct gc_ent)); curbytes.upper = curbytes.lower = 0L; @@ -371,47 +372,47 @@ static void help_proxy_garbage_coll(request_rec *r) sub_garbage_coll(r, files, cachedir, "/"); if (cmp_long61(&curbytes, &cachesize) < 0L) { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, - "proxy GC: Cache is %ld%% full (nothing deleted)", - (long)(((curbytes.upper<<20)|(curbytes.lower>>10))*100/conf->space)); - ap_unblock_alarms(); - return; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, + "proxy GC: Cache is %ld%% full (nothing deleted)", + (long)(((curbytes.upper<<20)|(curbytes.lower>>10))*100/conf->space)); + ap_unblock_alarms(); + return; } /* sort the files we found by expiration date */ qsort(files->elts, files->nelts, sizeof(struct gc_ent), gcdiff); for (i = 0; i < files->nelts; i++) { - fent = &((struct gc_ent *) files->elts)[i]; - sprintf(filename, "%s%s", cachedir, fent->file); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "GC Unlinking %s (expiry %ld, garbage_now %ld)", filename, (long)fent->expire, (long)garbage_now); + fent = &((struct gc_ent *) files->elts)[i]; + sprintf(filename, "%s%s", cachedir, fent->file); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "GC Unlinking %s (expiry %ld, garbage_now %ld)", filename, (long)fent->expire, (long)garbage_now); #if TESTING - fprintf(stderr, "Would unlink %s\n", filename); + fprintf(stderr, "Would unlink %s\n", filename); #else - if (unlink(filename) == -1) { - if (errno != ENOENT) - ap_log_error(APLOG_MARK, APLOG_ERR, r->server, - "proxy gc: unlink(%s)", filename); - } - else + if (unlink(filename) == -1) { + if (errno != ENOENT) + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "proxy gc: unlink(%s)", filename); + } + else #endif - { - sub_long61(&curbytes, ROUNDUP2BLOCKS(fent->len)); - if (cmp_long61(&curbytes, &cachesize) < 0) - break; - } + { + sub_long61(&curbytes, ROUNDUP2BLOCKS(fent->len)); + if (cmp_long61(&curbytes, &cachesize) < 0) + break; + } } ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, - "proxy GC: Cache is %ld%% full (%d deleted)", - (long)(((curbytes.upper<<20)|(curbytes.lower>>10))*100/conf->space), i); + "proxy GC: Cache is %ld%% full (%d deleted)", + (long)(((curbytes.upper<<20)|(curbytes.lower>>10))*100/conf->space), i); ap_unblock_alarms(); } static int sub_garbage_coll(request_rec *r, array_header *files, - const char *cachebasedir, const char *cachesubdir) + const char *cachebasedir, const char *cachesubdir) { - char line[27]; + char line[17*(3)]; char cachedir[HUGE_STRING_LEN]; struct stat buf; int fd, i; @@ -427,51 +428,51 @@ static int sub_garbage_coll(request_rec *r, array_header *files, ap_snprintf(cachedir, sizeof(cachedir), "%s%s", cachebasedir, cachesubdir); filename = ap_palloc(r->pool, strlen(cachedir) + HASH_LEN + 2); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "GC Examining directory %s", cachedir); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "GC Examining directory %s", cachedir); dir = opendir(cachedir); if (dir == NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR, r->server, - "proxy gc: opendir(%s)", cachedir); - return 0; + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "proxy gc: opendir(%s)", cachedir); + return 0; } while ((ent = readdir(dir)) != NULL) { - if (ent->d_name[0] == '.') - continue; - sprintf(filename, "%s%s", cachedir, ent->d_name); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "GC Examining file %s", filename); + if (ent->d_name[0] == '.') + continue; + sprintf(filename, "%s%s", cachedir, ent->d_name); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "GC Examining file %s", filename); /* is it a temporary file? */ - if (strncmp(ent->d_name, "tmp", 3) == 0) { + if (strncmp(ent->d_name, "tmp", 3) == 0) { /* then stat it to see how old it is; delete temporary files > 1 day old */ - if (stat(filename, &buf) == -1) { - if (errno != ENOENT) - ap_log_error(APLOG_MARK, APLOG_ERR, r->server, - "proxy gc: stat(%s)", filename); - } - else if (garbage_now != -1 && buf.st_atime < garbage_now - SEC_ONE_DAY && - buf.st_mtime < garbage_now - SEC_ONE_DAY) { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "GC unlink %s", filename); - ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r->server, - "proxy gc: deleting orphaned cache file %s", filename); + if (stat(filename, &buf) == -1) { + if (errno != ENOENT) + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "proxy gc: stat(%s)", filename); + } + else if (garbage_now != -1 && buf.st_atime < garbage_now - SEC_ONE_DAY && + buf.st_mtime < garbage_now - SEC_ONE_DAY) { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "GC unlink %s", filename); + ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r->server, + "proxy gc: deleting orphaned cache file %s", filename); #if TESTING - fprintf(stderr, "Would unlink %s\n", filename); + fprintf(stderr, "Would unlink %s\n", filename); #else - unlink(filename); + unlink(filename); #endif - } - continue; - } - ++nfiles; + } + continue; + } + ++nfiles; /* is it another file? */ - /* FIXME: Shouldn't any unexpected files be deleted? */ - /* if (strlen(ent->d_name) != HASH_LEN) continue; */ + /* FIXME: Shouldn't any unexpected files be deleted? */ + /* if (strlen(ent->d_name) != HASH_LEN) continue; */ /* under OS/2 use dirent's d_attr to identify a diretory */ /* under TPF use stat to identify a directory */ #if defined(OS2) || defined(TPF) /* is it a directory? */ #ifdef OS2 - if (ent->d_attr & A_DIR) { + if (ent->d_attr & A_DIR) { #elif defined(TPF) if (stat(filename, &buf) == -1) { if (errno != ENOENT) @@ -480,21 +481,21 @@ static int sub_garbage_coll(request_rec *r, array_header *files, } if (S_ISDIR(buf.st_mode)) { #endif - char newcachedir[HUGE_STRING_LEN]; - ap_snprintf(newcachedir, sizeof(newcachedir), - "%s%s/", cachesubdir, ent->d_name); - if (!sub_garbage_coll(r, files, cachebasedir, newcachedir)) { - ap_snprintf(newcachedir, sizeof(newcachedir), - "%s%s", cachedir, ent->d_name); + char newcachedir[HUGE_STRING_LEN]; + ap_snprintf(newcachedir, sizeof(newcachedir), + "%s%s/", cachesubdir, ent->d_name); + if (!sub_garbage_coll(r, files, cachebasedir, newcachedir)) { + ap_snprintf(newcachedir, sizeof(newcachedir), + "%s%s", cachedir, ent->d_name); #if TESTING - fprintf(stderr, "Would remove directory %s\n", newcachedir); + fprintf(stderr, "Would remove directory %s\n", newcachedir); #else - rmdir(newcachedir); + rmdir(newcachedir); #endif - --nfiles; - } - continue; - } + --nfiles; + } + continue; + } #endif /* read the file */ @@ -505,51 +506,51 @@ static int sub_garbage_coll(request_rec *r, array_header *files, */ if (stat(filename, &buf) == -1) { ap_log_error(APLOG_MARK, APLOG_ERR, r->server, - "proxy gc: stat(%s)", filename); + "proxy gc: stat(%s)", filename); continue; } fd = -1; #else - fd = open(filename, O_RDONLY | O_BINARY); - if (fd == -1) { - if (errno != ENOENT) - ap_log_error(APLOG_MARK, APLOG_ERR, r->server, - "proxy gc: open(%s)", filename); - continue; - } - if (fstat(fd, &buf) == -1) { - ap_log_error(APLOG_MARK, APLOG_ERR, r->server, - "proxy gc: fstat(%s)", filename); - close(fd); - continue; - } + fd = open(filename, O_RDONLY | O_BINARY); + if (fd == -1) { + if (errno != ENOENT) + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "proxy gc: open(%s)", filename); + continue; + } + if (fstat(fd, &buf) == -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "proxy gc: fstat(%s)", filename); + close(fd); + continue; + } #endif /* In OS/2 and TPF this has already been done above */ #if !defined(OS2) && !defined(TPF) - if (S_ISDIR(buf.st_mode)) { - char newcachedir[HUGE_STRING_LEN]; + if (S_ISDIR(buf.st_mode)) { + char newcachedir[HUGE_STRING_LEN]; #if !defined(WIN32) /* Win32 used stat, no file to close */ close(fd); #endif - ap_snprintf(newcachedir, sizeof(newcachedir), - "%s%s/", cachesubdir, ent->d_name); - if (!sub_garbage_coll(r, files, cachebasedir, newcachedir)) { - ap_snprintf(newcachedir, sizeof(newcachedir), - "%s%s", cachedir, ent->d_name); + ap_snprintf(newcachedir, sizeof(newcachedir), + "%s%s/", cachesubdir, ent->d_name); + if (!sub_garbage_coll(r, files, cachebasedir, newcachedir)) { + ap_snprintf(newcachedir, sizeof(newcachedir), + "%s%s", cachedir, ent->d_name); #if TESTING - fprintf(stderr, "Would remove directory %s\n", newcachedir); + fprintf(stderr, "Would remove directory %s\n", newcachedir); #else - rmdir(newcachedir); + rmdir(newcachedir); #endif - --nfiles; - } else { - /* Directory is not empty. Account for its size: */ - add_long61(&curbytes, ROUNDUP2BLOCKS(buf.st_size)); - } - continue; - } + --nfiles; + } else { + /* Directory is not empty. Account for its size: */ + add_long61(&curbytes, ROUNDUP2BLOCKS(buf.st_size)); + } + continue; + } #endif #if defined(WIN32) @@ -559,36 +560,36 @@ static int sub_garbage_coll(request_rec *r, array_header *files, fd = open(filename, O_RDONLY | O_BINARY); if (fd == -1) { if (errno != ENOENT) - ap_log_error(APLOG_MARK, APLOG_ERR, r->server, - "proxy gc: open(%s) = %d", filename, errno); + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "proxy gc: open(%s) = %d", filename, errno); continue; } #endif - i = read(fd, line, 26); - close(fd); - if (i == -1) { - ap_log_error(APLOG_MARK, APLOG_ERR, r->server, - "proxy gc: read(%s)", filename); - continue; - } - line[i] = '\0'; - garbage_expire = ap_proxy_hex2sec(line + 18); - if (!ap_checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&&") || - garbage_expire == BAD_DATE) { - /* bad file */ - if (garbage_now != -1 && buf.st_atime > garbage_now + SEC_ONE_DAY && - buf.st_mtime > garbage_now + SEC_ONE_DAY) { - ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server, - "proxy: deleting bad cache file with future date: %s", filename); + i = read(fd, line, 17*(3)-1); + close(fd); + if (i == -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "proxy gc: read(%s)", filename); + continue; + } + line[i] = '\0'; + garbage_expire = ap_proxy_hex2sec(line + 17*(2)); + if (!ap_checkmask(line, "&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&") || + garbage_expire == BAD_DATE) { + /* bad file */ + if (garbage_now != -1 && buf.st_atime > garbage_now + SEC_ONE_DAY && + buf.st_mtime > garbage_now + SEC_ONE_DAY) { + ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server, + "proxy: deleting bad cache file with future date: %s", filename); #if TESTING - fprintf(stderr, "Would unlink bad file %s\n", filename); + fprintf(stderr, "Would unlink bad file %s\n", filename); #else - unlink(filename); + unlink(filename); #endif - } - continue; - } + } + continue; + } /* * we need to calculate an 'old' factor, and remove the 'oldest' files @@ -596,14 +597,14 @@ static int sub_garbage_coll(request_rec *r, array_header *files, * file. * */ - fent = (struct gc_ent *) ap_push_array(files); - fent->len = buf.st_size; - fent->expire = garbage_expire; - strcpy(fent->file, cachesubdir); - strcat(fent->file, ent->d_name); + fent = (struct gc_ent *) ap_push_array(files); + fent->len = buf.st_size; + fent->expire = garbage_expire; + strcpy(fent->file, cachesubdir); + strcat(fent->file, ent->d_name); /* accumulate in blocks, to cope with directories > 4Gb */ - add_long61(&curbytes, ROUNDUP2BLOCKS(buf.st_size)); + add_long61(&curbytes, ROUNDUP2BLOCKS(buf.st_size)); } closedir(dir); @@ -612,75 +613,292 @@ static int sub_garbage_coll(request_rec *r, array_header *files, } + /* - * read a cache file; + * Read a cache file; * returns 1 on success, * 0 on failure (bad file or wrong URL) * -1 on UNIX error + * + * We read the cache hex header, then the message response line and + * response headers, and finally we return with the filepointer + * pointing at the start of the message body itself, ready to be + * shipped to the client later on, if appropriate. */ static int rdcache(request_rec *r, BUFF *cachefp, cache_req *c) { - char urlbuff[1034], *strp; + char urlbuff[HUGE_STRING_LEN], *strp; int len; -/* read the data from the cache file */ -/* format - * date SP lastmod SP expire SP count SP content-length CRLF - * dates are stored as hex seconds since 1970 - */ + + /* read the data from the cache file */ + + /* Format: + * + * The cache needs to keep track of the following information: + * - Date, LastMod, Version, ReqTime, RespTime, ContentLength + * - The original request headers (for Vary) + * - The original response headers (for returning with a cached response) + * - The body of the message + * + * date SP lastmod SP expire SP count SP request-time SP response-time SP content-lengthCRLF + * (dates are stored as hex seconds since 1970) + * Original URLCRLF + * Original Request Headers + * CRLF + * Original Response Headers + * CRLF + * Body + * + */ + + /* retrieve cachefile information values */ len = ap_bgets(urlbuff, sizeof urlbuff, cachefp); if (len == -1) - return -1; + return -1; if (len == 0 || urlbuff[len - 1] != '\n') - return 0; + return 0; urlbuff[len - 1] = '\0'; if (!ap_checkmask(urlbuff, - "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&")) - return 0; + "&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&")) + return 0; - c->date = ap_proxy_hex2sec(urlbuff); - c->lmod = ap_proxy_hex2sec(urlbuff + 9); - c->expire = ap_proxy_hex2sec(urlbuff + 18); - c->version = ap_proxy_hex2sec(urlbuff + 27); - c->len = ap_proxy_hex2sec(urlbuff + 36); + c->date = ap_proxy_hex2sec(urlbuff + 17*(0)); + c->lmod = ap_proxy_hex2sec(urlbuff + 17*(1)); + c->expire = ap_proxy_hex2sec(urlbuff + 17*(2)); + c->version = ap_proxy_hex2sec(urlbuff + 17*(3)); + c->req_time = ap_proxy_hex2sec(urlbuff + 17*(4)); + c->resp_time = ap_proxy_hex2sec(urlbuff + 17*(5)); + c->len = ap_proxy_hex2sec(urlbuff + 17*(6)); -/* check that we have the same URL */ + /* check that we have the same URL */ len = ap_bgets(urlbuff, sizeof urlbuff, cachefp); if (len == -1) - return -1; + return -1; if (len == 0 || strncmp(urlbuff, "X-URL: ", 7) != 0 || - urlbuff[len - 1] != '\n') - return 0; + urlbuff[len - 1] != '\n') + return 0; urlbuff[len - 1] = '\0'; if (strcmp(urlbuff + 7, c->url) != 0) - return 0; + return 0; + + /* then the original request headers */ + c->req_hdrs = ap_proxy_read_headers(r, urlbuff, sizeof urlbuff, cachefp); + if (c->req_hdrs == NULL) + return -1; -/* What follows is the message */ + /* then the original response headers */ len = ap_bgets(urlbuff, sizeof urlbuff, cachefp); if (len == -1) - return -1; + return -1; if (len == 0 || urlbuff[len - 1] != '\n') - return 0; + return 0; urlbuff[--len] = '\0'; c->resp_line = ap_pstrdup(r->pool, urlbuff); strp = strchr(urlbuff, ' '); if (strp == NULL) - return 0; + return 0; c->status = atoi(strp); c->hdrs = ap_proxy_read_headers(r, urlbuff, sizeof urlbuff, cachefp); if (c->hdrs == NULL) - return -1; - if (c->len != -1) { /* add a content-length header */ - if (ap_table_get(c->hdrs, "Content-Length") == NULL) { - ap_table_set(c->hdrs, "Content-Length", - ap_psprintf(r->pool, "%lu", (unsigned long)c->len)); - } - } + return -1; + if (c->len != -1) /* add a content-length header */ + if (ap_table_get(c->hdrs, "Content-Length") == NULL) { + ap_table_set(c->hdrs, "Content-Length", + ap_psprintf(r->pool, "%lu", (unsigned long)c->len)); + } + + return 1; } +/* + * Call this to check the possible conditional status of + * the client request, and return the response from the cache + * + * Conditionals include If-Modified-Since, If-Match, If-Unmodified-Since + * and If-None-Match. + * + * We don't yet understand If-Range, but we will... + */ +int ap_proxy_cache_conditional(request_rec *r, cache_req *c, BUFF *cachefp) +{ + const char *etag, *wetag = NULL; + + /* get etag */ + if ((etag = ap_table_get(c->hdrs, "Etag"))) { + wetag = ap_pstrcat(r->pool, "W/", etag, NULL); + } + + /* check for If-Match, If-Unmodified-Since */ + while (1) { + + /* check If-Match and If-Unmodified-Since exist + * + * If neither of these exist, the request is not conditional, and + * we serve it normally + */ + if (!c->im && BAD_DATE == c->ius) { + break; + } + + /* check If-Match + * + * we check if the Etag on the cached file is in the list of Etags + * in the If-Match field. The comparison must be a strong comparison, + * so the Etag cannot be marked as weak. If the comparision fails + * we return 412 Precondition Failed. + * + * if If-Match is specified AND + * If-Match is not a "*" AND + * Etag is missing or weak or not in the list THEN + * return 412 Precondition Failed + */ + + if (c->im) { + if (strcmp(c->im, "*") && + (!etag || (strlen(etag) > 1 && 'W' == etag[0] && '/' == etag[1]) || !ap_proxy_liststr(c->im, etag, NULL))) { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "If-Match specified, and it didn't - return 412"); + } + else { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "If-Match specified, and it matched"); + break; + } + } + + /* check If-Unmodified-Since + * + * if If-Unmodified-Since is specified AND + * Last-Modified is specified somewhere AND + * If-Unmodified-Since is in the past compared to Last-Modified THEN + * return 412 Precondition Failed + */ + if (BAD_DATE != c->ius && BAD_DATE != c->lmod) { + if (c->ius < c->lmod) { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "If-Unmodified-Since specified, but it wasn't - return 412"); + } + else { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "If-Unmodified-Since specified, and it was unmodified"); + break; + } + } + + /* if cache file is being updated */ + if (c->origfp) { + ap_proxy_write_headers(c, c->resp_line, c->hdrs); + ap_proxy_send_fb(c->origfp, r, c, c->len, 1); + ap_pclosef(r->pool, ap_bfileno(c->origfp, B_WR)); + ap_proxy_cache_tidy(c); + } + else + ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR)); + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Use your cached copy, conditional precondition failed."); + return HTTP_PRECONDITION_FAILED; + } + + + /* check for If-None-Match, If-Modified-Since */ + while (1) { + + /* check for existance of If-None-Match and If-Modified-Since + * + * if neither of these headers have been set, then the request + * is not conditional, and we just send the cached response and + * be done with it. + */ + if (!c->inm && BAD_DATE == c->ims) { + break; + } + + /* check If-None-Match + * + * we check if the Etag on the cached file is in the list of Etags + * in the If-None-Match field. The comparison must be a strong comparison, + * so the Etag cannot be marked as weak. If the comparision fails + * we return 412 Precondition Failed. + * + * if If-None-Match is specified: + * if If-None-Match is a "*" THEN 304 + * else if Etag is specified AND we get a match THEN 304 + * else if Weak Etag is specified AND we get a match THEN 304 + * else sent the original object + */ + if (c->inm) { + if (!strcmp(c->inm, "*")) { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "If-None-Match: * specified, return 304"); + } + else if (etag && ap_proxy_liststr(c->inm, etag, NULL)) { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "If-None-Match: specified and we got a strong match - return 304"); + } + else if (wetag && ap_proxy_liststr(c->inm, wetag, NULL)) { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "If-None-Match specified, and we got a weak match - return 304"); + } + else + break; + } + + /* check If-Modified-Since + * + * if If-Modified-Since is specified AND + * Last-Modified is specified somewhere: + * if last modification date is earlier than If-Modified-Since THEN 304 + * else send the original object + */ + if (BAD_DATE != c->ims && BAD_DATE != c->lmod) { + if (c->ims >= c->lmod) { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "If-Modified-Since specified and not modified, try return 304"); + } + else + break; + } + + + /* are we updating the cache file? */ + if (c->origfp) { + ap_proxy_write_headers(c, c->resp_line, c->hdrs); + ap_proxy_send_fb(c->origfp, r, c, c->len, 1); + ap_pclosef(r->pool, ap_bfileno(c->origfp, B_WR)); + ap_proxy_cache_tidy(c); + } + else + ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR)); + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Use local copy, cached file hasn't changed"); + return HTTP_NOT_MODIFIED; + } + + + /* No conditional - just send it cousin! */ + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Local copy modified, send it"); + r->status_line = strchr(c->resp_line, ' ') + 1; + r->status = c->status; + + /* Prepare and send headers to client */ + ap_overlap_tables(r->headers_out, c->hdrs, AP_OVERLAP_TABLES_SET); + ap_table_setn(r->headers_out, "X-Cache", c->xcache); + r->content_type = ap_table_get(r->headers_out, "Content-Type"); + ap_send_http_header(r); + + /* are we rewriting the cache file? */ + if (c->origfp) { + ap_proxy_write_headers(c, c->resp_line, c->hdrs); + ap_proxy_send_fb(c->origfp, r, c, c->len, r->header_only); + ap_pclosef(r->pool, ap_bfileno(c->origfp, B_WR)); + ap_proxy_cache_tidy(c); + return OK; + } + + /* no, we not */ + if (!r->header_only) + ap_proxy_send_fb(cachefp, r, NULL, c->len, 0); + + ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR)); + return OK; +} + /* * Call this to test for a resource in the cache @@ -692,149 +910,365 @@ static int rdcache(request_rec *r, BUFF *cachefp, cache_req *c) * if cached file is not expired then * if last modified after if-modified-since then send body * else send 304 Not modified - * else + * else if cached file is expired then * if last modified after if-modified-since then add * last modified date to request */ int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf, - cache_req **cr) + cache_req **cr) { - char hashfile[66]; - const char *imstr, *pragma, *auth; + const char *datestr, *pragma_req = NULL, *pragma_cresp = NULL, *cc_req = NULL, *cc_cresp = NULL, *vary = NULL; cache_req *c; time_t now; BUFF *cachefp; - int cfd, i; - const long int zero = 0L; + int i; void *sconf = r->server->module_config; proxy_server_conf *pconf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + const char *agestr = NULL; + char *val; + time_t age_c = 0; + time_t age, maxage_req, maxage_cresp, maxage, smaxage, maxstale, minfresh; c = ap_pcalloc(r->pool, sizeof(cache_req)); *cr = c; c->req = r; c->url = ap_pstrdup(r->pool, url); + c->filename = NULL; + c->tempfile = NULL; + c->fp = NULL; + c->origfp = NULL; + c->version = 0; + c->len = -1; + c->req_hdrs = NULL; + c->hdrs = NULL; + c->xcache = NULL; -/* get the If-Modified-Since date of the request */ + /* get the If-Modified-Since date of the request, if it exists */ c->ims = BAD_DATE; - imstr = ap_table_get(r->headers_in, "If-Modified-Since"); - if (imstr != NULL) { -/* this may modify the value in the original table */ - imstr = ap_proxy_date_canon(r->pool, imstr); - c->ims = ap_parseHTTPdate(imstr); - if (c->ims == BAD_DATE) /* bad or out of range date; remove it */ - ap_table_unset(r->headers_in, "If-Modified-Since"); + datestr = ap_table_get(r->headers_in, "If-Modified-Since"); + if (datestr != NULL) { + /* this may modify the value in the original table */ + datestr = ap_proxy_date_canon(r->pool, datestr); + c->ims = ap_parseHTTPdate(datestr); + if (c->ims == BAD_DATE) /* bad or out of range date; remove it */ + ap_table_unset(r->headers_in, "If-Modified-Since"); + } + +/* get the If-Unmodified-Since date of the request, if it exists */ + c->ius = BAD_DATE; + datestr = ap_table_get(r->headers_in, "If-Unmodified-Since"); + if (datestr != NULL) { + /* this may modify the value in the original table */ + datestr = ap_proxy_date_canon(r->pool, datestr); + c->ius = ap_parseHTTPdate(datestr); + if (c->ius == BAD_DATE) /* bad or out of range date; remove it */ + ap_table_unset(r->headers_in, "If-Unmodified-Since"); } + +/* get the If-Match of the request, if it exists */ + c->im = ap_table_get(r->headers_in, "If-Match"); + +/* get the If-None-Match of the request, if it exists */ + c->inm = ap_table_get(r->headers_in, "If-None-Match"); /* find the filename for this cache entry */ - ap_proxy_hash(url, hashfile, pconf->cache.dirlevels, pconf->cache.dirlength); - if (conf->root != NULL) - c->filename = ap_pstrcat(r->pool, conf->root, "/", hashfile, NULL); - else - c->filename = NULL; + if (conf->root != NULL) { + char hashfile[66]; + ap_proxy_hash(url, hashfile, pconf->cache.dirlevels, pconf->cache.dirlength); + c->filename = ap_pstrcat(r->pool, conf->root, "/", hashfile, NULL); + } + else { + c->filename = NULL; + c->fp = NULL; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "No CacheRoot, so no caching. Declining."); + return DECLINED; + } +/* find certain cache controlling headers */ + pragma_req = ap_table_get(r->headers_in, "Pragma"); + cc_req = ap_table_get(r->headers_in, "Cache-Control"); + +/* first things first - does the request allow us to return + * cached information at all? If not, just decline the request. + * + * Note that there is a big difference between not being allowed + * to cache a request (no-store) and not being allowed to return + * a cached request without revalidation (max-age=0). + * + * Caching is forbidden under the following circumstances: + * + * - RFC2616 14.9.2 Cache-Control: no-store + * we are not supposed to store this request at all. Behave as a tunnel. + * + */ + if (ap_proxy_liststr(cc_req, "no-store", NULL)) { + +/* delete the previously cached file */ + if (c->filename) + unlink(c->filename); + c->fp = NULL; + c->filename = NULL; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "no-store forbids caching. Declining."); + return DECLINED; + } + +/* if the cache file exists, open it */ cachefp = NULL; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Request for %s, pragma_req=%s, ims=%ld", url, + pragma_req, c->ims); /* find out about whether the request can access the cache */ - pragma = ap_table_get(r->headers_in, "Pragma"); - auth = ap_table_get(r->headers_in, "Authorization"); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Request for %s, pragma=%s, auth=%s, ims=%ld, imstr=%s", url, - pragma, auth, (long)c->ims, imstr); if (c->filename != NULL && r->method_number == M_GET && - strlen(url) < 1024 && !ap_proxy_liststr(pragma, "no-cache") && - auth == NULL) { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Check file %s", c->filename); - cfd = open(c->filename, O_RDWR | O_BINARY); - if (cfd != -1) { - ap_note_cleanups_for_fd(r->pool, cfd); - cachefp = ap_bcreate(r->pool, B_RD | B_WR); - ap_bpushfd(cachefp, cfd, cfd); - } - else if (errno != ENOENT) - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error opening cache file %s", - c->filename); - else - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "File %s not found", c->filename); + strlen(url) < 1024 ) { + cachefp = ap_proxy_open_cachefile(r, c->filename); } + + /* if a cache file exists, try reading body and headers from cache file */ if (cachefp != NULL) { - i = rdcache(r, cachefp, c); - if (i == -1) - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error reading cache file %s", - c->filename); - else if (i == 0) - ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, - "proxy: bad (short?) cache file: %s", c->filename); - if (i != 1) { - ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR)); - cachefp = NULL; - } + i = rdcache(r, cachefp, c); + if (i == -1) + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error reading cache file %s", + c->filename); + else if (i == 0) + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, + "proxy: bad (short?) cache file: %s", c->filename); + if (i != 1) { + ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR)); + cachefp = NULL; + } + if (c->hdrs) { + cc_cresp = ap_table_get(c->hdrs, "Cache-Control"); + pragma_cresp = ap_table_get(c->hdrs, "Pragma"); + vary = ap_table_get(c->hdrs, "Vary"); + if ((agestr = ap_table_get(c->hdrs, "Age"))) { + age_c = atoi(agestr); + } + } } + + /* if a cache file does not exist, create empty header array */ /* fixed? in this case, we want to get the headers from the remote server it will be handled later if we don't do this (I hope ;-) + if (cachefp == NULL) - c->hdrs = ap_make_table(r->pool, 20); + c->hdrs = ap_make_table(r->pool, 20); */ /* FIXME: Shouldn't we check the URL somewhere? */ + + /* Check Content-Negotiation - Vary + * + * At this point we need to make sure that the object we found in the cache + * is the same object that would be delivered to the client, when the + * effects of content negotiation are taken into effect. + * + * In plain english, we want to make sure that a language-negotiated + * document in one language is not given to a client asking for a + * language negotiated document in a different language by mistake. + * + * RFC2616 13.6 and 14.44 describe the Vary mechanism. + */ + if (c->hdrs && c->req_hdrs) { + char *vary = ap_pstrdup(r->pool, ap_table_get(c->hdrs, "Vary")); + + while (vary && *vary) { + char *name = vary; + const char *h1, *h2; + + /* isolate header name */ + while (*vary && !ap_isspace(*vary) && (*vary != ',')) + ++vary; + while (*vary && (ap_isspace(*vary) || (*vary == ','))) { + *vary = '\0'; + ++vary; + } + + /* is this header in the request and the header in the cached + * request identical? If not, we give up and do a straight get */ + h1 = ap_table_get(r->headers_in, name); + h2 = ap_table_get(c->req_hdrs, name); + if (h1 == h2) { + /* both headers NULL, so a match - do nothing */ + } + else if (h1 && h2 && !strcmp(h1, h2)) { + /* both headers exist and are equal - do nothing */ + } + else { + + /* headers do not match, so Vary failed */ + c->fp = cachefp; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Vary header mismatch - object must be fetched from scratch. Declining."); + return DECLINED; + } + } + } + + + /* We now want to check if our cached data is still fresh. This depends + * on a few things, in this order: + * + * - RFC2616 14.9.4 End to end reload, Cache-Control: no-cache + * no-cache in either the request or the cached response means that + * we must revalidate the request unconditionally, overriding any + * expiration mechanism. It's equivalent to max-age=0,must-revalidate. + * + * - RFC2616 14.32 Pragma: no-cache + * This is treated the same as Cache-Control: no-cache. + * + * - RFC2616 14.9.3 Cache-Control: max-stale, must-revalidate, proxy-revalidate + * if the max-stale request header exists, modify the stale calculations + * below so that an object can be at most seconds stale before + * we request a revalidation, _UNLESS_ a must-revalidate or + * proxy-revalidate cached response header exists to stop us doing this. + * + * - RFC2616 14.9.3 Cache-Control: s-maxage + * the origin server specifies the maximum age an object can be before + * it is considered stale. This directive has the effect of proxy|must + * revalidate, which in turn means simple ignore any max-stale setting. + * + * - RFC2616 14.9.4 Cache-Control: max-age + * this header can appear in both requests and responses. If both are + * specified, the smaller of the two takes priority. + * + * - RFC2616 14.21 Expires: + * if this request header exists in the cached entity, and it's value is + * in the past, it has expired. + * + */ + + /* calculate age of object */ + age = ap_proxy_current_age(c, age_c); + + /* extract s-maxage */ + if (cc_cresp && ap_proxy_liststr(cc_cresp, "s-maxage", &val)) + smaxage = atoi(val); + else + smaxage = -1; + + /* extract max-age from request */ + if (cc_cresp && ap_proxy_liststr(cc_req, "max-age", &val)) + maxage_req = atoi(val); + else + maxage_req = -1; + + /* extract max-age from response */ + if (cc_cresp && ap_proxy_liststr(cc_cresp, "max-age", &val)) + maxage_cresp = atoi(val); + else + maxage_cresp = -1; + + /* if both maxage request and response, the smaller one takes priority */ + if (-1 == maxage_req) + maxage = maxage_cresp; + else if (-1 == maxage_cresp) + maxage = maxage_req; + else + maxage = MIN(maxage_req, maxage_cresp); + + /* extract max-stale */ + if (cc_req && ap_proxy_liststr(cc_req, "max-stale", &val)) + maxstale = atoi(val); + else + maxstale = 0; + + /* extract min-fresh */ + if (cc_req && ap_proxy_liststr(cc_req, "min-fresh", &val)) + minfresh = atoi(val); + else + minfresh = 0; + + /* override maxstale if must-revalidate or proxy-revalidate */ + if (maxstale && ( (cc_cresp && ap_proxy_liststr(cc_cresp, "must-revalidate", NULL)) || (cc_cresp && ap_proxy_liststr(cc_cresp, "proxy-revalidate", NULL)) )) + maxstale = 0; + now = time(NULL); -/* Ok, have we got some un-expired data? */ - if (cachefp != NULL && c->expire != BAD_DATE && now < c->expire) { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Unexpired data available"); -/* check IMS */ - if (c->lmod != BAD_DATE && c->ims != BAD_DATE && c->ims >= c->lmod) { -/* has the cached file changed since this request? */ - if (c->date == BAD_DATE || c->date > c->ims) { -/* No, but these header values may have changed, so we send them with the - * 304 HTTP_NOT_MODIFIED response - */ - const char *q; - - if ((q = ap_table_get(c->hdrs, "Expires")) != NULL) - ap_table_set(r->headers_out, "Expires", q); - } - ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Use local copy, cached file hasn't changed"); - return HTTP_NOT_MODIFIED; - } - -/* Ok, has been modified */ - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Local copy modified, send it"); - r->status_line = strchr(c->resp_line, ' ') + 1; - r->status = c->status; - if (!r->assbackwards) { - ap_soft_timeout("proxy send headers", r); - ap_proxy_send_headers(r, c->resp_line, c->hdrs); - ap_kill_timeout(r); - } - ap_bsetopt(r->connection->client, BO_BYTECT, &zero); - r->sent_bodyct = 1; - if (!r->header_only) - ap_proxy_send_fb(cachefp, r, NULL); - ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR)); - return OK; + if (cachefp != NULL && + + /* handle no-cache */ + !( (cc_req && ap_proxy_liststr(cc_req, "no-cache", NULL)) || + (pragma_req && ap_proxy_liststr(pragma_req, "no-cache", NULL)) || + (cc_cresp && ap_proxy_liststr(cc_cresp, "no-cache", NULL)) || + (pragma_cresp && ap_proxy_liststr(pragma_cresp, "no-cache", NULL)) ) && + + /* handle expiration */ + ( (-1 < smaxage && age < (smaxage - minfresh)) || + (-1 < maxage && age < (maxage + maxstale - minfresh)) || + (c->expire != BAD_DATE && age < (c->expire - c->date + maxstale - minfresh)) ) + + ) { + + /* it's fresh darlings... */ + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Unexpired data available"); + + /* set age header on response */ + ap_table_set(c->hdrs, "Age", + ap_psprintf(r->pool, "%lu", (unsigned long)age)); + + /* add warning if maxstale overrode freshness calculation */ + if (!( (-1 < smaxage && age < smaxage) || + (-1 < maxage && age < maxage) || + (c->expire != BAD_DATE && (c->expire - c->date) > age) )) { + ap_table_set(c->hdrs, "Warning", "110 Response is stale"); + } + + /* check conditionals (If-Modified-Since, etc) */ + c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), NULL); + return ap_proxy_cache_conditional(r, c, cachefp); + + } -/* if we already have data and a last-modified date, and it is not a head - * request, then add an If-Modified-Since - */ + /* at this point we have determined our cached data needs revalidation + * but first - we check 1 thing: + * + * RFC2616 14.9.4 - if "only-if-cached" specified, send a + * 504 Gateway Timeout - we're not allowed to revalidate the object + */ + if (ap_proxy_liststr(cc_req, "only-if-cached", NULL)) { + if (cachefp) + ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR)); + return HTTP_GATEWAY_TIME_OUT; + } - if (cachefp != NULL && c->lmod != BAD_DATE && !r->header_only) { -/* - * use the later of the one from the request and the last-modified date - * from the cache - */ - if (c->ims == BAD_DATE || c->ims < c->lmod) { - const char *q; - if ((q = ap_table_get(c->hdrs, "Last-Modified")) != NULL) - ap_table_set(r->headers_in, "If-Modified-Since", - (char *) q); - } + /* If we already have cached data and a last-modified date, and it is + * not a head request, then add an If-Modified-Since. + * + * If we also have an Etag, then the object must have come from + * an HTTP/1.1 server. Add an If-None-Match as well. + * + * See RFC2616 13.3.4 + */ + + if (cachefp != NULL && !r->header_only) { + + const char *etag = ap_table_get(c->hdrs, "Etag"); + + /* If-Modified-Since */ + if (c->lmod != BAD_DATE) { + /* use the later of the one from the request and the last-modified date + * from the cache */ + if (c->ims == BAD_DATE || c->ims < c->lmod) { + const char *q; + + if ((q = ap_table_get(c->hdrs, "Last-Modified")) != NULL) + ap_table_set(r->headers_in, "If-Modified-Since", (char *) q); + } + } + + /* If-None-Match */ + if (etag) { + ap_table_set(r->headers_in, "If-None-Match", etag); + } + } + + c->fp = cachefp; - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Local copy not present or expired. Declining."); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Local copy not present or expired. Declining."); return DECLINED; } @@ -852,119 +1286,166 @@ int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf, * otherwise, delete the old cached file and open a new temporary file */ int ap_proxy_cache_update(cache_req *c, table *resp_hdrs, - const int is_HTTP1, int nocache) + const int is_HTTP1, int nocache) { #if defined(ULTRIX_BRAIN_DEATH) || defined(SINIX_D_RESOLVER_BUG) extern char *mktemp(char *template); #endif request_rec *r = c->req; char *p; - int i; const char *expire, *lmods, *dates, *clen; time_t expc, date, lmod, now; - char buff[46]; + char buff[17*7+1]; void *sconf = r->server->module_config; proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); - const long int zero = 0L; + const char *cc_resp; + table *req_hdrs; + + cc_resp = ap_table_get(resp_hdrs, "Cache-Control"); c->tempfile = NULL; -/* we've received the response */ -/* read expiry date; if a bad date, then leave it so the client can - * read it - */ + /* we've received the response from the origin server */ + + /* read expiry date; if a bad date, then leave it so the client can + * read it */ expire = ap_table_get(resp_hdrs, "Expires"); if (expire != NULL) - expc = ap_parseHTTPdate(expire); + expc = ap_parseHTTPdate(expire); else - expc = BAD_DATE; + expc = BAD_DATE; -/* - * read the last-modified date; if the date is bad, then delete it - */ + /* read the last-modified date; if the date is bad, then delete it */ lmods = ap_table_get(resp_hdrs, "Last-Modified"); if (lmods != NULL) { - lmod = ap_parseHTTPdate(lmods); - if (lmod == BAD_DATE) { -/* kill last modified date */ - lmods = NULL; - } + lmod = ap_parseHTTPdate(lmods); + if (lmod == BAD_DATE) { + /* kill last modified date */ + lmods = NULL; + } } else - lmod = BAD_DATE; + lmod = BAD_DATE; + + + /* + * what responses should we not cache? + * + * At this point we decide based on the response headers whether it + * is appropriate _NOT_ to cache the data from the server. There are + * a whole lot of conditions that prevent us from caching this data. + * They are tested here one by one to be clear and unambiguous. */ + + /* RFC2616 13.4 we are allowed to cache 200, 203, 206, 300, 301 or 410 + * We don't cache 206, because we don't (yet) cache partial responses. + * We include 304 Not Modified here too as this is the origin server + * telling us to serve the cached copy. */ + if ((r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE && r->status != HTTP_MULTIPLE_CHOICES && r->status != HTTP_MOVED_PERMANENTLY && r->status != HTTP_NOT_MODIFIED) || + + /* if a broken Expires header is present, don't cache it */ + (expire != NULL && expc == BAD_DATE) || + + /* if the server said 304 Not Modified but we have no cache file - pass + * this untouched to the user agent, it's not for us. */ + (r->status == HTTP_NOT_MODIFIED && (c == NULL || c->fp == NULL)) || + + /* 200 OK response from HTTP/1.0 and up without a Last-Modified header */ + (r->status == HTTP_OK && lmods == NULL && is_HTTP1) || + + /* HEAD requests */ + r->header_only || + + /* RFC2616 14.9.2 Cache-Control: no-store response indicating do not + * cache, or stop now if you are trying to cache it */ + ap_proxy_liststr(cc_resp, "no-store", NULL) || + + /* RFC2616 14.9.1 Cache-Control: private + * this object is marked for this user's eyes only. Behave as a tunnel. */ + ap_proxy_liststr(cc_resp, "private", NULL) || + + /* RFC2616 14.8 Authorisation: + * if authorisation is included in the request, we don't cache, but we + * can cache if the following exceptions are true: + * 1) If Cache-Control: s-maxage is included + * 2) If Cache-Control: must-revalidate is included + * 3) If Cache-Control: public is included + */ + (ap_table_get(r->headers_in, "Authorization") != NULL -/* - * what responses should we not cache? - * Unknown status responses and those known to be uncacheable - * 304 HTTP_NOT_MODIFIED response when we have no valid cache file, or - * 200 HTTP_OK response from HTTP/1.0 and up without a Last-Modified header, or - * HEAD requests, or - * requests with an Authorization header, or - * protocol requests nocache (e.g. ftp with user/password) - */ -/* @@@ XXX FIXME: is the test "r->status != HTTP_MOVED_PERMANENTLY" correct? - * or shouldn't it be "ap_is_HTTP_REDIRECT(r->status)" ? -MnKr */ - if ((r->status != HTTP_OK && r->status != HTTP_MOVED_PERMANENTLY && r->status != HTTP_NOT_MODIFIED) || - (expire != NULL && expc == BAD_DATE) || - (r->status == HTTP_NOT_MODIFIED && (c == NULL || c->fp == NULL)) || - (r->status == HTTP_OK && lmods == NULL && is_HTTP1) || - r->header_only || - ap_table_get(r->headers_in, "Authorization") != NULL || - nocache) { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Response is not cacheable, unlinking %s", c->filename); -/* close the file */ - if (c->fp != NULL) { - ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR)); - c->fp = NULL; - } -/* delete the previously cached file */ + && !(ap_proxy_liststr(cc_resp, "s-maxage", NULL) || ap_proxy_liststr(cc_resp, "must-revalidate", NULL) || ap_proxy_liststr(cc_resp, "public", NULL)) + ) || + + /* or we've been asked not to cache it above */ + nocache) { + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Response is not cacheable, unlinking %s", c->filename); + + /* close the file */ + if (c->fp != NULL) { + ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR)); + c->fp = NULL; + } + + /* delete the previously cached file */ if (c->filename) unlink(c->filename); - return DECLINED; /* send data to client but not cache */ + return DECLINED; /* send data to client but not cache */ } -/* otherwise, we are going to cache the response */ -/* - * Read the date. Generate one if one is not supplied - */ + + /* It's safe to cache the response. + * + * We now want to update the cache file header information with + * the new date, last modified, expire and content length and write + * it away to our cache file. First, we determine these values from + * the response, using heuristics if appropriate. + * + * In addition, we make HTTP/1.1 age calculations and write them away + * too. + */ + + /* Read the date. Generate one if one is not supplied */ dates = ap_table_get(resp_hdrs, "Date"); if (dates != NULL) - date = ap_parseHTTPdate(dates); + date = ap_parseHTTPdate(dates); else - date = BAD_DATE; + date = BAD_DATE; now = time(NULL); - if (date == BAD_DATE) { /* No, or bad date */ + if (date == BAD_DATE) { /* No, or bad date */ /* no date header! */ /* add one; N.B. use the time _now_ rather than when we were checking the cache */ - date = now; - dates = ap_gm_timestr_822(r->pool, now); - ap_table_set(resp_hdrs, "Date", dates); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Added date header"); + date = now; + dates = ap_gm_timestr_822(r->pool, now); + ap_table_set(resp_hdrs, "Date", dates); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Added date header"); } +/* set response_time for HTTP/1.1 age calculations */ + c->resp_time = now; + /* check last-modified date */ if (lmod != BAD_DATE && lmod > date) /* if its in the future, then replace by date */ { - lmod = date; - lmods = dates; - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Last modified is in the future, replacing with now"); + lmod = date; + lmods = dates; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Last modified is in the future, replacing with now"); } /* if the response did not contain the header, then use the cached version */ if (lmod == BAD_DATE && c->fp != NULL) { - lmod = c->lmod; - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Reusing cached last modified"); + lmod = c->lmod; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Reusing cached last modified"); } /* we now need to calculate the expire data for the object. */ - if (expire == NULL && c->fp != NULL) { /* no expiry data sent in response */ - expire = ap_table_get(c->hdrs, "Expires"); - if (expire != NULL) - expc = ap_parseHTTPdate(expire); + if (expire == NULL && c->fp != NULL) { /* no expiry data sent in response */ + expire = ap_table_get(c->hdrs, "Expires"); + if (expire != NULL) + expc = ap_parseHTTPdate(expire); } /* so we now have the expiry date */ /* if no expiry date then @@ -973,141 +1454,178 @@ int ap_proxy_cache_update(cache_req *c, table *resp_hdrs, * else * expire date = now + defaultexpire */ - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Expiry date is %ld", (long)expc); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Expiry date is %ld", (long)expc); if (expc == BAD_DATE) { - if (lmod != BAD_DATE) { - double x = (double) (date - lmod) * conf->cache.lmfactor; - double maxex = conf->cache.maxexpire; - if (x > maxex) - x = maxex; - expc = now + (int) x; - } - else - expc = now + conf->cache.defaultexpire; - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Expiry date calculated %ld", (long)expc); + if (lmod != BAD_DATE) { + double x = (double) (date - lmod) * conf->cache.lmfactor; + double maxex = conf->cache.maxexpire; + if (x > maxex) + x = maxex; + expc = now + (int) x; + } + else + expc = now + conf->cache.defaultexpire; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Expiry date calculated %ld", (long)expc); } /* get the content-length header */ clen = ap_table_get(resp_hdrs, "Content-Length"); if (clen == NULL) - c->len = -1; + c->len = -1; else - c->len = atoi(clen); - - ap_proxy_sec2hex(date, buff); - buff[8] = ' '; - ap_proxy_sec2hex(lmod, buff + 9); - buff[17] = ' '; - ap_proxy_sec2hex(expc, buff + 18); - buff[26] = ' '; - ap_proxy_sec2hex(c->version++, buff + 27); - buff[35] = ' '; - ap_proxy_sec2hex(c->len, buff + 36); - buff[44] = '\n'; - buff[45] = '\0'; - -/* if file not modified */ - if (r->status == HTTP_NOT_MODIFIED) { - if (c->ims != BAD_DATE && lmod != BAD_DATE && lmod <= c->ims) { -/* set any changed headers somehow */ -/* update dates and version, but not content-length */ - if (lmod != c->lmod || expc != c->expire || date != c->date) { - off_t curpos = lseek(ap_bfileno(c->fp, B_WR), 0, SEEK_SET); - if (curpos == -1) - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error seeking on cache file %s", - c->filename); - else if (write(ap_bfileno(c->fp, B_WR), buff, 35) == -1) - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error updating cache file %s", - c->filename); - } - ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Remote document not modified, use local copy"); - /* CHECKME: Is this right? Shouldn't we check IMS again here? */ - return HTTP_NOT_MODIFIED; - } - else { -/* return the whole document */ - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Remote document updated, sending"); - r->status_line = strchr(c->resp_line, ' ') + 1; - r->status = c->status; - if (!r->assbackwards) { - ap_soft_timeout("proxy send headers", r); - ap_proxy_send_headers(r, c->resp_line, c->hdrs); - ap_kill_timeout(r); - } - ap_bsetopt(r->connection->client, BO_BYTECT, &zero); - r->sent_bodyct = 1; - if (!r->header_only) - ap_proxy_send_fb(c->fp, r, NULL); -/* set any changed headers somehow */ -/* update dates and version, but not content-length */ - if (lmod != c->lmod || expc != c->expire || date != c->date) { - off_t curpos = lseek(ap_bfileno(c->fp, B_WR), 0, SEEK_SET); - - if (curpos == -1) - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error seeking on cache file %s", - c->filename); - else if (write(ap_bfileno(c->fp, B_WR), buff, 35) == -1) - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error updating cache file %s", - c->filename); - } - ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR)); - return OK; - } - } -/* new or modified file */ + c->len = atoi(clen); + +/* we have all the header information we need - write it to the cache file */ + c->version++; + ap_proxy_sec2hex(date, buff + 17*(0)); + buff[17*(1)-1] = ' '; + ap_proxy_sec2hex(lmod, buff + 17*(1)); + buff[17*(2)-1] = ' '; + ap_proxy_sec2hex(expc, buff + 17*(2)); + buff[17*(3)-1] = ' '; + ap_proxy_sec2hex(c->version, buff + 17*(3)); + buff[17*(4)-1] = ' '; + ap_proxy_sec2hex(c->req_time, buff + 17*(4)); + buff[17*(5)-1] = ' '; + ap_proxy_sec2hex(c->resp_time, buff + 17*(5)); + buff[17*(6)-1] = ' '; + ap_proxy_sec2hex(c->len, buff + 17*(6)); + buff[17*(7)-1] = '\n'; + buff[17*(7)] = '\0'; + +/* Was the server response a 304 Not Modified? + * + * If it was, it means that we requested a revalidation, and that + * the result of that revalidation was that the object was fresh. + * + */ + +/* if response from server 304 not modified */ + if (r->status == HTTP_NOT_MODIFIED) { + +/* Have the headers changed? + * + * if not - we fulfil the request and return now. + */ + + if (c->hdrs) { + if (!ap_proxy_table_replace(c->hdrs, resp_hdrs)) { + c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), " (with revalidation)", NULL); + return ap_proxy_cache_conditional(r, c, c->fp); + } + } + else + c->hdrs = resp_hdrs; +/* if we get here - the headers have changed. Go through the motions + * of creating a new temporary cache file below, we'll then serve + * the request like we would have in ap_proxy_cache_conditional() + * above, and at the same time we will also rewrite the contents + * to the new temporary file. + */ + } + +/* + * Ok - lets prepare and open the cached file + * + * If a cached file (in c->fp) is already open, then we want to + * update that cached file. Copy the c->fp to c->origfp and open + * up a new one. + * + * If the cached file (in c->fp) is NULL, we must open a new cached + * file from scratch. + * + * The new cache file will be moved to it's final location in the + * directory tree later, overwriting the old cache file should it exist. + */ + +/* if a cache file was already open */ if (c->fp != NULL) { - ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR)); + c->origfp = c->fp; } - c->version = 0; - ap_proxy_sec2hex(0, buff + 27); - buff[35] = ' '; - -/* open temporary file */ -#if !defined(TPF) && !defined(NETWARE) -#define TMPFILESTR "/tmpXXXXXX" - if (conf->cache.root == NULL) - return DECLINED; - c->tempfile = ap_palloc(r->pool, strlen(conf->cache.root) + sizeof(TMPFILESTR)); - strcpy(c->tempfile, conf->cache.root); - strcat(c->tempfile, TMPFILESTR); + + while (1) { +/* create temporary filename */ +#ifndef TPF +#define TMPFILESTR "/tmpXXXXXX" + if (conf->cache.root == NULL) { + c = ap_proxy_cache_error(c); + break; + } + c->tempfile = ap_palloc(r->pool, strlen(conf->cache.root) + sizeof(TMPFILESTR)); + strcpy(c->tempfile, conf->cache.root); + strcat(c->tempfile, TMPFILESTR); #undef TMPFILESTR - p = mktemp(c->tempfile); + p = mktemp(c->tempfile); #else - if (conf->cache.root == NULL) - return DECLINED; - c->tempfile = ap_palloc(r->pool, strlen(conf->cache.root) +1+ L_tmpnam); - strcpy(c->tempfile, conf->cache.root); - strcat(c->tempfile, "/"); - p = tmpnam(NULL); - strcat(c->tempfile, p); + if (conf->cache.root == NULL) { + c = ap_proxy_cache_error(c); + break; + } + c->tempfile = ap_palloc(r->pool, strlen(conf->cache.root) +1+ L_tmpnam); + strcpy(c->tempfile, conf->cache.root); + strcat(c->tempfile, "/"); + p = tmpnam(NULL); + strcat(c->tempfile, p); #endif - if (p == NULL) - return DECLINED; + if (p == NULL) { + c = ap_proxy_cache_error(c); + break; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Create temporary file %s", c->tempfile); + +/* create the new file */ + c->fp = ap_proxy_create_cachefile(r, c->tempfile); + if (NULL == c->fp) { + c = ap_proxy_cache_error(c); + break; + } + +/* write away the cache header and the URL */ + if (ap_bvputs(c->fp, buff, "X-URL: ", c->url, "\n", NULL) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error writing cache file(%s)", c->tempfile); + c = ap_proxy_cache_error(c); + break; + } + +/* get original request headers */ + if (c->req_hdrs) + req_hdrs = ap_copy_table(r->pool, c->req_hdrs); + else + req_hdrs = ap_copy_table(r->pool, r->headers_in); + +/* remove hop-by-hop headers */ + ap_proxy_clear_connection(r->pool, req_hdrs); + +/* save original request headers */ + if (c->req_hdrs) + ap_table_do(ap_proxy_send_hdr_line, c, c->req_hdrs, NULL); + else + ap_table_do(ap_proxy_send_hdr_line, c, r->headers_in, NULL); + if (ap_bputs(CRLF, c->fp) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing request headers terminating CRLF to %s", c->tempfile); + c = ap_proxy_cache_error(c); + break; + } + break; + } - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Create temporary file %s", c->tempfile); +/* Was the server response a 304 Not Modified? + * + * If so, we have some work to do that we didn't do when we first + * checked above. We need to fulfil the request, and we need to + * copy the body from the old object to the new one. + */ + +/* if response from server 304 not modified */ + if (r->status == HTTP_NOT_MODIFIED) { + +/* fulfil the request */ + c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), " (with revalidation)", NULL); + return ap_proxy_cache_conditional(r, c, c->fp); - i = open(c->tempfile, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0622); - if (i == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error creating cache file %s", - c->tempfile); - return DECLINED; - } - ap_note_cleanups_for_fd(r->pool, i); - c->fp = ap_bcreate(r->pool, B_WR); - ap_bpushfd(c->fp, -1, i); - - if (ap_bvputs(c->fp, buff, "X-URL: ", c->url, "\n", NULL) == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error writing cache file(%s)", c->tempfile); - ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR)); - unlink(c->tempfile); - c->fp = NULL; } return DECLINED; } @@ -1117,8 +1635,8 @@ void ap_proxy_cache_tidy(cache_req *c) server_rec *s; long int bc; - if (c == NULL || c->fp == NULL) - return; + if (!c || !c->fp) + return; s = c->req->server; @@ -1129,98 +1647,98 @@ void ap_proxy_cache_tidy(cache_req *c) if (c->len != -1) { /* file lengths don't match; don't cache it */ - if (bc != c->len) { - ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)); /* no need to flush */ - unlink(c->tempfile); - return; - } + if (bc != c->len) { + ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)); /* no need to flush */ + unlink(c->tempfile); + return; + } } /* don't care if aborted, cache it if fully retrieved from host! else if (c->req->connection->aborted) { - ap_pclosef(c->req->pool, c->fp->fd); / no need to flush / - unlink(c->tempfile); - return; + ap_pclosef(c->req->pool, c->fp->fd); / no need to flush / + unlink(c->tempfile); + return; } */ else { /* update content-length of file */ - char buff[9]; - off_t curpos; - - c->len = bc; - ap_bflush(c->fp); - ap_proxy_sec2hex(c->len, buff); - curpos = lseek(ap_bfileno(c->fp, B_WR), 36, SEEK_SET); - if (curpos == -1) - ap_log_error(APLOG_MARK, APLOG_ERR, s, - "proxy: error seeking on cache file %s", c->tempfile); - else if (write(ap_bfileno(c->fp, B_WR), buff, 8) == -1) - ap_log_error(APLOG_MARK, APLOG_ERR, s, - "proxy: error updating cache file %s", c->tempfile); + char buff[17]; + off_t curpos; + + c->len = bc; + ap_bflush(c->fp); + ap_proxy_sec2hex(c->len, buff); + curpos = lseek(ap_bfileno(c->fp, B_WR), 17*6, SEEK_SET); + if (curpos == -1) + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "proxy: error seeking on cache file %s", c->tempfile); + else if (write(ap_bfileno(c->fp, B_WR), buff, sizeof(buff) - 1) == -1) + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "proxy: error updating cache file %s", c->tempfile); } if (ap_bflush(c->fp) == -1) { - ap_log_error(APLOG_MARK, APLOG_ERR, s, - "proxy: error writing to cache file %s", - c->tempfile); - ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)); - unlink(c->tempfile); - return; + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "proxy: error writing to cache file %s", + c->tempfile); + ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)); + unlink(c->tempfile); + return; } if (ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)) == -1) { - ap_log_error(APLOG_MARK, APLOG_ERR, s, - "proxy: error closing cache file %s", c->tempfile); - unlink(c->tempfile); - return; + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "proxy: error closing cache file %s", c->tempfile); + unlink(c->tempfile); + return; } if (unlink(c->filename) == -1 && errno != ENOENT) { - ap_log_error(APLOG_MARK, APLOG_ERR, s, - "proxy: error deleting old cache file %s", - c->tempfile); + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "proxy: error deleting old cache file %s", + c->tempfile); } else { - char *p; - proxy_server_conf *conf = - (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module); - - for (p = c->filename + strlen(conf->cache.root) + 1;;) { - p = strchr(p, '/'); - if (!p) - break; - *p = '\0'; + char *p; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module); + + for (p = c->filename + strlen(conf->cache.root) + 1;;) { + p = strchr(p, '/'); + if (!p) + break; + *p = '\0'; #if defined(WIN32) || defined(NETWARE) - if (mkdir(c->filename) < 0 && errno != EEXIST) + if (mkdir(c->filename) < 0 && errno != EEXIST) #elif defined(__TANDEM) - if (mkdir(c->filename, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST) + if (mkdir(c->filename, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST) #else - if (mkdir(c->filename, S_IREAD | S_IWRITE | S_IEXEC) < 0 && errno != EEXIST) + if (mkdir(c->filename, S_IREAD | S_IWRITE | S_IEXEC) < 0 && errno != EEXIST) #endif /* WIN32 */ - ap_log_error(APLOG_MARK, APLOG_ERR, s, - "proxy: error creating cache directory %s", - c->filename); - *p = '/'; - ++p; - } + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "proxy: error creating cache directory %s", + c->filename); + *p = '/'; + ++p; + } #if defined(OS2) || defined(WIN32) || defined(NETWARE) || defined(MPE) - /* Under OS/2 use rename. */ - if (rename(c->tempfile, c->filename) == -1) - ap_log_error(APLOG_MARK, APLOG_ERR, s, - "proxy: error renaming cache file %s to %s", - c->tempfile, c->filename); + /* Under OS/2 use rename. */ + if (rename(c->tempfile, c->filename) == -1) + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "proxy: error renaming cache file %s to %s", + c->tempfile, c->filename); } #else - if (link(c->tempfile, c->filename) == -1) - ap_log_error(APLOG_MARK, APLOG_ERR, s, - "proxy: error linking cache file %s to %s", - c->tempfile, c->filename); + if (link(c->tempfile, c->filename) == -1) + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "proxy: error linking cache file %s to %s", + c->tempfile, c->filename); } if (unlink(c->tempfile) == -1) - ap_log_error(APLOG_MARK, APLOG_ERR, s, - "proxy: error deleting temp file %s", c->tempfile); + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "proxy: error deleting temp file %s", c->tempfile); #endif } diff --git a/src/modules/proxy/proxy_connect.c b/src/modules/proxy/proxy_connect.c index b40db425964..d10b84300dd 100644 --- a/src/modules/proxy/proxy_connect.c +++ b/src/modules/proxy/proxy_connect.c @@ -63,7 +63,7 @@ #include "http_main.h" #ifdef HAVE_BSTRING_H -#include /* for IRIX, FD_SET calls bzero() */ +#include /* for IRIX, FD_SET calls bzero() */ #endif /* @@ -103,15 +103,15 @@ allowed_port(proxy_server_conf *conf, int port) int *list = (int *) conf->allowed_connect_ports->elts; for(i = 0; i < conf->allowed_connect_ports->nelts; i++) { - if(port == list[i]) - return 1; + if(port == list[i]) + return 1; } return 0; } int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, - const char *proxyhost, int proxyport) + const char *proxyhost, int proxyport) { struct sockaddr_in server; struct in_addr destaddr; @@ -126,7 +126,7 @@ int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, void *sconf = r->server->module_config; proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); - struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; + struct noproxy_entry *npent = (struct noproxy_entry *)conf->noproxies->elts; memset(&server, '\0', sizeof(server)); server.sin_family = AF_INET; @@ -136,158 +136,157 @@ int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url, host = url; p = strchr(url, ':'); if (p == NULL) - port = DEFAULT_HTTPS_PORT; + port = DEFAULT_HTTPS_PORT; else { - port = atoi(p + 1); - *p = '\0'; + port = atoi(p + 1); + *p = '\0'; } /* check if ProxyBlock directive on this host */ destaddr.s_addr = ap_inet_addr(host); for (i = 0; i < conf->noproxies->nelts; i++) { - if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL) - || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*') - return ap_proxyerror(r, HTTP_FORBIDDEN, - "Connect to remote machine blocked"); + if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL) + || destaddr.s_addr == npent[i].addr.s_addr + || npent[i].name[0] == '*') + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); } /* Check if it is an allowed port */ if (conf->allowed_connect_ports->nelts == 0) { - /* Default setting if not overridden by AllowCONNECT */ - switch (port) { - case DEFAULT_HTTPS_PORT: - case DEFAULT_SNEWS_PORT: - break; - default: - return HTTP_FORBIDDEN; - } + /* Default setting if not overridden by AllowCONNECT */ + switch (port) { + case DEFAULT_HTTPS_PORT: + case DEFAULT_SNEWS_PORT: + break; + default: + return HTTP_FORBIDDEN; + } } else if(!allowed_port(conf, port)) - return HTTP_FORBIDDEN; + return HTTP_FORBIDDEN; if (proxyhost) { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "CONNECT to remote proxy %s on port %d", proxyhost, proxyport); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, + "CONNECT to remote proxy %s on port %d", proxyhost, proxyport); } else { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "CONNECT to %s on port %d", host, port); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, + "CONNECT to %s on port %d", host, port); } server.sin_port = (proxyport ? htons(proxyport) : htons(port)); err = ap_proxy_host2addr(proxyhost ? proxyhost : host, &server_hp); if (err != NULL) - return ap_proxyerror(r, - proxyhost ? HTTP_BAD_GATEWAY : HTTP_INTERNAL_SERVER_ERROR, - err); + return ap_proxyerror(r, + proxyhost ? HTTP_BAD_GATEWAY : HTTP_INTERNAL_SERVER_ERROR, err); sock = ap_psocket(r->pool, PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error creating socket"); - return HTTP_INTERNAL_SERVER_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "proxy: error creating socket"); + return HTTP_INTERNAL_SERVER_ERROR; } #ifdef CHECK_FD_SETSIZE if (sock >= FD_SETSIZE) { - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r->server, - "proxy_connect_handler: filedescriptor (%u) " - "larger than FD_SETSIZE (%u) " - "found, you probably need to rebuild Apache with a " - "larger FD_SETSIZE", sock, FD_SETSIZE); - ap_pclosesocket(r->pool, sock); - return HTTP_INTERNAL_SERVER_ERROR; + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "proxy_connect_handler: filedescriptor (%u) " + "larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", sock, FD_SETSIZE); + ap_pclosesocket(r->pool, sock); + return HTTP_INTERNAL_SERVER_ERROR; } #endif j = 0; while (server_hp.h_addr_list[j] != NULL) { - memcpy(&server.sin_addr, server_hp.h_addr_list[j], - sizeof(struct in_addr)); - i = ap_proxy_doconnect(sock, &server, r); - if (i == 0) - break; - j++; + memcpy(&server.sin_addr, server_hp.h_addr_list[j], + sizeof(struct in_addr)); + i = ap_proxy_doconnect(sock, &server, r); + if (i == 0) + break; + j++; } if (i == -1) { - ap_pclosesocket(r->pool, sock); - return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, ap_pstrcat(r->pool, - "Could not connect to remote machine:
", - strerror(errno), NULL)); + ap_pclosesocket(r->pool, sock); + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, ap_pstrcat(r->pool, + "Could not connect to remote machine:
", strerror(errno), NULL)); } /* If we are connecting through a remote proxy, we need to pass * the CONNECT request on to it. */ if (proxyport) { - /* FIXME: We should not be calling write() directly, but we currently - * have no alternative. Error checking ignored. Also, we force - * a HTTP/1.0 request to keep things simple. - */ - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Sending the CONNECT request to the remote proxy"); - ap_snprintf(buffer, sizeof(buffer), "CONNECT %s HTTP/1.0" CRLF, - r->uri); -#ifdef CHARSET_EBCDIC - /* We are writing to the pure socket, - * so we must convert our string to ASCII first - */ - ebcdic2ascii(buffer, buffer, strlen(buffer)); -#endif - send(sock, buffer, strlen(buffer),0); - ap_snprintf(buffer, sizeof(buffer), - "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); -#ifdef CHARSET_EBCDIC - ebcdic2ascii(buffer, buffer, strlen(buffer)); -#endif - send(sock, buffer, strlen(buffer),0); + /* FIXME: We should not be calling write() directly, but we currently + * have no alternative. Error checking ignored. Also, we force + * a HTTP/1.0 request to keep things simple. + */ + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, + "Sending the CONNECT request to the remote proxy"); + ap_snprintf(buffer, sizeof(buffer), "CONNECT %s HTTP/1.0" CRLF, r->uri); + send(sock, buffer, strlen(buffer),0); + ap_snprintf(buffer, sizeof(buffer), + "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); + send(sock, buffer, strlen(buffer),0); } else { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Returning 200 OK Status"); - ap_rvputs(r, "HTTP/1.0 200 Connection established" CRLF, NULL); - ap_rvputs(r, "Proxy-agent: ", ap_get_server_version(), CRLF CRLF, NULL); - ap_bflush(r->connection->client); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, + "Returning 200 OK Status"); + ap_rvputs(r, "HTTP/1.0 200 Connection established" CRLF, NULL); + ap_rvputs(r, "Proxy-agent: ", ap_get_server_version(), CRLF CRLF, NULL); + ap_bflush(r->connection->client); } - while (1) { /* Infinite loop until error (one side closes the connection) */ - FD_ZERO(&fds); - FD_SET(sock, &fds); - FD_SET(ap_bfileno(r->connection->client, B_WR), &fds); + while (1) { /* Infinite loop until error (one side closes the connection) */ + FD_ZERO(&fds); + FD_SET(sock, &fds); + FD_SET(ap_bfileno(r->connection->client, B_WR), &fds); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Going to sleep (select)"); - i = ap_select((ap_bfileno(r->connection->client, B_WR) > sock ? - ap_bfileno(r->connection->client, B_WR) + 1 : - sock + 1), &fds, NULL, NULL, NULL); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Woke from select(), i=%d", i); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, + "Going to sleep (select)"); + i = ap_select((ap_bfileno(r->connection->client, B_WR) > sock ? + ap_bfileno(r->connection->client, B_WR) + 1 : + sock + 1), &fds, NULL, NULL, NULL); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, + "Woke from select(), i=%d", i); - if (i) { - if (FD_ISSET(sock, &fds)) { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "sock was set"); - if ((nbytes = recv(sock, buffer, HUGE_STRING_LEN,0)) != 0) { - if (nbytes == -1) - break; - if (send(ap_bfileno(r->connection->client, B_WR), buffer, nbytes,0) == EOF) - break; - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Wrote %d bytes to client", nbytes); - } - else - break; - } - else if (FD_ISSET(ap_bfileno(r->connection->client, B_WR), &fds)) { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "client->fd was set"); - if ((nbytes = recv(ap_bfileno(r->connection->client, B_WR), buffer, - HUGE_STRING_LEN, 0)) != 0) { - if (nbytes == -1) - break; - if (send(sock, buffer, nbytes, 0) == EOF) - break; - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Wrote %d bytes to server", nbytes); - } - else - break; - } - else - break; /* Must be done waiting */ - } - else - break; + if (i) { + if (FD_ISSET(sock, &fds)) { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, + "sock was set"); + if ((nbytes = recv(sock, buffer, HUGE_STRING_LEN,0)) != 0) { + if (nbytes == -1) + break; + if (send(ap_bfileno(r->connection->client, B_WR), buffer, + nbytes,0) == EOF) + break; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, + r->server, "Wrote %d bytes to client", nbytes); + } + else + break; + } + else if (FD_ISSET(ap_bfileno(r->connection->client, B_WR), &fds)) { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, + "client->fd was set"); + if ((nbytes = recv(ap_bfileno(r->connection->client, B_WR), + buffer, HUGE_STRING_LEN, 0)) != 0) { + if (nbytes == -1) + break; + if (send(sock, buffer, nbytes, 0) == EOF) + break; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, + r->server, "Wrote %d bytes to server", nbytes); + } + else + break; + } + else + break; /* Must be done waiting */ + } + else + break; } ap_pclosesocket(r->pool, sock); diff --git a/src/modules/proxy/proxy_ftp.c b/src/modules/proxy/proxy_ftp.c index 3c674a174b8..036ebc31bb2 100644 --- a/src/modules/proxy/proxy_ftp.c +++ b/src/modules/proxy/proxy_ftp.c @@ -73,15 +73,15 @@ static int decodeenc(char *x) int i, j, ch; if (x[0] == '\0') - return 0; /* special case for no characters */ + return 0; /* special case for no characters */ for (i = 0, j = 0; x[i] != '\0'; i++, j++) { /* decode it if not already done */ - ch = x[i]; - if (ch == '%' && ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) { - ch = ap_proxy_hex2c(&x[i + 1]); - i += 2; - } - x[j] = ch; + ch = x[i]; + if (ch == '%' && ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) { + ch = ap_proxy_hex2c(&x[i + 1]); + i += 2; + } + x[j] = ch; } x[j] = '\0'; return j; @@ -96,13 +96,13 @@ static int ftp_check_string(const char *x) int i, ch; for (i = 0; x[i] != '\0'; i++) { - ch = x[i]; - if (ch == '%' && ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) { - ch = ap_proxy_hex2c(&x[i + 1]); - i += 2; - } - if (ch == CR || ch == LF || (OS_ASC(ch) & 0x80)) - return 0; + ch = x[i]; + if (ch == '%' && ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) { + ch = ap_proxy_hex2c(&x[i + 1]); + i += 2; + } + if (ch == CR || ch == LF || (OS_ASC(ch) & 0x80)) + return 0; } return 1; } @@ -120,11 +120,11 @@ int ap_proxy_ftp_canon(request_rec *r, char *url) port = DEFAULT_FTP_PORT; err = ap_proxy_canon_netloc(p, &url, &user, &password, &host, &port); if (err) - return HTTP_BAD_REQUEST; + return HTTP_BAD_REQUEST; if (user != NULL && !ftp_check_string(user)) - return HTTP_BAD_REQUEST; + return HTTP_BAD_REQUEST; if (password != NULL && !ftp_check_string(password)) - return HTTP_BAD_REQUEST; + return HTTP_BAD_REQUEST; /* now parse path/parameters args, according to rfc1738 */ /* N.B. if this isn't a true proxy request, then the URL path @@ -134,49 +134,49 @@ int ap_proxy_ftp_canon(request_rec *r, char *url) */ strp = strchr(url, ';'); if (strp != NULL) { - *(strp++) = '\0'; - parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm, - r->proxyreq); - if (parms == NULL) - return HTTP_BAD_REQUEST; + *(strp++) = '\0'; + parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm, + r->proxyreq); + if (parms == NULL) + return HTTP_BAD_REQUEST; } else - parms = ""; + parms = ""; path = ap_proxy_canonenc(p, url, strlen(url), enc_path, r->proxyreq); if (path == NULL) - return HTTP_BAD_REQUEST; + return HTTP_BAD_REQUEST; if (!ftp_check_string(path)) - return HTTP_BAD_REQUEST; + return HTTP_BAD_REQUEST; if (r->proxyreq == NOT_PROXY && r->args != NULL) { - if (strp != NULL) { - strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, STD_PROXY); - if (strp == NULL) - return HTTP_BAD_REQUEST; - parms = ap_pstrcat(p, parms, "?", strp, NULL); - } - else { - strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, STD_PROXY); - if (strp == NULL) - return HTTP_BAD_REQUEST; - path = ap_pstrcat(p, path, "?", strp, NULL); - } - r->args = NULL; + if (strp != NULL) { + strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, STD_PROXY); + if (strp == NULL) + return HTTP_BAD_REQUEST; + parms = ap_pstrcat(p, parms, "?", strp, NULL); + } + else { + strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, STD_PROXY); + if (strp == NULL) + return HTTP_BAD_REQUEST; + path = ap_pstrcat(p, path, "?", strp, NULL); + } + r->args = NULL; } /* now, rebuild URL */ if (port != DEFAULT_FTP_PORT) - ap_snprintf(sport, sizeof(sport), ":%d", port); + ap_snprintf(sport, sizeof(sport), ":%d", port); else - sport[0] = '\0'; + sport[0] = '\0'; r->filename = ap_pstrcat(p, "proxy:ftp://", (user != NULL) ? user : "", - (password != NULL) ? ":" : "", - (password != NULL) ? password : "", - (user != NULL) ? "@" : "", host, sport, "/", path, - (parms[0] != '\0') ? ";" : "", parms, NULL); + (password != NULL) ? ":" : "", + (password != NULL) ? password : "", + (user != NULL) ? "@" : "", host, sport, "/", path, + (parms[0] != '\0') ? ";" : "", parms, NULL); return OK; } @@ -192,30 +192,30 @@ static int ftp_getrc(BUFF *f) len = ap_bgets(linebuff, sizeof linebuff, f); if (len == -1) - return -1; + return -1; /* check format */ if (len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) || - !ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-')) - status = 0; + !ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-')) + status = 0; else - status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0'; + status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0'; if (linebuff[len - 1] != '\n') { - (void)ap_bskiplf(f); + (void)ap_bskiplf(f); } /* skip continuation lines */ if (linebuff[3] == '-') { - memcpy(buff, linebuff, 3); - buff[3] = ' '; - do { - len = ap_bgets(linebuff, sizeof linebuff, f); - if (len == -1) - return -1; - if (linebuff[len - 1] != '\n') { - (void)ap_bskiplf(f); - } - } while (memcmp(linebuff, buff, 4) != 0); + memcpy(buff, linebuff, 3); + buff[3] = ' '; + do { + len = ap_bgets(linebuff, sizeof linebuff, f); + if (len == -1) + return -1; + if (linebuff[len - 1] != '\n') { + (void)ap_bskiplf(f); + } + } while (memcmp(linebuff, buff, 4) != 0); } return status; @@ -230,34 +230,34 @@ static int ftp_getrc_msg(BUFF *f, char *msgbuf, int msglen) int len, status; char linebuff[100], buff[5]; char *mb = msgbuf, - *me = &msgbuf[msglen]; + *me = &msgbuf[msglen]; len = ap_bgets(linebuff, sizeof linebuff, f); if (len == -1) - return -1; + return -1; if (len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) || - !ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-')) - status = 0; + !ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-')) + status = 0; else - status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0'; + status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0'; mb = ap_cpystrn(mb, linebuff+4, me - mb); if (linebuff[len - 1] != '\n') - (void)ap_bskiplf(f); + (void)ap_bskiplf(f); if (linebuff[3] == '-') { - memcpy(buff, linebuff, 3); - buff[3] = ' '; - do { - len = ap_bgets(linebuff, sizeof linebuff, f); - if (len == -1) - return -1; - if (linebuff[len - 1] != '\n') { - (void)ap_bskiplf(f); - } - mb = ap_cpystrn(mb, linebuff+4, me - mb); - } while (memcmp(linebuff, buff, 4) != 0); + memcpy(buff, linebuff, 3); + buff[3] = ' '; + do { + len = ap_bgets(linebuff, sizeof linebuff, f); + if (len == -1) + return -1; + if (linebuff[len - 1] != '\n') { + (void)ap_bskiplf(f); + } + mb = ap_cpystrn(mb, linebuff+4, me - mb); + } while (memcmp(linebuff, buff, 4) != 0); } return status; } @@ -284,118 +284,118 @@ static long int send_dir(BUFF *f, request_rec *r, cache_req *c, char *cwd) /* Copy path, strip (all except the last) trailing slashes */ path = dir = ap_pstrcat(r->pool, path, "/", NULL); while ((n = strlen(path)) > 1 && path[n-1] == '/' && path[n-2] == '/') - path[n-1] = '\0'; + path[n-1] = '\0'; /* print "ftp://host/" */ n = ap_snprintf(buf, sizeof(buf), DOCTYPE_HTML_3_2 - "%s%s\n" - "\n" - "

Directory of " - "%s/", - site, path, site, path, site); + "%s%s\n" + "\n" + "

Directory of " + "%s/", + site, path, site, path, site); total_bytes_sent += ap_proxy_bputs2(buf, con->client, c); while ((dir = strchr(dir+1, '/')) != NULL) { - *dir = '\0'; - if ((reldir = strrchr(path+1, '/'))==NULL) - reldir = path+1; - else - ++reldir; - /* print "path/" component */ - ap_snprintf(buf, sizeof(buf), "%s/", path+1, reldir); - total_bytes_sent += ap_proxy_bputs2(buf, con->client, c); - *dir = '/'; + *dir = '\0'; + if ((reldir = strrchr(path+1, '/'))==NULL) + reldir = path+1; + else + ++reldir; + /* print "path/" component */ + ap_snprintf(buf, sizeof(buf), "%s/", path+1, reldir); + total_bytes_sent += ap_proxy_bputs2(buf, con->client, c); + *dir = '/'; } /* If the caller has determined the current directory, and it differs */ /* from what the client requested, then show the real name */ if (cwd == NULL || strncmp (cwd, path, strlen(cwd)) == 0) { - ap_snprintf(buf, sizeof(buf), "

\n
");
+        ap_snprintf(buf, sizeof(buf), "\n
");
     } else {
-	ap_snprintf(buf, sizeof(buf), "\n(%s)\n
", cwd);
+        ap_snprintf(buf, sizeof(buf), "\n(%s)\n
", cwd);
     }
     total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
 
     while (!con->aborted) {
-	n = ap_bgets(buf, sizeof buf, f);
-	if (n == -1) {		/* input error */
-	    if (c != NULL) {
-		ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
-		    "proxy: error reading from %s", c->url);
-		c = ap_proxy_cache_error(c);
-	    }
-	    break;
-	}
-	if (n == 0)
-	    break;		/* EOF */
-	if (buf[0] == 'l' && (filename=strstr(buf, " -> ")) != NULL) {
-	    char *link_ptr = filename;
-
-	    do {
-		filename--;
-	    } while (filename[0] != ' ');
-	    *(filename++) = '\0';
-	    *(link_ptr++) = '\0';
-	    if ((n = strlen(link_ptr)) > 1 && link_ptr[n - 1] == '\n')
-	      link_ptr[n - 1] = '\0';
-	    ap_snprintf(buf2, sizeof(buf2), "%s %s %s\n", buf, filename, filename, link_ptr);
-	    ap_cpystrn(buf, buf2, sizeof(buf));
-	    n = strlen(buf);
-	}
-	else if (buf[0] == 'd' || buf[0] == '-' || buf[0] == 'l' || ap_isdigit(buf[0])) {
-	    if (ap_isdigit(buf[0])) {	/* handle DOS dir */
-		searchptr = strchr(buf, '<');
-		if (searchptr != NULL)
-		    *searchptr = '[';
-		searchptr = strchr(buf, '>');
-		if (searchptr != NULL)
-		    *searchptr = ']';
-	    }
-
-	    filename = strrchr(buf, ' ');
-	    *(filename++) = 0;
-	    filename[strlen(filename) - 1] = 0;
-
-	    /* handle filenames with spaces in 'em */
-	    if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
-		firstfile = 0;
-		searchidx = filename - buf;
-	    }
-	    else if (searchidx != 0 && buf[searchidx] != 0) {
-		*(--filename) = ' ';
-		buf[searchidx - 1] = 0;
-		filename = &buf[searchidx];
-	    }
-
-	    /* Special handling for '.' and '..' */
-	    if (!strcmp(filename, ".") || !strcmp(filename, "..") || buf[0] == 'd') {
-		ap_snprintf(buf2, sizeof(buf2), "%s %s\n",
-		    buf, filename, filename);
-	    }
-	    else {
-		ap_snprintf(buf2, sizeof(buf2), "%s %s\n", buf, filename, filename);
-	    }
-	    ap_cpystrn(buf, buf2, sizeof(buf));
-	    n = strlen(buf);
-	}
-
-	o = 0;
-	total_bytes_sent += n;
-
-	if (c != NULL && c->fp && ap_bwrite(c->fp, buf, n) != n) {
-	    ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
-		"proxy: error writing to %s", c->tempfile);
-	    c = ap_proxy_cache_error(c);
-	}
-
-	while (n && !r->connection->aborted) {
-	    w = ap_bwrite(con->client, &buf[o], n);
-	    if (w <= 0)
-		break;
-	    ap_reset_timeout(r);	/* reset timeout after successfule write */
-	    n -= w;
-	    o += w;
-	}
+        n = ap_bgets(buf, sizeof buf, f);
+        if (n == -1) {          /* input error */
+            if (c != NULL) {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
+                    "proxy: error reading from %s", c->url);
+                c = ap_proxy_cache_error(c);
+            }
+            break;
+        }
+        if (n == 0)
+            break;                /* EOF */
+        if (buf[0] == 'l' && (filename=strstr(buf, " -> ")) != NULL) {
+            char *link_ptr = filename;
+
+            do {
+                filename--;
+            } while (filename[0] != ' ');
+            *(filename++) = '\0';
+            *(link_ptr++) = '\0';
+            if ((n = strlen(link_ptr)) > 1 && link_ptr[n - 1] == '\n')
+              link_ptr[n - 1] = '\0';
+            ap_snprintf(buf2, sizeof(buf2), "%s %s %s\n", buf, filename, filename, link_ptr);
+            ap_cpystrn(buf, buf2, sizeof(buf));
+            n = strlen(buf);
+        }
+        else if (buf[0] == 'd' || buf[0] == '-' || buf[0] == 'l' || ap_isdigit(buf[0])) {
+            if (ap_isdigit(buf[0])) {   /* handle DOS dir */
+                searchptr = strchr(buf, '<');
+                if (searchptr != NULL)
+                    *searchptr = '[';
+                searchptr = strchr(buf, '>');
+                if (searchptr != NULL)
+                    *searchptr = ']';
+            }
+
+            filename = strrchr(buf, ' ');
+            *(filename++) = 0;
+            filename[strlen(filename) - 1] = 0;
+
+            /* handle filenames with spaces in 'em */
+            if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
+                firstfile = 0;
+                searchidx = filename - buf;
+            }
+            else if (searchidx != 0 && buf[searchidx] != 0) {
+                *(--filename) = ' ';
+                buf[searchidx - 1] = 0;
+                filename = &buf[searchidx];
+            }
+
+            /* Special handling for '.' and '..' */
+            if (!strcmp(filename, ".") || !strcmp(filename, "..") || buf[0] == 'd') {
+                ap_snprintf(buf2, sizeof(buf2), "%s %s\n",
+                    buf, filename, filename);
+            }
+            else {
+                ap_snprintf(buf2, sizeof(buf2), "%s %s\n", buf, filename, filename);
+            }
+            ap_cpystrn(buf, buf2, sizeof(buf));
+            n = strlen(buf);
+        }
+
+        o = 0;
+        total_bytes_sent += n;
+
+        if (c != NULL && c->fp && ap_bwrite(c->fp, buf, n) != n) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
+                "proxy: error writing to %s", c->tempfile);
+            c = ap_proxy_cache_error(c);
+        }
+
+        while (n && !r->connection->aborted) {
+            w = ap_bwrite(con->client, &buf[o], n);
+            if (w <= 0)
+                break;
+            ap_reset_timeout(r); /* reset timeout after successfule write */
+            n -= w;
+            o += w;
+        }
     }
 
     total_bytes_sent += ap_proxy_bputs2("

\n", con->client, c); @@ -421,16 +421,16 @@ static int ftp_unauthorized (request_rec *r, int log_it) * (log username/password guessing attempts) */ if (log_it) - ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r, - "proxy: missing or failed auth to %s", - ap_unparse_uri_components(r->pool, - &r->parsed_uri, UNP_OMITPATHINFO)); + ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r, + "proxy: missing or failed auth to %s", + ap_unparse_uri_components(r->pool, + &r->parsed_uri, UNP_OMITPATHINFO)); ap_table_setn(r->err_headers_out, "WWW-Authenticate", ap_pstrcat(r->pool, "Basic realm=\"", - ap_unparse_uri_components(r->pool, &r->parsed_uri, - UNP_OMITPASSWORD|UNP_OMITPATHINFO), - "\"", NULL)); + ap_unparse_uri_components(r->pool, &r->parsed_uri, + UNP_OMITPASSWORD|UNP_OMITPATHINFO), + "\"", NULL)); return HTTP_UNAUTHORIZED; } @@ -459,9 +459,7 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) BUFF *data = NULL; pool *p = r->pool; int one = 1; - const long int zero = 0L; NET_SIZE_T clen; - struct tbl_do_args tdo; void *sconf = r->server->module_config; proxy_server_conf *conf = @@ -485,14 +483,14 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) /* we only support GET and HEAD */ if (r->method_number != M_GET) - return HTTP_NOT_IMPLEMENTED; + return HTTP_NOT_IMPLEMENTED; /* We break the URL into host, port, path-search */ host = r->parsed_uri.hostname; port = (r->parsed_uri.port != 0) - ? r->parsed_uri.port - : ap_default_port_for_request(r); + ? r->parsed_uri.port + : ap_default_port_for_request(r); path = ap_pstrdup(p, r->parsed_uri.path); path = (path != NULL && path[0] != '\0') ? &path[1] : ""; @@ -505,33 +503,33 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) * But chances are still smaller that the URL is logged regularly. */ if ((password = ap_table_get(r->headers_in, "Authorization")) != NULL - && strcasecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0 - && (password = ap_pbase64decode(r->pool, password))[0] != ':') { - /* Note that this allocation has to be made from r->connection->pool - * because it has the lifetime of the connection. The other allocations - * are temporary and can be tossed away any time. - */ - user = ap_getword_nulls (r->connection->pool, &password, ':'); - r->connection->ap_auth_type = "Basic"; - r->connection->user = r->parsed_uri.user = user; - nocache = 1; /* This resource only accessible with username/password */ + && strcasecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0 + && (password = ap_pbase64decode(r->pool, password))[0] != ':') { + /* Note that this allocation has to be made from r->connection->pool + * because it has the lifetime of the connection. The other allocations + * are temporary and can be tossed away any time. + */ + user = ap_getword_nulls (r->connection->pool, &password, ':'); + r->connection->ap_auth_type = "Basic"; + r->connection->user = r->parsed_uri.user = user; + nocache = 1; /* This resource only accessible with username/password */ } else if ((user = r->parsed_uri.user) != NULL) { - user = ap_pstrdup(p, user); - decodeenc(user); - if ((password = r->parsed_uri.password) != NULL) { - char *tmp = ap_pstrdup(p, password); - decodeenc(tmp); - password = tmp; - } - nocache = 1; /* This resource only accessible with username/password */ + user = ap_pstrdup(p, user); + decodeenc(user); + if ((password = r->parsed_uri.password) != NULL) { + char *tmp = ap_pstrdup(p, password); + decodeenc(tmp); + password = tmp; + } + nocache = 1; /* This resource only accessible with username/password */ } else { - user = "anonymous"; - password = "apache_proxy@"; + user = "anonymous"; + password = "apache_proxy@"; } -/* check if ProxyBlock directive on this host */ + /* check if ProxyBlock directive on this host */ destaddr.s_addr = ap_inet_addr(host); for (i = 0; i < conf->noproxies->nelts; i++) { if (destaddr.s_addr == npent[i].addr.s_addr || @@ -545,71 +543,74 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) parms = strchr(path, ';'); if (parms != NULL) - *(parms++) = '\0'; + *(parms++) = '\0'; memset(&server, 0, sizeof(struct sockaddr_in)); server.sin_family = AF_INET; server.sin_port = htons(port); err = ap_proxy_host2addr(host, &server_hp); if (err != NULL) - return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err); + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err); sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error creating socket"); - return HTTP_INTERNAL_SERVER_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error creating socket"); + return HTTP_INTERNAL_SERVER_ERROR; } #if !defined(TPF) && !defined(BEOS) if (conf->recv_buffer_size > 0 - && setsockopt(sock, SOL_SOCKET, SO_RCVBUF, - (const char *) &conf->recv_buffer_size, sizeof(int)) - == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); + && setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + (const char *) &conf->recv_buffer_size, sizeof(int)) + == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); } #endif if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &one, - sizeof(one)) == -1) { + sizeof(one)) == -1) { #ifndef _OSD_POSIX /* BS2000 has this option "always on" */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error setting reuseaddr option: setsockopt(SO_REUSEADDR)"); - ap_pclosesocket(p, sock); - return HTTP_INTERNAL_SERVER_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error setting reuseaddr option: setsockopt(SO_REUSEADDR)"); + ap_pclosesocket(p, sock); + return HTTP_INTERNAL_SERVER_ERROR; #endif /*_OSD_POSIX*/ } #ifdef SINIX_D_RESOLVER_BUG { - struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list; - - for (; ip_addr->s_addr != 0; ++ip_addr) { - memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr)); - i = ap_proxy_doconnect(sock, &server, r); - if (i == 0) - break; - } + struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list; + + for (; ip_addr->s_addr != 0; ++ip_addr) { + memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr)); + i = ap_proxy_doconnect(sock, &server, r); + if (i == 0) + break; + } } #else j = 0; while (server_hp.h_addr_list[j] != NULL) { - memcpy(&server.sin_addr, server_hp.h_addr_list[j], - sizeof(struct in_addr)); - i = ap_proxy_doconnect(sock, &server, r); - if (i == 0) - break; - j++; + memcpy(&server.sin_addr, server_hp.h_addr_list[j], + sizeof(struct in_addr)); + i = ap_proxy_doconnect(sock, &server, r); + if (i == 0) + break; + j++; } #endif if (i == -1) { - ap_pclosesocket(p, sock); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool, - "Could not connect to remote machine: ", - strerror(errno), NULL)); + ap_pclosesocket(p, sock); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool, + "Could not connect to remote machine: ", + strerror(errno), NULL)); } + /* record request_time for HTTP/1.1 age calculation */ + c->req_time = time(NULL); + f = ap_bcreate(p, B_RDWR | B_SOCKET); ap_bpushfd(f, sock, sock); /* shouldn't we implement telnet control options here? */ @@ -618,7 +619,7 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1); #endif /*CHARSET_EBCDIC*/ -/* possible results: */ + /* possible results: */ /* 120 Service ready in nnn minutes. */ /* 220 Service ready for new user. */ /* 421 Service not available, closing control connection. */ @@ -626,40 +627,40 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) i = ftp_getrc_msg(f, resp, sizeof resp); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i); if (i == -1) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); } #if 0 if (i == 120) { - ap_kill_timeout(r); - /* RFC2068 states: - * 14.38 Retry-After - * - * The Retry-After response-header field can be used with a 503 (Service - * Unavailable) response to indicate how long the service is expected to - * be unavailable to the requesting client. The value of this field can - * be either an HTTP-date or an integer number of seconds (in decimal) - * after the time of the response. - * Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds ) - */ - ap_set_header("Retry-After", ap_psprintf(p, "%u", 60*wait_mins); - return ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, resp); + ap_kill_timeout(r); + /* RFC2068 states: + * 14.38 Retry-After + * + * The Retry-After response-header field can be used with a 503 (Service + * Unavailable) response to indicate how long the service is expected to + * be unavailable to the requesting client. The value of this field can + * be either an HTTP-date or an integer number of seconds (in decimal) + * after the time of the response. + * Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds ) + */ + ap_set_header("Retry-After", ap_psprintf(p, "%u", 60*wait_mins); + return ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, resp); } #endif if (i != 220) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, resp); + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, resp); } ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: connected."); ap_bvputs(f, "USER ", user, CRLF, NULL); - ap_bflush(f); /* capture any errors */ + ap_bflush(f); /* capture any errors */ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: USER %s", user); -/* possible results; 230, 331, 332, 421, 500, 501, 530 */ -/* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */ + /* possible results; 230, 331, 332, 421, 500, 501, 530 */ + /* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */ /* 230 User logged in, proceed. */ /* 331 User name okay, need password. */ /* 332 Need account for login. */ @@ -671,27 +672,27 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) i = ftp_getrc(f); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i); if (i == -1) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); } if (i == 530) { - ap_kill_timeout(r); - return ftp_unauthorized (r, 1); /* log it: user name guessing attempt? */ + ap_kill_timeout(r); + return ftp_unauthorized (r, 1); /* log it: user name guessing attempt? */ } if (i != 230 && i != 331) { - ap_kill_timeout(r); - return HTTP_BAD_GATEWAY; + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; } - if (i == 331) { /* send password */ - if (password == NULL) { - return ftp_unauthorized (r, 0); - } - ap_bvputs(f, "PASS ", password, CRLF, NULL); - ap_bflush(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PASS %s", password); -/* possible results 202, 230, 332, 421, 500, 501, 503, 530 */ + if (i == 331) { /* send password */ + if (password == NULL) { + return ftp_unauthorized (r, 0); + } + ap_bvputs(f, "PASS ", password, CRLF, NULL); + ap_bflush(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PASS %s", password); + /* possible results 202, 230, 332, 421, 500, 501, 503, 530 */ /* 230 User logged in, proceed. */ /* 332 Need account for login. */ /* 421 Service not available, closing control connection. */ @@ -699,27 +700,27 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) /* 501 Syntax error in parameters or arguments. */ /* 503 Bad sequence of commands. */ /* 530 Not logged in. */ - i = ftp_getrc(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i); - if (i == -1) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); - } - if (i == 332) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_UNAUTHORIZED, - "Need account for login"); - } - /* @@@ questionable -- we might as well return a 403 Forbidden here */ - if (i == 530) { - ap_kill_timeout(r); - return ftp_unauthorized (r, 1); /* log it: passwd guessing attempt? */ - } - if (i != 230 && i != 202) { - ap_kill_timeout(r); - return HTTP_BAD_GATEWAY; - } + i = ftp_getrc(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i); + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (i == 332) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_UNAUTHORIZED, + "Need account for login"); + } + /* @@@ questionable -- we might as well return a 403 Forbidden here */ + if (i == 530) { + ap_kill_timeout(r); + return ftp_unauthorized (r, 1); /* log it: passwd guessing attempt? */ + } + if (i != 230 && i != 202) { + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } } /* set the directory (walk directory component by component): @@ -727,16 +728,16 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) * machine */ for (;;) { - strp = strchr(path, '/'); - if (strp == NULL) - break; - *strp = '\0'; - - len = decodeenc(path); - ap_bvputs(f, "CWD ", path, CRLF, NULL); - ap_bflush(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path); - *strp = '/'; + strp = strchr(path, '/'); + if (strp == NULL) + break; + *strp = '\0'; + + len = decodeenc(path); + ap_bvputs(f, "CWD ", path, CRLF, NULL); + ap_bflush(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path); + *strp = '/'; /* responses: 250, 421, 500, 501, 502, 530, 550 */ /* 250 Requested file action okay, completed. */ /* 421 Service not available, closing control connection. */ @@ -745,43 +746,43 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) /* 502 Command not implemented. */ /* 530 Not logged in. */ /* 550 Requested action not taken. */ - i = ftp_getrc(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i); - if (i == -1) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); - } - if (i == 550) { - ap_kill_timeout(r); - return HTTP_NOT_FOUND; - } - if (i != 250) { - ap_kill_timeout(r); - return HTTP_BAD_GATEWAY; - } - - path = strp + 1; + i = ftp_getrc(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i); + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (i == 550) { + ap_kill_timeout(r); + return HTTP_NOT_FOUND; + } + if (i != 250) { + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } + + path = strp + 1; } if (parms != NULL && strncmp(parms, "type=", 5) == 0) { - parms += 5; - if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') || - parms[1] != '\0') - parms = ""; + parms += 5; + if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') || + parms[1] != '\0') + parms = ""; } else - parms = ""; + parms = ""; /* changed to make binary transfers the default */ if (parms[0] != 'a') { - /* set type to image */ - /* TM - Added CRLF to the end of TYPE I, otherwise it hangs the - connection */ - ap_bputs("TYPE I" CRLF, f); - ap_bflush(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: TYPE I"); + /* set type to image */ + /* TM - Added CRLF to the end of TYPE I, otherwise it hangs the + connection */ + ap_bputs("TYPE I" CRLF, f); + ap_bflush(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: TYPE I"); /* responses: 200, 421, 500, 501, 504, 530 */ /* 200 Command okay. */ /* 421 Service not available, closing control connection. */ @@ -789,46 +790,46 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) /* 501 Syntax error in parameters or arguments. */ /* 504 Command not implemented for that parameter. */ /* 530 Not logged in. */ - i = ftp_getrc(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i); - if (i == -1) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); - } - if (i != 200 && i != 504) { - ap_kill_timeout(r); - return HTTP_BAD_GATEWAY; - } + i = ftp_getrc(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i); + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (i != 200 && i != 504) { + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } /* Allow not implemented */ - if (i == 504) - parms[0] = '\0'; + if (i == 504) + parms[0] = '\0'; } /* try to set up PASV data connection first */ dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP); if (dsock == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error creating PASV socket"); - ap_bclose(f); - ap_kill_timeout(r); - return HTTP_INTERNAL_SERVER_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error creating PASV socket"); + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_INTERNAL_SERVER_ERROR; } #if !defined (TPF) && !defined(BEOS) if (conf->recv_buffer_size) { - if (setsockopt(dsock, SOL_SOCKET, SO_RCVBUF, - (const char *) &conf->recv_buffer_size, sizeof(int)) == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); - } + if (setsockopt(dsock, SOL_SOCKET, SO_RCVBUF, + (const char *) &conf->recv_buffer_size, sizeof(int)) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); + } } #endif ap_bputs("PASV" CRLF, f); ap_bflush(f); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PASV command issued"); - /* possible results: 227, 421, 500, 501, 502, 530 */ +/* possible results: 227, 421, 500, 501, 502, 530 */ /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ /* 421 Service not available, closing control connection. */ /* 500 Syntax error, command unrecognized. */ @@ -837,102 +838,102 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) /* 530 Not logged in. */ i = ap_bgets(pasv, sizeof(pasv), f); if (i == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, - "PASV: control connection is toast"); - ap_pclosesocket(p, dsock); - ap_bclose(f); - ap_kill_timeout(r); - return HTTP_INTERNAL_SERVER_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, + "PASV: control connection is toast"); + ap_pclosesocket(p, dsock); + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_INTERNAL_SERVER_ERROR; } else { - pasv[i - 1] = '\0'; - pstr = strtok(pasv, " "); /* separate result code */ - if (pstr != NULL) { - presult = atoi(pstr); - if (*(pstr + strlen(pstr) + 1) == '=') - pstr += strlen(pstr) + 2; - else - { - pstr = strtok(NULL, "("); /* separate address & port params */ - if (pstr != NULL) - pstr = strtok(NULL, ")"); - } - } - else - presult = atoi(pasv); - - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", presult); - - if (presult == 227 && pstr != NULL && (sscanf(pstr, - "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) { - /* pardon the parens, but it makes gcc happy */ - paddr = (((((h3 << 8) + h2) << 8) + h1) << 8) + h0; - pport = (p1 << 8) + p0; - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: contacting host %d.%d.%d.%d:%d", - h3, h2, h1, h0, pport); - data_addr.sin_family = AF_INET; - data_addr.sin_addr.s_addr = htonl(paddr); - data_addr.sin_port = htons(pport); - i = ap_proxy_doconnect(dsock, &data_addr, r); - - if (i == -1) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - ap_pstrcat(r->pool, - "Could not connect to remote machine: ", - strerror(errno), NULL)); - } - else { - pasvmode = 1; - } - } - else - ap_pclosesocket(p, dsock); /* and try the regular way */ + pasv[i - 1] = '\0'; + pstr = strtok(pasv, " "); /* separate result code */ + if (pstr != NULL) { + presult = atoi(pstr); + if (*(pstr + strlen(pstr) + 1) == '=') + pstr += strlen(pstr) + 2; + else + { + pstr = strtok(NULL, "("); /* separate address & port params */ + if (pstr != NULL) + pstr = strtok(NULL, ")"); + } + } + else + presult = atoi(pasv); + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", presult); + + if (presult == 227 && pstr != NULL && (sscanf(pstr, + "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) { + /* pardon the parens, but it makes gcc happy */ + paddr = (((((h3 << 8) + h2) << 8) + h1) << 8) + h0; + pport = (p1 << 8) + p0; + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: contacting host %d.%d.%d.%d:%d", + h3, h2, h1, h0, pport); + data_addr.sin_family = AF_INET; + data_addr.sin_addr.s_addr = htonl(paddr); + data_addr.sin_port = htons(pport); + i = ap_proxy_doconnect(dsock, &data_addr, r); + + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + ap_pstrcat(r->pool, + "Could not connect to remote machine: ", + strerror(errno), NULL)); + } + else { + pasvmode = 1; + } + } + else + ap_pclosesocket(p, dsock); /* and try the regular way */ } - if (!pasvmode) { /* set up data connection */ - clen = sizeof(struct sockaddr_in); - if (getsockname(sock, (struct sockaddr *) &server, &clen) < 0) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error getting socket address"); - ap_bclose(f); - ap_kill_timeout(r); - return HTTP_INTERNAL_SERVER_ERROR; - } - - dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (dsock == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error creating socket"); - ap_bclose(f); - ap_kill_timeout(r); - return HTTP_INTERNAL_SERVER_ERROR; - } - - if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (void *) &one, - sizeof(one)) == -1) { + if (!pasvmode) { /* set up data connection */ + clen = sizeof(struct sockaddr_in); + if (getsockname(sock, (struct sockaddr *) &server, &clen) < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error getting socket address"); + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_INTERNAL_SERVER_ERROR; + } + + dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (dsock == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error creating socket"); + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_INTERNAL_SERVER_ERROR; + } + + if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (void *) &one, + sizeof(one)) == -1) { #ifndef _OSD_POSIX /* BS2000 has this option "always on" */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error setting reuseaddr option"); - ap_pclosesocket(p, dsock); - ap_bclose(f); - ap_kill_timeout(r); - return HTTP_INTERNAL_SERVER_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error setting reuseaddr option"); + ap_pclosesocket(p, dsock); + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_INTERNAL_SERVER_ERROR; #endif /*_OSD_POSIX*/ - } - - if (bind(dsock, (struct sockaddr *) &server, - sizeof(struct sockaddr_in)) == -1) { - char buff[22]; - - ap_snprintf(buff, sizeof(buff), "%s:%d", inet_ntoa(server.sin_addr), server.sin_port); - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error binding to ftp data socket %s", buff); - ap_bclose(f); - ap_pclosesocket(p, dsock); - return HTTP_INTERNAL_SERVER_ERROR; - } - listen(dsock, 2); /* only need a short queue */ + } + + if (bind(dsock, (struct sockaddr *) &server, + sizeof(struct sockaddr_in)) == -1) { + char buff[22]; + + ap_snprintf(buff, sizeof(buff), "%s:%d", inet_ntoa(server.sin_addr), server.sin_port); + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error binding to ftp data socket %s", buff); + ap_bclose(f); + ap_pclosesocket(p, dsock); + return HTTP_INTERNAL_SERVER_ERROR; + } + listen(dsock, 2); /* only need a short queue */ } /* set request; "path" holds last path component */ @@ -941,55 +942,55 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) /* TM - if len == 0 then it must be a directory (you can't RETR nothing) */ if (len == 0) { - parms = "d"; + parms = "d"; } else { - ap_bvputs(f, "SIZE ", path, CRLF, NULL); - ap_bflush(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: SIZE %s", path); - i = ftp_getrc_msg(f, resp, sizeof resp); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d with response %s", i, resp); - if (i != 500) { /* Size command not recognized */ - if (i == 550) { /* Not a regular file */ - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: SIZE shows this is a directory"); - parms = "d"; - ap_bvputs(f, "CWD ", path, CRLF, NULL); - ap_bflush(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path); - i = ftp_getrc(f); - /* possible results: 250, 421, 500, 501, 502, 530, 550 */ - /* 250 Requested file action okay, completed. */ - /* 421 Service not available, closing control connection. */ - /* 500 Syntax error, command unrecognized. */ - /* 501 Syntax error in parameters or arguments. */ - /* 502 Command not implemented. */ - /* 530 Not logged in. */ - /* 550 Requested action not taken. */ - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i); - if (i == -1) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); - } - if (i == 550) { - ap_kill_timeout(r); - return HTTP_NOT_FOUND; - } - if (i != 250) { - ap_kill_timeout(r); - return HTTP_BAD_GATEWAY; - } - path = ""; - len = 0; - } - else if (i == 213) { /* Size command ok */ - for (j = 0; j < sizeof resp && ap_isdigit(resp[j]); j++) - ; - resp[j] = '\0'; - if (resp[0] != '\0') - size = ap_pstrdup(p, resp); - } - } + ap_bvputs(f, "SIZE ", path, CRLF, NULL); + ap_bflush(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: SIZE %s", path); + i = ftp_getrc_msg(f, resp, sizeof resp); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d with response %s", i, resp); + if (i != 500) { /* Size command not recognized */ + if (i == 550) { /* Not a regular file */ + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: SIZE shows this is a directory"); + parms = "d"; + ap_bvputs(f, "CWD ", path, CRLF, NULL); + ap_bflush(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path); + i = ftp_getrc(f); + /* possible results: 250, 421, 500, 501, 502, 530, 550 */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i); + if (i == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (i == 550) { + ap_kill_timeout(r); + return HTTP_NOT_FOUND; + } + if (i != 250) { + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } + path = ""; + len = 0; + } + else if (i == 213) { /* Size command ok */ + for (j = 0; j < sizeof resp && ap_isdigit(resp[j]); j++) + ; + resp[j] = '\0'; + if (resp[0] != '\0') + size = ap_pstrdup(p, resp); + } + } } #ifdef AUTODETECT_PWD @@ -1006,30 +1007,30 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) i = ftp_getrc_msg(f, resp, sizeof resp); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD returned status %d", i); if (i == -1 || i == 421) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); } if (i == 550) { - ap_kill_timeout(r); - return HTTP_NOT_FOUND; + ap_kill_timeout(r); + return HTTP_NOT_FOUND; } if (i == 257) { - const char *dirp = resp; - cwd = ap_getword_conf(r->pool, &dirp); + const char *dirp = resp; + cwd = ap_getword_conf(r->pool, &dirp); } #endif /*AUTODETECT_PWD*/ if (parms[0] == 'd') { - if (len != 0) - ap_bvputs(f, "LIST ", path, CRLF, NULL); - else - ap_bputs("LIST -lag" CRLF, f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: LIST %s", (len == 0 ? "" : path)); + if (len != 0) + ap_bvputs(f, "LIST ", path, CRLF, NULL); + else + ap_bputs("LIST -lag" CRLF, f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: LIST %s", (len == 0 ? "" : path)); } else { - ap_bvputs(f, "RETR ", path, CRLF, NULL); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: RETR %s", path); + ap_bvputs(f, "RETR ", path, CRLF, NULL); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: RETR %s", path); } ap_bflush(f); /* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, 550 @@ -1051,80 +1052,80 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) rc = ftp_getrc(f); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc); if (rc == -1) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); } if (rc == 550) { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: RETR failed, trying LIST instead"); - parms = "d"; - ap_bvputs(f, "CWD ", path, CRLF, NULL); - ap_bflush(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path); - /* possible results: 250, 421, 500, 501, 502, 530, 550 */ - /* 250 Requested file action okay, completed. */ - /* 421 Service not available, closing control connection. */ - /* 500 Syntax error, command unrecognized. */ - /* 501 Syntax error in parameters or arguments. */ - /* 502 Command not implemented. */ - /* 530 Not logged in. */ - /* 550 Requested action not taken. */ - rc = ftp_getrc(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc); - if (rc == -1) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); - } - if (rc == 550) { - ap_kill_timeout(r); - return HTTP_NOT_FOUND; - } - if (rc != 250) { - ap_kill_timeout(r); - return HTTP_BAD_GATEWAY; - } + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: RETR failed, trying LIST instead"); + parms = "d"; + ap_bvputs(f, "CWD ", path, CRLF, NULL); + ap_bflush(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path); + /* possible results: 250, 421, 500, 501, 502, 530, 550 */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + rc = ftp_getrc(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc); + if (rc == -1) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 550) { + ap_kill_timeout(r); + return HTTP_NOT_FOUND; + } + if (rc != 250) { + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } #ifdef AUTODETECT_PWD - ap_bvputs(f, "PWD", CRLF, NULL); - ap_bflush(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD"); + ap_bvputs(f, "PWD", CRLF, NULL); + ap_bflush(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD"); /* responses: 257, 500, 501, 502, 421, 550 */ - /* 257 "" */ - /* 421 Service not available, closing control connection. */ - /* 500 Syntax error, command unrecognized. */ - /* 501 Syntax error in parameters or arguments. */ - /* 502 Command not implemented. */ - /* 550 Requested action not taken. */ - i = ftp_getrc_msg(f, resp, sizeof resp); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD returned status %d", i); - if (i == -1 || i == 421) { - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); - } - if (i == 550) { - ap_kill_timeout(r); - return HTTP_NOT_FOUND; - } - if (i == 257) { - const char *dirp = resp; - cwd = ap_getword_conf(r->pool, &dirp); - } + /* 257 "" */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 550 Requested action not taken. */ + i = ftp_getrc_msg(f, resp, sizeof resp); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD returned status %d", i); + if (i == -1 || i == 421) { + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (i == 550) { + ap_kill_timeout(r); + return HTTP_NOT_FOUND; + } + if (i == 257) { + const char *dirp = resp; + cwd = ap_getword_conf(r->pool, &dirp); + } #endif /*AUTODETECT_PWD*/ - ap_bputs("LIST -lag" CRLF, f); - ap_bflush(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: LIST -lag"); - rc = ftp_getrc(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc); - if (rc == -1) - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); + ap_bputs("LIST -lag" CRLF, f); + ap_bflush(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: LIST -lag"); + rc = ftp_getrc(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc); + if (rc == -1) + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); } ap_kill_timeout(r); if (rc != 125 && rc != 150 && rc != 226 && rc != 250) - return HTTP_BAD_GATEWAY; + return HTTP_BAD_GATEWAY; r->status = HTTP_OK; r->status_line = "200 OK"; @@ -1136,135 +1137,125 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) ap_table_setn(resp_hdrs, "Server", ap_get_server_version()); if (parms[0] == 'd') { - ap_table_setn(resp_hdrs, "Content-Type", "text/html"); + ap_table_setn(resp_hdrs, "Content-Type", "text/html"); #ifdef CHARSET_EBCDIC - r->ebcdic.conv_out = 1; /* server-generated */ + r->ebcdic.conv_out = 1; /* server-generated */ #endif } else { #ifdef CHARSET_EBCDIC - r->ebcdic.conv_out = 0; /* do not convert what we read from the ftp server */ + r->ebcdic.conv_out = 0; /* do not convert what we read from the ftp server */ #endif - if (r->content_type != NULL) { - ap_table_setn(resp_hdrs, "Content-Type", r->content_type); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Type set to %s", r->content_type); - } - else { - ap_table_setn(resp_hdrs, "Content-Type", ap_default_type(r)); - } - if (parms[0] != 'a' && size != NULL) { - /* We "trust" the ftp server to really serve (size) bytes... */ - ap_table_set(resp_hdrs, "Content-Length", size); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Length set to %s", size); - } + if (r->content_type != NULL) { + ap_table_setn(resp_hdrs, "Content-Type", r->content_type); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Type set to %s", r->content_type); + } + else { + ap_table_setn(resp_hdrs, "Content-Type", ap_default_type(r)); + } + if (parms[0] != 'a' && size != NULL) { + /* We "trust" the ftp server to really serve (size) bytes... */ + ap_table_set(resp_hdrs, "Content-Length", size); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Length set to %s", size); + } } if (r->content_encoding != NULL && r->content_encoding[0] != '\0') { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Encoding set to %s", r->content_encoding); - ap_table_setn(resp_hdrs, "Content-Encoding", r->content_encoding); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Encoding set to %s", r->content_encoding); + ap_table_setn(resp_hdrs, "Content-Encoding", r->content_encoding); } /* check if NoCache directive on this host */ if (nocache == 0) { - for (i = 0; i < conf->nocaches->nelts; i++) { - if (destaddr.s_addr == ncent[i].addr.s_addr || - (ncent[i].name != NULL && - (ncent[i].name[0] == '*' || - strstr(host, ncent[i].name) != NULL))) { - nocache = 1; - break; - } - } + for (i = 0; i < conf->nocaches->nelts; i++) { + if (destaddr.s_addr == ncent[i].addr.s_addr || + (ncent[i].name != NULL && + (ncent[i].name[0] == '*' || + strstr(host, ncent[i].name) != NULL))) { + nocache = 1; + break; + } + } } i = ap_proxy_cache_update(c, resp_hdrs, 0, nocache); if (i != DECLINED) { - ap_pclosesocket(p, dsock); - ap_bclose(f); - return i; + ap_pclosesocket(p, dsock); + ap_bclose(f); + return i; } - if (!pasvmode) { /* wait for connection */ - ap_hard_timeout("proxy ftp data connect", r); - clen = sizeof(struct sockaddr_in); - do - csd = accept(dsock, (struct sockaddr *) &server, &clen); - while (csd == -1 && errno == EINTR); - if (csd == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: failed to accept data connection"); - ap_pclosesocket(p, dsock); - ap_bclose(f); - ap_kill_timeout(r); - if (c != NULL) - c = ap_proxy_cache_error(c); - return HTTP_BAD_GATEWAY; - } - ap_note_cleanups_for_socket(p, csd); - data = ap_bcreate(p, B_RDWR | B_SOCKET); - ap_bpushfd(data, csd, -1); - ap_kill_timeout(r); + if (!pasvmode) { /* wait for connection */ + ap_hard_timeout("proxy ftp data connect", r); + clen = sizeof(struct sockaddr_in); + do + csd = accept(dsock, (struct sockaddr *) &server, &clen); + while (csd == -1 && errno == EINTR); + if (csd == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: failed to accept data connection"); + ap_pclosesocket(p, dsock); + ap_bclose(f); + ap_kill_timeout(r); + if (c != NULL) + c = ap_proxy_cache_error(c); + return HTTP_BAD_GATEWAY; + } + ap_note_cleanups_for_socket(p, csd); + data = ap_bcreate(p, B_RDWR | B_SOCKET); + ap_bpushfd(data, csd, -1); + ap_kill_timeout(r); } else { - data = ap_bcreate(p, B_RDWR | B_SOCKET); - ap_bpushfd(data, dsock, dsock); + data = ap_bcreate(p, B_RDWR | B_SOCKET); + ap_bpushfd(data, dsock, dsock); } ap_hard_timeout("proxy receive", r); -/* send response */ -/* write status line */ - if (!r->assbackwards) - ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL); - if (c != NULL && c->fp != NULL - && ap_bvputs(c->fp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, - "proxy: error writing CRLF to %s", c->tempfile); - c = ap_proxy_cache_error(c); - } -/* send headers */ - tdo.req = r; - tdo.cache = c; - ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL); - - if (!r->assbackwards) - ap_rputs(CRLF, r); - if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, - "proxy: error writing CRLF to %s", c->tempfile); - c = ap_proxy_cache_error(c); - } + /* send response */ + /* write status line and headers to the cache file */ + ap_proxy_write_headers(c, ap_pstrcat(p, "HTTP/1.1 ", r->status_line, NULL), resp_hdrs); + + /* Setup the headers for our client from upstreams response-headers */ + ap_overlap_tables(r->headers_out, resp_hdrs, AP_OVERLAP_TABLES_SET); + /* Add X-Cache header */ + ap_table_setn(r->headers_out, "X-Cache", + ap_pstrcat(r->pool, "MISS from ", + ap_get_server_name(r), NULL)); + /* The Content-Type of this response is the upstream one. */ + r->content_type = ap_table_get (r->headers_out, "Content-Type"); + /* finally output the headers to the client */ + ap_send_http_header(r); - ap_bsetopt(r->connection->client, BO_BYTECT, &zero); - r->sent_bodyct = 1; #ifdef CHARSET_EBCDIC ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, r->ebcdic.conv_out); #endif /* send body */ if (!r->header_only) { - if (parms[0] != 'd') { + if (parms[0] != 'd') { /* we need to set this for ap_proxy_send_fb()... */ - if (c != NULL) - c->cache_completion = 0; - ap_proxy_send_fb(data, r, c); - } else - send_dir(data, r, c, cwd); + if (c != NULL) + c->cache_completion = 0; + ap_proxy_send_fb(data, r, c, -1, 0); + } else + send_dir(data, r, c, cwd); - if (rc == 125 || rc == 150) - rc = ftp_getrc(f); + if (rc == 125 || rc == 150) + rc = ftp_getrc(f); - /* XXX: we checked for 125||150||226||250 above. This is redundant. */ - if (rc != 226 && rc != 250) + /* XXX: we checked for 125||150||226||250 above. This is redundant. */ + if (rc != 226 && rc != 250) /* XXX: we no longer log an "error writing to c->tempfile" - should we? */ - c = ap_proxy_cache_error(c); + c = ap_proxy_cache_error(c); } else { /* abort the transfer */ - ap_bputs("ABOR" CRLF, f); - ap_bflush(f); - if (!pasvmode) - ap_bclose(data); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: ABOR"); + ap_bputs("ABOR" CRLF, f); + ap_bflush(f); + if (!pasvmode) + ap_bclose(data); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: ABOR"); /* responses: 225, 226, 421, 500, 501, 502 */ /* 225 Data connection open; no transfer in progress. */ /* 226 Closing data connection. */ @@ -1272,8 +1263,8 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) /* 500 Syntax error, command unrecognized. */ /* 501 Syntax error in parameters or arguments. */ /* 502 Command not implemented. */ - i = ftp_getrc(f); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i); + i = ftp_getrc(f); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i); } ap_kill_timeout(r); @@ -1290,10 +1281,10 @@ int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url) ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: QUIT: status %d", i); if (pasvmode) - ap_bclose(data); + ap_bclose(data); ap_bclose(f); - ap_rflush(r); /* flush before garbage collection */ + ap_rflush(r); /* flush before garbage collection */ ap_proxy_garbage_coll(r); diff --git a/src/modules/proxy/proxy_http.c b/src/modules/proxy/proxy_http.c index 8136d640a67..2d339595f3d 100644 --- a/src/modules/proxy/proxy_http.c +++ b/src/modules/proxy/proxy_http.c @@ -76,43 +76,44 @@ int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme, int def_p const char *err; int port; -/* do syntatic check. - * We break the URL into host, port, path, search - */ + /* do syntatic check. + * We break the URL into host, port, path, search + */ port = def_port; err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); if (err) - return HTTP_BAD_REQUEST; + return HTTP_BAD_REQUEST; -/* now parse path/search args, according to rfc1738 */ -/* N.B. if this isn't a true proxy request, then the URL _path_ - * has already been decoded. True proxy requests have r->uri - * == r->unparsed_uri, and no others have that property. - */ + /* now parse path/search args, according to rfc1738 */ + /* N.B. if this isn't a true proxy request, then the URL _path_ + * has already been decoded. True proxy requests have r->uri + * == r->unparsed_uri, and no others have that property. + */ if (r->uri == r->unparsed_uri) { - search = strchr(url, '?'); - if (search != NULL) - *(search++) = '\0'; + search = strchr(url, '?'); + if (search != NULL) + *(search++) = '\0'; } else - search = r->args; + search = r->args; -/* process path */ + /* process path */ path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, - r->proxyreq); + r->proxyreq); if (path == NULL) - return HTTP_BAD_REQUEST; + return HTTP_BAD_REQUEST; if (port != def_port) - ap_snprintf(sport, sizeof(sport), ":%d", port); + ap_snprintf(sport, sizeof(sport), ":%d", port); else - sport[0] = '\0'; + sport[0] = '\0'; r->filename = ap_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/", - path, (search) ? "?" : "", (search) ? search : "", NULL); + path, (search) ? "?" : "", (search) ? search : "", NULL); return OK; } - + +/* handle the conversion of URLs in the ProxyPassReverse function */ static const char *proxy_location_reverse_map(request_rec *r, const char *url) { void *sconf; @@ -135,29 +136,6 @@ static const char *proxy_location_reverse_map(request_rec *r, const char *url) return url; } -/* Clear all connection-based headers from the incoming headers table */ -static void clear_connection(pool *p, table *headers) -{ - const char *name; - char *next = ap_pstrdup(p, ap_table_get(headers, "Connection")); - - ap_table_unset(headers, "Proxy-Connection"); - if (!next) - return; - - while (*next) { - name = next; - while (*next && !ap_isspace(*next) && (*next != ',')) - ++next; - while (*next && (ap_isspace(*next) || (*next == ','))) { - *next = '\0'; - ++next; - } - ap_table_unset(headers, name); - } - ap_table_unset(headers, "Connection"); -} - /* * This handles http:// URLs, and other URLs using a remote proxy over http * If proxyhost is NULL, then contact the server directly, otherwise @@ -168,15 +146,15 @@ static void clear_connection(pool *p, table *headers) * route.) */ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, - const char *proxyhost, int proxyport) + const char *proxyhost, int proxyport) { const char *strp; char *strp2; - const char *err, *desthost, *pragma; + const char *err, *desthost; int i, j, sock, len, backasswards; + table *req_hdrs, *resp_hdrs; array_header *reqhdrs_arr; - table *resp_hdrs; - table_entry *reqhdrs; + table_entry *reqhdrs_elts; struct sockaddr_in server; struct in_addr destaddr; struct hostent server_hp; @@ -184,12 +162,10 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, char buffer[HUGE_STRING_LEN]; char portstr[32]; pool *p = r->pool; - const long int zero = 0L; int destport = 0; char *destportstr = NULL; const char *urlptr = NULL; - const char *datestr; - struct tbl_do_args tdo; + const char *datestr, *urlstr; void *sconf = r->server->module_config; proxy_server_conf *conf = @@ -198,39 +174,41 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts; int nocache = 0; + if (conf->cache.root == NULL) nocache = 1; + memset(&server, '\0', sizeof(server)); server.sin_family = AF_INET; -/* We break the URL into host, port, path-search */ + /* We break the URL into host, port, path-search */ urlptr = strstr(url, "://"); if (urlptr == NULL) - return HTTP_BAD_REQUEST; + return HTTP_BAD_REQUEST; urlptr += 3; destport = DEFAULT_HTTP_PORT; strp = strchr(urlptr, '/'); if (strp == NULL) { - desthost = ap_pstrdup(p, urlptr); - urlptr = "/"; + desthost = ap_pstrdup(p, urlptr); + urlptr = "/"; } else { - char *q = ap_palloc(p, strp - urlptr + 1); - memcpy(q, urlptr, strp - urlptr); - q[strp - urlptr] = '\0'; - urlptr = strp; - desthost = q; + char *q = ap_palloc(p, strp - urlptr + 1); + memcpy(q, urlptr, strp - urlptr); + q[strp - urlptr] = '\0'; + urlptr = strp; + desthost = q; } strp2 = strchr(desthost, ':'); if (strp2 != NULL) { - *(strp2++) = '\0'; - if (ap_isdigit(*strp2)) { - destport = atoi(strp2); - destportstr = strp2; - } + *(strp2++) = '\0'; + if (ap_isdigit(*strp2)) { + destport = atoi(strp2); + destportstr = strp2; + } } -/* check if ProxyBlock directive on this host */ + /* check if ProxyBlock directive on this host */ destaddr.s_addr = ap_inet_addr(desthost); for (i = 0; i < conf->noproxies->nelts; i++) { if (destaddr.s_addr == npent[i].addr.s_addr || @@ -241,304 +219,339 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, } if (proxyhost != NULL) { - server.sin_port = htons(proxyport); - err = ap_proxy_host2addr(proxyhost, &server_hp); - if (err != NULL) - return DECLINED; /* try another */ + server.sin_port = htons(proxyport); + err = ap_proxy_host2addr(proxyhost, &server_hp); + if (err != NULL) + return DECLINED; /* try another */ } else { - server.sin_port = htons(destport); - err = ap_proxy_host2addr(desthost, &server_hp); - if (err != NULL) - return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err); + server.sin_port = htons(destport); + err = ap_proxy_host2addr(desthost, &server_hp); + if (err != NULL) + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err); } + + /* we have worked out who exactly we are going to connect to, now + * make that connection... + */ sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy: error creating socket"); - return HTTP_INTERNAL_SERVER_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error creating socket"); + return HTTP_INTERNAL_SERVER_ERROR; } #if !defined(TPF) && !defined(BEOS) if (conf->recv_buffer_size) { - if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, - (const char *) &conf->recv_buffer_size, sizeof(int)) - == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); - } + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + (const char *) &conf->recv_buffer_size, sizeof(int)) + == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); + } } #endif #ifdef SINIX_D_RESOLVER_BUG { - struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list; - - for (; ip_addr->s_addr != 0; ++ip_addr) { - memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr)); - i = ap_proxy_doconnect(sock, &server, r); - if (i == 0) - break; - } + struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list; + + for (; ip_addr->s_addr != 0; ++ip_addr) { + memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr)); + i = ap_proxy_doconnect(sock, &server, r); + if (i == 0) + break; + } } #else j = 0; while (server_hp.h_addr_list[j] != NULL) { - memcpy(&server.sin_addr, server_hp.h_addr_list[j], - sizeof(struct in_addr)); - i = ap_proxy_doconnect(sock, &server, r); - if (i == 0) - break; - j++; + memcpy(&server.sin_addr, server_hp.h_addr_list[j], + sizeof(struct in_addr)); + i = ap_proxy_doconnect(sock, &server, r); + if (i == 0) + break; + j++; } #endif if (i == -1) { - if (proxyhost != NULL) - return DECLINED; /* try again another way */ - else - return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool, - "Could not connect to remote machine: ", - strerror(errno), NULL)); + if (proxyhost != NULL) + return DECLINED; /* try again another way */ + else + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool, + "Could not connect to remote machine: ", + strerror(errno), NULL)); } - clear_connection(r->pool, r->headers_in); /* Strip connection-based headers */ + /* record request_time for HTTP/1.1 age calculation */ + c->req_time = time(NULL); + /* build upstream-request headers by stripping r->headers_in from + * connection specific headers. + * We must not remove the Connection: header from r->headers_in, + * we still have to react to Connection: close + */ + req_hdrs = ap_copy_table(r->pool, r->headers_in); + ap_proxy_clear_connection(r->pool, req_hdrs); + + /* At this point, we start sending the HTTP/1.1 request to the + * remote server (proxy or otherwise). + */ f = ap_bcreate(p, B_RDWR | B_SOCKET); ap_bpushfd(f, sock, sock); ap_hard_timeout("proxy send", r); ap_bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.0" CRLF, - NULL); + NULL); + /* Send Host: now, adding it to req_hdrs wouldn't be much better */ if (destportstr != NULL && destport != DEFAULT_HTTP_PORT) - ap_bvputs(f, "Host: ", desthost, ":", destportstr, CRLF, NULL); + ap_bvputs(f, "Host: ", desthost, ":", destportstr, CRLF, NULL); else - ap_bvputs(f, "Host: ", desthost, CRLF, NULL); + ap_bvputs(f, "Host: ", desthost, CRLF, NULL); if (conf->viaopt == via_block) { - /* Block all outgoing Via: headers */ - ap_table_unset(r->headers_in, "Via"); + /* Block all outgoing Via: headers */ + ap_table_unset(req_hdrs, "Via"); } else if (conf->viaopt != via_off) { - /* Create a "Via:" request header entry and merge it */ - i = ap_get_server_port(r); - if (ap_is_default_port(i,r)) { - strcpy(portstr,""); - } else { - ap_snprintf(portstr, sizeof portstr, ":%d", i); - } - /* Generate outgoing Via: header with/without server comment: */ - ap_table_mergen(r->headers_in, "Via", - (conf->viaopt == via_full) - ? ap_psprintf(p, "%d.%d %s%s (%s)", - HTTP_VERSION_MAJOR(r->proto_num), - HTTP_VERSION_MINOR(r->proto_num), - ap_get_server_name(r), portstr, - SERVER_BASEVERSION) - : ap_psprintf(p, "%d.%d %s%s", - HTTP_VERSION_MAJOR(r->proto_num), - HTTP_VERSION_MINOR(r->proto_num), - ap_get_server_name(r), portstr) - ); + /* Create a "Via:" request header entry and merge it */ + i = ap_get_server_port(r); + if (ap_is_default_port(i,r)) { + strcpy(portstr,""); + } else { + ap_snprintf(portstr, sizeof portstr, ":%d", i); + } + /* Generate outgoing Via: header with/without server comment: */ + ap_table_mergen(req_hdrs, "Via", + (conf->viaopt == via_full) + ? ap_psprintf(p, "%d.%d %s%s (%s)", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + ap_get_server_name(r), portstr, + SERVER_BASEVERSION) + : ap_psprintf(p, "%d.%d %s%s", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + ap_get_server_name(r), portstr) + ); } - reqhdrs_arr = ap_table_elts(r->headers_in); - reqhdrs = (table_entry *) reqhdrs_arr->elts; + /* Add X-Forwarded-For: so that the upstream has a chance to + determine, where the original request came from. */ + ap_table_mergen(req_hdrs, "X-Forwarded-For", r->connection->remote_ip); + + /* we don't yet support keepalives - but we will soon, I promise! */ + ap_table_set(req_hdrs, "Connection", "close"); + + reqhdrs_arr = ap_table_elts(req_hdrs); + reqhdrs_elts = (table_entry *) reqhdrs_arr->elts; for (i = 0; i < reqhdrs_arr->nelts; i++) { - if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL - /* Clear out headers not to send */ - || !strcasecmp(reqhdrs[i].key, "Host") /* Already sent */ - /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be - * suppressed if THIS server requested the authentication, - * not when a frontend proxy requested it! - */ - || !strcasecmp(reqhdrs[i].key, "Proxy-Authorization")) - continue; - ap_bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, CRLF, NULL); + if (reqhdrs_elts[i].key == NULL || reqhdrs_elts[i].val == NULL + + /* Clear out hop-by-hop request headers not to send: + * RFC2616 13.5.1 says we should strip these headers: + */ + || !strcasecmp(reqhdrs_elts[i].key, "Host") /* Already sent */ + || !strcasecmp(reqhdrs_elts[i].key, "Keep-Alive") + || !strcasecmp(reqhdrs_elts[i].key, "TE") + || !strcasecmp(reqhdrs_elts[i].key, "Trailer") + || !strcasecmp(reqhdrs_elts[i].key, "Transfer-Encoding") + || !strcasecmp(reqhdrs_elts[i].key, "Upgrade") + + /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be + * suppressed if THIS server requested the authentication, + * not when a frontend proxy requested it! + * + * The solution to this problem is probably to strip out + * the Proxy-Authorisation header in the authorisation + * code itself, not here. This saves us having to signal + * somehow whether this request was authenticated or not. + */ + || !strcasecmp(reqhdrs_elts[i].key, "Proxy-Authorization")) + continue; + ap_bvputs(f, reqhdrs_elts[i].key, ": ", reqhdrs_elts[i].val, CRLF, NULL); } + /* the obligatory empty line to mark the end of the headers */ ap_bputs(CRLF, f); -/* send the request data, if any. */ + /* send the request data, if any. */ if (ap_should_client_block(r)) { - while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) - ap_bwrite(f, buffer, i); + while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) + ap_bwrite(f, buffer, i); } ap_bflush(f); ap_kill_timeout(r); + + /* Right - now it's time to listen for a response. + */ ap_hard_timeout("proxy receive", r); len = ap_bgets(buffer, sizeof buffer - 1, f); if (len == -1) { - ap_bclose(f); - ap_kill_timeout(r); - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "ap_bgets() - proxy receive - Error reading from remote server %s (length %d)", - proxyhost ? proxyhost : desthost, len); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); + ap_bclose(f); + ap_kill_timeout(r); + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "ap_bgets() - proxy receive - Error reading from remote server %s (length %d)", + proxyhost ? proxyhost : desthost, len); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); } else if (len == 0) { - ap_bclose(f); - ap_kill_timeout(r); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Document contains no data"); + ap_bclose(f); + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Document contains no data"); } -/* Is it an HTTP/1 response? This is buggy if we ever see an HTTP/1.10 */ + /* Is it an HTTP/1 response? + * Do some sanity checks on the response. + * (This is buggy if we ever see an HTTP/1.10) + */ if (ap_checkmask(buffer, "HTTP/#.# ###*")) { - int major, minor; - if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) { - major = 1; - minor = 0; - } - -/* If not an HTTP/1 message or if the status line was > 8192 bytes */ - if (buffer[5] != '1' || buffer[len - 1] != '\n') { - ap_bclose(f); - ap_kill_timeout(r); - return HTTP_BAD_GATEWAY; - } - backasswards = 0; - buffer[--len] = '\0'; - - buffer[12] = '\0'; - r->status = atoi(&buffer[9]); - buffer[12] = ' '; - r->status_line = ap_pstrdup(p, &buffer[9]); - -/* read the headers. */ -/* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */ -/* Also, take care with headers with multiple occurences. */ - - resp_hdrs = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, f); - if (resp_hdrs == NULL) { - ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server, - "proxy: Bad HTTP/%d.%d header returned by %s (%s)", - major, minor, r->uri, r->method); - resp_hdrs = ap_make_table(p, 20); - nocache = 1; /* do not cache this broken file */ - } - - if (conf->viaopt != via_off && conf->viaopt != via_block) { - /* Create a "Via:" response header entry and merge it */ - i = ap_get_server_port(r); - if (ap_is_default_port(i,r)) { - strcpy(portstr,""); - } else { - ap_snprintf(portstr, sizeof portstr, ":%d", i); - } - ap_table_mergen((table *)resp_hdrs, "Via", - (conf->viaopt == via_full) - ? ap_psprintf(p, "%d.%d %s%s (%s)", - major, minor, - ap_get_server_name(r), portstr, - SERVER_BASEVERSION) - : ap_psprintf(p, "%d.%d %s%s", - major, minor, - ap_get_server_name(r), portstr) - ); - } - - clear_connection(p, resp_hdrs); /* Strip Connection hdrs */ + int major, minor; + if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) { + /* if no response, default to HTTP/1.1 - is this correct? */ + major = 1; + minor = 1; + } + + /* If not an HTTP/1 message or if the status line was > 8192 bytes */ + if (buffer[5] != '1' || buffer[len - 1] != '\n') { + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } + backasswards = 0; + buffer[--len] = '\0'; + + buffer[12] = '\0'; + r->status = atoi(&buffer[9]); + buffer[12] = ' '; + r->status_line = ap_pstrdup(p, &buffer[9]); + + /* read the response headers. */ + /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */ + /* Also, take care with headers with multiple occurences. */ + + resp_hdrs = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, f); + if (resp_hdrs == NULL) { + ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server, + "proxy: Bad HTTP/%d.%d header returned by %s (%s)", + major, minor, r->uri, r->method); + resp_hdrs = ap_make_table(p, 20); + nocache = 1; /* do not cache this broken file */ + } + + /* handle Via header in the response */ + if (conf->viaopt != via_off && conf->viaopt != via_block) { + /* Create a "Via:" response header entry and merge it */ + i = ap_get_server_port(r); + if (ap_is_default_port(i,r)) { + strcpy(portstr,""); + } else { + ap_snprintf(portstr, sizeof portstr, ":%d", i); + } + ap_table_mergen((table *)resp_hdrs, "Via", + (conf->viaopt == via_full) + ? ap_psprintf(p, "%d.%d %s%s (%s)", + major, minor, + ap_get_server_name(r), portstr, + SERVER_BASEVERSION) + : ap_psprintf(p, "%d.%d %s%s", + major, minor, + ap_get_server_name(r), portstr) + ); + } + + /* strip hop-by-hop headers defined by Connection */ + ap_proxy_clear_connection(p, resp_hdrs); /* Now add out bound headers set by other modules */ resp_hdrs = ap_overlay_tables(r->pool, r->err_headers_out, resp_hdrs); - } +} else { -/* an http/0.9 response */ - backasswards = 1; - r->status = 200; - r->status_line = "200 OK"; + /* an http/0.9 response */ + backasswards = 1; + r->status = 200; + r->status_line = "200 OK"; -/* no headers */ - resp_hdrs = ap_make_table(p, 20); + /* no headers */ + resp_hdrs = ap_make_table(p, 20); } - c->hdrs = resp_hdrs; - ap_kill_timeout(r); -/* - * HTTP/1.0 requires us to accept 3 types of dates, but only generate - * one type - */ + /* + * HTTP/1.1 requires us to accept 3 types of dates, but only generate + * one type + */ if ((datestr = ap_table_get(resp_hdrs, "Date")) != NULL) - ap_table_set(resp_hdrs, "Date", ap_proxy_date_canon(p, datestr)); + ap_table_set(resp_hdrs, "Date", ap_proxy_date_canon(p, datestr)); if ((datestr = ap_table_get(resp_hdrs, "Last-Modified")) != NULL) - ap_table_set(resp_hdrs, "Last-Modified", ap_proxy_date_canon(p, datestr)); + ap_table_set(resp_hdrs, "Last-Modified", ap_proxy_date_canon(p, datestr)); if ((datestr = ap_table_get(resp_hdrs, "Expires")) != NULL) - ap_table_set(resp_hdrs, "Expires", ap_proxy_date_canon(p, datestr)); - - if ((datestr = ap_table_get(resp_hdrs, "Location")) != NULL) - ap_table_set(resp_hdrs, "Location", proxy_location_reverse_map(r, datestr)); - if ((datestr = ap_table_get(resp_hdrs, "URI")) != NULL) - ap_table_set(resp_hdrs, "URI", proxy_location_reverse_map(r, datestr)); - - /* - * If "Pragma: no-cache" set nocache and make reply un-buffered to - * ensure timely delivery - */ - if (((pragma = ap_table_get(resp_hdrs, "Pragma")) != NULL && - ap_proxy_liststr(pragma, "no-cache"))) { - nocache = 1; - r->connection->client->flags &= ~B_WR; - } + ap_table_set(resp_hdrs, "Expires", ap_proxy_date_canon(p, datestr)); + + /* handle the ProxyPassReverse mappings */ + if ((urlstr = ap_table_get(resp_hdrs, "Location")) != NULL) + ap_table_set(resp_hdrs, "Location", proxy_location_reverse_map(r, urlstr)); + if ((urlstr = ap_table_get(resp_hdrs, "URI")) != NULL) + ap_table_set(resp_hdrs, "URI", proxy_location_reverse_map(r, urlstr)); + if ((urlstr = ap_table_get(resp_hdrs, "Content-Location")) != NULL) + ap_table_set(resp_hdrs, "Content-Location", proxy_location_reverse_map(r , urlstr)); /* check if NoCache directive on this host */ if (nocache == 0) { - for (i = 0; i < conf->nocaches->nelts; i++) { - if (destaddr.s_addr == ncent[i].addr.s_addr || - (ncent[i].name != NULL && - (ncent[i].name[0] == '*' || - strstr(desthost, ncent[i].name) != NULL))) { - nocache = 1; - break; - } - } - } + for (i = 0; i < conf->nocaches->nelts; i++) { + if (destaddr.s_addr == ncent[i].addr.s_addr || + (ncent[i].name != NULL && + (ncent[i].name[0] == '*' || + strstr(desthost, ncent[i].name) != NULL))) { + nocache = 1; + break; + } + } + /* update the cache file, possibly even fulfilling the request if + * it turns out a conditional allowed us to serve the object from the + * cache... + */ i = ap_proxy_cache_update(c, resp_hdrs, !backasswards, nocache); if (i != DECLINED) { - ap_bclose(f); - return i; - } - - ap_hard_timeout("proxy receive", r); - -/* write status line */ - if (!r->assbackwards) - ap_rvputs(r, "HTTP/1.0 ", r->status_line, CRLF, NULL); - if (c != NULL && c->fp != NULL && - ap_bvputs(c->fp, "HTTP/1.0 ", r->status_line, CRLF, NULL) == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, - "proxy: error writing status line to %s", c->tempfile); - c = ap_proxy_cache_error(c); + ap_bclose(f); + return i; } -/* send headers */ - tdo.req = r; - tdo.cache = c; - ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL); - - if (!r->assbackwards) - ap_rputs(CRLF, r); - if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, - "proxy: error writing CRLF to %s", c->tempfile); - c = ap_proxy_cache_error(c); + /* write status line and headers to the cache file */ + ap_proxy_write_headers(c, ap_pstrcat(p, "HTTP/1.1 ", r->status_line, NULL), resp_hdrs); } - ap_bsetopt(r->connection->client, BO_BYTECT, &zero); - r->sent_bodyct = 1; -/* Is it an HTTP/0.9 respose? If so, send the extra data */ + /* Setup the headers for our client from upstreams response-headers */ + ap_overlap_tables(r->headers_out, resp_hdrs, AP_OVERLAP_TABLES_SET); + /* Add X-Cache header */ + ap_table_setn(r->headers_out, "X-Cache", + ap_pstrcat(r->pool, "MISS from ", + ap_get_server_name(r), NULL)); + /* The Content-Type of this response is the upstream one. */ + r->content_type = ap_table_get (r->headers_out, "Content-Type"); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "Content-Type: %s", r->content_type); + /* finally output the headers to the client */ + ap_send_http_header(r); + + /* Is it an HTTP/0.9 respose? If so, send the extra data we read + from upstream as the start of the reponse to client */ if (backasswards) { - ap_bwrite(r->connection->client, buffer, len); - if (c != NULL && c->fp != NULL && ap_bwrite(c->fp, buffer, len) != len) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, - "proxy: error writing extra data to %s", c->tempfile); - c = ap_proxy_cache_error(c); - } + ap_hard_timeout("proxy send assbackward", r); + + ap_bwrite(r->connection->client, buffer, len); + if (c != NULL && c->fp != NULL && ap_bwrite(c->fp, buffer, len) != len) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing extra data to %s", c->tempfile); + c = ap_proxy_cache_error(c); + } + ap_kill_timeout(r); } - ap_kill_timeout(r); + #ifdef CHARSET_EBCDIC /* What we read/write after the header should not be modified @@ -551,10 +564,17 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url, /* send body */ /* if header only, then cache will be NULL */ /* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */ +/* XXX CHANGEME: We want to eventually support keepalives, which means + * we must read content-length bytes... */ if (!r->header_only) { /* we need to set this for ap_proxy_send_fb()... */ - c->cache_completion = conf->cache.cache_completion; - ap_proxy_send_fb(f, r, c); + c->cache_completion = conf->cache.cache_completion; + +/* XXX CHECKME: c->len should be the expected content length, or -1 if the + * content length is not known. We need to make 100% sure c->len is always + * set correctly before we get here to correctly do keepalive. + */ + ap_proxy_send_fb(f, r, c, c->len, 0); } ap_proxy_cache_tidy(c); diff --git a/src/modules/proxy/proxy_util.c b/src/modules/proxy/proxy_util.c index bbadcd62600..5fc9ac44505 100644 --- a/src/modules/proxy/proxy_util.c +++ b/src/modules/proxy/proxy_util.c @@ -63,7 +63,7 @@ #include "multithread.h" #include "http_log.h" #include "util_uri.h" -#include "util_date.h" /* get ap_checkmask() decl. */ +#include "util_date.h" /* get ap_checkmask() decl. */ static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r); static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r); @@ -78,20 +78,20 @@ int ap_proxy_hex2c(const char *x) #ifndef CHARSET_EBCDIC ch = x[0]; if (ap_isdigit(ch)) - i = ch - '0'; + i = ch - '0'; else if (ap_isupper(ch)) - i = ch - ('A' - 10); + i = ch - ('A' - 10); else - i = ch - ('a' - 10); + i = ch - ('a' - 10); i <<= 4; ch = x[1]; if (ap_isdigit(ch)) - i += ch - '0'; + i += ch - '0'; else if (ap_isupper(ch)) - i += ch - ('A' - 10); + i += ch - ('A' - 10); else - i += ch - ('a' - 10); + i += ch - ('a' - 10); return i; #else /*CHARSET_EBCDIC*/ return (1 == sscanf(x, "%2x", &i)) ? os_toebcdic[i&0xFF] : 0; @@ -106,15 +106,15 @@ void ap_proxy_c2hex(int ch, char *x) x[0] = '%'; i = (ch & 0xF0) >> 4; if (i >= 10) - x[1] = ('A' - 10) + i; + x[1] = ('A' - 10) + i; else - x[1] = '0' + i; + x[1] = '0' + i; i = ch & 0x0F; if (i >= 10) - x[2] = ('A' - 10) + i; + x[2] = ('A' - 10) + i; else - x[2] = '0' + i; + x[2] = '0' + i; #else /*CHARSET_EBCDIC*/ static const char ntoa[] = { "0123456789ABCDEF" }; ch = os_toascii[ch & 0xFF]; @@ -136,12 +136,12 @@ void ap_proxy_c2hex(int ch, char *x) * those which must not be touched. */ char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t, - enum proxyreqtype isenc) + enum proxyreqtype isenc) { int i, j, ch; char *y; - const char *allowed; /* characters which should not be encoded */ - const char *reserved; /* characters which much not be en/de-coded */ + const char *allowed; /* characters which should not be encoded */ + const char *reserved; /* characters which much not be en/de-coded */ /* N.B. in addition to :@&=, this allows ';' in an http path * and '?' in an ftp path -- this may be revised @@ -151,51 +151,51 @@ char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t, * it only permits ; / ? : @ = & as reserved chars.) */ if (t == enc_path) - allowed = "$-_.+!*'(),;:@&="; + allowed = "$-_.+!*'(),;:@&="; else if (t == enc_search) - allowed = "$-_.!*'(),;:@&="; + allowed = "$-_.!*'(),;:@&="; else if (t == enc_user) - allowed = "$-_.+!*'(),;@&="; + allowed = "$-_.+!*'(),;@&="; else if (t == enc_fpath) - allowed = "$-_.+!*'(),?:@&="; - else /* if (t == enc_parm) */ - allowed = "$-_.+!*'(),?/:@&="; + allowed = "$-_.+!*'(),?:@&="; + else /* if (t == enc_parm) */ + allowed = "$-_.+!*'(),?/:@&="; if (t == enc_path) - reserved = "/"; + reserved = "/"; else if (t == enc_search) - reserved = "+"; + reserved = "+"; else - reserved = ""; + reserved = ""; y = ap_palloc(p, 3 * len + 1); for (i = 0, j = 0; i < len; i++, j++) { /* always handle '/' first */ - ch = x[i]; - if (strchr(reserved, ch)) { - y[j] = ch; - continue; - } + ch = x[i]; + if (strchr(reserved, ch)) { + y[j] = ch; + continue; + } /* decode it if not already done */ - if (isenc != NOT_PROXY && ch == '%') { - if (!ap_isxdigit(x[i + 1]) || !ap_isxdigit(x[i + 2])) - return NULL; - ch = ap_proxy_hex2c(&x[i + 1]); - i += 2; - if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */ - ap_proxy_c2hex(ch, &y[j]); - j += 2; - continue; - } - } + if (isenc != NOT_PROXY && ch == '%') { + if (!ap_isxdigit(x[i + 1]) || !ap_isxdigit(x[i + 2])) + return NULL; + ch = ap_proxy_hex2c(&x[i + 1]); + i += 2; + if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */ + ap_proxy_c2hex(ch, &y[j]); + j += 2; + continue; + } + } /* recode it, if necessary */ - if (!ap_isalnum(ch) && !strchr(allowed, ch)) { - ap_proxy_c2hex(ch, &y[j]); - j += 2; - } - else - y[j] = ch; + if (!ap_isalnum(ch) && !strchr(allowed, ch)) { + ap_proxy_c2hex(ch, &y[j]); + j += 2; + } + else + y[j] = ch; } y[j] = '\0'; return y; @@ -213,73 +213,73 @@ char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t, */ char * ap_proxy_canon_netloc(pool *p, char **const urlp, char **userp, - char **passwordp, char **hostp, int *port) + char **passwordp, char **hostp, int *port) { int i; char *strp, *host, *url = *urlp; char *user = NULL, *password = NULL; if (url[0] != '/' || url[1] != '/') - return "Malformed URL"; + return "Malformed URL"; host = url + 2; url = strchr(host, '/'); if (url == NULL) - url = ""; + url = ""; else - *(url++) = '\0'; /* skip seperating '/' */ + *(url++) = '\0'; /* skip seperating '/' */ /* find _last_ '@' since it might occur in user/password part */ strp = strrchr(host, '@'); if (strp != NULL) { - *strp = '\0'; - user = host; - host = strp + 1; + *strp = '\0'; + user = host; + host = strp + 1; /* find password */ - strp = strchr(user, ':'); - if (strp != NULL) { - *strp = '\0'; - password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, STD_PROXY); - if (password == NULL) - return "Bad %-escape in URL (password)"; - } - - user = ap_proxy_canonenc(p, user, strlen(user), enc_user, STD_PROXY); - if (user == NULL) - return "Bad %-escape in URL (username)"; + strp = strchr(user, ':'); + if (strp != NULL) { + *strp = '\0'; + password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, STD_PROXY); + if (password == NULL) + return "Bad %-escape in URL (password)"; + } + + user = ap_proxy_canonenc(p, user, strlen(user), enc_user, STD_PROXY); + if (user == NULL) + return "Bad %-escape in URL (username)"; } if (userp != NULL) { - *userp = user; + *userp = user; } if (passwordp != NULL) { - *passwordp = password; + *passwordp = password; } strp = strrchr(host, ':'); if (strp != NULL) { - *(strp++) = '\0'; - - for (i = 0; strp[i] != '\0'; i++) - if (!ap_isdigit(strp[i])) - break; - - /* if (i == 0) the no port was given; keep default */ - if (strp[i] != '\0') { - return "Bad port number in URL"; - } else if (i > 0) { - *port = atoi(strp); - if (*port > 65535) - return "Port number in URL > 65535"; - } + *(strp++) = '\0'; + + for (i = 0; strp[i] != '\0'; i++) + if (!ap_isdigit(strp[i])) + break; + + /* if (i == 0) the no port was given; keep default */ + if (strp[i] != '\0') { + return "Bad port number in URL"; + } else if (i > 0) { + *port = atoi(strp); + if (*port > 65535) + return "Port number in URL > 65535"; + } } - ap_str_tolower(host); /* DNS names are case-insensitive */ + ap_str_tolower(host); /* DNS names are case-insensitive */ if (*host == '\0') - return "Missing host in URL"; + return "Missing host in URL"; /* check hostname syntax */ for (i = 0; host[i] != '\0'; i++) - if (!ap_isdigit(host[i]) && host[i] != '.') - break; + if (!ap_isdigit(host[i]) && host[i] != '.') + break; /* must be an IP address */ #if defined(WIN32) || defined(NETWARE) || defined(TPF) || defined(BEOS) if (host[i] == '\0' && (inet_addr(host) == -1)) @@ -287,9 +287,12 @@ char * if (host[i] == '\0' && (ap_inet_addr(host) == -1 || inet_network(host) == -1)) #endif { - return "Bad IP address in URL"; + return "Bad IP address in URL"; } +/* if (strchr(host,'.') == NULL && domain != NULL) + host = pstrcat(p, host, domain, NULL); + */ *urlp = url; *hostp = host; @@ -315,49 +318,49 @@ const char * q = strchr(x, ','); /* check for RFC 850 date */ if (q != NULL && q - x > 3 && q[1] == ' ') { - *q = '\0'; - for (wk = 0; wk < 7; wk++) - if (strcmp(x, lwday[wk]) == 0) - break; - *q = ','; - if (wk == 7) - return x; /* not a valid date */ - if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' || - q[17] != ':' || strcmp(&q[20], " GMT") != 0) - return x; - if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year, - &hour, &min, &sec, zone) != 7) - return x; - if (year < 70) - year += 2000; - else - year += 1900; + *q = '\0'; + for (wk = 0; wk < 7; wk++) + if (strcmp(x, lwday[wk]) == 0) + break; + *q = ','; + if (wk == 7) + return x; /* not a valid date */ + if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' || + q[17] != ':' || strcmp(&q[20], " GMT") != 0) + return x; + if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year, + &hour, &min, &sec, zone) != 7) + return x; + if (year < 70) + year += 2000; + else + year += 1900; } else { /* check for acstime() date */ - if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' || - x[16] != ':' || x[19] != ' ' || x[24] != '\0') - return x; - if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour, - &min, &sec, &year) != 7) - return x; - for (wk = 0; wk < 7; wk++) - if (strcmp(week, ap_day_snames[wk]) == 0) - break; - if (wk == 7) - return x; + if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' || + x[16] != ':' || x[19] != ' ' || x[24] != '\0') + return x; + if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour, + &min, &sec, &year) != 7) + return x; + for (wk = 0; wk < 7; wk++) + if (strcmp(week, ap_day_snames[wk]) == 0) + break; + if (wk == 7) + return x; } /* check date */ for (mon = 0; mon < 12; mon++) - if (strcmp(month, ap_month_snames[mon]) == 0) - break; + if (strcmp(month, ap_month_snames[mon]) == 0) + break; if (mon == 12) - return x; + return x; q = ap_palloc(p, 30); ap_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", ap_day_snames[wk], mday, - ap_month_snames[mon], year, hour, min, sec); + ap_month_snames[mon], year, hour, min, sec); return q; } @@ -440,61 +443,66 @@ table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f) * the connection closes (EOF), or we timeout. */ while ((len = proxy_getline(buffer, size, f, 1)) > 0) { - - if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */ - - /* Buggy MS IIS servers sometimes return invalid headers - * (an extra "HTTP/1.0 200, OK" line sprinkled in between - * the usual MIME headers). Try to deal with it in a sensible - * way, but log the fact. - * XXX: The mask check is buggy if we ever see an HTTP/1.10 */ - - if (!ap_checkmask(buffer, "HTTP/#.# ###*")) { - /* Nope, it wasn't even an extra HTTP header. Give up. */ - return NULL; - } - - ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server, - "proxy: Ignoring duplicate HTTP header " - "returned by %s (%s)", r->uri, r->method); - continue; - } + + if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */ + + /* Buggy MS IIS servers sometimes return invalid headers + * (an extra "HTTP/1.0 200, OK" line sprinkled in between + * the usual MIME headers). Try to deal with it in a sensible + * way, but log the fact. + * XXX: The mask check is buggy if we ever see an HTTP/1.10 */ + + if (!ap_checkmask(buffer, "HTTP/#.# ###*")) { + /* Nope, it wasn't even an extra HTTP header. Give up. */ + return NULL; + } + + ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server, + "proxy: Ignoring duplicate HTTP header " + "returned by %s (%s)", r->uri, r->method); + continue; + } *value = '\0'; ++value; - /* XXX: RFC2068 defines only SP and HT as whitespace, this test is - * wrong... and so are many others probably. - */ + /* XXX: RFC2068 defines only SP and HT as whitespace, this test is + * wrong... and so are many others probably. + */ while (ap_isspace(*value)) ++value; /* Skip to start of value */ - /* should strip trailing whitespace as well */ - for (end = &value[strlen(value)-1]; end > value && ap_isspace(*end); --end) - *end = '\0'; + /* should strip trailing whitespace as well */ + for (end = &value[strlen(value)-1]; end > value && ap_isspace(*end); --end) + *end = '\0'; ap_table_add(resp_hdrs, buffer, value); - /* the header was too long; at the least we should skip extra data */ - if (len >= size - 1) { - while ((len = proxy_getline(field, MAX_STRING_LEN, f, 1)) - >= MAX_STRING_LEN - 1) { - /* soak up the extra data */ - } - if (len == 0) /* time to exit the larger loop as well */ - break; - } + /* the header was too long; at the least we should skip extra data */ + if (len >= size - 1) { + while ((len = proxy_getline(field, MAX_STRING_LEN, f, 1)) + >= MAX_STRING_LEN - 1) { + /* soak up the extra data */ + } + if (len == 0) /* time to exit the larger loop as well */ + break; + } } return resp_hdrs; } -long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c) +/* read data from f, write it to: + * - c->fp, if it is open + * - r->connection->client, if nowrite == 0 + */ + +long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c, off_t len, int nowrite) { int ok; char buf[IOBUFSIZE]; long total_bytes_rcvd; register int n, o, w; conn_rec *con = r->connection; - int alternate_timeouts = 1; /* 1 if we alternate between soft & hard timeouts */ + int alternate_timeouts = 1; /* 1 if we alternate between soft & hard timeouts */ total_bytes_rcvd = 0; if (c != NULL) @@ -504,7 +512,7 @@ long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c) /* The cache copy is ASCII, not EBCDIC, even for text/html) */ ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); if (c != NULL && c->fp != NULL) - ap_bsetflag(c->fp, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); + ap_bsetflag(c->fp, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); ap_bsetflag(con->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0); #endif @@ -542,41 +550,46 @@ long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c) if (alternate_timeouts) ap_hard_timeout("proxy recv body from upstream server", r); - /* Read block from server */ - n = ap_bread(f, buf, IOBUFSIZE); + /* Read block from server */ + if (-1 == len) { + n = ap_bread(f, buf, IOBUFSIZE); + } + else { + n = ap_bread(f, buf, MIN(IOBUFSIZE, len - total_bytes_rcvd)); + } if (alternate_timeouts) ap_kill_timeout(r); else ap_reset_timeout(r); - if (n == -1) { /* input error */ - if (c != NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, - "proxy: error reading from %s", c->url); - c = ap_proxy_cache_error(c); - } - break; - } - if (n == 0) - break; /* EOF */ - o = 0; - total_bytes_rcvd += n; - - /* Write to cache first. */ - /*@@@ XXX FIXME: Assuming that writing the cache file won't time out?!!? */ + if (n == -1) { /* input error */ + if (c != NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error reading from %s", c->url); + c = ap_proxy_cache_error(c); + } + break; + } + if (n == 0) + break; /* EOF */ + o = 0; + total_bytes_rcvd += n; + + /* Write to cache first. */ + /*@@@ XXX FIXME: Assuming that writing the cache file won't time out?!!? */ if (c != NULL && c->fp != NULL) { if (ap_bwrite(c->fp, &buf[0], n) != n) { ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, - "proxy: error writing to %s", c->tempfile); - c = ap_proxy_cache_error(c); + "proxy: error writing to %s", c->tempfile); + c = ap_proxy_cache_error(c); } else { c->written += n; } } - /* Write the block to the client, detect aborted transfers */ - while (!con->aborted && n > 0) { + /* Write the block to the client, detect aborted transfers */ + while (!nowrite && !con->aborted && n > 0) { if (alternate_timeouts) ap_soft_timeout("proxy send body", r); @@ -603,7 +616,7 @@ long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c) c->fp = NULL; } unlink(c->tempfile); - c = NULL; + c = NULL; } } con->aborted = 1; @@ -612,38 +625,45 @@ long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c) n -= w; o += w; } /* while client alive and more data to send */ + + /* if we've received everything, leave now */ + if (total_bytes_rcvd == len) + break; + } /* loop and ap_bread while "ok" */ if (!con->aborted) - ap_bflush(con->client); + ap_bflush(con->client); ap_kill_timeout(r); return total_bytes_rcvd; } /* - * Sends response line and headers. Uses the client fd and the - * headers_out array from the passed request_rec to talk to the client - * and to properly set the headers it sends for things such as logging. - * - * A timeout should be set before calling this routine. + * Writes response line and headers to the cache file. + * + * If respline is NULL, no response line will be written. */ -void ap_proxy_send_headers(request_rec *r, const char *respline, table *t) +void ap_proxy_write_headers(cache_req *c, const char *respline, table *t) { - int i; - BUFF *fp = r->connection->client; - table_entry *elts = (table_entry *) ap_table_elts(t)->elts; + /* write status line */ + if (respline && c->fp != NULL && + ap_bvputs(c->fp, respline, CRLF, NULL) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing status line to %s", c->tempfile); + c = ap_proxy_cache_error(c); + return; + } - ap_bvputs(fp, respline, CRLF, NULL); + /* write response headers to the cache file */ + ap_table_do(ap_proxy_send_hdr_line, c, t, NULL); - for (i = 0; i < ap_table_elts(t)->nelts; ++i) { - if (elts[i].key != NULL) { - ap_bvputs(fp, elts[i].key, ": ", elts[i].val, CRLF, NULL); - ap_table_addn(r->headers_out, elts[i].key, elts[i].val); - } + /* write terminating CRLF */ + if (c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing CRLF to %s", c->tempfile); + c = ap_proxy_cache_error(c); } - - ap_bputs(CRLF, fp); } @@ -653,29 +673,45 @@ void ap_proxy_send_headers(request_rec *r, const char *respline, table *t) * The return returns 1 if the token val is found in the list, or 0 * otherwise. */ -int ap_proxy_liststr(const char *list, const char *val) +int ap_proxy_liststr(const char *list, const char *key, char **val) { int len, i; const char *p; + char valbuf[HUGE_STRING_LEN]; + valbuf[sizeof(valbuf)-1] = 0; /* safety terminating zero */ - len = strlen(val); + len = strlen(key); while (list != NULL) { - p = strchr(list, ','); - if (p != NULL) { - i = p - list; - do - p++; - while (ap_isspace(*p)); - } - else - i = strlen(list); - - while (i > 0 && ap_isspace(list[i - 1])) - i--; - if (i == len && strncasecmp(list, val, len) == 0) - return 1; - list = p; + p = strchr(list, ','); + if (p != NULL) { + i = p - list; + do + p++; + while (ap_isspace(*p)); + } + else + i = strlen(list); + + while (i > 0 && ap_isspace(list[i - 1])) + i--; + if (i == len && strncasecmp(list, key, len) == 0) { + if (val) { + p = strchr(list, ','); + while (ap_isspace(*list)) { + list++; + } + if ('=' == list[0]) + list++; + while (ap_isspace(*list)) { + list++; + } + strncpy(valbuf, list, MIN(p-list, sizeof(valbuf)-1)); + *val = valbuf; + } + return 1; + } + list = p; } return 0; } @@ -704,28 +740,28 @@ void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength) * i.e. 128 bits is 3 x 5 bytes + 1 byte -> 3 * 8 characters + 2 characters */ for (i = 0, k = 0; i < 15; i += 5) { - x = (digest[i] << 24) | (digest[i + 1] << 16) | (digest[i + 2] << 8) | digest[i + 3]; - tmp[k++] = enc_table[x >> 27]; - tmp[k++] = enc_table[(x >> 22) & 0x1f]; - tmp[k++] = enc_table[(x >> 17) & 0x1f]; - tmp[k++] = enc_table[(x >> 12) & 0x1f]; - tmp[k++] = enc_table[(x >> 7) & 0x1f]; - tmp[k++] = enc_table[(x >> 2) & 0x1f]; - x = ((x & 0x3) << 8) | digest[i + 4]; - tmp[k++] = enc_table[x >> 5]; - tmp[k++] = enc_table[x & 0x1f]; + x = (digest[i] << 24) | (digest[i + 1] << 16) | (digest[i + 2] << 8) | digest[i + 3]; + tmp[k++] = enc_table[x >> 27]; + tmp[k++] = enc_table[(x >> 22) & 0x1f]; + tmp[k++] = enc_table[(x >> 17) & 0x1f]; + tmp[k++] = enc_table[(x >> 12) & 0x1f]; + tmp[k++] = enc_table[(x >> 7) & 0x1f]; + tmp[k++] = enc_table[(x >> 2) & 0x1f]; + x = ((x & 0x3) << 8) | digest[i + 4]; + tmp[k++] = enc_table[x >> 5]; + tmp[k++] = enc_table[x & 0x1f]; } /* one byte left */ x = digest[15]; - tmp[k++] = enc_table[x >> 3]; /* use up 5 bits */ + tmp[k++] = enc_table[x >> 3]; /* use up 5 bits */ tmp[k++] = enc_table[x & 0x7]; /* now split into directory levels */ for (i = k = d = 0; d < ndepth; ++d) { - memcpy(&val[i], &tmp[k], nlength); - k += nlength; - val[i + nlength] = '/'; - i += nlength + 1; + memcpy(&val[i], &tmp[k], nlength); + k += nlength; + val[i + nlength] = '/'; + i += nlength + 1; } memcpy(&val[i], &tmp[k], 26 - k); val[i + 26 - k] = '\0'; @@ -744,10 +780,10 @@ void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength) /* Believe it or not, AIX 1.x does not allow you to name a file '@', * so hack around it in the encoding. */ static const char enc_table[64] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_%"; + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_%"; #else static const char enc_table[64] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@"; + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@"; #endif ap_MD5Init(&context); @@ -759,23 +795,23 @@ void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength) * i.e. 128 bits is 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters */ for (i = 0, k = 0; i < 15; i += 3) { - x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2]; - tmp[k++] = enc_table[x >> 18]; - tmp[k++] = enc_table[(x >> 12) & 0x3f]; - tmp[k++] = enc_table[(x >> 6) & 0x3f]; - tmp[k++] = enc_table[x & 0x3f]; + x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2]; + tmp[k++] = enc_table[x >> 18]; + tmp[k++] = enc_table[(x >> 12) & 0x3f]; + tmp[k++] = enc_table[(x >> 6) & 0x3f]; + tmp[k++] = enc_table[x & 0x3f]; } /* one byte left */ x = digest[15]; - tmp[k++] = enc_table[x >> 2]; /* use up 6 bits */ + tmp[k++] = enc_table[x >> 2]; /* use up 6 bits */ tmp[k++] = enc_table[(x << 4) & 0x3f]; /* now split into directory levels */ for (i = k = d = 0; d < ndepth; ++d) { - memcpy(&val[i], &tmp[k], nlength); - k += nlength; - val[i + nlength] = '/'; - i += nlength + 1; + memcpy(&val[i], &tmp[k], nlength); + k += nlength; + val[i + nlength] = '/'; + i += nlength + 1; } memcpy(&val[i], &tmp[k], 22 - k); val[i + 22 - k] = '\0'; @@ -784,57 +820,68 @@ void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength) #endif /* CASE_BLIND_FILESYSTEM */ /* - * Converts 8 hex digits to a time integer + * Converts 16 hex digits to a time integer */ int ap_proxy_hex2sec(const char *x) { int i, ch; unsigned int j; - for (i = 0, j = 0; i < 8; i++) { - ch = x[i]; - j <<= 4; - if (ap_isdigit(ch)) - j |= ch - '0'; - else if (ap_isupper(ch)) - j |= ch - ('A' - 10); - else - j |= ch - ('a' - 10); + for (i = 0, j = 0; i < 16; i++) { + ch = x[i]; + j <<= 4; + if (ap_isdigit(ch)) + j |= ch - '0'; + else if (ap_isupper(ch)) + j |= ch - ('A' - 10); + else + j |= ch - ('a' - 10); } - if (j == 0xffffffff) - return -1; /* so that it works with 8-byte ints */ - else - return j; +/* no longer necessary, as the source hex is 8-byte int */ +/* if (j == 0xffffffff)*/ +/* return -1;*/ /* so that it works with 8-byte ints */ +/* else */ + return j; } /* - * Converts a time integer to 8 hex digits + * Converts a time integer to 16 hex digits */ void ap_proxy_sec2hex(int t, char *y) { int i, ch; unsigned int j = t; - for (i = 7; i >= 0; i--) { - ch = j & 0xF; - j >>= 4; - if (ch >= 10) - y[i] = ch + ('A' - 10); - else - y[i] = ch + '0'; + if (-1 == t) { + strcpy(y, "FFFFFFFFFFFFFFFF"); + return; } - y[8] = '\0'; + + for (i = 15; i >= 0; i--) { + ch = j & 0xF; + j >>= 4; + if (ch >= 10) + y[i] = ch + ('A' - 10); + else + y[i] = ch + '0'; + } + y[16] = '\0'; } cache_req *ap_proxy_cache_error(cache_req *c) { if (c != NULL) { - if (c->fp != NULL) { - ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)); - c->fp = NULL; - } - if (c->tempfile) unlink(c->tempfile); + if (c->fp != NULL) { + ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)); + c->fp = NULL; + } + if (c->origfp != NULL) { + ap_pclosef(c->req->pool, ap_bfileno(c->origfp, B_WR)); + c->origfp = NULL; + } + if (c->tempfile) + unlink(c->tempfile); } return NULL; } @@ -842,15 +889,15 @@ cache_req *ap_proxy_cache_error(cache_req *c) int ap_proxyerror(request_rec *r, int statuscode, const char *message) { ap_table_setn(r->notes, "error-notes", - ap_pstrcat(r->pool, - "The proxy server could not handle the request " - "pool, r->uri), - "\">", ap_escape_html(r->pool, r->method), - " ", - ap_escape_html(r->pool, r->uri), ".

\n" - "Reason: ", - ap_escape_html(r->pool, message), - "", NULL)); + ap_pstrcat(r->pool, + "The proxy server could not handle the request " + "pool, r->uri), + "\">", ap_escape_html(r->pool, r->method), + " ", + ap_escape_html(r->pool, r->uri), ".

\n" + "Reason: ", + ap_escape_html(r->pool, message), + "", NULL)); /* Allow "error-notes" string to be printed by ap_send_error_response() */ ap_table_setn(r->notes, "verbose-error-to", ap_pstrdup(r->pool, "*")); @@ -870,27 +917,27 @@ const char * struct per_thread_data *ptd = get_per_thread_data(); for (i = 0; host[i] != '\0'; i++) - if (!ap_isdigit(host[i]) && host[i] != '.') - break; + if (!ap_isdigit(host[i]) && host[i] != '.') + break; if (host[i] != '\0') { - hp = gethostbyname(host); - if (hp == NULL) - return "Host not found"; + hp = gethostbyname(host); + if (hp == NULL) + return "Host not found"; } else { - ptd->ipaddr = ap_inet_addr(host); - hp = gethostbyaddr((char *) &ptd->ipaddr, sizeof(ptd->ipaddr), AF_INET); - if (hp == NULL) { - memset(&ptd->hpbuf, 0, sizeof(ptd->hpbuf)); - ptd->hpbuf.h_name = 0; - ptd->hpbuf.h_addrtype = AF_INET; - ptd->hpbuf.h_length = sizeof(ptd->ipaddr); - ptd->hpbuf.h_addr_list = ptd->charpbuf; - ptd->hpbuf.h_addr_list[0] = (char *) &ptd->ipaddr; - ptd->hpbuf.h_addr_list[1] = 0; - hp = &ptd->hpbuf; - } + ptd->ipaddr = ap_inet_addr(host); + hp = gethostbyaddr((char *) &ptd->ipaddr, sizeof(ptd->ipaddr), AF_INET); + if (hp == NULL) { + memset(&ptd->hpbuf, 0, sizeof(ptd->hpbuf)); + ptd->hpbuf.h_name = 0; + ptd->hpbuf.h_addrtype = AF_INET; + ptd->hpbuf.h_length = sizeof(ptd->ipaddr); + ptd->hpbuf.h_addr_list = ptd->charpbuf; + ptd->hpbuf.h_addr_list[0] = (char *) &ptd->ipaddr; + ptd->hpbuf.h_addr_list[1] = 0; + hp = &ptd->hpbuf; + } } *reqhp = *hp; return NULL; @@ -903,24 +950,24 @@ static const char * int port = -1; if (r->hostname != NULL) - return r->hostname; + return r->hostname; /* Set url to the first char after "scheme://" */ if ((url = strchr(r->uri, ':')) == NULL - || url[1] != '/' || url[2] != '/') - return NULL; + || url[1] != '/' || url[2] != '/') + return NULL; - url = ap_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */ + url = ap_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */ err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port); if (err != NULL) - ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, - "%s", err); + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, + "%s", err); r->hostname = host; - return host; /* ought to return the port, too */ + return host; /* ought to return the port, too */ } /* Return TRUE if addr represents an IP address (or an IP network address) */ @@ -945,84 +992,84 @@ int ap_proxy_is_ipaddr(struct dirconn_entry *This, pool *p) /* Iterate over up to 4 (dotted) quads. */ for (quads = 0; quads < 4 && *addr != '\0'; ++quads) { - char *tmp; + char *tmp; - if (*addr == '/' && quads > 0) /* netmask starts here. */ - break; + if (*addr == '/' && quads > 0) /* netmask starts here. */ + break; - if (!ap_isdigit(*addr)) - return 0; /* no digit at start of quad */ + if (!ap_isdigit(*addr)) + return 0; /* no digit at start of quad */ - ip_addr[quads] = strtol(addr, &tmp, 0); + ip_addr[quads] = strtol(addr, &tmp, 0); - if (tmp == addr) /* expected a digit, found something else */ - return 0; + if (tmp == addr) /* expected a digit, found something else */ + return 0; - if (ip_addr[quads] < 0 || ip_addr[quads] > 255) { - /* invalid octet */ - return 0; - } + if (ip_addr[quads] < 0 || ip_addr[quads] > 255) { + /* invalid octet */ + return 0; + } - addr = tmp; + addr = tmp; - if (*addr == '.' && quads != 3) - ++addr; /* after the 4th quad, a dot would be illegal */ + if (*addr == '.' && quads != 3) + ++addr; /* after the 4th quad, a dot would be illegal */ } for (This->addr.s_addr = 0, i = 0; i < quads; ++i) - This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); + This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); - if (addr[0] == '/' && ap_isdigit(addr[1])) { /* net mask follows: */ - char *tmp; + if (addr[0] == '/' && ap_isdigit(addr[1])) { /* net mask follows: */ + char *tmp; - ++addr; + ++addr; - bits = strtol(addr, &tmp, 0); + bits = strtol(addr, &tmp, 0); - if (tmp == addr) /* expected a digit, found something else */ - return 0; + if (tmp == addr) /* expected a digit, found something else */ + return 0; - addr = tmp; + addr = tmp; - if (bits < 0 || bits > 32) /* netmask must be between 0 and 32 */ - return 0; + if (bits < 0 || bits > 32) /* netmask must be between 0 and 32 */ + return 0; } else { - /* Determine (i.e., "guess") netmask by counting the */ - /* number of trailing .0's; reduce #quads appropriately */ - /* (so that 192.168.0.0 is equivalent to 192.168.) */ - while (quads > 0 && ip_addr[quads - 1] == 0) - --quads; - - /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */ - if (quads < 1) - return 0; - - /* every zero-byte counts as 8 zero-bits */ - bits = 8 * quads; - - if (bits != 32) /* no warning for fully qualified IP address */ - fprintf(stderr, "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n", - inet_ntoa(This->addr), bits); + /* Determine (i.e., "guess") netmask by counting the */ + /* number of trailing .0's; reduce #quads appropriately */ + /* (so that 192.168.0.0 is equivalent to 192.168.) */ + while (quads > 0 && ip_addr[quads - 1] == 0) + --quads; + + /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */ + if (quads < 1) + return 0; + + /* every zero-byte counts as 8 zero-bits */ + bits = 8 * quads; + + if (bits != 32) /* no warning for fully qualified IP address */ + fprintf(stderr, "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n", + inet_ntoa(This->addr), bits); } This->mask.s_addr = htonl(INADDR_NONE << (32 - bits)); if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) { - fprintf(stderr, "Warning: NetMask and IP-Addr disagree in %s/%ld\n", - inet_ntoa(This->addr), bits); - This->addr.s_addr &= This->mask.s_addr; - fprintf(stderr, " Set to %s/%ld\n", - inet_ntoa(This->addr), bits); + fprintf(stderr, "Warning: NetMask and IP-Addr disagree in %s/%ld\n", + inet_ntoa(This->addr), bits); + This->addr.s_addr &= This->mask.s_addr; + fprintf(stderr, " Set to %s/%ld\n", + inet_ntoa(This->addr), bits); } if (*addr == '\0') { - This->matcher = proxy_match_ipaddr; - return 1; + This->matcher = proxy_match_ipaddr; + return 1; } else - return (*addr == '\0'); /* okay iff we've parsed the whole string */ + return (*addr == '\0'); /* okay iff we've parsed the whole string */ } /* Return TRUE if addr represents an IP address (or an IP network address) */ @@ -1043,62 +1090,62 @@ static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r) memset(ip_addr, '\0', sizeof ip_addr); if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) { - for (addr.s_addr = 0, i = 0; i < 4; ++i) - addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); + for (addr.s_addr = 0, i = 0; i < 4; ++i) + addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); - if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) { + if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) { #if DEBUGGING - fprintf(stderr, "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr)); - fprintf(stderr, "%s/", inet_ntoa(This->addr)); - fprintf(stderr, "%s\n", inet_ntoa(This->mask)); + fprintf(stderr, "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr)); + fprintf(stderr, "%s/", inet_ntoa(This->addr)); + fprintf(stderr, "%s\n", inet_ntoa(This->mask)); #endif - return 1; - } + return 1; + } #if DEBUGGING - else { - fprintf(stderr, "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr)); - fprintf(stderr, "%s/", inet_ntoa(This->addr)); - fprintf(stderr, "%s\n", inet_ntoa(This->mask)); - } + else { + fprintf(stderr, "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr)); + fprintf(stderr, "%s/", inet_ntoa(This->addr)); + fprintf(stderr, "%s\n", inet_ntoa(This->mask)); + } #endif } else { - struct hostent the_host; + struct hostent the_host; - memset(&the_host, '\0', sizeof the_host); - found = ap_proxy_host2addr(host, &the_host); + memset(&the_host, '\0', sizeof the_host); + found = ap_proxy_host2addr(host, &the_host); - if (found != NULL) { + if (found != NULL) { #if DEBUGGING - fprintf(stderr, "2)IP-NoMatch: hostname=%s msg=%s\n", host, found); + fprintf(stderr, "2)IP-NoMatch: hostname=%s msg=%s\n", host, found); #endif - return 0; - } - - if (the_host.h_name != NULL) - found = the_host.h_name; - else - found = host; - - /* Try to deal with multiple IP addr's for a host */ - for (ip_listptr = the_host.h_addr_list; *ip_listptr; ++ip_listptr) { - ip_list = (struct in_addr *) *ip_listptr; - if (This->addr.s_addr == (ip_list->s_addr & This->mask.s_addr)) { + return 0; + } + + if (the_host.h_name != NULL) + found = the_host.h_name; + else + found = host; + + /* Try to deal with multiple IP addr's for a host */ + for (ip_listptr = the_host.h_addr_list; *ip_listptr; ++ip_listptr) { + ip_list = (struct in_addr *) *ip_listptr; + if (This->addr.s_addr == (ip_list->s_addr & This->mask.s_addr)) { #if DEBUGGING - fprintf(stderr, "3)IP-Match: %s[%s] <-> ", found, inet_ntoa(*ip_list)); - fprintf(stderr, "%s/", inet_ntoa(This->addr)); - fprintf(stderr, "%s\n", inet_ntoa(This->mask)); + fprintf(stderr, "3)IP-Match: %s[%s] <-> ", found, inet_ntoa(*ip_list)); + fprintf(stderr, "%s/", inet_ntoa(This->addr)); + fprintf(stderr, "%s\n", inet_ntoa(This->mask)); #endif - return 1; - } + return 1; + } #if DEBUGGING - else { - fprintf(stderr, "3)IP-NoMatch: %s[%s] <-> ", found, inet_ntoa(*ip_list)); - fprintf(stderr, "%s/", inet_ntoa(This->addr)); - fprintf(stderr, "%s\n", inet_ntoa(This->mask)); - } + else { + fprintf(stderr, "3)IP-NoMatch: %s[%s] <-> ", found, inet_ntoa(*ip_list)); + fprintf(stderr, "%s/", inet_ntoa(This->addr)); + fprintf(stderr, "%s\n", inet_ntoa(This->mask)); + } #endif - } + } } return 0; @@ -1112,25 +1159,25 @@ int ap_proxy_is_domainname(struct dirconn_entry *This, pool *p) /* Domain name must start with a '.' */ if (addr[0] != '.') - return 0; + return 0; /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ for (i = 0; ap_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) - continue; + continue; #if 0 if (addr[i] == ':') { - fprintf(stderr, "@@@@ handle optional port in proxy_is_domainname()\n"); - /* @@@@ handle optional port */ + fprintf(stderr, "@@@@ handle optional port in proxy_is_domainname()\n"); + /* @@@@ handle optional port */ } #endif if (addr[i] != '\0') - return 0; + return 0; /* Strip trailing dots */ for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) - addr[i] = '\0'; + addr[i] = '\0'; This->matcher = proxy_match_domainname; return 1; @@ -1142,19 +1189,19 @@ static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r) const char *host = proxy_get_host_of_request(r); int d_len = strlen(This->name), h_len; - if (host == NULL) /* some error was logged already */ - return 0; + if (host == NULL) /* some error was logged already */ + return 0; h_len = strlen(host); /* @@@ do this within the setup? */ /* Ignore trailing dots in domain comparison: */ while (d_len > 0 && This->name[d_len - 1] == '.') - --d_len; + --d_len; while (h_len > 0 && host[h_len - 1] == '.') - --h_len; + --h_len; return h_len > d_len - && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0; + && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0; } /* Return TRUE if addr represents a host name */ @@ -1166,26 +1213,26 @@ int ap_proxy_is_hostname(struct dirconn_entry *This, pool *p) /* Host names must not start with a '.' */ if (addr[0] == '.') - return 0; + return 0; /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ for (i = 0; ap_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i); #if 0 if (addr[i] == ':') { - fprintf(stderr, "@@@@ handle optional port in proxy_is_hostname()\n"); - /* @@@@ handle optional port */ + fprintf(stderr, "@@@@ handle optional port in proxy_is_hostname()\n"); + /* @@@@ handle optional port */ } #endif if (addr[i] != '\0' || ap_proxy_host2addr(addr, &host) != NULL) - return 0; + return 0; This->hostentry = ap_pduphostent (p, &host); /* Strip trailing dots */ for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) - addr[i] = '\0'; + addr[i] = '\0'; This->matcher = proxy_match_hostname; return 1; @@ -1210,17 +1257,17 @@ static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r) /* Try to deal with multiple IP addr's for a host */ for (ip_list = *This->hostentry->h_addr_list; *ip_list != 0UL; ++ip_list) - if (*ip_list == ? ? ? ? ? ? ? ? ? ? ? ? ?) - return 1; + if (*ip_list == ? ? ? ? ? ? ? ? ? ? ? ? ?) + return 1; #endif /* Ignore trailing dots in host2 comparison: */ while (h2_len > 0 && host2[h2_len - 1] == '.') - --h2_len; + --h2_len; while (h1_len > 0 && host[h1_len - 1] == '.') - --h1_len; + --h1_len; return h1_len == h2_len - && strncasecmp(host, host2, h1_len) == 0; + && strncasecmp(host, host2, h1_len) == 0; } /* Return TRUE if addr is to be matched as a word */ @@ -1243,41 +1290,38 @@ int ap_proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r) ap_hard_timeout("proxy connect", r); do { - i = connect(sock, (struct sockaddr *) addr, sizeof(struct sockaddr_in)); + i = connect(sock, (struct sockaddr *) addr, sizeof(struct sockaddr_in)); #if defined(WIN32) || defined(NETWARE) - if (i == SOCKET_ERROR) - errno = WSAGetLastError(); + if (i == SOCKET_ERROR) + errno = WSAGetLastError(); #endif /* WIN32 */ } while (i == -1 && errno == EINTR); if (i == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "proxy connect to %s port %d failed", - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy connect to %s port %d failed", + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); } ap_kill_timeout(r); return i; } -/* This function is called by ap_table_do() for all header lines */ -/* (from proxy_http.c and proxy_ftp.c) */ -/* It is passed a table_do_args struct pointer and a MIME field and value pair */ +/* This function is called by ap_table_do() for all header lines + * (from proxy_http.c and proxy_ftp.c) + * It is passed a cache_req struct pointer and a MIME field and value pair + */ int ap_proxy_send_hdr_line(void *p, const char *key, const char *value) { - struct tbl_do_args *parm = (struct tbl_do_args *)p; - - if (key == NULL) - return 1; - if (value == NULL) - value = ""; - if (!parm->req->assbackwards) - ap_rvputs(parm->req, key, ": ", value, CRLF, NULL); - ap_table_add(parm->req->headers_out, key, value); - if (parm->cache != NULL && parm->cache->fp != NULL && - ap_bvputs(parm->cache->fp, key, ": ", value, CRLF, NULL) == -1) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, parm->cache->req, - "proxy: error writing header to %s", parm->cache->tempfile); - parm->cache = ap_proxy_cache_error(parm->cache); + cache_req *c = (cache_req *)p; + + if (key == NULL || value == NULL || value[0] == '\0') + return 1; + if (c->fp != NULL && + ap_bvputs(c->fp, key, ": ", value, CRLF, NULL) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing header to %s", c->tempfile); + c = ap_proxy_cache_error(c); + return 0; /* no need to continue, it failed already */ } return 1; /* tell ap_table_do() to continue calling us for more headers */ } @@ -1287,10 +1331,114 @@ unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache) { unsigned len = ap_bputs(data, client); if (cache != NULL && cache->fp != NULL) - ap_bputs(data, cache->fp); + ap_bputs(data, cache->fp); return len; } +/* do a HTTP/1.1 age calculation */ +time_t ap_proxy_current_age(cache_req *c, const time_t age_value) +{ + time_t apparent_age, corrected_received_age, response_delay, corrected_initial_age, resident_time, current_age; + + /* Perform an HTTP/1.1 age calculation. (RFC2616 13.2.3) */ + + apparent_age = MAX(0, c->resp_time - c->date); + corrected_received_age = MAX(apparent_age, age_value); + response_delay = c->resp_time - c->req_time; + corrected_initial_age = corrected_received_age + response_delay; + resident_time = time(NULL) - c->resp_time; + current_age = corrected_initial_age + resident_time; + + return (current_age); +} + +/* open a cache file and return a pointer to a BUFF */ +BUFF *ap_proxy_open_cachefile(request_rec *r, char *filename) +{ + BUFF *cachefp = NULL; + int cfd; + + if (filename != NULL) { + cfd = open(filename, O_RDWR | O_BINARY); + if (cfd != -1) { + ap_note_cleanups_for_fd(r->pool, cfd); + cachefp = ap_bcreate(r->pool, B_RD | B_WR); + ap_bpushfd(cachefp, cfd, cfd); + } + else if (errno != ENOENT) + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error opening cache file %s", + filename); + else + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "File %s not found", filename); + + } + return cachefp; +} + +/* create a cache file and return a pointer to a BUFF */ +BUFF *ap_proxy_create_cachefile(request_rec *r, char *filename) +{ + BUFF *cachefp = NULL; + int cfd; + + if (filename != NULL) { + cfd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0622); + if (cfd != -1) { + ap_note_cleanups_for_fd(r->pool, cfd); + cachefp = ap_bcreate(r->pool, B_WR); + ap_bpushfd(cachefp, -1, cfd); + } + else if (errno != ENOENT) + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error creating cache file %s", + filename); + } + return cachefp; +} + +/* Clear all connection-based headers from headers table */ +void ap_proxy_clear_connection(pool *p, table *headers) +{ + const char *name; + char *next = ap_pstrdup(p, ap_table_get(headers, "Connection")); + + ap_table_unset(headers, "Proxy-Connection"); + if (!next) + return; + + while (*next) { + name = next; + while (*next && !ap_isspace(*next) && (*next != ',')) + ++next; + while (*next && (ap_isspace(*next) || (*next == ','))) { + *next = '\0'; + ++next; + } + ap_table_unset(headers, name); + } + ap_table_unset(headers, "Connection"); +} + +/* overlay one table on another + * keys in base will be replaced by keys in overlay + */ +int ap_proxy_table_replace(table *base, table *overlay) +{ + table_entry *elts = (table_entry *) overlay->a.elts; + int i, q = 0; + const char *val; + + for (i = 0; i < overlay->a.nelts; ++i) { + val = ap_table_get(base, elts[i].key); + if (!val || strcmp(val, elts[i].val)) + q = 1; + ap_table_set(base, elts[i].key, elts[i].val); + } + + return q; +} + #if defined WIN32 static DWORD tls_index; @@ -1301,17 +1449,17 @@ BOOL WINAPI DllMain (HINSTANCE dllhandle, DWORD reason, LPVOID reserved) switch (reason) { case DLL_PROCESS_ATTACH: - tls_index = TlsAlloc(); + tls_index = TlsAlloc(); case DLL_THREAD_ATTACH: /* intentional no break */ - TlsSetValue (tls_index, malloc (sizeof (struct per_thread_data))); - break; + TlsSetValue (tls_index, malloc (sizeof (struct per_thread_data))); + break; case DLL_THREAD_DETACH: - memptr = TlsGetValue (tls_index); - if (memptr) { - free (memptr); - TlsSetValue (tls_index, 0); - } - break; + memptr = TlsGetValue (tls_index); + if (memptr) { + free (memptr); + TlsSetValue (tls_index, 0); + } + break; } return TRUE;