Skip to content

Commit f99f2cf

Browse files
idna: *labelIter.next() to skip last label only if it is empty and verifyDNSLength 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
1 parent 2df65d7 commit f99f2cf

File tree

4 files changed

+26
-18
lines changed

4 files changed

+26
-18
lines changed

internal/export/idna/idna10.0.0.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
351351
if err == nil && p.verifyDNSLength && s == "" {
352352
err = &labelError{s, "A4"}
353353
}
354-
labels := labelIter{orig: s}
354+
labels := labelIter{orig: s, verifyDNSLength: p.verifyDNSLength}
355355
for ; !labels.done(); labels.next() {
356356
label := labels.label()
357357
if label == "" {
@@ -538,11 +538,12 @@ func validateAndMap(p *Profile, s string) (vm string, bidi bool, err error) {
538538

539539
// A labelIter allows iterating over domain name labels.
540540
type labelIter struct {
541-
orig string
542-
slice []string
543-
curStart int
544-
curEnd int
545-
i int
541+
orig string
542+
slice []string
543+
curStart int
544+
curEnd int
545+
i int
546+
verifyDNSLength bool
546547
}
547548

548549
func (l *labelIter) reset() {
@@ -574,7 +575,8 @@ func (l *labelIter) label() string {
574575
return l.orig[l.curStart:l.curEnd]
575576
}
576577

577-
// next sets the value to the next label. It skips the last label if it is empty.
578+
// next sets the value to the next label. It skips the last label if it is empty
579+
// and l.verifyDNSLength is false.
578580
func (l *labelIter) next() {
579581
l.i++
580582
if l.slice != nil {
@@ -583,7 +585,7 @@ func (l *labelIter) next() {
583585
}
584586
} else {
585587
l.curStart = l.curEnd + 1
586-
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
588+
if !l.verifyDNSLength && l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
587589
l.curStart = len(l.orig)
588590
}
589591
}

internal/export/idna/idna10.0.0_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ func TestLabelErrors(t *testing.T) {
6767
{lengthA, ".b", ".b", "A4"},
6868
{lengthA, "\u3002b", ".b", "A4"},
6969
{lengthA, "..b", "..b", "A4"},
70-
{lengthA, "b..", "b..", ""},
70+
{lengthA, "b..", "b..", "A4"},
71+
{lengthA, "ƀ..", "xn--lha..", "A4"},
7172

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

8688
// Raw punycode

internal/export/idna/idna9.0.0.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
351351
if err == nil && p.verifyDNSLength && s == "" {
352352
err = &labelError{s, "A4"}
353353
}
354-
labels := labelIter{orig: s}
354+
labels := labelIter{orig: s, verifyDNSLength: p.verifyDNSLength}
355355
for ; !labels.done(); labels.next() {
356356
label := labels.label()
357357
if label == "" {
@@ -500,11 +500,12 @@ func validateAndMap(p *Profile, s string) (string, error) {
500500

501501
// A labelIter allows iterating over domain name labels.
502502
type labelIter struct {
503-
orig string
504-
slice []string
505-
curStart int
506-
curEnd int
507-
i int
503+
orig string
504+
slice []string
505+
curStart int
506+
curEnd int
507+
i int
508+
verifyDNSLength bool
508509
}
509510

510511
func (l *labelIter) reset() {
@@ -536,7 +537,8 @@ func (l *labelIter) label() string {
536537
return l.orig[l.curStart:l.curEnd]
537538
}
538539

539-
// next sets the value to the next label. It skips the last label if it is empty.
540+
// next sets the value to the next label. It skips the last label if it is empty
541+
// and l.verifyDNSLength is false.
540542
func (l *labelIter) next() {
541543
l.i++
542544
if l.slice != nil {
@@ -545,7 +547,7 @@ func (l *labelIter) next() {
545547
}
546548
} else {
547549
l.curStart = l.curEnd + 1
548-
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
550+
if !l.verifyDNSLength && l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
549551
l.curStart = len(l.orig)
550552
}
551553
}

internal/export/idna/idna9.0.0_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,15 @@ func TestLabelErrors(t *testing.T) {
7171
{lengthA, ".b", "b", ""},
7272
{lengthA, "\u3002b", "b", ""},
7373
{lengthA, "..b", "b", ""},
74-
{lengthA, "b..", "b..", ""},
74+
{lengthA, "b..", "b..", "A4"},
75+
{lengthA, "ƀ..", "xn--lha..", "A4"},
7576

7677
{resolve, "a..b", "a..b", ""},
7778
{resolve, ".b", "b", ""},
7879
{resolve, "\u3002b", "b", ""},
7980
{resolve, "..b", "b", ""},
8081
{resolve, "b..", "b..", ""},
82+
{resolve, "ƀ..", "xn--lha..", ""},
8183
{resolve, "\xed", "", "P1"},
8284

8385
// Raw punycode

0 commit comments

Comments
 (0)