From fc22331ccd015cfdbae73abe72cb6f3e8e2415b9 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 5 Aug 2015 21:22:10 -0400 Subject: [PATCH] net/url: restrict :port checking to [ipv6]:port form Go 1.4 and earlier accepted mysql://x@y(z:123)/foo and I don't see any compelling reason to break that. The CL during Go 1.5 that broke this syntax was trying to fix #11208 and was probably too aggressive. I added a test case for #11208 to make sure that stays fixed. Relaxing the check did not re-break #11208 nor did it cause any existing test to fail. I added a test for the mysql://x@y(z:123)/foo syntax being preserved. Fixes #12023. Change-Id: I659d39f18c85111697732ad24b757169d69284fc Reviewed-on: https://go-review.googlesource.com/13253 Reviewed-by: Andrew Gerrand Reviewed-by: Mikio Hara --- src/net/url/url.go | 14 +++++--------- src/net/url/url_test.go | 18 +++++++++++------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/net/url/url.go b/src/net/url/url.go index abcd23bb76f29d..1cec43b899c4bf 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -478,7 +478,6 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) { // information. That is, as host[:port]. func parseHost(host string) (string, error) { litOrName := host - var colonPort string // ":80" or "" if strings.HasPrefix(host, "[") { // Parse an IP-Literal in RFC 3986 and RFC 6874. // E.g., "[fe80::1], "[fe80::1%25en0]" @@ -490,7 +489,10 @@ func parseHost(host string) (string, error) { if i < 0 { return "", errors.New("missing ']' in host") } - colonPort = host[i+1:] + colonPort := host[i+1:] + if !validOptionalPort(colonPort) { + return "", fmt.Errorf("invalid port %q after host", colonPort) + } // Parse a host subcomponent without a ZoneID in RFC // 6874 because the ZoneID is allowed to use the // percent encoded form. @@ -500,11 +502,8 @@ func parseHost(host string) (string, error) { } else { litOrName = host[1:j] } - } else { - if i := strings.Index(host, ":"); i != -1 { - colonPort = host[i:] - } } + // A URI containing an IP-Literal without a ZoneID or // IPv4address in RFC 3986 and RFC 6847 must not be // percent-encoded. @@ -517,9 +516,6 @@ func parseHost(host string) (string, error) { if strings.Contains(litOrName, "%") { return "", errors.New("percent-encoded characters in host") } - if !validOptionalPort(colonPort) { - return "", fmt.Errorf("invalid port %q after host", colonPort) - } var err error if host, err = unescape(host, encodeHost); err != nil { return "", err diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index dbdba67834e2f3..2db2d72e7cdcda 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -1081,13 +1081,17 @@ func TestParseAuthority(t *testing.T) { {"http://[::1]/", false}, {"http://[::1]a", true}, {"http://[::1]%23", true}, - {"http://[::1%25en0]", false}, // valid zone id - {"http://[::1]:", true}, // colon, but no port - {"http://[::1]:%38%30", true}, // no hex in port - {"http://[::1%25%10]", false}, // TODO: reject the %10 after the valid zone %25 separator? - {"http://[%10::1]", true}, // no %xx escapes in IP address - {"http://[::1]/%48", false}, // %xx in path is fine - {"http://%41:8080/", true}, // TODO: arguably we should accept reg-name with %xx + {"http://[::1%25en0]", false}, // valid zone id + {"http://[::1]:", true}, // colon, but no port + {"http://[::1]:%38%30", true}, // no hex in port + {"http://[::1%25%10]", false}, // TODO: reject the %10 after the valid zone %25 separator? + {"http://[%10::1]", true}, // no %xx escapes in IP address + {"http://[::1]/%48", false}, // %xx in path is fine + {"http://%41:8080/", true}, // TODO: arguably we should accept reg-name with %xx + {"mysql://x@y(z:123)/foo", false}, // golang.org/issue/12023 + {"mysql://x@y(1.2.3.4:123)/foo", false}, + {"mysql://x@y([2001:db8::1]:123)/foo", false}, + {"http://[]%20%48%54%54%50%2f%31%2e%31%0a%4d%79%48%65%61%64%65%72%3a%20%31%32%33%0a%0a/", true}, // golang.org/issue/11208 } for _, tt := range tests { u, err := Parse(tt.in)