Skip to content

Commit

Permalink
idna: *labelIter.next() to skip last label only if it is empty and ve…
Browse files Browse the repository at this point in the history
…rifyDNSLength is false.

The existing implementation always skips the empty last label regardless of what verifyDNSLength is set to, which causes pure-ASCII domains ending with a single empty root label to be wrongly accepted when verifyDNSLength is true.

This behavior is described in the Unicode Technical Standard 46 at https://unicode.org/reports/tr46/\#ToASCII

Fixes golang/go#47182
  • Loading branch information
elliotwutingfeng committed Jun 15, 2023
1 parent 2df65d7 commit f99f2cf
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 18 deletions.
18 changes: 10 additions & 8 deletions internal/export/idna/idna10.0.0.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
if err == nil && p.verifyDNSLength && s == "" {
err = &labelError{s, "A4"}
}
labels := labelIter{orig: s}
labels := labelIter{orig: s, verifyDNSLength: p.verifyDNSLength}
for ; !labels.done(); labels.next() {
label := labels.label()
if label == "" {
Expand Down Expand Up @@ -538,11 +538,12 @@ func validateAndMap(p *Profile, s string) (vm string, bidi bool, err error) {

// A labelIter allows iterating over domain name labels.
type labelIter struct {
orig string
slice []string
curStart int
curEnd int
i int
orig string
slice []string
curStart int
curEnd int
i int
verifyDNSLength bool
}

func (l *labelIter) reset() {
Expand Down Expand Up @@ -574,7 +575,8 @@ func (l *labelIter) label() string {
return l.orig[l.curStart:l.curEnd]
}

// next sets the value to the next label. It skips the last label if it is empty.
// next sets the value to the next label. It skips the last label if it is empty
// and l.verifyDNSLength is false.
func (l *labelIter) next() {
l.i++
if l.slice != nil {
Expand All @@ -583,7 +585,7 @@ func (l *labelIter) next() {
}
} else {
l.curStart = l.curEnd + 1
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
if !l.verifyDNSLength && l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
l.curStart = len(l.orig)
}
}
Expand Down
4 changes: 3 additions & 1 deletion internal/export/idna/idna10.0.0_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ func TestLabelErrors(t *testing.T) {
{lengthA, ".b", ".b", "A4"},
{lengthA, "\u3002b", ".b", "A4"},
{lengthA, "..b", "..b", "A4"},
{lengthA, "b..", "b..", ""},
{lengthA, "b..", "b..", "A4"},
{lengthA, "ƀ..", "xn--lha..", "A4"},

// Sharpened Bidi rules for Unicode 10.0.0. Apply for ALL labels in ANY
// of the labels is RTL.
Expand All @@ -81,6 +82,7 @@ func TestLabelErrors(t *testing.T) {
{resolve, "\u3002b", ".b", ""},
{resolve, "..b", "..b", ""},
{resolve, "b..", "b..", ""},
{resolve, "ƀ..", "xn--lha..", ""},
{resolve, "\xed", "", "P1"},

// Raw punycode
Expand Down
18 changes: 10 additions & 8 deletions internal/export/idna/idna9.0.0.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
if err == nil && p.verifyDNSLength && s == "" {
err = &labelError{s, "A4"}
}
labels := labelIter{orig: s}
labels := labelIter{orig: s, verifyDNSLength: p.verifyDNSLength}
for ; !labels.done(); labels.next() {
label := labels.label()
if label == "" {
Expand Down Expand Up @@ -500,11 +500,12 @@ func validateAndMap(p *Profile, s string) (string, error) {

// A labelIter allows iterating over domain name labels.
type labelIter struct {
orig string
slice []string
curStart int
curEnd int
i int
orig string
slice []string
curStart int
curEnd int
i int
verifyDNSLength bool
}

func (l *labelIter) reset() {
Expand Down Expand Up @@ -536,7 +537,8 @@ func (l *labelIter) label() string {
return l.orig[l.curStart:l.curEnd]
}

// next sets the value to the next label. It skips the last label if it is empty.
// next sets the value to the next label. It skips the last label if it is empty
// and l.verifyDNSLength is false.
func (l *labelIter) next() {
l.i++
if l.slice != nil {
Expand All @@ -545,7 +547,7 @@ func (l *labelIter) next() {
}
} else {
l.curStart = l.curEnd + 1
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
if !l.verifyDNSLength && l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
l.curStart = len(l.orig)
}
}
Expand Down
4 changes: 3 additions & 1 deletion internal/export/idna/idna9.0.0_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ func TestLabelErrors(t *testing.T) {
{lengthA, ".b", "b", ""},
{lengthA, "\u3002b", "b", ""},
{lengthA, "..b", "b", ""},
{lengthA, "b..", "b..", ""},
{lengthA, "b..", "b..", "A4"},
{lengthA, "ƀ..", "xn--lha..", "A4"},

{resolve, "a..b", "a..b", ""},
{resolve, ".b", "b", ""},
{resolve, "\u3002b", "b", ""},
{resolve, "..b", "b", ""},
{resolve, "b..", "b..", ""},
{resolve, "ƀ..", "xn--lha..", ""},
{resolve, "\xed", "", "P1"},

// Raw punycode
Expand Down

0 comments on commit f99f2cf

Please sign in to comment.