diff options
author | Christopher Koch <c@chrisko.ch> | 2019-01-20 04:47:08 +0000 |
---|---|---|
committer | insomniac <insomniacslk@users.noreply.github.com> | 2019-01-26 23:34:26 +0000 |
commit | 3478513076477d0f19eaeaf441f29949a9f6bc92 (patch) | |
tree | 4d621abff002cef480901a2ef015620472293db3 /dhcpv6 | |
parent | e62883f5b5683ae2259ad01b7ffbe20671deb6b2 (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.go | 36 | ||||
-rw-r--r-- | dhcpv6/option_bootfileurl.go | 21 | ||||
-rw-r--r-- | dhcpv6/option_clientid.go | 26 | ||||
-rw-r--r-- | dhcpv6/option_dnsrecursivenameserver.go | 36 | ||||
-rw-r--r-- | dhcpv6/option_domainsearchlist.go | 25 | ||||
-rw-r--r-- | dhcpv6/option_elapsedtime.go | 31 | ||||
-rw-r--r-- | dhcpv6/option_iaaddress.go | 45 | ||||
-rw-r--r-- | dhcpv6/option_iaprefix.go | 51 | ||||
-rw-r--r-- | dhcpv6/option_interfaceid.go | 23 | ||||
-rw-r--r-- | dhcpv6/option_nii.go | 40 | ||||
-rw-r--r-- | dhcpv6/option_nontemporaryaddress.go | 50 | ||||
-rw-r--r-- | dhcpv6/option_prefixdelegation.go | 37 | ||||
-rw-r--r-- | dhcpv6/option_relaymsg.go | 15 | ||||
-rw-r--r-- | dhcpv6/option_remoteid.go | 36 | ||||
-rw-r--r-- | dhcpv6/option_requestedoption.go | 36 | ||||
-rw-r--r-- | dhcpv6/option_serverid.go | 28 | ||||
-rw-r--r-- | dhcpv6/option_statuscode.go | 32 | ||||
-rw-r--r-- | dhcpv6/option_userclass.go | 51 | ||||
-rw-r--r-- | dhcpv6/option_vendor_opts.go | 84 | ||||
-rw-r--r-- | dhcpv6/option_vendor_opts_test.go | 25 | ||||
-rw-r--r-- | dhcpv6/option_vendorclass.go | 46 | ||||
-rw-r--r-- | dhcpv6/options.go | 76 |
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() } |