mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
net: bring domain name length checks into RFC compliance
The 255-octet limit applies to wire format, not presentation format. Fixes #17549 Change-Id: I2b5181c53fba32fea60178e0d8df9114aa992b55 Reviewed-on: https://go-review.googlesource.com/31722 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
add721ef91
commit
9a5bddd7ed
4 changed files with 90 additions and 21 deletions
|
|
@ -113,12 +113,20 @@ func equalASCIILabel(x, y string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isDomainName checks if a string is a presentation-format domain name
|
||||||
|
// (currently restricted to hostname-compatible "preferred name" LDH labels and
|
||||||
|
// SRV-like "underscore labels"; see golang.org/issue/12421).
|
||||||
func isDomainName(s string) bool {
|
func isDomainName(s string) bool {
|
||||||
// See RFC 1035, RFC 3696.
|
// See RFC 1035, RFC 3696.
|
||||||
if len(s) == 0 {
|
// Presentation format has dots before every label except the first, and the
|
||||||
return false
|
// terminal empty label is optional here because we assume fully-qualified
|
||||||
}
|
// (absolute) input. We must therefore reserve space for the first and last
|
||||||
if len(s) > 255 {
|
// labels' length octets in wire format, where they are necessary and the
|
||||||
|
// maximum total length is 255.
|
||||||
|
// So our _effective_ maximum is 253, but 254 is not rejected if the last
|
||||||
|
// character is a dot.
|
||||||
|
l := len(s)
|
||||||
|
if l == 0 || l > 254 || l == 254 && s[l-1] != '.' {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -362,14 +362,21 @@ func (conf *dnsConfig) nameList(name string) []string {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check name length (see isDomainName).
|
||||||
|
l := len(name)
|
||||||
|
rooted := l > 0 && name[l-1] == '.'
|
||||||
|
if l > 254 || l == 254 && rooted {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// If name is rooted (trailing dot), try only that name.
|
// If name is rooted (trailing dot), try only that name.
|
||||||
rooted := len(name) > 0 && name[len(name)-1] == '.'
|
|
||||||
if rooted {
|
if rooted {
|
||||||
return []string{name}
|
return []string{name}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasNdots := count(name, '.') >= conf.ndots
|
hasNdots := count(name, '.') >= conf.ndots
|
||||||
name += "."
|
name += "."
|
||||||
|
l++
|
||||||
|
|
||||||
// Build list of search choices.
|
// Build list of search choices.
|
||||||
names := make([]string, 0, 1+len(conf.search))
|
names := make([]string, 0, 1+len(conf.search))
|
||||||
|
|
@ -377,9 +384,11 @@ func (conf *dnsConfig) nameList(name string) []string {
|
||||||
if hasNdots {
|
if hasNdots {
|
||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
// Try suffixes.
|
// Try suffixes that are not too long (see isDomainName).
|
||||||
for _, suffix := range conf.search {
|
for _, suffix := range conf.search {
|
||||||
names = append(names, name+suffix)
|
if l+len(suffix) <= 254 {
|
||||||
|
names = append(names, name+suffix)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Try unsuffixed, if not tried first above.
|
// Try unsuffixed, if not tried first above.
|
||||||
if !hasNdots {
|
if !hasNdots {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -184,3 +185,55 @@ func TestDNSDefaultSearch(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDNSNameLength(t *testing.T) {
|
||||||
|
origGetHostname := getHostname
|
||||||
|
defer func() { getHostname = origGetHostname }()
|
||||||
|
getHostname = func() (string, error) { return "host.domain.local", nil }
|
||||||
|
|
||||||
|
var char63 = ""
|
||||||
|
for i := 0; i < 63; i++ {
|
||||||
|
char63 += "a"
|
||||||
|
}
|
||||||
|
longDomain := strings.Repeat(char63+".", 5) + "example"
|
||||||
|
|
||||||
|
for _, tt := range dnsReadConfigTests {
|
||||||
|
conf := dnsReadConfig(tt.name)
|
||||||
|
if conf.err != nil {
|
||||||
|
t.Fatal(conf.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var shortestSuffix int
|
||||||
|
for _, suffix := range tt.want.search {
|
||||||
|
if shortestSuffix == 0 || len(suffix) < shortestSuffix {
|
||||||
|
shortestSuffix = len(suffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a name that will be maximally long when prefixing the shortest
|
||||||
|
// suffix (accounting for the intervening dot).
|
||||||
|
longName := longDomain[len(longDomain)-254+1+shortestSuffix:]
|
||||||
|
if longName[0] == '.' || longName[1] == '.' {
|
||||||
|
longName = "aa." + longName[3:]
|
||||||
|
}
|
||||||
|
for _, fqdn := range conf.nameList(longName) {
|
||||||
|
if len(fqdn) > 254 {
|
||||||
|
t.Errorf("got %d; want less than or equal to 254", len(fqdn))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now test a name that's too long for suffixing.
|
||||||
|
unsuffixable := "a." + longName[1:]
|
||||||
|
unsuffixableResults := conf.nameList(unsuffixable)
|
||||||
|
if len(unsuffixableResults) != 1 {
|
||||||
|
t.Errorf("suffixed names %v; want []", unsuffixableResults[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now test a name that's too long for DNS.
|
||||||
|
tooLong := "a." + longDomain
|
||||||
|
tooLongResults := conf.nameList(tooLong)
|
||||||
|
if tooLongResults != nil {
|
||||||
|
t.Errorf("suffixed names %v; want nil", tooLongResults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,14 +32,12 @@ var dnsNameTests = []dnsNameTest{
|
||||||
|
|
||||||
func emitDNSNameTest(ch chan<- dnsNameTest) {
|
func emitDNSNameTest(ch chan<- dnsNameTest) {
|
||||||
defer close(ch)
|
defer close(ch)
|
||||||
var char59 = ""
|
|
||||||
var char63 = ""
|
var char63 = ""
|
||||||
var char64 = ""
|
for i := 0; i < 63; i++ {
|
||||||
for i := 0; i < 59; i++ {
|
char63 += "a"
|
||||||
char59 += "a"
|
|
||||||
}
|
}
|
||||||
char63 = char59 + "aaaa"
|
char64 := char63 + "a"
|
||||||
char64 = char63 + "a"
|
longDomain := strings.Repeat(char63+".", 5) + "example"
|
||||||
|
|
||||||
for _, tc := range dnsNameTests {
|
for _, tc := range dnsNameTests {
|
||||||
ch <- tc
|
ch <- tc
|
||||||
|
|
@ -47,14 +45,15 @@ func emitDNSNameTest(ch chan<- dnsNameTest) {
|
||||||
|
|
||||||
ch <- dnsNameTest{char63 + ".com", true}
|
ch <- dnsNameTest{char63 + ".com", true}
|
||||||
ch <- dnsNameTest{char64 + ".com", false}
|
ch <- dnsNameTest{char64 + ".com", false}
|
||||||
// 255 char name is fine:
|
|
||||||
ch <- dnsNameTest{char59 + "." + char63 + "." + char63 + "." +
|
// Remember: wire format is two octets longer than presentation
|
||||||
char63 + ".com",
|
// (length octets for the first and [root] last labels).
|
||||||
true}
|
// 253 is fine:
|
||||||
// 256 char name is bad:
|
ch <- dnsNameTest{longDomain[len(longDomain)-253:], true}
|
||||||
ch <- dnsNameTest{char59 + "a." + char63 + "." + char63 + "." +
|
// A terminal dot doesn't contribute to length:
|
||||||
char63 + ".com",
|
ch <- dnsNameTest{longDomain[len(longDomain)-253:] + ".", true}
|
||||||
false}
|
// 254 is bad:
|
||||||
|
ch <- dnsNameTest{longDomain[len(longDomain)-254:], false}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDNSName(t *testing.T) {
|
func TestDNSName(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue