summaryrefslogtreecommitdiffhomepage
path: root/dhcpv4/bsdp
diff options
context:
space:
mode:
Diffstat (limited to 'dhcpv4/bsdp')
-rw-r--r--dhcpv4/bsdp/boot_image.go136
-rw-r--r--dhcpv4/bsdp/boot_image_test.go139
-rw-r--r--dhcpv4/bsdp/bsdp.go254
-rw-r--r--dhcpv4/bsdp/bsdp_option_boot_image_list.go53
-rw-r--r--dhcpv4/bsdp/bsdp_option_boot_image_list_test.go114
-rw-r--r--dhcpv4/bsdp/bsdp_option_message_type.go57
-rw-r--r--dhcpv4/bsdp/bsdp_option_message_type_test.go31
-rw-r--r--dhcpv4/bsdp/bsdp_option_misc.go66
-rw-r--r--dhcpv4/bsdp/bsdp_option_misc_test.go105
-rw-r--r--dhcpv4/bsdp/bsdp_test.go410
-rw-r--r--dhcpv4/bsdp/client.go75
-rw-r--r--dhcpv4/bsdp/doc.go10
-rw-r--r--dhcpv4/bsdp/option_vendor_specific_information.go157
-rw-r--r--dhcpv4/bsdp/option_vendor_specific_information_test.go79
-rw-r--r--dhcpv4/bsdp/types.go64
-rw-r--r--dhcpv4/bsdp/vendor_class_identifier.go9
-rw-r--r--dhcpv4/bsdp/vendor_class_identifier_darwin.go18
17 files changed, 0 insertions, 1777 deletions
diff --git a/dhcpv4/bsdp/boot_image.go b/dhcpv4/bsdp/boot_image.go
deleted file mode 100644
index 5e81530..0000000
--- a/dhcpv4/bsdp/boot_image.go
+++ /dev/null
@@ -1,136 +0,0 @@
-package bsdp
-
-import (
- "fmt"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/u-root/uio/uio"
-)
-
-// BootImageType represents the different BSDP boot image types.
-type BootImageType byte
-
-// Different types of BootImages - e.g. for different flavors of macOS.
-const (
- BootImageTypeMacOS9 BootImageType = 0
- BootImageTypeMacOSX BootImageType = 1
- BootImageTypeMacOSXServer BootImageType = 2
- BootImageTypeHardwareDiagnostics BootImageType = 3
- // 4 - 127 are reserved for future use.
-)
-
-// bootImageTypeToString maps the different BootImageTypes to human-readable
-// representations.
-var bootImageTypeToString = map[BootImageType]string{
- BootImageTypeMacOS9: "macOS 9",
- BootImageTypeMacOSX: "macOS",
- BootImageTypeMacOSXServer: "macOS Server",
- BootImageTypeHardwareDiagnostics: "Hardware Diagnostic",
-}
-
-// BootImageID describes a boot image ID - whether it's an install image and
-// what kind of boot image (e.g. OS 9, macOS, hardware diagnostics)
-type BootImageID struct {
- IsInstall bool
- ImageType BootImageType
- Index uint16
-}
-
-// ToBytes implements dhcpv4.OptionValue.
-func (b BootImageID) ToBytes() []byte {
- return uio.ToBigEndian(b)
-}
-
-// FromBytes reads data into b.
-func (b *BootImageID) FromBytes(data []byte) error {
- return uio.FromBigEndian(b, data)
-}
-
-// Marshal writes the binary representation to buf.
-func (b BootImageID) Marshal(buf *uio.Lexer) {
- var byte0 byte
- if b.IsInstall {
- byte0 |= 0x80
- }
- byte0 |= byte(b.ImageType)
- buf.Write8(byte0)
- buf.Write8(byte(0))
- buf.Write16(b.Index)
-}
-
-// String converts a BootImageID to a human-readable representation.
-func (b BootImageID) String() string {
- s := fmt.Sprintf("[%d]", b.Index)
- if b.IsInstall {
- s += " installable"
- } else {
- s += " uninstallable"
- }
- t, ok := bootImageTypeToString[b.ImageType]
- if !ok {
- t = "unknown"
- }
- return s + " " + t + " image"
-}
-
-// Unmarshal reads b's binary representation from buf.
-func (b *BootImageID) Unmarshal(buf *uio.Lexer) error {
- byte0 := buf.Read8()
- _ = buf.Read8()
- b.IsInstall = byte0&0x80 != 0
- b.ImageType = BootImageType(byte0 & 0x7f)
- b.Index = buf.Read16()
- return buf.Error()
-}
-
-// BootImage describes a boot image - contains the boot image ID and the name.
-type BootImage struct {
- ID BootImageID
- Name string
-}
-
-// Marshal write a BootImage to buf.
-func (b BootImage) Marshal(buf *uio.Lexer) {
- b.ID.Marshal(buf)
- buf.Write8(uint8(len(b.Name)))
- buf.WriteBytes([]byte(b.Name))
-}
-
-// String converts a BootImage to a human-readable representation.
-func (b BootImage) String() string {
- return fmt.Sprintf("%v %v", b.Name, b.ID.String())
-}
-
-// Unmarshal reads data from buf into b.
-func (b *BootImage) Unmarshal(buf *uio.Lexer) error {
- if err := (&b.ID).Unmarshal(buf); err != nil {
- return err
- }
- nameLength := buf.Read8()
- b.Name = string(buf.Consume(int(nameLength)))
- return buf.Error()
-}
-
-func getBootImageID(code dhcpv4.OptionCode, o dhcpv4.Options) *BootImageID {
- v := o.Get(code)
- if v == nil {
- return nil
- }
- var b BootImageID
- if err := uio.FromBigEndian(&b, v); err != nil {
- return nil
- }
- return &b
-}
-
-// OptDefaultBootImageID returns a new default boot image ID option as per
-// BSDP.
-func OptDefaultBootImageID(b BootImageID) dhcpv4.Option {
- return dhcpv4.Option{Code: OptionDefaultBootImageID, Value: b}
-}
-
-// OptSelectedBootImageID returns a new selected boot image ID option as per
-// BSDP.
-func OptSelectedBootImageID(b BootImageID) dhcpv4.Option {
- return dhcpv4.Option{Code: OptionSelectedBootImageID, Value: b}
-}
diff --git a/dhcpv4/bsdp/boot_image_test.go b/dhcpv4/bsdp/boot_image_test.go
deleted file mode 100644
index 0b51287..0000000
--- a/dhcpv4/bsdp/boot_image_test.go
+++ /dev/null
@@ -1,139 +0,0 @@
-package bsdp
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
- "github.com/u-root/uio/uio"
-)
-
-func TestBootImageIDToBytes(t *testing.T) {
- b := BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOSX,
- Index: 0x1000,
- }
- actual := uio.ToBigEndian(b)
- expected := []byte{0x81, 0, 0x10, 0}
- require.Equal(t, expected, actual)
-
- b.IsInstall = false
- actual = uio.ToBigEndian(b)
- expected = []byte{0x01, 0, 0x10, 0}
- require.Equal(t, expected, actual)
-}
-
-func TestBootImageIDFromBytes(t *testing.T) {
- b := BootImageID{
- IsInstall: false,
- ImageType: BootImageTypeMacOSX,
- Index: 0x1000,
- }
- var newBootImage BootImageID
- require.NoError(t, uio.FromBigEndian(&newBootImage, uio.ToBigEndian(b)))
- require.Equal(t, b, newBootImage)
-
- b = BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOSX,
- Index: 0x1011,
- }
- require.NoError(t, uio.FromBigEndian(&newBootImage, uio.ToBigEndian(b)))
- require.Equal(t, b, newBootImage)
-}
-
-func TestBootImageIDFromBytesFail(t *testing.T) {
- serialized := []byte{0x81, 0, 0x10} // intentionally left short
- var deserialized BootImageID
- require.Error(t, uio.FromBigEndian(&deserialized, serialized))
-}
-
-func TestBootImageIDString(t *testing.T) {
- b := BootImageID{IsInstall: false, ImageType: BootImageTypeMacOSX, Index: 1001}
- require.Equal(t, "[1001] uninstallable macOS image", b.String())
-}
-
-/*
- * BootImage
- */
-func TestBootImageToBytes(t *testing.T) {
- b := BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOSX,
- Index: 0x1000,
- },
- Name: "bsdp-1",
- }
- expected := []byte{
- 0x81, 0, 0x10, 0, // boot image ID
- 6, // len(Name)
- 98, 115, 100, 112, 45, 49, // byte-encoding of Name
- }
- actual := uio.ToBigEndian(b)
- require.Equal(t, expected, actual)
-
- b = BootImage{
- ID: BootImageID{
- IsInstall: false,
- ImageType: BootImageTypeMacOSX,
- Index: 0x1010,
- },
- Name: "bsdp-21",
- }
- expected = []byte{
- 0x1, 0, 0x10, 0x10, // boot image ID
- 7, // len(Name)
- 98, 115, 100, 112, 45, 50, 49, // byte-encoding of Name
- }
- actual = uio.ToBigEndian(b)
- require.Equal(t, expected, actual)
-}
-
-func TestBootImageFromBytes(t *testing.T) {
- input := []byte{
- 0x1, 0, 0x10, 0x10, // boot image ID
- 7, // len(Name)
- 98, 115, 100, 112, 45, 50, 49, // byte-encoding of Name
- }
- var b BootImage
- require.NoError(t, uio.FromBigEndian(&b, input))
- expectedBootImage := BootImage{
- ID: BootImageID{
- IsInstall: false,
- ImageType: BootImageTypeMacOSX,
- Index: 0x1010,
- },
- Name: "bsdp-21",
- }
- require.Equal(t, expectedBootImage, b)
-}
-
-func TestBootImageFromBytesOnlyBootImageID(t *testing.T) {
- // Only a BootImageID, nothing else.
- input := []byte{0x1, 0, 0x10, 0x10}
- var b BootImage
- require.Error(t, uio.FromBigEndian(&b, input))
-}
-
-func TestBootImageFromBytesShortBootImage(t *testing.T) {
- input := []byte{
- 0x1, 0, 0x10, 0x10, // boot image ID
- 7, // len(Name)
- 98, 115, 100, 112, 45, 50, // Name bytes (intentionally off-by-one)
- }
- var b BootImage
- require.Error(t, uio.FromBigEndian(&b, input))
-}
-
-func TestBootImageString(t *testing.T) {
- b := BootImage{
- ID: BootImageID{
- IsInstall: false,
- ImageType: BootImageTypeMacOSX,
- Index: 0x1010,
- },
- Name: "bsdp-21",
- }
- require.Equal(t, "bsdp-21 [4112] uninstallable macOS image", b.String())
-}
diff --git a/dhcpv4/bsdp/bsdp.go b/dhcpv4/bsdp/bsdp.go
deleted file mode 100644
index d90e884..0000000
--- a/dhcpv4/bsdp/bsdp.go
+++ /dev/null
@@ -1,254 +0,0 @@
-package bsdp
-
-import (
- "errors"
- "fmt"
- "net"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
-)
-
-// MaxDHCPMessageSize is the size set in DHCP option 57 (DHCP Maximum Message Size).
-// BSDP includes its own sub-option (12) to indicate to NetBoot servers that the
-// client can support larger message sizes, and modern NetBoot servers will
-// prefer this BSDP-specific option over the DHCP standard option.
-const MaxDHCPMessageSize = 1500
-
-// AppleVendorID is the string constant set in the vendor class identifier (DHCP
-// option 60) that is sent by the server.
-const AppleVendorID = "AAPLBSDPC"
-
-// ReplyConfig is a struct containing some common configuration values for a
-// BSDP reply (ACK).
-type ReplyConfig struct {
- ServerIP net.IP
- ServerHostname, BootFileName string
- ServerPriority uint16
- Images []BootImage
- DefaultImage, SelectedImage *BootImage
-}
-
-// ParseBootImageListFromAck parses the list of boot images presented in the
-// ACK[LIST] packet and returns them as a list of BootImages.
-func ParseBootImageListFromAck(ack *dhcpv4.DHCPv4) ([]BootImage, error) {
- vendorOpts := GetVendorOptions(ack.Options)
- if vendorOpts == nil {
- return nil, errors.New("ParseBootImageListFromAck: could not find vendor-specific option")
- }
- return vendorOpts.BootImageList(), nil
-}
-
-func needsReplyPort(replyPort uint16) bool {
- return replyPort != 0 && replyPort != dhcpv4.ClientPort
-}
-
-// MessageTypeFromPacket extracts the BSDP message type (LIST, SELECT) from the
-// vendor-specific options and returns it. If the message type option cannot be
-// found, returns false.
-func MessageTypeFromPacket(packet *dhcpv4.DHCPv4) MessageType {
- vendorOpts := GetVendorOptions(packet.Options)
- if vendorOpts == nil {
- return MessageTypeNone
- }
- return vendorOpts.MessageType()
-}
-
-// Packet is a BSDP packet wrapper around a DHCPv4 packet in order to print the
-// correct vendor-specific BSDP information in String().
-type Packet struct {
- dhcpv4.DHCPv4
-}
-
-// PacketFor returns a wrapped BSDP Packet given a DHCPv4 packet.
-func PacketFor(d *dhcpv4.DHCPv4) *Packet {
- return &Packet{*d}
-}
-
-func (p Packet) v4() *dhcpv4.DHCPv4 {
- return &p.DHCPv4
-}
-
-func (p Packet) String() string {
- return p.DHCPv4.String()
-}
-
-// Summary prints the BSDP packet with the correct vendor-specific options.
-func (p Packet) Summary() string {
- return p.DHCPv4.SummaryWithVendor(&VendorOptions{})
-}
-
-// NewInformListForInterface creates a new INFORM packet for interface ifname
-// with configuration options specified by config.
-func NewInformListForInterface(ifname string, replyPort uint16) (*Packet, error) {
- iface, err := net.InterfaceByName(ifname)
- if err != nil {
- return nil, err
- }
- // Get currently configured IP.
- addrs, err := iface.Addrs()
- if err != nil {
- return nil, err
- }
- localIPs, err := dhcpv4.GetExternalIPv4Addrs(addrs)
- if err != nil {
- return nil, fmt.Errorf("could not get local IPv4 addr for %s: %v", iface.Name, err)
- }
- if len(localIPs) == 0 {
- return nil, fmt.Errorf("could not get local IPv4 addr for %s", iface.Name)
- }
- return NewInformList(iface.HardwareAddr, localIPs[0], replyPort)
-}
-
-// NewInformList creates a new INFORM packet for interface with hardware address
-// `hwaddr` and IP `localIP`. Packet will be sent out on port `replyPort`.
-func NewInformList(hwaddr net.HardwareAddr, localIP net.IP, replyPort uint16, modifiers ...dhcpv4.Modifier) (*Packet, error) {
- // Validate replyPort first
- if needsReplyPort(replyPort) && replyPort >= 1024 {
- return nil, errors.New("replyPort must be a privileged port")
- }
-
- vendorClassID, err := MakeVendorClassIdentifier()
- if err != nil {
- return nil, err
- }
-
- // These are vendor-specific options used to pass along BSDP information.
- vendorOpts := []dhcpv4.Option{
- OptMessageType(MessageTypeList),
- OptVersion(Version1_1),
- }
- if needsReplyPort(replyPort) {
- vendorOpts = append(vendorOpts, OptReplyPort(replyPort))
- }
-
- d, err := dhcpv4.NewInform(hwaddr, localIP,
- dhcpv4.PrependModifiers(modifiers, dhcpv4.WithRequestedOptions(
- dhcpv4.OptionVendorSpecificInformation,
- dhcpv4.OptionClassIdentifier,
- ),
- dhcpv4.WithOption(dhcpv4.OptMaxMessageSize(MaxDHCPMessageSize)),
- dhcpv4.WithOption(dhcpv4.OptClassIdentifier(vendorClassID)),
- dhcpv4.WithOption(OptVendorOptions(vendorOpts...)),
- )...)
- if err != nil {
- return nil, err
- }
- return PacketFor(d), nil
-}
-
-// InformSelectForAck constructs an INFORM[SELECT] packet given an ACK to the
-// previously-sent INFORM[LIST].
-func InformSelectForAck(ack *Packet, replyPort uint16, selectedImage BootImage) (*Packet, error) {
- if needsReplyPort(replyPort) && replyPort >= 1024 {
- return nil, errors.New("replyPort must be a privileged port")
- }
-
- // Data for OptionSelectedBootImageID
- vendorOpts := []dhcpv4.Option{
- OptMessageType(MessageTypeSelect),
- OptVersion(Version1_1),
- OptSelectedBootImageID(selectedImage.ID),
- }
-
- // Validate replyPort if requested.
- if needsReplyPort(replyPort) {
- vendorOpts = append(vendorOpts, OptReplyPort(replyPort))
- }
-
- // Find server IP address
- serverIP := ack.ServerIdentifier()
- if serverIP.To4() == nil {
- return nil, fmt.Errorf("could not parse server identifier from ACK")
- }
- vendorOpts = append(vendorOpts, OptServerIdentifier(serverIP))
-
- vendorClassID, err := MakeVendorClassIdentifier()
- if err != nil {
- return nil, err
- }
-
- d, err := dhcpv4.New(dhcpv4.WithReply(ack.v4()),
- dhcpv4.WithOption(dhcpv4.OptClassIdentifier(vendorClassID)),
- dhcpv4.WithRequestedOptions(
- dhcpv4.OptionSubnetMask,
- dhcpv4.OptionRouter,
- dhcpv4.OptionBootfileName,
- dhcpv4.OptionVendorSpecificInformation,
- dhcpv4.OptionClassIdentifier,
- ),
- dhcpv4.WithMessageType(dhcpv4.MessageTypeInform),
- dhcpv4.WithOption(OptVendorOptions(vendorOpts...)),
- )
- if err != nil {
- return nil, err
- }
- return PacketFor(d), nil
-}
-
-// NewReplyForInformList constructs an ACK for the INFORM[LIST] packet `inform`
-// with additional options in `config`.
-func NewReplyForInformList(inform *Packet, config ReplyConfig) (*Packet, error) {
- if config.DefaultImage == nil {
- return nil, errors.New("NewReplyForInformList: no default boot image ID set")
- }
- if config.Images == nil || len(config.Images) == 0 {
- return nil, errors.New("NewReplyForInformList: no boot images provided")
- }
- reply, err := dhcpv4.NewReplyFromRequest(&inform.DHCPv4)
- if err != nil {
- return nil, err
- }
- reply.ClientIPAddr = inform.ClientIPAddr
- reply.GatewayIPAddr = inform.GatewayIPAddr
- reply.ServerIPAddr = config.ServerIP
- reply.ServerHostName = config.ServerHostname
-
- reply.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
- reply.UpdateOption(dhcpv4.OptServerIdentifier(config.ServerIP))
- reply.UpdateOption(dhcpv4.OptClassIdentifier(AppleVendorID))
-
- // BSDP opts.
- vendorOpts := []dhcpv4.Option{
- OptMessageType(MessageTypeList),
- OptServerPriority(config.ServerPriority),
- OptDefaultBootImageID(config.DefaultImage.ID),
- OptBootImageList(config.Images...),
- }
- if config.SelectedImage != nil {
- vendorOpts = append(vendorOpts, OptSelectedBootImageID(config.SelectedImage.ID))
- }
- reply.UpdateOption(OptVendorOptions(vendorOpts...))
- return PacketFor(reply), nil
-}
-
-// NewReplyForInformSelect constructs an ACK for the INFORM[Select] packet
-// `inform` with additional options in `config`.
-func NewReplyForInformSelect(inform *Packet, config ReplyConfig) (*Packet, error) {
- if config.SelectedImage == nil {
- return nil, errors.New("NewReplyForInformSelect: no selected boot image ID set")
- }
- if config.Images == nil || len(config.Images) == 0 {
- return nil, errors.New("NewReplyForInformSelect: no boot images provided")
- }
- reply, err := dhcpv4.NewReplyFromRequest(&inform.DHCPv4)
- if err != nil {
- return nil, err
- }
-
- reply.ClientIPAddr = inform.ClientIPAddr
- reply.GatewayIPAddr = inform.GatewayIPAddr
- reply.ServerIPAddr = config.ServerIP
- reply.ServerHostName = config.ServerHostname
- reply.BootFileName = config.BootFileName
-
- reply.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
- reply.UpdateOption(dhcpv4.OptServerIdentifier(config.ServerIP))
- reply.UpdateOption(dhcpv4.OptClassIdentifier(AppleVendorID))
-
- // BSDP opts.
- reply.UpdateOption(OptVendorOptions(
- OptMessageType(MessageTypeSelect),
- OptSelectedBootImageID(config.SelectedImage.ID),
- ))
- return PacketFor(reply), nil
-}
diff --git a/dhcpv4/bsdp/bsdp_option_boot_image_list.go b/dhcpv4/bsdp/bsdp_option_boot_image_list.go
deleted file mode 100644
index ed70243..0000000
--- a/dhcpv4/bsdp/bsdp_option_boot_image_list.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package bsdp
-
-import (
- "strings"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/u-root/uio/uio"
-)
-
-// BootImageList contains a list of boot images presented by a netboot server.
-//
-// Implements the BSDP option listing the boot images.
-type BootImageList []BootImage
-
-// FromBytes deserializes data into bil.
-func (bil *BootImageList) FromBytes(data []byte) error {
- buf := uio.NewBigEndianBuffer(data)
-
- for buf.Has(5) {
- var image BootImage
- if err := image.Unmarshal(buf); err != nil {
- return err
- }
- *bil = append(*bil, image)
- }
- return nil
-}
-
-// ToBytes returns a serialized stream of bytes for this option.
-func (bil BootImageList) ToBytes() []byte {
- buf := uio.NewBigEndianBuffer(nil)
- for _, image := range bil {
- image.Marshal(buf)
- }
- return buf.Data()
-}
-
-// String returns a human-readable string for this option.
-func (bil BootImageList) String() string {
- s := make([]string, 0, len(bil))
- for _, image := range bil {
- s = append(s, image.String())
- }
- return strings.Join(s, ", ")
-}
-
-// OptBootImageList returns a new BSDP boot image list.
-func OptBootImageList(b ...BootImage) dhcpv4.Option {
- return dhcpv4.Option{
- Code: OptionBootImageList,
- Value: BootImageList(b),
- }
-}
diff --git a/dhcpv4/bsdp/bsdp_option_boot_image_list_test.go b/dhcpv4/bsdp/bsdp_option_boot_image_list_test.go
deleted file mode 100644
index 6282156..0000000
--- a/dhcpv4/bsdp/bsdp_option_boot_image_list_test.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package bsdp
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestOptBootImageListInterfaceMethods(t *testing.T) {
- bs := []BootImage{
- BootImage{
- ID: BootImageID{
- IsInstall: false,
- ImageType: BootImageTypeMacOSX,
- Index: 1001,
- },
- Name: "bsdp-1",
- },
- BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOS9,
- Index: 9009,
- },
- Name: "bsdp-2",
- },
- }
- o := OptBootImageList(bs...)
- require.Equal(t, OptionBootImageList, o.Code, "Code")
- expectedBytes := []byte{
- // boot image 1
- 0x1, 0x0, 0x03, 0xe9, // ID
- 6, // name length
- 'b', 's', 'd', 'p', '-', '1',
- // boot image 1
- 0x80, 0x0, 0x23, 0x31, // ID
- 6, // name length
- 'b', 's', 'd', 'p', '-', '2',
- }
- require.Equal(t, expectedBytes, o.Value.ToBytes(), "ToBytes")
-}
-
-func TestParseOptBootImageList(t *testing.T) {
- data := []byte{
- // boot image 1
- 0x1, 0x0, 0x03, 0xe9, // ID
- 6, // name length
- 'b', 's', 'd', 'p', '-', '1',
- // boot image 1
- 0x80, 0x0, 0x23, 0x31, // ID
- 6, // name length
- 'b', 's', 'd', 'p', '-', '2',
- }
- var o BootImageList
- err := o.FromBytes(data)
- require.NoError(t, err)
- expectedBootImages := BootImageList{
- BootImage{
- ID: BootImageID{
- IsInstall: false,
- ImageType: BootImageTypeMacOSX,
- Index: 1001,
- },
- Name: "bsdp-1",
- },
- BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOS9,
- Index: 9009,
- },
- Name: "bsdp-2",
- },
- }
- require.Equal(t, expectedBootImages, o)
-
- // Error parsing boot image (malformed)
- data = []byte{
- // boot image 1
- 0x1, 0x0, 0x03, 0xe9, // ID
- 4, // name length
- 'b', 's', 'd', 'p', '-', '1',
- // boot image 2
- 0x80, 0x0, 0x23, 0x31, // ID
- 6, // name length
- 'b', 's', 'd', 'p', '-', '2',
- }
- err = o.FromBytes(data)
- require.Error(t, err, "should get error from bad boot image")
-}
-
-func TestOptBootImageListString(t *testing.T) {
- bs := []BootImage{
- BootImage{
- ID: BootImageID{
- IsInstall: false,
- ImageType: BootImageTypeMacOSX,
- Index: 1001,
- },
- Name: "bsdp-1",
- },
- BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOS9,
- Index: 9009,
- },
- Name: "bsdp-2",
- },
- }
- o := OptBootImageList(bs...)
- expectedString := "BSDP Boot Image List: bsdp-1 [1001] uninstallable macOS image, bsdp-2 [9009] installable macOS 9 image"
- require.Equal(t, expectedString, o.String())
-}
diff --git a/dhcpv4/bsdp/bsdp_option_message_type.go b/dhcpv4/bsdp/bsdp_option_message_type.go
deleted file mode 100644
index 541cd96..0000000
--- a/dhcpv4/bsdp/bsdp_option_message_type.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package bsdp
-
-import (
- "fmt"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/u-root/uio/uio"
-)
-
-// MessageType represents the different BSDP message types.
-//
-// Implements the BSDP option message type. Can be one of LIST, SELECT, or
-// FAILED.
-type MessageType byte
-
-// BSDP Message types - e.g. LIST, SELECT, FAILED
-const (
- MessageTypeNone MessageType = 0
- MessageTypeList MessageType = 1
- MessageTypeSelect MessageType = 2
- MessageTypeFailed MessageType = 3
-)
-
-// ToBytes returns a serialized stream of bytes for this option.
-func (m MessageType) ToBytes() []byte {
- return []byte{byte(m)}
-}
-
-// String returns a human-friendly representation of MessageType.
-func (m MessageType) String() string {
- if s, ok := messageTypeToString[m]; ok {
- return s
- }
- return fmt.Sprintf("unknown (%d)", m)
-}
-
-// messageTypeToString maps each BSDP message type to a human-readable string.
-var messageTypeToString = map[MessageType]string{
- MessageTypeList: "LIST",
- MessageTypeSelect: "SELECT",
- MessageTypeFailed: "FAILED",
-}
-
-// FromBytes reads data into m.
-func (m *MessageType) FromBytes(data []byte) error {
- buf := uio.NewBigEndianBuffer(data)
- *m = MessageType(buf.Read8())
- return buf.FinError()
-}
-
-// OptMessageType returns a new BSDP Message Type option.
-func OptMessageType(mt MessageType) dhcpv4.Option {
- return dhcpv4.Option{
- Code: OptionMessageType,
- Value: mt,
- }
-}
diff --git a/dhcpv4/bsdp/bsdp_option_message_type_test.go b/dhcpv4/bsdp/bsdp_option_message_type_test.go
deleted file mode 100644
index 6666137..0000000
--- a/dhcpv4/bsdp/bsdp_option_message_type_test.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package bsdp
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestOptMessageTypeInterfaceMethods(t *testing.T) {
- o := OptMessageType(MessageTypeList)
- require.Equal(t, OptionMessageType, o.Code, "Code")
- require.Equal(t, []byte{1}, o.Value.ToBytes(), "ToBytes")
-}
-
-func TestParseOptMessageType(t *testing.T) {
- var o MessageType
- data := []byte{1} // DISCOVER
- err := o.FromBytes(data)
- require.NoError(t, err)
- require.Equal(t, MessageTypeList, o)
-}
-
-func TestOptMessageTypeString(t *testing.T) {
- // known
- o := OptMessageType(MessageTypeList)
- require.Equal(t, "BSDP Message Type: LIST", o.String())
-
- // unknown
- o = OptMessageType(99)
- require.Equal(t, "BSDP Message Type: unknown (99)", o.String())
-}
diff --git a/dhcpv4/bsdp/bsdp_option_misc.go b/dhcpv4/bsdp/bsdp_option_misc.go
deleted file mode 100644
index 3848a6c..0000000
--- a/dhcpv4/bsdp/bsdp_option_misc.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package bsdp
-
-import (
- "fmt"
- "net"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/u-root/uio/uio"
-)
-
-// OptReplyPort returns a new BSDP reply port option.
-//
-// Implements the BSDP option reply port. This is used when BSDP responses
-// should be sent to a reply port other than the DHCP default. The macOS GUI
-// "Startup Disk Select" sends this option since it's operating in an
-// unprivileged context.
-func OptReplyPort(port uint16) dhcpv4.Option {
- return dhcpv4.Option{Code: OptionReplyPort, Value: dhcpv4.Uint16(port)}
-}
-
-// OptServerPriority returns a new BSDP server priority option.
-func OptServerPriority(prio uint16) dhcpv4.Option {
- return dhcpv4.Option{Code: OptionServerPriority, Value: dhcpv4.Uint16(prio)}
-}
-
-// OptMachineName returns a BSDP Machine Name option.
-func OptMachineName(name string) dhcpv4.Option {
- return dhcpv4.Option{Code: OptionMachineName, Value: dhcpv4.String(name)}
-}
-
-// Version is the BSDP protocol version. Can be one of 1.0 or 1.1.
-type Version [2]byte
-
-// Specific versions.
-var (
- Version1_0 = Version{1, 0}
- Version1_1 = Version{1, 1}
-)
-
-// ToBytes returns a serialized stream of bytes for this option.
-func (o Version) ToBytes() []byte {
- return o[:]
-}
-
-// String returns a human-readable string for this option.
-func (o Version) String() string {
- return fmt.Sprintf("%d.%d", o[0], o[1])
-}
-
-// FromBytes constructs a Version struct from a sequence of
-// bytes and returns it, or an error.
-func (o *Version) FromBytes(data []byte) error {
- buf := uio.NewBigEndianBuffer(data)
- buf.ReadBytes(o[:])
- return buf.FinError()
-}
-
-// OptVersion returns a new BSDP version option.
-func OptVersion(version Version) dhcpv4.Option {
- return dhcpv4.Option{Code: OptionVersion, Value: version}
-}
-
-// OptServerIdentifier returns a new BSDP Server Identifier option.
-func OptServerIdentifier(ip net.IP) dhcpv4.Option {
- return dhcpv4.Option{Code: OptionServerIdentifier, Value: dhcpv4.IP(ip)}
-}
diff --git a/dhcpv4/bsdp/bsdp_option_misc_test.go b/dhcpv4/bsdp/bsdp_option_misc_test.go
deleted file mode 100644
index 675a5db..0000000
--- a/dhcpv4/bsdp/bsdp_option_misc_test.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package bsdp
-
-import (
- "net"
- "testing"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/stretchr/testify/require"
-)
-
-func TestOptReplyPort(t *testing.T) {
- o := OptReplyPort(1234)
- require.Equal(t, OptionReplyPort, o.Code, "Code")
- require.Equal(t, []byte{4, 210}, o.Value.ToBytes(), "ToBytes")
- require.Equal(t, "BSDP Reply Port: 1234", o.String())
-}
-
-func TestGetReplyPort(t *testing.T) {
- o := VendorOptions{dhcpv4.OptionsFromList(OptReplyPort(1234))}
- port, err := o.ReplyPort()
- require.NoError(t, err)
- require.Equal(t, uint16(1234), port)
-
- o = VendorOptions{dhcpv4.Options{}}
- _, err = o.ReplyPort()
- require.Error(t, err, "no reply port present")
-}
-
-func TestOptServerPriority(t *testing.T) {
- o := OptServerPriority(1234)
- require.Equal(t, OptionServerPriority, o.Code, "Code")
- require.Equal(t, []byte{4, 210}, o.Value.ToBytes(), "ToBytes")
- require.Equal(t, "BSDP Server Priority: 1234", o.String())
-}
-
-func TestGetServerPriority(t *testing.T) {
- o := VendorOptions{dhcpv4.OptionsFromList(OptServerPriority(1234))}
- prio, err := o.ServerPriority()
- require.NoError(t, err)
- require.Equal(t, uint16(1234), prio)
-
- o = VendorOptions{dhcpv4.Options{}}
- _, err = o.ServerPriority()
- require.Error(t, err, "no server prio present")
-}
-
-func TestOptMachineName(t *testing.T) {
- o := OptMachineName("foo")
- require.Equal(t, OptionMachineName, o.Code, "Code")
- require.Equal(t, []byte("foo"), o.Value.ToBytes(), "ToBytes")
- require.Equal(t, "BSDP Machine Name: foo", o.String())
-}
-
-func TestGetMachineName(t *testing.T) {
- o := VendorOptions{dhcpv4.OptionsFromList(OptMachineName("foo"))}
- require.Equal(t, "foo", o.MachineName())
-
- o = VendorOptions{dhcpv4.Options{}}
- require.Equal(t, "", o.MachineName())
-}
-
-func TestOptVersion(t *testing.T) {
- o := OptVersion(Version1_1)
- require.Equal(t, OptionVersion, o.Code, "Code")
- require.Equal(t, []byte{1, 1}, o.Value.ToBytes(), "ToBytes")
- require.Equal(t, "BSDP Version: 1.1", o.String())
-}
-
-func TestGetVersion(t *testing.T) {
- o := VendorOptions{dhcpv4.OptionsFromList(OptVersion(Version1_1))}
- ver, err := o.Version()
- require.NoError(t, err)
- require.Equal(t, ver, Version1_1)
-
- o = VendorOptions{dhcpv4.Options{}}
- _, err = o.Version()
- require.Error(t, err, "no version present")
-
- o = VendorOptions{dhcpv4.Options{OptionVersion.Code(): []byte{}}}
- _, err = o.Version()
- require.Error(t, err, "empty version field")
-
- o = VendorOptions{dhcpv4.Options{OptionVersion.Code(): []byte{1}}}
- _, err = o.Version()
- require.Error(t, err, "version option too short")
-
- o = VendorOptions{dhcpv4.Options{OptionVersion.Code(): []byte{1, 2, 3}}}
- _, err = o.Version()
- require.Error(t, err, "version option too long")
-}
-
-func TestOptServerIdentifier(t *testing.T) {
- o := OptServerIdentifier(net.IP{1, 1, 1, 1})
- require.Equal(t, OptionServerIdentifier, o.Code, "Code")
- require.Equal(t, []byte{1, 1, 1, 1}, o.Value.ToBytes(), "ToBytes")
- require.Equal(t, "BSDP Server Identifier: 1.1.1.1", o.String())
-}
-
-func TestGetServerIdentifier(t *testing.T) {
- o := VendorOptions{dhcpv4.OptionsFromList(OptServerIdentifier(net.IP{1, 1, 1, 1}))}
- require.Equal(t, net.IP{1, 1, 1, 1}, o.ServerIdentifier())
-
- o = VendorOptions{dhcpv4.Options{}}
- require.Nil(t, o.ServerIdentifier())
-}
diff --git a/dhcpv4/bsdp/bsdp_test.go b/dhcpv4/bsdp/bsdp_test.go
deleted file mode 100644
index 05cd85c..0000000
--- a/dhcpv4/bsdp/bsdp_test.go
+++ /dev/null
@@ -1,410 +0,0 @@
-package bsdp
-
-import (
- "net"
- "testing"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/insomniacslk/dhcp/iana"
- "github.com/stretchr/testify/require"
-)
-
-func RequireHasOption(t *testing.T, opts dhcpv4.Options, opt dhcpv4.Option) {
- require.NotNil(t, opts, "must pass list of options")
- require.NotNil(t, opt, "must pass option")
- require.True(t, opts.Has(opt.Code))
- actual := opts.Get(opt.Code)
- require.Equal(t, opt.Value.ToBytes(), actual)
-}
-
-func TestParseBootImageListFromAck(t *testing.T) {
- expectedBootImages := []BootImage{
- BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOSX,
- Index: 0x1010,
- },
- Name: "bsdp-1",
- },
- BootImage{
- ID: BootImageID{
- IsInstall: false,
- ImageType: BootImageTypeMacOS9,
- Index: 0x1111,
- },
- Name: "bsdp-2",
- },
- }
- ack, _ := dhcpv4.New()
- ack.UpdateOption(OptVendorOptions(
- OptBootImageList(expectedBootImages...),
- ))
-
- images, err := ParseBootImageListFromAck(ack)
- require.NoError(t, err)
- require.NotEmpty(t, images, "should get BootImages")
- require.Equal(t, expectedBootImages, images, "should get same BootImages")
-}
-
-func TestParseBootImageListFromAckNoVendorOption(t *testing.T) {
- ack, _ := dhcpv4.New()
- images, err := ParseBootImageListFromAck(ack)
- require.Error(t, err)
- require.Empty(t, images, "no BootImages")
-}
-
-func TestNeedsReplyPort(t *testing.T) {
- require.True(t, needsReplyPort(123))
- require.False(t, needsReplyPort(0))
- require.False(t, needsReplyPort(dhcpv4.ClientPort))
-}
-
-func TestNewInformList_NoReplyPort(t *testing.T) {
- hwAddr := net.HardwareAddr{1, 2, 3, 4, 5, 6}
- localIP := net.IPv4(10, 10, 11, 11)
- m, err := NewInformList(hwAddr, localIP, 0)
-
- require.NoError(t, err)
- require.True(t, m.Options.Has(dhcpv4.OptionVendorSpecificInformation))
- require.True(t, m.Options.Has(dhcpv4.OptionParameterRequestList))
- require.True(t, m.Options.Has(dhcpv4.OptionMaximumDHCPMessageSize))
-
- vendorOpts := GetVendorOptions(m.Options)
- require.NotNil(t, vendorOpts, "vendor opts not present")
- require.True(t, vendorOpts.Has(OptionMessageType))
- require.True(t, vendorOpts.Has(OptionVersion))
-
- mt := vendorOpts.MessageType()
- require.Equal(t, MessageTypeList, mt)
-}
-
-func TestNewInformList_ReplyPort(t *testing.T) {
- hwAddr := net.HardwareAddr{1, 2, 3, 4, 5, 6}
- localIP := net.IPv4(10, 10, 11, 11)
- replyPort := uint16(11223)
-
- // Bad reply port
- _, err := NewInformList(hwAddr, localIP, replyPort)
- require.Error(t, err)
-
- // Good reply port
- replyPort = uint16(999)
- m, err := NewInformList(hwAddr, localIP, replyPort)
- require.NoError(t, err)
-
- vendorOpts := GetVendorOptions(m.Options)
- require.True(t, vendorOpts.Options.Has(OptionReplyPort))
-
- port, err := vendorOpts.ReplyPort()
- require.NoError(t, err)
- require.Equal(t, replyPort, port)
-}
-
-func newAck(hwAddr net.HardwareAddr, transactionID [4]byte) *dhcpv4.DHCPv4 {
- ack, _ := dhcpv4.New()
- ack.OpCode = dhcpv4.OpcodeBootReply
- ack.TransactionID = transactionID
- ack.HWType = iana.HWTypeEthernet
- ack.ClientHWAddr = hwAddr
- ack.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
- return ack
-}
-
-func TestInformSelectForAck_Broadcast(t *testing.T) {
- hwAddr := net.HardwareAddr{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}
- tid := [4]byte{0x22, 0, 0, 0}
- serverID := net.IPv4(1, 2, 3, 4)
- bootImage := BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOSX,
- Index: 0x1000,
- },
- Name: "bsdp-1",
- }
- ack := newAck(hwAddr, tid)
- ack.SetBroadcast()
- ack.UpdateOption(dhcpv4.OptServerIdentifier(serverID))
-
- m, err := InformSelectForAck(PacketFor(ack), 0, bootImage)
- require.NoError(t, err)
- require.Equal(t, dhcpv4.OpcodeBootRequest, m.OpCode)
- require.Equal(t, ack.HWType, m.HWType)
- require.Equal(t, ack.ClientHWAddr, m.ClientHWAddr)
- require.Equal(t, ack.TransactionID, m.TransactionID)
- require.True(t, m.IsBroadcast())
-
- // Validate options.
- require.True(t, m.Options.Has(dhcpv4.OptionClassIdentifier))
- require.True(t, m.Options.Has(dhcpv4.OptionParameterRequestList))
- require.True(t, m.Options.Has(dhcpv4.OptionDHCPMessageType))
- mt := m.MessageType()
- require.Equal(t, dhcpv4.MessageTypeInform, mt)
-
- // Validate vendor opts.
- require.True(t, m.Options.Has(dhcpv4.OptionVendorSpecificInformation))
- vendorOpts := GetVendorOptions(m.Options).Options
- RequireHasOption(t, vendorOpts, OptMessageType(MessageTypeSelect))
- require.True(t, vendorOpts.Has(OptionVersion))
- RequireHasOption(t, vendorOpts, OptSelectedBootImageID(bootImage.ID))
- RequireHasOption(t, vendorOpts, OptServerIdentifier(serverID))
-}
-
-func TestInformSelectForAck_NoServerID(t *testing.T) {
- hwAddr := net.HardwareAddr{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}
- tid := [4]byte{0x22, 0, 0, 0}
- bootImage := BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOSX,
- Index: 0x1000,
- },
- Name: "bsdp-1",
- }
- ack := newAck(hwAddr, tid)
-
- _, err := InformSelectForAck(PacketFor(ack), 0, bootImage)
- require.Error(t, err, "expect error for no server identifier option")
-}
-
-func TestInformSelectForAck_BadReplyPort(t *testing.T) {
- hwAddr := net.HardwareAddr{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}
- tid := [4]byte{0x22, 0, 0, 0}
- serverID := net.IPv4(1, 2, 3, 4)
- bootImage := BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOSX,
- Index: 0x1000,
- },
- Name: "bsdp-1",
- }
- ack := newAck(hwAddr, tid)
- ack.SetBroadcast()
- ack.UpdateOption(dhcpv4.OptServerIdentifier(serverID))
-
- _, err := InformSelectForAck(PacketFor(ack), 11223, bootImage)
- require.Error(t, err, "expect error for > 1024 replyPort")
-}
-
-func TestInformSelectForAck_ReplyPort(t *testing.T) {
- hwAddr := net.HardwareAddr{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}
- tid := [4]byte{0x22, 0, 0, 0}
- serverID := net.IPv4(1, 2, 3, 4)
- bootImage := BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOSX,
- Index: 0x1000,
- },
- Name: "bsdp-1",
- }
- ack := newAck(hwAddr, tid)
- ack.SetBroadcast()
- ack.UpdateOption(dhcpv4.OptServerIdentifier(serverID))
-
- replyPort := uint16(999)
- m, err := InformSelectForAck(PacketFor(ack), replyPort, bootImage)
- require.NoError(t, err)
-
- require.True(t, m.Options.Has(dhcpv4.OptionVendorSpecificInformation))
- vendorOpts := GetVendorOptions(m.Options).Options
- RequireHasOption(t, vendorOpts, OptReplyPort(replyPort))
-}
-
-func TestNewReplyForInformList_NoDefaultImage(t *testing.T) {
- inform, _ := NewInformList(net.HardwareAddr{1, 2, 3, 4, 5, 6}, net.IP{1, 2, 3, 4}, dhcpv4.ClientPort)
- _, err := NewReplyForInformList(inform, ReplyConfig{})
- require.Error(t, err)
-}
-
-func TestNewReplyForInformList_NoImages(t *testing.T) {
- inform, _ := NewInformList(net.HardwareAddr{1, 2, 3, 4, 5, 6}, net.IP{1, 2, 3, 4}, dhcpv4.ClientPort)
- fakeImage := BootImage{
- ID: BootImageID{ImageType: BootImageTypeMacOSX},
- }
- _, err := NewReplyForInformList(inform, ReplyConfig{
- Images: []BootImage{},
- DefaultImage: &fakeImage,
- })
- require.Error(t, err)
-
- _, err = NewReplyForInformList(inform, ReplyConfig{
- Images: nil,
- SelectedImage: &fakeImage,
- })
- require.Error(t, err)
-}
-
-func TestNewReplyForInformList(t *testing.T) {
- inform, _ := NewInformList(net.HardwareAddr{1, 2, 3, 4, 5, 6}, net.IP{1, 2, 3, 4}, dhcpv4.ClientPort)
- images := []BootImage{
- BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOSX,
- Index: 0x7070,
- },
- Name: "image-1",
- },
- BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOSX,
- Index: 0x8080,
- },
- Name: "image-2",
- },
- }
- config := ReplyConfig{
- Images: images,
- DefaultImage: &images[0],
- ServerIP: net.IP{9, 9, 9, 9},
- ServerHostname: "bsdp.foo.com",
- ServerPriority: 0x7070,
- }
- ack, err := NewReplyForInformList(inform, config)
- require.NoError(t, err)
- require.Equal(t, net.IP{1, 2, 3, 4}, ack.ClientIPAddr)
- require.Equal(t, net.IPv4zero, ack.YourIPAddr)
- require.Equal(t, "bsdp.foo.com", ack.ServerHostName)
-
- // Validate options.
- RequireHasOption(t, ack.Options, dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
- RequireHasOption(t, ack.Options, dhcpv4.OptServerIdentifier(net.IP{9, 9, 9, 9}))
- RequireHasOption(t, ack.Options, dhcpv4.OptClassIdentifier(AppleVendorID))
- require.NotNil(t, ack.GetOneOption(dhcpv4.OptionVendorSpecificInformation))
-
- // Vendor-specific options.
- vendorOpts := GetVendorOptions(ack.Options).Options
- RequireHasOption(t, vendorOpts, OptMessageType(MessageTypeList))
- RequireHasOption(t, vendorOpts, OptDefaultBootImageID(images[0].ID))
- RequireHasOption(t, vendorOpts, OptServerPriority(0x7070))
- RequireHasOption(t, vendorOpts, OptBootImageList(images...))
-
- // Add in selected boot image, ensure it's in the generated ACK.
- config.SelectedImage = &images[0]
- ack, err = NewReplyForInformList(inform, config)
- require.NoError(t, err)
- vendorOpts = GetVendorOptions(ack.Options).Options
- RequireHasOption(t, vendorOpts, OptSelectedBootImageID(images[0].ID))
-}
-
-func TestNewReplyForInformSelect_NoSelectedImage(t *testing.T) {
- inform, _ := NewInformList(net.HardwareAddr{1, 2, 3, 4, 5, 6}, net.IP{1, 2, 3, 4}, dhcpv4.ClientPort)
- _, err := NewReplyForInformSelect(inform, ReplyConfig{})
- require.Error(t, err)
-}
-
-func TestNewReplyForInformSelect_NoImages(t *testing.T) {
- inform, _ := NewInformList(net.HardwareAddr{1, 2, 3, 4, 5, 6}, net.IP{1, 2, 3, 4}, dhcpv4.ClientPort)
- fakeImage := BootImage{
- ID: BootImageID{ImageType: BootImageTypeMacOSX},
- }
- _, err := NewReplyForInformSelect(inform, ReplyConfig{
- Images: []BootImage{},
- SelectedImage: &fakeImage,
- })
- require.Error(t, err)
-
- _, err = NewReplyForInformSelect(inform, ReplyConfig{
- Images: nil,
- SelectedImage: &fakeImage,
- })
- require.Error(t, err)
-}
-
-func TestNewReplyForInformSelect(t *testing.T) {
- inform, _ := NewInformList(net.HardwareAddr{1, 2, 3, 4, 5, 6}, net.IP{1, 2, 3, 4}, dhcpv4.ClientPort)
- images := []BootImage{
- BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOSX,
- Index: 0x7070,
- },
- Name: "image-1",
- },
- BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOSX,
- Index: 0x8080,
- },
- Name: "image-2",
- },
- }
- config := ReplyConfig{
- Images: images,
- SelectedImage: &images[0],
- ServerIP: net.IP{9, 9, 9, 9},
- ServerHostname: "bsdp.foo.com",
- ServerPriority: 0x7070,
- }
- ack, err := NewReplyForInformSelect(inform, config)
- require.NoError(t, err)
- require.Equal(t, net.IP{1, 2, 3, 4}, ack.ClientIPAddr)
- require.Equal(t, net.IPv4zero, ack.YourIPAddr)
- require.Equal(t, "bsdp.foo.com", ack.ServerHostName)
-
- // Validate options.
- RequireHasOption(t, ack.Options, dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
- RequireHasOption(t, ack.Options, dhcpv4.OptServerIdentifier(net.IP{9, 9, 9, 9}))
- RequireHasOption(t, ack.Options, dhcpv4.OptServerIdentifier(net.IP{9, 9, 9, 9}))
- RequireHasOption(t, ack.Options, dhcpv4.OptClassIdentifier(AppleVendorID))
- require.NotNil(t, ack.GetOneOption(dhcpv4.OptionVendorSpecificInformation))
-
- vendorOpts := GetVendorOptions(ack.Options)
- RequireHasOption(t, vendorOpts.Options, OptMessageType(MessageTypeSelect))
- RequireHasOption(t, vendorOpts.Options, OptSelectedBootImageID(images[0].ID))
-}
-
-func TestMessageTypeForPacket(t *testing.T) {
- testcases := []struct {
- tcName string
- opts []dhcpv4.Option
- wantMessageType MessageType
- }{
- {
- tcName: "No options",
- opts: []dhcpv4.Option{},
- },
- {
- tcName: "Some options, no vendor opts",
- opts: []dhcpv4.Option{
- dhcpv4.OptHostName("foobar1234"),
- },
- },
- {
- tcName: "Vendor opts, no message type",
- opts: []dhcpv4.Option{
- dhcpv4.OptHostName("foobar1234"),
- OptVendorOptions(
- OptVersion(Version1_1),
- ),
- },
- },
- {
- tcName: "Vendor opts, with message type",
- opts: []dhcpv4.Option{
- dhcpv4.OptHostName("foobar1234"),
- OptVendorOptions(
- OptVersion(Version1_1),
- OptMessageType(MessageTypeList),
- ),
- },
- wantMessageType: MessageTypeList,
- },
- }
- for _, tt := range testcases {
- t.Run(tt.tcName, func(t *testing.T) {
- pkt, _ := dhcpv4.New()
- for _, opt := range tt.opts {
- pkt.UpdateOption(opt)
- }
- gotMessageType := MessageTypeFromPacket(pkt)
- require.Equal(t, tt.wantMessageType, gotMessageType)
- })
- }
-}
diff --git a/dhcpv4/bsdp/client.go b/dhcpv4/bsdp/client.go
deleted file mode 100644
index 5d0e667..0000000
--- a/dhcpv4/bsdp/client.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package bsdp
-
-import (
- "errors"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/insomniacslk/dhcp/dhcpv4/client4"
-)
-
-// Client represents a BSDP client that can perform BSDP exchanges via the
-// broadcast address.
-type Client struct {
- client4.Client
-}
-
-// NewClient constructs a new client with default read and write timeouts from
-// dhcpv4.Client.
-func NewClient() *Client {
- return &Client{Client: client4.Client{}}
-}
-
-// Exchange runs a full BSDP exchange (Inform[list], Ack, Inform[select],
-// Ack). Returns a list of DHCPv4 structures representing the exchange.
-func (c *Client) Exchange(ifname string) ([]*Packet, error) {
- conversation := make([]*Packet, 0)
-
- // Get our file descriptor for the broadcast socket.
- sendFd, err := client4.MakeBroadcastSocket(ifname)
- if err != nil {
- return conversation, err
- }
- recvFd, err := client4.MakeListeningSocket(ifname)
- if err != nil {
- return conversation, err
- }
-
- // INFORM[LIST]
- informList, err := NewInformListForInterface(ifname, dhcpv4.ClientPort)
- if err != nil {
- return conversation, err
- }
- conversation = append(conversation, informList)
-
- // ACK[LIST]
- ackForList, err := c.Client.SendReceive(sendFd, recvFd, informList.v4(), dhcpv4.MessageTypeAck)
- if err != nil {
- return conversation, err
- }
-
- // Rewrite vendor-specific option for pretty printing.
- conversation = append(conversation, PacketFor(ackForList))
-
- // Parse boot images sent back by server
- bootImages, err := ParseBootImageListFromAck(ackForList)
- if err != nil {
- return conversation, err
- }
- if len(bootImages) == 0 {
- return conversation, errors.New("got no BootImages from server")
- }
-
- // INFORM[SELECT]
- informSelect, err := InformSelectForAck(PacketFor(ackForList), dhcpv4.ClientPort, bootImages[0])
- if err != nil {
- return conversation, err
- }
- conversation = append(conversation, informSelect)
-
- // ACK[SELECT]
- ackForSelect, err := c.Client.SendReceive(sendFd, recvFd, informSelect.v4(), dhcpv4.MessageTypeAck)
- if err != nil {
- return conversation, err
- }
- return append(conversation, PacketFor(ackForSelect)), nil
-}
diff --git a/dhcpv4/bsdp/doc.go b/dhcpv4/bsdp/doc.go
deleted file mode 100644
index b3a2186..0000000
--- a/dhcpv4/bsdp/doc.go
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
-The BSDP package implements Apple's Boot Service Discovery Protocol (a
-pxe-boot-like netboot protocol for booting macOS hardware from
-network-connected servers).
-
-The Canonical implementation is defined here:
-http://opensource.apple.com/source/bootp/bootp-198.1/Documentation/BSDP.doc
-*/
-
-package bsdp
diff --git a/dhcpv4/bsdp/option_vendor_specific_information.go b/dhcpv4/bsdp/option_vendor_specific_information.go
deleted file mode 100644
index 4c2365c..0000000
--- a/dhcpv4/bsdp/option_vendor_specific_information.go
+++ /dev/null
@@ -1,157 +0,0 @@
-package bsdp
-
-import (
- "fmt"
- "net"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
-)
-
-// VendorOptions is like dhcpv4.Options, but stringifies using BSDP-specific
-// option codes.
-type VendorOptions struct {
- dhcpv4.Options
-}
-
-// String prints the contained options using BSDP-specific option code parsing.
-func (v VendorOptions) String() string {
- return v.Options.ToString(bsdpHumanizer)
-}
-
-// FromBytes parses vendor options from
-func (v *VendorOptions) FromBytes(data []byte) error {
- v.Options = make(dhcpv4.Options)
- return v.Options.FromBytes(data)
-}
-
-// DefaultBootImageID returns the default boot image ID in v.
-func (v VendorOptions) DefaultBootImageID() *BootImageID {
- return getBootImageID(OptionDefaultBootImageID, v.Options)
-}
-
-// SelectedBootImageID returns the selected boot image ID in v.
-func (v VendorOptions) SelectedBootImageID() *BootImageID {
- return getBootImageID(OptionSelectedBootImageID, v.Options)
-}
-
-// BootImageList returns the BSDP boot image list in v.
-func (v VendorOptions) BootImageList() BootImageList {
- val := v.Options.Get(OptionBootImageList)
- if val == nil {
- return nil
- }
- var bil BootImageList
- if err := bil.FromBytes(val); err != nil {
- return nil
- }
- return bil
-}
-
-// MessageType returns the BSDP Message Type in v.
-func (v VendorOptions) MessageType() MessageType {
- val := v.Options.Get(OptionMessageType)
- if val == nil {
- return MessageTypeNone
- }
- var m MessageType
- if err := m.FromBytes(val); err != nil {
- return MessageTypeNone
- }
- return m
-}
-
-// GetVersion returns the BSDP version in v if present.
-func (v VendorOptions) Version() (Version, error) {
- val := v.Options.Get(OptionVersion)
- if val == nil {
- return Version{0, 0}, fmt.Errorf("version not found")
- }
- var ver Version
- if err := ver.FromBytes(val); err != nil {
- return Version{0, 0}, err
- }
- return ver, nil
-}
-
-// GetServerIdentifier returns the BSDP Server Identifier value in v if present.
-func (v VendorOptions) ServerIdentifier() net.IP {
- return dhcpv4.GetIP(OptionServerIdentifier, v.Options)
-}
-
-// GetReplyPort returns the BSDP reply port in v if present.
-func (v VendorOptions) ReplyPort() (uint16, error) {
- return dhcpv4.GetUint16(OptionReplyPort, v.Options)
-}
-
-// GetServerPriority returns the BSDP server priority in v if present.
-func (v VendorOptions) ServerPriority() (uint16, error) {
- return dhcpv4.GetUint16(OptionServerPriority, v.Options)
-}
-
-// GetMachineName finds and parses the BSDP Machine Name option from v.
-func (v VendorOptions) MachineName() string {
- return dhcpv4.GetString(OptionMachineName, v.Options)
-}
-
-// OptVendorOptions returns the BSDP Vendor Specific Info in o.
-func OptVendorOptions(o ...dhcpv4.Option) dhcpv4.Option {
- return dhcpv4.Option{
- Code: dhcpv4.OptionVendorSpecificInformation,
- Value: VendorOptions{dhcpv4.OptionsFromList(o...)},
- }
-}
-
-// GetVendorOptions returns a new BSDP Vendor Specific Info option.
-func GetVendorOptions(o dhcpv4.Options) *VendorOptions {
- v := o.Get(dhcpv4.OptionVendorSpecificInformation)
- if v == nil {
- return nil
- }
- var vo VendorOptions
- if err := vo.FromBytes(v); err != nil {
- return nil
- }
- return &vo
-}
-
-var bsdpHumanizer = dhcpv4.OptionHumanizer{
- ValueHumanizer: parseOption,
- CodeHumanizer: func(c uint8) dhcpv4.OptionCode {
- return optionCode(c)
- },
-}
-
-// parseOption is similar to dhcpv4.parseOption, except that it interprets
-// option codes based on the BSDP-specific options.
-func parseOption(code dhcpv4.OptionCode, data []byte) fmt.Stringer {
- var d dhcpv4.OptionDecoder
- switch code {
- case OptionMachineName:
- var s dhcpv4.String
- d = &s
-
- case OptionServerIdentifier:
- d = &dhcpv4.IP{}
-
- case OptionServerPriority, OptionReplyPort:
- var u dhcpv4.Uint16
- d = &u
-
- case OptionBootImageList:
- d = &BootImageList{}
-
- case OptionDefaultBootImageID, OptionSelectedBootImageID:
- d = &BootImageID{}
-
- case OptionMessageType:
- var m MessageType
- d = &m
-
- case OptionVersion:
- d = &Version{}
- }
- if d != nil && d.FromBytes(data) == nil {
- return d
- }
- return dhcpv4.OptionGeneric{Data: data}
-}
diff --git a/dhcpv4/bsdp/option_vendor_specific_information_test.go b/dhcpv4/bsdp/option_vendor_specific_information_test.go
deleted file mode 100644
index a6727f5..0000000
--- a/dhcpv4/bsdp/option_vendor_specific_information_test.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package bsdp
-
-import (
- "net"
- "testing"
-
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/stretchr/testify/require"
-)
-
-func TestOptVendorSpecificInformationInterfaceMethods(t *testing.T) {
- o := OptVendorOptions(
- OptVersion(Version1_1),
- OptMessageType(MessageTypeList),
- )
- require.Equal(t, dhcpv4.OptionVendorSpecificInformation, o.Code, "Code")
-
- expectedBytes := []byte{
- 1, 1, 1, // List option
- 2, 2, 1, 1, // Version option
- }
- require.Equal(t, expectedBytes, o.Value.ToBytes(), "ToBytes")
-}
-
-func TestOptVendorSpecificInformationString(t *testing.T) {
- o := OptVendorOptions(
- OptMessageType(MessageTypeList),
- OptVersion(Version1_1),
- )
- expectedString := "Vendor Specific Information:\n BSDP Message Type: LIST\n BSDP Version: 1.1\n"
- require.Equal(t, expectedString, o.String())
-
- // Test more complicated string - sub options of sub options.
- o = OptVendorOptions(
- OptMessageType(MessageTypeList),
- OptBootImageList(
- BootImage{
- ID: BootImageID{
- IsInstall: false,
- ImageType: BootImageTypeMacOSX,
- Index: 1001,
- },
- Name: "bsdp-1",
- },
- BootImage{
- ID: BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOS9,
- Index: 9009,
- },
- Name: "bsdp-2",
- },
- ),
- OptMachineName("foo"),
- OptServerIdentifier(net.IP{1, 1, 1, 1}),
- OptServerPriority(1234),
- OptReplyPort(1235),
- OptDefaultBootImageID(BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOS9,
- Index: 9009,
- }),
- OptSelectedBootImageID(BootImageID{
- IsInstall: true,
- ImageType: BootImageTypeMacOS9,
- Index: 9009,
- }),
- )
- expectedString = "Vendor Specific Information:\n" +
- " BSDP Message Type: LIST\n" +
- " BSDP Server Identifier: 1.1.1.1\n" +
- " BSDP Server Priority: 1234\n" +
- " BSDP Reply Port: 1235\n" +
- " BSDP Default Boot Image ID: [9009] installable macOS 9 image\n" +
- " BSDP Selected Boot Image ID: [9009] installable macOS 9 image\n" +
- " BSDP Boot Image List: bsdp-1 [1001] uninstallable macOS image, bsdp-2 [9009] installable macOS 9 image\n" +
- " BSDP Machine Name: foo\n"
- require.Equal(t, expectedString, o.String())
-}
diff --git a/dhcpv4/bsdp/types.go b/dhcpv4/bsdp/types.go
deleted file mode 100644
index 4931081..0000000
--- a/dhcpv4/bsdp/types.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package bsdp
-
-import (
- "fmt"
-)
-
-// DefaultMacOSVendorClassIdentifier is a default vendor class identifier used
-// on non-darwin hosts where the vendor class identifier cannot be determined.
-// It should mostly be used for debugging if testing BSDP on a non-darwin
-// system.
-const DefaultMacOSVendorClassIdentifier = AppleVendorID + "/i386/MacMini6,1"
-
-// optionCode are BSDP option codes.
-//
-// optionCode implements the dhcpv4.OptionCode interface.
-type optionCode uint8
-
-func (o optionCode) Code() uint8 {
- return uint8(o)
-}
-
-func (o optionCode) String() string {
- if s, ok := optionCodeToString[o]; ok {
- return s
- }
- return fmt.Sprintf("unknown (%d)", o)
-}
-
-// Options (occur as sub-options of DHCP option 43).
-const (
- OptionMessageType optionCode = 1
- OptionVersion optionCode = 2
- OptionServerIdentifier optionCode = 3
- OptionServerPriority optionCode = 4
- OptionReplyPort optionCode = 5
- OptionBootImageListPath optionCode = 6 // Not used
- OptionDefaultBootImageID optionCode = 7
- OptionSelectedBootImageID optionCode = 8
- OptionBootImageList optionCode = 9
- OptionNetboot1_0Firmware optionCode = 10
- OptionBootImageAttributesFilterList optionCode = 11
- OptionShadowMountPath optionCode = 128
- OptionShadowFilePath optionCode = 129
- OptionMachineName optionCode = 130
-)
-
-// optionCodeToString maps BSDP OptionCodes to human-readable strings
-// describing what they are.
-var optionCodeToString = map[optionCode]string{
- OptionMessageType: "BSDP Message Type",
- OptionVersion: "BSDP Version",
- OptionServerIdentifier: "BSDP Server Identifier",
- OptionServerPriority: "BSDP Server Priority",
- OptionReplyPort: "BSDP Reply Port",
- OptionBootImageListPath: "", // Not used
- OptionDefaultBootImageID: "BSDP Default Boot Image ID",
- OptionSelectedBootImageID: "BSDP Selected Boot Image ID",
- OptionBootImageList: "BSDP Boot Image List",
- OptionNetboot1_0Firmware: "BSDP Netboot 1.0 Firmware",
- OptionBootImageAttributesFilterList: "BSDP Boot Image Attributes Filter List",
- OptionShadowMountPath: "BSDP Shadow Mount Path",
- OptionShadowFilePath: "BSDP Shadow File Path",
- OptionMachineName: "BSDP Machine Name",
-}
diff --git a/dhcpv4/bsdp/vendor_class_identifier.go b/dhcpv4/bsdp/vendor_class_identifier.go
deleted file mode 100644
index dfd1ca9..0000000
--- a/dhcpv4/bsdp/vendor_class_identifier.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// +build !darwin
-
-package bsdp
-
-// MakeVendorClassIdentifier returns a static vendor class identifier for BSDP
-// use on non-darwin hosts.
-func MakeVendorClassIdentifier() (string, error) {
- return DefaultMacOSVendorClassIdentifier, nil
-}
diff --git a/dhcpv4/bsdp/vendor_class_identifier_darwin.go b/dhcpv4/bsdp/vendor_class_identifier_darwin.go
deleted file mode 100644
index 46ef656..0000000
--- a/dhcpv4/bsdp/vendor_class_identifier_darwin.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package bsdp
-
-import (
- "fmt"
-
- "golang.org/x/sys/unix"
-)
-
-// MakeVendorClassIdentifier calls the sysctl syscall on macOS to get the
-// platform model.
-func MakeVendorClassIdentifier() (string, error) {
- // Fetch hardware model for class ID.
- hwModel, err := unix.Sysctl("hw.model")
- if err != nil {
- return "", err
- }
- return fmt.Sprintf("%s/i386/%s", AppleVendorID, hwModel), nil
-}