Merge branch 'master' of github.com:miekg/dns
This commit is contained in:
commit
06e7420b3e
|
@ -6,3 +6,4 @@ Dusty Wilson
|
|||
Marek Majkowski
|
||||
Peter van Dijk
|
||||
Omri Bahumi
|
||||
Alex Sergeyev
|
||||
|
|
|
@ -34,7 +34,8 @@ A not-so-up-to-date-list-that-may-be-actually-current:
|
|||
* https://github.com/skynetservices/skydns
|
||||
* https://github.com/DevelopersPL/godnsagent
|
||||
* https://github.com/duedil-ltd/discodns
|
||||
* ... (send pull request if you want to be listed here)
|
||||
|
||||
Send pull request if you want to be listed here.
|
||||
|
||||
# Features
|
||||
|
||||
|
|
33
edns.go
33
edns.go
|
@ -35,16 +35,16 @@ import (
|
|||
|
||||
// EDNS0 Option codes.
|
||||
const (
|
||||
EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
|
||||
EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt
|
||||
EDNS0NSID = 0x3 // nsid (RFC5001)
|
||||
EDNS0DAU = 0x5 // DNSSEC Algorithm Understood
|
||||
EDNS0DHU = 0x6 // DS Hash Understood
|
||||
EDNS0N3U = 0x7 // NSEC3 Hash Understood
|
||||
EDNS0SUBNET = 0x8 // client-subnet (RFC6891)
|
||||
EDNS0EXPIRE = 0x9 // EDNS0 expire
|
||||
EDNS0SUBNETDRAFT = 0x50fa // Don't use! Use EDNS0SUBNET
|
||||
_DO = 1 << 7 // dnssec ok
|
||||
EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
|
||||
EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt
|
||||
EDNS0NSID = 0x3 // nsid (RFC5001)
|
||||
EDNS0DAU = 0x5 // DNSSEC Algorithm Understood
|
||||
EDNS0DHU = 0x6 // DS Hash Understood
|
||||
EDNS0N3U = 0x7 // NSEC3 Hash Understood
|
||||
EDNS0SUBNET = 0x8 // client-subnet (RFC6891)
|
||||
EDNS0EXPIRE = 0x9 // EDNS0 expire
|
||||
EDNS0SUBNETDRAFT = 0x50fa // Don't use! Use EDNS0SUBNET
|
||||
_DO = 1 << 15 // dnssec ok
|
||||
)
|
||||
|
||||
type OPT struct {
|
||||
|
@ -114,12 +114,12 @@ func (rr *OPT) copy() RR {
|
|||
|
||||
// Version returns the EDNS version used. Only zero is defined.
|
||||
func (rr *OPT) Version() uint8 {
|
||||
return uint8(rr.Hdr.Ttl & 0x00FF00FFFF)
|
||||
return uint8((rr.Hdr.Ttl & 0x00FF0000) >> 16)
|
||||
}
|
||||
|
||||
// SetVersion sets the version of EDNS. This is usually zero.
|
||||
func (rr *OPT) SetVersion(v uint8) {
|
||||
rr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | uint32(v)
|
||||
rr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | (uint32(v) << 16)
|
||||
}
|
||||
|
||||
// UDPSize returns the UDP buffer size.
|
||||
|
@ -134,17 +134,12 @@ func (rr *OPT) SetUDPSize(size uint16) {
|
|||
|
||||
// Do returns the value of the DO (DNSSEC OK) bit.
|
||||
func (rr *OPT) Do() bool {
|
||||
return byte(rr.Hdr.Ttl>>8)&_DO == _DO
|
||||
return rr.Hdr.Ttl&_DO == _DO
|
||||
}
|
||||
|
||||
// SetDo sets the DO (DNSSEC OK) bit.
|
||||
func (rr *OPT) SetDo() {
|
||||
b1 := byte(rr.Hdr.Ttl >> 24)
|
||||
b2 := byte(rr.Hdr.Ttl >> 16)
|
||||
b3 := byte(rr.Hdr.Ttl >> 8)
|
||||
b4 := byte(rr.Hdr.Ttl)
|
||||
b3 |= _DO // Set it
|
||||
rr.Hdr.Ttl = uint32(b1)<<24 | uint32(b2)<<16 | uint32(b3)<<8 | uint32(b4)
|
||||
rr.Hdr.Ttl |= _DO
|
||||
}
|
||||
|
||||
// EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package dns
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestOPTTtl(t *testing.T) {
|
||||
e := &OPT{}
|
||||
e.Hdr.Name = "."
|
||||
e.Hdr.Rrtype = TypeOPT
|
||||
|
||||
if e.Do() {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
e.SetDo()
|
||||
if !e.Do() {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
oldTtl := e.Hdr.Ttl
|
||||
|
||||
if e.Version() != 0 {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
e.SetVersion(42)
|
||||
if e.Version() != 42 {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
e.SetVersion(0)
|
||||
if e.Hdr.Ttl != oldTtl {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
122
example_test.go
122
example_test.go
|
@ -1,25 +1,29 @@
|
|||
package dns
|
||||
package dns_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/miekg/dns"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Retrieve the MX records for miek.nl.
|
||||
func ExampleMX() {
|
||||
config, _ := ClientConfigFromFile("/etc/resolv.conf")
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
m.SetQuestion("miek.nl.", TypeMX)
|
||||
config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
|
||||
c := new(dns.Client)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("miek.nl.", dns.TypeMX)
|
||||
m.RecursionDesired = true
|
||||
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if r.Rcode != RcodeSuccess {
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return
|
||||
}
|
||||
for _, a := range r.Answer {
|
||||
if mx, ok := a.(*MX); ok {
|
||||
if mx, ok := a.(*dns.MX); ok {
|
||||
fmt.Printf("%s\n", mx.String())
|
||||
}
|
||||
}
|
||||
|
@ -28,26 +32,116 @@ func ExampleMX() {
|
|||
// Retrieve the DNSKEY records of a zone and convert them
|
||||
// to DS records for SHA1, SHA256 and SHA384.
|
||||
func ExampleDS(zone string) {
|
||||
config, _ := ClientConfigFromFile("/etc/resolv.conf")
|
||||
c := new(Client)
|
||||
m := new(Msg)
|
||||
config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
|
||||
c := new(dns.Client)
|
||||
m := new(dns.Msg)
|
||||
if zone == "" {
|
||||
zone = "miek.nl"
|
||||
}
|
||||
m.SetQuestion(Fqdn(zone), TypeDNSKEY)
|
||||
m.SetQuestion(dns.Fqdn(zone), dns.TypeDNSKEY)
|
||||
m.SetEdns0(4096, true)
|
||||
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if r.Rcode != RcodeSuccess {
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return
|
||||
}
|
||||
for _, k := range r.Answer {
|
||||
if key, ok := k.(*DNSKEY); ok {
|
||||
for _, alg := range []int{SHA1, SHA256, SHA384} {
|
||||
if key, ok := k.(*dns.DNSKEY); ok {
|
||||
for _, alg := range []int{dns.SHA1, dns.SHA256, dns.SHA384} {
|
||||
fmt.Printf("%s; %d\n", key.ToDS(alg).String(), key.Flags)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const TypeAPAIR = 0x0F99
|
||||
|
||||
type APAIR struct {
|
||||
addr [2]net.IP
|
||||
}
|
||||
|
||||
func NewAPAIR() dns.PrivateRdata { return new(APAIR) }
|
||||
|
||||
func (rd *APAIR) String() string { return rd.addr[0].String() + " " + rd.addr[1].String() }
|
||||
func (rd *APAIR) Parse(txt []string) error {
|
||||
if len(txt) != 2 {
|
||||
return errors.New("two addresses required for APAIR")
|
||||
}
|
||||
for i, s := range txt {
|
||||
ip := net.ParseIP(s)
|
||||
if ip == nil {
|
||||
return errors.New("invalid IP in APAIR text representation")
|
||||
}
|
||||
rd.addr[i] = ip
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *APAIR) Pack(buf []byte) (int, error) {
|
||||
b := append([]byte(rd.addr[0]), []byte(rd.addr[1])...)
|
||||
n := copy(buf, b)
|
||||
if n != len(b) {
|
||||
return n, dns.ErrBuf
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (rd *APAIR) Unpack(buf []byte) (int, error) {
|
||||
ln := net.IPv4len * 2
|
||||
if len(buf) != ln {
|
||||
return 0, errors.New("invalid length of APAIR rdata")
|
||||
}
|
||||
cp := make([]byte, ln)
|
||||
copy(cp, buf) // clone bytes to use them in IPs
|
||||
|
||||
rd.addr[0] = net.IP(cp[:3])
|
||||
rd.addr[1] = net.IP(cp[4:])
|
||||
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (rd *APAIR) Copy(dest dns.PrivateRdata) error {
|
||||
cp := make([]byte, rd.Len())
|
||||
_, err := rd.Pack(cp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d := dest.(*APAIR)
|
||||
d.addr[0] = net.IP(cp[:3])
|
||||
d.addr[1] = net.IP(cp[4:])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *APAIR) Len() int {
|
||||
return net.IPv4len * 2
|
||||
}
|
||||
|
||||
func ExamplePrivateHandle() {
|
||||
dns.PrivateHandle("APAIR", TypeAPAIR, NewAPAIR)
|
||||
defer dns.PrivateHandleRemove(TypeAPAIR)
|
||||
|
||||
rr, err := dns.NewRR("miek.nl. APAIR (1.2.3.4 1.2.3.5)")
|
||||
if err != nil {
|
||||
log.Fatal("could not parse APAIR record: ", err)
|
||||
}
|
||||
fmt.Println(rr)
|
||||
// Output: miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.Id = 12345
|
||||
m.SetQuestion("miek.nl.", TypeAPAIR)
|
||||
m.Answer = append(m.Answer, rr)
|
||||
|
||||
fmt.Println(m)
|
||||
// ;; opcode: QUERY, status: NOERROR, id: 12345
|
||||
// ;; flags: rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
|
||||
//
|
||||
// ;; QUESTION SECTION:
|
||||
// ;miek.nl. IN APAIR
|
||||
//
|
||||
// ;; ANSWER SECTION:
|
||||
// miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@ package dns
|
|||
|
||||
// SplitDomainName splits a name string into it's labels.
|
||||
// www.miek.nl. returns []string{"www", "miek", "nl"}
|
||||
// The root label (.) returns nil.
|
||||
// The root label (.) returns nil. Note that using
|
||||
// strings.Split(s) will work in most cases, but does not handle
|
||||
// escaped dots (\.) for instance.
|
||||
func SplitDomainName(s string) (labels []string) {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
|
@ -101,7 +103,7 @@ func CountLabel(s string) (labels int) {
|
|||
|
||||
// Split splits a name s into its label indexes.
|
||||
// www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}.
|
||||
// The root name (.) returns nil.
|
||||
// The root name (.) returns nil. Also see dns.SplitDomainName.
|
||||
func Split(s string) []int {
|
||||
if s == "." {
|
||||
return nil
|
||||
|
|
26
msg.go
26
msg.go
|
@ -576,6 +576,19 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str
|
|||
switch fv := val.Field(i); fv.Kind() {
|
||||
default:
|
||||
return lenmsg, &Error{err: "bad kind packing"}
|
||||
case reflect.Interface:
|
||||
// PrivateRR is the only RR implementation that has interface field.
|
||||
// therefore it's expected that this interface would be PrivateRdata
|
||||
switch data := fv.Interface().(type) {
|
||||
case PrivateRdata:
|
||||
n, err := data.Pack(msg[off:])
|
||||
if err != nil {
|
||||
return lenmsg, err
|
||||
}
|
||||
off += n
|
||||
default:
|
||||
return lenmsg, &Error{err: "bad kind interface packing"}
|
||||
}
|
||||
case reflect.Slice:
|
||||
switch typefield.Tag {
|
||||
default:
|
||||
|
@ -869,6 +882,19 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
switch fv := val.Field(i); fv.Kind() {
|
||||
default:
|
||||
return lenmsg, &Error{err: "bad kind unpacking"}
|
||||
case reflect.Interface:
|
||||
// PrivateRR is the only RR implementation that has interface field.
|
||||
// therefore it's expected that this interface would be PrivateRdata
|
||||
switch data := fv.Interface().(type) {
|
||||
case PrivateRdata:
|
||||
n, err := data.Unpack(msg[off:rdend])
|
||||
if err != nil {
|
||||
return lenmsg, err
|
||||
}
|
||||
off += n
|
||||
default:
|
||||
return lenmsg, &Error{err: "bad kind interface unpacking"}
|
||||
}
|
||||
case reflect.Slice:
|
||||
switch val.Type().Field(i).Tag {
|
||||
default:
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
a.example.com. IN A 127.0.0.1
|
||||
8db7._openpgpkey.example.com. IN OPENPGPKEY mQCNAzIG
|
|
@ -7,7 +7,6 @@ import (
|
|||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -563,13 +562,17 @@ func TestParseFailure(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestZoneParsing(t *testing.T) {
|
||||
f, err := os.Open("parse_test.db")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
// parse_test.db
|
||||
db := `
|
||||
a.example.com. IN A 127.0.0.1
|
||||
8db7._openpgpkey.example.com. IN OPENPGPKEY mQCNAzIG
|
||||
$ORIGIN a.example.com.
|
||||
test IN A 127.0.0.1
|
||||
$ORIGIN b.example.com.
|
||||
test IN CNAME test.a.example.com.
|
||||
`
|
||||
start := time.Now().UnixNano()
|
||||
to := ParseZone(f, "", "parse_test.db")
|
||||
to := ParseZone(strings.NewReader(db), "", "parse_test.db")
|
||||
var i int
|
||||
for x := range to {
|
||||
i++
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
PRIVATE RR
|
||||
|
||||
RFC 6895 sets aside a range of type codes for private use. This range
|
||||
is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these
|
||||
can be used, before requesting an official type code from IANA.
|
||||
*/
|
||||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PrivateRdata is an interface used for implementing "Private Use" RR types, see
|
||||
// RFC 6895. This allows one to experiment with new RR types, without requesting an
|
||||
// official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove.
|
||||
type PrivateRdata interface {
|
||||
// String returns the text presentaton of the Rdata of the Private RR.
|
||||
String() string
|
||||
// Parse parses the Rdata of the private RR.
|
||||
Parse([]string) error
|
||||
// Pack is used when packing a private RR into a buffer.
|
||||
Pack([]byte) (int, error)
|
||||
// Unpack is used when unpacking a private RR from a buffer.
|
||||
// TODO(miek): diff. signature than Pack, see edns0.go for instance.
|
||||
Unpack([]byte) (int, error)
|
||||
// Copy copies the Rdata.
|
||||
Copy(PrivateRdata) error
|
||||
// Len returns the length in octets of the Rdata.
|
||||
Len() int
|
||||
}
|
||||
|
||||
// PrivateRR represents an RR that uses a PrivateRdata user-defined type.
|
||||
// It mocks normal RRs and implements dns.RR interface.
|
||||
type PrivateRR struct {
|
||||
Hdr RR_Header
|
||||
Data PrivateRdata
|
||||
}
|
||||
|
||||
func mkPrivateRR(rrtype uint16) *PrivateRR {
|
||||
// Panics if RR is not an instance of PrivateRR.
|
||||
rrfunc, ok := typeToRR[rrtype]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype))
|
||||
}
|
||||
|
||||
anyrr := rrfunc()
|
||||
switch rr := anyrr.(type) {
|
||||
case *PrivateRR:
|
||||
return rr
|
||||
}
|
||||
panic(fmt.Sprintf("dns: RR is not a PrivateRR, typeToRR[%d] generator returned %T", rrtype, anyrr))
|
||||
}
|
||||
|
||||
func (r *PrivateRR) Header() *RR_Header { return &r.Hdr }
|
||||
func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }
|
||||
|
||||
// Private len and copy parts to satisfy RR interface.
|
||||
func (r *PrivateRR) len() int { return r.Hdr.len() + r.Data.Len() }
|
||||
func (r *PrivateRR) copy() RR {
|
||||
// make new RR like this:
|
||||
rr := mkPrivateRR(r.Hdr.Rrtype)
|
||||
newh := r.Hdr.copyHeader()
|
||||
rr.Hdr = *newh
|
||||
|
||||
err := r.Data.Copy(rr.Data)
|
||||
if err != nil {
|
||||
panic("dns: got value that could not be used to copy Private rdata")
|
||||
}
|
||||
return rr
|
||||
}
|
||||
|
||||
// PrivateHandle registers a private resource record type. It requires
|
||||
// string and numeric representation of private RR type and generator function as argument.
|
||||
func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {
|
||||
rtypestr = strings.ToUpper(rtypestr)
|
||||
|
||||
typeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
|
||||
TypeToString[rtype] = rtypestr
|
||||
StringToType[rtypestr] = rtype
|
||||
|
||||
setPrivateRR := func(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
||||
rr := mkPrivateRR(h.Rrtype)
|
||||
rr.Hdr = h
|
||||
|
||||
var l lex
|
||||
text := make([]string, 0, 2) // could be 0..N elements, median is probably 1
|
||||
FETCH:
|
||||
for {
|
||||
// TODO(miek): we could also be returning _QUOTE, this might or might not
|
||||
// be an issue (basically parsing TXT becomes hard)
|
||||
switch l = <-c; l.value {
|
||||
case _NEWLINE, _EOF:
|
||||
break FETCH
|
||||
case _STRING:
|
||||
text = append(text, l.token)
|
||||
}
|
||||
}
|
||||
|
||||
err := rr.Data.Parse(text)
|
||||
if err != nil {
|
||||
return nil, &ParseError{f, err.Error(), l}, ""
|
||||
}
|
||||
|
||||
return rr, nil, ""
|
||||
}
|
||||
|
||||
typeToparserFunc[rtype] = parserFunc{setPrivateRR, true}
|
||||
}
|
||||
|
||||
// PrivateHandleRemove removes defenitions required to support private RR type.
|
||||
func PrivateHandleRemove(rtype uint16) {
|
||||
rtypestr, ok := TypeToString[rtype]
|
||||
if ok {
|
||||
delete(typeToRR, rtype)
|
||||
delete(TypeToString, rtype)
|
||||
delete(typeToparserFunc, rtype)
|
||||
delete(StringToType, rtypestr)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
package dns_test
|
||||
|
||||
import (
|
||||
"github.com/miekg/dns"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const TypeISBN uint16 = 0x0F01
|
||||
|
||||
// A crazy new RR type :)
|
||||
type ISBN struct {
|
||||
x string // rdata with 10 or 13 numbers, dashes or spaces allowed
|
||||
}
|
||||
|
||||
func NewISBN() dns.PrivateRdata { return &ISBN{""} }
|
||||
|
||||
func (rd *ISBN) Len() int { return len([]byte(rd.x)) }
|
||||
func (rd *ISBN) String() string { return rd.x }
|
||||
|
||||
func (rd *ISBN) Parse(txt []string) error {
|
||||
rd.x = strings.TrimSpace(strings.Join(txt, " "))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *ISBN) Pack(buf []byte) (int, error) {
|
||||
b := []byte(rd.x)
|
||||
n := copy(buf, b)
|
||||
if n != len(b) {
|
||||
return n, dns.ErrBuf
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (rd *ISBN) Unpack(buf []byte) (int, error) {
|
||||
rd.x = string(buf)
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (rd *ISBN) Copy(dest dns.PrivateRdata) error {
|
||||
isbn, ok := dest.(*ISBN)
|
||||
if !ok {
|
||||
return dns.ErrRdata
|
||||
}
|
||||
isbn.x = rd.x
|
||||
return nil
|
||||
}
|
||||
|
||||
var testrecord = strings.Join([]string{"example.org.", "3600", "IN", "ISBN", "12-3 456789-0-123"}, "\t")
|
||||
|
||||
func TestPrivateText(t *testing.T) {
|
||||
dns.PrivateHandle("ISBN", TypeISBN, NewISBN)
|
||||
defer dns.PrivateHandleRemove(TypeISBN)
|
||||
|
||||
rr, err := dns.NewRR(testrecord)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if rr.String() != testrecord {
|
||||
t.Errorf("record string representation did not match original %#v != %#v", rr.String(), testrecord)
|
||||
} else {
|
||||
t.Log(rr.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateByteSlice(t *testing.T) {
|
||||
dns.PrivateHandle("ISBN", TypeISBN, NewISBN)
|
||||
defer dns.PrivateHandleRemove(TypeISBN)
|
||||
|
||||
rr, err := dns.NewRR(testrecord)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
buf := make([]byte, 100)
|
||||
off, err := dns.PackRR(rr, buf, 0, nil, false)
|
||||
if err != nil {
|
||||
t.Errorf("got error packing ISBN: %s", err)
|
||||
}
|
||||
|
||||
custrr := rr.(*dns.PrivateRR)
|
||||
if ln := custrr.Data.Len() + len(custrr.Header().Name) + 11; ln != off {
|
||||
t.Errorf("offset is not matching to length of Private RR: %d!=%d", off, ln)
|
||||
}
|
||||
|
||||
rr1, off1, err := dns.UnpackRR(buf[:off], 0)
|
||||
if err != nil {
|
||||
t.Errorf("got error unpacking ISBN: %s", err)
|
||||
}
|
||||
|
||||
if off1 != off {
|
||||
t.Errorf("Offset after unpacking differs: %d != %d", off1, off)
|
||||
}
|
||||
|
||||
if rr1.String() != testrecord {
|
||||
t.Errorf("Record string representation did not match original %#v != %#v", rr1.String(), testrecord)
|
||||
} else {
|
||||
t.Log(rr1.String())
|
||||
}
|
||||
}
|
||||
|
||||
const TypeVERSION uint16 = 0x0F02
|
||||
|
||||
type VERSION struct {
|
||||
x string
|
||||
}
|
||||
|
||||
func NewVersion() dns.PrivateRdata { return &VERSION{""} }
|
||||
|
||||
func (rd *VERSION) String() string { return rd.x }
|
||||
func (rd *VERSION) Parse(txt []string) error {
|
||||
rd.x = strings.TrimSpace(strings.Join(txt, " "))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *VERSION) Pack(buf []byte) (int, error) {
|
||||
b := []byte(rd.x)
|
||||
n := copy(buf, b)
|
||||
if n != len(b) {
|
||||
return n, dns.ErrBuf
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (rd *VERSION) Unpack(buf []byte) (int, error) {
|
||||
rd.x = string(buf)
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (rd *VERSION) Copy(dest dns.PrivateRdata) error {
|
||||
isbn, ok := dest.(*VERSION)
|
||||
if !ok {
|
||||
return dns.ErrRdata
|
||||
}
|
||||
isbn.x = rd.x
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *VERSION) Len() int {
|
||||
return len([]byte(rd.x))
|
||||
}
|
||||
|
||||
var smallzone = `$ORIGIN example.org.
|
||||
@ SOA sns.dns.icann.org. noc.dns.icann.org. (
|
||||
2014091518 7200 3600 1209600 3600
|
||||
)
|
||||
A 1.2.3.4
|
||||
ok ISBN 1231-92110-12
|
||||
go VERSION (
|
||||
1.3.1 ; comment
|
||||
)
|
||||
www ISBN 1231-92110-16
|
||||
* CNAME @
|
||||
`
|
||||
|
||||
func TestPrivateZoneParser(t *testing.T) {
|
||||
dns.PrivateHandle("ISBN", TypeISBN, NewISBN)
|
||||
dns.PrivateHandle("VERSION", TypeVERSION, NewVersion)
|
||||
defer dns.PrivateHandleRemove(TypeISBN)
|
||||
defer dns.PrivateHandleRemove(TypeVERSION)
|
||||
|
||||
r := strings.NewReader(smallzone)
|
||||
for x := range dns.ParseZone(r, ".", "") {
|
||||
if err := x.Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(x.RR)
|
||||
}
|
||||
}
|
23
zscan_rr.go
23
zscan_rr.go
|
@ -24,23 +24,20 @@ type parserFunc struct {
|
|||
// or immediately a _NEWLINE. If this is not the case we flag
|
||||
// an *ParseError: garbage after rdata.
|
||||
func setRR(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
||||
var r RR
|
||||
e := new(ParseError)
|
||||
|
||||
parserfunc, ok := typeToparserFunc[h.Rrtype]
|
||||
if ok {
|
||||
r, e, cm := parserfunc.Func(h, c, o, f)
|
||||
if parserfunc.Variable {
|
||||
r, e, _ = parserfunc.Func(h, c, o, f)
|
||||
if e != nil {
|
||||
return nil, e, ""
|
||||
}
|
||||
se, com := slurpRemainder(c, f)
|
||||
if se != nil {
|
||||
return nil, se, ""
|
||||
}
|
||||
return r, e, com
|
||||
return r, e, cm
|
||||
}
|
||||
return parserfunc.Func(h, c, o, f)
|
||||
if e != nil {
|
||||
return nil, e, ""
|
||||
}
|
||||
e, cm = slurpRemainder(c, f)
|
||||
if e != nil {
|
||||
return nil, e, ""
|
||||
}
|
||||
return r, nil, cm
|
||||
}
|
||||
// RFC3957 RR (Unknown RR handling)
|
||||
return setRFC3597(h, c, o, f)
|
||||
|
|
Loading…
Reference in New Issue