/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/* * protocol.c --- routines which directly communicate with the client. * * Code originally by Rob McCool; much redone by Robert S. Thau * and the Apache Software Foundation.
*/
/* Patterns to match in ap_make_content_type() */ staticconstchar *needcset[] = { "text/plain", "text/html",
NULL
}; staticconst apr_strmatch_pattern **needcset_patterns; staticconst apr_strmatch_pattern *charset_pattern;
AP_DECLARE(void) ap_setup_make_content_type(apr_pool_t *pool)
{ int i; for (i = 0; needcset[i]; i++) { continue;
}
needcset_patterns = (const apr_strmatch_pattern **)
apr_palloc(pool, (i + 1) * sizeof(apr_strmatch_pattern *)); for (i = 0; needcset[i]; i++) {
needcset_patterns[i] = apr_strmatch_precompile(pool, needcset[i], 0);
}
needcset_patterns[i] = NULL;
charset_pattern = apr_strmatch_precompile(pool, "charset=", 0);
}
/* * Builds the content-type that should be sent to the client from the * content-type specified. The following rules are followed: * - if type is NULL or "", return NULL (do not set content-type). * - if charset adding is disabled, stop processing and return type. * - then, if there are no parameters on type, add the default charset * - return type
*/
AP_DECLARE(constchar *)ap_make_content_type(request_rec *r, constchar *type)
{ const apr_strmatch_pattern **pcset;
core_dir_config *conf =
(core_dir_config *)ap_get_core_module_config(r->per_dir_config);
core_request_config *request_conf;
apr_size_t type_len;
if (!type || *type == '\0') { return NULL;
}
if (conf->add_default_charset != ADD_DEFAULT_CHARSET_ON) { return type;
}
request_conf = ap_get_core_module_config(r->request_config); if (request_conf->suppress_charset) { return type;
}
type_len = strlen(type);
if (apr_strmatch(charset_pattern, type, type_len) != NULL) { /* already has parameter, do nothing */ /* XXX we don't check the validity */
;
} else { /* see if it makes sense to add the charset. At present, * we only add it if the Content-type is one of needcset[]
*/ for (pcset = needcset_patterns; *pcset ; pcset++) { if (apr_strmatch(*pcset, type, type_len) != NULL) { struct iovec concat[3];
concat[0].iov_base = (void *)type;
concat[0].iov_len = type_len;
concat[1].iov_base = (void *)"; charset=";
concat[1].iov_len = sizeof("; charset=") - 1;
concat[2].iov_base = (void *)(conf->add_default_charset_name);
concat[2].iov_len = strlen(conf->add_default_charset_name);
type = apr_pstrcatv(r->pool, concat, 3, NULL); break;
}
}
}
/* * Return the latest rational time from a request/mtime (modification time) * pair. We return the mtime unless it's in the future, in which case we * return the current time. We use the request time as a reference in order * to limit the number of calls to time(). We don't check for futurosity * unless the mtime is at least as new as the reference.
*/
AP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime)
{
apr_time_t now;
/* For all static responses, it's almost certain that the file was * last modified before the beginning of the request. So there's * no reason to call time(NULL) again. But if the response has been * created on demand, then it might be newer than the time the request * started. In this event we really have to call time(NULL) again * so that we can give the clients the most accurate Last-Modified. If we * were given a time in the future, we return the current time - the * Last-Modified can't be in the future.
*/
now = (mtime < r->request_time) ? r->request_time : apr_time_now(); return (mtime > now) ? now : mtime;
}
/* Get a line of protocol input, including any continuation lines * caused by MIME folding (or broken clients) if fold != 0, and place it * in the buffer s, of size n bytes, without the ending newline. * * Pulls from r->proto_input_filters instead of r->input_filters for * stricter protocol adherence and better input filter behavior during * chunked trailer processing (for http). * * If s is NULL, ap_rgetline_core will allocate necessary memory from r->pool. * * Returns APR_SUCCESS if there are no problems and sets *read to be * the full length of s. * * APR_ENOSPC is returned if there is not enough buffer space. * Other errors may be returned on other errors. * * The [CR]LF are *not* returned in the buffer. Therefore, a *read of 0 * indicates that an empty line was read. * * Notes: Because the buffer uses 1 char for NUL, the most we can return is * (n - 1) actual characters. * * If no LF is detected on the last line due to a dropped connection * or a full buffer, that's considered an error.
*/
AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
apr_size_t *read, request_rec *r, int flags, apr_bucket_brigade *bb)
{
apr_status_t rv;
apr_bucket *e;
apr_size_t bytes_handled = 0, current_alloc = 0; char *pos, *last_char = *s; int do_alloc = (*s == NULL), saw_eos = 0; int fold = flags & AP_GETLINE_FOLD; int crlf = flags & AP_GETLINE_CRLF; int nospc_eol = flags & AP_GETLINE_NOSPC_EOL; int saw_eol = 0, saw_nospc = 0;
if (!n) { /* Needs room for NUL byte at least */
*read = 0; return APR_BADARG;
}
/* * Initialize last_char as otherwise a random value will be compared * against APR_ASCII_LF at the end of the loop if bb only contains * zero-length buckets.
*/ if (last_char)
*last_char = '\0';
do {
apr_brigade_cleanup(bb);
rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_GETLINE,
APR_BLOCK_READ, 0); if (rv != APR_SUCCESS) { goto cleanup;
}
/* Something horribly wrong happened. Someone didn't block! * (this also happens at the end of each keepalive connection)
*/ if (APR_BRIGADE_EMPTY(bb)) {
rv = APR_EGENERAL; goto cleanup;
}
for (e = APR_BRIGADE_FIRST(bb);
e != APR_BRIGADE_SENTINEL(bb);
e = APR_BUCKET_NEXT(e))
{ constchar *str;
apr_size_t len;
/* If we see an EOS, don't bother doing anything more. */ if (APR_BUCKET_IS_EOS(e)) {
saw_eos = 1; break;
}
if (len == 0) { /* no use attempting a zero-byte alloc (hurts when * using --with-efence --enable-pool-debug) or * doing any of the other logic either
*/ continue;
}
/* Would this overrun our buffer? If so, we'll die. */ if (n < bytes_handled + len) { /* Before we die, let's fill the buffer up to its limit (i.e. * fall through with the remaining length, if any), setting * saw_eol on LF to stop the outer loop appropriately; we may * come back here once the buffer is filled (no LF seen), and * either be done at that time or continue to wait for LF here * if nospc_eol is set. * * But there is also a corner cases which we want to address, * namely if the buffer is overrun by the final LF only (i.e. * the CR fits in); this is not really an overrun since we'll * strip the CR finally (and use it for NUL byte), but anyway * we have to handle the case so that it's not returned to the * caller as part of the truncated line (it's not!). This is * easier to consider that LF is out of counting and thus fall * through with no error (saw_eol is set to 2 so that we later * ignore LF handling already done here), while folding and * nospc_eol logics continue to work (or fail) appropriately.
*/
saw_eol = (str[len - 1] == APR_ASCII_LF); if (/* First time around */
saw_eol && !saw_nospc /* Single LF completing the buffered CR, */
&& ((len == 1 && ((*s)[bytes_handled - 1] == APR_ASCII_CR)) /* or trailing CRLF overuns by LF only */
|| (len > 1 && str[len - 2] == APR_ASCII_CR
&& n - bytes_handled + 1 == len))) { /* In both cases *last_char is (to be) the CR stripped by * later 'bytes_handled = last_char - *s'.
*/
saw_eol = 2;
} else { /* In any other case we'd lose data. */
rv = APR_ENOSPC;
saw_nospc = 1;
}
len = n - bytes_handled; if (!len) { if (saw_eol) { break;
} if (nospc_eol) { continue;
} goto cleanup;
}
}
/* Do we have to handle the allocation ourselves? */ if (do_alloc) { /* We'll assume the common case where one bucket is enough. */ if (!*s) {
current_alloc = len;
*s = apr_palloc(r->pool, current_alloc + 1);
} elseif (bytes_handled + len > current_alloc) { /* Increase the buffer size */
apr_size_t new_size = current_alloc * 2; char *new_buffer;
if (bytes_handled + len > new_size) {
new_size = (bytes_handled + len) * 2;
}
new_buffer = apr_palloc(r->pool, new_size + 1);
/* Copy what we already had. */
memcpy(new_buffer, *s, bytes_handled);
current_alloc = new_size;
*s = new_buffer;
}
}
/* Just copy the rest of the data to the end of the old buffer. */
pos = *s + bytes_handled;
memcpy(pos, str, len);
last_char = pos + len - 1;
/* We've now processed that new data - update accordingly. */
bytes_handled += len;
}
/* If we got a full line of input, stop reading */ if (last_char && (*last_char == APR_ASCII_LF)) {
saw_eol = 1;
}
} while (!saw_eol);
if (rv != APR_SUCCESS) { /* End of line after APR_ENOSPC above */ goto cleanup;
}
/* Now terminate the string at the end of the line; * if the last-but-one character is a CR, terminate there. * LF is handled above (not accounted) when saw_eol == 2, * the last char is CR to terminate at still.
*/ if (saw_eol < 2) { if (last_char > *s && last_char[-1] == APR_ASCII_CR) {
last_char--;
} elseif (crlf) {
rv = APR_EINVAL; goto cleanup;
}
}
bytes_handled = last_char - *s;
/* If we're folding, we have more work to do. * * Note that if an EOS was seen, we know we can't have another line.
*/ if (fold && bytes_handled && !saw_eos) { for (;;) { constchar *str;
apr_size_t len; char c;
/* Clear the temp brigade for this filter read. */
apr_brigade_cleanup(bb);
/* We only care about the first byte. */
rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_SPECULATIVE,
APR_BLOCK_READ, 1); if (rv != APR_SUCCESS) { goto cleanup;
}
if (APR_BRIGADE_EMPTY(bb)) { break;
}
e = APR_BRIGADE_FIRST(bb);
/* If we see an EOS, don't bother doing anything more. */ if (APR_BUCKET_IS_EOS(e)) { break;
}
/* Found one, so call ourselves again to get the next line. * * FIXME: If the folding line is completely blank, should we * stop folding? Does that require also looking at the next * char?
*/ /* When we call destroy, the buckets are deleted, so save that * one character we need. This simplifies our execution paths * at the cost of one character read.
*/
c = *str; if (c == APR_ASCII_BLANK || c == APR_ASCII_TAB) { /* Do we have enough space? We may be full now. */ if (bytes_handled >= n) {
rv = APR_ENOSPC; goto cleanup;
} else {
apr_size_t next_size, next_len; char *tmp;
/* If we're doing the allocations for them, we have to * give ourselves a NULL and copy it on return.
*/ if (do_alloc) {
tmp = NULL;
} else {
tmp = last_char;
}
/* we need to alloc an extra byte for a null */
new_buffer = apr_palloc(r->pool, new_size);
/* Copy what we already had. */
memcpy(new_buffer, *s, bytes_handled);
/* copy the new line, including the trailing null */
memcpy(new_buffer + bytes_handled, tmp, next_len);
*s = new_buffer;
}
last_char += next_len;
bytes_handled += next_len;
}
} else { /* next character is not tab or space */ break;
}
}
}
cleanup: if (bytes_handled >= n) {
bytes_handled = n - 1;
}
*read = bytes_handled; if (*s) { /* ensure the string is NUL terminated */
(*s)[*read] = '\0';
/* PR#43039: We shouldn't accept NULL bytes within the line */
bytes_handled = strlen(*s); if (bytes_handled < *read) {
ap_log_data(APLOG_MARK, APLOG_DEBUG, ap_server_conf, "NULL bytes in header", *s, *read, 0);
*read = bytes_handled; if (rv == APR_SUCCESS) {
rv = APR_EINVAL;
}
}
} return rv;
}
#if APR_CHARSET_EBCDIC
AP_DECLARE(apr_status_t) ap_rgetline(char **s, apr_size_t n,
apr_size_t *read, request_rec *r, int fold, apr_bucket_brigade *bb)
{ /* on ASCII boxes, ap_rgetline is a macro which simply invokes * ap_rgetline_core with the same parms * * on EBCDIC boxes, each complete http protocol input line needs to be * translated into the code page used by the compiler. Since * ap_rgetline_core uses recursion, we do the translation in a wrapper * function to ensure that each input character gets translated only once.
*/
apr_status_t rv;
/* Map the out-of-space condition to the old API. */ if (rv == APR_ENOSPC) { return n;
}
/* Anything else is just bad. */ if (rv != APR_SUCCESS) { return -1;
}
return (int)len;
}
/* parse_uri: break apart the uri * Side Effects: * - sets r->args to rest after '?' (or NULL if no '?') * - sets r->uri to request uri (without r->args part) * - sets r->hostname (if not set already) from request (scheme://host:port)
*/
AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, constchar *uri)
{ int status = HTTP_OK;
r->unparsed_uri = apr_pstrdup(r->pool, uri);
/* http://issues.apache.org/bugzilla/show_bug.cgi?id=31875 * http://issues.apache.org/bugzilla/show_bug.cgi?id=28450 * * This is not in fact a URI, it's a path. That matters in the * case of a leading double-slash. We need to resolve the issue * by normalizing that out before treating it as a URI.
*/ while ((uri[0] == '/') && (uri[1] == '/')) {
++uri ;
} if (r->method_number == M_CONNECT) {
status = apr_uri_parse_hostinfo(r->pool, uri, &r->parsed_uri);
} else {
status = apr_uri_parse(r->pool, uri, &r->parsed_uri);
}
if (status == APR_SUCCESS) { /* if it has a scheme we may need to do absoluteURI vhost stuff */ if (r->parsed_uri.scheme
&& !ap_cstr_casecmp(r->parsed_uri.scheme, ap_http_scheme(r))) {
r->hostname = r->parsed_uri.hostname;
} elseif (r->method_number == M_CONNECT) {
r->hostname = r->parsed_uri.hostname;
}
#ifdefined(OS2) || defined(WIN32) /* Handle path translations for OS/2 and plug security hole. * This will prevent "http://www.wherever.com/..\..\/" from * returning a directory for the root drive.
*/
{ char *x;
/* get the length of the field name for logging, but no more than 80 bytes */ #define LOG_NAME_MAX_LEN 80 staticint field_name_len(constchar *field)
{ constchar *end = ap_strchr_c(field, ':'); if (end == NULL || end - field > LOG_NAME_MAX_LEN) return LOG_NAME_MAX_LEN; return end - field;
}
/* Read past empty lines until we get a real request line, * a read error, the connection closes (EOF), or we timeout. * * We skip empty lines because browsers have to tack a CRLF on to the end * of POSTs to support old CERN webservers. But note that we may not * have flushed any previous response completely to the client yet. * We delay the flush as long as possible so that we can improve * performance for clients that are pipelining requests. If a request * is pipelined then we won't block during the (implicit) read() below. * If the requests aren't pipelined, then the client is still waiting * for the final buffer flush from us, and we will block in the implicit * read(). B_SAFEREAD ensures that the BUFF layer flushes if it will * have to block during a read.
*/
do {
apr_status_t rv;
/* ensure ap_rgetline allocates memory each time thru the loop * if there are empty lines
*/
r->the_request = NULL;
rv = ap_rgetline(&(r->the_request), (apr_size_t)(r->server->limit_req_line + 2),
&len, r, strict ? AP_GETLINE_CRLF : 0, bb);
if (rv != APR_SUCCESS) {
r->request_time = apr_time_now();
/* ap_rgetline returns APR_ENOSPC if it fills up the * buffer before finding the end-of-line. This is only going to * happen if it exceeds the configured limit for a request-line.
*/ if (APR_STATUS_IS_ENOSPC(rv)) {
r->status = HTTP_REQUEST_URI_TOO_LARGE;
} elseif (APR_STATUS_IS_TIMEUP(rv)) {
r->status = HTTP_REQUEST_TIME_OUT;
} elseif (APR_STATUS_IS_EINVAL(rv)) {
r->status = HTTP_BAD_REQUEST;
}
r->proto_num = HTTP_VERSION(1,0);
r->protocol = apr_pstrdup(r->pool, "HTTP/1.0"); return 0;
}
} while ((len <= 0) && (--num_blank_lines >= 0));
/* Set r->request_time before any logging, mod_unique_id needs it. */
r->request_time = apr_time_now();
if (APLOGrtrace5(r)) {
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "Request received from client: %s",
ap_escape_logitem(r->pool, r->the_request));
}
/* If there is whitespace before a method, skip it and mark in error */ if (apr_isspace(*r->method)) {
deferred_error = rrl_badwhitespace; for ( ; apr_isspace(*r->method); ++r->method)
;
}
/* Scan the method up to the next whitespace, ensure it contains only * valid http-token characters, otherwise mark in error
*/ if (strict) {
ll = (char*) ap_scan_http_token(r->method);
} else {
ll = (char*) ap_scan_vchar_obstext(r->method);
}
/* Verify method terminated with a single SP, or mark as specific error */ if (!ll) { if (deferred_error == rrl_none)
deferred_error = rrl_missinguri;
r->protocol = uri = ""; goto rrl_done;
} elseif (strict && ll[0] && apr_isspace(ll[1])
&& deferred_error == rrl_none) {
deferred_error = rrl_excesswhitespace;
}
/* Advance uri pointer over leading whitespace, NUL terminate the method * If non-SP whitespace is encountered, mark as specific error
*/ for (uri = ll; apr_isspace(*uri); ++uri) if (*uri != ' ' && deferred_error == rrl_none)
deferred_error = rrl_badwhitespace;
*ll = '\0';
if (!*uri && deferred_error == rrl_none)
deferred_error = rrl_missinguri;
/* Scan the URI up to the next whitespace, ensure it contains no raw * control characters, otherwise mark in error
*/
ll = (char*) ap_scan_vchar_obstext(uri); if (ll == uri || (*ll && !apr_isspace(*ll))) {
deferred_error = rrl_baduri;
ll = strpbrk(ll, "\t\n\v\f\r ");
}
/* Verify URI terminated with a single SP, or mark as specific error */ if (!ll) {
r->protocol = ""; goto rrl_done;
} elseif (strict && ll[0] && apr_isspace(ll[1])
&& deferred_error == rrl_none) {
deferred_error = rrl_excesswhitespace;
}
/* Advance protocol pointer over leading whitespace, NUL terminate the uri * If non-SP whitespace is encountered, mark as specific error
*/ for (r->protocol = ll; apr_isspace(*r->protocol); ++r->protocol) if (*r->protocol != ' ' && deferred_error == rrl_none)
deferred_error = rrl_badwhitespace;
*ll = '\0';
/* Scan the protocol up to the next whitespace, validation comes later */ if (!(ll = (char*) ap_scan_vchar_obstext(r->protocol))) {
len = strlen(r->protocol); goto rrl_done;
}
len = ll - r->protocol;
/* Advance over trailing whitespace, if found mark in error, * determine if trailing text is found, unconditionally mark in error, * finally NUL terminate the protocol string
*/ if (*ll && !apr_isspace(*ll)) {
deferred_error = rrl_badprotocol;
} elseif (strict && *ll) {
deferred_error = rrl_excesswhitespace;
} else { for ( ; apr_isspace(*ll); ++ll) if (*ll != ' ' && deferred_error == rrl_none)
deferred_error = rrl_badwhitespace; if (*ll && deferred_error == rrl_none)
deferred_error = rrl_trailingtext;
}
*((char *)r->protocol + len) = '\0';
rrl_done: /* For internal integrity and palloc efficiency, reconstruct the_request * in one palloc, using only single SP characters, per spec.
*/
r->the_request = apr_pstrcat(r->pool, r->method, *uri ? " " : NULL, uri,
*r->protocol ? " " : NULL, r->protocol, NULL);
/* Determine the method_number and parse the uri prior to invoking error * handling, such that these fields are available for substitution
*/
r->method_number = ap_method_number_of(r->method); if (r->method_number == M_GET && r->method[0] == 'H')
r->header_only = 1;
if (strict) { if (r->parsed_uri.fragment) { /* RFC3986 3.5: no fragment */
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02421) "HTTP Request Line; URI must not contain a fragment");
r->status = HTTP_BAD_REQUEST; goto rrl_failed;
} if (r->parsed_uri.user || r->parsed_uri.password) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02422) "HTTP Request Line; URI must not contain a " "username/password");
r->status = HTTP_BAD_REQUEST; goto rrl_failed;
}
}
return 1;
rrl_failed: if (r->proto_num == HTTP_VERSION(0, 9)) { /* Send all parsing and protocol error response with 1.x behavior, * and reserve 505 errors for actual HTTP protocols presented. * As called out in RFC7230 3.5, any errors parsing the protocol * from the request line are nearly always misencoded HTTP/1.x * requests. Only a valid 0.9 request with no parsing errors * at all may be treated as a simple request, if allowed.
*/
r->assbackwards = 0;
r->connection->keepalive = AP_CONN_CLOSE;
r->proto_num = HTTP_VERSION(1, 0);
r->protocol = apr_pstrdup(r->pool, "HTTP/1.0");
} return 0;
}
AP_DECLARE(int) ap_check_request_header(request_rec *r)
{
core_server_config *conf; int strict_host_check; constchar *expect; int access_status;
/* update what we think the virtual host is based on the headers we've * now read. may update status.
*/
strict_host_check = (conf->strict_host_check == AP_CORE_CONFIG_ON);
access_status = ap_update_vhost_from_headers_ex(r, strict_host_check); if (strict_host_check && access_status != HTTP_OK) { if (r->server == ap_server_conf) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10156) "Requested hostname '%s' did not match any ServerName/ServerAlias " "in the global server configuration ", r->hostname);
} else {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10157) "Requested hostname '%s' did not match any ServerName/ServerAlias " "in the matching virtual host (default vhost for " "current connection is %s:%u)",
r->hostname, r->server->defn_name, r->server->defn_line_number);
}
r->status = access_status;
} if (r->status != HTTP_OK) { return 0;
}
if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1)))
|| ((r->proto_num == HTTP_VERSION(1, 1))
&& !apr_table_get(r->headers_in, "Host"))) { /* * Client sent us an HTTP/1.1 or later request without telling us the * hostname, either with a full URL or a Host: header. We therefore * need to (as per the 1.1 spec) send an error. As a special case, * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain * a Host: header, and the server MUST respond with 400 if it doesn't.
*/
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00569) "client sent HTTP/1.1 request without hostname " "(see RFC2616 section 14.23): %s", r->uri);
r->status = HTTP_BAD_REQUEST; return 0;
}
if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL)
&& (expect[0] != '\0')) { /* * The Expect header field was added to HTTP/1.1 after RFC 2068 * as a means to signal when a 100 response is desired and, * unfortunately, to signal a poor man's mandatory extension that * the server must understand or return 417 Expectation Failed.
*/ if (ap_cstr_casecmp(expect, "100-continue") == 0) {
r->expecting_100 = 1;
} else {
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00570) "client sent an unrecognized expectation value " "of Expect: %s", expect);
r->status = HTTP_EXPECTATION_FAILED; return 0;
}
}
/* * Read header lines until we get the empty separator line, a read error, * the connection closes (EOF), reach the server limit, or we timeout.
*/ while(1) {
apr_status_t rv;
if (rv != APR_SUCCESS) { if (APR_STATUS_IS_TIMEUP(rv)) {
r->status = HTTP_REQUEST_TIME_OUT;
} else {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, "Failed to read request header line %s", field);
r->status = HTTP_BAD_REQUEST;
}
/* ap_rgetline returns APR_ENOSPC if it fills up the buffer before * finding the end-of-line. This is only going to happen if it * exceeds the configured limit for a field size.
*/ if (rv == APR_ENOSPC) {
apr_table_setn(r->notes, "error-notes", "Size of a request header field " "exceeds server limit.");
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00561) "Request header exceeds LimitRequestFieldSize%s" "%.*s",
(field && *field) ? ": " : "",
(field) ? field_name_len(field) : 0,
(field) ? field : "");
} return;
}
/* For all header values, and all obs-fold lines, the presence of * additional whitespace is a no-op, so collapse trailing whitespace * to save buffer allocation and optimize copy operations. * Do not remove the last single whitespace under any condition.
*/ while (len > 1 && (field[len-1] == '\t' || field[len-1] == ' ')) {
field[--len] = '\0';
}
if (*field == '\t' || *field == ' ') {
/* Append any newly-read obs-fold line onto the preceding * last_field line we are processing
*/
apr_size_t fold_len;
if (field[1] == '\0') {
r->status = HTTP_BAD_REQUEST;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03443) "Empty folded line encountered"); return;
}
/* Leading whitespace on an obs-fold line can be
* similarly discarded */ while (field[1] == '\t' || field[1] == ' ') {
++field; --len;
}
/* This line is a continuation of the preceding line(s), * so append it to the line that we've set aside. * Note: this uses a power-of-two allocator to avoid * doing O(n) allocs and using O(n^2) space for * continuations that span many many lines.
*/
fold_len = last_len + len + 1; /* trailing null */
if (fold_len >= (apr_size_t)(r->server->limit_req_fieldsize)) {
r->status = HTTP_BAD_REQUEST; /* report what we have accumulated so far before the * overflow (last_field) as the field with the problem
*/
apr_table_setn(r->notes, "error-notes", "Size of a request header field " "exceeds server limit.");
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00562) "Request header exceeds LimitRequestFieldSize " "after folding: %.*s",
field_name_len(last_field), last_field); return;
}
/* We've appended this obs-fold line to last_len, proceed to * read the next input line
*/ continue;
} elseif (last_field != NULL) {
/* Process the previous last_field header line with all obs-folded * segments already concatenated (this is not operating on the * most recently read input line).
*/
if (r->server->limit_req_fields
&& (++fields_read > r->server->limit_req_fields)) {
r->status = HTTP_BAD_REQUEST;
apr_table_setn(r->notes, "error-notes", "The number of request header fields " "exceeds this server's limit.");
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00563) "Number of request headers exceeds " "LimitRequestFields"); return;
}
if (!strict)
{ /* Not Strict ('Unsafe' mode), using the legacy parser */
if (!(value = strchr(last_field, ':'))) { /* Find ':' or */
r->status = HTTP_BAD_REQUEST; /* abort bad request */
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00564) "Request header field is missing ':' " "separator: %.*s", (int)LOG_NAME_MAX_LEN,
last_field); return;
}
if (value == last_field) {
r->status = HTTP_BAD_REQUEST;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03453) "Request header field name was empty"); return;
}
*value++ = '\0'; /* NUL-terminate at colon */
if (strpbrk(last_field, "\t\n\v\f\r ")) {
r->status = HTTP_BAD_REQUEST;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03452) "Request header field name presented" " invalid whitespace"); return;
}
while (*value == ' ' || *value == '\t') {
++value; /* Skip to start of value */
}
if (strpbrk(value, "\n\v\f\r")) {
r->status = HTTP_BAD_REQUEST;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03451) "Request header field value presented" " bad whitespace"); return;
}
} else/* Using strict RFC7230 parsing */
{ /* Ensure valid token chars before ':' per RFC 7230 3.2.4 */
value = (char *)ap_scan_http_token(last_field); if ((value == last_field) || *value != ':') {
r->status = HTTP_BAD_REQUEST;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02426) "Request header field name is malformed: " "%.*s", (int)LOG_NAME_MAX_LEN, last_field); return;
}
*value++ = '\0'; /* NUL-terminate last_field name at ':' */
while (*value == ' ' || *value == '\t') {
++value; /* Skip LWS of value */
}
/* Find invalid, non-HT ctrl char, or the trailing NULL */
tmp_field = (char *)ap_scan_http_field_content(value);
/* Reject value for all garbage input (CTRLs excluding HT) * e.g. only VCHAR / SP / HT / obs-text are allowed per * RFC7230 3.2.6 - leave all more explicit rule enforcement * for specific header handler logic later in the cycle
*/ if (*tmp_field != '\0') {
r->status = HTTP_BAD_REQUEST;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02427) "Request header value is malformed: " "%.*s", (int)LOG_NAME_MAX_LEN, value); return;
}
}
apr_table_addn(r->headers_in, last_field, value);
/* This last_field header is now stored in headers_in, * resume processing of the current input line.
*/
}
/* Found the terminating empty end-of-headers line, stop. */ if (len == 0) { break;
}
/* Keep track of this new header line so that we can extend it across * any obs-fold or parse it on the next loop iteration. We referenced * our previously allocated buffer in r->headers_in, * so allocate a fresh buffer if required.
*/
alloc_len = 0;
last_field = field;
last_len = len;
}
/* Combine multiple message-header fields with the same * field-name, following RFC 2616, 4.2.
*/
apr_table_compress(r->headers_in, APR_OVERLAP_TABLES_MERGE);
r->status = HTTP_OK; /* Until further notice */
r->header_only = 0;
r->the_request = NULL;
/* Begin by presuming any module can make its own path_info assumptions, * until some module interjects and changes the value.
*/
r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
/* Get the request... */ if (!read_request_line(r, tmp_bb) || !ap_parse_request_line(r)) {
apr_brigade_cleanup(tmp_bb); switch (r->status) { case HTTP_REQUEST_URI_TOO_LARGE: case HTTP_BAD_REQUEST: case HTTP_VERSION_NOT_SUPPORTED: case HTTP_NOT_IMPLEMENTED: if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00565) "request failed: client's request-line exceeds LimitRequestLine (longer than %d)",
r->server->limit_req_line);
} elseif (r->method == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00566) "request failed: malformed request line");
}
access_status = r->status; goto die_unusable_input;
case HTTP_REQUEST_TIME_OUT: /* Just log, no further action on this connection. */
ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, NULL); if (!r->connection->keepalives)
ap_run_log_transaction(r); break;
} /* Not worth dying with. */
conn->keepalive = AP_CONN_CLOSE;
apr_pool_destroy(r->pool); goto ignore;
}
apr_brigade_cleanup(tmp_bb);
/* We may have been in keep_alive_timeout mode, so toggle back * to the normal timeout mode as we fetch the header lines, * as necessary.
*/
apply_server_config(r);
tenc = apr_table_get(r->headers_in, "Transfer-Encoding"); if (tenc) { /* https://tools.ietf.org/html/rfc7230 * Section 3.3.3.3: "If a Transfer-Encoding header field is * present in a request and the chunked transfer coding is not * the final encoding ...; the server MUST respond with the 400 * (Bad Request) status code and then close the connection".
*/ if (!ap_is_chunked(r->pool, tenc)) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02539) "client sent unknown Transfer-Encoding " "(%s): %s", tenc, r->uri);
access_status = HTTP_BAD_REQUEST; goto die_unusable_input;
}
/* https://tools.ietf.org/html/rfc7230 * Section 3.3.3.3: "If a message is received with both a * Transfer-Encoding and a Content-Length header field, the * Transfer-Encoding overrides the Content-Length. ... A sender * MUST remove the received Content-Length field".
*/ if (clen) {
apr_table_unset(r->headers_in, "Content-Length");
/* Don't reuse this connection anyway to avoid confusion with * intermediaries and request/reponse spltting.
*/
conn->keepalive = AP_CONN_CLOSE;
}
}
}
/* * Add the HTTP_IN filter here to ensure that ap_discard_request_body * called by ap_die and by ap_send_error_response works correctly on * status codes that do not cause the connection to be dropped and * in situations where the connection should be kept alive.
*/
ap_add_input_filter_handle(ap_http_input_filter_handle,
NULL, r, r->connection);
/* Validate Host/Expect headers and select vhost. */ if (!ap_check_request_header(r)) { /* we may have switched to another server still */
apply_server_config(r);
access_status = r->status; goto die_before_hooks;
}
/* we may have switched to another server */
apply_server_config(r);
if ((access_status = ap_post_read_request(r))) { goto die;
}
die_unusable_input: /* Input filters are in an undeterminate state, cleanup (including * CORE_IN's socket) such that any further attempt to read is EOF.
*/
{
ap_filter_t *f = conn->input_filters; while (f) { if (f->frec == ap_core_input_filter_handle) {
core_net_rec *net = f->ctx;
apr_brigade_cleanup(net->in_ctx->b); break;
}
ap_remove_input_filter(f);
f = f->next;
}
conn->input_filters = r->input_filters = f;
conn->keepalive = AP_CONN_CLOSE;
}
die_before_hooks: /* First call to ap_die() (non recursive) */
r->status = HTTP_OK;
die:
ap_die(access_status, r);
/* ap_die() sent the response through the output filters, we must now
* end the request with an EOR bucket for stream/pipeline accounting.
*/
{
apr_bucket_brigade *eor_bb;
eor_bb = apr_brigade_create(conn->pool, conn->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(eor_bb,
ap_bucket_eor_create(conn->bucket_alloc, r));
ap_pass_brigade(conn->output_filters, eor_bb);
apr_brigade_cleanup(eor_bb);
}
ignore:
r = NULL;
AP_READ_REQUEST_FAILURE((uintptr_t)r);
return NULL;
}
AP_DECLARE(int) ap_post_read_request(request_rec *r)
{
int status;
if ((status = ap_run_post_read_request(r))) {
return status;
}
/* if a request with a body creates a subrequest, remove original request's
* input headers which pertain to the body which has already been read.
* out-of-line helper function for ap_set_sub_req_protocol.
*/
/*
* A couple of other functions which initialize some of the fields of
* a request structure, as appropriate for adjuncts of one kind or another
* to a request in progress. Best here, rather than elsewhere, since
* *someone* has to set the protocol-specific fields...
*/
/* did the original request have a body? (e.g. POST w/SSI tags)
* if so, make sure the subrequest doesn't inherit body headers
*/ if (!r->kept_body && (apr_table_get(r->headers_in, "Content-Length")
|| apr_table_get(r->headers_in, "Transfer-Encoding"))) {
strip_headers_request_body(rnew);
}
rnew->subprocess_env = apr_table_copy(rnew->pool, r->subprocess_env);
rnew->headers_out = apr_table_make(rnew->pool, 5);
rnew->err_headers_out = apr_table_make(rnew->pool, 5);
rnew->trailers_out = apr_table_make(rnew->pool, 5);
rnew->notes = apr_table_make(rnew->pool, 5);
AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub)
{
/* tell the filter chain there is no more content coming */ if (!sub->eos_sent) {
end_output_stream(sub, OK);
}
}
/* finalize_request_protocol is called at completion of sending the
* response. Its sole purpose is to send the terminating protocol
* information for any wrappers around the response message body
* (i.e., transfer encodings). It should have been named finalize_response.
*/
AP_DECLARE(void) ap_finalize_request_protocol(request_rec *r)
{
int status = ap_discard_request_body(r);
/* tell the filter chain there is no more content coming */ if (!r->eos_sent) {
end_output_stream(r, status);
}
}
/*
* Support for the Basic authentication protocol, and a bit for Digest.
*/
AP_DECLARE(void) ap_note_auth_failure(request_rec *r)
{ const char *type = ap_auth_type(r); if (type) {
ap_run_note_auth_failure(r, type);
} else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00571) "need AuthType to note auth failure: %s", r->uri);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.