summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Fritsch <sf@apache.org>2012-12-29 21:39:49 +0100
committerStefan Fritsch <sf@apache.org>2012-12-29 21:39:49 +0100
commit33a2fbf2bc4ee469cbd4be5455f157b0802515db (patch)
tree3f7c222fbb3897d9756982ba3243f02f77f3dadb
parentextend description of r->hostname (diff)
downloadapache2-33a2fbf2bc4ee469cbd4be5455f157b0802515db.tar.xz
apache2-33a2fbf2bc4ee469cbd4be5455f157b0802515db.zip
Correctly parse an IPv6 literal host specification in an absolute URL
in the request line. - Fix handling of brackets [ ] surrounding the IPv6 address. - Skip parsing r->hostname again if not necessary. - Do some checks that the IPv6 address is sane. This is not done by apr_parse_addr_port(). git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1426827 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--CHANGES3
-rw-r--r--server/vhost.c162
2 files changed, 117 insertions, 48 deletions
diff --git a/CHANGES b/CHANGES
index d15d466746..c4aa84244f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
-*- coding: utf-8 -*-
Changes with Apache 2.5.0
+ *) core: Correctly parse an IPv6 literal host specification in an absolute
+ URL in the request line. [Stefan Fritsch]
+
*) mod_ssl: add support for subjectAltName-based host name checking
in proxy mode. PR 54030. [Kaspar Brand]
diff --git a/server/vhost.c b/server/vhost.c
index fd7c0ad646..697bbdcdcd 100644
--- a/server/vhost.c
+++ b/server/vhost.c
@@ -675,6 +675,66 @@ static int vhost_check_config(apr_pool_t *p, apr_pool_t *plog,
* run-time vhost matching functions
*/
+static apr_status_t fix_hostname_v6_literal(request_rec *r, char *host)
+{
+ char *dst;
+ int double_colon = 0;
+
+ for (dst = host; *dst; dst++) {
+ if (apr_isxdigit(*dst)) {
+ if (apr_isupper(*dst)) {
+ *dst = apr_tolower(*dst);
+ }
+ }
+ else if (*dst == ':') {
+ if (*(dst + 1) == ':') {
+ if (double_colon)
+ return APR_EINVAL;
+ double_colon = 1;
+ }
+ else if (*(dst + 1) == '.') {
+ return APR_EINVAL;
+ }
+ }
+ else if (*dst == '.') {
+ /* For IPv4-mapped IPv6 addresses like ::FFFF:129.144.52.38 */
+ if (*(dst + 1) == ':' || *(dst + 1) == '.')
+ return APR_EINVAL;
+ }
+ else {
+ return APR_EINVAL;
+ }
+ }
+ return APR_SUCCESS;
+}
+
+static apr_status_t fix_hostname_non_v6(request_rec *r, char *host)
+{
+ char *dst;
+
+ for (dst = host; *dst; dst++) {
+ if (apr_islower(*dst)) {
+ /* leave char unchanged */
+ }
+ else if (*dst == '.') {
+ if (*(dst + 1) == '.') {
+ return APR_EINVAL;
+ }
+ }
+ else if (apr_isupper(*dst)) {
+ *dst = apr_tolower(*dst);
+ }
+ else if (*dst == '/' || *dst == '\\') {
+ return APR_EINVAL;
+ }
+ }
+ /* strip trailing gubbins */
+ if (dst > host && dst[-1] == '.') {
+ dst[-1] = '\0';
+ }
+ return APR_SUCCESS;
+}
+
/* Lowercase and remove any trailing dot and/or :port from the hostname,
* and check that it is sane.
*
@@ -688,67 +748,65 @@ static int vhost_check_config(apr_pool_t *p, apr_pool_t *plog,
* Instead we just check for filesystem metacharacters: directory
* separators / and \ and sequences of more than one dot.
*/
-static void fix_hostname(request_rec *r)
+static void fix_hostname(request_rec *r, const char *host_header)
{
+ const char *src;
char *host, *scope_id;
- char *dst;
apr_port_t port;
apr_status_t rv;
const char *c;
- /* According to RFC 2616, Host header field CAN be blank. */
- if (!*r->hostname) {
+ src = host_header ? host_header : r->hostname;
+
+ /* According to RFC 2616, Host header field CAN be blank.
+ * XXX But only 'if the requested URI does not include an Internet host
+ * XXX name'. Can this happen?
+ */
+ if (!*src) {
return;
}
/* apr_parse_addr_port will interpret a bare integer as a port
* which is incorrect in this context. So treat it separately.
*/
- for (c = r->hostname; apr_isdigit(*c); ++c);
- if (!*c) { /* pure integer */
+ for (c = src; apr_isdigit(*c); ++c);
+ if (!*c) {
+ /* pure integer */
+ r->hostname = src;
return;
}
- rv = apr_parse_addr_port(&host, &scope_id, &port, r->hostname, r->pool);
- if (rv != APR_SUCCESS || scope_id) {
- goto bad;
+ if (host_header) {
+ rv = apr_parse_addr_port(&host, &scope_id, &port, src, r->pool);
+ if (rv != APR_SUCCESS || scope_id)
+ goto bad;
+ if (port) {
+ /* Don't throw the Host: header's port number away:
+ save it in parsed_uri -- ap_get_server_port() needs it! */
+ /* @@@ XXX there should be a better way to pass the port.
+ * Like r->hostname, there should be a r->portno
+ */
+ r->parsed_uri.port = port;
+ r->parsed_uri.port_str = apr_itoa(r->pool, (int)port);
+ }
+ if (host_header[0] == '[')
+ rv = fix_hostname_v6_literal(r, host);
+ else
+ rv = fix_hostname_non_v6(r, host);
}
-
- if (port) {
- /* Don't throw the Host: header's port number away:
- save it in parsed_uri -- ap_get_server_port() needs it! */
- /* @@@ XXX there should be a better way to pass the port.
- * Like r->hostname, there should be a r->portno
+ else {
+ /*
+ * Already parsed, surrounding [ ] (if IPv6 literal) and :port have
+ * already been removed.
*/
- r->parsed_uri.port = port;
- r->parsed_uri.port_str = apr_itoa(r->pool, (int)port);
- }
-
- /* if the hostname is an IPv6 numeric address string, it was validated
- * already; otherwise, further validation is needed
- */
- if (r->hostname[0] != '[') {
- for (dst = host; *dst; dst++) {
- if (apr_islower(*dst)) {
- /* leave char unchanged */
- }
- else if (*dst == '.') {
- if (*(dst + 1) == '.') {
- goto bad;
- }
- }
- else if (apr_isupper(*dst)) {
- *dst = apr_tolower(*dst);
- }
- else if (*dst == '/' || *dst == '\\') {
- goto bad;
- }
- }
- /* strip trailing gubbins */
- if (dst > host && dst[-1] == '.') {
- dst[-1] = '\0';
- }
+ host = apr_pstrdup(r->pool, r->hostname);
+ if (ap_strchr(host, ':') != NULL)
+ rv = fix_hostname_v6_literal(r, host);
+ else
+ rv = fix_hostname_non_v6(r, host);
}
+ if (rv != APR_SUCCESS)
+ goto bad;
r->hostname = host;
return;
@@ -973,12 +1031,20 @@ static void check_serverpath(request_rec *r)
AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
{
- /* must set this for HTTP/1.1 support */
- if (r->hostname || (r->hostname = apr_table_get(r->headers_in, "Host"))) {
- fix_hostname(r);
- if (r->status != HTTP_OK)
- return;
+ const char *host_header;
+
+ if (r->hostname) {
+ /*
+ * If there was a host part in the Request-URI, ignore the 'Host'
+ * header.
+ */
+ fix_hostname(r, NULL);
+ }
+ else if ((host_header = apr_table_get(r->headers_in, "Host")) != NULL ) {
+ fix_hostname(r, host_header);
}
+ if (r->status != HTTP_OK)
+ return;
/* check if we tucked away a name_chain */
if (r->connection->vhost_lookup_data) {
if (r->hostname)