From 57dbb4a4a8cf520eb62b0f2df2b485c1f900dbe3 Mon Sep 17 00:00:00 2001 From: David Fifield Date: Tue, 10 Mar 2020 15:19:10 -0600 Subject: [PATCH] MessageFromWireFormat --- dns/dns.go | 315 +++++++++++++++++++++++++++++++++++- dns/dns_test.go | 377 +++++++++++++++++++++++++++++++++++++++++++ dnstt-server/main.go | 9 +- 3 files changed, 697 insertions(+), 4 deletions(-) create mode 100644 dns/dns_test.go diff --git a/dns/dns.go b/dns/dns.go index 88d8a23..dc61920 100644 --- a/dns/dns.go +++ b/dns/dns.go @@ -1,11 +1,322 @@ +// Package dns deals with encoding and decoding DNS wire format. package dns import ( "bytes" + "encoding/binary" + "errors" + "io" ) +// The maximum number of DNS name compression pointers we are willing to follow. +// Without something like this, infinite loops are possible. +const compressionPointerLimit = 10 + +var ( + // ErrZeroLengthLabel is the error returned for names that contain a + // zero-length label, like "example..com". + ErrZeroLengthLabel = errors.New("name contains a zero-length label") + + // ErrLabelTooLong is the error returned for labels that are longer than + // 63 octets. + ErrLabelTooLong = errors.New("name contains a label longer than 63 octets") + + // ErrNameTooLong is the error returned for names whose encoded + // representation is longer than 255 octets. + ErrNameTooLong = errors.New("name is longer than 255 octets") + + // ErrReservedLabelType is the error returned when reading a label type + // prefix whose two most significant bits are not 00 or 11. + ErrReservedLabelType = errors.New("reserved label type") + + // ErrTooManyPointers is the error returned when reading a compressed + // name that has too many compression pointers. + ErrTooManyPointers = errors.New("too many compression pointers") + + // ErrTrailingBytes is the error returned when bytes remain in the parse + // buffer after parsing a message. + ErrTrailingBytes = errors.New("trailing bytes after message") +) + +// Name represents a domain name, a sequence of labels each of which is 63 +// octets or less in length. +// +// https://tools.ietf.org/html/rfc1035#section-3.1 type Name [][]byte -func ParseName(s []byte) (Name, error) { - return bytes.Split(bytes.TrimSuffix(s, []byte(".")), []byte(".")), nil +// NewName returns a Name from a slice of labels, after checking the labels for +// validity. Do not include a zero-length label at the end of the slice. +func NewName(labels [][]byte) (Name, error) { + name := Name(labels) + // https://tools.ietf.org/html/rfc1035#section-2.3.4 + // Various objects and parameters in the DNS have size limits. + // labels 63 octets or less + // names 255 octets or less + totalLen := 1 // "...a domain name is terminated by a length byte of zero." + for _, label := range labels { + if len(label) == 0 { + return nil, ErrZeroLengthLabel + } + if len(label) > 63 { + return nil, ErrLabelTooLong + } + totalLen += 1 + len(label) + if totalLen > 255 { + return nil, ErrNameTooLong + } + } + return name, nil +} + +// ParseName returns a new Name from a string of labels separated by dots, after +// checking the name for validity. A single dot at the end of the string is +// ignored. +func ParseName(s string) (Name, error) { + b := bytes.TrimSuffix([]byte(s), []byte(".")) + if len(b) == 0 { + // bytes.Split(b, ".") would return [""] in this case + return NewName([][]byte{}) + } else { + return NewName(bytes.Split(b, []byte("."))) + } + return NewName(bytes.Split(b, []byte("."))) +} + +// String returns a string representation of name, with labels separated by +// dots. +func (name Name) String() string { + if len(name) == 0 { + return "." + } else { + return string(bytes.Join(name, []byte("."))) + } +} + +// Message represents a DNS message. +// +// https://tools.ietf.org/html/rfc1035#section-4.1 +type Message struct { + ID uint16 + Flags uint16 + + Question []Question + Answer []RR + Authority []RR + Additional []RR +} + +// Question represents the question section of a message. +// +// https://tools.ietf.org/html/rfc1035#section-4.1.2 +type Question struct { + Name Name + Type uint16 + Class uint16 +} + +// RR represents a resource record. +// +// https://tools.ietf.org/html/rfc1035#section-4.1.3 +type RR struct { + Name Name + Type uint16 + Class uint16 + TTL uint32 + Data []byte +} + +func readName(r io.ReadSeeker) (Name, error) { + var labels [][]byte + // We limit the number of compression pointers we are willing to follow. + numPointers := 0 + // If we followed any compression pointers, we must finally seek to just + // past the first pointer. + var seekTo int64 +loop: + for { + var labelType byte + err := binary.Read(r, binary.BigEndian, &labelType) + if err != nil { + return nil, err + } + + switch labelType & 0xc0 { + case 0x00: + // This is an ordinary label. + // https://tools.ietf.org/html/rfc1035#section-3.1 + length := int(labelType & 0x3f) + if length == 0 { + break loop + } + label := make([]byte, length) + _, err := io.ReadFull(r, label) + if err != nil { + return nil, err + } + labels = append(labels, label) + case 0xc0: + // This is a compression pointer. + // https://tools.ietf.org/html/rfc1035#section-4.1.4 + upper := labelType & 0x3f + var lower byte + err := binary.Read(r, binary.BigEndian, &lower) + if err != nil { + return nil, err + } + offset := (uint16(upper) << 8) | uint16(lower) + + if numPointers == 0 { + // The first time we encounter a pointer, + // remember our position so we can seek back to + // it when done. + seekTo, err = r.Seek(0, io.SeekCurrent) + if err != nil { + return nil, err + } + } + numPointers++ + if numPointers > compressionPointerLimit { + return nil, ErrTooManyPointers + } + + // Follow the pointer and continue. + _, err = r.Seek(int64(offset), io.SeekStart) + if err != nil { + return nil, err + } + default: + // "The 10 and 01 combinations are reserved for future + // use." + return nil, ErrReservedLabelType + } + } + // If we followed any pointers, then seek back to just after the first + // one. + if numPointers > 0 { + _, err := r.Seek(seekTo, io.SeekStart) + if err != nil { + return nil, err + } + } + return NewName(labels) +} + +func readQuestion(r io.ReadSeeker) (Question, error) { + var question Question + + // https://tools.ietf.org/html/rfc1035#section-4.1.2 + var err error + question.Name, err = readName(r) + if err != nil { + return question, err + } + for _, ptr := range []*uint16{&question.Type, &question.Class} { + err := binary.Read(r, binary.BigEndian, ptr) + if err != nil { + return question, err + } + } + + return question, nil +} + +func readRR(r io.ReadSeeker) (RR, error) { + var rr RR + + // https://tools.ietf.org/html/rfc1035#section-4.1.3 + var err error + rr.Name, err = readName(r) + if err != nil { + return rr, err + } + for _, ptr := range []*uint16{&rr.Type, &rr.Class} { + err := binary.Read(r, binary.BigEndian, ptr) + if err != nil { + return rr, err + } + } + err = binary.Read(r, binary.BigEndian, &rr.TTL) + if err != nil { + return rr, err + } + var rdLength uint16 + err = binary.Read(r, binary.BigEndian, &rdLength) + if err != nil { + return rr, err + } + rr.Data = make([]byte, rdLength) + _, err = io.ReadFull(r, rr.Data) + if err != nil { + return rr, err + } + + return rr, nil +} + +func readMessage(r io.ReadSeeker) (Message, error) { + var message Message + + // Header section + // https://tools.ietf.org/html/rfc1035#section-4.1.1 + var qdCount, anCount, nsCount, arCount uint16 + for _, ptr := range []*uint16{ + &message.ID, &message.Flags, + &qdCount, &anCount, &nsCount, &arCount, + } { + err := binary.Read(r, binary.BigEndian, ptr) + if err != nil { + return message, err + } + } + + // Question section + // https://tools.ietf.org/html/rfc1035#section-4.1.2 + for i := 0; i < int(qdCount); i++ { + question, err := readQuestion(r) + if err != nil { + return message, err + } + message.Question = append(message.Question, question) + } + + // Answer, Authority, and Additional sections + // https://tools.ietf.org/html/rfc1035#section-4.1.3 + for _, rec := range []struct { + ptr *[]RR + count uint16 + }{ + {&message.Answer, anCount}, + {&message.Authority, nsCount}, + {&message.Additional, arCount}, + } { + for i := 0; i < int(rec.count); i++ { + rr, err := readRR(r) + if err != nil { + return message, err + } + *rec.ptr = append(*rec.ptr, rr) + } + } + + // Check for trailing bytes. + var buf [1]byte + _, err := io.ReadFull(r, buf[:]) + if err == nil { + err = ErrTrailingBytes + } + if err != io.EOF { + return message, err + } + + return message, nil +} + +// MessageFromWireFormat parses a message from a buffer of bytes and returns a +// Message object. +func MessageFromWireFormat(buf []byte) (Message, error) { + message, err := readMessage(bytes.NewReader(buf)) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return message, err } diff --git a/dns/dns_test.go b/dns/dns_test.go new file mode 100644 index 0000000..a10aa2e --- /dev/null +++ b/dns/dns_test.go @@ -0,0 +1,377 @@ +package dns + +import ( + "bytes" + "io" + "strings" + "testing" +) + +func namesEqual(a, b Name) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if !bytes.Equal(a[i], b[i]) { + return false + } + } + return true +} + +func anyLabelContainsDot(labels [][]byte) bool { + for _, label := range labels { + if bytes.Contains(label, []byte(".")) { + return true + } + } + return false +} + +func TestName(t *testing.T) { + for _, test := range []struct { + labels [][]byte + err error + s string + }{ + {[][]byte{}, nil, "."}, + {[][]byte{[]byte("test")}, nil, "test"}, + {[][]byte{[]byte("a"), []byte("b"), []byte("c")}, nil, "a.b.c"}, + + {[][]byte{[]byte{}}, ErrZeroLengthLabel, ""}, + {[][]byte{[]byte("a"), []byte{}, []byte("c")}, ErrZeroLengthLabel, ""}, + + // 63 octets. + {[][]byte{[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE")}, nil, + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"}, + // 64 octets. + {[][]byte{[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF")}, ErrLabelTooLong, ""}, + + // 64+64+64+61 octets. + {[][]byte{ + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"), + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"), + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"), + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABC"), + }, nil, + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE.0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE.0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE.0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABC"}, + // 64+64+64+62 octets. + {[][]byte{ + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"), + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"), + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"), + []byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD"), + }, ErrNameTooLong, ""}, + // 127 one-octet labels. + {[][]byte{ + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, + }, nil, + "0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E"}, + // 128 one-octet labels. + {[][]byte{ + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, + {'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, + }, ErrNameTooLong, ""}, + + // Labels may contain any octets, though ones containing dots + // cannot be losslessly roundtripped through a string. + {[][]byte{[]byte("\x00"), []byte("a.b")}, nil, "\x00.a.b"}, + } { + // Test that NewName returns proper error codes, and otherwise + // returns an equal slice of labels. + name, err := NewName(test.labels) + if err != test.err || (err == nil && !namesEqual(name, test.labels)) { + t.Errorf("%+q returned (%+q, %v), expected (%+q, %v)", + test.labels, name, err, test.labels, test.err) + continue + } + if test.err != nil { + continue + } + + // Test that the string version of the name comes out as + // expected. + s := name.String() + if s != test.s { + t.Errorf("%+q became string %+q, expected %+q", test.labels, s, test.s) + continue + } + + // Test that parsing from a string back to a Name results in the + // original slice of labels. + if !anyLabelContainsDot(test.labels) { + name, err := ParseName(s) + if err != nil || !namesEqual(name, test.labels) { + t.Errorf("%+q parsing %+q returned (%+q, %v), expected (%+q, %v)", + test.labels, s, name, err, test.labels, nil) + continue + } + // A trailing dot should be ignored. + if !strings.HasSuffix(s, ".") { + dotName, dotErr := ParseName(s + ".") + if dotErr != err || !namesEqual(dotName, name) { + t.Errorf("%+q parsing %+q returned (%+q, %v), expected (%+q, %v)", + test.labels, s+".", dotName, dotErr, name, err) + continue + } + } + } + } +} + +func TestParseName(t *testing.T) { + for _, test := range []struct { + s string + name Name + err error + }{ + // This case can't be tested by TestName above because String + // will never produce "" (it produces "." instead). + {"", [][]byte{}, nil}, + } { + name, err := ParseName(test.s) + if err != test.err || (err == nil && !namesEqual(name, test.name)) { + t.Errorf("%+q returned (%+q, %v), expected (%+q, %v)", + test.s, name, err, test.name, test.err) + continue + } + } +} + +func TestReadName(t *testing.T) { + // Good tests. + for _, test := range []struct { + start int64 + end int64 + input string + s string + }{ + // Empty name. + {0, 1, "\x00abcd", "."}, + // No pointers. + {12, 25, "AAAABBBBCCCC\x07example\x03com\x00", "example.com"}, + // Backward pointer. + {25, 31, "AAAABBBBCCCC\x07example\x03com\x00\x03sub\xc0\x0c", "sub.example.com"}, + // Forward pointer. + {0, 4, "\x01a\xc0\x04\x03bcd\x00", "a.bcd"}, + // Two backwards pointers. + {31, 38, "AAAABBBBCCCC\x07example\x03com\x00\x03sub\xc0\x0c\x04sub2\xc0\x19", "sub2.sub.example.com"}, + // Forward then backward pointer. + {25, 31, "AAAABBBBCCCC\x07example\x03com\x00\x03sub\xc0\x1f\x04sub2\xc0\x0c", "sub.sub2.example.com"}, + // Overlapping codons. + {0, 4, "\x01a\xc0\x03bcd\x00", "a.bcd"}, + // Pointer to empty label. + {0, 10, "\x07example\xc0\x0a\x00", "example"}, + {1, 11, "\x00\x07example\xc0\x00", "example"}, + // Pointer to pointer to empty label. + {0, 10, "\x07example\xc0\x0a\xc0\x0c\x00", "example"}, + {1, 11, "\x00\x07example\xc0\x0c\xc0\x00", "example"}, + } { + r := bytes.NewReader([]byte(test.input)) + _, err := r.Seek(test.start, io.SeekStart) + if err != nil { + panic(err) + } + name, err := readName(r) + if err != nil { + t.Errorf("%+q returned error %s", test.input, err) + continue + } + s := name.String() + if s != test.s { + t.Errorf("%+q returned %+q, expected %+q", test.input, s, test.s) + continue + } + cur, _ := r.Seek(0, io.SeekCurrent) + if cur != test.end { + t.Errorf("%+q left offset %d, expected %d", test.input, cur, test.end) + continue + } + } + + // Bad tests. + for _, test := range []struct { + start int64 + input string + err error + }{ + {0, "", io.ErrUnexpectedEOF}, + // Reserved label type. + {0, "\x80example", ErrReservedLabelType}, + // Reserved label type. + {0, "\x40example", ErrReservedLabelType}, + // No Terminating empty label. + {0, "\x07example\x03com", io.ErrUnexpectedEOF}, + // Pointer past end of buffer. + {0, "\x07example\xc0\xff", io.ErrUnexpectedEOF}, + // Pointer to self. + {0, "\x07example\x03com\xc0\x0c", ErrTooManyPointers}, + // Pointer to self with intermediate label. + {0, "\x07example\x03com\xc0\x08", ErrTooManyPointers}, + // Two pointers that point to each other. + {0, "\xc0\x02\xc0\x00", ErrTooManyPointers}, + // Two pointers that point to each other, with intermediate labels. + {0, "\x01a\xc0\x04\x01b\xc0\x00", ErrTooManyPointers}, + // EOF while reading label. + {0, "\x0aexample", io.ErrUnexpectedEOF}, + // EOF before second byte of pointer. + {0, "\x07example\xc0", io.ErrUnexpectedEOF}, + } { + r := bytes.NewReader([]byte(test.input)) + _, err := r.Seek(test.start, io.SeekStart) + if err != nil { + panic(err) + } + name, err := readName(r) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + if err != test.err { + t.Errorf("%+q returned (%+q, %v), expected %v", test.input, name, err, test.err) + continue + } + } +} + +func mustParseName(s string) Name { + name, err := ParseName(s) + if err != nil { + panic(err) + } + return name +} + +func questionsEqual(a, b *Question) bool { + if !namesEqual(a.Name, b.Name) { + return false + } + if a.Type != b.Type || a.Class != b.Class { + return false + } + return true +} + +func rrsEqual(a, b *RR) bool { + if !namesEqual(a.Name, b.Name) { + return false + } + if a.Type != b.Type || a.Class != b.Class || a.TTL != b.TTL { + return false + } + if !bytes.Equal(a.Data, b.Data) { + return false + } + return true +} + +func messagesEqual(a, b *Message) bool { + if a.ID != b.ID || a.Flags != b.Flags { + return false + } + if len(a.Question) != len(b.Question) { + return false + } + for i := 0; i < len(a.Question); i++ { + if !questionsEqual(&a.Question[i], &b.Question[i]) { + return false + } + } + for _, rec := range []struct{ rrA, rrB []RR }{ + {a.Answer, b.Answer}, + {a.Authority, b.Authority}, + {a.Additional, b.Additional}, + } { + if len(rec.rrA) != len(rec.rrB) { + return false + } + for i := 0; i < len(rec.rrA); i++ { + if !rrsEqual(&rec.rrA[i], &rec.rrB[i]) { + return false + } + } + } + return true +} + +func TestMessageFromWireFormat(t *testing.T) { + for _, test := range []struct { + buf string + expected Message + err error + }{ + { + "\x12\x34", + Message{}, + io.ErrUnexpectedEOF, + }, + { + "\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x07example\x03com\x00\x00\x01\x00\x01", + Message{ + ID: 0x1234, + Flags: 0x0100, + Question: []Question{ + { + Name: mustParseName("www.example.com"), + Type: 1, + Class: 1, + }, + }, + Answer: []RR{}, + Authority: []RR{}, + Additional: []RR{}, + }, + nil, + }, + { + "\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x07example\x03com\x00\x00\x01\x00\x01X", + Message{}, + ErrTrailingBytes, + }, + { + "\x12\x34\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x03www\x07example\x03com\x00\x00\x01\x00\x01\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x80\x00\x04\xc0\x00\x02\x01", + Message{ + ID: 0x1234, + Flags: 0x8180, + Question: []Question{ + { + Name: mustParseName("www.example.com"), + Type: 1, + Class: 1, + }, + }, + Answer: []RR{ + { + Name: mustParseName("www.example.com"), + Type: 1, + Class: 1, + TTL: 128, + Data: []byte{192, 0, 2, 1}, + }, + }, + Authority: []RR{}, + Additional: []RR{}, + }, + nil, + }, + } { + message, err := MessageFromWireFormat([]byte(test.buf)) + if err != test.err || (err == nil && !messagesEqual(&message, &test.expected)) { + t.Errorf("%+q\nreturned (%+v, %v)\nexpected (%+v, %v)", + test.buf, message, err, test.expected, test.err) + continue + } + } +} diff --git a/dnstt-server/main.go b/dnstt-server/main.go index 4acf62e..bd6ba31 100644 --- a/dnstt-server/main.go +++ b/dnstt-server/main.go @@ -115,7 +115,12 @@ func acceptSessions(ln *kcp.Listener, mtu int, upstream *net.TCPAddr) error { func handle(c net.PacketConn, p []byte, addr net.Addr) error { fmt.Printf("handle %v %x\n", addr, p) - _, err := c.WriteTo([]byte("hello"), addr) + message, err := dns.MessageFromWireFormat(p) + if err != nil { + return err + } + fmt.Printf("%#v\n", message) + _, err = c.WriteTo([]byte("hello"), addr) return err } @@ -215,7 +220,7 @@ func main() { fmt.Fprintf(os.Stderr, "usage: %s -udp ADDR DOMAIN UPSTREAMADDR\n", os.Args[0]) os.Exit(1) } - domain, err := dns.ParseName([]byte(flag.Arg(0))) + domain, err := dns.ParseName(flag.Arg(0)) if err != nil { fmt.Fprintf(os.Stderr, "invalid domain %+q: %v\n", flag.Arg(0), err) os.Exit(1)