diff options
-rw-r--r-- | dhcpv4/option_tftp_server_name.go | 54 | ||||
-rw-r--r-- | dhcpv4/option_tftp_server_name_test.go | 60 | ||||
-rw-r--r-- | dhcpv4/options.go | 6 |
3 files changed, 120 insertions, 0 deletions
diff --git a/dhcpv4/option_tftp_server_name.go b/dhcpv4/option_tftp_server_name.go new file mode 100644 index 0000000..b4029e1 --- /dev/null +++ b/dhcpv4/option_tftp_server_name.go @@ -0,0 +1,54 @@ +package dhcpv4 + +import ( + "fmt" +) + +// This option implements the TFTP server name option. +// https://tools.ietf.org/html/rfc2132 + +// OptTFTPServerName implements the TFTP server name option. +type OptTFTPServerName struct { + TFTPServerName []byte +} + +// Code returns the option code +func (op *OptTFTPServerName) Code() OptionCode { + return OptionTFTPServerName +} + +// ToBytes serializes the option and returns it as a sequence of bytes +func (op *OptTFTPServerName) ToBytes() []byte { + return append([]byte{byte(op.Code()), byte(op.Length())}, op.TFTPServerName...) +} + +// Length returns the option length in bytes +func (op *OptTFTPServerName) Length() int { + return len(op.TFTPServerName) +} + +func (op *OptTFTPServerName) String() string { + return fmt.Sprintf("OptTFTPServerName{TFTPServerName=%s}", op.TFTPServerName) +} + +// ParseOptTFTPServerName returns a new OptTFTPServerName fomr a byte stream or error if any +func ParseOptTFTPServerName(data []byte) (*OptTFTPServerName, error) { + if len(data) < 3 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionTFTPServerName { + return nil, fmt.Errorf("ParseOptTFTPServerName: invalid code: %v; want %v", + code, OptionTFTPServerName) + } + length := int(data[1]) + if length < 1 { + return nil, fmt.Errorf("TFTP server name has invalid length of %d", length) + } + TFTPServerNameData := data[2:] + if len(TFTPServerNameData) < length { + return nil, fmt.Errorf("ParseOptTFTPServerName: short data: %d bytes; want %d", + len(TFTPServerNameData), length) + } + return &OptTFTPServerName{TFTPServerName: TFTPServerNameData[:length]}, nil +} diff --git a/dhcpv4/option_tftp_server_name_test.go b/dhcpv4/option_tftp_server_name_test.go new file mode 100644 index 0000000..caddf95 --- /dev/null +++ b/dhcpv4/option_tftp_server_name_test.go @@ -0,0 +1,60 @@ +package dhcpv4 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOptTFTPServerNameCode(t *testing.T) { + opt := OptTFTPServerName{} + require.Equal(t, OptionTFTPServerName, opt.Code()) +} + +func TestOptTFTPServerNameToBytes(t *testing.T) { + opt := OptTFTPServerName{ + TFTPServerName: []byte("linuxboot"), + } + data := opt.ToBytes() + expected := []byte{ + 66, // OptionTFTPServerName + 9, // length + 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', + } + require.Equal(t, expected, data) +} + +func TestParseOptTFTPServerName(t *testing.T) { + expected := []byte{ + 66, 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', + } + opt, err := ParseOptTFTPServerName(expected) + require.NoError(t, err) + require.Equal(t, 9, opt.Length()) + require.Equal(t, "linuxboot", string(opt.TFTPServerName)) +} + +func TestParseOptTFTPServerNameZeroLength(t *testing.T) { + expected := []byte{ + 66, 0, + } + _, err := ParseOptTFTPServerName(expected) + require.Error(t, err) +} + +func TestParseOptTFTPServerNameInvalidLength(t *testing.T) { + expected := []byte{ + 66, 9, 'l', 'i', 'n', 'u', 'x', 'b', + } + _, err := ParseOptTFTPServerName(expected) + require.Error(t, err) +} + +func TestParseOptTFTPServerNameShortLength(t *testing.T) { + expected := []byte{ + 66, 4, 'l', 'i', 'n', 'u', 'x', + } + opt, err := ParseOptTFTPServerName(expected) + require.NoError(t, err) + require.Equal(t, []byte("linu"), opt.TFTPServerName) +} diff --git a/dhcpv4/options.go b/dhcpv4/options.go index 2bd26a5..5e6da80 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -60,6 +60,12 @@ func ParseOption(data []byte) (Option, error) { opt, err = ParseOptDomainNameServer(data) case OptionVendorIdentifyingVendorClass: opt, err = ParseOptVIVC(data) + case OptionTFTPServerName: + opt, err = ParseOptTFTPServerName(data) + case OptionBootfileName: + opt, err = ParseOptBootfileName(data) + case OptionUserClassInformation: + opt, err = ParseOptUserClass(data) default: opt, err = ParseOptionGeneric(data) } |