diff --git a/dnsutils/dns.go b/dnsutils/dns.go index f6fa8de5..30ac0cc8 100644 --- a/dnsutils/dns.go +++ b/dnsutils/dns.go @@ -4,7 +4,7 @@ import ( "encoding/binary" "errors" "fmt" - "strconv" + "net" "strings" ) @@ -300,11 +300,12 @@ func DecodeAnswer(ancount int, start_offset int, payload []byte) ([]DnsAnswer, i // ignore OPT, this type is decoded in the EDNS extension if t == 41 { + offset = offset_next + 10 + int(rdlength) continue } // parse rdata rdatatype := RdatatypeToString(int(t)) - parsed, err := ParseRdata(rdatatype, rdata, payload, offset_next+10) + parsed, err := ParseRdata(rdatatype, rdata, payload[:offset_next+10+int(rdlength)], offset_next+10) if err != nil { return answers, offset, err } @@ -463,7 +464,12 @@ func ParseSOA(rdata_offset int, payload []byte) (string, error) { if err != nil { return "", err } - rdata := payload[offset:] + + // ensure there is enough data to parse rest of the fields + if offset+20 > len(payload) { + return "", ErrDecodeDnsAnswerRdataTooShort + } + rdata := payload[offset : offset+20] serial := binary.BigEndian.Uint32(rdata[0:4]) refresh := int32(binary.BigEndian.Uint32(rdata[4:8])) @@ -485,12 +491,13 @@ IPv4 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ func ParseA(r []byte) (string, error) { - var ip []string - for i := 0; i < len(r); i++ { - ip = append(ip, strconv.Itoa(int(r[i]))) + + if len(r) < net.IPv4len { + return "", ErrDecodeDnsAnswerRdataTooShort } - a := strings.Join(ip, ".") - return a, nil + addr := make(net.IP, net.IPv4len) + copy(addr, r[:net.IPv4len]) + return addr.String(), nil } /* @@ -509,12 +516,12 @@ IPv6 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ func ParseAAAA(rdata []byte) (string, error) { - var ip []string - for i := 0; i < len(rdata); i += 2 { - ip = append(ip, fmt.Sprintf("%x", binary.BigEndian.Uint16(rdata[i:i+2]))) + if len(rdata) < net.IPv6len { + return "", ErrDecodeDnsAnswerRdataTooShort } - aaaa := strings.Join(ip, ":") - return aaaa, nil + addr := make(net.IP, net.IPv6len) + copy(addr, rdata[:net.IPv6len]) + return addr.String(), nil } /* @@ -546,11 +553,17 @@ MX +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ func ParseMX(rdata_offset int, payload []byte) (string, error) { + // ensure there is enough data for pereference and at least + // one byte for label + if len(payload) < rdata_offset+3 { + return "", ErrDecodeDnsAnswerRdataTooShort + } pref := binary.BigEndian.Uint16(payload[rdata_offset : rdata_offset+2]) host, _, err := ParseLabels(rdata_offset+2, payload) if err != nil { return "", err } + mx := fmt.Sprintf("%d %s", pref, host) return mx, err } @@ -570,6 +583,9 @@ SRV +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ func ParseSRV(rdata_offset int, payload []byte) (string, error) { + if len(payload) < rdata_offset+7 { + return "", ErrDecodeDnsAnswerRdataTooShort + } priority := binary.BigEndian.Uint16(payload[rdata_offset : rdata_offset+2]) weight := binary.BigEndian.Uint16(payload[rdata_offset+2 : rdata_offset+4]) port := binary.BigEndian.Uint16(payload[rdata_offset+4 : rdata_offset+6]) @@ -609,7 +625,14 @@ TXT +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ func ParseTXT(rdata []byte) (string, error) { + // ensure there is enough data to read the length + if len(rdata) < 1 { + return "", ErrDecodeDnsAnswerRdataTooShort + } length := int(rdata[0]) + if len(rdata)-1 < length { + return "", ErrDecodeDnsAnswerRdataTooShort + } txt := string(rdata[1 : length+1]) return txt, nil } diff --git a/dnsutils/dns_test.go b/dnsutils/dns_test.go index 9c227ff1..3c9f38bc 100644 --- a/dnsutils/dns_test.go +++ b/dnsutils/dns_test.go @@ -153,13 +153,47 @@ func TestDecodeRdataA(t *testing.T) { } } +func TestDecodeRdataA_Short(t *testing.T) { + payload := []byte{ + 0x43, 0xac, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + // Query section + 0x0f, 0x64, 0x6e, 0x73, + 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, 0x65, 0x73, + 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer Resource Record + 0x0f, 0x64, + 0x6e, 0x73, 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x00, + // type A, class IN + 0x00, 0x01, 0x00, 0x01, + // TTL + 0x00, 0x00, 0x0e, 0x10, + // RDLENGTH + 0x00, 0x03, + // RDATA (1 byte too short for A record) + 0x7f, 0x00, 0x00, + } + _, _, offsetrr, err := DecodeQuestion(payload) + if err != nil { + t.Errorf("Unexpected error decoding question: %v", err) + } + + _, _, erra := DecodeAnswer(1, offsetrr, payload) + if !errors.Is(erra, ErrDecodeDnsAnswerRdataTooShort) { + t.Errorf("bad error returned: %v", erra) + } +} + func TestDecodeRdataAAAA(t *testing.T) { fqdn := "dnstapcollector.test." dm := new(dns.Msg) dm.SetQuestion(fqdn, dns.TypeA) - rdata := "fe80:0:0:0:0:0:0:2" + rdata := "fe8::2" rr1, _ := dns.NewRR(fmt.Sprintf("%s AAAA %s", fqdn, rdata)) dm.Answer = append(dm.Answer, rr1) @@ -172,6 +206,42 @@ func TestDecodeRdataAAAA(t *testing.T) { t.Errorf("invalid decode for rdata AAAA, want %s, got: %s", rdata, answer[0].Rdata) } } +func TestDecodeRdataAAAA_Short(t *testing.T) { + payload := []byte{ + // header + 0x3b, 0x33, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + // Query section + 0x0f, 0x64, 0x6e, 0x73, + 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, 0x65, 0x73, + 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer resource record + 0x0f, 0x64, + 0x6e, 0x73, 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x00, + // type AAAA, class IN + 0x00, 0x1c, 0x00, 0x01, + // TTL + 0x00, 0x00, 0x0e, 0x10, + // RDLENGTH + 0x00, 0x0c, + // RDATA + 0xfe, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + } + + _, _, offsetset_rr, err := DecodeQuestion(payload) + if err != nil { + t.Errorf("unexpected error while decoding question: %v", err) + } + _, _, erra := DecodeAnswer(1, offsetset_rr, payload) + if !errors.Is(erra, ErrDecodeDnsAnswerRdataTooShort) { + t.Errorf("bad error returned: %v", erra) + } +} func TestDecodeRdataCNAME(t *testing.T) { fqdn := "dnstapcollector.test." @@ -213,6 +283,79 @@ func TestDecodeRdataMX(t *testing.T) { } } +func TestDecodeRdataMX_Short(t *testing.T) { + payload := []byte{ + // header + 0xed, 0x7f, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + // Question seection + 0x0f, 0x64, 0x6e, 0x73, + 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, 0x65, 0x73, + 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer Resource Record + 0x0f, 0x64, + 0x6e, 0x73, 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x00, + // type MX, class IN + 0x00, 0x0f, 0x00, 0x01, + // TTL + 0x00, 0x00, 0x0e, 0x10, + // RDLENGTH + 0x00, 0x01, + // RDATA + 0x00, + } + _, _, offset_rr, err := DecodeQuestion(payload) + if err != nil { + t.Errorf("unexpected error while decoding question: %v", err) + } + _, _, erra := DecodeAnswer(1, offset_rr, payload) + if !errors.Is(erra, ErrDecodeDnsAnswerRdataTooShort) { + t.Errorf("bad error returned: %v", erra) + } + +} + +func TestDecodeRdataMX_Minimal(t *testing.T) { + payload := []byte{ + // header + 0xed, 0x7f, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + // Question seection + 0x0f, 0x64, 0x6e, 0x73, + 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, 0x65, 0x73, + 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer Resource Record + 0x0f, 0x64, + 0x6e, 0x73, 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x00, + // type MX, class IN + 0x00, 0x0f, 0x00, 0x01, + // TTL + 0x00, 0x00, 0x0e, 0x10, + // RDLENGTH + 0x00, 0x03, + // RDATA + 0x00, 0x00, 0x00, + } + _, _, offset_rr, err := DecodeQuestion(payload) + if err != nil { + t.Errorf("unexpected error while decoding question: %v", err) + } + answer, _, erra := DecodeAnswer(1, offset_rr, payload) + if erra != nil { + t.Errorf("unexpected error while decoding answer: %v", err) + } + expected := "0 " + if answer[0].Rdata != expected { + t.Errorf("invalid decode for MX rdata, expected %s got %s", expected, answer[0].Rdata) + } +} + func TestDecodeRdataSRV(t *testing.T) { fqdn := "dnstapcollector.test." @@ -233,6 +376,90 @@ func TestDecodeRdataSRV(t *testing.T) { } } +func TestDecodeRdataSRV_Short(t *testing.T) { + payload := []byte{ + // header + 0xd9, 0x93, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + // Question section + 0x0f, 0x64, 0x6e, 0x73, + 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, 0x65, 0x73, + 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer section + 0x0f, 0x64, + 0x6e, 0x73, 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x00, + // type SRV, class IN + 0x00, 0x21, 0x00, 0x01, + // TTL + 0x00, 0x00, 0x0e, 0x10, + // RDLENGTH + 0x00, 0x04, + // RDATA + // priority + 0x00, 0x14, + // weight + 0x00, 0x00, + // missing port and target + } + + _, _, offset_rr, err := DecodeQuestion(payload) + if err != nil { + t.Errorf("unexpected error while decoding question: %v", err) + } + _, _, erra := DecodeAnswer(1, offset_rr, payload) + if !errors.Is(erra, ErrDecodeDnsAnswerRdataTooShort) { + t.Errorf("bad error returned: %v", erra) + } +} + +func TestDecodeRdataSRV_Minimal(t *testing.T) { + payload := []byte{ + // header + 0xd9, 0x93, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + // Question section + 0x0f, 0x64, 0x6e, 0x73, + 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, 0x65, 0x73, + 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer section + 0x0f, 0x64, + 0x6e, 0x73, 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x00, + // type SRV, class IN + 0x00, 0x21, 0x00, 0x01, + // TTL + 0x00, 0x00, 0x0e, 0x10, + // RDLENGTH + 0x00, 0x07, + // RDATA + // priority + 0x00, 0x14, + // weight + 0x00, 0x00, + // port + 0x00, 0x10, + // empty target + 0x00, + } + + _, _, offset_rr, err := DecodeQuestion(payload) + if err != nil { + t.Errorf("unexpected error while decoding question: %v", err) + } + answer, _, erra := DecodeAnswer(1, offset_rr, payload) + if erra != nil { + t.Errorf("unexpected error while decoding answer: %v", err) + } + expected_rdata := "20 0 16 " + if answer[0].Rdata != expected_rdata { + t.Errorf("invalid decode for rdata SRV, want %s, got: %s", expected_rdata, answer[0].Rdata) + } +} func TestDecodeRdataNS(t *testing.T) { fqdn := "dnstapcollector.test." @@ -273,6 +500,122 @@ func TestDecodeRdataTXT(t *testing.T) { } } +func TestDecodeRdataTXT_Empty(t *testing.T) { + payload := []byte{ + // header + 0x86, 0x08, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + // question section + 0x0f, 0x64, 0x6e, 0x73, + 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, 0x65, 0x73, + 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, + // answer section + 0x0f, 0x64, + 0x6e, 0x73, 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x00, + // type TXT, class IN + 0x00, 0x10, 0x00, 0x01, + // TTL + 0x00, 0x00, 0x0e, 0x10, + // RDLENGTH + 0x00, 0x00, + // no data + } + + _, _, offset_rr, err := DecodeQuestion(payload) + if err != nil { + t.Errorf("unexpected error while decoding question: %v", err) + } + _, _, erra := DecodeAnswer(1, offset_rr, payload) + if !errors.Is(erra, ErrDecodeDnsAnswerRdataTooShort) { + t.Errorf("bad error returned: %v", erra) + } + +} +func TestDecodeRdataTXT_Short(t *testing.T) { + payload := []byte{ + // header + 0x86, 0x08, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + // question section + 0x0f, 0x64, 0x6e, 0x73, + 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, 0x65, 0x73, + 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, + // answer section + 0x0f, 0x64, + 0x6e, 0x73, 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x00, + // type TXT, class IN + 0x00, 0x10, 0x00, 0x01, + // TTL + 0x00, 0x00, 0x0e, 0x10, + // RDLENGTH + 0x00, 0x0a, + // RDATA + // length + 0x0b, + // characters + 0x68, + 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, + // missing two bytes + } + + _, _, offset_rr, err := DecodeQuestion(payload) + if err != nil { + t.Errorf("unexpected error while decoding question: %v", err) + } + _, _, erra := DecodeAnswer(1, offset_rr, payload) + if !errors.Is(erra, ErrDecodeDnsAnswerRdataTooShort) { + t.Errorf("bad error returned: %v", erra) + } + +} +func TestDecodeRdataTXT_NoTxt(t *testing.T) { + payload := []byte{ + // header + 0x86, 0x08, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + // question section + 0x0f, 0x64, 0x6e, 0x73, + 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, 0x65, 0x73, + 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, + // answer section + 0x0f, 0x64, + 0x6e, 0x73, 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x00, + // type TXT, class IN + 0x00, 0x10, 0x00, 0x01, + // TTL + 0x00, 0x00, 0x0e, 0x10, + // RDLENGTH + 0x00, 0x01, + // RDATA + // length + 0x00, + // no txt-data + } + + _, _, offset_rr, err := DecodeQuestion(payload) + if err != nil { + t.Errorf("unexpected error while decoding question: %v", err) + } + answer, _, erra := DecodeAnswer(1, offset_rr, payload) + if erra != nil { + t.Errorf("unexpected error while decoding answer: %v", err) + } + + if answer[0].Rdata != "" { + t.Errorf("expected empty string in RDATA, got: %s", answer[0].Rdata) + } + +} + func TestDecodeRdataPTR(t *testing.T) { fqdn := "dnstapcollector.test." @@ -313,6 +656,58 @@ func TestDecodeRdataSOA(t *testing.T) { } } +func TestDecodeRdataSOA_Short(t *testing.T) { + payload := []byte{ + // header + 0x28, 0xba, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + // Query section + 0x0f, 0x64, 0x6e, 0x73, + 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, 0x65, 0x73, + 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer Resource Record, + 0x0f, 0x64, + 0x6e, 0x73, 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x00, + // type SOA, class IN + 0x00, 0x06, 0x00, 0x01, + // TTL + 0x00, 0x00, 0x0e, 0x10, + // RDLENGTH 54 + 0x00, 0x36, + // RDATA + // MNAME + 0x03, 0x6e, + 0x73, 0x31, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, + // RNAME + 0x09, 0x64, + 0x6e, 0x73, 0x2d, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00, + // serial + 0x18, 0x94, 0xea, 0xef, + // refresh + 0x00, 0x00, 0x03, 0x84, + // retry + 0x00, 0x00, 0x03, 0x84, + // expire + 0x00, 0x00, 0x07, 0x08, + // minimum -field missing from the RDATA + } + + _, _, offsset_rr, err := DecodeQuestion(payload) + if err != nil { + t.Errorf("Unable to decode question: %v", err) + } + _, _, erra := DecodeAnswer(1, offsset_rr, payload) + if !errors.Is(erra, ErrDecodeDnsAnswerRdataTooShort) { + t.Errorf("bad error returned: %v", erra) + } +} + func TestDecodeRdataSOA_Minimization(t *testing.T) { // loop between qnames payload := []byte{164, 66, 129, 128, 0, 1, 0, 0, 0, 1, 0, 0, 8, 102, 114, 101, 115, 104, 114, 115, 115, 4, 109, @@ -326,6 +721,58 @@ func TestDecodeRdataSOA_Minimization(t *testing.T) { t.Errorf(" error returned: %v", err) } } +func TestDecodeQuestion_SkipOpt(t *testing.T) { + payload := []byte{ + 0x43, 0xac, 0x01, 0x00, 0x00, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, + // Query section + 0x0f, 0x64, 0x6e, 0x73, + 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, 0x65, 0x73, + 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, + // Answer Resource Records + 0x0f, 0x64, + 0x6e, 0x73, 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x00, + // type OPT, class IN + 0x00, 0x29, 0x00, 0x01, + // TTL + 0x00, 0x00, 0x0e, 0x10, + // RDLENGTH + 0x00, 0x01, + //RDATA + 0x01, + // 2nd resource record + 0x0f, 0x64, + 0x6e, 0x73, 0x74, 0x61, 0x70, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x04, 0x74, + 0x65, 0x73, 0x74, 0x00, + // type A, class IN + 0x00, 0x01, 0x00, 0x01, + // TTL + 0x00, 0x00, 0x0e, 0x10, + // RDLENGTH + 0x00, 0x04, + // RDATA + 0x7f, 0x00, 0x00, 0x01, + } + _, _, offsetrr, err := DecodeQuestion(payload) + if err != nil { + t.Errorf("Unexpected error decoding question: %v", err) + } + + answer, _, erra := DecodeAnswer(2, offsetrr, payload) + if erra != nil { + t.Errorf("Unexpected error decoding answer: %v", erra) + } + if len(answer) != 1 { + t.Fatalf("Expected answer to contain one resource record, got %d", len(answer)) + } + if answer[0].Rdatatype != RdatatypeToString(0x01) || answer[0].Rdata != "127.0.0.1" { + t.Errorf("unexpected answer %s %s, expected A 127.0.0.1", answer[0].Rdatatype, answer[0].Rdata) + } +} func TestDecodeDns_HeaderTooShort(t *testing.T) { decoded := []byte{183, 59}