2017-03-30 06:43:02 +11:00
|
|
|
package dns
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
Fix dominikh/go-tools nits (#758)
* Remove unused functions and consts
* Address gosimple nits
* Address staticcheck nits
This excludes several that were intentional or weren't actual errors.
* Reduce size of lex struct
This reduces the size of the lex struct by 8 bytes from:
lex.token string: 0-16 (size 16, align 8)
lex.tokenUpper string: 16-32 (size 16, align 8)
lex.length int: 32-40 (size 8, align 8)
lex.err bool: 40-41 (size 1, align 1)
lex.value uint8: 41-42 (size 1, align 1)
padding: 42-48 (size 6, align 0)
lex.line int: 48-56 (size 8, align 8)
lex.column int: 56-64 (size 8, align 8)
lex.torc uint16: 64-66 (size 2, align 2)
padding: 66-72 (size 6, align 0)
lex.comment string: 72-88 (size 16, align 8)
to:
lex.token string: 0-16 (size 16, align 8)
lex.tokenUpper string: 16-32 (size 16, align 8)
lex.length int: 32-40 (size 8, align 8)
lex.err bool: 40-41 (size 1, align 1)
lex.value uint8: 41-42 (size 1, align 1)
lex.torc uint16: 42-44 (size 2, align 2)
padding: 44-48 (size 4, align 0)
lex.line int: 48-56 (size 8, align 8)
lex.column int: 56-64 (size 8, align 8)
lex.comment string: 64-80 (size 16, align 8)
* Reduce size of response struct
This reduces the size of the response struct by 8 bytes from:
response.msg []byte: 0-24 (size 24, align 8)
response.hijacked bool: 24-25 (size 1, align 1)
padding: 25-32 (size 7, align 0)
response.tsigStatus error: 32-48 (size 16, align 8)
response.tsigTimersOnly bool: 48-49 (size 1, align 1)
padding: 49-56 (size 7, align 0)
response.tsigRequestMAC string: 56-72 (size 16, align 8)
response.tsigSecret map[string]string: 72-80 (size 8, align 8)
response.udp *net.UDPConn: 80-88 (size 8, align 8)
response.tcp net.Conn: 88-104 (size 16, align 8)
response.udpSession *github.com/tmthrgd/dns.SessionUDP: 104-112 (size 8, align 8)
response.writer github.com/tmthrgd/dns.Writer: 112-128 (size 16, align 8)
response.wg *sync.WaitGroup: 128-136 (size 8, align 8)
to:
response.msg []byte: 0-24 (size 24, align 8)
response.hijacked bool: 24-25 (size 1, align 1)
response.tsigTimersOnly bool: 25-26 (size 1, align 1)
padding: 26-32 (size 6, align 0)
response.tsigStatus error: 32-48 (size 16, align 8)
response.tsigRequestMAC string: 48-64 (size 16, align 8)
response.tsigSecret map[string]string: 64-72 (size 8, align 8)
response.udp *net.UDPConn: 72-80 (size 8, align 8)
response.tcp net.Conn: 80-96 (size 16, align 8)
response.udpSession *github.com/tmthrgd/dns.SessionUDP: 96-104 (size 8, align 8)
response.writer github.com/tmthrgd/dns.Writer: 104-120 (size 16, align 8)
response.wg *sync.WaitGroup: 120-128 (size 8, align 8)
2018-09-27 04:32:05 +10:00
|
|
|
const maxPrintableLabel = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789x"
|
2017-03-30 06:43:02 +11:00
|
|
|
|
|
|
|
var (
|
|
|
|
longDomain = maxPrintableLabel[:53] + strings.TrimSuffix(
|
|
|
|
strings.Join([]string{".", ".", ".", ".", "."}, maxPrintableLabel[:49]), ".")
|
2018-11-30 06:55:51 +11:00
|
|
|
|
2017-03-30 06:43:02 +11:00
|
|
|
reChar = regexp.MustCompile(`.`)
|
|
|
|
i = -1
|
|
|
|
maxUnprintableLabel = reChar.ReplaceAllStringFunc(maxPrintableLabel, func(ch string) string {
|
|
|
|
if i++; i >= 32 {
|
|
|
|
i = 0
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("\\%03d", i)
|
|
|
|
})
|
2018-11-30 06:55:51 +11:00
|
|
|
|
|
|
|
// These are the longest possible domain names in presentation format.
|
|
|
|
longestDomain = maxPrintableLabel[:61] + strings.Join([]string{".", ".", ".", "."}, maxPrintableLabel)
|
|
|
|
longestUnprintableDomain = maxUnprintableLabel[:61*4] + strings.Join([]string{".", ".", ".", "."}, maxUnprintableLabel)
|
2017-03-30 06:43:02 +11:00
|
|
|
)
|
|
|
|
|
2018-05-13 17:36:02 +10:00
|
|
|
func TestPackNoSideEffect(t *testing.T) {
|
|
|
|
m := new(Msg)
|
|
|
|
m.SetQuestion(Fqdn("example.com."), TypeNS)
|
|
|
|
|
|
|
|
a := new(Msg)
|
|
|
|
o := &OPT{
|
|
|
|
Hdr: RR_Header{
|
|
|
|
Name: ".",
|
|
|
|
Rrtype: TypeOPT,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
o.SetUDPSize(DefaultMsgSize)
|
|
|
|
|
|
|
|
a.Extra = append(a.Extra, o)
|
|
|
|
a.SetRcode(m, RcodeBadVers)
|
|
|
|
|
|
|
|
a.Pack()
|
|
|
|
if a.Rcode != RcodeBadVers {
|
|
|
|
t.Errorf("after pack: Rcode is expected to be BADVERS")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-17 11:00:14 +11:00
|
|
|
func TestPackExtendedBadCookie(t *testing.T) {
|
|
|
|
m := new(Msg)
|
|
|
|
m.SetQuestion(Fqdn("example.com."), TypeNS)
|
|
|
|
|
|
|
|
a := new(Msg)
|
|
|
|
a.SetReply(m)
|
|
|
|
o := &OPT{
|
|
|
|
Hdr: RR_Header{
|
|
|
|
Name: ".",
|
|
|
|
Rrtype: TypeOPT,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
o.SetUDPSize(DefaultMsgSize)
|
|
|
|
a.Extra = append(a.Extra, o)
|
|
|
|
|
|
|
|
a.SetRcode(m, RcodeBadCookie)
|
|
|
|
|
|
|
|
edns0 := a.IsEdns0()
|
|
|
|
if edns0 == nil {
|
|
|
|
t.Fatal("Expected OPT RR")
|
|
|
|
}
|
|
|
|
// SetExtendedRcode is only called as part of `Pack()`, hence at this stage,
|
|
|
|
// the OPT RR is not set yet.
|
|
|
|
if edns0.ExtendedRcode() == RcodeBadCookie&0xFFFFFFF0 {
|
|
|
|
t.Errorf("ExtendedRcode is expected to not be BADCOOKIE before Pack")
|
|
|
|
}
|
|
|
|
|
|
|
|
a.Pack()
|
|
|
|
|
|
|
|
edns0 = a.IsEdns0()
|
|
|
|
if edns0 == nil {
|
|
|
|
t.Fatal("Expected OPT RR")
|
|
|
|
}
|
|
|
|
|
|
|
|
if edns0.ExtendedRcode() != RcodeBadCookie&0xFFFFFFF0 {
|
|
|
|
t.Errorf("ExtendedRcode is expected to be BADCOOKIE after Pack")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestUnPackExtendedRcode(t *testing.T) {
|
|
|
|
m := new(Msg)
|
|
|
|
m.SetQuestion(Fqdn("example.com."), TypeNS)
|
|
|
|
|
|
|
|
a := new(Msg)
|
|
|
|
a.SetReply(m)
|
|
|
|
o := &OPT{
|
|
|
|
Hdr: RR_Header{
|
|
|
|
Name: ".",
|
|
|
|
Rrtype: TypeOPT,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
o.SetUDPSize(DefaultMsgSize)
|
|
|
|
a.Extra = append(a.Extra, o)
|
|
|
|
|
|
|
|
a.SetRcode(m, RcodeBadCookie)
|
|
|
|
|
|
|
|
packed, err := a.Pack()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Could not unpack %v", a)
|
|
|
|
}
|
|
|
|
|
|
|
|
unpacked := new(Msg)
|
|
|
|
if err := unpacked.Unpack(packed); err != nil {
|
|
|
|
t.Fatalf("Failed to unpack message")
|
|
|
|
}
|
|
|
|
|
|
|
|
if unpacked.Rcode != RcodeBadCookie {
|
|
|
|
t.Fatalf("Rcode should be matching RcodeBadCookie (%d), got (%d)", RcodeBadCookie, unpacked.Rcode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-30 06:43:02 +11:00
|
|
|
func TestUnpackDomainName(t *testing.T) {
|
|
|
|
var cases = []struct {
|
|
|
|
label string
|
|
|
|
input string
|
|
|
|
expectedOutput string
|
|
|
|
expectedError string
|
|
|
|
}{
|
|
|
|
{"empty domain",
|
|
|
|
"\x00",
|
|
|
|
".",
|
|
|
|
""},
|
|
|
|
{"long label",
|
2020-05-14 20:50:37 +10:00
|
|
|
"?" + maxPrintableLabel + "\x00",
|
2017-03-30 06:43:02 +11:00
|
|
|
maxPrintableLabel + ".",
|
|
|
|
""},
|
|
|
|
{"unprintable label",
|
2020-05-14 20:50:37 +10:00
|
|
|
"?" + regexp.MustCompile(`\\[0-9]+`).ReplaceAllStringFunc(maxUnprintableLabel,
|
2017-03-30 06:43:02 +11:00
|
|
|
func(escape string) string {
|
|
|
|
n, _ := strconv.ParseInt(escape[1:], 10, 8)
|
2020-05-14 20:50:37 +10:00
|
|
|
return string(rune(n))
|
2017-03-30 06:43:02 +11:00
|
|
|
}) + "\x00",
|
|
|
|
maxUnprintableLabel + ".",
|
|
|
|
""},
|
|
|
|
{"long domain",
|
2020-05-14 20:50:37 +10:00
|
|
|
"5" + strings.Replace(longDomain, ".", "1", -1) + "\x00",
|
2017-03-30 06:43:02 +11:00
|
|
|
longDomain + ".",
|
|
|
|
""},
|
|
|
|
{"compression pointer",
|
|
|
|
// an unrealistic but functional test referencing an offset _inside_ a label
|
|
|
|
"\x03foo" + "\x05\x03com\x00" + "\x07example" + "\xC0\x05",
|
|
|
|
"foo.\\003com\\000.example.com.",
|
|
|
|
""},
|
|
|
|
{"too long domain",
|
2020-05-14 20:50:37 +10:00
|
|
|
"6" + "x" + strings.Replace(longDomain, ".", "1", -1) + "\x00",
|
2018-11-29 08:56:30 +11:00
|
|
|
"",
|
2017-03-30 06:43:02 +11:00
|
|
|
ErrLongDomain.Error()},
|
|
|
|
{"too long by pointer",
|
|
|
|
// a matryoshka doll name to get over 255 octets after expansion via internal pointers
|
|
|
|
string([]byte{
|
|
|
|
// 11 length values, first to last
|
|
|
|
40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 0,
|
|
|
|
// 12 filler values
|
|
|
|
120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120,
|
|
|
|
// 10 pointers, last to first
|
|
|
|
192, 10, 192, 9, 192, 8, 192, 7, 192, 6, 192, 5, 192, 4, 192, 3, 192, 2, 192, 1,
|
|
|
|
}),
|
|
|
|
"",
|
|
|
|
ErrLongDomain.Error()},
|
|
|
|
{"long by pointer",
|
|
|
|
// a matryoshka doll name _not_ exceeding 255 octets after expansion
|
|
|
|
string([]byte{
|
|
|
|
// 11 length values, first to last
|
|
|
|
37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 0,
|
|
|
|
// 9 filler values
|
|
|
|
120, 120, 120, 120, 120, 120, 120, 120, 120,
|
|
|
|
// 10 pointers, last to first
|
|
|
|
192, 10, 192, 9, 192, 8, 192, 7, 192, 6, 192, 5, 192, 4, 192, 3, 192, 2, 192, 1,
|
|
|
|
}),
|
|
|
|
"" +
|
|
|
|
(`\"\031\028\025\022\019\016\013\010\000xxxxxxxxx` +
|
|
|
|
`\192\010\192\009\192\008\192\007\192\006\192\005\192\004\192\003\192\002.`) +
|
|
|
|
(`\031\028\025\022\019\016\013\010\000xxxxxxxxx` +
|
|
|
|
`\192\010\192\009\192\008\192\007\192\006\192\005\192\004\192\003.`) +
|
|
|
|
(`\028\025\022\019\016\013\010\000xxxxxxxxx` +
|
|
|
|
`\192\010\192\009\192\008\192\007\192\006\192\005\192\004.`) +
|
|
|
|
(`\025\022\019\016\013\010\000xxxxxxxxx` +
|
|
|
|
`\192\010\192\009\192\008\192\007\192\006\192\005.`) +
|
|
|
|
`\022\019\016\013\010\000xxxxxxxxx\192\010\192\009\192\008\192\007\192\006.` +
|
|
|
|
`\019\016\013\010\000xxxxxxxxx\192\010\192\009\192\008\192\007.` +
|
|
|
|
`\016\013\010\000xxxxxxxxx\192\010\192\009\192\008.` +
|
|
|
|
`\013\010\000xxxxxxxxx\192\010\192\009.` +
|
|
|
|
`\010\000xxxxxxxxx\192\010.` +
|
|
|
|
`\000xxxxxxxxx.`,
|
|
|
|
""},
|
|
|
|
{"truncated name", "\x07example\x03", "", "dns: buffer size too small"},
|
|
|
|
{"non-absolute name", "\x07example\x03com", "", "dns: buffer size too small"},
|
2018-11-29 08:56:30 +11:00
|
|
|
{"compression pointer cycle (too many)", "\xC0\x00", "", "dns: too many compression pointers"},
|
|
|
|
{"compression pointer cycle (too long)",
|
2017-03-30 06:43:02 +11:00
|
|
|
"\x03foo" + "\x03bar" + "\x07example" + "\xC0\x04",
|
|
|
|
"",
|
2018-11-29 08:56:30 +11:00
|
|
|
ErrLongDomain.Error()},
|
2020-05-27 18:05:25 +10:00
|
|
|
{"forward compression pointer", "\x02\xC0\xFF\xC0\x01", "", ErrBuf.Error()},
|
2017-03-30 06:43:02 +11:00
|
|
|
{"reserved compression pointer 0b10", "\x07example\x80", "", "dns: bad rdata"},
|
|
|
|
{"reserved compression pointer 0b01", "\x07example\x40", "", "dns: bad rdata"},
|
|
|
|
}
|
|
|
|
for _, test := range cases {
|
|
|
|
output, idx, err := UnpackDomainName([]byte(test.input), 0)
|
|
|
|
if test.expectedOutput != "" && output != test.expectedOutput {
|
|
|
|
t.Errorf("%s: expected %s, got %s", test.label, test.expectedOutput, output)
|
|
|
|
}
|
|
|
|
if test.expectedError == "" && err != nil {
|
|
|
|
t.Errorf("%s: expected no error, got %d %v", test.label, idx, err)
|
|
|
|
} else if test.expectedError != "" && (err == nil || err.Error() != test.expectedError) {
|
|
|
|
t.Errorf("%s: expected error %s, got %d %v", test.label, test.expectedError, idx, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-04 16:39:45 +10:00
|
|
|
|
2018-11-26 16:23:29 +11:00
|
|
|
func TestPackDomainNameCompressionMap(t *testing.T) {
|
2018-11-29 10:18:02 +11:00
|
|
|
expected := map[string]struct{}{
|
2019-05-20 16:44:53 +10:00
|
|
|
`www\.this.is.\131an.example.org.`: {},
|
|
|
|
`is.\131an.example.org.`: {},
|
|
|
|
`\131an.example.org.`: {},
|
|
|
|
`example.org.`: {},
|
|
|
|
`org.`: {},
|
2018-11-29 10:18:02 +11:00
|
|
|
}
|
2018-11-26 16:23:29 +11:00
|
|
|
|
2018-11-29 10:18:02 +11:00
|
|
|
msg := make([]byte, 256)
|
2018-11-26 16:34:34 +11:00
|
|
|
for _, compress := range []bool{true, false} {
|
|
|
|
compression := make(map[string]int)
|
|
|
|
|
|
|
|
_, err := PackDomainName(`www\.this.is.\131an.example.org.`, msg, 0, compression, compress)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("PackDomainName failed: %v", err)
|
|
|
|
}
|
2018-11-26 16:23:29 +11:00
|
|
|
|
2018-11-29 10:18:02 +11:00
|
|
|
if !compressionMapsEqual(expected, compression) {
|
2018-12-01 19:30:40 +11:00
|
|
|
t.Errorf("expected compression maps to be equal\n%s", compressionMapsDifference(expected, compression))
|
2018-11-26 16:23:29 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-04 16:39:45 +10:00
|
|
|
func TestPackDomainNameNSECTypeBitmap(t *testing.T) {
|
|
|
|
ownername := "some-very-long-ownername.com."
|
|
|
|
msg := &Msg{
|
|
|
|
Compress: true,
|
|
|
|
Answer: []RR{
|
|
|
|
&NS{
|
|
|
|
Hdr: RR_Header{
|
|
|
|
Name: ownername,
|
|
|
|
Rrtype: TypeNS,
|
|
|
|
Class: ClassINET,
|
|
|
|
},
|
|
|
|
Ns: "ns1.server.com.",
|
|
|
|
},
|
|
|
|
&NSEC{
|
|
|
|
Hdr: RR_Header{
|
|
|
|
Name: ownername,
|
|
|
|
Rrtype: TypeNSEC,
|
|
|
|
Class: ClassINET,
|
|
|
|
},
|
|
|
|
NextDomain: "a.com.",
|
|
|
|
TypeBitMap: []uint16{TypeNS, TypeNSEC},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pack msg and then unpack into msg2
|
|
|
|
buf, err := msg.Pack()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("msg.Pack failed: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var msg2 Msg
|
|
|
|
if err := msg2.Unpack(buf); err != nil {
|
|
|
|
t.Fatalf("msg2.Unpack failed: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !IsDuplicate(msg.Answer[1], msg2.Answer[1]) {
|
|
|
|
t.Error("message differs after packing and unpacking")
|
|
|
|
|
|
|
|
// Print NSEC RR for both cases
|
|
|
|
t.Logf("expected: %v", msg.Answer[1])
|
|
|
|
t.Logf("got: %v", msg2.Answer[1])
|
|
|
|
}
|
|
|
|
}
|
2018-11-28 20:23:00 +11:00
|
|
|
|
|
|
|
func TestPackUnpackManyCompressionPointers(t *testing.T) {
|
|
|
|
m := new(Msg)
|
|
|
|
m.Compress = true
|
|
|
|
m.SetQuestion("example.org.", TypeNS)
|
|
|
|
|
|
|
|
for domain := "a."; len(domain) < maxDomainNameWireOctets; domain += "a." {
|
|
|
|
m.Answer = append(m.Answer, &NS{Hdr: RR_Header{Name: domain, Rrtype: TypeNS, Class: ClassINET}, Ns: "example.org."})
|
|
|
|
|
|
|
|
b, err := m.Pack()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Pack failed for %q and %d records with: %v", domain, len(m.Answer), err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var m2 Msg
|
|
|
|
if err := m2.Unpack(b); err != nil {
|
|
|
|
t.Fatalf("Unpack failed for %q and %d records with: %v", domain, len(m.Answer), err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-29 08:53:54 +11:00
|
|
|
|
|
|
|
func TestLenDynamicA(t *testing.T) {
|
|
|
|
for _, rr := range []RR{
|
|
|
|
testRR("example.org. A"),
|
|
|
|
testRR("example.org. AAAA"),
|
|
|
|
testRR("example.org. L32"),
|
|
|
|
} {
|
|
|
|
msg := make([]byte, Len(rr))
|
|
|
|
off, err := PackRR(rr, msg, 0, nil, false)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("PackRR failed for %T: %v", rr, err)
|
|
|
|
}
|
|
|
|
if off != len(msg) {
|
|
|
|
t.Errorf("Len(rr) wrong for %T: Len(rr) = %d, PackRR(rr) = %d", rr, len(msg), off)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|