X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;ds=sidebyside;f=server%2Fcore.c;h=e5f5ef0da71d35a41adfe46d7b753d775f9d4ba2;hb=aa21671e13767135f0ee3f88d6a3ff6d039e6534;hp=bff8cb4ad2fef4f6d11143cf1453543f231faa73;hpb=2604f8bdbb31b86c446dc7a6f7cb41361ac42c4d;p=apache diff --git a/server/core.c b/server/core.c index bff8cb4ad2..e5f5ef0da7 100644 --- a/server/core.c +++ b/server/core.c @@ -1,7 +1,7 @@ /* ==================================================================== * The Apache Software License, Version 1.1 * - * Copyright (c) 2000-2002 The Apache Software Foundation. All rights + * Copyright (c) 2000-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without @@ -79,7 +79,6 @@ #include "http_vhost.h" #include "http_main.h" /* For the default_handler below... */ #include "http_log.h" -#include "rfc1413.h" #include "util_md5.h" #include "http_connection.h" #include "apr_buckets.h" @@ -144,7 +143,6 @@ static void *create_core_dir_config(apr_pool_t *a, char *dir) conf->use_canonical_name = USE_CANONICAL_NAME_UNSET; conf->hostname_lookups = HOSTNAME_LOOKUP_UNSET; - conf->do_rfc1413 = DEFAULT_RFC1413 | 2; /* set bit 1 to indicate default */ conf->satisfy = SATISFY_NOSPEC; #ifdef RLIMIT_CPU @@ -181,6 +179,8 @@ static void *create_core_dir_config(apr_pool_t *a, char *dir) conf->etag_remove = ETAG_UNSET; conf->enable_mmap = ENABLE_MMAP_UNSET; + conf->enable_sendfile = ENABLE_SENDFILE_UNSET; + conf->allow_encoded_slashes = 0; return (void *)conf; } @@ -322,10 +322,6 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv) conf->hostname_lookups = new->hostname_lookups; } - if ((new->do_rfc1413 & 2) == 0) { - conf->do_rfc1413 = new->do_rfc1413; - } - if ((new->content_md5 & 2) == 0) { conf->content_md5 = new->content_md5; } @@ -447,6 +443,12 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv) conf->enable_mmap = new->enable_mmap; } + if (new->enable_sendfile != ENABLE_SENDFILE_UNSET) { + conf->enable_sendfile = new->enable_sendfile; + } + + conf->allow_encoded_slashes = new->allow_encoded_slashes; + return (void*)conf; } @@ -466,6 +468,10 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s) conf->sec_dir = apr_array_make(a, 40, sizeof(ap_conf_vector_t *)); conf->sec_url = apr_array_make(a, 40, sizeof(ap_conf_vector_t *)); + /* recursion stopper */ + conf->redirect_limit = 0; /* 0 == unset */ + conf->subreq_limit = 0; + return (void *)conf; } @@ -489,6 +495,14 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv) conf->sec_dir = apr_array_append(p, base->sec_dir, virt->sec_dir); conf->sec_url = apr_array_append(p, base->sec_url, virt->sec_url); + conf->redirect_limit = virt->redirect_limit + ? virt->redirect_limit + : base->redirect_limit; + + conf->subreq_limit = virt->subreq_limit + ? virt->subreq_limit + : base->subreq_limit; + return conf; } @@ -822,24 +836,22 @@ AP_DECLARE(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config, } } +/* + * Optional function coming from mod_ident, used for looking up ident user + */ +static APR_OPTIONAL_FN_TYPE(ap_ident_lookup) *ident_lookup; + AP_DECLARE(const char *) ap_get_remote_logname(request_rec *r) { - core_dir_config *dir_conf; - if (r->connection->remote_logname != NULL) { return r->connection->remote_logname; } - /* If we haven't checked the identity, and we want to */ - dir_conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, - &core_module); - - if (dir_conf->do_rfc1413 & 1) { - return ap_rfc1413(r->connection, r->server); - } - else { - return NULL; + if (ident_lookup) { + return ident_lookup(r); } + + return NULL; } /* There are two options regarding what the "name" of a server is. The @@ -885,6 +897,23 @@ AP_DECLARE(const char *) ap_get_server_name(request_rec *r) return r->server->server_hostname; } +/* + * Get the current server name from the request for the purposes + * of using in a URL. If the server name is an IPv6 literal + * address, it will be returned in URL format (e.g., "[fe80::1]"). + */ +static const char *get_server_name_for_url(request_rec *r) +{ + const char *plain_server_name = ap_get_server_name(r); + +#if APR_HAVE_IPV6 + if (ap_strchr_c(plain_server_name, ':')) { /* IPv6 literal? */ + return apr_psprintf(r->pool, "[%s]", plain_server_name); + } +#endif + return plain_server_name; +} + AP_DECLARE(apr_port_t) ap_get_server_port(const request_rec *r) { apr_port_t port; @@ -925,7 +954,7 @@ AP_DECLARE(char *) ap_construct_url(apr_pool_t *p, const char *uri, request_rec *r) { unsigned port = ap_get_server_port(r); - const char *host = ap_get_server_name(r); + const char *host = get_server_name_for_url(r); if (ap_is_default_port(port, r)) { return apr_pstrcat(p, ap_http_method(r), "://", host, uri, NULL); @@ -1084,8 +1113,10 @@ static const char *set_document_root(cmd_parms *cmd, void *dummy, return err; } + /* Make it absolute, relative to ServerRoot */ + arg = ap_server_root_relative(cmd->pool, arg); + /* TODO: ap_configtestonly && ap_docrootcheck && */ - /* XXX Shouldn't this be relative to ServerRoot ??? */ if (apr_filepath_merge((char**)&conf->ap_document_root, NULL, arg, APR_FILEPATH_TRUENAME, cmd->pool) != APR_SUCCESS || !ap_is_directory(cmd->pool, arg)) { @@ -1196,6 +1227,13 @@ static const char *set_override(cmd_parms *cmd, void *d_, const char *l) return err; } + /* Throw a warning if we're in or */ + if (ap_check_cmd_context(cmd, NOT_IN_LOCATION | NOT_IN_FILES)) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, + "Useless use of AllowOverride in line %d.", + cmd->directive->line_num); + } + d->override = OR_NONE; while (l[0]) { w = ap_getword_conf(cmd->pool, &l); @@ -1458,6 +1496,29 @@ static const char *set_enable_mmap(cmd_parms *cmd, void *d_, return NULL; } +static const char *set_enable_sendfile(cmd_parms *cmd, void *d_, + const char *arg) +{ + core_dir_config *d = d_; + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + + if (err != NULL) { + return err; + } + + if (strcasecmp(arg, "on") == 0) { + d->enable_sendfile = ENABLE_SENDFILE_ON; + } + else if (strcasecmp(arg, "off") == 0) { + d->enable_sendfile = ENABLE_SENDFILE_OFF; + } + else { + return "parameter must be 'on' or 'off'"; + } + + return NULL; +} + static const char *satisfy(cmd_parms *cmd, void *c_, const char *arg) { core_dir_config *c = c_; @@ -1607,8 +1668,9 @@ static const char *dirsection(cmd_parms *cmd, void *mconfig, const char *arg) /* * Ensure that the pathname is canonical, and append the trailing / */ - if (apr_filepath_merge(&newpath, NULL, cmd->path, - APR_FILEPATH_TRUENAME, cmd->pool) != APR_SUCCESS) { + apr_status_t rv = apr_filepath_merge(&newpath, NULL, cmd->path, + APR_FILEPATH_TRUENAME, cmd->pool); + if (rv != APR_SUCCESS && rv != APR_EPATHWILD) { return apr_pstrcat(cmd->pool, "path, "\"> path is invalid.", NULL); } @@ -2045,7 +2107,7 @@ static const char *set_timeout(cmd_parms *cmd, void *dummy, const char *arg) return NULL; } -static const char *set_idcheck(cmd_parms *cmd, void *d_, int arg) +static const char *set_allow2f(cmd_parms *cmd, void *d_, int arg) { core_dir_config *d = d_; const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); @@ -2054,7 +2116,7 @@ static const char *set_idcheck(cmd_parms *cmd, void *d_, int arg) return err; } - d->do_rfc1413 = arg != 0; + d->allow_encoded_slashes = arg != 0; return NULL; } @@ -2163,7 +2225,7 @@ static const char *include_config (cmd_parms *cmd, void *dummy, { ap_directive_t *conftree = NULL; const char* conffile = ap_server_root_relative(cmd->pool, name); - + if (!conffile) { return apr_pstrcat(cmd->pool, "Invalid Include path ", name, NULL); @@ -2237,15 +2299,19 @@ AP_DECLARE(const char *) ap_psignature(const char *prefix, request_rec *r) apr_snprintf(sport, sizeof sport, "%u", (unsigned) ap_get_server_port(r)); if (conf->server_signature == srv_sig_withmail) { - return apr_pstrcat(r->pool, prefix, "
" AP_SERVER_BASEVERSION + return apr_pstrcat(r->pool, prefix, "
", + ap_get_server_version(), " Server at server->server_admin, "\">", - ap_get_server_name(r), " Port ", sport, + ap_escape_html(r->pool, ap_get_server_name(r)), + " Port ", sport, "
\n", NULL); } - return apr_pstrcat(r->pool, prefix, "
" AP_SERVER_BASEVERSION - " Server at ", ap_get_server_name(r), " Port ", sport, + return apr_pstrcat(r->pool, prefix, "
", ap_get_server_version(), + " Server at ", + ap_escape_html(r->pool, ap_get_server_name(r)), + " Port ", sport, "
\n", NULL); } @@ -2262,18 +2328,6 @@ static const char *set_authname(cmd_parms *cmd, void *mconfig, return NULL; } -#ifdef _OSD_POSIX /* BS2000 Logon Passwd file */ -static const char *set_bs2000_account(cmd_parms *cmd, void *dummy, char *name) -{ - const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); - if (err != NULL) { - return err; - } - - return os_set_account(cmd->pool, name); -} -#endif /*_OSD_POSIX*/ - /* * Handle a request to include the server's OS platform in the Server * response header field (the ServerTokens directive). Unfortunately @@ -2286,9 +2340,11 @@ static char *server_version = NULL; static int version_locked = 0; enum server_token_type { - SrvTk_MIN, /* eg: Apache/1.3.0 */ - SrvTk_OS, /* eg: Apache/1.3.0 (UNIX) */ - SrvTk_FULL, /* eg: Apache/1.3.0 (UNIX) PHP/3.0 FooBar/1.2b */ + SrvTk_MAJOR, /* eg: Apache/2 */ + SrvTk_MINOR, /* eg. Apache/2.0 */ + SrvTk_MINIMAL, /* eg: Apache/2.0.41 */ + SrvTk_OS, /* eg: Apache/2.0.41 (UNIX) */ + SrvTk_FULL, /* eg: Apache/2.0.41 (UNIX) PHP/4.2.2 FooBar/1.2b */ SrvTk_PRODUCT_ONLY /* eg: Apache */ }; static enum server_token_type ap_server_tokens = SrvTk_FULL; @@ -2339,9 +2395,15 @@ static void ap_set_version(apr_pool_t *pconf) if (ap_server_tokens == SrvTk_PRODUCT_ONLY) { ap_add_version_component(pconf, AP_SERVER_BASEPRODUCT); } - else if (ap_server_tokens == SrvTk_MIN) { + else if (ap_server_tokens == SrvTk_MINIMAL) { ap_add_version_component(pconf, AP_SERVER_BASEVERSION); } + else if (ap_server_tokens == SrvTk_MINOR) { + ap_add_version_component(pconf, AP_SERVER_BASEPRODUCT "/" AP_SERVER_MINORREVISION); + } + else if (ap_server_tokens == SrvTk_MAJOR) { + ap_add_version_component(pconf, AP_SERVER_BASEPRODUCT "/" AP_SERVER_MAJORVERSION); + } else { ap_add_version_component(pconf, AP_SERVER_BASEVERSION " (" PLATFORM ")"); } @@ -2368,7 +2430,13 @@ static const char *set_serv_tokens(cmd_parms *cmd, void *dummy, ap_server_tokens = SrvTk_OS; } else if (!strcasecmp(arg, "Min") || !strcasecmp(arg, "Minimal")) { - ap_server_tokens = SrvTk_MIN; + ap_server_tokens = SrvTk_MINIMAL; + } + else if (!strcasecmp(arg, "Major")) { + ap_server_tokens = SrvTk_MAJOR; + } + else if (!strcasecmp(arg, "Minor") ) { + ap_server_tokens = SrvTk_MINOR; } else if (!strcasecmp(arg, "Prod") || !strcasecmp(arg, "ProductOnly")) { ap_server_tokens = SrvTk_PRODUCT_ONLY; @@ -2559,11 +2627,135 @@ static const char *set_limit_nproc(cmd_parms *cmd, void *conf_, } #endif +static const char *set_recursion_limit(cmd_parms *cmd, void *dummy, + const char *arg1, const char *arg2) +{ + core_server_config *conf = ap_get_module_config(cmd->server->module_config, + &core_module); + int limit = atoi(arg1); + + if (limit <= 0) { + return "The recursion limit must be greater than zero."; + } + if (limit < 4) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, + "Limiting internal redirects to very low numbers may " + "cause normal requests to fail."); + } + + conf->redirect_limit = limit; + + if (arg2) { + limit = atoi(arg2); + + if (limit <= 0) { + return "The recursion limit must be greater than zero."; + } + if (limit < 4) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, + "Limiting the subrequest depth to a very low level may" + " cause normal requests to fail."); + } + } + + conf->subreq_limit = limit; + + return NULL; +} + +static void log_backtrace(const request_rec *r) +{ + const request_rec *top = r; + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "r->uri = %s", r->uri ? r->uri : "(unexpectedly NULL)"); + + while (top && (top->prev || top->main)) { + if (top->prev) { + top = top->prev; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "redirected from r->uri = %s", + top->uri ? top->uri : "(unexpectedly NULL)"); + } + + if (!top->prev && top->main) { + top = top->main; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "subrequested from r->uri = %s", + top->uri ? top->uri : "(unexpectedly NULL)"); + } + } +} + +/* + * check whether redirect limit is reached + */ +AP_DECLARE(int) ap_is_recursion_limit_exceeded(const request_rec *r) +{ + core_server_config *conf = ap_get_module_config(r->server->module_config, + &core_module); + const request_rec *top = r; + int redirects = 0, subreqs = 0; + int rlimit = conf->redirect_limit + ? conf->redirect_limit + : AP_DEFAULT_MAX_INTERNAL_REDIRECTS; + int slimit = conf->subreq_limit + ? conf->subreq_limit + : AP_DEFAULT_MAX_SUBREQ_DEPTH; + + + while (top->prev || top->main) { + if (top->prev) { + if (++redirects >= rlimit) { + /* uuh, too much. */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Request exceeded the limit of %d internal " + "redirects due to probable configuration error. " + "Use 'LimitInternalRecursion' to increase the " + "limit if necessary. Use 'LogLevel debug' to get " + "a backtrace.", rlimit); + + /* post backtrace */ + log_backtrace(r); + + /* return failure */ + return 1; + } + + top = top->prev; + } + + if (!top->prev && top->main) { + if (++subreqs >= slimit) { + /* uuh, too much. */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Request exceeded the limit of %d subrequest " + "nesting levels due to probable confguration " + "error. Use 'LimitInternalRecursion' to increase " + "the limit if necessary. Use 'LogLevel debug' to " + "get a backtrace.", slimit); + + /* post backtrace */ + log_backtrace(r); + + /* return failure */ + return 1; + } + + top = top->main; + } + } + + /* recursion state: ok */ + return 0; +} + static const char *add_ct_output_filters(cmd_parms *cmd, void *conf_, const char *arg, const char *arg2) { core_dir_config *conf = conf_; - ap_filter_rec_t *old, *new; + ap_filter_rec_t *old, *new = NULL; + const char *filter_name; if (!conf->ct_output_filters) { conf->ct_output_filters = apr_hash_make(cmd->pool); @@ -2572,30 +2764,47 @@ static const char *add_ct_output_filters(cmd_parms *cmd, void *conf_, else { old = (ap_filter_rec_t*) apr_hash_get(conf->ct_output_filters, arg2, APR_HASH_KEY_STRING); + /* find last entry */ + if (old) { + while (old->next) { + old = old->next; + } + } } - new = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t)); - new->name = apr_pstrdup(cmd->pool, arg); + while (*arg && + (filter_name = ap_getword(cmd->pool, &arg, ';')) && + strcmp(filter_name, "")) { + new = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t)); + new->name = filter_name; - /* We found something, so let's append it. */ - if (old) { - new->next = old; + /* We found something, so let's append it. */ + if (old) { + old->next = new; + } + else { + apr_hash_set(conf->ct_output_filters, arg2, + APR_HASH_KEY_STRING, new); + } + old = new; } - apr_hash_set(conf->ct_output_filters, arg2, APR_HASH_KEY_STRING, new); - + if (!new) { + return "invalid filter name"; + } + return NULL; } /* - * Insert filters requested by the AddOutputFiltersByType + * Insert filters requested by the AddOutputFilterByType * configuration directive. We cannot add filters based * on content-type until after the handler has started - * to run. Only then do we reliabily know the content-type. + * to run. Only then do we reliably know the content-type. */ void ap_add_output_filters_by_type(request_rec *r) { core_dir_config *conf; - const char *ctype, *ctypes; + const char *ctype; conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); @@ -2608,10 +2817,9 @@ void ap_add_output_filters_by_type(request_rec *r) return; } - ctypes = r->content_type; - - /* We must be able to handle decorated content-types. */ - while (*ctypes && (ctype = ap_getword(r->pool, &ctypes, ';'))) { + /* remove c-t decoration */ + ctype = ap_field_noparam(r->pool, r->content_type); + if (ctype) { ap_filter_rec_t *ct_filter; ct_filter = apr_hash_get(conf->ct_output_filters, ctype, APR_HASH_KEY_STRING); @@ -2637,7 +2845,7 @@ static apr_status_t writev_it_all(apr_socket_t *s, /* XXX handle checking for non-blocking socket */ while (bytes_written != len) { - rv = apr_sendv(s, vec + i, nvec - i, &n); + rv = apr_socket_sendv(s, vec + i, nvec - i, &n); bytes_written += n; if (rv != APR_SUCCESS) return rv; @@ -2645,7 +2853,7 @@ static apr_status_t writev_it_all(apr_socket_t *s, *nbytes += n; /* If the write did not complete, adjust the iovecs and issue - * apr_sendv again + * apr_socket_sendv again */ if (bytes_written < len) { /* Skip over the vectors that have already been written */ @@ -2682,22 +2890,27 @@ static apr_status_t sendfile_it_all(core_net_rec *c, apr_off_t file_offset, apr_size_t file_bytes_left, apr_size_t total_bytes_left, + apr_size_t *bytes_sent, apr_int32_t flags) { apr_status_t rv; #ifdef AP_DEBUG - apr_int32_t timeout = 0; + apr_interval_time_t timeout = 0; #endif - AP_DEBUG_ASSERT((apr_getsocketopt(c->client_socket, APR_SO_TIMEOUT, - &timeout) == APR_SUCCESS) + AP_DEBUG_ASSERT((apr_socket_timeout_get(c->client_socket, &timeout) + == APR_SUCCESS) && timeout > 0); /* socket must be in timeout mode */ + /* Reset the bytes_sent field */ + *bytes_sent = 0; + do { apr_size_t tmplen = file_bytes_left; - rv = apr_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen, - flags); + rv = apr_socket_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen, + flags); + *bytes_sent += tmplen; total_bytes_left -= tmplen; if (!total_bytes_left || rv != APR_SUCCESS) { return rv; /* normal case & error exit */ @@ -2806,7 +3019,7 @@ static apr_status_t emulate_sendfile(core_net_rec *c, apr_file_t *fd, rv = apr_file_read(fd, buffer, &sendlen); while (rv == APR_SUCCESS && sendlen) { bytes_sent = sendlen; - rv = apr_send(c->client_socket, &buffer[o], &bytes_sent); + rv = apr_socket_send(c->client_socket, &buffer[o], &bytes_sent); if (rv == APR_SUCCESS) { sendlen -= bytes_sent; /* sendlen != bytes_sent ==> partial write */ o += bytes_sent; /* o is where we are in the buffer */ @@ -2909,6 +3122,8 @@ AP_INIT_RAW_ARGS("FileETag", set_etag_bits, NULL, OR_FILEINFO, "Specify components used to construct a file's ETag"), AP_INIT_TAKE1("EnableMMAP", set_enable_mmap, NULL, OR_FILEINFO, "Controls whether memory-mapping may be used to read files"), +AP_INIT_TAKE1("EnableSendfile", set_enable_sendfile, NULL, OR_FILEINFO, + "Controls whether sendfile may be used to transmit files"), /* Old server config file commands */ @@ -2936,8 +3151,6 @@ AP_INIT_TAKE1("ServerPath", set_serverpath, NULL, RSRC_CONF, "The pathname the server can be reached at"), AP_INIT_TAKE1("Timeout", set_timeout, NULL, RSRC_CONF, "Timeout duration (sec)"), -AP_INIT_FLAG("IdentityCheck", set_idcheck, NULL, RSRC_CONF|ACCESS_CONF, - "Enable identd (RFC 1413) user lookups - SLOW"), AP_INIT_FLAG("ContentDigest", set_content_md5, NULL, OR_OPTIONS, "whether or not to send a Content-MD5 header with each request"), AP_INIT_TAKE1("UseCanonicalName", set_use_canonical_name, NULL, @@ -2952,10 +3165,6 @@ AP_INIT_TAKE1("LogLevel", set_loglevel, NULL, RSRC_CONF, "Level of verbosity in error logging"), AP_INIT_TAKE1("NameVirtualHost", ap_set_name_virtual_host, NULL, RSRC_CONF, "A numeric IP address:port, or the name of a host"), -#ifdef _OSD_POSIX -AP_INIT_TAKE1("BS2000Account", set_bs2000_account, NULL, RSRC_CONF, - "Name of server User's bs2000 logon account name"), -#endif AP_INIT_TAKE1("ServerTokens", set_serv_tokens, NULL, RSRC_CONF, "Determine tokens displayed in the Server: header - Min(imal), OS or Full"), AP_INIT_TAKE1("LimitRequestLine", set_limit_req_line, NULL, RSRC_CONF, @@ -2998,6 +3207,10 @@ AP_INIT_TAKE12("RLimitNPROC", no_set_limit, NULL, OR_ALL, "soft/hard limits for max number of processes per uid"), #endif +/* internal recursion stopper */ +AP_INIT_TAKE12("LimitInternalRecursion", set_recursion_limit, NULL, RSRC_CONF, + "maximum recursion depth of internal redirects and subrequests"), + AP_INIT_TAKE1("ForceType", ap_set_string_slot_lower, (void *)APR_OFFSETOF(core_dir_config, mime_type), OR_FILEINFO, "a mime type that overrides other configured type"), @@ -3013,6 +3226,8 @@ AP_INIT_TAKE1("SetInputFilter", ap_set_string_slot, AP_INIT_ITERATE2("AddOutputFilterByType", add_ct_output_filters, (void *)APR_OFFSETOF(core_dir_config, ct_output_filters), OR_FILEINFO, "output filter name followed by one or more content-types"), +AP_INIT_FLAG("AllowEncodedSlashes", set_allow2f, NULL, RSRC_CONF, + "Allow URLs containing '/' encoded as '%2F'"), /* * These are default configuration directives that mpms can/should @@ -3020,31 +3235,31 @@ AP_INIT_ITERATE2("AddOutputFilterByType", add_ct_output_filters, * #defined them in mpm.h. */ #ifdef AP_MPM_WANT_SET_PIDFILE -AP_INIT_TAKE1("PidFile", ap_mpm_set_pidfile, NULL, RSRC_CONF, \ +AP_INIT_TAKE1("PidFile", ap_mpm_set_pidfile, NULL, RSRC_CONF, "A file for logging the server process ID"), #endif #ifdef AP_MPM_WANT_SET_SCOREBOARD -AP_INIT_TAKE1("ScoreBoardFile", ap_mpm_set_scoreboard, NULL, RSRC_CONF, \ +AP_INIT_TAKE1("ScoreBoardFile", ap_mpm_set_scoreboard, NULL, RSRC_CONF, "A file for Apache to maintain runtime process management information"), #endif #ifdef AP_MPM_WANT_SET_LOCKFILE -AP_INIT_TAKE1("LockFile", ap_mpm_set_lockfile, NULL, RSRC_CONF, \ +AP_INIT_TAKE1("LockFile", ap_mpm_set_lockfile, NULL, RSRC_CONF, "The lockfile used when Apache needs to lock the accept() call"), #endif #ifdef AP_MPM_WANT_SET_MAX_REQUESTS -AP_INIT_TAKE1("MaxRequestsPerChild", ap_mpm_set_max_requests, NULL, RSRC_CONF,\ +AP_INIT_TAKE1("MaxRequestsPerChild", ap_mpm_set_max_requests, NULL, RSRC_CONF, "Maximum number of requests a particular child serves before dying."), #endif #ifdef AP_MPM_WANT_SET_COREDUMPDIR -AP_INIT_TAKE1("CoreDumpDirectory", ap_mpm_set_coredumpdir, NULL, RSRC_CONF, \ +AP_INIT_TAKE1("CoreDumpDirectory", ap_mpm_set_coredumpdir, NULL, RSRC_CONF, "The location of the directory Apache changes to before dumping core"), #endif #ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH -AP_INIT_TAKE1("AcceptMutex", ap_mpm_set_accept_lock_mech, NULL, RSRC_CONF, \ +AP_INIT_TAKE1("AcceptMutex", ap_mpm_set_accept_lock_mech, NULL, RSRC_CONF, ap_valid_accept_mutex_string), #endif #ifdef AP_MPM_WANT_SET_MAX_MEM_FREE -AP_INIT_TAKE1("MaxMemFree", ap_mpm_set_max_mem_free, NULL, RSRC_CONF,\ +AP_INIT_TAKE1("MaxMemFree", ap_mpm_set_max_mem_free, NULL, RSRC_CONF, "Maximum number of 1k blocks a particular childs allocator may hold."), #endif { NULL } @@ -3076,9 +3291,16 @@ AP_DECLARE_NONSTD(int) ap_core_translate(request_rec *r) && !strncmp(r->uri, r->server->path, r->server->pathlen) && (r->server->path[r->server->pathlen - 1] == '/' || r->uri[r->server->pathlen] == '/' - || r->uri[r->server->pathlen] == '\0')) { - if (apr_filepath_merge(&r->filename, conf->ap_document_root, - r->uri + r->server->pathlen, + || r->uri[r->server->pathlen] == '\0')) + { + /* skip all leading /'s (e.g. http://localhost///foo) + * so we are looking at only the relative path. + */ + char *path = r->uri + r->server->pathlen; + while (*path == '/') { + ++path; + } + if (apr_filepath_merge(&r->filename, conf->ap_document_root, path, APR_FILEPATH_TRUENAME | APR_FILEPATH_SECUREROOT, r->pool) != APR_SUCCESS) { @@ -3092,8 +3314,14 @@ AP_DECLARE_NONSTD(int) ap_core_translate(request_rec *r) * /'s in a row. This happens under windows when the document * root ends with a / */ - if (apr_filepath_merge(&r->filename, conf->ap_document_root, - r->uri + ((*(r->uri) == '/') ? 1 : 0), + /* skip all leading /'s (e.g. http://localhost///foo) + * so we are looking at only the relative path. + */ + char *path = r->uri; + while (*path == '/') { + ++path; + } + if (apr_filepath_merge(&r->filename, conf->ap_document_root, path, APR_FILEPATH_TRUENAME | APR_FILEPATH_SECUREROOT, r->pool) != APR_SUCCESS) { @@ -3222,8 +3450,34 @@ static int default_handler(request_rec *r) return HTTP_NOT_FOUND; } - if ((status = apr_file_open(&fd, r->filename, APR_READ | APR_BINARY, 0, - r->pool)) != APR_SUCCESS) { + /* We understood the (non-GET) method, but it might not be legal for + this particular resource. Check to see if the 'deliver_script' + flag is set. If so, then we go ahead and deliver the file since + it isn't really content (only GET normally returns content). + + Note: based on logic further above, the only possible non-GET + method at this point is POST. In the future, we should enable + script delivery for all methods. */ + if (r->method_number != M_GET) { + core_request_config *req_cfg; + + req_cfg = ap_get_module_config(r->request_config, &core_module); + if (!req_cfg->deliver_script) { + /* The flag hasn't been set for this request. Punt. */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "This resource does not accept the %s method.", + r->method); + return HTTP_METHOD_NOT_ALLOWED; + } + } + + + if ((status = apr_file_open(&fd, r->filename, APR_READ | APR_BINARY +#if APR_HAS_SENDFILE + | ((d->enable_sendfile == ENABLE_SENDFILE_OFF) + ? 0 : APR_SENDFILE_ENABLED) +#endif + , 0, r->pool)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, "file permissions deny server access: %s", r->filename); return HTTP_FORBIDDEN; @@ -3245,8 +3499,9 @@ static int default_handler(request_rec *r) } bb = apr_brigade_create(r->pool, c->bucket_alloc); -#if APR_HAS_LARGE_FILES - if (r->finfo.size > AP_MAX_SENDFILE) { +#if APR_HAS_SENDFILE && APR_HAS_LARGE_FILES + if ((d->enable_sendfile != ENABLE_SENDFILE_OFF) && + (r->finfo.size > AP_MAX_SENDFILE)) { /* APR_HAS_LARGE_FILES issue; must split into mutiple buckets, * no greater than MAX(apr_size_t), and more granular than that * in case the brigade code/filters attempt to read it directly. @@ -3293,31 +3548,34 @@ static int default_handler(request_rec *r) } } +typedef struct net_time_filter_ctx { + apr_socket_t *csd; + int first_line; +} net_time_filter_ctx_t; static int net_time_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { + net_time_filter_ctx_t *ctx = f->ctx; int keptalive = f->c->keepalive == AP_CONN_KEEPALIVE; - apr_socket_t *csd = ap_get_module_config(f->c->conn_config, &core_module); - int *first_line = f->ctx; - if (!f->ctx) { - f->ctx = first_line = apr_palloc(f->r->pool, sizeof(*first_line)); - *first_line = 1; + if (!ctx) { + f->ctx = ctx = apr_palloc(f->r->pool, sizeof(*ctx)); + ctx->first_line = 1; + ctx->csd = ap_get_module_config(f->c->conn_config, &core_module); } if (mode != AP_MODE_INIT && mode != AP_MODE_EATCRLF) { - if (*first_line) { - apr_setsocketopt(csd, APR_SO_TIMEOUT, - (int)(keptalive - ? f->c->base_server->keep_alive_timeout - : f->c->base_server->timeout)); - *first_line = 0; + if (ctx->first_line) { + apr_socket_timeout_set(ctx->csd, + keptalive + ? f->c->base_server->keep_alive_timeout + : f->c->base_server->timeout); + ctx->first_line = 0; } else { if (keptalive) { - apr_setsocketopt(csd, APR_SO_TIMEOUT, - (int)(f->c->base_server->timeout)); + apr_socket_timeout_set(ctx->csd, f->c->base_server->timeout); } } } @@ -3393,6 +3651,19 @@ static int core_input_filter(ap_filter_t *f, apr_bucket_brigade *b, return APR_EOF; } + if (mode == AP_MODE_GETLINE) { + /* we are reading a single LF line, e.g. the HTTP headers */ + rv = apr_brigade_split_line(b, ctx->b, block, HUGE_STRING_LEN); + /* We should treat EAGAIN here the same as we do for EOF (brigade is + * empty). We do this by returning whatever we have read. This may + * or may not be bogus, but is consistent (for now) with EOF logic. + */ + if (APR_STATUS_IS_EAGAIN(rv)) { + rv = APR_SUCCESS; + } + return rv; + } + /* ### AP_MODE_PEEK is a horrific name for this mode because we also * eat any CRLFs that we see. That's not the obvious intention of * this mode. Determine whether anyone actually uses this or not. */ @@ -3435,6 +3706,7 @@ static int core_input_filter(ap_filter_t *f, apr_bucket_brigade *b, /* FIXME: Is this the right thing to do in the core? */ apr_bucket_delete(e); } + return APR_SUCCESS; } /* If mode is EXHAUSTIVE, we want to just read everything until the end @@ -3527,22 +3799,8 @@ static int core_input_filter(ap_filter_t *f, apr_bucket_brigade *b, /* Take what was originally there and place it back on ctx->b */ APR_BRIGADE_CONCAT(ctx->b, newbb); - - return APR_SUCCESS; - } - - /* we are reading a single LF line, e.g. the HTTP headers */ - rv = apr_brigade_split_line(b, ctx->b, block, HUGE_STRING_LEN); - - /* We should treat EAGAIN here the same as we do for EOF (brigade is - * empty). We do this by returning whatever we have read. This may - * or may not be bogus, but is consistent (for now) with EOF logic. - */ - if (APR_STATUS_IS_EAGAIN(rv)) { - rv = APR_SUCCESS; } - - return rv; + return APR_SUCCESS; } /* Default filter. This filter should almost always be used. Its only job @@ -3551,12 +3809,20 @@ static int core_input_filter(ap_filter_t *f, apr_bucket_brigade *b, */ #define MAX_IOVEC_TO_WRITE 16 +/* Optional function coming from mod_logio, used for logging of output + * traffic + */ +static APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *logio_add_bytes_out; + static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b) { apr_status_t rv; + apr_bucket_brigade *more; conn_rec *c = f->c; core_net_rec *net = f->ctx; core_output_filter_ctx_t *ctx = net->out_ctx; + apr_read_type_e eblock = APR_NONBLOCK_READ; + apr_pool_t *input_pool = b->p; if (ctx == NULL) { ctx = apr_pcalloc(c->pool, sizeof(*ctx)); @@ -3577,9 +3843,6 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b) apr_bucket *last_e = NULL; /* initialized for debugging */ apr_bucket *e; - /* tail of brigade if we need another pass */ - apr_bucket_brigade *more = NULL; - /* one group of iovecs per pass over the brigade */ apr_size_t nvec = 0; apr_size_t nvec_trailers = 0; @@ -3596,6 +3859,9 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b) */ apr_bucket *last_merged_bucket = NULL; + /* tail of brigade if we need another pass */ + more = NULL; + /* Iterate over the brigade: collect iovecs and/or a file */ APR_BRIGADE_FOREACH(e, b) { /* keep track of the last bucket processed */ @@ -3604,7 +3870,9 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b) break; } if (APR_BUCKET_IS_FLUSH(e)) { - more = apr_brigade_split(b, APR_BUCKET_NEXT(e)); + if (e != APR_BRIGADE_LAST(b)) { + more = apr_brigade_split(b, APR_BUCKET_NEXT(e)); + } break; } @@ -3632,7 +3900,16 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b) const char *str; apr_size_t n; - rv = apr_bucket_read(e, &str, &n, APR_BLOCK_READ); + rv = apr_bucket_read(e, &str, &n, eblock); + if (APR_STATUS_IS_EAGAIN(rv)) { + /* send what we have so far since we shouldn't expect more + * output for a while... next time we read, block + */ + more = apr_brigade_split(b, e); + eblock = APR_BLOCK_READ; + break; + } + eblock = APR_NONBLOCK_READ; if (n) { if (!fd) { if (nvec == MAX_IOVEC_TO_WRITE) { @@ -3744,78 +4021,76 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b) } - /* Completed iterating over the brigades, now determine if we want + /* Completed iterating over the brigade, now determine if we want * to buffer the brigade or send the brigade out on the network. * - * Save if: + * Save if we haven't accumulated enough bytes to send, and: * * 1) we didn't see a file, we don't have more passes over the - * brigade to perform, we haven't accumulated enough bytes to - * send, AND we didn't stop at a FLUSH bucket. - * (IOW, we will save away plain old bytes) + * brigade to perform, AND we didn't stop at a FLUSH bucket. + * (IOW, we will save plain old bytes such as HTTP headers) * or * 2) we hit the EOS and have a keep-alive connection * (IOW, this response is a bit more complex, but we save it * with the hope of concatenating with another response) */ - if ((!fd && !more - && (nbytes + flen < AP_MIN_BYTES_TO_WRITE) - && !APR_BUCKET_IS_FLUSH(last_e)) - || (nbytes + flen < AP_MIN_BYTES_TO_WRITE - && APR_BUCKET_IS_EOS(last_e) - && c->keepalive == AP_CONN_KEEPALIVE)) { + if (nbytes + flen < AP_MIN_BYTES_TO_WRITE + && ((!fd && !more && !APR_BUCKET_IS_FLUSH(last_e)) + || (APR_BUCKET_IS_EOS(last_e) + && c->keepalive == AP_CONN_KEEPALIVE))) { /* NEVER save an EOS in here. If we are saving a brigade with * an EOS bucket, then we are doing keepalive connections, and * we want to process to second request fully. */ if (APR_BUCKET_IS_EOS(last_e)) { - apr_bucket *bucket = NULL; - /* If we are in here, then this request is a keepalive. We - * need to be certain that any data in a bucket is valid - * after the request_pool is cleared. - */ - if (ctx->b == NULL) { - ctx->b = apr_brigade_create(net->c->pool, - net->c->bucket_alloc); - } - - APR_BRIGADE_FOREACH(bucket, b) { - const char *str; - apr_size_t n; - - rv = apr_bucket_read(bucket, &str, &n, APR_BLOCK_READ); - - /* This apr_brigade_write does not use a flush function - because we assume that we will not write enough data - into it to cause a flush. However, if we *do* write - "too much", then we could end up with transient - buckets which would suck. This works for now, but is - a bit shaky if changes are made to some of the - buffering sizes. Let's do an assert to prevent - potential future problems... */ - AP_DEBUG_ASSERT(AP_MIN_BYTES_TO_WRITE <= - APR_BUCKET_BUFF_SIZE); - if (rv != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server, - "core_output_filter: Error reading from bucket."); - return HTTP_INTERNAL_SERVER_ERROR; + apr_bucket *bucket; + int file_bucket_saved = 0; + apr_bucket_delete(last_e); + for (bucket = APR_BRIGADE_FIRST(b); + bucket != APR_BRIGADE_SENTINEL(b); + bucket = APR_BUCKET_NEXT(bucket)) { + + /* Do a read on each bucket to pull in the + * data from pipe and socket buckets, so + * that we don't leave their file descriptors + * open indefinitely. Do the same for file + * buckets, with one exception: allow the + * first file bucket in the brigade to remain + * a file bucket, so that we don't end up + * doing an mmap+memcpy every time a client + * requests a <8KB file over a keepalive + * connection. + */ + if (APR_BUCKET_IS_FILE(bucket) && !file_bucket_saved) { + file_bucket_saved = 1; + } + else { + const char *buf; + apr_size_t len = 0; + rv = apr_bucket_read(bucket, &buf, &len, + APR_BLOCK_READ); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, + c->base_server, "core_output_filter:" + " Error reading from bucket."); + return HTTP_INTERNAL_SERVER_ERROR; + } } - - apr_brigade_write(ctx->b, NULL, NULL, str, n); } - - apr_brigade_destroy(b); } - else { - ap_save_brigade(f, &ctx->b, &b, c->pool); + if (!ctx->deferred_write_pool) { + apr_pool_create(&ctx->deferred_write_pool, c->pool); } + ap_save_brigade(f, &ctx->b, &b, ctx->deferred_write_pool); return APR_SUCCESS; } if (fd) { apr_hdtr_t hdtr; + apr_size_t bytes_sent; + #if APR_HAS_SENDFILE apr_int32_t flags = 0; #endif @@ -3832,45 +4107,77 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b) } #if APR_HAS_SENDFILE - if (c->keepalive == AP_CONN_CLOSE && APR_BUCKET_IS_EOS(last_e)) { - /* Prepare the socket to be reused */ - flags |= APR_SENDFILE_DISCONNECT_SOCKET; - } + if (apr_file_flags_get(fd) & APR_SENDFILE_ENABLED) { - rv = sendfile_it_all(net, /* the network information */ - fd, /* the file to send */ - &hdtr, /* header and trailer iovecs */ - foffset, /* offset in the file to begin - sending from */ - flen, /* length of file */ - nbytes + flen, /* total length including - headers */ - flags); /* apr_sendfile flags */ - - /* If apr_sendfile() returns APR_ENOTIMPL, call emulate_sendfile(). - * emulate_sendfile() is useful to enable the same Apache binary - * distribution to support Windows NT/2000 (supports TransmitFile) - * and Win95/98 (do not support TransmitFile) - */ - if (rv == APR_ENOTIMPL) + if (c->keepalive == AP_CONN_CLOSE && APR_BUCKET_IS_EOS(last_e)) { + /* Prepare the socket to be reused */ + flags |= APR_SENDFILE_DISCONNECT_SOCKET; + } + + rv = sendfile_it_all(net, /* the network information */ + fd, /* the file to send */ + &hdtr, /* header and trailer iovecs */ + foffset, /* offset in the file to begin + sending from */ + flen, /* length of file */ + nbytes + flen, /* total length including + headers */ + &bytes_sent, /* how many bytes were + sent */ + flags); /* apr_sendfile flags */ + + if (logio_add_bytes_out && bytes_sent > 0) + logio_add_bytes_out(c, bytes_sent); + } + else #endif { - apr_size_t unused_bytes_sent; rv = emulate_sendfile(net, fd, &hdtr, foffset, flen, - &unused_bytes_sent); + &bytes_sent); + + if (logio_add_bytes_out && bytes_sent > 0) + logio_add_bytes_out(c, bytes_sent); } fd = NULL; } else { - apr_size_t unused_bytes_sent; + apr_size_t bytes_sent; rv = writev_it_all(net->client_socket, vec, nvec, - nbytes, &unused_bytes_sent); + nbytes, &bytes_sent); + + if (logio_add_bytes_out && bytes_sent > 0) + logio_add_bytes_out(c, bytes_sent); } apr_brigade_destroy(b); + + /* drive cleanups for resources which were set aside + * this may occur before or after termination of the request which + * created the resource + */ + if (ctx->deferred_write_pool) { + if (more && more->p == ctx->deferred_write_pool) { + /* "more" belongs to the deferred_write_pool, + * which is about to be cleared. + */ + if (APR_BRIGADE_EMPTY(more)) { + more = NULL; + } + else { + /* uh oh... change more's lifetime + * to the input brigade's lifetime + */ + apr_bucket_brigade *tmp_more = more; + more = NULL; + ap_save_brigade(f, &more, &tmp_more, input_pool); + } + } + apr_pool_clear(ctx->deferred_write_pool); + } + if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_INFO, rv, c->base_server, "core_output_filter: writing data to the network"); @@ -3878,9 +4185,8 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b) if (more) apr_brigade_destroy(more); - if (APR_STATUS_IS_ECONNABORTED(rv) - || APR_STATUS_IS_ECONNRESET(rv) - || APR_STATUS_IS_EPIPE(rv)) { + /* No need to check for SUCCESS, we did that above. */ + if (!APR_STATUS_IS_EAGAIN(rv)) { c->aborted = 1; } @@ -3900,17 +4206,14 @@ static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b) static int core_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { + logio_add_bytes_out = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_out); + ident_lookup = APR_RETRIEVE_OPTIONAL_FN(ap_ident_lookup); + ap_set_version(pconf); ap_setup_make_content_type(pconf); return OK; } -static int core_open_logs(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) -{ - ap_open_logs(s, plog); - return OK; -} - static void core_insert_filter(request_rec *r) { core_dir_config *conf = (core_dir_config *) @@ -3975,6 +4278,10 @@ static int core_create_req(request_rec *r) req_cfg = apr_pcalloc(r->pool, sizeof(core_request_config) + sizeof(void *) * num_request_notes); req_cfg->notes = (void **)((char *)req_cfg + sizeof(core_request_config)); + + /* ### temporarily enable script delivery as the default */ + req_cfg->deliver_script = 1; + if (r->main) { core_request_config *main_req_cfg = (core_request_config *) ap_get_module_config(r->main->request_config, &core_module); @@ -4011,7 +4318,7 @@ static conn_rec *core_create_conn(apr_pool_t *ptrans, server_rec *server, conn_rec *c = (conn_rec *) apr_pcalloc(ptrans, sizeof(conn_rec)); c->sbh = sbh; - (void) ap_update_child_status(c->sbh, SERVER_BUSY_READ, (request_rec *) NULL); + (void)ap_update_child_status(c->sbh, SERVER_BUSY_READ, (request_rec *)NULL); /* Got a connection structure, so initialize what fields we can * (the rest are zeroed out by pcalloc). @@ -4084,7 +4391,7 @@ static void register_hooks(apr_pool_t *p) ap_hook_post_config(core_post_config,NULL,NULL,APR_HOOK_REALLY_FIRST); ap_hook_translate_name(ap_core_translate,NULL,NULL,APR_HOOK_REALLY_LAST); ap_hook_map_to_storage(core_map_to_storage,NULL,NULL,APR_HOOK_REALLY_LAST); - ap_hook_open_logs(core_open_logs,NULL,NULL,APR_HOOK_MIDDLE); + ap_hook_open_logs(ap_open_logs,NULL,NULL,APR_HOOK_REALLY_FIRST); ap_hook_handler(default_handler,NULL,NULL,APR_HOOK_REALLY_LAST); /* FIXME: I suspect we can eliminate the need for these do_nothings - Ben */ ap_hook_type_checker(do_nothing,NULL,NULL,APR_HOOK_REALLY_LAST);