New functionality for adding private RRs to dns lib.
This commit is contained in:
parent
d353d0190a
commit
7c507e7592
|
@ -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
|
||||
}
|
|
@ -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
10
dns.go
|
@ -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
20
msg.go
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue