summaryrefslogtreecommitdiffhomepage
path: root/dhcpv6
diff options
context:
space:
mode:
authorChristopher Koch <c@chrisko.ch>2019-01-20 04:47:08 +0000
committerinsomniac <insomniacslk@users.noreply.github.com>2019-01-26 23:34:26 +0000
commit3478513076477d0f19eaeaf441f29949a9f6bc92 (patch)
tree4d621abff002cef480901a2ef015620472293db3 /dhcpv6
parente62883f5b5683ae2259ad01b7ffbe20671deb6b2 (diff)
dhcpv6: easier option parsing
- move option parsing to uio buffer library. - move option code and length reading into FromBytes rather than implementing it in each OptionParser.
Diffstat (limited to 'dhcpv6')
-rw-r--r--dhcpv6/option_archtype.go36
-rw-r--r--dhcpv6/option_bootfileurl.go21
-rw-r--r--dhcpv6/option_clientid.go26
-rw-r--r--dhcpv6/option_dnsrecursivenameserver.go36
-rw-r--r--dhcpv6/option_domainsearchlist.go25
-rw-r--r--dhcpv6/option_elapsedtime.go31
-rw-r--r--dhcpv6/option_iaaddress.go45
-rw-r--r--dhcpv6/option_iaprefix.go51
-rw-r--r--dhcpv6/option_interfaceid.go23
-rw-r--r--dhcpv6/option_nii.go40
-rw-r--r--dhcpv6/option_nontemporaryaddress.go50
-rw-r--r--dhcpv6/option_prefixdelegation.go37
-rw-r--r--dhcpv6/option_relaymsg.go15
-rw-r--r--dhcpv6/option_remoteid.go36
-rw-r--r--dhcpv6/option_requestedoption.go36
-rw-r--r--dhcpv6/option_serverid.go28
-rw-r--r--dhcpv6/option_statuscode.go32
-rw-r--r--dhcpv6/option_userclass.go51
-rw-r--r--dhcpv6/option_vendor_opts.go84
-rw-r--r--dhcpv6/option_vendor_opts_test.go25
-rw-r--r--dhcpv6/option_vendorclass.go46
-rw-r--r--dhcpv6/options.go76
22 files changed, 360 insertions, 490 deletions
diff --git a/dhcpv6/option_archtype.go b/dhcpv6/option_archtype.go
index 058754a..b3d36a4 100644
--- a/dhcpv6/option_archtype.go
+++ b/dhcpv6/option_archtype.go
@@ -1,17 +1,17 @@
package dhcpv6
-// This module defines the OptClientArchType structure.
-// https://www.ietf.org/rfc/rfc5970.txt
-
import (
- "encoding/binary"
"fmt"
"strings"
"github.com/insomniacslk/dhcp/iana"
+ "github.com/u-root/u-root/pkg/uio"
)
// OptClientArchType represents an option CLIENT_ARCH_TYPE
+//
+// This module defines the OptClientArchType structure.
+// https://www.ietf.org/rfc/rfc5970.txt
type OptClientArchType struct {
ArchTypes []iana.Arch
}
@@ -20,20 +20,19 @@ func (op *OptClientArchType) Code() OptionCode {
return OptionClientArchType
}
+// ToBytes marshals the client arch type as defined by RFC 5970.
func (op *OptClientArchType) ToBytes() []byte {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionClientArchType))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- u16 := make([]byte, 2)
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionClientArchType))
+ buf.Write16(uint16(op.Length()))
for _, at := range op.ArchTypes {
- binary.BigEndian.PutUint16(u16, uint16(at))
- buf = append(buf, u16...)
+ buf.Write16(uint16(at))
}
- return buf
+ return buf.Data()
}
func (op *OptClientArchType) Length() int {
- return 2*len(op.ArchTypes)
+ return 2 * len(op.ArchTypes)
}
func (op *OptClientArchType) String() string {
@@ -48,13 +47,10 @@ func (op *OptClientArchType) String() string {
// a sequence of bytes The input data does not include option code and
// length bytes.
func ParseOptClientArchType(data []byte) (*OptClientArchType, error) {
- opt := OptClientArchType{}
- if len(data) == 0 || len(data)%2 != 0 {
- return nil, fmt.Errorf("Invalid arch type data length. Expected multiple of 2 larger than 2, got %v", len(data))
- }
- for idx := 0; idx < len(data); idx += 2 {
- b := data[idx : idx+2]
- opt.ArchTypes = append(opt.ArchTypes, iana.Arch(binary.BigEndian.Uint16(b)))
+ var opt OptClientArchType
+ buf := uio.NewBigEndianBuffer(data)
+ for buf.Has(2) {
+ opt.ArchTypes = append(opt.ArchTypes, iana.Arch(buf.Read16()))
}
- return &opt, nil
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/option_bootfileurl.go b/dhcpv6/option_bootfileurl.go
index d2da0c3..e0ad63e 100644
--- a/dhcpv6/option_bootfileurl.go
+++ b/dhcpv6/option_bootfileurl.go
@@ -1,14 +1,15 @@
package dhcpv6
-// This module defines the OptBootFileURL structure.
-// https://www.ietf.org/rfc/rfc5970.txt
-
import (
- "encoding/binary"
"fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
)
// OptBootFileURL implements the OptionBootfileURL option
+//
+// This module defines the OptBootFileURL structure.
+// https://www.ietf.org/rfc/rfc5970.txt
type OptBootFileURL struct {
BootFileURL []byte
}
@@ -20,11 +21,11 @@ func (op *OptBootFileURL) Code() OptionCode {
// ToBytes serializes the option and returns it as a sequence of bytes
func (op *OptBootFileURL) ToBytes() []byte {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionBootfileURL))
- binary.BigEndian.PutUint16(buf[2:4], uint16(len(op.BootFileURL)))
- buf = append(buf, op.BootFileURL...)
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionBootfileURL))
+ buf.Write16(uint16(len(op.BootFileURL)))
+ buf.WriteBytes(op.BootFileURL)
+ return buf.Data()
}
// Length returns the option length in bytes
@@ -39,7 +40,7 @@ func (op *OptBootFileURL) String() string {
// ParseOptBootFileURL builds an OptBootFileURL structure from a sequence
// of bytes. The input data does not include option code and length bytes.
func ParseOptBootFileURL(data []byte) (*OptBootFileURL, error) {
- opt := OptBootFileURL{}
+ var opt OptBootFileURL
opt.BootFileURL = append([]byte(nil), data...)
return &opt, nil
}
diff --git a/dhcpv6/option_clientid.go b/dhcpv6/option_clientid.go
index fc68ae7..92ceb24 100644
--- a/dhcpv6/option_clientid.go
+++ b/dhcpv6/option_clientid.go
@@ -1,14 +1,15 @@
package dhcpv6
-// This module defines the OptClientId and DUID structures.
-// https://www.ietf.org/rfc/rfc3315.txt
-
import (
- "encoding/binary"
"fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
)
// OptClientId represents a Client ID option
+//
+// This module defines the OptClientId and DUID structures.
+// https://www.ietf.org/rfc/rfc3315.txt
type OptClientId struct {
Cid Duid
}
@@ -17,12 +18,13 @@ func (op *OptClientId) Code() OptionCode {
return OptionClientID
}
+// ToBytes marshals the Client ID option as defined by RFC 3315, Section 22.2.
func (op *OptClientId) ToBytes() []byte {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionClientID))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- buf = append(buf, op.Cid.ToBytes()...)
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionClientID))
+ buf.Write16(uint16(op.Length()))
+ buf.WriteBytes(op.Cid.ToBytes())
+ return buf.Data()
}
func (op *OptClientId) Length() int {
@@ -37,11 +39,7 @@ func (op *OptClientId) String() string {
// of bytes. The input data does not include option code and length
// bytes.
func ParseOptClientId(data []byte) (*OptClientId, error) {
- if len(data) < 2 {
- // at least the DUID type is necessary to continue
- return nil, fmt.Errorf("Invalid OptClientId data: shorter than 2 bytes")
- }
- opt := OptClientId{}
+ var opt OptClientId
cid, err := DuidFromBytes(data)
if err != nil {
return nil, err
diff --git a/dhcpv6/option_dnsrecursivenameserver.go b/dhcpv6/option_dnsrecursivenameserver.go
index 72b0767..9018c4d 100644
--- a/dhcpv6/option_dnsrecursivenameserver.go
+++ b/dhcpv6/option_dnsrecursivenameserver.go
@@ -1,15 +1,16 @@
package dhcpv6
-// This module defines the OptDNSRecursiveNameServer structure.
-// https://www.ietf.org/rfc/rfc3646.txt
-
import (
- "encoding/binary"
"fmt"
"net"
+
+ "github.com/u-root/u-root/pkg/uio"
)
// OptDNSRecursiveNameServer represents a OptionDNSRecursiveNameServer option
+//
+// This module defines the OptDNSRecursiveNameServer structure.
+// https://www.ietf.org/rfc/rfc3646.txt
type OptDNSRecursiveNameServer struct {
NameServers []net.IP
}
@@ -19,16 +20,15 @@ func (op *OptDNSRecursiveNameServer) Code() OptionCode {
return OptionDNSRecursiveNameServer
}
-// ToBytes returns the option serialized to bytes, including option code and
-// length
+// ToBytes returns the option serialized to bytes.
func (op *OptDNSRecursiveNameServer) ToBytes() []byte {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionDNSRecursiveNameServer))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionDNSRecursiveNameServer))
+ buf.Write16(uint16(op.Length()))
for _, ns := range op.NameServers {
- buf = append(buf, ns...)
+ buf.WriteBytes(ns.To16())
}
- return buf
+ return buf.Data()
}
// Length returns the option length
@@ -44,14 +44,10 @@ func (op *OptDNSRecursiveNameServer) String() string {
// from a sequence of bytes. The input data does not include option code and length
// bytes.
func ParseOptDNSRecursiveNameServer(data []byte) (*OptDNSRecursiveNameServer, error) {
- if len(data)%net.IPv6len != 0 {
- return nil, fmt.Errorf("Invalid OptDNSRecursiveNameServer data: length is not a multiple of %d", net.IPv6len)
- }
- opt := OptDNSRecursiveNameServer{}
- var nameServers []net.IP
- for i := 0; i < len(data); i += net.IPv6len {
- nameServers = append(nameServers, data[i:i+net.IPv6len])
+ var opt OptDNSRecursiveNameServer
+ buf := uio.NewBigEndianBuffer(data)
+ for buf.Has(net.IPv6len) {
+ opt.NameServers = append(opt.NameServers, buf.CopyN(net.IPv6len))
}
- opt.NameServers = nameServers
- return &opt, nil
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/option_domainsearchlist.go b/dhcpv6/option_domainsearchlist.go
index b7a356e..404a109 100644
--- a/dhcpv6/option_domainsearchlist.go
+++ b/dhcpv6/option_domainsearchlist.go
@@ -1,16 +1,16 @@
package dhcpv6
-// This module defines the OptDomainSearchList structure.
-// https://www.ietf.org/rfc/rfc3646.txt
-
import (
- "encoding/binary"
"fmt"
"github.com/insomniacslk/dhcp/rfc1035label"
+ "github.com/u-root/u-root/pkg/uio"
)
// OptDomainSearchList list implements a OptionDomainSearchList option
+//
+// This module defines the OptDomainSearchList structure.
+// https://www.ietf.org/rfc/rfc3646.txt
type OptDomainSearchList struct {
DomainSearchList *rfc1035label.Labels
}
@@ -19,12 +19,13 @@ func (op *OptDomainSearchList) Code() OptionCode {
return OptionDomainSearchList
}
+// ToBytes marshals this option to bytes.
func (op *OptDomainSearchList) ToBytes() []byte {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionDomainSearchList))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- buf = append(buf, op.DomainSearchList.ToBytes()...)
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionDomainSearchList))
+ buf.Write16(uint16(op.Length()))
+ buf.WriteBytes(op.DomainSearchList.ToBytes())
+ return buf.Data()
}
func (op *OptDomainSearchList) Length() int {
@@ -42,11 +43,11 @@ func (op *OptDomainSearchList) String() string {
// ParseOptDomainSearchList builds an OptDomainSearchList structure from a sequence
// of bytes. The input data does not include option code and length bytes.
func ParseOptDomainSearchList(data []byte) (*OptDomainSearchList, error) {
- opt := OptDomainSearchList{}
- labels, err := rfc1035label.FromBytes(data)
+ var opt OptDomainSearchList
+ var err error
+ opt.DomainSearchList, err = rfc1035label.FromBytes(data)
if err != nil {
return nil, err
}
- opt.DomainSearchList = labels
return &opt, nil
}
diff --git a/dhcpv6/option_elapsedtime.go b/dhcpv6/option_elapsedtime.go
index 6009bf8..cca5a5d 100644
--- a/dhcpv6/option_elapsedtime.go
+++ b/dhcpv6/option_elapsedtime.go
@@ -1,13 +1,15 @@
package dhcpv6
-// This module defines the OptElapsedTime structure.
-// https://www.ietf.org/rfc/rfc3315.txt
-
import (
- "encoding/binary"
"fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
)
+// OptElapsedTime implements the Elapsed Time option.
+//
+// This module defines the OptElapsedTime structure.
+// https://www.ietf.org/rfc/rfc3315.txt
type OptElapsedTime struct {
ElapsedTime uint16
}
@@ -16,12 +18,13 @@ func (op *OptElapsedTime) Code() OptionCode {
return OptionElapsedTime
}
+// ToBytes marshals this option to bytes.
func (op *OptElapsedTime) ToBytes() []byte {
- buf := make([]byte, 6)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionElapsedTime))
- binary.BigEndian.PutUint16(buf[2:4], 2)
- binary.BigEndian.PutUint16(buf[4:6], uint16(op.ElapsedTime))
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionElapsedTime))
+ buf.Write16(2)
+ buf.Write16(uint16(op.ElapsedTime))
+ return buf.Data()
}
func (op *OptElapsedTime) Length() int {
@@ -35,10 +38,8 @@ func (op *OptElapsedTime) String() string {
// build an OptElapsedTime structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOptElapsedTime(data []byte) (*OptElapsedTime, error) {
- opt := OptElapsedTime{}
- if len(data) != 2 {
- return nil, fmt.Errorf("Invalid elapsed time data length. Expected 2 bytes, got %v", len(data))
- }
- opt.ElapsedTime = binary.BigEndian.Uint16(data)
- return &opt, nil
+ var opt OptElapsedTime
+ buf := uio.NewBigEndianBuffer(data)
+ opt.ElapsedTime = buf.Read16()
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/option_iaaddress.go b/dhcpv6/option_iaaddress.go
index 811cbbe..096e50c 100644
--- a/dhcpv6/option_iaaddress.go
+++ b/dhcpv6/option_iaaddress.go
@@ -1,15 +1,16 @@
package dhcpv6
-// This module defines the OptIAAddress structure.
-// https://www.ietf.org/rfc/rfc3633.txt
-
import (
- "encoding/binary"
"fmt"
"net"
+
+ "github.com/u-root/u-root/pkg/uio"
)
-// OptIAAddress represents an OptionIAAddr
+// OptIAAddress represents an OptionIAAddr.
+//
+// This module defines the OptIAAddress structure.
+// https://www.ietf.org/rfc/rfc3633.txt
type OptIAAddress struct {
IPv6Addr net.IP
PreferredLifetime uint32
@@ -24,16 +25,14 @@ func (op *OptIAAddress) Code() OptionCode {
// ToBytes serializes the option and returns it as a sequence of bytes
func (op *OptIAAddress) ToBytes() []byte {
- buf := make([]byte, 28)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionIAAddr))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- copy(buf[4:20], op.IPv6Addr[:])
- binary.BigEndian.PutUint32(buf[20:24], op.PreferredLifetime)
- binary.BigEndian.PutUint32(buf[24:28], op.ValidLifetime)
- for _, opt := range op.Options {
- buf = append(buf, opt.ToBytes()...)
- }
- return buf
+ buf := uio.NewBigEndianBuffer(make([]byte, 0, 28))
+ buf.Write16(uint16(OptionIAAddr))
+ buf.Write16(uint16(op.Length()))
+ buf.WriteBytes(op.IPv6Addr.To16())
+ buf.Write32(op.PreferredLifetime)
+ buf.Write32(op.ValidLifetime)
+ buf.WriteBytes(op.Options.ToBytes())
+ return buf.Data()
}
// Length returns the option length
@@ -54,15 +53,13 @@ func (op *OptIAAddress) String() string {
// of bytes. The input data does not include option code and length
// bytes.
func ParseOptIAAddress(data []byte) (*OptIAAddress, error) {
- opt := OptIAAddress{}
- if len(data) < 24 {
- return nil, fmt.Errorf("Invalid IA Address data length. Expected at least 24 bytes, got %v", len(data))
- }
- opt.IPv6Addr = net.IP(data[:16])
- opt.PreferredLifetime = binary.BigEndian.Uint32(data[16:20])
- opt.ValidLifetime = binary.BigEndian.Uint32(data[20:24])
- if err := opt.Options.FromBytes(data[24:]); err != nil {
+ var opt OptIAAddress
+ buf := uio.NewBigEndianBuffer(data)
+ opt.IPv6Addr = net.IP(buf.CopyN(net.IPv6len))
+ opt.PreferredLifetime = buf.Read32()
+ opt.ValidLifetime = buf.Read32()
+ if err := opt.Options.FromBytes(buf.ReadAll()); err != nil {
return nil, err
}
- return &opt, nil
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/option_iaprefix.go b/dhcpv6/option_iaprefix.go
index 2f71fb0..cad1a0c 100644
--- a/dhcpv6/option_iaprefix.go
+++ b/dhcpv6/option_iaprefix.go
@@ -1,14 +1,16 @@
package dhcpv6
-// This module defines the OptIAPrefix structure.
-// https://www.ietf.org/rfc/rfc3633.txt
-
import (
- "encoding/binary"
"fmt"
"net"
+
+ "github.com/u-root/u-root/pkg/uio"
)
+// OptIAPrefix implements the IAPrefix option.
+//
+// This module defines the OptIAPrefix structure.
+// https://www.ietf.org/rfc/rfc3633.txt
type OptIAPrefix struct {
PreferredLifetime uint32
ValidLifetime uint32
@@ -21,18 +23,19 @@ func (op *OptIAPrefix) Code() OptionCode {
return OptionIAPrefix
}
+// ToBytes marshals this option according to RFC 3633, Section 10.
func (op *OptIAPrefix) ToBytes() []byte {
- buf := make([]byte, 12)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionIAPrefix))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- binary.BigEndian.PutUint32(buf[4:8], op.PreferredLifetime)
- binary.BigEndian.PutUint32(buf[8:12], op.ValidLifetime)
- buf = append(buf, op.prefixLength)
- buf = append(buf, op.ipv6Prefix...)
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionIAPrefix))
+ buf.Write16(uint16(op.Length()))
+ buf.Write32(op.PreferredLifetime)
+ buf.Write32(op.ValidLifetime)
+ buf.Write8(op.prefixLength)
+ buf.WriteBytes(op.ipv6Prefix.To16())
for _, opt := range op.Options {
- buf = append(buf, opt.ToBytes()...)
+ buf.WriteBytes(opt.ToBytes())
}
- return buf
+ return buf.Data()
}
func (op *OptIAPrefix) PrefixLength() byte {
@@ -78,19 +81,17 @@ func (op *OptIAPrefix) DelOption(code OptionCode) {
op.Options.Del(code)
}
-// build an OptIAPrefix structure from a sequence of bytes.
-// The input data does not include option code and length bytes.
+// ParseOptIAPrefix an OptIAPrefix structure from a sequence of bytes. The
+// input data does not include option code and length bytes.
func ParseOptIAPrefix(data []byte) (*OptIAPrefix, error) {
- opt := OptIAPrefix{}
- if len(data) < 25 {
- return nil, fmt.Errorf("Invalid IA for Prefix Delegation data length. Expected at least 25 bytes, got %v", len(data))
- }
- opt.PreferredLifetime = binary.BigEndian.Uint32(data[:4])
- opt.ValidLifetime = binary.BigEndian.Uint32(data[4:8])
- opt.prefixLength = data[8]
- opt.ipv6Prefix = net.IP(data[9:25])
- if err := opt.Options.FromBytes(data[25:]); err != nil {
+ buf := uio.NewBigEndianBuffer(data)
+ var opt OptIAPrefix
+ opt.PreferredLifetime = buf.Read32()
+ opt.ValidLifetime = buf.Read32()
+ opt.prefixLength = buf.Read8()
+ opt.ipv6Prefix = net.IP(buf.CopyN(net.IPv6len))
+ if err := opt.Options.FromBytes(buf.ReadAll()); err != nil {
return nil, err
}
- return &opt, nil
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/option_interfaceid.go b/dhcpv6/option_interfaceid.go
index 2bee744..000be5d 100644
--- a/dhcpv6/option_interfaceid.go
+++ b/dhcpv6/option_interfaceid.go
@@ -1,13 +1,16 @@
package dhcpv6
-// This module defines the OptInterfaceId structure.
-// https://www.ietf.org/rfc/rfc3315.txt
-
import (
- "encoding/binary"
"fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
)
+// OptInterfaceId implements the interface-id option as defined by RFC 3315,
+// Section 22.18.
+//
+// This module defines the OptInterfaceId structure.
+// https://www.ietf.org/rfc/rfc3315.txt
type OptInterfaceId struct {
interfaceId []byte
}
@@ -17,11 +20,11 @@ func (op *OptInterfaceId) Code() OptionCode {
}
func (op *OptInterfaceId) ToBytes() []byte {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionInterfaceID))
- binary.BigEndian.PutUint16(buf[2:4], uint16(len(op.interfaceId)))
- buf = append(buf, op.interfaceId...)
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionInterfaceID))
+ buf.Write16(uint16(len(op.interfaceId)))
+ buf.WriteBytes(op.interfaceId)
+ return buf.Data()
}
func (op *OptInterfaceId) InterfaceID() []byte {
@@ -43,7 +46,7 @@ func (op *OptInterfaceId) String() string {
// build an OptInterfaceId structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOptInterfaceId(data []byte) (*OptInterfaceId, error) {
- opt := OptInterfaceId{}
+ var opt OptInterfaceId
opt.interfaceId = append([]byte(nil), data...)
return &opt, nil
}
diff --git a/dhcpv6/option_nii.go b/dhcpv6/option_nii.go
index 68e315e..723c57a 100644
--- a/dhcpv6/option_nii.go
+++ b/dhcpv6/option_nii.go
@@ -1,11 +1,9 @@
package dhcpv6
-// This module defines the OptNetworkInterfaceId structure.
-// https://www.ietf.org/rfc/rfc5970.txt
-
import (
- "encoding/binary"
"fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
)
// see rfc4578
@@ -18,7 +16,7 @@ const (
NII_UNDI_EFI_GEN_II = 5
)
-var NIIToStringMap = map[uint8]string{
+var niiToStringMap = map[uint8]string{
NII_LANDESK_NOPXE: "LANDesk service agent boot ROMs. No PXE",
NII_PXE_GEN_I: "First gen. PXE boot ROMs",
NII_PXE_GEN_II: "Second gen. PXE boot ROMs",
@@ -27,6 +25,8 @@ var NIIToStringMap = map[uint8]string{
NII_UNDI_EFI_GEN_II: "UNDI 32/64 bit. UEFI runtime 2nd gen",
}
+// OptNetworkInterfaceId implements the NIC ID option for network booting as
+// defined by RFC 4578 Section 2.2 and RFC 5970 Section 3.4.
type OptNetworkInterfaceId struct {
type_ uint8
major, minor uint8 // revision number
@@ -37,13 +37,13 @@ func (op *OptNetworkInterfaceId) Code() OptionCode {
}
func (op *OptNetworkInterfaceId) ToBytes() []byte {
- buf := make([]byte, 7)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionNII))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- buf[4] = op.type_
- buf[5] = op.major
- buf[6] = op.minor
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionNII))
+ buf.Write16(uint16(op.Length()))
+ buf.Write8(op.type_)
+ buf.Write8(op.major)
+ buf.Write8(op.minor)
+ return buf.Data()
}
func (op *OptNetworkInterfaceId) Type() uint8 {
@@ -75,7 +75,7 @@ func (op *OptNetworkInterfaceId) Length() int {
}
func (op *OptNetworkInterfaceId) String() string {
- typeName, ok := NIIToStringMap[op.type_]
+ typeName, ok := niiToStringMap[op.type_]
if !ok {
typeName = "Unknown"
}
@@ -87,12 +87,10 @@ func (op *OptNetworkInterfaceId) String() string {
// build an OptNetworkInterfaceId structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOptNetworkInterfaceId(data []byte) (*OptNetworkInterfaceId, error) {
- opt := OptNetworkInterfaceId{}
- if len(data) != 3 {
- return nil, fmt.Errorf("Invalid arch type data length. Expected 3 bytes, got %v", len(data))
- }
- opt.type_ = data[0]
- opt.major = data[1]
- opt.minor = data[2]
- return &opt, nil
+ buf := uio.NewBigEndianBuffer(data)
+ var opt OptNetworkInterfaceId
+ opt.type_ = buf.Read8()
+ opt.major = buf.Read8()
+ opt.minor = buf.Read8()
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/option_nontemporaryaddress.go b/dhcpv6/option_nontemporaryaddress.go
index 94f79c0..7e8bbaf 100644
--- a/dhcpv6/option_nontemporaryaddress.go
+++ b/dhcpv6/option_nontemporaryaddress.go
@@ -1,13 +1,16 @@
package dhcpv6
-// This module defines the OptIANA structure.
-// https://www.ietf.org/rfc/rfc3633.txt
-
import (
- "encoding/binary"
"fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
)
+// OptIANA implements the identity association for non-temporary addresses
+// option.
+//
+// This module defines the OptIANA structure.
+// https://www.ietf.org/rfc/rfc3633.txt
type OptIANA struct {
IaId [4]byte
T1 uint32
@@ -19,17 +22,16 @@ func (op *OptIANA) Code() OptionCode {
return OptionIANA
}
+// ToBytes serializes IANA to DHCPv6 bytes.
func (op *OptIANA) ToBytes() []byte {
- buf := make([]byte, 16)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionIANA))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- copy(buf[4:8], op.IaId[:])
- binary.BigEndian.PutUint32(buf[8:12], op.T1)
- binary.BigEndian.PutUint32(buf[12:16], op.T2)
- for _, opt := range op.Options {
- buf = append(buf, opt.ToBytes()...)
- }
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionIANA))
+ buf.Write16(uint16(op.Length()))
+ buf.WriteBytes(op.IaId[:])
+ buf.Write32(op.T1)
+ buf.Write32(op.T2)
+ buf.WriteBytes(op.Options.ToBytes())
+ return buf.Data()
}
func (op *OptIANA) Length() int {
@@ -61,18 +63,16 @@ func (op *OptIANA) DelOption(code OptionCode) {
op.Options.Del(code)
}
-// build an OptIANA structure from a sequence of bytes.
-// The input data does not include option code and length bytes.
+// ParseOptIANA builds an OptIANA structure from a sequence of bytes. The
+// input data does not include option code and length bytes.
func ParseOptIANA(data []byte) (*OptIANA, error) {
- opt := OptIANA{}
- if len(data) < 12 {
- return nil, fmt.Errorf("Invalid IA for Non-temporary Addresses data length. Expected at least 12 bytes, got %v", len(data))
- }
- copy(opt.IaId[:], data[:4])
- opt.T1 = binary.BigEndian.Uint32(data[4:8])
- opt.T2 = binary.BigEndian.Uint32(data[8:12])
- if err := opt.Options.FromBytes(data[12:]); err != nil {
+ var opt OptIANA
+ buf := uio.NewBigEndianBuffer(data)
+ buf.ReadBytes(opt.IaId[:])
+ opt.T1 = buf.Read32()
+ opt.T2 = buf.Read32()
+ if err := opt.Options.FromBytes(buf.ReadAll()); err != nil {
return nil, err
}
- return &opt, nil
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/option_prefixdelegation.go b/dhcpv6/option_prefixdelegation.go
index 5875162..ee46876 100644
--- a/dhcpv6/option_prefixdelegation.go
+++ b/dhcpv6/option_prefixdelegation.go
@@ -1,8 +1,9 @@
package dhcpv6
import (
- "encoding/binary"
"fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
)
// OptIAForPrefixDelegation implements the identity association for prefix
@@ -21,16 +22,14 @@ func (op *OptIAForPrefixDelegation) Code() OptionCode {
// ToBytes serializes the option and returns it as a sequence of bytes
func (op *OptIAForPrefixDelegation) ToBytes() []byte {
- buf := make([]byte, 16)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionIAPD))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- copy(buf[4:8], op.IaId[:])
- binary.BigEndian.PutUint32(buf[8:12], op.T1)
- binary.BigEndian.PutUint32(buf[12:16], op.T2)
- for _, opt := range op.Options {
- buf = append(buf, opt.ToBytes()...)
- }
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionIAPD))
+ buf.Write16(uint16(op.Length()))
+ buf.WriteBytes(op.IaId[:])
+ buf.Write32(op.T1)
+ buf.Write32(op.T2)
+ buf.WriteBytes(op.Options.ToBytes())
+ return buf.Data()
}
// Length returns the option length
@@ -62,15 +61,13 @@ func (op *OptIAForPrefixDelegation) DelOption(code OptionCode) {
// build an OptIAForPrefixDelegation structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOptIAForPrefixDelegation(data []byte) (*OptIAForPrefixDelegation, error) {
- opt := OptIAForPrefixDelegation{}
- if len(data) < 12 {
- return nil, fmt.Errorf("Invalid IA for Prefix Delegation data length. Expected at least 12 bytes, got %v", len(data))
- }
- copy(opt.IaId[:], data[:4])
- opt.T1 = binary.BigEndian.Uint32(data[4:8])
- opt.T2 = binary.BigEndian.Uint32(data[8:12])
- if err := opt.Options.FromBytes(data[12:]); err != nil {
+ var opt OptIAForPrefixDelegation
+ buf := uio.NewBigEndianBuffer(data)
+ buf.ReadBytes(opt.IaId[:])
+ opt.T1 = buf.Read32()
+ opt.T2 = buf.Read32()
+ if err := opt.Options.FromBytes(buf.ReadAll()); err != nil {
return nil, err
}
- return &opt, nil
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/option_relaymsg.go b/dhcpv6/option_relaymsg.go
index 89621ec..09589f6 100644
--- a/dhcpv6/option_relaymsg.go
+++ b/dhcpv6/option_relaymsg.go
@@ -4,8 +4,9 @@ package dhcpv6
// https://www.ietf.org/rfc/rfc3315.txt
import (
- "encoding/binary"
"fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
)
type OptRelayMsg struct {
@@ -17,11 +18,11 @@ func (op *OptRelayMsg) Code() OptionCode {
}
func (op *OptRelayMsg) ToBytes() []byte {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionRelayMsg))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- buf = append(buf, op.relayMessage.ToBytes()...)
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionRelayMsg))
+ buf.Write16(uint16(op.Length()))
+ buf.WriteBytes(op.relayMessage.ToBytes())
+ return buf.Data()
}
func (op *OptRelayMsg) RelayMessage() DHCPv6 {
@@ -44,7 +45,7 @@ func (op *OptRelayMsg) String() string {
// The input data does not include option code and length bytes.
func ParseOptRelayMsg(data []byte) (*OptRelayMsg, error) {
var err error
- opt := OptRelayMsg{}
+ var opt OptRelayMsg
opt.relayMessage, err = FromBytes(data)
if err != nil {
return nil, err
diff --git a/dhcpv6/option_remoteid.go b/dhcpv6/option_remoteid.go
index 9d249a7..6b1831d 100644
--- a/dhcpv6/option_remoteid.go
+++ b/dhcpv6/option_remoteid.go
@@ -1,13 +1,14 @@
package dhcpv6
-// This module defines the OptRemoteId structure.
-// https://www.ietf.org/rfc/rfc4649.txt
-
import (
- "encoding/binary"
"fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
)
+// OptRemoteId implemens the Remote ID option.
+//
+// https://www.ietf.org/rfc/rfc4649.txt
type OptRemoteId struct {
enterpriseNumber uint32
remoteId []byte
@@ -17,13 +18,14 @@ func (op *OptRemoteId) Code() OptionCode {
return OptionRemoteID
}
+// ToBytes serializes this option to a byte stream.
func (op *OptRemoteId) ToBytes() []byte {
- buf := make([]byte, 8)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionRemoteID))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- binary.BigEndian.PutUint32(buf[4:8], uint32(op.enterpriseNumber))
- buf = append(buf, op.remoteId...)
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionRemoteID))
+ buf.Write16(uint16(op.Length()))
+ buf.Write32(uint32(op.enterpriseNumber))
+ buf.WriteBytes(op.remoteId)
+ return buf.Data()
}
func (op *OptRemoteId) EnterpriseNumber() uint32 {
@@ -52,14 +54,12 @@ func (op *OptRemoteId) String() string {
)
}
-// build an OptRemoteId structure from a sequence of bytes.
+// ParseOptRemoteId builds an OptRemoteId structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOptRemoteId(data []byte) (*OptRemoteId, error) {
- opt := OptRemoteId{}
- if len(data) < 4 {
- return nil, fmt.Errorf("Invalid remote id data length. Expected at least 4 bytes, got %v", len(data))
- }
- opt.enterpriseNumber = binary.BigEndian.Uint32(data[:4])
- opt.remoteId = append([]byte(nil), data[4:]...)
- return &opt, nil
+ var opt OptRemoteId
+ buf := uio.NewBigEndianBuffer(data)
+ opt.enterpriseNumber = buf.Read32()
+ opt.remoteId = buf.ReadAll()
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/option_requestedoption.go b/dhcpv6/option_requestedoption.go
index 0324642..7f345f4 100644
--- a/dhcpv6/option_requestedoption.go
+++ b/dhcpv6/option_requestedoption.go
@@ -1,14 +1,16 @@
package dhcpv6
-// This module defines the OptRequestedOption structure.
-// https://www.ietf.org/rfc/rfc3315.txt
-
import (
- "encoding/binary"
"fmt"
"strings"
+
+ "github.com/u-root/u-root/pkg/uio"
)
+// OptRequestedOption implements the requested options option.
+//
+// This module defines the OptRequestedOption structure.
+// https://www.ietf.org/rfc/rfc3315.txt
type OptRequestedOption struct {
requestedOptions []OptionCode
}
@@ -18,15 +20,13 @@ func (op *OptRequestedOption) Code() OptionCode {
}
func (op *OptRequestedOption) ToBytes() []byte {
- buf := make([]byte, 4)
- roBytes := make([]byte, 2)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionORO))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionORO))
+ buf.Write16(uint16(op.Length()))
for _, ro := range op.requestedOptions {
- binary.BigEndian.PutUint16(roBytes, uint16(ro))
- buf = append(buf, roBytes...)
+ buf.Write16(uint16(ro))
}
- return buf
+ return buf.Data()
}
func (op *OptRequestedOption) RequestedOptions() []OptionCode {
@@ -61,14 +61,10 @@ func (op *OptRequestedOption) String() string {
// build an OptRequestedOption structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOptRequestedOption(data []byte) (*OptRequestedOption, error) {
- if len(data)%2 != 0 {
- return nil, fmt.Errorf("Invalid OptRequestedOption data: length is not a multiple of 2")
- }
- opt := OptRequestedOption{}
- var rOpts []OptionCode
- for i := 0; i < len(data); i += 2 {
- rOpts = append(rOpts, OptionCode(binary.BigEndian.Uint16(data[i:i+2])))
+ var opt OptRequestedOption
+ buf := uio.NewBigEndianBuffer(data)
+ for buf.Has(2) {
+ opt.requestedOptions = append(opt.requestedOptions, OptionCode(buf.Read16()))
}
- opt.requestedOptions = rOpts
- return &opt, nil
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/option_serverid.go b/dhcpv6/option_serverid.go
index dbc17f5..4b1a828 100644
--- a/dhcpv6/option_serverid.go
+++ b/dhcpv6/option_serverid.go
@@ -1,14 +1,15 @@
package dhcpv6
-// This module defines the OptServerId and DUID structures.
-// https://www.ietf.org/rfc/rfc3315.txt
-
import (
- "encoding/binary"
"fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
)
-// OptServerId represents a Client ID option
+// OptServerId represents a Server ID option
+//
+// This module defines the OptServerId and DUID structures.
+// https://www.ietf.org/rfc/rfc3315.txt
type OptServerId struct {
Sid Duid
}
@@ -17,12 +18,13 @@ func (op *OptServerId) Code() OptionCode {
return OptionServerID
}
+// ToBytes serializes this option.
func (op *OptServerId) ToBytes() []byte {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionServerID))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- buf = append(buf, op.Sid.ToBytes()...)
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionServerID))
+ buf.Write16(uint16(op.Length()))
+ buf.WriteBytes(op.Sid.ToBytes())
+ return buf.Data()
}
func (op *OptServerId) Length() int {
@@ -36,11 +38,7 @@ func (op *OptServerId) String() string {
// ParseOptServerId builds an OptServerId structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOptServerId(data []byte) (*OptServerId, error) {
- if len(data) < 2 {
- // at least the DUID type is necessary to continue
- return nil, fmt.Errorf("Invalid OptServerId data: shorter than 2 bytes")
- }
- opt := OptServerId{}
+ var opt OptServerId
sid, err := DuidFromBytes(data)
if err != nil {
return nil, err
diff --git a/dhcpv6/option_statuscode.go b/dhcpv6/option_statuscode.go
index 74d200c..a368a09 100644
--- a/dhcpv6/option_statuscode.go
+++ b/dhcpv6/option_statuscode.go
@@ -1,16 +1,16 @@
package dhcpv6
-// This module defines the OptStatusCode structure.
-// https://www.ietf.org/rfc/rfc3315.txt
-
import (
- "encoding/binary"
"fmt"
"github.com/insomniacslk/dhcp/iana"
+ "github.com/u-root/u-root/pkg/uio"
)
// OptStatusCode represents a DHCPv6 Status Code option
+//
+// This module defines the OptStatusCode structure.
+// https://www.ietf.org/rfc/rfc3315.txt
type OptStatusCode struct {
StatusCode iana.StatusCode
StatusMessage []byte
@@ -23,12 +23,12 @@ func (op *OptStatusCode) Code() OptionCode {
// ToBytes serializes the option and returns it as a sequence of bytes
func (op *OptStatusCode) ToBytes() []byte {
- buf := make([]byte, 6)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionStatusCode))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- binary.BigEndian.PutUint16(buf[4:6], uint16(op.StatusCode))
- buf = append(buf, op.StatusMessage...)
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionStatusCode))
+ buf.Write16(uint16(op.Length()))
+ buf.Write16(uint16(op.StatusCode))
+ buf.WriteBytes(op.StatusMessage)
+ return buf.Data()
}
// Length returns the option length
@@ -45,11 +45,9 @@ func (op *OptStatusCode) String() string {
// ParseOptStatusCode builds an OptStatusCode structure from a sequence of
// bytes. The input data does not include option code and length bytes.
func ParseOptStatusCode(data []byte) (*OptStatusCode, error) {
- if len(data) < 2 {
- return nil, fmt.Errorf("Invalid OptStatusCode data: length is shorter than 2")
- }
- opt := OptStatusCode{}
- opt.StatusCode = iana.StatusCode(binary.BigEndian.Uint16(data[0:2]))
- opt.StatusMessage = append(opt.StatusMessage, data[2:]...)
- return &opt, nil
+ var opt OptStatusCode
+ buf := uio.NewBigEndianBuffer(data)
+ opt.StatusCode = iana.StatusCode(buf.Read16())
+ opt.StatusMessage = buf.ReadAll()
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/option_userclass.go b/dhcpv6/option_userclass.go
index 652c216..15f3512 100644
--- a/dhcpv6/option_userclass.go
+++ b/dhcpv6/option_userclass.go
@@ -1,16 +1,16 @@
package dhcpv6
-// This module defines the OptUserClass structure.
-// https://www.ietf.org/rfc/rfc3315.txt
-
import (
- "encoding/binary"
- "errors"
"fmt"
"strings"
+
+ "github.com/u-root/u-root/pkg/uio"
)
// OptUserClass represent a DHCPv6 User Class option
+//
+// This module defines the OptUserClass structure.
+// https://www.ietf.org/rfc/rfc3315.txt
type OptUserClass struct {
UserClasses [][]byte
}
@@ -22,16 +22,14 @@ func (op *OptUserClass) Code() OptionCode {
// ToBytes serializes the option and returns it as a sequence of bytes
func (op *OptUserClass) ToBytes() []byte {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionUserClass))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- u16 := make([]byte, 2)
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionUserClass))
+ buf.Write16(uint16(op.Length()))
for _, uc := range op.UserClasses {
- binary.BigEndian.PutUint16(u16, uint16(len(uc)))
- buf = append(buf, u16...)
- buf = append(buf, uc...)
+ buf.Write16(uint16(len(uc)))
+ buf.WriteBytes(uc)
}
- return buf
+ return buf.Data()
}
// Length returns the option length
@@ -44,7 +42,7 @@ func (op *OptUserClass) Length() int {
}
func (op *OptUserClass) String() string {
- ucStrings := make([]string, 0)
+ ucStrings := make([]string, 0, len(op.UserClasses))
for _, uc := range op.UserClasses {
ucStrings = append(ucStrings, string(uc))
}
@@ -54,23 +52,14 @@ func (op *OptUserClass) String() string {
// ParseOptUserClass builds an OptUserClass structure from a sequence of
// bytes. The input data does not include option code and length bytes.
func ParseOptUserClass(data []byte) (*OptUserClass, error) {
- opt := OptUserClass{}
- for {
- if len(data) == 0 {
- break
- }
- if len(data) < 2 {
- return nil, errors.New("ParseOptUserClass: short data: missing length field")
- }
- ucLen := int(binary.BigEndian.Uint16(data[:2]))
- if len(data) < ucLen+2 {
- return nil, fmt.Errorf("ParseOptUserClass: short data: less than %d bytes", ucLen+2)
- }
- opt.UserClasses = append(opt.UserClasses, data[2:ucLen+2])
- data = data[2+ucLen:]
+ var opt OptUserClass
+ if len(data) == 0 {
+ return nil, fmt.Errorf("user class option must not be empty")
}
- if len(opt.UserClasses) < 1 {
- return nil, errors.New("ParseOptUserClass: at least one user class is required")
+ buf := uio.NewBigEndianBuffer(data)
+ for buf.Has(2) {
+ len := buf.Read16()
+ opt.UserClasses = append(opt.UserClasses, buf.CopyN(int(len)))
}
- return &opt, nil
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/option_vendor_opts.go b/dhcpv6/option_vendor_opts.go
index f791ddf..027e7c3 100644
--- a/dhcpv6/option_vendor_opts.go
+++ b/dhcpv6/option_vendor_opts.go
@@ -1,39 +1,15 @@
package dhcpv6
-/*
- This module defines the OptVendorOpts structure.
- https://tools.ietf.org/html/rfc3315#section-22.17
-
- Option 17
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | OPTION_VENDOR_OPTS | option-len |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | enterprise-number |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- . .
- . option-data (sub-options) .
- . .
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
- Sub-Option
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | opt-code | option-len |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- . .
- . option-data .
- . .
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-*/
-
import (
- "encoding/binary"
- "errors"
"fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
)
// OptVendorOpts represents a DHCPv6 Status Code option
+//
+// This module defines the OptVendorOpts structure.
+// https://tools.ietf.org/html/rfc3315#section-22.17
type OptVendorOpts struct {
EnterpriseNumber uint32
VendorOpts Options
@@ -46,14 +22,12 @@ func (op *OptVendorOpts) Code() OptionCode {
// ToBytes serializes the option and returns it as a sequence of bytes
func (op *OptVendorOpts) ToBytes() []byte {
- buf := make([]byte, 8)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionVendorOpts))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- binary.BigEndian.PutUint32(buf[4:8], uint32(op.EnterpriseNumber))
- for _, opt := range op.VendorOpts {
- buf = append(buf, opt.ToBytes()...)
- }
- return buf
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionVendorOpts))
+ buf.Write16(uint16(op.Length()))
+ buf.Write32(uint32(op.EnterpriseNumber))
+ buf.WriteData(op.VendorOpts.ToBytes())
+ return buf.Data()
}
// Length returns the option length
@@ -75,41 +49,19 @@ func (op *OptVendorOpts) String() string {
// ParseOptVendorOpts builds an OptVendorOpts structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOptVendorOpts(data []byte) (*OptVendorOpts, error) {
- opt := OptVendorOpts{}
- if len(data) < 4 {
- return nil, fmt.Errorf("Invalid vendor opts data length. Expected at least 4 bytes, got %v", len(data))
- }
- opt.EnterpriseNumber = binary.BigEndian.Uint32(data[:4])
-
- if err := opt.VendorOpts.FromBytesWithParser(data[4:], vendParseOption); err != nil {
+ var opt OptVendorOpts
+ buf := uio.NewBigEndianBuffer(data)
+ opt.EnterpriseNumber = buf.Read32()
+ if err := opt.VendorOpts.FromBytesWithParser(buf.ReadAll(), vendParseOption); err != nil {
return nil, err
}
- return &opt, nil
+ return &opt, buf.FinError()
}
// vendParseOption builds a GenericOption from a slice of bytes
// We cannot use the existing ParseOption function in options.go because the
// sub-options include codes specific to each vendor. There are overlaps in these
// codes with RFC standard codes.
-func vendParseOption(dataStart []byte) (Option, error) {
- // Parse a sequence of bytes as a single DHCPv6 option.
- // Returns the option structure, or an error if any.
-
- if len(dataStart) < 4 {
- return nil, fmt.Errorf("Invalid DHCPv6 vendor option: less than 4 bytes")
- }
- code := OptionCode(binary.BigEndian.Uint16(dataStart[:2]))
- length := int(binary.BigEndian.Uint16(dataStart[2:4]))
- if len(dataStart) < length+4 {
- return nil, fmt.Errorf("Invalid option length for vendor option %v. Declared %v, actual %v",
- code, length, len(dataStart)-4,
- )
- }
-
- optData := dataStart[4 : 4+length]
- if len(optData) < 1 {
- return nil, errors.New("vendParseOption: at least one vendor options data is required")
- }
-
- return &OptionGeneric{OptionCode: code, OptionData: optData}, nil
+func vendParseOption(code OptionCode, data []byte) (Option, error) {
+ return &OptionGeneric{OptionCode: code, OptionData: data}, nil
}
diff --git a/dhcpv6/option_vendor_opts_test.go b/dhcpv6/option_vendor_opts_test.go
index 468fea5..cce4650 100644
--- a/dhcpv6/option_vendor_opts_test.go
+++ b/dhcpv6/option_vendor_opts_test.go
@@ -48,28 +48,3 @@ func TestOptVendorOptsToBytes(t *testing.T) {
toBytes := opt.ToBytes()
require.Equal(t, expected, toBytes)
}
-
-func TestVendParseOption(t *testing.T) {
- var buf []byte
- buf = append(buf, []byte{00, 1, 00, 33}...)
- buf = append(buf, []byte("Arista;DCS-7304;01.00;HSH14425148")...)
-
- expected := &OptionGeneric{OptionCode: 1, OptionData: []byte("Arista;DCS-7304;01.00;HSH14425148")}
- opt, err := vendParseOption(buf)
- require.NoError(t, err)
- require.Equal(t, expected, opt)
-
- shortData := make([]byte, 1) // data length too small
- _, err = vendParseOption(shortData)
- require.Error(t, err)
-
- shortData = []byte{0, 0, 0, 0} // missing actual vendor data.
- _, err = vendParseOption(shortData)
- require.Error(t, err)
-
- shortData = []byte{0, 0,
- 0, 4, // declared length
- 0} // data starts here, length of 1
- _, err = vendParseOption(shortData)
- require.Error(t, err)
-}
diff --git a/dhcpv6/option_vendorclass.go b/dhcpv6/option_vendorclass.go
index b920fa7..b7204e7 100644
--- a/dhcpv6/option_vendorclass.go
+++ b/dhcpv6/option_vendorclass.go
@@ -1,10 +1,11 @@
package dhcpv6
import (
- "encoding/binary"
"errors"
"fmt"
"strings"
+
+ "github.com/u-root/u-root/pkg/uio"
)
// OptVendorClass represents a DHCPv6 Vendor Class option
@@ -20,17 +21,15 @@ func (op *OptVendorClass) Code() OptionCode {
// ToBytes serializes the option and returns it as a sequence of bytes
func (op *OptVendorClass) ToBytes() []byte {
- buf := make([]byte, 8)
- binary.BigEndian.PutUint16(buf[0:2], uint16(OptionVendorClass))
- binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- binary.BigEndian.PutUint32(buf[4:8], uint32(op.EnterpriseNumber))
- u16 := make([]byte, 2)
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(OptionVendorClass))
+ buf.Write16(uint16(op.Length()))
+ buf.Write32(uint32(op.EnterpriseNumber))
for _, data := range op.Data {
- binary.BigEndian.PutUint16(u16, uint16(len(data)))
- buf = append(buf, u16...)
- buf = append(buf, data...)
+ buf.Write16(uint16(len(data)))
+ buf.WriteBytes(data)
}
- return buf
+ return buf.Data()
}
// Length returns the option length
@@ -54,28 +53,15 @@ func (op *OptVendorClass) String() string {
// ParseOptVendorClass builds an OptVendorClass structure from a sequence of
// bytes. The input data does not include option code and length bytes.
func ParseOptVendorClass(data []byte) (*OptVendorClass, error) {
- opt := OptVendorClass{}
- if len(data) < 4 {
- return nil, fmt.Errorf("Invalid vendor opts data length. Expected at least 4 bytes, got %v", len(data))
- }
- opt.EnterpriseNumber = binary.BigEndian.Uint32(data[:4])
- data = data[4:]
- for {
- if len(data) == 0 {
- break
- }
- if len(data) < 2 {
- return nil, errors.New("ParseOptVendorClass: short data: missing length field")
- }
- vcLen := int(binary.BigEndian.Uint16(data[:2]))
- if len(data) < vcLen+2 {
- return nil, fmt.Errorf("ParseOptVendorClass: short data: less than %d bytes", vcLen+2)
- }
- opt.Data = append(opt.Data, data[2:vcLen+2])
- data = data[2+vcLen:]
+ var opt OptVendorClass
+ buf := uio.NewBigEndianBuffer(data)
+ opt.EnterpriseNumber = buf.Read32()
+ for buf.Has(2) {
+ len := buf.Read16()
+ opt.Data = append(opt.Data, buf.CopyN(int(len)))
}
if len(opt.Data) < 1 {
return nil, errors.New("ParseOptVendorClass: at least one vendor class data is required")
}
- return &opt, nil
+ return &opt, buf.FinError()
}
diff --git a/dhcpv6/options.go b/dhcpv6/options.go
index 23ad589..59742de 100644
--- a/dhcpv6/options.go
+++ b/dhcpv6/options.go
@@ -1,8 +1,9 @@
package dhcpv6
import (
- "encoding/binary"
"fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
)
// Option is an interface that all DHCPv6 options adhere to.
@@ -23,15 +24,11 @@ func (og *OptionGeneric) Code() OptionCode {
}
func (og *OptionGeneric) ToBytes() []byte {
- var ret []byte
- codeBytes := make([]byte, 2)
- binary.BigEndian.PutUint16(codeBytes, uint16(og.OptionCode))
- ret = append(ret, codeBytes...)
- lengthBytes := make([]byte, 2)
- binary.BigEndian.PutUint16(lengthBytes, uint16(len(og.OptionData)))
- ret = append(ret, lengthBytes...)
- ret = append(ret, og.OptionData...)
- return ret
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.Write16(uint16(og.OptionCode))
+ buf.Write16(uint16(len(og.OptionData)))
+ buf.WriteBytes(og.OptionData)
+ return buf.Data()
}
func (og *OptionGeneric) String() string {
@@ -42,24 +39,14 @@ func (og *OptionGeneric) Length() int {
return len(og.OptionData)
}
-func ParseOption(dataStart []byte) (Option, error) {
+// ParseOption parses data according to the given code.
+func ParseOption(code OptionCode, optData []byte) (Option, error) {
// Parse a sequence of bytes as a single DHCPv6 option.
// Returns the option structure, or an error if any.
- if len(dataStart) < 4 {
- return nil, fmt.Errorf("Invalid DHCPv6 option: less than 4 bytes")
- }
- code := OptionCode(binary.BigEndian.Uint16(dataStart[:2]))
- length := int(binary.BigEndian.Uint16(dataStart[2:4]))
- if len(dataStart) < length+4 {
- return nil, fmt.Errorf("Invalid option length for option %v. Declared %v, actual %v",
- code, length, len(dataStart)-4,
- )
- }
var (
err error
opt Option
)
- optData := dataStart[4 : 4+length]
switch code {
case OptionClientID:
opt, err = ParseOptClientId(optData)
@@ -107,10 +94,6 @@ func ParseOption(dataStart []byte) (Option, error) {
if err != nil {
return nil, err
}
- if length != opt.Length() {
- return nil, fmt.Errorf("Error: declared length is different from actual length for option %d: %d != %d",
- code, opt.Length(), length)
- }
return opt, nil
}
@@ -167,6 +150,15 @@ func (o *Options) Update(option Option) {
o.Add(option)
}
+// ToBytes marshals all options to bytes.
+func (o Options) ToBytes() []byte {
+ var buf []byte
+ for _, opt := range o {
+ buf = append(buf, opt.ToBytes()...)
+ }
+ return buf
+}
+
// FromBytes reads data into o and returns an error if the options are not a
// valid serialized representation of DHCPv6 options per RFC 3315.
func (o *Options) FromBytes(data []byte) error {
@@ -174,37 +166,31 @@ func (o *Options) FromBytes(data []byte) error {
}
// OptionParser is a function signature for option parsing
-type OptionParser func(data []byte) (Option, error)
+type OptionParser func(code OptionCode, data []byte) (Option, error)
// FromBytesWithParser parses Options from byte sequences using the parsing
// function that is passed in as a paremeter
func (o *Options) FromBytesWithParser(data []byte, parser OptionParser) error {
- // Parse a sequence of bytes until the end and build a list of options from
- // it. Returns an error if any invalid option or length is found.
*o = make(Options, 0, 10)
if len(data) == 0 {
// no options, no party
return nil
}
- if len(data) < 4 {
- // cannot be shorter than option code (2 bytes) + length (2 bytes)
- return fmt.Errorf("Invalid options: shorter than 4 bytes")
- }
- idx := 0
- for {
- if idx == len(data) {
- break
- }
- if idx > len(data) {
- // this should never happen
- return fmt.Errorf("Error: reading past the end of options")
- }
- opt, err := parser(data[idx:])
+
+ buf := uio.NewBigEndianBuffer(data)
+ for buf.Has(4) {
+ code := OptionCode(buf.Read16())
+ length := int(buf.Read16())
+
+ // Consume, but do not Copy. Each parser will make a copy of
+ // pertinent data.
+ optData := buf.Consume(length)
+
+ opt, err := parser(code, optData)
if err != nil {
return err
}
*o = append(*o, opt)
- idx += opt.Length() + 4 // 4 bytes for type + length
}
- return nil
+ return buf.FinError()
}