Give edns0 support as much need upgrade

Its now much nicer and more Go like. In essence ENDS0
is a sort of a mini RR parser.
This commit is contained in:
Miek Gieben 2012-05-31 18:36:27 +02:00
parent 034a182324
commit b0c12388e8
3 changed files with 86 additions and 46 deletions

87
edns.go
View File

@ -2,24 +2,20 @@ package dns
import (
"encoding/hex"
"net"
"strconv"
)
// EDNS0 Option codes.
const (
_ = iota
OptionCodeLLQ // not used
OptionCodeUL // not used
OptionCodeNSID // NSID, RFC5001
_DO = 1 << 7 // dnssec ok
_ = iota
OptionLLQ // not used
OptionUL // not used
OptionNSID // NSID, RFC5001
OptionSUBNET = 0x50fa // client-subnet draft
_DO = 1 << 7 // dnssec ok
)
// An ENDS0 option rdata element.
type Option struct {
Code uint16
Data string `dns:"hex"`
}
/*
* EDNS extended RR.
* This is the EDNS0 Header
@ -34,7 +30,7 @@ type Option struct {
type RR_OPT struct {
Hdr RR_Header
Option []Option `dns:"opt"` // tag is used in Pack and Unpack
Option []EDNS0 `dns:"opt"` // tag is used in Pack and Unpack
}
func (rr *RR_OPT) Header() *RR_Header {
@ -51,10 +47,10 @@ func (rr *RR_OPT) String() string {
s += "udp: " + strconv.Itoa(int(rr.UDPSize()))
for _, o := range rr.Option {
switch o.Code {
case OptionCodeNSID:
s += "\n; NSID: " + o.Data
h, e := hex.DecodeString(o.Data)
switch o.(type) {
case *EDNS0_NSID:
s += "\n; NSID: " + o.String()
h, e := o.Bytes()
var r string
if e == nil {
for _, c := range h {
@ -70,7 +66,8 @@ func (rr *RR_OPT) String() string {
func (rr *RR_OPT) Len() int {
l := rr.Hdr.Len()
for i := 0; i < len(rr.Option); i++ {
l += 2 + len(rr.Option[i].Data)/2
lo, _ := rr.Option[i].Bytes()
l += 2 + len(lo)
}
return l
}
@ -119,19 +116,49 @@ func (rr *RR_OPT) SetDo() {
rr.Hdr.Ttl = uint32(b1)<<24 | uint32(b2)<<16 | uint32(b3)<<8 | uint32(b4)
}
// Nsid returns the NSID as hex character string.
func (rr *RR_OPT) Nsid() string {
for i := 0; i < len(rr.Option); i++ {
if rr.Option[i].Code == OptionCodeNSID {
return "NSID: " + rr.Option[i].Data
}
}
// TODO: error or nil string?
return "Not found"
// EDNS0 defines a EDNS0 Option
type EDNS0 interface {
// Option return the option code for the option.
Option() uint16
// Bytes returns the bytes of the option data.
Bytes() ([]byte, error)
// String returns the string representation of the option.
String() string
// SetBytes sets the data as found in the packet. Is also sets
// the length of the slice as the length of the option data.
SetBytes([]byte)
}
// SetNsid sets the NSID from a hex character string.
// Use the empty string when requesting an NSID.
func (rr *RR_OPT) SetNsid(hexnsid string) {
rr.Option = append(rr.Option, Option{OptionCodeNSID, hexnsid})
type EDNS0_NSID struct {
Code uint16
Nsid string // This string must be encoded as Hex
}
func (e *EDNS0_NSID) Option() uint16 {
return e.Code
}
func (e *EDNS0_NSID) Bytes() ([]byte, error) {
h, err := hex.DecodeString(e.Nsid)
if err != nil {
return nil, err
}
return h, nil
}
func (e *EDNS0_NSID) String() string {
return string(e.Nsid)
}
func (e *EDNS0_NSID) SetBytes(b []byte) {
e.Code = OptionNSID
e.Nsid = hex.EncodeToString(b)
}
type EDNS0_SUBNET struct {
Code uint16
Family uint16
SourceNetmask uint8
SourceScope uint8
Address []net.IP
}

View File

@ -171,7 +171,10 @@ Flags:
o.SetUDPSize(dns.DefaultMsgSize)
}
if *nsid {
o.SetNsid("")
// Ask for it
e := new(dns.EDNS0_NSID)
e.SetBytes([]byte(""))
o.Option = append(o.Option, e)
}
m.Extra = append(m.Extra, o)
}

40
msg.go
View File

@ -400,23 +400,21 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str
off += len(element)
}
case "opt": // edns
// Length of the entire option section
for j := 0; j < val.Field(i).Len(); j++ {
element := val.Field(i).Index(j)
// for each code we should do something else
h, e := hex.DecodeString(string(element.Field(1).String()))
element := val.Field(i).Index(j).Interface()
b, e := element.(EDNS0).Bytes()
if e != nil {
println("dns: failure packing OTP")
return lenmsg, false
}
code := uint16(element.Field(0).Uint())
msg[off], msg[off+1] = packUint16(code)
// Option code
msg[off], msg[off+1] = packUint16(element.(EDNS0).Option())
// Length
msg[off+2], msg[off+3] = packUint16(uint16(len(string(h))))
msg[off+2], msg[off+3] = packUint16(uint16(len(b)))
off += 4
copy(msg[off:off+len(string(h))], h)
off += len(string(h))
// Actual data
copy(msg[off:off+len(b)], b)
off += len(b)
}
case "a":
// It must be a slice of 4, even if it is 16, we encode
@ -685,22 +683,34 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo
}
fv.Set(reflect.ValueOf(txt))
case "opt": // edns0
// TODO: multiple EDNS0 options
rdlength := int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint())
if rdlength == 0 {
// This is an EDNS0 (OPT Record) with no rdata
// We can savely return here.
break
}
opt := make([]Option, 1)
opt[0].Code, off = unpackUint16(msg, off)
edns := make([]EDNS0, 0)
// Goto to this place, when there is a goto
code := uint16(0)
code, off = unpackUint16(msg, off) // Overflow? TODO
optlen, off1 := unpackUint16(msg, off)
if off1+int(optlen) > off+rdlength {
println("dns: overflow unpacking OPT")
return lenmsg, false
}
opt[0].Data = hex.EncodeToString(msg[off1 : off1+int(optlen)])
fv.Set(reflect.ValueOf(opt))
off = off1 + int(optlen)
switch code {
case OptionNSID:
e := new(EDNS0_NSID)
e.SetBytes(msg[off1 : off1+int(optlen)])
edns = append(edns, e)
off = off1 + int(optlen)
case OptionSUBNET:
// ..
}
fv.Set(reflect.ValueOf(edns))
// goto ??
case "a":
if off+net.IPv4len > len(msg) {
println("dns: overflow unpacking A")