diff --git a/edns.go b/edns.go index 4191057f..4ca712a7 100644 --- a/edns.go +++ b/edns.go @@ -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 } diff --git a/ex/q/q.go b/ex/q/q.go index a720aef1..ba6932f1 100644 --- a/ex/q/q.go +++ b/ex/q/q.go @@ -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) } diff --git a/msg.go b/msg.go index 63006cfa..1d5f2004 100644 --- a/msg.go +++ b/msg.go @@ -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")