Skip to content

Commit 2b0b97d

Browse files
mateusz834neild
authored andcommitted
dns/dnsmessage: reject packing of 255B rooted names, reject unpacking of 256B (dns encoded) names
Packing a 255B (rooted) name will create an 256B (dns encoded) name, which is an invalid name. Similar with unpacking, we can't unpack 256B (dns encoded) name, because it is too long. Change-Id: I17cc93a93a17a879a2a789629e56ad39999da9ac GitHub-Last-Rev: ddf151a GitHub-Pull-Request: #156 Reviewed-on: https://go-review.googlesource.com/c/net/+/448156 Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Damien Neil <dneil@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Mateusz Poliwczak <mpoliwczak34@gmail.com>
1 parent d28c0b1 commit 2b0b97d

File tree

2 files changed

+62
-22
lines changed

2 files changed

+62
-22
lines changed

dns/dnsmessage/message.go

+12-7
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ var (
263263
errNilResouceBody = errors.New("nil resource body")
264264
errResourceLen = errors.New("insufficient data for resource body length")
265265
errSegTooLong = errors.New("segment length too long")
266+
errNameTooLong = errors.New("name too long")
266267
errZeroSegLen = errors.New("zero length segment")
267268
errResTooLong = errors.New("resource length too long")
268269
errTooManyQuestions = errors.New("too many Questions to pack (>65535)")
@@ -1728,7 +1729,7 @@ const (
17281729
//
17291730
// The provided extRCode must be an extended RCode.
17301731
func (h *ResourceHeader) SetEDNS0(udpPayloadLen int, extRCode RCode, dnssecOK bool) error {
1731-
h.Name = Name{Data: [nameLen]byte{'.'}, Length: 1} // RFC 6891 section 6.1.2
1732+
h.Name = Name{Data: [255]byte{'.'}, Length: 1} // RFC 6891 section 6.1.2
17321733
h.Type = TypeOPT
17331734
h.Class = Class(udpPayloadLen)
17341735
h.TTL = uint32(extRCode) >> 4 << 24
@@ -1888,21 +1889,21 @@ func unpackBytes(msg []byte, off int, field []byte) (int, error) {
18881889
return newOff, nil
18891890
}
18901891

1891-
const nameLen = 255
1892+
const nonEncodedNameMax = 254
18921893

18931894
// A Name is a non-encoded domain name. It is used instead of strings to avoid
18941895
// allocations.
18951896
type Name struct {
1896-
Data [nameLen]byte // 255 bytes
1897+
Data [255]byte
18971898
Length uint8
18981899
}
18991900

19001901
// NewName creates a new Name from a string.
19011902
func NewName(name string) (Name, error) {
1902-
if len(name) > nameLen {
1903+
n := Name{Length: uint8(len(name))}
1904+
if len(name) > len(n.Data) {
19031905
return Name{}, errCalcLen
19041906
}
1905-
n := Name{Length: uint8(len(name))}
19061907
copy(n.Data[:], name)
19071908
return n, nil
19081909
}
@@ -1936,6 +1937,10 @@ func (n *Name) GoString() string {
19361937
func (n *Name) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) {
19371938
oldMsg := msg
19381939

1940+
if n.Length > nonEncodedNameMax {
1941+
return nil, errNameTooLong
1942+
}
1943+
19391944
// Add a trailing dot to canonicalize name.
19401945
if n.Length == 0 || n.Data[n.Length-1] != '.' {
19411946
return oldMsg, errNonCanonicalName
@@ -2057,8 +2062,8 @@ Loop:
20572062
if len(name) == 0 {
20582063
name = append(name, '.')
20592064
}
2060-
if len(name) > len(n.Data) {
2061-
return off, errCalcLen
2065+
if len(name) > nonEncodedNameMax {
2066+
return off, errNameTooLong
20622067
}
20632068
n.Length = uint8(len(name))
20642069
if ptr == 0 {

dns/dnsmessage/message_test.go

+50-15
Original file line numberDiff line numberDiff line change
@@ -212,25 +212,28 @@ func TestName(t *testing.T) {
212212
}
213213

214214
func TestNamePackUnpack(t *testing.T) {
215+
const suffix = ".go.dev."
216+
var longDNSPrefix = strings.Repeat("verylongdomainlabel.", 20)
217+
215218
tests := []struct {
216-
in string
217-
want string
218-
err error
219+
in string
220+
err error
219221
}{
220-
{"", "", errNonCanonicalName},
221-
{".", ".", nil},
222-
{"google..com", "", errNonCanonicalName},
223-
{"google.com", "", errNonCanonicalName},
224-
{"google..com.", "", errZeroSegLen},
225-
{"google.com.", "google.com.", nil},
226-
{".google.com.", "", errZeroSegLen},
227-
{"www..google.com.", "", errZeroSegLen},
228-
{"www.google.com.", "www.google.com.", nil},
222+
{"", errNonCanonicalName},
223+
{".", nil},
224+
{"google..com", errNonCanonicalName},
225+
{"google.com", errNonCanonicalName},
226+
{"google..com.", errZeroSegLen},
227+
{"google.com.", nil},
228+
{".google.com.", errZeroSegLen},
229+
{"www..google.com.", errZeroSegLen},
230+
{"www.google.com.", nil},
231+
{in: longDNSPrefix[:254-len(suffix)] + suffix}, // 254B name, with ending dot.
232+
{in: longDNSPrefix[:255-len(suffix)] + suffix, err: errNameTooLong}, // 255B name, with ending dot.
229233
}
230234

231235
for _, test := range tests {
232236
in := MustNewName(test.in)
233-
want := MustNewName(test.want)
234237
buf, err := in.pack(make([]byte, 0, 30), map[string]int{}, 0)
235238
if err != test.err {
236239
t.Errorf("got %q.pack() = %v, want = %v", test.in, err, test.err)
@@ -253,8 +256,40 @@ func TestNamePackUnpack(t *testing.T) {
253256
len(buf),
254257
)
255258
}
256-
if got != want {
257-
t.Errorf("unpacking packing of %q: got = %#v, want = %#v", test.in, got, want)
259+
if got != in {
260+
t.Errorf("unpacking packing of %q: got = %#v, want = %#v", test.in, got, in)
261+
}
262+
}
263+
}
264+
265+
func TestNameUnpackTooLongName(t *testing.T) {
266+
var suffix = []byte{2, 'g', 'o', 3, 'd', 'e', 'v', 0}
267+
268+
const label = "longdnslabel"
269+
labelBinary := append([]byte{byte(len(label))}, []byte(label)...)
270+
var longDNSPrefix = bytes.Repeat(labelBinary, 18)
271+
longDNSPrefix = longDNSPrefix[:len(longDNSPrefix):len(longDNSPrefix)]
272+
273+
prepName := func(length int) []byte {
274+
missing := length - (len(longDNSPrefix) + len(suffix) + 1)
275+
name := append(longDNSPrefix, byte(missing))
276+
name = append(name, bytes.Repeat([]byte{'a'}, missing)...)
277+
return append(name, suffix...)
278+
}
279+
280+
tests := []struct {
281+
name []byte
282+
err error
283+
}{
284+
{name: prepName(255)},
285+
{name: prepName(256), err: errNameTooLong},
286+
}
287+
288+
for i, test := range tests {
289+
var got Name
290+
_, err := got.unpack(test.name, 0)
291+
if err != test.err {
292+
t.Errorf("%v: %v: expected error: %v, got %v", i, test.name, test.err, err)
258293
}
259294
}
260295
}

0 commit comments

Comments
 (0)