diff options
-rw-r--r-- | dhcpv4/option_class_identifier_test.go | 8 | ||||
-rw-r--r-- | dhcpv4/option_domain_name.go | 49 | ||||
-rw-r--r-- | dhcpv4/option_domain_name_test.go | 41 | ||||
-rw-r--r-- | dhcpv4/option_parameter_request_list.go | 11 | ||||
-rw-r--r-- | dhcpv4/option_parameter_request_list_test.go | 2 | ||||
-rw-r--r-- | dhcpv4/options.go | 2 | ||||
-rw-r--r-- | dhcpv6/options.go | 2 |
7 files changed, 109 insertions, 6 deletions
diff --git a/dhcpv4/option_class_identifier_test.go b/dhcpv4/option_class_identifier_test.go index a6a8f21..786ecfb 100644 --- a/dhcpv4/option_class_identifier_test.go +++ b/dhcpv4/option_class_identifier_test.go @@ -10,17 +10,17 @@ func TestOptClassIdentifierInterfaceMethods(t *testing.T) { o := OptClassIdentifier{Identifier: "foo"} require.Equal(t, OptionClassIdentifier, o.Code(), "Code") require.Equal(t, 3, o.Length(), "Length") - require.Equal(t, []byte{60, 3, 'f', 'o', 'o'}, o.ToBytes(), "ToBytes") + require.Equal(t, []byte{byte(OptionClassIdentifier), 3, 'f', 'o', 'o'}, o.ToBytes(), "ToBytes") } func TestParseOptClassIdentifier(t *testing.T) { - data := []byte{60, 4, 't', 'e', 's', 't'} // DISCOVER + data := []byte{byte(OptionClassIdentifier), 4, 't', 'e', 's', 't'} // DISCOVER o, err := ParseOptClassIdentifier(data) require.NoError(t, err) require.Equal(t, &OptClassIdentifier{Identifier: "test"}, o) // Short byte stream - data = []byte{60} + data = []byte{byte(OptionClassIdentifier)} _, err = ParseOptClassIdentifier(data) require.Error(t, err, "should get error from short byte stream") @@ -30,7 +30,7 @@ func TestParseOptClassIdentifier(t *testing.T) { require.Error(t, err, "should get error from wrong code") // Bad length - data = []byte{60, 6, 1, 1, 1} + data = []byte{byte(OptionClassIdentifier), 6, 1, 1, 1} _, err = ParseOptClassIdentifier(data) require.Error(t, err, "should get error from bad length") } diff --git a/dhcpv4/option_domain_name.go b/dhcpv4/option_domain_name.go new file mode 100644 index 0000000..71d7ef1 --- /dev/null +++ b/dhcpv4/option_domain_name.go @@ -0,0 +1,49 @@ +package dhcpv4 + +import "fmt" + +// This option implements the server domani name option +// https://tools.ietf.org/html/rfc2132 + +// OptDomainName represents an option encapsulating the server identifier. +type OptDomainName struct { + DomainName string +} + +// ParseOptDomainName returns a new OptDomainName from a byte +// stream, or error if any. +func ParseOptDomainName(data []byte) (*OptDomainName, error) { + if len(data) < 2 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionDomainName { + return nil, fmt.Errorf("expected code %v, got %v", OptionDomainName, code) + } + length := int(data[1]) + if len(data) < 2+length { + return nil, ErrShortByteStream + } + return &OptDomainName{DomainName: string(data[2 : 2+length])}, nil +} + +// Code returns the option code. +func (o *OptDomainName) Code() OptionCode { + return OptionDomainName +} + +// ToBytes returns a serialized stream of bytes for this option. +func (o *OptDomainName) ToBytes() []byte { + return append([]byte{byte(o.Code()), byte(o.Length())}, []byte(o.DomainName)...) +} + +// String returns a human-readable string. +func (o *OptDomainName) String() string { + return fmt.Sprintf("Domain Name -> %v", o.DomainName) +} + +// Length returns the length of the data portion (excluding option code an byte +// length). +func (o *OptDomainName) Length() int { + return len(o.DomainName) +} diff --git a/dhcpv4/option_domain_name_test.go b/dhcpv4/option_domain_name_test.go new file mode 100644 index 0000000..ab66e8b --- /dev/null +++ b/dhcpv4/option_domain_name_test.go @@ -0,0 +1,41 @@ +package dhcpv4 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOptDomainNameInterfaceMethods(t *testing.T) { + o := OptDomainName{DomainName: "foo"} + require.Equal(t, OptionDomainName, o.Code(), "Code") + require.Equal(t, 3, o.Length(), "Length") + require.Equal(t, []byte{byte(OptionDomainName), 3, 'f', 'o', 'o'}, o.ToBytes(), "ToBytes") +} + +func TestParseOptDomainName(t *testing.T) { + data := []byte{byte(OptionDomainName), 4, 't', 'e', 's', 't'} // DISCOVER + o, err := ParseOptDomainName(data) + require.NoError(t, err) + require.Equal(t, &OptDomainName{DomainName: "test"}, o) + + // Short byte stream + data = []byte{byte(OptionDomainName)} + _, err = ParseOptDomainName(data) + require.Error(t, err, "should get error from short byte stream") + + // Wrong code + data = []byte{54, 2, 1, 1} + _, err = ParseOptDomainName(data) + require.Error(t, err, "should get error from wrong code") + + // Bad length + data = []byte{byte(OptionDomainName), 6, 1, 1, 1} + _, err = ParseOptDomainName(data) + require.Error(t, err, "should get error from bad length") +} + +func TestOptDomainNameString(t *testing.T) { + o := OptDomainName{DomainName: "testy test"} + require.Equal(t, "Domain Name -> testy test", o.String()) +} diff --git a/dhcpv4/option_parameter_request_list.go b/dhcpv4/option_parameter_request_list.go index f6b0348..324832e 100644 --- a/dhcpv4/option_parameter_request_list.go +++ b/dhcpv4/option_parameter_request_list.go @@ -2,6 +2,7 @@ package dhcpv4 import ( "fmt" + "strings" ) // This option implements the parameter request list option @@ -50,7 +51,15 @@ func (o *OptParameterRequestList) ToBytes() []byte { // String returns a human-readable string for this option. func (o *OptParameterRequestList) String() string { - return fmt.Sprintf("Parameter Request List -> %v", o.RequestedOpts) + var optNames []string + for _, ro := range o.RequestedOpts { + if name, ok := OptionCodeToString[ro]; ok { + optNames = append(optNames, name) + } else { + optNames = append(optNames, fmt.Sprintf("Unknown (%v)", ro)) + } + } + return fmt.Sprintf("Parameter Request List -> [%v]", strings.Join(optNames, ", ")) } // Length returns the length of the data portion (excluding option code and byte diff --git a/dhcpv4/option_parameter_request_list_test.go b/dhcpv4/option_parameter_request_list_test.go index 4441b29..f600a70 100644 --- a/dhcpv4/option_parameter_request_list_test.go +++ b/dhcpv4/option_parameter_request_list_test.go @@ -14,7 +14,7 @@ func TestOptParameterRequestListInterfaceMethods(t *testing.T) { expectedBytes := []byte{55, 2, 67, 5} require.Equal(t, expectedBytes, o.ToBytes(), "ToBytes") - expectedString := "Parameter Request List -> [67 5]" + expectedString := "Parameter Request List -> [Bootfile Name, Name Server]" require.Equal(t, expectedString, o.String(), "String") } diff --git a/dhcpv4/options.go b/dhcpv4/options.go index a1b5c93..acc2bf5 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -52,6 +52,8 @@ func ParseOption(data []byte) (Option, error) { opt, err = ParseOptMaximumDHCPMessageSize(data) case OptionClassIdentifier: opt, err = ParseOptClassIdentifier(data) + case OptionDomainName: + opt, err = ParseOptDomainName(data) default: opt, err = ParseOptionGeneric(data) } diff --git a/dhcpv6/options.go b/dhcpv6/options.go index fcfed37..ad52998 100644 --- a/dhcpv6/options.go +++ b/dhcpv6/options.go @@ -5,8 +5,10 @@ import ( "fmt" ) +// OptionCode is a single byte representing the code for a given Option. type OptionCode uint16 +// Option is an interface that all DHCPv6 options adhere to. type Option interface { Code() OptionCode ToBytes() []byte |