Added docstrings, tests and example for PrivateRRs.
(renamed CustomRR to PrivateRR)
This commit is contained in:
parent
7c507e7592
commit
5b8552609c
56
customrr.go
56
customrr.go
|
@ -1,56 +1,69 @@
|
|||
package dns
|
||||
|
||||
type CustomRData interface {
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PrivateRData is an interface to implement non-RFC dictated resource records. See also dns.PrivateRR, dns.RegisterPrivateRR and dns.UnregisterPrivateRR
|
||||
type PrivateRData interface {
|
||||
String() string
|
||||
ReadText([]string) error
|
||||
Write([]byte) (int, error)
|
||||
Read([]byte) (int, error)
|
||||
CopyTo(CustomRData) error
|
||||
CopyTo(PrivateRData) error
|
||||
RdataLen() int
|
||||
}
|
||||
|
||||
// CopyTo needs to be here to avoid write/read pass that would require make([]byte, xxxxxx)
|
||||
|
||||
type CustomRR struct {
|
||||
// PrivateRR represents RR that uses PrivateRData user-defined type. It mocks normal RRs and implements dns.RR interface.
|
||||
type PrivateRR struct {
|
||||
Hdr RR_Header
|
||||
Data CustomRData
|
||||
Data PrivateRData
|
||||
}
|
||||
|
||||
func (r *CustomRR) Header() *RR_Header { return &r.Hdr }
|
||||
func (r *CustomRR) String() string { return r.Hdr.String() + r.Data.String() }
|
||||
func (r *CustomRR) copy() RR {
|
||||
// Header returns Private RR header.
|
||||
func (r *PrivateRR) Header() *RR_Header { return &r.Hdr }
|
||||
|
||||
// String returns text representation of a Private Resource Record.
|
||||
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.RdataLen() }
|
||||
func (r *PrivateRR) copy() RR {
|
||||
// make new RR like this:
|
||||
rrfunc, ok := typeToRR[r.Hdr.Rrtype]
|
||||
if !ok {
|
||||
panic("dns: invalid operation with custom RR " + r.Hdr.String())
|
||||
panic("dns: invalid operation with Private RR " + r.Hdr.String())
|
||||
}
|
||||
rr := rrfunc()
|
||||
r.Header().CopyTo(rr)
|
||||
|
||||
rrcust, ok := rr.(*CustomRR)
|
||||
rrcust, ok := rr.(*PrivateRR)
|
||||
if !ok {
|
||||
panic("dns: custom RR generator returned wrong interface value")
|
||||
panic("dns: Private RR generator returned wrong interface value")
|
||||
}
|
||||
|
||||
err := r.Data.CopyTo(rrcust.Data)
|
||||
if err != nil {
|
||||
panic("dns: got value that could not be used to copy custom rdata")
|
||||
panic("dns: got value that could not be used to copy Private rdata")
|
||||
}
|
||||
|
||||
return rr
|
||||
}
|
||||
|
||||
func (r *CustomRR) len() int { panic("TODO: WHERE THIS IS USED?"); return 0 }
|
||||
// RegisterPrivateRR adds support for user-defined resource record type to internals of dns library. Requires
|
||||
// string and numeric representation of RR type and generator function as argument.
|
||||
func RegisterPrivateRR(rtypestr string, rtype uint16, generator func() PrivateRData) {
|
||||
rtypestr = strings.ToUpper(rtypestr)
|
||||
|
||||
func RegisterCustomRR(rtypestr string, rtype uint16, generator func() CustomRData) {
|
||||
typeToRR[rtype] = func() RR { return &CustomRR{RR_Header{}, generator()} }
|
||||
typeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
|
||||
TypeToString[rtype] = rtypestr
|
||||
StringToType[rtypestr] = rtype
|
||||
|
||||
setCustomRR := func(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
||||
setPrivateRR := func(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
|
||||
rrfunc := typeToRR[h.Rrtype]
|
||||
rr, ok := rrfunc().(*CustomRR)
|
||||
rr, ok := rrfunc().(*PrivateRR)
|
||||
if !ok {
|
||||
panic("dns: invalid handler registered for custom RR " + rtypestr)
|
||||
panic("dns: invalid handler registered for Private RR " + rtypestr)
|
||||
}
|
||||
h.CopyTo(rr)
|
||||
|
||||
|
@ -75,10 +88,11 @@ func RegisterCustomRR(rtypestr string, rtype uint16, generator func() CustomRDat
|
|||
return rr, nil, ""
|
||||
}
|
||||
|
||||
typeToparserFunc[rtype] = parserFunc{setCustomRR, false}
|
||||
typeToparserFunc[rtype] = parserFunc{setPrivateRR, false}
|
||||
}
|
||||
|
||||
func UnregisterCustomRR(rtype uint16) {
|
||||
// UnregisterPrivateRR removes defenitions required to support user RR type.
|
||||
func UnregisterPrivateRR(rtype uint16) {
|
||||
rtypestr, ok := TypeToString[rtype]
|
||||
if ok {
|
||||
delete(typeToRR, rtype)
|
||||
|
|
|
@ -14,12 +14,13 @@ type ISBN struct {
|
|||
x string // rdata with 10 or 13 numbers, dashes or spaces allowed
|
||||
}
|
||||
|
||||
func (rd *ISBN) String() string { return rd.x }
|
||||
func (rd *ISBN) ReadText(txt []string) error { rd.x = strings.Join(txt, " "); return nil }
|
||||
func NewISBN() dns.PrivateRData { return &ISBN{""} }
|
||||
|
||||
func NewISBN() dns.CustomRData { return &ISBN{""} }
|
||||
|
||||
var testrecord = "example.org.\t3600\tIN\tISBN\t12-3 456789-0-123"
|
||||
func (rd *ISBN) String() string { return rd.x }
|
||||
func (rd *ISBN) ReadText(txt []string) error {
|
||||
rd.x = strings.TrimSpace(strings.Join(txt, " "))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *ISBN) Write(buf []byte) (int, error) {
|
||||
b := []byte(rd.x)
|
||||
|
@ -35,7 +36,7 @@ func (rd *ISBN) Read(buf []byte) (int, error) {
|
|||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (rd *ISBN) CopyTo(dest dns.CustomRData) error {
|
||||
func (rd *ISBN) CopyTo(dest dns.PrivateRData) error {
|
||||
isbn, ok := dest.(*ISBN)
|
||||
if !ok {
|
||||
return dns.ErrRdata
|
||||
|
@ -44,9 +45,15 @@ func (rd *ISBN) CopyTo(dest dns.CustomRData) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func TestCustomText(t *testing.T) {
|
||||
dns.RegisterCustomRR("ISBN", TypeISBN, NewISBN)
|
||||
defer dns.UnregisterCustomRR(TypeISBN)
|
||||
func (rd *ISBN) RdataLen() int {
|
||||
return len([]byte(rd.x))
|
||||
}
|
||||
|
||||
var testrecord = "example.org.\t3600\tIN\tISBN\t12-3 456789-0-123"
|
||||
|
||||
func TestPrivateText(t *testing.T) {
|
||||
dns.RegisterPrivateRR("ISBN", TypeISBN, NewISBN)
|
||||
defer dns.UnregisterPrivateRR(TypeISBN)
|
||||
|
||||
rr, err := dns.NewRR(testrecord)
|
||||
if err != nil {
|
||||
|
@ -59,9 +66,9 @@ func TestCustomText(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCustomWire(t *testing.T) {
|
||||
dns.RegisterCustomRR("ISBN", TypeISBN, NewISBN)
|
||||
defer dns.UnregisterCustomRR(TypeISBN)
|
||||
func TestPrivateWire(t *testing.T) {
|
||||
dns.RegisterPrivateRR("ISBN", TypeISBN, NewISBN)
|
||||
defer dns.UnregisterPrivateRR(TypeISBN)
|
||||
|
||||
rr, err := dns.NewRR(testrecord)
|
||||
if err != nil {
|
||||
|
@ -74,8 +81,9 @@ func TestCustomWire(t *testing.T) {
|
|||
t.Errorf("Got error packing ISBN: %s", err)
|
||||
}
|
||||
|
||||
if ln := 40; ln != off {
|
||||
t.Errorf("Offset is not matching to length of custom RR: %d!=%d", off, ln)
|
||||
custrr := rr.(*dns.PrivateRR)
|
||||
if ln := custrr.Data.RdataLen() + 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)
|
||||
|
@ -94,18 +102,66 @@ func TestCustomWire(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
const TypeVERSION uint16 = 0x0F02
|
||||
|
||||
type VERSION struct {
|
||||
x string // to make it simpler it's just as simple as ISBN but ignoring
|
||||
}
|
||||
|
||||
func NewVersion() dns.PrivateRData { return &VERSION{""} }
|
||||
|
||||
func (rd *VERSION) String() string { return rd.x }
|
||||
func (rd *VERSION) ReadText(txt []string) error {
|
||||
rd.x = strings.TrimSpace(strings.Join(txt, " "))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *VERSION) Write(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) Read(buf []byte) (int, error) {
|
||||
rd.x = string(buf)
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (rd *VERSION) CopyTo(dest dns.PrivateRData) error {
|
||||
isbn, ok := dest.(*VERSION)
|
||||
if !ok {
|
||||
return dns.ErrRdata
|
||||
}
|
||||
isbn.x = rd.x
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rd *VERSION) RdataLen() 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
|
||||
@ 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 ISBN 1231-92110-13
|
||||
go VERSION (
|
||||
1.3.1 ; comment
|
||||
)
|
||||
www ISBN 1231-92110-16
|
||||
* CNAME @
|
||||
`
|
||||
|
||||
func TestCustomZoneParser(t *testing.T) {
|
||||
dns.RegisterCustomRR("ISBN", TypeISBN, NewISBN)
|
||||
defer dns.UnregisterCustomRR(TypeISBN)
|
||||
func TestPrivateZoneParser(t *testing.T) {
|
||||
dns.RegisterPrivateRR("ISBN", TypeISBN, NewISBN)
|
||||
dns.RegisterPrivateRR("VERSION", TypeVERSION, NewVersion)
|
||||
defer dns.UnregisterPrivateRR(TypeISBN)
|
||||
defer dns.UnregisterPrivateRR(TypeVERSION)
|
||||
|
||||
r := strings.NewReader(smallzone)
|
||||
for x := range dns.ParseZone(r, ".", "") {
|
||||
if err := x.Error; err != nil {
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package dns_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/miekg/dns"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
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) ReadText(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) Write(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) Read(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) CopyTo(dest dns.PrivateRData) error {
|
||||
cp := make([]byte, rd.RdataLen())
|
||||
_, err := rd.Write(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) RdataLen() int {
|
||||
return net.IPv4len * 2
|
||||
}
|
||||
|
||||
func ExampleRegisterPrivateRR() {
|
||||
dns.RegisterPrivateRR("APAIR", TypeAPAIR, NewAPAIR)
|
||||
defer dns.UnregisterPrivateRR(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
msg.go
4
msg.go
|
@ -577,7 +577,7 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str
|
|||
default:
|
||||
return lenmsg, &Error{err: "bad kind packing"}
|
||||
case reflect.Interface:
|
||||
if data, ok := fv.Interface().(CustomRData); ok {
|
||||
if data, ok := fv.Interface().(PrivateRData); ok {
|
||||
n, err := data.Write(msg[off:])
|
||||
if err != nil {
|
||||
return lenmsg, err
|
||||
|
@ -880,7 +880,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
|||
default:
|
||||
return lenmsg, &Error{err: "bad kind unpacking"}
|
||||
case reflect.Interface:
|
||||
if data, ok := fv.Interface().(CustomRData); ok {
|
||||
if data, ok := fv.Interface().(PrivateRData); ok {
|
||||
n, err := data.Read(msg[off:rdend])
|
||||
if err != nil {
|
||||
return lenmsg, err
|
||||
|
|
Loading…
Reference in New Issue