mirror of
https://github.com/tladesignz/dnstt.git
synced 2026-10-05 21:38:09 +03:00
MessageFromWireFormat
This commit is contained in:
+313
-2
@@ -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
|
||||
}
|
||||
|
||||
+377
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user