]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: don't send IPv6 zone identifier in outbound request, per RFC 6874
authorMikio Hara <mikioh.mikioh@gmail.com>
Wed, 21 Jan 2015 10:27:34 +0000 (19:27 +0900)
committerMikio Hara <mikioh.mikioh@gmail.com>
Thu, 9 Apr 2015 09:26:52 +0000 (09:26 +0000)
When making a request to an IPv6 address with a zone identifier, for
exmaple [fe80::1%en0], RFC 6874 says HTTP clients must remove the zone
identifier "%en0" before writing the request for security reason.

This change removes any IPv6 zone identifer attached to URI in the Host
header field in requests.

Fixes #9544.

Change-Id: I7406bd0aa961d260d96f1f887c2e45854e921452
Reviewed-on: https://go-review.googlesource.com/3111
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/net/http/request.go
src/net/http/request_test.go
src/net/http/requestwrite_test.go

index 639a579bdf56c7321e870a7f97bba33353b1b2e3..a4e515c7901bca14299be55d25b3f57b2e48ed58 100644 (file)
@@ -361,12 +361,15 @@ func (r *Request) WriteProxy(w io.Writer) error {
 
 // extraHeaders may be nil
 func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) error {
-       host := req.Host
+       // According to RFC 6874, an HTTP client, proxy, or other
+       // intermediary must remove any IPv6 zone identifier attached
+       // to an outgoing URI.
+       host := removeZone(req.Host)
        if host == "" {
                if req.URL == nil {
                        return errors.New("http: Request.Write on Request with no Host or URL set")
                }
-               host = req.URL.Host
+               host = removeZone(req.URL.Host)
        }
 
        ruri := req.URL.RequestURI()
@@ -453,6 +456,23 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
        return nil
 }
 
+// removeZone removes IPv6 zone identifer from host.
+// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
+func removeZone(host string) string {
+       if !strings.HasPrefix(host, "[") {
+               return host
+       }
+       i := strings.LastIndex(host, "]")
+       if i < 0 {
+               return host
+       }
+       j := strings.LastIndex(host[:i], "%")
+       if j < 0 {
+               return host
+       }
+       return host[:j] + host[i:]
+}
+
 // ParseHTTPVersion parses a HTTP version string.
 // "HTTP/1.0" returns (1, 0, true).
 func ParseHTTPVersion(vers string) (major, minor int, ok bool) {
index 671841ff70d1905bc265f452043d8badc98d6aac..9228d50ef74e7e39f0f87aa5d30f6acc2da2ecad 100644 (file)
@@ -326,13 +326,31 @@ func TestReadRequestErrors(t *testing.T) {
        }
 }
 
+var newRequestHostTests = []struct {
+       in, out string
+}{
+       {"http://www.example.com/", "www.example.com"},
+       {"http://www.example.com:8080/", "www.example.com:8080"},
+
+       {"http://192.168.0.1/", "192.168.0.1"},
+       {"http://192.168.0.1:8080/", "192.168.0.1:8080"},
+
+       {"http://[fe80::1]/", "[fe80::1]"},
+       {"http://[fe80::1]:8080/", "[fe80::1]:8080"},
+       {"http://[fe80::1%25en0]/", "[fe80::1%en0]"},
+       {"http://[fe80::1%25en0]:8080/", "[fe80::1%en0]:8080"},
+}
+
 func TestNewRequestHost(t *testing.T) {
-       req, err := NewRequest("GET", "http://localhost:1234/", nil)
-       if err != nil {
-               t.Fatal(err)
-       }
-       if req.Host != "localhost:1234" {
-               t.Errorf("Host = %q; want localhost:1234", req.Host)
+       for i, tt := range newRequestHostTests {
+               req, err := NewRequest("GET", tt.in, nil)
+               if err != nil {
+                       t.Errorf("#%v: %v", i, err)
+                       continue
+               }
+               if req.Host != tt.out {
+                       t.Errorf("got %q; want %q", req.Host, tt.out)
+               }
        }
 }
 
index 7a6bd5878636ded01b80e3fcbd66d257a4590b40..e9a5f5f08020ca0d35035a994402efb6c3401a63 100644 (file)
@@ -455,6 +455,37 @@ var reqWriteTests = []reqWriteTest{
                        "ALL-CAPS: x\r\n" +
                        "\r\n",
        },
+
+       // Request with host header field; IPv6 address with zone identifier
+       {
+               Req: Request{
+                       Method: "GET",
+                       URL: &url.URL{
+                               Host: "[fe80::1%en0]",
+                       },
+               },
+
+               WantWrite: "GET / HTTP/1.1\r\n" +
+                       "Host: [fe80::1]\r\n" +
+                       "User-Agent: Go 1.1 package http\r\n" +
+                       "\r\n",
+       },
+
+       // Request with optional host header field; IPv6 address with zone identifier
+       {
+               Req: Request{
+                       Method: "GET",
+                       URL: &url.URL{
+                               Host: "www.example.com",
+                       },
+                       Host: "[fe80::1%en0]:8080",
+               },
+
+               WantWrite: "GET / HTTP/1.1\r\n" +
+                       "Host: [fe80::1]:8080\r\n" +
+                       "User-Agent: Go 1.1 package http\r\n" +
+                       "\r\n",
+       },
 }
 
 func TestRequestWrite(t *testing.T) {