Export EDNS0 interface (#1041)
Replace all the private methods in the EDNS0 with public methods. Additionally, as suggested in issue #857, made Pack receive a pre-allocated byte array, introduce a Len method, and have Pack and Unpack return the number of octets written and read (respectively) if there was no error. Closes #857
This commit is contained in:
parent
22cda6dc4f
commit
a98e771ba5
290
edns.go
290
edns.go
|
@ -47,7 +47,8 @@ func (rr *OPT) String() string {
|
||||||
switch o.(type) {
|
switch o.(type) {
|
||||||
case *EDNS0_NSID:
|
case *EDNS0_NSID:
|
||||||
s += "\n; NSID: " + o.String()
|
s += "\n; NSID: " + o.String()
|
||||||
h, e := o.pack()
|
h := make([]byte, o.Len())
|
||||||
|
_, e := o.Pack(h)
|
||||||
var r string
|
var r string
|
||||||
if e == nil {
|
if e == nil {
|
||||||
for _, c := range h {
|
for _, c := range h {
|
||||||
|
@ -82,8 +83,7 @@ func (rr *OPT) len(off int, compression map[string]struct{}) int {
|
||||||
l := rr.Hdr.len(off, compression)
|
l := rr.Hdr.len(off, compression)
|
||||||
for _, o := range rr.Option {
|
for _, o := range rr.Option {
|
||||||
l += 4 // Account for 2-byte option code and 2-byte option length.
|
l += 4 // Account for 2-byte option code and 2-byte option length.
|
||||||
lo, _ := o.pack()
|
l += o.Len()
|
||||||
l += len(lo)
|
|
||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
@ -152,15 +152,19 @@ func (rr *OPT) SetDo(do ...bool) {
|
||||||
type EDNS0 interface {
|
type EDNS0 interface {
|
||||||
// Option returns the option code for the option.
|
// Option returns the option code for the option.
|
||||||
Option() uint16
|
Option() uint16
|
||||||
// pack returns the bytes of the option data.
|
// Len returns the length (number of bytes) of the option data when
|
||||||
pack() ([]byte, error)
|
// packed
|
||||||
// unpack sets the data as found in the buffer. Is also sets
|
Len() int
|
||||||
|
// Pack writes the option data into the buffer. The buffer should be at
|
||||||
|
// least Len() bytes long. Returns the number of bytes written.
|
||||||
|
Pack(b []byte) (int, error)
|
||||||
|
// Unpack sets the data as found in the buffer. Is also sets
|
||||||
// the length of the slice as the length of the option data.
|
// the length of the slice as the length of the option data.
|
||||||
unpack([]byte) error
|
Unpack([]byte) (int, error)
|
||||||
// String returns the string representation of the option.
|
// String returns the string representation of the option.
|
||||||
String() string
|
String() string
|
||||||
// copy returns a deep-copy of the option.
|
// copy returns a deep-copy of the option.
|
||||||
copy() EDNS0
|
Copy() EDNS0
|
||||||
}
|
}
|
||||||
|
|
||||||
// EDNS0_NSID option is used to retrieve a nameserver
|
// EDNS0_NSID option is used to retrieve a nameserver
|
||||||
|
@ -180,19 +184,20 @@ type EDNS0_NSID struct {
|
||||||
Nsid string // This string needs to be hex encoded
|
Nsid string // This string needs to be hex encoded
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_NSID) pack() ([]byte, error) {
|
func (e *EDNS0_NSID) Len() int {
|
||||||
h, err := hex.DecodeString(e.Nsid)
|
return hex.DecodedLen(len(e.Nsid))
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
|
||||||
}
|
func (e *EDNS0_NSID) Pack(b []byte) (int, error) {
|
||||||
return h, nil
|
nsid := []byte(e.Nsid)
|
||||||
|
return hex.Decode(nsid, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID } // Option returns the option code.
|
func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID } // Option returns the option code.
|
||||||
func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil }
|
func (e *EDNS0_NSID) Unpack(b []byte) (int, error) { e.Nsid = hex.EncodeToString(b); return len(b), nil }
|
||||||
func (e *EDNS0_NSID) String() string { return e.Nsid }
|
func (e *EDNS0_NSID) String() string { return e.Nsid }
|
||||||
func (e *EDNS0_NSID) copy() EDNS0 { return &EDNS0_NSID{e.Code, e.Nsid} }
|
func (e *EDNS0_NSID) Copy() EDNS0 { return &EDNS0_NSID{e.Code, e.Nsid} }
|
||||||
|
|
||||||
// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver
|
// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver
|
||||||
// an idea of where the client lives. See RFC 7871. It can then give back a different
|
// an idea of where the client lives. See RFC 7871. It can then give back a different
|
||||||
|
@ -225,77 +230,82 @@ type EDNS0_SUBNET struct {
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET }
|
func (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET }
|
||||||
|
|
||||||
func (e *EDNS0_SUBNET) pack() ([]byte, error) {
|
func (e *EDNS0_SUBNET) Len() int {
|
||||||
b := make([]byte, 4)
|
return 4 + int((e.SourceNetmask + 8 - 1) / 8) // division rounding up
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EDNS0_SUBNET) Pack(b []byte) (int, error) {
|
||||||
binary.BigEndian.PutUint16(b[0:], e.Family)
|
binary.BigEndian.PutUint16(b[0:], e.Family)
|
||||||
b[2] = e.SourceNetmask
|
b[2] = e.SourceNetmask
|
||||||
b[3] = e.SourceScope
|
b[3] = e.SourceScope
|
||||||
|
var copied int = 0
|
||||||
switch e.Family {
|
switch e.Family {
|
||||||
case 0:
|
case 0:
|
||||||
// "dig" sets AddressFamily to 0 if SourceNetmask is also 0
|
// "dig" sets AddressFamily to 0 if SourceNetmask is also 0
|
||||||
// We might don't need to complain either
|
// We might don't need to complain either
|
||||||
if e.SourceNetmask != 0 {
|
if e.SourceNetmask != 0 {
|
||||||
return nil, errors.New("dns: bad address family")
|
return 0, errors.New("dns: bad address family")
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
if e.SourceNetmask > net.IPv4len*8 {
|
if e.SourceNetmask > net.IPv4len*8 {
|
||||||
return nil, errors.New("dns: bad netmask")
|
return 0, errors.New("dns: bad netmask")
|
||||||
}
|
}
|
||||||
if len(e.Address.To4()) != net.IPv4len {
|
if len(e.Address.To4()) != net.IPv4len {
|
||||||
return nil, errors.New("dns: bad address")
|
return 0, errors.New("dns: bad address")
|
||||||
}
|
}
|
||||||
ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8))
|
ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8))
|
||||||
needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
|
needLength := int((e.SourceNetmask + 8 - 1) / 8) // division rounding up
|
||||||
b = append(b, ip[:needLength]...)
|
copied = copy(b[4:], ip[:needLength])
|
||||||
case 2:
|
case 2:
|
||||||
if e.SourceNetmask > net.IPv6len*8 {
|
if e.SourceNetmask > net.IPv6len*8 {
|
||||||
return nil, errors.New("dns: bad netmask")
|
return 0, errors.New("dns: bad netmask")
|
||||||
}
|
}
|
||||||
if len(e.Address) != net.IPv6len {
|
if len(e.Address) != net.IPv6len {
|
||||||
return nil, errors.New("dns: bad address")
|
return 0, errors.New("dns: bad address")
|
||||||
}
|
}
|
||||||
ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8))
|
ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8))
|
||||||
needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up
|
needLength := int((e.SourceNetmask + 8 - 1) / 8) // division rounding up
|
||||||
b = append(b, ip[:needLength]...)
|
copied = copy(b[4:], ip[:needLength])
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("dns: bad address family")
|
return 0, errors.New("dns: bad address family")
|
||||||
}
|
}
|
||||||
return b, nil
|
return 4 + copied, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_SUBNET) unpack(b []byte) error {
|
func (e *EDNS0_SUBNET) Unpack(b []byte) (int, error) {
|
||||||
if len(b) < 4 {
|
if len(b) < 4 {
|
||||||
return ErrBuf
|
return 0, ErrBuf
|
||||||
}
|
}
|
||||||
e.Family = binary.BigEndian.Uint16(b)
|
e.Family = binary.BigEndian.Uint16(b)
|
||||||
e.SourceNetmask = b[2]
|
e.SourceNetmask = b[2]
|
||||||
e.SourceScope = b[3]
|
e.SourceScope = b[3]
|
||||||
|
var l int = 0
|
||||||
switch e.Family {
|
switch e.Family {
|
||||||
case 0:
|
case 0:
|
||||||
// "dig" sets AddressFamily to 0 if SourceNetmask is also 0
|
// "dig" sets AddressFamily to 0 if SourceNetmask is also 0
|
||||||
// It's okay to accept such a packet
|
// It's okay to accept such a packet
|
||||||
if e.SourceNetmask != 0 {
|
if e.SourceNetmask != 0 {
|
||||||
return errors.New("dns: bad address family")
|
return 0, errors.New("dns: bad address family")
|
||||||
}
|
}
|
||||||
e.Address = net.IPv4(0, 0, 0, 0)
|
e.Address = net.IPv4(0, 0, 0, 0)
|
||||||
case 1:
|
case 1:
|
||||||
if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 {
|
if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 {
|
||||||
return errors.New("dns: bad netmask")
|
return 0, errors.New("dns: bad netmask")
|
||||||
}
|
}
|
||||||
addr := make(net.IP, net.IPv4len)
|
addr := make(net.IP, net.IPv4len)
|
||||||
copy(addr, b[4:])
|
l = copy(addr, b[4:])
|
||||||
e.Address = addr.To16()
|
e.Address = addr.To16()
|
||||||
case 2:
|
case 2:
|
||||||
if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 {
|
if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 {
|
||||||
return errors.New("dns: bad netmask")
|
return 0, errors.New("dns: bad netmask")
|
||||||
}
|
}
|
||||||
addr := make(net.IP, net.IPv6len)
|
addr := make(net.IP, net.IPv6len)
|
||||||
copy(addr, b[4:])
|
l = copy(addr, b[4:])
|
||||||
e.Address = addr
|
e.Address = addr
|
||||||
default:
|
default:
|
||||||
return errors.New("dns: bad address family")
|
return 0, errors.New("dns: bad address family")
|
||||||
}
|
}
|
||||||
return nil
|
return 4 + l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_SUBNET) String() (s string) {
|
func (e *EDNS0_SUBNET) String() (s string) {
|
||||||
|
@ -310,7 +320,7 @@ func (e *EDNS0_SUBNET) String() (s string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_SUBNET) copy() EDNS0 {
|
func (e *EDNS0_SUBNET) Copy() EDNS0 {
|
||||||
return &EDNS0_SUBNET{
|
return &EDNS0_SUBNET{
|
||||||
e.Code,
|
e.Code,
|
||||||
e.Family,
|
e.Family,
|
||||||
|
@ -343,19 +353,23 @@ type EDNS0_COOKIE struct {
|
||||||
Cookie string // Hex-encoded cookie data
|
Cookie string // Hex-encoded cookie data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_COOKIE) pack() ([]byte, error) {
|
func (e *EDNS0_COOKIE) Len() int {
|
||||||
h, err := hex.DecodeString(e.Cookie)
|
return hex.DecodedLen(len(e.Cookie))
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
|
||||||
}
|
func (e *EDNS0_COOKIE) Pack(b []byte) (int, error) {
|
||||||
return h, nil
|
cookie := []byte(e.Cookie)
|
||||||
|
return hex.Decode(cookie, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE }
|
func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE }
|
||||||
func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil }
|
func (e *EDNS0_COOKIE) Unpack(b []byte) (int, error) {
|
||||||
func (e *EDNS0_COOKIE) String() string { return e.Cookie }
|
e.Cookie = hex.EncodeToString(b)
|
||||||
func (e *EDNS0_COOKIE) copy() EDNS0 { return &EDNS0_COOKIE{e.Code, e.Cookie} }
|
return len(b), nil
|
||||||
|
}
|
||||||
|
func (e *EDNS0_COOKIE) String() string { return e.Cookie }
|
||||||
|
func (e *EDNS0_COOKIE) Copy() EDNS0 { return &EDNS0_COOKIE{e.Code, e.Cookie} }
|
||||||
|
|
||||||
// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set
|
// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set
|
||||||
// an expiration on an update RR. This is helpful for clients that cannot clean
|
// an expiration on an update RR. This is helpful for clients that cannot clean
|
||||||
|
@ -378,32 +392,39 @@ type EDNS0_UL struct {
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_UL) Option() uint16 { return EDNS0UL }
|
func (e *EDNS0_UL) Option() uint16 { return EDNS0UL }
|
||||||
func (e *EDNS0_UL) String() string { return fmt.Sprintf("%d %d", e.Lease, e.KeyLease) }
|
func (e *EDNS0_UL) String() string { return fmt.Sprintf("%d %d", e.Lease, e.KeyLease) }
|
||||||
func (e *EDNS0_UL) copy() EDNS0 { return &EDNS0_UL{e.Code, e.Lease, e.KeyLease} }
|
func (e *EDNS0_UL) Copy() EDNS0 { return &EDNS0_UL{e.Code, e.Lease, e.KeyLease} }
|
||||||
|
|
||||||
// Copied: http://golang.org/src/pkg/net/dnsmsg.go
|
func (e *EDNS0_UL) Len() int {
|
||||||
func (e *EDNS0_UL) pack() ([]byte, error) {
|
|
||||||
var b []byte
|
|
||||||
if e.KeyLease == 0 {
|
if e.KeyLease == 0 {
|
||||||
b = make([]byte, 4)
|
return 4
|
||||||
} else {
|
|
||||||
b = make([]byte, 8)
|
|
||||||
binary.BigEndian.PutUint32(b[4:], e.KeyLease)
|
|
||||||
}
|
}
|
||||||
binary.BigEndian.PutUint32(b, e.Lease)
|
return 8
|
||||||
return b, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_UL) unpack(b []byte) error {
|
// Copied: http://golang.org/src/pkg/net/dnsmsg.go
|
||||||
switch len(b) {
|
func (e *EDNS0_UL) Pack(b []byte) (int, error) {
|
||||||
|
l := 4
|
||||||
|
if e.KeyLease != 0 {
|
||||||
|
binary.BigEndian.PutUint32(b[4:], e.KeyLease)
|
||||||
|
l += 4
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint32(b, e.Lease)
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EDNS0_UL) Unpack(b []byte) (int, error) {
|
||||||
|
l := len(b)
|
||||||
|
switch l {
|
||||||
case 4:
|
case 4:
|
||||||
e.KeyLease = 0
|
e.KeyLease = 0
|
||||||
case 8:
|
case 8:
|
||||||
e.KeyLease = binary.BigEndian.Uint32(b[4:])
|
e.KeyLease = binary.BigEndian.Uint32(b[4:])
|
||||||
default:
|
default:
|
||||||
return ErrBuf
|
return 0, ErrBuf
|
||||||
}
|
}
|
||||||
e.Lease = binary.BigEndian.Uint32(b)
|
e.Lease = binary.BigEndian.Uint32(b)
|
||||||
return nil
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
|
// EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
|
||||||
|
@ -420,26 +441,29 @@ type EDNS0_LLQ struct {
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ }
|
func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ }
|
||||||
|
|
||||||
func (e *EDNS0_LLQ) pack() ([]byte, error) {
|
func (e *EDNS0_LLQ) Len() int {
|
||||||
b := make([]byte, 18)
|
return 18
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EDNS0_LLQ) Pack(b []byte) (int, error) {
|
||||||
binary.BigEndian.PutUint16(b[0:], e.Version)
|
binary.BigEndian.PutUint16(b[0:], e.Version)
|
||||||
binary.BigEndian.PutUint16(b[2:], e.Opcode)
|
binary.BigEndian.PutUint16(b[2:], e.Opcode)
|
||||||
binary.BigEndian.PutUint16(b[4:], e.Error)
|
binary.BigEndian.PutUint16(b[4:], e.Error)
|
||||||
binary.BigEndian.PutUint64(b[6:], e.Id)
|
binary.BigEndian.PutUint64(b[6:], e.Id)
|
||||||
binary.BigEndian.PutUint32(b[14:], e.LeaseLife)
|
binary.BigEndian.PutUint32(b[14:], e.LeaseLife)
|
||||||
return b, nil
|
return 18, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_LLQ) unpack(b []byte) error {
|
func (e *EDNS0_LLQ) Unpack(b []byte) (int, error) {
|
||||||
if len(b) < 18 {
|
if len(b) < 18 {
|
||||||
return ErrBuf
|
return 0, ErrBuf
|
||||||
}
|
}
|
||||||
e.Version = binary.BigEndian.Uint16(b[0:])
|
e.Version = binary.BigEndian.Uint16(b[0:])
|
||||||
e.Opcode = binary.BigEndian.Uint16(b[2:])
|
e.Opcode = binary.BigEndian.Uint16(b[2:])
|
||||||
e.Error = binary.BigEndian.Uint16(b[4:])
|
e.Error = binary.BigEndian.Uint16(b[4:])
|
||||||
e.Id = binary.BigEndian.Uint64(b[6:])
|
e.Id = binary.BigEndian.Uint64(b[6:])
|
||||||
e.LeaseLife = binary.BigEndian.Uint32(b[14:])
|
e.LeaseLife = binary.BigEndian.Uint32(b[14:])
|
||||||
return nil
|
return 18, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_LLQ) String() string {
|
func (e *EDNS0_LLQ) String() string {
|
||||||
|
@ -448,7 +472,7 @@ func (e *EDNS0_LLQ) String() string {
|
||||||
" " + strconv.FormatUint(uint64(e.LeaseLife), 10)
|
" " + strconv.FormatUint(uint64(e.LeaseLife), 10)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
func (e *EDNS0_LLQ) copy() EDNS0 {
|
func (e *EDNS0_LLQ) Copy() EDNS0 {
|
||||||
return &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife}
|
return &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,9 +483,13 @@ type EDNS0_DAU struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_DAU) Option() uint16 { return EDNS0DAU }
|
func (e *EDNS0_DAU) Option() uint16 { return EDNS0DAU }
|
||||||
func (e *EDNS0_DAU) pack() ([]byte, error) { return e.AlgCode, nil }
|
func (e *EDNS0_DAU) Len() int { return len(e.AlgCode) }
|
||||||
func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil }
|
func (e *EDNS0_DAU) Pack(b []byte) (int, error) { return copy(b, e.AlgCode), nil }
|
||||||
|
func (e *EDNS0_DAU) Unpack(b []byte) (int, error) {
|
||||||
|
e.AlgCode = make([]byte, len(b))
|
||||||
|
return copy(e.AlgCode, b), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *EDNS0_DAU) String() string {
|
func (e *EDNS0_DAU) String() string {
|
||||||
s := ""
|
s := ""
|
||||||
|
@ -474,7 +502,7 @@ func (e *EDNS0_DAU) String() string {
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
func (e *EDNS0_DAU) copy() EDNS0 { return &EDNS0_DAU{e.Code, e.AlgCode} }
|
func (e *EDNS0_DAU) Copy() EDNS0 { return &EDNS0_DAU{e.Code, e.AlgCode} }
|
||||||
|
|
||||||
// EDNS0_DHU implements the EDNS0 "DS Hash Understood" option. See RFC 6975.
|
// EDNS0_DHU implements the EDNS0 "DS Hash Understood" option. See RFC 6975.
|
||||||
type EDNS0_DHU struct {
|
type EDNS0_DHU struct {
|
||||||
|
@ -483,9 +511,13 @@ type EDNS0_DHU struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_DHU) Option() uint16 { return EDNS0DHU }
|
func (e *EDNS0_DHU) Option() uint16 { return EDNS0DHU }
|
||||||
func (e *EDNS0_DHU) pack() ([]byte, error) { return e.AlgCode, nil }
|
func (e *EDNS0_DHU) Len() int { return len(e.AlgCode) }
|
||||||
func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil }
|
func (e *EDNS0_DHU) Pack(b []byte) (int, error) { return copy(b, e.AlgCode), nil }
|
||||||
|
func (e *EDNS0_DHU) Unpack(b []byte) (int, error) {
|
||||||
|
e.AlgCode = make([]byte, len(b))
|
||||||
|
return copy(e.AlgCode, b), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *EDNS0_DHU) String() string {
|
func (e *EDNS0_DHU) String() string {
|
||||||
s := ""
|
s := ""
|
||||||
|
@ -498,7 +530,7 @@ func (e *EDNS0_DHU) String() string {
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
func (e *EDNS0_DHU) copy() EDNS0 { return &EDNS0_DHU{e.Code, e.AlgCode} }
|
func (e *EDNS0_DHU) Copy() EDNS0 { return &EDNS0_DHU{e.Code, e.AlgCode} }
|
||||||
|
|
||||||
// EDNS0_N3U implements the EDNS0 "NSEC3 Hash Understood" option. See RFC 6975.
|
// EDNS0_N3U implements the EDNS0 "NSEC3 Hash Understood" option. See RFC 6975.
|
||||||
type EDNS0_N3U struct {
|
type EDNS0_N3U struct {
|
||||||
|
@ -507,9 +539,13 @@ type EDNS0_N3U struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_N3U) Option() uint16 { return EDNS0N3U }
|
func (e *EDNS0_N3U) Option() uint16 { return EDNS0N3U }
|
||||||
func (e *EDNS0_N3U) pack() ([]byte, error) { return e.AlgCode, nil }
|
func (e *EDNS0_N3U) Len() int { return len(e.AlgCode) }
|
||||||
func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil }
|
func (e *EDNS0_N3U) Pack(b []byte) (int, error) { return copy(b, e.AlgCode), nil }
|
||||||
|
func (e *EDNS0_N3U) Unpack(b []byte) (int, error) {
|
||||||
|
e.AlgCode = make([]byte, len(b))
|
||||||
|
return copy(e.AlgCode, b), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *EDNS0_N3U) String() string {
|
func (e *EDNS0_N3U) String() string {
|
||||||
// Re-use the hash map
|
// Re-use the hash map
|
||||||
|
@ -523,7 +559,7 @@ func (e *EDNS0_N3U) String() string {
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} }
|
func (e *EDNS0_N3U) Copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} }
|
||||||
|
|
||||||
// EDNS0_EXPIRE implementes the EDNS0 option as described in RFC 7314.
|
// EDNS0_EXPIRE implementes the EDNS0 option as described in RFC 7314.
|
||||||
type EDNS0_EXPIRE struct {
|
type EDNS0_EXPIRE struct {
|
||||||
|
@ -534,20 +570,26 @@ type EDNS0_EXPIRE struct {
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE }
|
func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE }
|
||||||
func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) }
|
func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) }
|
||||||
func (e *EDNS0_EXPIRE) copy() EDNS0 { return &EDNS0_EXPIRE{e.Code, e.Expire} }
|
func (e *EDNS0_EXPIRE) Copy() EDNS0 { return &EDNS0_EXPIRE{e.Code, e.Expire} }
|
||||||
|
|
||||||
func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
|
func (e *EDNS0_EXPIRE) Len() int {
|
||||||
b := make([]byte, 4)
|
return 4
|
||||||
binary.BigEndian.PutUint32(b, e.Expire)
|
|
||||||
return b, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_EXPIRE) unpack(b []byte) error {
|
func (e *EDNS0_EXPIRE) Pack(b []byte) (int, error) {
|
||||||
if len(b) < 4 {
|
if len(b) < 4 {
|
||||||
return ErrBuf
|
return 0, ErrBuf
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint32(b, e.Expire)
|
||||||
|
return 4, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EDNS0_EXPIRE) Unpack(b []byte) (int, error) {
|
||||||
|
if len(b) < 4 {
|
||||||
|
return 0, ErrBuf
|
||||||
}
|
}
|
||||||
e.Expire = binary.BigEndian.Uint32(b)
|
e.Expire = binary.BigEndian.Uint32(b)
|
||||||
return nil
|
return 4, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// The EDNS0_LOCAL option is used for local/experimental purposes. The option
|
// The EDNS0_LOCAL option is used for local/experimental purposes. The option
|
||||||
|
@ -573,28 +615,31 @@ func (e *EDNS0_LOCAL) Option() uint16 { return e.Code }
|
||||||
func (e *EDNS0_LOCAL) String() string {
|
func (e *EDNS0_LOCAL) String() string {
|
||||||
return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data)
|
return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data)
|
||||||
}
|
}
|
||||||
func (e *EDNS0_LOCAL) copy() EDNS0 {
|
func (e *EDNS0_LOCAL) Copy() EDNS0 {
|
||||||
b := make([]byte, len(e.Data))
|
b := make([]byte, len(e.Data))
|
||||||
copy(b, e.Data)
|
copy(b, e.Data)
|
||||||
return &EDNS0_LOCAL{e.Code, b}
|
return &EDNS0_LOCAL{e.Code, b}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_LOCAL) pack() ([]byte, error) {
|
func (e *EDNS0_LOCAL) Len() int {
|
||||||
b := make([]byte, len(e.Data))
|
return len(e.Data)
|
||||||
copied := copy(b, e.Data)
|
|
||||||
if copied != len(e.Data) {
|
|
||||||
return nil, ErrBuf
|
|
||||||
}
|
|
||||||
return b, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_LOCAL) unpack(b []byte) error {
|
func (e *EDNS0_LOCAL) Pack(b []byte) (int, error) {
|
||||||
|
copied := copy(b, e.Data)
|
||||||
|
if copied != len(e.Data) {
|
||||||
|
return 0, ErrBuf
|
||||||
|
}
|
||||||
|
return copied, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EDNS0_LOCAL) Unpack(b []byte) (int, error) {
|
||||||
e.Data = make([]byte, len(b))
|
e.Data = make([]byte, len(b))
|
||||||
copied := copy(e.Data, b)
|
copied := copy(e.Data, b)
|
||||||
if copied != len(b) {
|
if copied != len(b) {
|
||||||
return ErrBuf
|
return 0, ErrBuf
|
||||||
}
|
}
|
||||||
return nil
|
return copied, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EDNS0_TCP_KEEPALIVE is an EDNS0 option that instructs the server to keep
|
// EDNS0_TCP_KEEPALIVE is an EDNS0 option that instructs the server to keep
|
||||||
|
@ -608,37 +653,40 @@ type EDNS0_TCP_KEEPALIVE struct {
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE }
|
func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE }
|
||||||
|
|
||||||
func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) {
|
func (e *EDNS0_TCP_KEEPALIVE) Len() int {
|
||||||
|
return int(4 + e.Length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EDNS0_TCP_KEEPALIVE) Pack(b []byte) (int, error) {
|
||||||
if e.Timeout != 0 && e.Length != 2 {
|
if e.Timeout != 0 && e.Length != 2 {
|
||||||
return nil, errors.New("dns: timeout specified but length is not 2")
|
return 0, errors.New("dns: timeout specified but length is not 2")
|
||||||
}
|
}
|
||||||
if e.Timeout == 0 && e.Length != 0 {
|
if e.Timeout == 0 && e.Length != 0 {
|
||||||
return nil, errors.New("dns: timeout not specified but length is not 0")
|
return 0, errors.New("dns: timeout not specified but length is not 0")
|
||||||
}
|
}
|
||||||
b := make([]byte, 4+e.Length)
|
|
||||||
binary.BigEndian.PutUint16(b[0:], e.Code)
|
binary.BigEndian.PutUint16(b[0:], e.Code)
|
||||||
binary.BigEndian.PutUint16(b[2:], e.Length)
|
binary.BigEndian.PutUint16(b[2:], e.Length)
|
||||||
if e.Length == 2 {
|
if e.Length == 2 {
|
||||||
binary.BigEndian.PutUint16(b[4:], e.Timeout)
|
binary.BigEndian.PutUint16(b[4:], e.Timeout)
|
||||||
}
|
}
|
||||||
return b, nil
|
return e.Len(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error {
|
func (e *EDNS0_TCP_KEEPALIVE) Unpack(b []byte) (int, error) {
|
||||||
if len(b) < 4 {
|
if len(b) < 4 {
|
||||||
return ErrBuf
|
return 0, ErrBuf
|
||||||
}
|
}
|
||||||
e.Length = binary.BigEndian.Uint16(b[2:4])
|
e.Length = binary.BigEndian.Uint16(b[2:4])
|
||||||
if e.Length != 0 && e.Length != 2 {
|
if e.Length != 0 && e.Length != 2 {
|
||||||
return errors.New("dns: length mismatch, want 0/2 but got " + strconv.FormatUint(uint64(e.Length), 10))
|
return 0, errors.New("dns: length mismatch, want 0/2 but got " + strconv.FormatUint(uint64(e.Length), 10))
|
||||||
}
|
}
|
||||||
if e.Length == 2 {
|
if e.Length == 2 {
|
||||||
if len(b) < 6 {
|
if len(b) < 6 {
|
||||||
return ErrBuf
|
return 0, ErrBuf
|
||||||
}
|
}
|
||||||
e.Timeout = binary.BigEndian.Uint16(b[4:6])
|
e.Timeout = binary.BigEndian.Uint16(b[4:6])
|
||||||
}
|
}
|
||||||
return nil
|
return e.Len(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EDNS0_TCP_KEEPALIVE) String() (s string) {
|
func (e *EDNS0_TCP_KEEPALIVE) String() (s string) {
|
||||||
|
@ -650,7 +698,7 @@ func (e *EDNS0_TCP_KEEPALIVE) String() (s string) {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (e *EDNS0_TCP_KEEPALIVE) copy() EDNS0 { return &EDNS0_TCP_KEEPALIVE{e.Code, e.Length, e.Timeout} }
|
func (e *EDNS0_TCP_KEEPALIVE) Copy() EDNS0 { return &EDNS0_TCP_KEEPALIVE{e.Code, e.Length, e.Timeout} }
|
||||||
|
|
||||||
// EDNS0_PADDING option is used to add padding to a request/response. The default
|
// EDNS0_PADDING option is used to add padding to a request/response. The default
|
||||||
// value of padding SHOULD be 0x0 but other values MAY be used, for instance if
|
// value of padding SHOULD be 0x0 but other values MAY be used, for instance if
|
||||||
|
@ -660,11 +708,15 @@ type EDNS0_PADDING struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option implements the EDNS0 interface.
|
// Option implements the EDNS0 interface.
|
||||||
func (e *EDNS0_PADDING) Option() uint16 { return EDNS0PADDING }
|
func (e *EDNS0_PADDING) Option() uint16 { return EDNS0PADDING }
|
||||||
func (e *EDNS0_PADDING) pack() ([]byte, error) { return e.Padding, nil }
|
func (e *EDNS0_PADDING) Len() int { return len(e.Padding) }
|
||||||
func (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = b; return nil }
|
func (e *EDNS0_PADDING) Pack(b []byte) (int, error) { return copy(b, e.Padding), nil }
|
||||||
func (e *EDNS0_PADDING) String() string { return fmt.Sprintf("%0X", e.Padding) }
|
func (e *EDNS0_PADDING) Unpack(b []byte) (int, error) {
|
||||||
func (e *EDNS0_PADDING) copy() EDNS0 {
|
e.Padding = make([]byte, len(b))
|
||||||
|
return copy(e.Padding, b), nil
|
||||||
|
}
|
||||||
|
func (e *EDNS0_PADDING) String() string { return fmt.Sprintf("%0X", e.Padding) }
|
||||||
|
func (e *EDNS0_PADDING) Copy() EDNS0 {
|
||||||
b := make([]byte, len(e.Padding))
|
b := make([]byte, len(e.Padding))
|
||||||
copy(b, e.Padding)
|
copy(b, e.Padding)
|
||||||
return &EDNS0_PADDING{b}
|
return &EDNS0_PADDING{b}
|
||||||
|
|
10
edns_test.go
10
edns_test.go
|
@ -94,13 +94,14 @@ func TestEDNS0_SUBNETUnpack(t *testing.T) {
|
||||||
s1.SourceNetmask = net.IPv4len * 8
|
s1.SourceNetmask = net.IPv4len * 8
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := s1.pack()
|
b := make([]byte, s1.Len())
|
||||||
|
_, err := s1.Pack(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to pack: %v", err)
|
t.Fatalf("failed to pack: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var s2 EDNS0_SUBNET
|
var s2 EDNS0_SUBNET
|
||||||
if err := s2.unpack(b); err != nil {
|
if _, err := s2.Unpack(b); err != nil {
|
||||||
t.Fatalf("failed to unpack: %v", err)
|
t.Fatalf("failed to unpack: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,12 +121,13 @@ func TestEDNS0_UL(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
expect := EDNS0_UL{EDNS0UL, c.l, c.kl}
|
expect := EDNS0_UL{EDNS0UL, c.l, c.kl}
|
||||||
b, err := expect.pack()
|
b := make([]byte, expect.Len())
|
||||||
|
_, err := expect.Pack(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to pack: %v", err)
|
t.Fatalf("failed to pack: %v", err)
|
||||||
}
|
}
|
||||||
actual := EDNS0_UL{EDNS0UL, ^uint32(0), ^uint32(0)}
|
actual := EDNS0_UL{EDNS0UL, ^uint32(0), ^uint32(0)}
|
||||||
if err := actual.unpack(b); err != nil {
|
if _, err := actual.Unpack(b); err != nil {
|
||||||
t.Fatalf("failed to unpack: %v", err)
|
t.Fatalf("failed to unpack: %v", err)
|
||||||
}
|
}
|
||||||
if expect != actual {
|
if expect != actual {
|
||||||
|
|
|
@ -426,63 +426,63 @@ Option:
|
||||||
switch code {
|
switch code {
|
||||||
case EDNS0NSID:
|
case EDNS0NSID:
|
||||||
e := new(EDNS0_NSID)
|
e := new(EDNS0_NSID)
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
if _, err := e.Unpack(msg[off : off+int(optlen)]); err != nil {
|
||||||
return nil, len(msg), err
|
return nil, len(msg), err
|
||||||
}
|
}
|
||||||
edns = append(edns, e)
|
edns = append(edns, e)
|
||||||
off += int(optlen)
|
off += int(optlen)
|
||||||
case EDNS0SUBNET:
|
case EDNS0SUBNET:
|
||||||
e := new(EDNS0_SUBNET)
|
e := new(EDNS0_SUBNET)
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
if _, err := e.Unpack(msg[off : off+int(optlen)]); err != nil {
|
||||||
return nil, len(msg), err
|
return nil, len(msg), err
|
||||||
}
|
}
|
||||||
edns = append(edns, e)
|
edns = append(edns, e)
|
||||||
off += int(optlen)
|
off += int(optlen)
|
||||||
case EDNS0COOKIE:
|
case EDNS0COOKIE:
|
||||||
e := new(EDNS0_COOKIE)
|
e := new(EDNS0_COOKIE)
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
if _, err := e.Unpack(msg[off : off+int(optlen)]); err != nil {
|
||||||
return nil, len(msg), err
|
return nil, len(msg), err
|
||||||
}
|
}
|
||||||
edns = append(edns, e)
|
edns = append(edns, e)
|
||||||
off += int(optlen)
|
off += int(optlen)
|
||||||
case EDNS0UL:
|
case EDNS0UL:
|
||||||
e := new(EDNS0_UL)
|
e := new(EDNS0_UL)
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
if _, err := e.Unpack(msg[off : off+int(optlen)]); err != nil {
|
||||||
return nil, len(msg), err
|
return nil, len(msg), err
|
||||||
}
|
}
|
||||||
edns = append(edns, e)
|
edns = append(edns, e)
|
||||||
off += int(optlen)
|
off += int(optlen)
|
||||||
case EDNS0LLQ:
|
case EDNS0LLQ:
|
||||||
e := new(EDNS0_LLQ)
|
e := new(EDNS0_LLQ)
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
if _, err := e.Unpack(msg[off : off+int(optlen)]); err != nil {
|
||||||
return nil, len(msg), err
|
return nil, len(msg), err
|
||||||
}
|
}
|
||||||
edns = append(edns, e)
|
edns = append(edns, e)
|
||||||
off += int(optlen)
|
off += int(optlen)
|
||||||
case EDNS0DAU:
|
case EDNS0DAU:
|
||||||
e := new(EDNS0_DAU)
|
e := new(EDNS0_DAU)
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
if _, err := e.Unpack(msg[off : off+int(optlen)]); err != nil {
|
||||||
return nil, len(msg), err
|
return nil, len(msg), err
|
||||||
}
|
}
|
||||||
edns = append(edns, e)
|
edns = append(edns, e)
|
||||||
off += int(optlen)
|
off += int(optlen)
|
||||||
case EDNS0DHU:
|
case EDNS0DHU:
|
||||||
e := new(EDNS0_DHU)
|
e := new(EDNS0_DHU)
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
if _, err := e.Unpack(msg[off : off+int(optlen)]); err != nil {
|
||||||
return nil, len(msg), err
|
return nil, len(msg), err
|
||||||
}
|
}
|
||||||
edns = append(edns, e)
|
edns = append(edns, e)
|
||||||
off += int(optlen)
|
off += int(optlen)
|
||||||
case EDNS0N3U:
|
case EDNS0N3U:
|
||||||
e := new(EDNS0_N3U)
|
e := new(EDNS0_N3U)
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
if _, err := e.Unpack(msg[off : off+int(optlen)]); err != nil {
|
||||||
return nil, len(msg), err
|
return nil, len(msg), err
|
||||||
}
|
}
|
||||||
edns = append(edns, e)
|
edns = append(edns, e)
|
||||||
off += int(optlen)
|
off += int(optlen)
|
||||||
case EDNS0PADDING:
|
case EDNS0PADDING:
|
||||||
e := new(EDNS0_PADDING)
|
e := new(EDNS0_PADDING)
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
if _, err := e.Unpack(msg[off : off+int(optlen)]); err != nil {
|
||||||
return nil, len(msg), err
|
return nil, len(msg), err
|
||||||
}
|
}
|
||||||
edns = append(edns, e)
|
edns = append(edns, e)
|
||||||
|
@ -490,7 +490,7 @@ Option:
|
||||||
default:
|
default:
|
||||||
e := new(EDNS0_LOCAL)
|
e := new(EDNS0_LOCAL)
|
||||||
e.Code = code
|
e.Code = code
|
||||||
if err := e.unpack(msg[off : off+int(optlen)]); err != nil {
|
if _, err := e.Unpack(msg[off : off+int(optlen)]); err != nil {
|
||||||
return nil, len(msg), err
|
return nil, len(msg), err
|
||||||
}
|
}
|
||||||
edns = append(edns, e)
|
edns = append(edns, e)
|
||||||
|
@ -506,7 +506,8 @@ Option:
|
||||||
|
|
||||||
func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) {
|
func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) {
|
||||||
for _, el := range options {
|
for _, el := range options {
|
||||||
b, err := el.pack()
|
b := make([]byte, el.Len())
|
||||||
|
_, err := el.Pack(b)
|
||||||
if err != nil || off+4 > len(msg) {
|
if err != nil || off+4 > len(msg) {
|
||||||
return len(msg), &Error{err: "overflow packing opt"}
|
return len(msg), &Error{err: "overflow packing opt"}
|
||||||
}
|
}
|
||||||
|
|
|
@ -805,7 +805,7 @@ func (rr *OPENPGPKEY) copy() RR {
|
||||||
func (rr *OPT) copy() RR {
|
func (rr *OPT) copy() RR {
|
||||||
Option := make([]EDNS0, len(rr.Option))
|
Option := make([]EDNS0, len(rr.Option))
|
||||||
for i, e := range rr.Option {
|
for i, e := range rr.Option {
|
||||||
Option[i] = e.copy()
|
Option[i] = e.Copy()
|
||||||
}
|
}
|
||||||
return &OPT{rr.Hdr, Option}
|
return &OPT{rr.Hdr, Option}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue