New functionality for adding private RRs to dns lib.

This commit is contained in:
Alex Sergeyev 2014-09-18 23:04:53 -04:00
parent d353d0190a
commit 7c507e7592
4 changed files with 236 additions and 0 deletions

90
customrr.go Normal file
View File

@ -0,0 +1,90 @@
package dns
type CustomRData interface {
String() string
ReadText([]string) error
Write([]byte) (int, error)
Read([]byte) (int, error)
CopyTo(CustomRData) error
}
// CopyTo needs to be here to avoid write/read pass that would require make([]byte, xxxxxx)
type CustomRR struct {
Hdr RR_Header
Data CustomRData
}
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 {
// make new RR like this:
rrfunc, ok := typeToRR[r.Hdr.Rrtype]
if !ok {
panic("dns: invalid operation with custom RR " + r.Hdr.String())
}
rr := rrfunc()
r.Header().CopyTo(rr)
rrcust, ok := rr.(*CustomRR)
if !ok {
panic("dns: custom 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")
}
return rr
}
func (r *CustomRR) len() int { panic("TODO: WHERE THIS IS USED?"); return 0 }
func RegisterCustomRR(rtypestr string, rtype uint16, generator func() CustomRData) {
typeToRR[rtype] = func() RR { return &CustomRR{RR_Header{}, generator()} }
TypeToString[rtype] = rtypestr
StringToType[rtypestr] = rtype
setCustomRR := func(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rrfunc := typeToRR[h.Rrtype]
rr, ok := rrfunc().(*CustomRR)
if !ok {
panic("dns: invalid handler registered for custom RR " + rtypestr)
}
h.CopyTo(rr)
var l lex
text := make([]string, 0)
for end := false; !end; {
switch l = <-c; l.value {
case _NEWLINE, _EOF:
end = true
case _STRING:
text = append(text, l.token)
case _BLANK:
continue
}
}
err := rr.Data.ReadText(text)
if err != nil {
return nil, &ParseError{f, err.Error(), l}, ""
}
return rr, nil, ""
}
typeToparserFunc[rtype] = parserFunc{setCustomRR, false}
}
func UnregisterCustomRR(rtype uint16) {
rtypestr, ok := TypeToString[rtype]
if ok {
delete(typeToRR, rtype)
delete(TypeToString, rtype)
delete(typeToparserFunc, rtype)
delete(StringToType, rtypestr)
}
return
}

116
customrr_test.go Normal file
View File

@ -0,0 +1,116 @@
package dns_test
import (
"github.com/miekg/dns"
"strings"
"testing"
)
const TypeISBN uint16 = 0x0F01
// sorry DNS RFC writers, here we go with crazy idea test :)
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.CustomRData { return &ISBN{""} }
var testrecord = "example.org.\t3600\tIN\tISBN\t12-3 456789-0-123"
func (rd *ISBN) 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 *ISBN) Read(buf []byte) (int, error) {
rd.x = string(buf)
return len(buf), nil
}
func (rd *ISBN) CopyTo(dest dns.CustomRData) error {
isbn, ok := dest.(*ISBN)
if !ok {
return dns.ErrRdata
}
isbn.x = rd.x
return nil
}
func TestCustomText(t *testing.T) {
dns.RegisterCustomRR("ISBN", TypeISBN, NewISBN)
defer dns.UnregisterCustomRR(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 TestCustomWire(t *testing.T) {
dns.RegisterCustomRR("ISBN", TypeISBN, NewISBN)
defer dns.UnregisterCustomRR(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)
}
if ln := 40; ln != off {
t.Errorf("Offset is not matching to length of custom 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())
}
}
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 ISBN 1231-92110-13
www ISBN 1231-92110-16
* CNAME @
`
func TestCustomZoneParser(t *testing.T) {
dns.RegisterCustomRR("ISBN", TypeISBN, NewISBN)
defer dns.UnregisterCustomRR(TypeISBN)
r := strings.NewReader(smallzone)
for x := range dns.ParseZone(r, ".", "") {
if err := x.Error; err != nil {
t.Fatal(err)
}
t.Log(x.RR)
}
}

10
dns.go
View File

@ -154,6 +154,16 @@ func (h *RR_Header) copyHeader() *RR_Header {
return r
}
func (h *RR_Header) CopyTo(rr RR) *RR_Header {
rrh := rr.Header()
rrh.Name = h.Name
rrh.Rrtype = h.Rrtype
rrh.Class = h.Class
rrh.Ttl = h.Ttl
rrh.Rdlength = h.Rdlength
return rrh
}
func (h *RR_Header) String() string {
var s string

20
msg.go
View File

@ -576,6 +576,16 @@ 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:
if data, ok := fv.Interface().(CustomRData); ok {
n, err := data.Write(msg[off:])
if err != nil {
return lenmsg, err
}
off += n
} else {
return lenmsg, &Error{err: "bad kind interface packing"}
}
case reflect.Slice:
switch typefield.Tag {
default:
@ -869,6 +879,16 @@ 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:
if data, ok := fv.Interface().(CustomRData); ok {
n, err := data.Read(msg[off:rdend])
if err != nil {
return lenmsg, err
}
off += n
} else {
return lenmsg, &Error{err: "bad kind interface unpacking"}
}
case reflect.Slice:
switch val.Type().Field(i).Tag {
default: