Skip to content

Commit 55577aa

Browse files
neildgopherbot
authored andcommitted
http2: use IDNA Lookup profile, not raw Punycode translation
For golang/go#81010 Change-Id: I184386da6e62b5cc4108537f297191436a6a6964 Reviewed-on: https://go-review.googlesource.com/c/net/+/819201 Auto-Submit: Damien Neil <dneil@google.com> Reviewed-by: Nicholas Husin <nsh@golang.org> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Nicholas Husin <husin@google.com>
1 parent b22627e commit 55577aa

3 files changed

Lines changed: 77 additions & 3 deletions

File tree

http2/ascii.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
package http2
66

7-
import "strings"
7+
import (
8+
"strings"
9+
)
810

911
// The HTTP protocols are defined in terms of ASCII, not Unicode. This file
1012
// contains helper functions which may use Unicode-aware functions which would
@@ -43,6 +45,16 @@ func isASCIIPrint(s string) bool {
4345
return true
4446
}
4547

48+
// isASCII returns whether s is ASCII.
49+
func isASCII(s string) bool {
50+
for i := 0; i < len(s); i++ {
51+
if s[i] > 0x7f {
52+
return false
53+
}
54+
}
55+
return true
56+
}
57+
4658
// asciiToLower returns the lowercase version of s if s is ASCII and printable,
4759
// and whether or not it was.
4860
func asciiToLower(s string) (lower string, ok bool) {

http2/transport_api_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,61 @@ func TestAPITransportCountError(t *testing.T) {
462462
})
463463
}
464464

465+
func TestAPITransportIDNA(t *testing.T) {
466+
type dialErr struct {
467+
error
468+
address string
469+
}
470+
tr := &http2.Transport{
471+
DialTLSContext: func(ctx context.Context, network, address string, conf *tls.Config) (net.Conn, error) {
472+
host, _, _ := net.SplitHostPort(address)
473+
return nil, dialErr{
474+
error: errors.New("dial error"),
475+
address: host,
476+
}
477+
},
478+
}
479+
for _, test := range []struct {
480+
url string
481+
wantDialAddr string
482+
skip bool
483+
}{{
484+
url: "https://example.tld/",
485+
wantDialAddr: "example.tld",
486+
}, {
487+
// Invalid host, but permitted because all-ASCII.
488+
url: "https://xn-example-.tld/",
489+
wantDialAddr: "xn-example-.tld",
490+
}, {
491+
url: "https://гофер.го/",
492+
wantDialAddr: "xn--c1ae0ajs.xn--c1aw",
493+
}, {
494+
// IDNA translation returns an error, we try using the untranslated string.
495+
url: "https://a⒈com/",
496+
wantDialAddr: "a⒈com",
497+
}, {
498+
// Unicode name converted to the empty string.
499+
url: "https://\u00ad/",
500+
wantDialAddr: "\u00ad",
501+
// When !http2legacy, this relies on a fix added in Go 1.28.
502+
// Just skip the test here when wrapping net/http.
503+
skip: wrappedAPI,
504+
}} {
505+
if test.skip {
506+
continue
507+
}
508+
req, _ := http.NewRequest("GET", test.url, nil)
509+
_, err := tr.RoundTrip(req)
510+
e, ok := err.(dialErr)
511+
if !ok {
512+
t.Fatalf("RoundTrip error %v; want dialErr", err)
513+
}
514+
if got, want := e.address, test.wantDialAddr; got != want {
515+
t.Errorf("RoundTrip for %q dialed %q, want %q", test.url, got, want)
516+
}
517+
}
518+
}
519+
465520
type testClientConnPool struct {
466521
t *testing.T
467522
li *synctestNetListener

http2/transport_common.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,15 @@ func authorityAddr(scheme string, authority string) (addr string) {
316316
port = "80"
317317
}
318318
}
319-
if a, err := idna.ToASCII(host); err == nil {
320-
host = a
319+
// Skip IDNA processing on hosts which are already ASCII.
320+
// This is consistent with net/http and the WHATWG URL Specification.
321+
// (We currently don't follow all of WHATWG, but we're aligned on
322+
// permitting all-ASCII hostnames which fail IDNA validation.
323+
// There are existing, valid domain names which TR #46 processing rejects.)
324+
if !isASCII(host) {
325+
if a, err := idna.Lookup.ToASCII(host); err == nil && a != "" {
326+
host = a
327+
}
321328
}
322329
// IPv6 address literal, without a port:
323330
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {

0 commit comments

Comments
 (0)