Skip to content

Commit 957255f

Browse files
committed
net/http: don't send IPv6 zone identifier in outbound request, per RFC 6874
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 <[email protected]>
1 parent 92c5736 commit 957255f

File tree

3 files changed

+77
-8
lines changed

3 files changed

+77
-8
lines changed

src/net/http/request.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,12 +361,15 @@ func (r *Request) WriteProxy(w io.Writer) error {
361361

362362
// extraHeaders may be nil
363363
func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) error {
364-
host := req.Host
364+
// According to RFC 6874, an HTTP client, proxy, or other
365+
// intermediary must remove any IPv6 zone identifier attached
366+
// to an outgoing URI.
367+
host := removeZone(req.Host)
365368
if host == "" {
366369
if req.URL == nil {
367370
return errors.New("http: Request.Write on Request with no Host or URL set")
368371
}
369-
host = req.URL.Host
372+
host = removeZone(req.URL.Host)
370373
}
371374

372375
ruri := req.URL.RequestURI()
@@ -453,6 +456,23 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
453456
return nil
454457
}
455458

459+
// removeZone removes IPv6 zone identifer from host.
460+
// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
461+
func removeZone(host string) string {
462+
if !strings.HasPrefix(host, "[") {
463+
return host
464+
}
465+
i := strings.LastIndex(host, "]")
466+
if i < 0 {
467+
return host
468+
}
469+
j := strings.LastIndex(host[:i], "%")
470+
if j < 0 {
471+
return host
472+
}
473+
return host[:j] + host[i:]
474+
}
475+
456476
// ParseHTTPVersion parses a HTTP version string.
457477
// "HTTP/1.0" returns (1, 0, true).
458478
func ParseHTTPVersion(vers string) (major, minor int, ok bool) {

src/net/http/request_test.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -326,13 +326,31 @@ func TestReadRequestErrors(t *testing.T) {
326326
}
327327
}
328328

329+
var newRequestHostTests = []struct {
330+
in, out string
331+
}{
332+
{"http://www.example.com/", "www.example.com"},
333+
{"http://www.example.com:8080/", "www.example.com:8080"},
334+
335+
{"http://192.168.0.1/", "192.168.0.1"},
336+
{"http://192.168.0.1:8080/", "192.168.0.1:8080"},
337+
338+
{"http://[fe80::1]/", "[fe80::1]"},
339+
{"http://[fe80::1]:8080/", "[fe80::1]:8080"},
340+
{"http://[fe80::1%25en0]/", "[fe80::1%en0]"},
341+
{"http://[fe80::1%25en0]:8080/", "[fe80::1%en0]:8080"},
342+
}
343+
329344
func TestNewRequestHost(t *testing.T) {
330-
req, err := NewRequest("GET", "http://localhost:1234/", nil)
331-
if err != nil {
332-
t.Fatal(err)
333-
}
334-
if req.Host != "localhost:1234" {
335-
t.Errorf("Host = %q; want localhost:1234", req.Host)
345+
for i, tt := range newRequestHostTests {
346+
req, err := NewRequest("GET", tt.in, nil)
347+
if err != nil {
348+
t.Errorf("#%v: %v", i, err)
349+
continue
350+
}
351+
if req.Host != tt.out {
352+
t.Errorf("got %q; want %q", req.Host, tt.out)
353+
}
336354
}
337355
}
338356

src/net/http/requestwrite_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,37 @@ var reqWriteTests = []reqWriteTest{
455455
"ALL-CAPS: x\r\n" +
456456
"\r\n",
457457
},
458+
459+
// Request with host header field; IPv6 address with zone identifier
460+
{
461+
Req: Request{
462+
Method: "GET",
463+
URL: &url.URL{
464+
Host: "[fe80::1%en0]",
465+
},
466+
},
467+
468+
WantWrite: "GET / HTTP/1.1\r\n" +
469+
"Host: [fe80::1]\r\n" +
470+
"User-Agent: Go 1.1 package http\r\n" +
471+
"\r\n",
472+
},
473+
474+
// Request with optional host header field; IPv6 address with zone identifier
475+
{
476+
Req: Request{
477+
Method: "GET",
478+
URL: &url.URL{
479+
Host: "www.example.com",
480+
},
481+
Host: "[fe80::1%en0]:8080",
482+
},
483+
484+
WantWrite: "GET / HTTP/1.1\r\n" +
485+
"Host: [fe80::1]:8080\r\n" +
486+
"User-Agent: Go 1.1 package http\r\n" +
487+
"\r\n",
488+
},
458489
}
459490

460491
func TestRequestWrite(t *testing.T) {

0 commit comments

Comments
 (0)