From 51bf62d96cb1dabe3419a7bdcc0efc415e134d72 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Sun, 29 Jul 2018 12:48:27 +0200 Subject: add OptSubnetMask --- dhcpv4/dhcpv4.go | 8 +++--- dhcpv4/option_subnet_mask.go | 56 +++++++++++++++++++++++++++++++++++++++ dhcpv4/option_subnet_mask_test.go | 44 ++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 dhcpv4/option_subnet_mask.go create mode 100644 dhcpv4/option_subnet_mask_test.go diff --git a/dhcpv4/dhcpv4.go b/dhcpv4/dhcpv4.go index 2d73003..7965161 100644 --- a/dhcpv4/dhcpv4.go +++ b/dhcpv4/dhcpv4.go @@ -690,10 +690,10 @@ func (d *DHCPv4) ToBytes() []byte { ret = append(ret, u16...) binary.BigEndian.PutUint16(u16, d.flags) ret = append(ret, u16...) - ret = append(ret, d.clientIPAddr[:4]...) - ret = append(ret, d.yourIPAddr[:4]...) - ret = append(ret, d.serverIPAddr[:4]...) - ret = append(ret, d.gatewayIPAddr[:4]...) + ret = append(ret, d.clientIPAddr.To4()...) + ret = append(ret, d.yourIPAddr.To4()...) + ret = append(ret, d.serverIPAddr.To4()...) + ret = append(ret, d.gatewayIPAddr.To4()...) ret = append(ret, d.clientHwAddr[:16]...) ret = append(ret, d.serverHostName[:64]...) ret = append(ret, d.bootFileName[:128]...) diff --git a/dhcpv4/option_subnet_mask.go b/dhcpv4/option_subnet_mask.go new file mode 100644 index 0000000..8d5a68c --- /dev/null +++ b/dhcpv4/option_subnet_mask.go @@ -0,0 +1,56 @@ +package dhcpv4 + +import ( + "fmt" + "net" +) + +// This option implements the subnet mask option +// https://tools.ietf.org/html/rfc2132 + +// OptSubnetMask represents an option encapsulating the subnet mask. +type OptSubnetMask struct { + SubnetMask net.IPMask +} + +// ParseOptSubnetMask returns a new OptSubnetMask from a byte +// stream, or error if any. +func ParseOptSubnetMask(data []byte) (*OptSubnetMask, error) { + if len(data) < 2 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionSubnetMask { + return nil, fmt.Errorf("expected code %v, got %v", OptionSubnetMask, code) + } + length := int(data[1]) + if length != 4 { + return nil, fmt.Errorf("unexepcted length: expected 4, got %v", length) + } + if len(data) < 6 { + return nil, ErrShortByteStream + } + return &OptSubnetMask{SubnetMask: net.IPMask(data[2 : 2+length])}, nil +} + +// Code returns the option code. +func (o *OptSubnetMask) Code() OptionCode { + return OptionSubnetMask +} + +// ToBytes returns a serialized stream of bytes for this option. +func (o *OptSubnetMask) ToBytes() []byte { + ret := []byte{byte(o.Code()), byte(o.Length())} + return append(ret, o.SubnetMask[:4]...) +} + +// String returns a human-readable string. +func (o *OptSubnetMask) String() string { + return fmt.Sprintf("Subnet Mask -> %v", o.SubnetMask.String()) +} + +// Length returns the length of the data portion (excluding option code an byte +// length). +func (o *OptSubnetMask) Length() int { + return len(o.SubnetMask) +} diff --git a/dhcpv4/option_subnet_mask_test.go b/dhcpv4/option_subnet_mask_test.go new file mode 100644 index 0000000..4cb8819 --- /dev/null +++ b/dhcpv4/option_subnet_mask_test.go @@ -0,0 +1,44 @@ +package dhcpv4 + +import ( + "net" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOptSubnetMaskInterfaceMethods(t *testing.T) { + mask := net.IPMask{255, 255, 255, 0} + o := OptSubnetMask{SubnetMask: mask} + + require.Equal(t, OptionSubnetMask, o.Code(), "Code") + + expectedBytes := []byte{1, 4, 255, 255, 255, 0} + require.Equal(t, expectedBytes, o.ToBytes(), "ToBytes") + + require.Equal(t, 4, o.Length(), "Length") + + require.Equal(t, "Subnet Mask -> ffffff00", o.String(), "String") +} + +func TestParseOptSubnetMask(t *testing.T) { + var ( + o *OptSubnetMask + err error + ) + o, err = ParseOptSubnetMask([]byte{}) + require.Error(t, err, "empty byte stream") + + o, err = ParseOptSubnetMask([]byte{1, 4, 255}) + require.Error(t, err, "short byte stream") + + o, err = ParseOptSubnetMask([]byte{1, 3, 255, 255, 255, 0}) + require.Error(t, err, "wrong IP length") + + o, err = ParseOptSubnetMask([]byte{2, 4, 255, 255, 255}) + require.Error(t, err, "wrong option code") + + o, err = ParseOptSubnetMask([]byte{1, 4, 255, 255, 255, 0}) + require.NoError(t, err) + require.Equal(t, net.IPMask{255, 255, 255, 0}, o.SubnetMask) +} -- cgit v1.2.3 From a5582a6202195fe191b4b4fc7673b615ceef499a Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Sun, 29 Jul 2018 12:51:47 +0200 Subject: OptSubnetMask: change Length() --- dhcpv4/option_subnet_mask.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhcpv4/option_subnet_mask.go b/dhcpv4/option_subnet_mask.go index 8d5a68c..f1ff4a4 100644 --- a/dhcpv4/option_subnet_mask.go +++ b/dhcpv4/option_subnet_mask.go @@ -52,5 +52,5 @@ func (o *OptSubnetMask) String() string { // Length returns the length of the data portion (excluding option code an byte // length). func (o *OptSubnetMask) Length() int { - return len(o.SubnetMask) + return 4 } -- cgit v1.2.3 From 620189eaa8ecfd3cd9f8209a145d2b24a0cf447f Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Sun, 29 Jul 2018 13:03:00 +0200 Subject: parse OptSubnetMask --- dhcpv4/options.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dhcpv4/options.go b/dhcpv4/options.go index 5e6da80..198fbb0 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -40,6 +40,8 @@ func ParseOption(data []byte) (Option, error) { err error ) switch OptionCode(data[0]) { + case OptionSubnetMask: + opt, err = ParseOptSubnetMask(data) case OptionDHCPMessageType: opt, err = ParseOptMessageType(data) case OptionParameterRequestList: -- cgit v1.2.3 From 377731752ec3ddf7162413ad5e5ef8620e0b3857 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Sun, 29 Jul 2018 13:24:36 +0200 Subject: OptSubnetMask: add options test --- dhcpv4/options_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go index 41ef415..01d7427 100644 --- a/dhcpv4/options_test.go +++ b/dhcpv4/options_test.go @@ -17,6 +17,14 @@ func TestParseOption(t *testing.T) { require.Equal(t, 4, generic.Length()) require.Equal(t, "Name Server -> [192 168 1 254]", generic.String()) + // Option subnet mask + option = []byte{1, 4, 255, 255, 255, 0} + opt, err = ParseOption(option) + require.NoError(t, err) + require.Equal(t, OptionSubnetMask, opt.Code(), "Code") + require.Equal(t, 4, opt.Length(), "Length") + require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Message type option = []byte{53, 1, 1} opt, err = ParseOption(option) -- cgit v1.2.3 From b5c839d7f28cc635376d5bee36de0e898ad8a1b8 Mon Sep 17 00:00:00 2001 From: Owen Mooney Date: Sun, 29 Jul 2018 14:14:31 +0100 Subject: Add support for modifiers to dhcpv4 --- dhcpv4/client.go | 10 +++++++--- dhcpv4/dhcpv4.go | 14 ++++++++++++-- dhcpv4/modifiers.go | 11 +++++++++++ dhcpv4/modifiers_test.go | 14 ++++++++++++++ dhcpv4/option_userclass.go | 2 +- 5 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 dhcpv4/modifiers.go create mode 100644 dhcpv4/modifiers_test.go diff --git a/dhcpv4/client.go b/dhcpv4/client.go index 1ce6a5c..ce6f242 100644 --- a/dhcpv4/client.go +++ b/dhcpv4/client.go @@ -124,7 +124,7 @@ func MakeListeningSocket(ifname string) (int, error) { // ordered as Discovery, Offer, Request and Acknowledge. In case of errors, an // error is returned, and the list of DHCPv4 objects will be shorted than 4, // containing all the sent and received DHCPv4 messages. -func (c *Client) Exchange(ifname string, discover *DHCPv4) ([]DHCPv4, error) { +func (c *Client) Exchange(ifname string, discover *DHCPv4, modifiers ...Modifier) ([]DHCPv4, error) { conversation := make([]DHCPv4, 1) var err error @@ -140,10 +140,14 @@ func (c *Client) Exchange(ifname string, discover *DHCPv4) ([]DHCPv4, error) { // Discover if discover == nil { - discover, err = NewDiscoveryForInterface(ifname) + discover, err = NewDiscoveryForInterface(ifname, modifiers...) if err != nil { return conversation, err } + } else { + for _, mod := range modifiers { + discover = mod(discover) + } } conversation[0] = *discover @@ -155,7 +159,7 @@ func (c *Client) Exchange(ifname string, discover *DHCPv4) ([]DHCPv4, error) { conversation = append(conversation, *offer) // Request - request, err := RequestFromOffer(*offer) + request, err := RequestFromOffer(*offer, modifiers...) if err != nil { return conversation, err } diff --git a/dhcpv4/dhcpv4.go b/dhcpv4/dhcpv4.go index 2d73003..a74f675 100644 --- a/dhcpv4/dhcpv4.go +++ b/dhcpv4/dhcpv4.go @@ -38,6 +38,10 @@ type DHCPv4 struct { options []Option } +// Modifier defines the signature for functions that can modify DHCPv4 +// structures. This is used to simplify packet manipulation +type Modifier func(d *DHCPv4) *DHCPv4 + // IPv4AddrsForInterface obtains the currently-configured, non-loopback IPv4 // addresses for iface. func IPv4AddrsForInterface(iface *net.Interface) ([]net.IP, error) { @@ -120,7 +124,7 @@ func New() (*DHCPv4, error) { // NewDiscoveryForInterface builds a new DHCPv4 Discovery message, with a default // Ethernet HW type and the hardware address obtained from the specified // interface. -func NewDiscoveryForInterface(ifname string) (*DHCPv4, error) { +func NewDiscoveryForInterface(ifname string, modifiers ...Modifier) (*DHCPv4, error) { d, err := New() if err != nil { return nil, err @@ -144,6 +148,9 @@ func NewDiscoveryForInterface(ifname string) (*DHCPv4, error) { OptionDomainNameServer, }, }) + for _, mod := range modifiers { + d = mod(d) + } return d, nil } @@ -184,7 +191,7 @@ func NewInformForInterface(ifname string, needsBroadcast bool) (*DHCPv4, error) } // RequestFromOffer builds a DHCPv4 request from an offer. -func RequestFromOffer(offer DHCPv4) (*DHCPv4, error) { +func RequestFromOffer(offer DHCPv4, modifiers ...Modifier) (*DHCPv4, error) { d, err := New() if err != nil { return nil, err @@ -214,6 +221,9 @@ func RequestFromOffer(offer DHCPv4) (*DHCPv4, error) { d.AddOption(&OptMessageType{MessageType: MessageTypeRequest}) d.AddOption(&OptRequestedIPAddress{RequestedAddr: offer.YourIPAddr()}) d.AddOption(&OptServerIdentifier{ServerID: serverIP}) + for _, mod := range modifiers { + d = mod(d) + } return d, nil } diff --git a/dhcpv4/modifiers.go b/dhcpv4/modifiers.go new file mode 100644 index 0000000..bc19219 --- /dev/null +++ b/dhcpv4/modifiers.go @@ -0,0 +1,11 @@ +package dhcpv4 + +// WithUserClass adds a user class option to the packet +func WithUserClass(uc []byte) Modifier { + // TODO let the user specify multiple user classes + return func(d *DHCPv4) *DHCPv4 { + ouc := OptUserClass{UserClasses: [][]byte{uc}} + d.AddOption(&ouc) + return d + } +} \ No newline at end of file diff --git a/dhcpv4/modifiers_test.go b/dhcpv4/modifiers_test.go new file mode 100644 index 0000000..415a4ea --- /dev/null +++ b/dhcpv4/modifiers_test.go @@ -0,0 +1,14 @@ +package dhcpv4 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestUserClassModifier(t *testing.T) { + d, _ := New() + userClass := WithUserClass([]byte("linuxboot")) + d = userClass(d) + require.Equal(t, "OptUserClass{userclass=[linuxboot]}", d.options[0].String()) +} \ No newline at end of file diff --git a/dhcpv4/option_userclass.go b/dhcpv4/option_userclass.go index 1505dbb..307a128 100644 --- a/dhcpv4/option_userclass.go +++ b/dhcpv4/option_userclass.go @@ -39,7 +39,7 @@ func (op *OptUserClass) Length() int { } func (op *OptUserClass) String() string { - ucStrings := make([]string, len(op.UserClasses)) + ucStrings := make([]string, 0, len(op.UserClasses)) for _, uc := range op.UserClasses { ucStrings = append(ucStrings, string(uc)) } -- cgit v1.2.3 From a6ecc33c23d061098e9a2bf4d9d2d4c0a710406b Mon Sep 17 00:00:00 2001 From: Owen Mooney Date: Sun, 29 Jul 2018 16:34:55 +0100 Subject: Add modifier support to NewReplyFromRequest --- dhcpv4/client.go | 9 ++++----- dhcpv4/dhcpv4.go | 10 +++++----- dhcpv4/dhcpv4_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/dhcpv4/client.go b/dhcpv4/client.go index ce6f242..db7e71a 100644 --- a/dhcpv4/client.go +++ b/dhcpv4/client.go @@ -140,14 +140,13 @@ func (c *Client) Exchange(ifname string, discover *DHCPv4, modifiers ...Modifier // Discover if discover == nil { - discover, err = NewDiscoveryForInterface(ifname, modifiers...) + discover, err = NewDiscoveryForInterface(ifname) if err != nil { return conversation, err } - } else { - for _, mod := range modifiers { - discover = mod(discover) - } + } + for _, mod := range modifiers { + discover = mod(discover) } conversation[0] = *discover diff --git a/dhcpv4/dhcpv4.go b/dhcpv4/dhcpv4.go index a74f675..e027f28 100644 --- a/dhcpv4/dhcpv4.go +++ b/dhcpv4/dhcpv4.go @@ -124,7 +124,7 @@ func New() (*DHCPv4, error) { // NewDiscoveryForInterface builds a new DHCPv4 Discovery message, with a default // Ethernet HW type and the hardware address obtained from the specified // interface. -func NewDiscoveryForInterface(ifname string, modifiers ...Modifier) (*DHCPv4, error) { +func NewDiscoveryForInterface(ifname string) (*DHCPv4, error) { d, err := New() if err != nil { return nil, err @@ -148,9 +148,6 @@ func NewDiscoveryForInterface(ifname string, modifiers ...Modifier) (*DHCPv4, er OptionDomainNameServer, }, }) - for _, mod := range modifiers { - d = mod(d) - } return d, nil } @@ -228,7 +225,7 @@ func RequestFromOffer(offer DHCPv4, modifiers ...Modifier) (*DHCPv4, error) { } // NewReplyFromRequest builds a DHCPv4 reply from a request. -func NewReplyFromRequest(request *DHCPv4) (*DHCPv4, error) { +func NewReplyFromRequest(request *DHCPv4, modifiers ...Modifier) (*DHCPv4, error) { reply, err := New() if err != nil { return nil, err @@ -241,6 +238,9 @@ func NewReplyFromRequest(request *DHCPv4) (*DHCPv4, error) { reply.SetTransactionID(request.TransactionID()) reply.SetFlags(request.Flags()) reply.SetGatewayIPAddr(request.GatewayIPAddr()) + for _, mod := range modifiers { + reply = mod(reply) + } return reply, nil } diff --git a/dhcpv4/dhcpv4_test.go b/dhcpv4/dhcpv4_test.go index 55e082d..7e5f083 100644 --- a/dhcpv4/dhcpv4_test.go +++ b/dhcpv4/dhcpv4_test.go @@ -353,6 +353,19 @@ func TestDHCPv4RequestFromOffer(t *testing.T) { require.Equal(t, MessageTypeRequest, *req.MessageType()) } +func TestDHCPv4RequestFromOfferWithModifier(t *testing.T) { + offer, err := New() + require.NoError(t, err) + offer.AddOption(&OptMessageType{MessageType: MessageTypeOffer}) + offer.AddOption(&OptServerIdentifier{ServerID: net.IPv4(192, 168, 0, 1)}) + userClass := WithUserClass([]byte("linuxboot")) + req, err := RequestFromOffer(*offer, userClass) + require.NoError(t, err) + require.NotEqual(t, (*MessageType)(nil), *req.MessageType()) + require.Equal(t, MessageTypeRequest, *req.MessageType()) + require.Equal(t, "OptUserClass{userclass=[linuxboot]}", req.options[3].String()) +} + func TestNewReplyFromRequest(t *testing.T) { discover, err := New() require.NoError(t, err) @@ -363,6 +376,18 @@ func TestNewReplyFromRequest(t *testing.T) { require.Equal(t, discover.GatewayIPAddr(), reply.GatewayIPAddr()) } +func TestNewReplyFromRequestWithModifier(t *testing.T) { + discover, err := New() + require.NoError(t, err) + discover.SetGatewayIPAddr(net.IPv4(192, 168, 0, 1)) + userClass := WithUserClass([]byte("linuxboot")) + reply, err := NewReplyFromRequest(discover, userClass) + require.NoError(t, err) + require.Equal(t, discover.TransactionID(), reply.TransactionID()) + require.Equal(t, discover.GatewayIPAddr(), reply.GatewayIPAddr()) + require.Equal(t, "OptUserClass{userclass=[linuxboot]}", reply.options[0].String()) +} + func TestDHCPv4MessageTypeNil(t *testing.T) { m, err := New() require.NoError(t, err) -- cgit v1.2.3 From 5d3c53961b899757aef35269e39c11c21e4087fe Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Sun, 29 Jul 2018 22:24:53 +0200 Subject: add OptRouter --- dhcpv4/option_domain_name_server.go | 2 +- dhcpv4/option_router.go | 70 +++++++++++++++++++++++++++++++++++++ dhcpv4/option_router_test.go | 65 ++++++++++++++++++++++++++++++++++ dhcpv4/options.go | 6 ++-- dhcpv4/options_test.go | 16 ++++----- 5 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 dhcpv4/option_router.go create mode 100644 dhcpv4/option_router_test.go diff --git a/dhcpv4/option_domain_name_server.go b/dhcpv4/option_domain_name_server.go index 78aaf90..470eaa0 100644 --- a/dhcpv4/option_domain_name_server.go +++ b/dhcpv4/option_domain_name_server.go @@ -48,7 +48,7 @@ func (o *OptDomainNameServer) Code() OptionCode { func (o *OptDomainNameServer) ToBytes() []byte { ret := []byte{byte(o.Code()), byte(o.Length())} for _, ns := range o.NameServers { - ret = append(ret, ns...) + ret = append(ret, ns.To4()...) } return ret } diff --git a/dhcpv4/option_router.go b/dhcpv4/option_router.go new file mode 100644 index 0000000..6991f89 --- /dev/null +++ b/dhcpv4/option_router.go @@ -0,0 +1,70 @@ +package dhcpv4 + +import ( + "fmt" + "net" +) + +// This option implements the router option +// https://tools.ietf.org/html/rfc2132 + +// OptDomainRouter represents an option encapsulating the routers. +type OptRouter struct { + Routers []net.IP +} + +// ParseOptRouter returns a new OptRouter from a byte stream, or error if any. +func ParseOptRouter(data []byte) (*OptRouter, error) { + if len(data) < 2 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionRouter { + return nil, fmt.Errorf("expected code %v, got %v", OptionRouter, code) + } + length := int(data[1]) + if length == 0 || length%4 != 0 { + return nil, fmt.Errorf("Invalid length: expected multiple of 4 larger than 4, got %v", length) + } + if len(data) < 2+length { + return nil, ErrShortByteStream + } + routers := make([]net.IP, 0, length%4) + for idx := 0; idx < length; idx += 4 { + b := data[2+idx : 2+idx+4] + routers = append(routers, net.IPv4(b[0], b[1], b[2], b[3])) + } + return &OptRouter{Routers: routers}, nil +} + +// Code returns the option code. +func (o *OptRouter) Code() OptionCode { + return OptionRouter +} + +// ToBytes returns a serialized stream of bytes for this option. +func (o *OptRouter) ToBytes() []byte { + ret := []byte{byte(o.Code()), byte(o.Length())} + for _, router := range o.Routers { + ret = append(ret, router.To4()...) + } + return ret +} + +// String returns a human-readable string. +func (o *OptRouter) String() string { + var routers string + for idx, router := range o.Routers { + routers += router.String() + if idx < len(o.Routers)-1 { + routers += ", " + } + } + return fmt.Sprintf("Routers -> %v", routers) +} + +// Length returns the length of the data portion (excluding option code an byte +// length). +func (o *OptRouter) Length() int { + return len(o.Routers) * 4 +} diff --git a/dhcpv4/option_router_test.go b/dhcpv4/option_router_test.go new file mode 100644 index 0000000..4e08699 --- /dev/null +++ b/dhcpv4/option_router_test.go @@ -0,0 +1,65 @@ +package dhcpv4 + +import ( + "net" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOptRoutersInterfaceMethods(t *testing.T) { + routers := []net.IP{ + net.IPv4(192, 168, 0, 10), + net.IPv4(192, 168, 0, 20), + } + o := OptRouter{Routers: routers} + require.Equal(t, OptionRouter, o.Code(), "Code") + require.Equal(t, net.IPv4len*len(routers), o.Length(), "Length") + require.Equal(t, routers, o.Routers, "Routers") +} + +func TestParseOptRouter(t *testing.T) { + data := []byte{ + byte(OptionRouter), + 8, // Length + 192, 168, 0, 10, // Router #1 + 192, 168, 0, 20, // Router #2 + } + o, err := ParseOptRouter(data) + require.NoError(t, err) + routers := []net.IP{ + net.IPv4(192, 168, 0, 10), + net.IPv4(192, 168, 0, 20), + } + require.Equal(t, &OptRouter{Routers: routers}, o) + + // Short byte stream + data = []byte{byte(OptionDomainNameServer)} + _, err = ParseOptDomainNameServer(data) + require.Error(t, err, "should get error from short byte stream") + + // Wrong code + data = []byte{54, 2, 1, 1} + _, err = ParseOptDomainNameServer(data) + require.Error(t, err, "should get error from wrong code") + + // Bad length + data = []byte{byte(OptionDomainNameServer), 6, 1, 1, 1} + _, err = ParseOptDomainNameServer(data) + require.Error(t, err, "should get error from bad length") +} + +func TestParseOptRouterNoRouters(t *testing.T) { + // RFC2132 requires that at least one Router IP is specified + data := []byte{ + byte(OptionRouter), + 0, // Length + } + _, err := ParseOptRouter(data) + require.Error(t, err) +} + +func TestOptRouterString(t *testing.T) { + o := OptRouter{Routers: []net.IP{net.IPv4(192, 168, 0, 1), net.IPv4(192, 168, 0, 10)}} + require.Equal(t, "Routers -> 192.168.0.1, 192.168.0.10", o.String()) +} diff --git a/dhcpv4/options.go b/dhcpv4/options.go index 198fbb0..4224fa0 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -42,12 +42,14 @@ func ParseOption(data []byte) (Option, error) { switch OptionCode(data[0]) { case OptionSubnetMask: opt, err = ParseOptSubnetMask(data) + case OptionRouter: + opt, err = ParseOptRouter(data) + case OptionRequestedIPAddress: + opt, err = ParseOptRequestedIPAddress(data) case OptionDHCPMessageType: opt, err = ParseOptMessageType(data) case OptionParameterRequestList: opt, err = ParseOptParameterRequestList(data) - case OptionRequestedIPAddress: - opt, err = ParseOptRequestedIPAddress(data) case OptionServerIdentifier: opt, err = ParseOptServerIdentifier(data) case OptionBroadcastAddress: diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go index 01d7427..b3d7605 100644 --- a/dhcpv4/options_test.go +++ b/dhcpv4/options_test.go @@ -25,6 +25,14 @@ func TestParseOption(t *testing.T) { require.Equal(t, 4, opt.Length(), "Length") require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Requested IP address + option = []byte{50, 4, 1, 2, 3, 4} + opt, err = ParseOption(option) + require.NoError(t, err) + require.Equal(t, OptionRequestedIPAddress, opt.Code(), "Code") + require.Equal(t, 4, opt.Length(), "Length") + require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Message type option = []byte{53, 1, 1} opt, err = ParseOption(option) @@ -41,14 +49,6 @@ func TestParseOption(t *testing.T) { require.Equal(t, 3, opt.Length(), "Length") require.Equal(t, option, opt.ToBytes(), "ToBytes") - // Requested IP address - option = []byte{50, 4, 1, 2, 3, 4} - opt, err = ParseOption(option) - require.NoError(t, err) - require.Equal(t, OptionRequestedIPAddress, opt.Code(), "Code") - require.Equal(t, 4, opt.Length(), "Length") - require.Equal(t, option, opt.ToBytes(), "ToBytes") - // Option server ID option = []byte{54, 4, 1, 2, 3, 4} opt, err = ParseOption(option) -- cgit v1.2.3 From fff9cfc916c93ef5108efba95198231c08d9e0ad Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Sun, 29 Jul 2018 22:28:55 +0200 Subject: OptRouter: fix test --- dhcpv4/option_router_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dhcpv4/option_router_test.go b/dhcpv4/option_router_test.go index 4e08699..f492c22 100644 --- a/dhcpv4/option_router_test.go +++ b/dhcpv4/option_router_test.go @@ -34,18 +34,18 @@ func TestParseOptRouter(t *testing.T) { require.Equal(t, &OptRouter{Routers: routers}, o) // Short byte stream - data = []byte{byte(OptionDomainNameServer)} - _, err = ParseOptDomainNameServer(data) + data = []byte{byte(OptionRouter)} + _, err = ParseOptRouter(data) require.Error(t, err, "should get error from short byte stream") // Wrong code data = []byte{54, 2, 1, 1} - _, err = ParseOptDomainNameServer(data) + _, err = ParseOptRouter(data) require.Error(t, err, "should get error from wrong code") // Bad length - data = []byte{byte(OptionDomainNameServer), 6, 1, 1, 1} - _, err = ParseOptDomainNameServer(data) + data = []byte{byte(OptionRouter), 6, 1, 1, 1} + _, err = ParseOptRouter(data) require.Error(t, err, "should get error from bad length") } -- cgit v1.2.3 From 8922ac28f1dd0b814f7df49378ef0b716d12ef72 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Sun, 29 Jul 2018 22:31:42 +0200 Subject: OptRouter: fix lint --- dhcpv4/option_router.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhcpv4/option_router.go b/dhcpv4/option_router.go index 6991f89..b9bdff1 100644 --- a/dhcpv4/option_router.go +++ b/dhcpv4/option_router.go @@ -8,7 +8,7 @@ import ( // This option implements the router option // https://tools.ietf.org/html/rfc2132 -// OptDomainRouter represents an option encapsulating the routers. +// OptRouter represents an option encapsulating the routers. type OptRouter struct { Routers []net.IP } -- cgit v1.2.3 From 07d7f10958223760dd9c5393f6040b64e5e672b8 Mon Sep 17 00:00:00 2001 From: Sean Karlage Date: Sun, 29 Jul 2018 15:21:22 -0700 Subject: DHCPv6: Fix message type constants Renames DHCPv6 constants for message types to CamelCase --- dhcpv6/client.go | 24 ++++++------- dhcpv6/dhcpv6.go | 6 ++-- dhcpv6/dhcpv6_test.go | 46 ++++++++++++------------ dhcpv6/dhcpv6message.go | 20 +++++------ dhcpv6/dhcpv6relay.go | 4 +-- dhcpv6/dhcpv6relay_test.go | 26 +++++++------- dhcpv6/option_relaymsg_test.go | 16 ++++----- dhcpv6/types.go | 80 +++++++++++++++++++++++------------------- dhcpv6/utils_test.go | 2 +- 9 files changed, 115 insertions(+), 109 deletions(-) diff --git a/dhcpv6/client.go b/dhcpv6/client.go index 8431482..3492a47 100644 --- a/dhcpv6/client.go +++ b/dhcpv6/client.go @@ -81,16 +81,16 @@ func (c *Client) sendReceive(ifname string, packet DHCPv6, expectedType MessageT if packet == nil { return nil, fmt.Errorf("Packet to send cannot be nil") } - if expectedType == MSGTYPE_NONE { + if expectedType == MessageTypeNone { // infer the expected type from the packet being sent - if packet.Type() == SOLICIT { - expectedType = ADVERTISE - } else if packet.Type() == REQUEST { - expectedType = REPLY - } else if packet.Type() == RELAY_FORW { - expectedType = RELAY_REPL - } else if packet.Type() == LEASEQUERY { - expectedType = LEASEQUERY_REPLY + if packet.Type() == MessageTypeSolicit { + expectedType = MessageTypeAdvertise + } else if packet.Type() == MessageTypeRequest { + expectedType = MessageTypeReply + } else if packet.Type() == MessageTypeRelayForward { + expectedType = MessageTypeRelayReply + } else if packet.Type() == MessageTypeLeaseQuery { + expectedType = MessageTypeLeaseQueryReply } // and probably more } // if no LocalAddr is specified, get the interface's link-local address @@ -167,7 +167,7 @@ func (c *Client) sendReceive(ifname string, packet DHCPv6, expectedType MessageT continue } } - if expectedType == MSGTYPE_NONE { + if expectedType == MessageTypeNone { // just take whatever arrived break } else if adv.Type() == expectedType { @@ -190,7 +190,7 @@ func (c *Client) Solicit(ifname string, solicit DHCPv6, modifiers ...Modifier) ( for _, mod := range modifiers { solicit = mod(solicit) } - advertise, err := c.sendReceive(ifname, solicit, MSGTYPE_NONE) + advertise, err := c.sendReceive(ifname, solicit, MessageTypeNone) return solicit, advertise, err } @@ -207,6 +207,6 @@ func (c *Client) Request(ifname string, advertise, request DHCPv6, modifiers ... for _, mod := range modifiers { request = mod(request) } - reply, err := c.sendReceive(ifname, request, MSGTYPE_NONE) + reply, err := c.sendReceive(ifname, request, MessageTypeNone) return request, reply, err } diff --git a/dhcpv6/dhcpv6.go b/dhcpv6/dhcpv6.go index b424101..253a635 100644 --- a/dhcpv6/dhcpv6.go +++ b/dhcpv6/dhcpv6.go @@ -30,7 +30,7 @@ func FromBytes(data []byte) (DHCPv6, error) { headerSize int messageType = MessageType(data[0]) ) - if messageType == RELAY_FORW || messageType == RELAY_REPL { + if messageType == MessageTypeRelayForward || messageType == MessageTypeRelayReply { isRelay = true } if isRelay { @@ -85,7 +85,7 @@ func NewMessage(modifiers ...Modifier) (DHCPv6, error) { return nil, err } msg := DHCPv6Message{ - messageType: SOLICIT, + messageType: MessageTypeSolicit, transactionID: *tid, } // apply modifiers @@ -181,7 +181,7 @@ func DecapsulateRelayIndex(l DHCPv6, index int) (DHCPv6, error) { // message as payload. The passed message type must be either RELAY_FORW or // RELAY_REPL func EncapsulateRelay(d DHCPv6, mType MessageType, linkAddr, peerAddr net.IP) (DHCPv6, error) { - if mType != RELAY_FORW && mType != RELAY_REPL { + if mType != MessageTypeRelayForward && mType != MessageTypeRelayReply { return nil, fmt.Errorf("Message type must be either RELAY_FORW or RELAY_REPL") } outer := DHCPv6Relay{ diff --git a/dhcpv6/dhcpv6_test.go b/dhcpv6/dhcpv6_test.go index 3e2b6bc..656c62f 100644 --- a/dhcpv6/dhcpv6_test.go +++ b/dhcpv6/dhcpv6_test.go @@ -33,18 +33,18 @@ func TestNewMessage(t *testing.T) { d, err := NewMessage() require.NoError(t, err) require.NotNil(t, d) - require.Equal(t, SOLICIT, d.Type()) + require.Equal(t, MessageTypeSolicit, d.Type()) require.NotEqual(t, 0, d.(*DHCPv6Message).transactionID) require.Empty(t, d.(*DHCPv6Message).options) } func TestDecapsulateRelayIndex(t *testing.T) { m := DHCPv6Message{} - r1, err := EncapsulateRelay(&m, RELAY_FORW, net.IPv6linklocalallnodes, net.IPv6interfacelocalallnodes) + r1, err := EncapsulateRelay(&m, MessageTypeRelayForward, net.IPv6linklocalallnodes, net.IPv6interfacelocalallnodes) require.NoError(t, err) - r2, err := EncapsulateRelay(r1, RELAY_FORW, net.IPv6loopback, net.IPv6linklocalallnodes) + r2, err := EncapsulateRelay(r1, MessageTypeRelayForward, net.IPv6loopback, net.IPv6linklocalallnodes) require.NoError(t, err) - r3, err := EncapsulateRelay(r2, RELAY_FORW, net.IPv6unspecified, net.IPv6linklocalallrouters) + r3, err := EncapsulateRelay(r2, MessageTypeRelayForward, net.IPv6unspecified, net.IPv6linklocalallrouters) require.NoError(t, err) first, err := DecapsulateRelayIndex(r3, 0) @@ -83,10 +83,10 @@ func TestDecapsulateRelayIndex(t *testing.T) { func TestSettersAndGetters(t *testing.T) { d := DHCPv6Message{} // Message - d.SetMessage(SOLICIT) - require.Equal(t, SOLICIT, d.Type()) - d.SetMessage(ADVERTISE) - require.Equal(t, ADVERTISE, d.Type()) + d.SetMessage(MessageTypeSolicit) + require.Equal(t, MessageTypeSolicit, d.Type()) + d.SetMessage(MessageTypeAdvertise) + require.Equal(t, MessageTypeAdvertise, d.Type()) // TransactionID d.SetTransactionID(12345) @@ -109,7 +109,7 @@ func TestAddOption(t *testing.T) { func TestToBytes(t *testing.T) { d := DHCPv6Message{} - d.SetMessage(SOLICIT) + d.SetMessage(MessageTypeSolicit) d.SetTransactionID(0xabcdef) opt := OptionGeneric{OptionCode: 0, OptionData: []byte{}} d.AddOption(&opt) @@ -128,7 +128,7 @@ func TestFromAndToBytes(t *testing.T) { func TestNewAdvertiseFromSolicit(t *testing.T) { s := DHCPv6Message{} - s.SetMessage(SOLICIT) + s.SetMessage(MessageTypeSolicit) s.SetTransactionID(0xabcdef) cid := OptClientId{} s.AddOption(&cid) @@ -137,7 +137,7 @@ func TestNewAdvertiseFromSolicit(t *testing.T) { a, err := NewAdvertiseFromSolicit(&s, WithServerID(duid)) require.NoError(t, err) require.Equal(t, a.(*DHCPv6Message).TransactionID(), s.TransactionID()) - require.Equal(t, a.Type(), ADVERTISE) + require.Equal(t, a.Type(), MessageTypeAdvertise) } func TestNewReplyFromDHCPv6Message(t *testing.T) { @@ -150,31 +150,31 @@ func TestNewReplyFromDHCPv6Message(t *testing.T) { sid.Sid = duid msg.AddOption(&sid) - msg.SetMessage(CONFIRM) + msg.SetMessage(MessageTypeConfirm) rep, err := NewReplyFromDHCPv6Message(&msg, WithServerID(duid)) require.NoError(t, err) require.Equal(t, rep.(*DHCPv6Message).TransactionID(), msg.TransactionID()) - require.Equal(t, rep.Type(), REPLY) + require.Equal(t, rep.Type(), MessageTypeReply) - msg.SetMessage(RENEW) + msg.SetMessage(MessageTypeRenew) rep, err = NewReplyFromDHCPv6Message(&msg, WithServerID(duid)) require.NoError(t, err) require.Equal(t, rep.(*DHCPv6Message).TransactionID(), msg.TransactionID()) - require.Equal(t, rep.Type(), REPLY) + require.Equal(t, rep.Type(), MessageTypeReply) - msg.SetMessage(REBIND) + msg.SetMessage(MessageTypeRebind) rep, err = NewReplyFromDHCPv6Message(&msg, WithServerID(duid)) require.NoError(t, err) require.Equal(t, rep.(*DHCPv6Message).TransactionID(), msg.TransactionID()) - require.Equal(t, rep.Type(), REPLY) + require.Equal(t, rep.Type(), MessageTypeReply) - msg.SetMessage(RELEASE) + msg.SetMessage(MessageTypeRelease) rep, err = NewReplyFromDHCPv6Message(&msg, WithServerID(duid)) require.NoError(t, err) require.Equal(t, rep.(*DHCPv6Message).TransactionID(), msg.TransactionID()) - require.Equal(t, rep.Type(), REPLY) + require.Equal(t, rep.Type(), MessageTypeReply) - msg.SetMessage(SOLICIT) + msg.SetMessage(MessageTypeSolicit) rep, err = NewReplyFromDHCPv6Message(&msg) require.Error(t, err) @@ -183,7 +183,7 @@ func TestNewReplyFromDHCPv6Message(t *testing.T) { require.Error(t, err) } -func TestNewSolicitWithCID(t *testing.T) { +func TestNewMessageTypeSolicitWithCID(t *testing.T) { hwAddr, err := net.ParseMAC("24:0A:9E:9F:EB:2B") require.NoError(t, err) @@ -196,7 +196,7 @@ func TestNewSolicitWithCID(t *testing.T) { s, err := NewSolicitWithCID(duid) require.NoError(t, err) - require.Equal(t, s.Type(), SOLICIT) + require.Equal(t, s.Type(), MessageTypeSolicit) // Check CID cidOption := s.GetOneOption(OPTION_CLIENTID) require.NotNil(t, cidOption) @@ -215,5 +215,5 @@ func TestNewSolicitWithCID(t *testing.T) { require.Equal(t, len(opts), 2) } -// TODO test NewSolicit +// TODO test NewMessageTypeSolicit // test String and Summary diff --git a/dhcpv6/dhcpv6message.go b/dhcpv6/dhcpv6message.go index 0b84dae..be413f5 100644 --- a/dhcpv6/dhcpv6message.go +++ b/dhcpv6/dhcpv6message.go @@ -71,7 +71,7 @@ func NewSolicitWithCID(duid Duid, modifiers ...Modifier) (DHCPv6, error) { if err != nil { return nil, err } - d.(*DHCPv6Message).SetMessage(SOLICIT) + d.(*DHCPv6Message).SetMessage(MessageTypeSolicit) d.AddOption(&OptClientId{Cid: duid}) oro := new(OptRequestedOption) oro.SetRequestedOptions([]OptionCode{ @@ -114,7 +114,7 @@ func NewAdvertiseFromSolicit(solicit DHCPv6, modifiers ...Modifier) (DHCPv6, err if solicit == nil { return nil, errors.New("SOLICIT cannot be nil") } - if solicit.Type() != SOLICIT { + if solicit.Type() != MessageTypeSolicit { return nil, errors.New("The passed SOLICIT must have SOLICIT type set") } sol, ok := solicit.(*DHCPv6Message) @@ -123,7 +123,7 @@ func NewAdvertiseFromSolicit(solicit DHCPv6, modifiers ...Modifier) (DHCPv6, err } // build ADVERTISE from SOLICIT adv := DHCPv6Message{} - adv.SetMessage(ADVERTISE) + adv.SetMessage(MessageTypeAdvertise) adv.SetTransactionID(sol.TransactionID()) // add Client ID cid := sol.GetOneOption(OPTION_CLIENTID) @@ -146,7 +146,7 @@ func NewRequestFromAdvertise(advertise DHCPv6, modifiers ...Modifier) (DHCPv6, e if advertise == nil { return nil, fmt.Errorf("ADVERTISE cannot be nil") } - if advertise.Type() != ADVERTISE { + if advertise.Type() != MessageTypeAdvertise { return nil, fmt.Errorf("The passed ADVERTISE must have ADVERTISE type set") } adv, ok := advertise.(*DHCPv6Message) @@ -155,7 +155,7 @@ func NewRequestFromAdvertise(advertise DHCPv6, modifiers ...Modifier) (DHCPv6, e } // build REQUEST from ADVERTISE req := DHCPv6Message{} - req.SetMessage(REQUEST) + req.SetMessage(MessageTypeRequest) req.SetTransactionID(adv.TransactionID()) // add Client ID cid := adv.GetOneOption(OPTION_CLIENTID) @@ -207,9 +207,9 @@ func NewReplyFromDHCPv6Message(message DHCPv6, modifiers ...Modifier) (DHCPv6, e return nil, errors.New("DHCPv6Message cannot be nil") } switch message.Type() { - case REQUEST, CONFIRM, RENEW, REBIND, RELEASE: - default: - return nil, errors.New("Cannot create REPLY from the passed message type set") + case MessageTypeRequest, MessageTypeConfirm, MessageTypeRenew, MessageTypeRebind, MessageTypeRelease: + default: + return nil, errors.New("Cannot create REPLY from the passed message type set") } msg, ok := message.(*DHCPv6Message) if !ok { @@ -217,7 +217,7 @@ func NewReplyFromDHCPv6Message(message DHCPv6, modifiers ...Modifier) (DHCPv6, e } // build REPLY from MESSAGE rep := DHCPv6Message{} - rep.SetMessage(REPLY) + rep.SetMessage(MessageTypeReply) rep.SetTransactionID(msg.TransactionID()) // add Client ID cid := message.GetOneOption(OPTION_CLIENTID) @@ -243,7 +243,7 @@ func (d *DHCPv6Message) SetMessage(messageType MessageType) { if msgString == "" { log.Printf("Warning: unknown DHCPv6 message type: %v", messageType) } - if messageType == RELAY_FORW || messageType == RELAY_REPL { + if messageType == MessageTypeRelayForward || messageType == MessageTypeRelayReply { log.Printf("Warning: using a RELAY message type with a non-relay message: %v (%v)", msgString, messageType) } diff --git a/dhcpv6/dhcpv6relay.go b/dhcpv6/dhcpv6relay.go index 1bde354..221c65e 100644 --- a/dhcpv6/dhcpv6relay.go +++ b/dhcpv6/dhcpv6relay.go @@ -173,7 +173,7 @@ func NewRelayReplFromRelayForw(relayForw, msg DHCPv6) (DHCPv6, error) { if !ok { return nil, errors.New("Not a DHCPv6Relay") } - if relay.Type() != RELAY_FORW { + if relay.Type() != MessageTypeRelayForward { return nil, errors.New("The passed packet is not of type RELAY_FORW") } if msg == nil { @@ -197,7 +197,7 @@ func NewRelayReplFromRelayForw(relayForw, msg DHCPv6) (DHCPv6, error) { } } for i := len(linkAddr) - 1; i >= 0; i-- { - msg, err = EncapsulateRelay(msg, RELAY_REPL, linkAddr[i], peerAddr[i]) + msg, err = EncapsulateRelay(msg, MessageTypeRelayReply, linkAddr[i], peerAddr[i]) if opt := optiids[i]; opt != nil { msg.AddOption(opt) } diff --git a/dhcpv6/dhcpv6relay_test.go b/dhcpv6/dhcpv6relay_test.go index e08e6a4..afb4086 100644 --- a/dhcpv6/dhcpv6relay_test.go +++ b/dhcpv6/dhcpv6relay_test.go @@ -12,15 +12,15 @@ func TestDHCPv6Relay(t *testing.T) { ll := net.IP{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0xcc, 0xff, 0xfe, 0xdd, 0xee, 0xff} ma := net.IP{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} r := DHCPv6Relay{ - messageType: RELAY_FORW, + messageType: MessageTypeRelayForward, hopCount: 10, linkAddr: ll, peerAddr: ma, // options is left empty here for testing purposes, even if it's // mandatory to have at least a relay message option } - if mt := r.MessageType(); mt != RELAY_FORW { - t.Fatalf("Invalid message type. Expected %v, got %v", RELAY_FORW, mt) + if mt := r.MessageType(); mt != MessageTypeRelayForward { + t.Fatalf("Invalid message type. Expected %v, got %v", MessageTypeRelayForward, mt) } if hc := r.HopCount(); hc != 10 { t.Fatalf("Invalid hop count. Expected 10, got %v", hc) @@ -39,9 +39,9 @@ func TestDHCPv6Relay(t *testing.T) { func TestDHCPv6RelaySettersAndGetters(t *testing.T) { r := DHCPv6Relay{} // set and get message type - r.SetMessageType(RELAY_REPL) - if mt := r.MessageType(); mt != RELAY_REPL { - t.Fatalf("Invalid message type. Expected %v, got %v", RELAY_REPL, mt) + r.SetMessageType(MessageTypeRelayReply) + if mt := r.MessageType(); mt != MessageTypeRelayReply { + t.Fatalf("Invalid message type. Expected %v, got %v", MessageTypeRelayReply, mt) } // set and get hop count r.SetHopCount(5) @@ -68,7 +68,7 @@ func TestDHCPv6RelaySettersAndGetters(t *testing.T) { func TestDHCPv6RelayToBytes(t *testing.T) { expected := []byte{ - 12, // RELAY_FORW + 12, // MessageTypeRelayForward 1, // hop count 0xff, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, // link addr 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, // peer addr @@ -76,7 +76,7 @@ func TestDHCPv6RelayToBytes(t *testing.T) { 0, 9, // relay msg 0, 10, // option length // inner dhcp solicit - 1, // SOLICIT + 1, // MessageTypeSolicit 0xaa, 0xbb, 0xcc, // transaction ID // inner option - elapsed time 0, 8, // elapsed time @@ -84,14 +84,14 @@ func TestDHCPv6RelayToBytes(t *testing.T) { 0, 0, } r := DHCPv6Relay{ - messageType: RELAY_FORW, + messageType: MessageTypeRelayForward, hopCount: 1, linkAddr: net.IPv6interfacelocalallnodes, peerAddr: net.IPv6linklocalallrouters, } opt := OptRelayMsg{ relayMessage: &DHCPv6Message{ - messageType: SOLICIT, + messageType: MessageTypeSolicit, transactionID: 0xaabbcc, options: []Option{ &OptElapsedTime{ @@ -109,12 +109,12 @@ func TestDHCPv6RelayToBytes(t *testing.T) { func TestNewRelayRepFromRelayForw(t *testing.T) { rf := DHCPv6Relay{} - rf.SetMessageType(RELAY_FORW) + rf.SetMessageType(MessageTypeRelayForward) rf.SetPeerAddr(net.IPv6linklocalallrouters) rf.SetLinkAddr(net.IPv6interfacelocalallnodes) oro := OptRelayMsg{} s := DHCPv6Message{} - s.SetMessage(SOLICIT) + s.SetMessage(MessageTypeSolicit) cid := OptClientId{} s.AddOption(&cid) oro.SetRelayMessage(&s) @@ -125,7 +125,7 @@ func TestNewRelayRepFromRelayForw(t *testing.T) { rr, err := NewRelayReplFromRelayForw(&rf, a) require.NoError(t, err) relay := rr.(*DHCPv6Relay) - require.Equal(t, rr.Type(), RELAY_REPL) + require.Equal(t, rr.Type(), MessageTypeRelayReply) require.Equal(t, relay.HopCount(), rf.HopCount()) require.Equal(t, relay.PeerAddr(), rf.PeerAddr()) require.Equal(t, relay.LinkAddr(), rf.LinkAddr()) diff --git a/dhcpv6/option_relaymsg_test.go b/dhcpv6/option_relaymsg_test.go index 6d9d6a6..6376813 100644 --- a/dhcpv6/option_relaymsg_test.go +++ b/dhcpv6/option_relaymsg_test.go @@ -7,7 +7,7 @@ import ( func TestRelayMsgParseOptRelayMsg(t *testing.T) { opt, err := ParseOptRelayMsg([]byte{ - 1, // SOLICIT + 1, // MessageTypeSolicit 0xaa, 0xbb, 0xcc, // transaction ID 0, 8, // option: elapsed time 0, 2, // option length @@ -27,7 +27,7 @@ func TestRelayMsgOptionsFromBytes(t *testing.T) { opts, err := OptionsFromBytes([]byte{ 0, 9, // option: relay message 0, 10, // relayed message length - 1, // SOLICIT + 1, // MessageTypeSolicit 0xaa, 0xbb, 0xcc, // transaction ID 0, 8, // option: elapsed time 0, 2, // option length @@ -55,7 +55,7 @@ func TestRelayMsgParseOptRelayMsgSingleEncapsulation(t *testing.T) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, // peerAddr 0, 9, // option: relay message 0, 10, // relayed message length - 1, // SOLICIT + 1, // MessageTypeSolicit 0xaa, 0xbb, 0xcc, // transaction ID 0, 8, // option: elapsed time 0, 2, // option length @@ -70,8 +70,8 @@ func TestRelayMsgParseOptRelayMsgSingleEncapsulation(t *testing.T) { reflect.TypeOf(d), ) } - if mType := r.Type(); mType != RELAY_FORW { - t.Fatalf("Invalid messge type for relay. Expected %v, got %v", RELAY_FORW, mType) + if mType := r.Type(); mType != MessageTypeRelayForward { + t.Fatalf("Invalid messge type for relay. Expected %v, got %v", MessageTypeRelayForward, mType) } if len(r.options) != 1 { t.Fatalf("Invalid number of options. Expected 1, got %v", len(r.options)) @@ -94,9 +94,9 @@ func TestRelayMsgParseOptRelayMsgSingleEncapsulation(t *testing.T) { reflect.TypeOf(innerDHCP), ) } - if dType := innerDHCP.Type(); dType != SOLICIT { - t.Fatalf("Invalid inner DHCP type. Expected SOLICIT (%v), got %v", - SOLICIT, dType, + if dType := innerDHCP.Type(); dType != MessageTypeSolicit { + t.Fatalf("Invalid inner DHCP type. Expected MessageTypeSolicit (%v), got %v", + MessageTypeSolicit, dType, ) } if tID := innerDHCP.TransactionID(); tID != 0xaabbcc { diff --git a/dhcpv6/types.go b/dhcpv6/types.go index 795a284..1ef5938 100644 --- a/dhcpv6/types.go +++ b/dhcpv6/types.go @@ -2,53 +2,59 @@ package dhcpv6 // from http://www.networksorcery.com/enp/protocol/dhcpv6.htm +// MessageType represents the kind of DHCPv6 message. type MessageType uint8 +// The different kinds of DHCPv6 message types. const ( - // MSGTYPE_NONE is used internally and is not part of the RFC - MSGTYPE_NONE MessageType = 0 - SOLICIT MessageType = 1 - ADVERTISE MessageType = 2 - REQUEST MessageType = 3 - CONFIRM MessageType = 4 - RENEW MessageType = 5 - REBIND MessageType = 6 - REPLY MessageType = 7 - RELEASE MessageType = 8 - DECLINE MessageType = 9 - RECONFIGURE MessageType = 10 - INFORMATION_REQUEST MessageType = 11 - RELAY_FORW MessageType = 12 - RELAY_REPL MessageType = 13 - LEASEQUERY MessageType = 14 - LEASEQUERY_REPLY MessageType = 15 - LEASEQUERY_DONE MessageType = 16 - LEASEQUERY_DATA MessageType = 17 + // MessageTypeNone is used internally and is not part of the RFC + MessageTypeNone MessageType = 0 + MessageTypeSolicit MessageType = 1 + MessageTypeAdvertise MessageType = 2 + MessageTypeRequest MessageType = 3 + MessageTypeConfirm MessageType = 4 + MessageTypeRenew MessageType = 5 + MessageTypeRebind MessageType = 6 + MessageTypeReply MessageType = 7 + MessageTypeRelease MessageType = 8 + MessageTypeDecline MessageType = 9 + MessageTypeReconfigure MessageType = 10 + MessageTypeInformationRequest MessageType = 11 + MessageTypeRelayForward MessageType = 12 + MessageTypeRelayReply MessageType = 13 + MessageTypeLeaseQuery MessageType = 14 + MessageTypeLeaseQueryReply MessageType = 15 + MessageTypeLeaseQueryDone MessageType = 16 + MessageTypeLeaseQueryData MessageType = 17 ) +// MessageTypeToString converts a MessageType to a human-readable string +// representation. func MessageTypeToString(t MessageType) string { - if m := MessageTypeToStringMap[t]; m != "" { + if m, ok := MessageTypeToStringMap[t]; ok { return m } return "Unknown" } +// MessageTypeToStringMap contains the mapping of MessageTypes to human-readable +// strings. var MessageTypeToStringMap = map[MessageType]string{ - SOLICIT: "SOLICIT", - ADVERTISE: "ADVERTISE", - REQUEST: "REQUEST", - CONFIRM: "CONFIRM", - RENEW: "RENEW", - REBIND: "REBIND", - REPLY: "REPLY", - RELEASE: "RELEASE", - DECLINE: "DECLINE", - RECONFIGURE: "RECONFIGURE", - INFORMATION_REQUEST: "INFORMATION-REQUEST", - RELAY_FORW: "RELAY-FORW", - RELAY_REPL: "RELAY-REPL", - LEASEQUERY: "LEASEQUERY", - LEASEQUERY_REPLY: "LEASEQUERY-REPLY", - LEASEQUERY_DONE: "LEASEQUERY-DONE", - LEASEQUERY_DATA: "LEASEQUERY-DATA", + MessageTypeSolicit: "SOLICIT", + MessageTypeAdvertise: "ADVERTISE", + MessageTypeRequest: "REQUEST", + MessageTypeConfirm: "CONFIRM", + MessageTypeRenew: "RENEW", + MessageTypeRebind: "REBIND", + MessageTypeReply: "REPLY", + MessageTypeRelease: "RELEASE", + MessageTypeDecline: "DECLINE", + MessageTypeReconfigure: "RECONFIGURE", + MessageTypeInformationRequest: "INFORMATION-REQUEST", + MessageTypeRelayForward: "RELAY-FORW", + MessageTypeRelayReply: "RELAY-REPL", + MessageTypeLeaseQuery: "LEASEQUERY", + MessageTypeLeaseQueryReply: "LEASEQUERY-REPLY", + MessageTypeLeaseQueryDone: "LEASEQUERY-DONE", + MessageTypeLeaseQueryData: "LEASEQUERY-DATA", } diff --git a/dhcpv6/utils_test.go b/dhcpv6/utils_test.go index 77205b4..36b1683 100644 --- a/dhcpv6/utils_test.go +++ b/dhcpv6/utils_test.go @@ -61,7 +61,7 @@ func TestGetTransactionIDMessage(t *testing.T) { func TestGetTransactionIDRelay(t *testing.T) { message, err := NewMessage() require.NoError(t, err) - relay, err := EncapsulateRelay(message, RELAY_FORW, nil, nil) + relay, err := EncapsulateRelay(message, MessageTypeRelayForward, nil, nil) require.NoError(t, err) transactionID, err := GetTransactionID(relay) require.NoError(t, err) -- cgit v1.2.3 From b57d4fdd4144e8935756273a4524ee3cd952bd00 Mon Sep 17 00:00:00 2001 From: Sean Karlage Date: Sun, 29 Jul 2018 18:10:16 -0700 Subject: Fix message type usage in netboot package --- netboot/netboot.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netboot/netboot.go b/netboot/netboot.go index 97a3240..643c5ec 100644 --- a/netboot/netboot.go +++ b/netboot/netboot.go @@ -50,7 +50,7 @@ func ConversationToNetconf(conversation []dhcpv6.DHCPv6) (*NetConf, string, erro var reply dhcpv6.DHCPv6 for _, m := range conversation { // look for a REPLY - if m.Type() == dhcpv6.REPLY { + if m.Type() == dhcpv6.MessageTypeReply { reply = m break } @@ -74,7 +74,7 @@ func ConversationToNetconf(conversation []dhcpv6.DHCPv6) (*NetConf, string, erro var advertise dhcpv6.DHCPv6 for _, m := range conversation { // look for an ADVERTISE - if m.Type() == dhcpv6.ADVERTISE { + if m.Type() == dhcpv6.MessageTypeAdvertise { advertise = m break } -- cgit v1.2.3 From 74f2ee0b2fc6af0602360c563490c0b140f98b9e Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Mon, 30 Jul 2018 10:42:41 +0200 Subject: add OptIPAddressLeaseTime --- dhcpv4/option_ip_address_lease_time.go | 57 +++++++++++++++++++++++++ dhcpv4/option_ip_address_lease_time_test.go | 41 ++++++++++++++++++ dhcpv4/option_maximum_dhcp_message_size.go | 4 +- dhcpv4/option_maximum_dhcp_message_size_test.go | 2 +- dhcpv4/options.go | 2 + 5 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 dhcpv4/option_ip_address_lease_time.go create mode 100644 dhcpv4/option_ip_address_lease_time_test.go diff --git a/dhcpv4/option_ip_address_lease_time.go b/dhcpv4/option_ip_address_lease_time.go new file mode 100644 index 0000000..a171b04 --- /dev/null +++ b/dhcpv4/option_ip_address_lease_time.go @@ -0,0 +1,57 @@ +package dhcpv4 + +import ( + "encoding/binary" + "fmt" +) + +// This option implements the IP Address Lease Time option +// https://tools.ietf.org/html/rfc2132 + +// OptIPAddressLeaseTime represents the IP Address Lease Time option. +type OptIPAddressLeaseTime struct { + LeaseTime uint32 +} + +// ParseOptIPAddressLeaseTime constructs an OptIPAddressLeaseTime struct from a +// sequence of bytes and returns it, or an error. +func ParseOptIPAddressLeaseTime(data []byte) (*OptIPAddressLeaseTime, error) { + // Should at least have code, length, and lease time. + if len(data) < 4 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionIPAddressLeaseTime { + return nil, fmt.Errorf("expected option %v, got %v instead", OptionIPAddressLeaseTime, code) + } + length := int(data[1]) + if length != 4 { + return nil, fmt.Errorf("expected length 4, got %v instead", length) + } + leaseTime := binary.BigEndian.Uint32(data[2:6]) + return &OptIPAddressLeaseTime{LeaseTime: leaseTime}, nil +} + +// Code returns the option code. +func (o *OptIPAddressLeaseTime) Code() OptionCode { + return OptionIPAddressLeaseTime +} + +// ToBytes returns a serialized stream of bytes for this option. +func (o *OptIPAddressLeaseTime) ToBytes() []byte { + serializedTime := make([]byte, 4) + binary.BigEndian.PutUint32(serializedTime, o.LeaseTime) + serializedOpt := []byte{byte(o.Code()), byte(o.Length())} + return append(serializedOpt, serializedTime...) +} + +// String returns a human-readable string for this option. +func (o *OptIPAddressLeaseTime) String() string { + return fmt.Sprintf("IP Addresses Lease Time -> %v", o.LeaseTime) +} + +// Length returns the length of the data portion (excluding option code and byte +// for length, if any). +func (o *OptIPAddressLeaseTime) Length() int { + return 4 +} diff --git a/dhcpv4/option_ip_address_lease_time_test.go b/dhcpv4/option_ip_address_lease_time_test.go new file mode 100644 index 0000000..36bfe40 --- /dev/null +++ b/dhcpv4/option_ip_address_lease_time_test.go @@ -0,0 +1,41 @@ +package dhcpv4 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOptIPAddressLeaseTimeInterfaceMethods(t *testing.T) { + o := OptIPAddressLeaseTime{LeaseTime: 43200} + require.Equal(t, OptionIPAddressLeaseTime, o.Code(), "Code") + require.Equal(t, 4, o.Length(), "Length") + require.Equal(t, []byte{51, 4, 0, 0, 168, 192}, o.ToBytes(), "ToBytes") +} + +func TestParseOptIPAddressLeaseTime(t *testing.T) { + data := []byte{51, 4, 0, 0, 168, 192} + o, err := ParseOptIPAddressLeaseTime(data) + require.NoError(t, err) + require.Equal(t, &OptIPAddressLeaseTime{LeaseTime: 43200}, o) + + // Short byte stream + data = []byte{51, 4} + _, err = ParseOptIPAddressLeaseTime(data) + require.Error(t, err, "should get error from short byte stream") + + // Wrong code + data = []byte{54, 4, 0, 0, 168, 192} + _, err = ParseOptIPAddressLeaseTime(data) + require.Error(t, err, "should get error from wrong code") + + // Bad length + data = []byte{51, 5, 1, 1, 1, 1, 1} + _, err = ParseOptMaximumDHCPMessageSize(data) + require.Error(t, err, "should get error from bad length") +} + +func TestOptIPAddressLeaseTimeString(t *testing.T) { + o := OptIPAddressLeaseTime{LeaseTime: 43200} + require.Equal(t, "IP Addresses Lease Time -> 43200", o.String()) +} diff --git a/dhcpv4/option_maximum_dhcp_message_size.go b/dhcpv4/option_maximum_dhcp_message_size.go index 05186f5..e5fedc6 100644 --- a/dhcpv4/option_maximum_dhcp_message_size.go +++ b/dhcpv4/option_maximum_dhcp_message_size.go @@ -8,7 +8,7 @@ import ( // This option implements the Maximum DHCP Message size option // https://tools.ietf.org/html/rfc2132 -// OptMaximumDHCPMessageSize represents the DHCP message type option. +// OptMaximumDHCPMessageSize represents the Maximum DHCP Message size option. type OptMaximumDHCPMessageSize struct { Size uint16 } @@ -16,7 +16,7 @@ type OptMaximumDHCPMessageSize struct { // ParseOptMaximumDHCPMessageSize constructs an OptMaximumDHCPMessageSize struct from a sequence of // bytes and returns it, or an error. func ParseOptMaximumDHCPMessageSize(data []byte) (*OptMaximumDHCPMessageSize, error) { - // Should at least have code, length, and message type. + // Should at least have code, length, and message size. if len(data) < 4 { return nil, ErrShortByteStream } diff --git a/dhcpv4/option_maximum_dhcp_message_size_test.go b/dhcpv4/option_maximum_dhcp_message_size_test.go index 65a26fc..f24b499 100644 --- a/dhcpv4/option_maximum_dhcp_message_size_test.go +++ b/dhcpv4/option_maximum_dhcp_message_size_test.go @@ -14,7 +14,7 @@ func TestOptMaximumDHCPMessageSizeInterfaceMethods(t *testing.T) { } func TestParseOptMaximumDHCPMessageSize(t *testing.T) { - data := []byte{57, 2, 5, 220} // DISCOVER + data := []byte{57, 2, 5, 220} o, err := ParseOptMaximumDHCPMessageSize(data) require.NoError(t, err) require.Equal(t, &OptMaximumDHCPMessageSize{Size: 1500}, o) diff --git a/dhcpv4/options.go b/dhcpv4/options.go index 4224fa0..760bc98 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -46,6 +46,8 @@ func ParseOption(data []byte) (Option, error) { opt, err = ParseOptRouter(data) case OptionRequestedIPAddress: opt, err = ParseOptRequestedIPAddress(data) + case OptionIPAddressLeaseTime: + opt, err = ParseOptIPAddressLeaseTime(data) case OptionDHCPMessageType: opt, err = ParseOptMessageType(data) case OptionParameterRequestList: -- cgit v1.2.3 From b64f581df56ab8050636c58d5096b6b499725807 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Mon, 30 Jul 2018 10:45:53 +0200 Subject: OptIPAddressLeaseTime: fix test --- dhcpv4/option_ip_address_lease_time_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhcpv4/option_ip_address_lease_time_test.go b/dhcpv4/option_ip_address_lease_time_test.go index 36bfe40..66715bd 100644 --- a/dhcpv4/option_ip_address_lease_time_test.go +++ b/dhcpv4/option_ip_address_lease_time_test.go @@ -31,7 +31,7 @@ func TestParseOptIPAddressLeaseTime(t *testing.T) { // Bad length data = []byte{51, 5, 1, 1, 1, 1, 1} - _, err = ParseOptMaximumDHCPMessageSize(data) + _, err = ParseOptIPAddressLeaseTime(data) require.Error(t, err, "should get error from bad length") } -- cgit v1.2.3 From c3434f38325856dcdc19adc29b5e31944614221b Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Mon, 30 Jul 2018 19:05:13 +0200 Subject: fix OptIPAddressLeaseTime --- dhcpv4/option_ip_address_lease_time.go | 2 +- dhcpv4/option_ip_address_lease_time_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dhcpv4/option_ip_address_lease_time.go b/dhcpv4/option_ip_address_lease_time.go index a171b04..7562c58 100644 --- a/dhcpv4/option_ip_address_lease_time.go +++ b/dhcpv4/option_ip_address_lease_time.go @@ -17,7 +17,7 @@ type OptIPAddressLeaseTime struct { // sequence of bytes and returns it, or an error. func ParseOptIPAddressLeaseTime(data []byte) (*OptIPAddressLeaseTime, error) { // Should at least have code, length, and lease time. - if len(data) < 4 { + if len(data) < 6 { return nil, ErrShortByteStream } code := OptionCode(data[0]) diff --git a/dhcpv4/option_ip_address_lease_time_test.go b/dhcpv4/option_ip_address_lease_time_test.go index 66715bd..7d507bf 100644 --- a/dhcpv4/option_ip_address_lease_time_test.go +++ b/dhcpv4/option_ip_address_lease_time_test.go @@ -20,7 +20,7 @@ func TestParseOptIPAddressLeaseTime(t *testing.T) { require.Equal(t, &OptIPAddressLeaseTime{LeaseTime: 43200}, o) // Short byte stream - data = []byte{51, 4} + data = []byte{51, 4, 168, 192} _, err = ParseOptIPAddressLeaseTime(data) require.Error(t, err, "should get error from short byte stream") -- cgit v1.2.3 From 7319b8f8a1a601b5e69c5bcaa33914c0f85b92e6 Mon Sep 17 00:00:00 2001 From: Sean Karlage Date: Sun, 29 Jul 2018 17:54:30 -0700 Subject: DHCPv6: Rename Option constants to CamelCase To appease linters --- dhcpv4/option_userclass_test.go | 4 +- dhcpv6/dhcpv6.go | 2 +- dhcpv6/dhcpv6_test.go | 8 +- dhcpv6/dhcpv6message.go | 20 +- dhcpv6/dhcpv6relay.go | 2 +- dhcpv6/modifiers.go | 8 +- dhcpv6/modifiers_test.go | 4 +- dhcpv6/option_archtype.go | 4 +- dhcpv6/option_archtype_test.go | 4 +- dhcpv6/option_bootfileurl.go | 6 +- dhcpv6/option_clientid.go | 4 +- dhcpv6/option_clientid_test.go | 6 +- dhcpv6/option_dnsrecursivenameserver.go | 6 +- dhcpv6/option_dnsrecursivenameserver_test.go | 2 +- dhcpv6/option_domainsearchlist.go | 6 +- dhcpv6/option_domainsearchlist_test.go | 2 +- dhcpv6/option_elapsedtime.go | 4 +- dhcpv6/option_iaaddress.go | 6 +- dhcpv6/option_iaaddress_test.go | 2 +- dhcpv6/option_iaprefix.go | 4 +- dhcpv6/option_interfaceid.go | 4 +- dhcpv6/option_nii.go | 4 +- dhcpv6/option_nontemporaryaddress.go | 4 +- dhcpv6/option_nontemporaryaddress_test.go | 10 +- dhcpv6/option_prefixdelegation.go | 4 +- dhcpv6/option_relaymsg.go | 4 +- dhcpv6/option_relaymsg_test.go | 18 +- dhcpv6/option_remoteid.go | 4 +- dhcpv6/option_requestedoption.go | 4 +- dhcpv6/option_serverid.go | 4 +- dhcpv6/option_serverid_test.go | 6 +- dhcpv6/option_statuscode.go | 4 +- dhcpv6/option_statuscode_test.go | 2 +- dhcpv6/option_types.go | 287 ++++++++++++++------------- dhcpv6/option_userclass.go | 4 +- dhcpv6/option_userclass_test.go | 4 +- dhcpv6/options.go | 36 ++-- dhcpv6/utils.go | 10 +- dhcpv6/utils_test.go | 2 +- netboot/netboot.go | 4 +- netboot/netconf.go | 8 +- 41 files changed, 266 insertions(+), 265 deletions(-) diff --git a/dhcpv4/option_userclass_test.go b/dhcpv4/option_userclass_test.go index 5b71ea5..492cabd 100644 --- a/dhcpv4/option_userclass_test.go +++ b/dhcpv4/option_userclass_test.go @@ -12,7 +12,7 @@ func TestOptUserClassToBytes(t *testing.T) { } data := opt.ToBytes() expected := []byte{ - 77, // OPTION_USER_CLASS + 77, // OptionUserClass 10, // length 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', } @@ -57,7 +57,7 @@ func TestOptUserClassToBytesMultiple(t *testing.T) { } data := opt.ToBytes() expected := []byte{ - 77, // OPTION_USER_CLASS + 77, // OptionUserClass 15, // length 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', 4, 't', 'e', 's', 't', diff --git a/dhcpv6/dhcpv6.go b/dhcpv6/dhcpv6.go index 253a635..0dabca5 100644 --- a/dhcpv6/dhcpv6.go +++ b/dhcpv6/dhcpv6.go @@ -134,7 +134,7 @@ func DecapsulateRelay(l DHCPv6) (DHCPv6, error) { if !l.IsRelay() { return l, nil } - opt := l.GetOneOption(OPTION_RELAY_MSG) + opt := l.GetOneOption(OptionRelayMsg) if opt == nil { return nil, fmt.Errorf("No OptRelayMsg found") } diff --git a/dhcpv6/dhcpv6_test.go b/dhcpv6/dhcpv6_test.go index 656c62f..4a44e0e 100644 --- a/dhcpv6/dhcpv6_test.go +++ b/dhcpv6/dhcpv6_test.go @@ -198,20 +198,20 @@ func TestNewMessageTypeSolicitWithCID(t *testing.T) { require.Equal(t, s.Type(), MessageTypeSolicit) // Check CID - cidOption := s.GetOneOption(OPTION_CLIENTID) + cidOption := s.GetOneOption(OptionClientID) require.NotNil(t, cidOption) cid, ok := cidOption.(*OptClientId) require.True(t, ok) require.Equal(t, cid.Cid, duid) // Check ORO - oroOption := s.GetOneOption(OPTION_ORO) + oroOption := s.GetOneOption(OptionORO) require.NotNil(t, oroOption) oro, ok := oroOption.(*OptRequestedOption) require.True(t, ok) opts := oro.RequestedOptions() - require.Contains(t, opts, DNS_RECURSIVE_NAME_SERVER) - require.Contains(t, opts, DOMAIN_SEARCH_LIST) + require.Contains(t, opts, OptionDNSRecursiveNameServer) + require.Contains(t, opts, OptionDomainSearchList) require.Equal(t, len(opts), 2) } diff --git a/dhcpv6/dhcpv6message.go b/dhcpv6/dhcpv6message.go index be413f5..e601932 100644 --- a/dhcpv6/dhcpv6message.go +++ b/dhcpv6/dhcpv6message.go @@ -75,8 +75,8 @@ func NewSolicitWithCID(duid Duid, modifiers ...Modifier) (DHCPv6, error) { d.AddOption(&OptClientId{Cid: duid}) oro := new(OptRequestedOption) oro.SetRequestedOptions([]OptionCode{ - DNS_RECURSIVE_NAME_SERVER, - DOMAIN_SEARCH_LIST, + OptionDNSRecursiveNameServer, + OptionDomainSearchList, }) d.AddOption(oro) d.AddOption(&OptElapsedTime{}) @@ -126,7 +126,7 @@ func NewAdvertiseFromSolicit(solicit DHCPv6, modifiers ...Modifier) (DHCPv6, err adv.SetMessage(MessageTypeAdvertise) adv.SetTransactionID(sol.TransactionID()) // add Client ID - cid := sol.GetOneOption(OPTION_CLIENTID) + cid := sol.GetOneOption(OptionClientID) if cid == nil { return nil, errors.New("Client ID cannot be nil in SOLICIT when building ADVERTISE") } @@ -158,13 +158,13 @@ func NewRequestFromAdvertise(advertise DHCPv6, modifiers ...Modifier) (DHCPv6, e req.SetMessage(MessageTypeRequest) req.SetTransactionID(adv.TransactionID()) // add Client ID - cid := adv.GetOneOption(OPTION_CLIENTID) + cid := adv.GetOneOption(OptionClientID) if cid == nil { return nil, fmt.Errorf("Client ID cannot be nil in ADVERTISE when building REQUEST") } req.AddOption(cid) // add Server ID - sid := adv.GetOneOption(OPTION_SERVERID) + sid := adv.GetOneOption(OptionServerID) if sid == nil { return nil, fmt.Errorf("Server ID cannot be nil in ADVERTISE when building REQUEST") } @@ -172,7 +172,7 @@ func NewRequestFromAdvertise(advertise DHCPv6, modifiers ...Modifier) (DHCPv6, e // add Elapsed Time req.AddOption(&OptElapsedTime{}) // add IA_NA - iaNa := adv.GetOneOption(OPTION_IA_NA) + iaNa := adv.GetOneOption(OptionIANA) if iaNa == nil { return nil, fmt.Errorf("IA_NA cannot be nil in ADVERTISE when building REQUEST") } @@ -180,13 +180,13 @@ func NewRequestFromAdvertise(advertise DHCPv6, modifiers ...Modifier) (DHCPv6, e // add OptRequestedOption oro := OptRequestedOption{} oro.SetRequestedOptions([]OptionCode{ - DNS_RECURSIVE_NAME_SERVER, - DOMAIN_SEARCH_LIST, + OptionDNSRecursiveNameServer, + OptionDomainSearchList, }) req.AddOption(&oro) // add OPTION_VENDOR_CLASS, only if present in the original request // TODO implement OptionVendorClass - vClass := adv.GetOneOption(OPTION_VENDOR_CLASS) + vClass := adv.GetOneOption(OptionVendorClass) if vClass != nil { req.AddOption(vClass) } @@ -220,7 +220,7 @@ func NewReplyFromDHCPv6Message(message DHCPv6, modifiers ...Modifier) (DHCPv6, e rep.SetMessage(MessageTypeReply) rep.SetTransactionID(msg.TransactionID()) // add Client ID - cid := message.GetOneOption(OPTION_CLIENTID) + cid := message.GetOneOption(OptionClientID) if cid == nil { return nil, errors.New("Client ID cannot be nil when building REPLY") } diff --git a/dhcpv6/dhcpv6relay.go b/dhcpv6/dhcpv6relay.go index 221c65e..d9555cb 100644 --- a/dhcpv6/dhcpv6relay.go +++ b/dhcpv6/dhcpv6relay.go @@ -185,7 +185,7 @@ func NewRelayReplFromRelayForw(relayForw, msg DHCPv6) (DHCPv6, error) { for { linkAddr = append(linkAddr, relay.LinkAddr()) peerAddr = append(peerAddr, relay.PeerAddr()) - optiids = append(optiids, relay.GetOneOption(OPTION_INTERFACE_ID)) + optiids = append(optiids, relay.GetOneOption(OptionInterfaceID)) decap, err := DecapsulateRelay(relay) if err != nil { return nil, err diff --git a/dhcpv6/modifiers.go b/dhcpv6/modifiers.go index 54285cd..32d11ac 100644 --- a/dhcpv6/modifiers.go +++ b/dhcpv6/modifiers.go @@ -29,15 +29,15 @@ func WithNetboot(d DHCPv6) DHCPv6 { log.Printf("WithNetboot: not a DHCPv6Message") return d } - // add OPT_BOOTFILE_URL and OPT_BOOTFILE_PARAM - opt := msg.GetOneOption(OPTION_ORO) + // add OptionBootfileURL and OptionBootfileParam + opt := msg.GetOneOption(OptionORO) if opt == nil { opt = &OptRequestedOption{} } // TODO only add options if they are not there already oro := opt.(*OptRequestedOption) - oro.AddRequestedOption(OPT_BOOTFILE_URL) - oro.AddRequestedOption(OPT_BOOTFILE_PARAM) + oro.AddRequestedOption(OptionBootfileURL) + oro.AddRequestedOption(OptionBootfileParam) msg.UpdateOption(oro) return d } diff --git a/dhcpv6/modifiers_test.go b/dhcpv6/modifiers_test.go index da1ef56..ab21367 100644 --- a/dhcpv6/modifiers_test.go +++ b/dhcpv6/modifiers_test.go @@ -16,7 +16,7 @@ func TestWithClientID(t *testing.T) { } m, err := NewMessage(WithClientID(duid)) require.NoError(t, err) - opt := m.GetOneOption(OPTION_CLIENTID) + opt := m.GetOneOption(OptionClientID) require.NotNil(t, opt) cid := opt.(*OptClientId) require.Equal(t, cid.Cid, duid) @@ -30,7 +30,7 @@ func TestWithServerID(t *testing.T) { } m, err := NewMessage(WithServerID(duid)) require.NoError(t, err) - opt := m.GetOneOption(OPTION_SERVERID) + opt := m.GetOneOption(OptionServerID) require.NotNil(t, opt) sid := opt.(*OptServerId) require.Equal(t, sid.Sid, duid) diff --git a/dhcpv6/option_archtype.go b/dhcpv6/option_archtype.go index 802e334..a1b4a9b 100644 --- a/dhcpv6/option_archtype.go +++ b/dhcpv6/option_archtype.go @@ -45,12 +45,12 @@ type OptClientArchType struct { } func (op *OptClientArchType) Code() OptionCode { - return OPTION_CLIENT_ARCH_TYPE + return OptionClientArchType } func (op *OptClientArchType) ToBytes() []byte { buf := make([]byte, 6) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_CLIENT_ARCH_TYPE)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionClientArchType)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) binary.BigEndian.PutUint16(buf[4:6], uint16(op.ArchType)) return buf diff --git a/dhcpv6/option_archtype_test.go b/dhcpv6/option_archtype_test.go index b446661..748c8c5 100644 --- a/dhcpv6/option_archtype_test.go +++ b/dhcpv6/option_archtype_test.go @@ -26,7 +26,7 @@ func TestOptClientArchTypeParseAndToBytes(t *testing.T) { 0, 8, // EFI_XSCALE } expected := []byte{ - 0, 61, // OPTION_CLIENT_ARCH_TYPE + 0, 61, // OptionClientArchType 0, 2, // length 0, 8, // EFI_XSCALE } @@ -40,5 +40,5 @@ func TestOptClientArchType(t *testing.T) { ArchType: EFI_ITANIUM, } require.Equal(t, opt.Length(), 2) - require.Equal(t, opt.Code(), OPTION_CLIENT_ARCH_TYPE) + require.Equal(t, opt.Code(), OptionClientArchType) } diff --git a/dhcpv6/option_bootfileurl.go b/dhcpv6/option_bootfileurl.go index e4817cb..d2da0c3 100644 --- a/dhcpv6/option_bootfileurl.go +++ b/dhcpv6/option_bootfileurl.go @@ -8,20 +8,20 @@ import ( "fmt" ) -// OptBootFileURL implements the OPT_BOOTFILE_URL option +// OptBootFileURL implements the OptionBootfileURL option type OptBootFileURL struct { BootFileURL []byte } // Code returns the option code func (op *OptBootFileURL) Code() OptionCode { - return OPT_BOOTFILE_URL + return OptionBootfileURL } // 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(OPT_BOOTFILE_URL)) + 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 diff --git a/dhcpv6/option_clientid.go b/dhcpv6/option_clientid.go index 3c01d8c..fc68ae7 100644 --- a/dhcpv6/option_clientid.go +++ b/dhcpv6/option_clientid.go @@ -14,12 +14,12 @@ type OptClientId struct { } func (op *OptClientId) Code() OptionCode { - return OPTION_CLIENTID + return OptionClientID } func (op *OptClientId) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_CLIENTID)) + 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 diff --git a/dhcpv6/option_clientid_test.go b/dhcpv6/option_clientid_test.go index dcaffca..77839f3 100644 --- a/dhcpv6/option_clientid_test.go +++ b/dhcpv6/option_clientid_test.go @@ -30,7 +30,7 @@ func TestOptClientIdToBytes(t *testing.T) { }, } expected := []byte{ - 0, 1, // OPTION_CLIENTID + 0, 1, // OptionClientID 0, 10, // length 0, 3, // DUID_LL 0, 1, // hwtype ethernet @@ -46,7 +46,7 @@ func TestOptClientIdDecodeEncode(t *testing.T) { 5, 4, 3, 2, 1, 0, // hw addr } expected := append([]byte{ - 0, 1, // OPTION_CLIENTID + 0, 1, // OptionClientID 0, 10, // length }, data...) opt, err := ParseOptClientId(data) @@ -63,5 +63,5 @@ func TestOptionClientId(t *testing.T) { }, } require.Equal(t, opt.Length(), 10) - require.Equal(t, opt.Code(), OPTION_CLIENTID) + require.Equal(t, opt.Code(), OptionClientID) } diff --git a/dhcpv6/option_dnsrecursivenameserver.go b/dhcpv6/option_dnsrecursivenameserver.go index 737a2d3..72b0767 100644 --- a/dhcpv6/option_dnsrecursivenameserver.go +++ b/dhcpv6/option_dnsrecursivenameserver.go @@ -9,21 +9,21 @@ import ( "net" ) -// OptDNSRecursiveNameServer represents a DNS_RECURSIVE_NAME_SERVER option +// OptDNSRecursiveNameServer represents a OptionDNSRecursiveNameServer option type OptDNSRecursiveNameServer struct { NameServers []net.IP } // Code returns the option code func (op *OptDNSRecursiveNameServer) Code() OptionCode { - return DNS_RECURSIVE_NAME_SERVER + return OptionDNSRecursiveNameServer } // ToBytes returns the option serialized to bytes, including option code and // length func (op *OptDNSRecursiveNameServer) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(DNS_RECURSIVE_NAME_SERVER)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionDNSRecursiveNameServer)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) for _, ns := range op.NameServers { buf = append(buf, ns...) diff --git a/dhcpv6/option_dnsrecursivenameserver_test.go b/dhcpv6/option_dnsrecursivenameserver_test.go index 85bb982..71fe6d3 100644 --- a/dhcpv6/option_dnsrecursivenameserver_test.go +++ b/dhcpv6/option_dnsrecursivenameserver_test.go @@ -25,7 +25,7 @@ func TestOptDNSRecursiveNameServerToBytes(t *testing.T) { ns2 := net.ParseIP("2001:4860:4860::8888") nameservers := []net.IP{ns1, ns2} expected := []byte{ - 0, 23, // DNS_RECURSIVE_NAME_SERVER + 0, 23, // OptionDNSRecursiveNameServer 0, 32, // length } expected = append(expected, []byte(ns1)...) diff --git a/dhcpv6/option_domainsearchlist.go b/dhcpv6/option_domainsearchlist.go index 402c68e..6e5504c 100644 --- a/dhcpv6/option_domainsearchlist.go +++ b/dhcpv6/option_domainsearchlist.go @@ -8,18 +8,18 @@ import ( "fmt" ) -// OptDomainSearchList list implements a DOMAIN_SEARCH_LIST option +// OptDomainSearchList list implements a OptionDomainSearchList option type OptDomainSearchList struct { DomainSearchList []string } func (op *OptDomainSearchList) Code() OptionCode { - return DOMAIN_SEARCH_LIST + return OptionDomainSearchList } func (op *OptDomainSearchList) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(DOMAIN_SEARCH_LIST)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionDomainSearchList)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) buf = append(buf, LabelsToBytes(op.DomainSearchList)...) return buf diff --git a/dhcpv6/option_domainsearchlist_test.go b/dhcpv6/option_domainsearchlist_test.go index d5b8bf7..68f93ba 100644 --- a/dhcpv6/option_domainsearchlist_test.go +++ b/dhcpv6/option_domainsearchlist_test.go @@ -20,7 +20,7 @@ func TestParseOptDomainSearchList(t *testing.T) { func TestOptDomainSearchListToBytes(t *testing.T) { expected := []byte{ - 0, 24, // DOMAIN_SEARCH_LIST + 0, 24, // OptionDomainSearchList 0, 33, // length 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 6, 's', 'u', 'b', 'n', 'e', 't', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'o', 'r', 'g', 0, diff --git a/dhcpv6/option_elapsedtime.go b/dhcpv6/option_elapsedtime.go index e0a1094..6009bf8 100644 --- a/dhcpv6/option_elapsedtime.go +++ b/dhcpv6/option_elapsedtime.go @@ -13,12 +13,12 @@ type OptElapsedTime struct { } func (op *OptElapsedTime) Code() OptionCode { - return OPTION_ELAPSED_TIME + return OptionElapsedTime } func (op *OptElapsedTime) ToBytes() []byte { buf := make([]byte, 6) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_ELAPSED_TIME)) + 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 diff --git a/dhcpv6/option_iaaddress.go b/dhcpv6/option_iaaddress.go index 1a89a25..0500d83 100644 --- a/dhcpv6/option_iaaddress.go +++ b/dhcpv6/option_iaaddress.go @@ -9,7 +9,7 @@ import ( "net" ) -// OptIAAddress represents an OPTION_IAADDR +// OptIAAddress represents an OptionIAAddr type OptIAAddress struct { IPv6Addr net.IP PreferredLifetime uint32 @@ -19,13 +19,13 @@ type OptIAAddress struct { // Code returns the option's code func (op *OptIAAddress) Code() OptionCode { - return OPTION_IAADDR + return OptionIAAddr } // 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(OPTION_IAADDR)) + 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) diff --git a/dhcpv6/option_iaaddress_test.go b/dhcpv6/option_iaaddress_test.go index ad4f69c..0240949 100644 --- a/dhcpv6/option_iaaddress_test.go +++ b/dhcpv6/option_iaaddress_test.go @@ -45,7 +45,7 @@ func TestOptIAAddressParseInvalidBrokenOptions(t *testing.T) { func TestOptIAAddressToBytes(t *testing.T) { expected := []byte{ - 0, 5, // OPTION_IAADDR + 0, 5, // OptionIAAddr 0, 30, // length } ipBytes := []byte{0x24, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} diff --git a/dhcpv6/option_iaprefix.go b/dhcpv6/option_iaprefix.go index 57810b1..3f6227b 100644 --- a/dhcpv6/option_iaprefix.go +++ b/dhcpv6/option_iaprefix.go @@ -18,12 +18,12 @@ type OptIAPrefix struct { } func (op *OptIAPrefix) Code() OptionCode { - return OPTION_IAPREFIX + return OptionIAPrefix } func (op *OptIAPrefix) ToBytes() []byte { buf := make([]byte, 12) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_IAPREFIX)) + 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) diff --git a/dhcpv6/option_interfaceid.go b/dhcpv6/option_interfaceid.go index 2634d9f..2bee744 100644 --- a/dhcpv6/option_interfaceid.go +++ b/dhcpv6/option_interfaceid.go @@ -13,12 +13,12 @@ type OptInterfaceId struct { } func (op *OptInterfaceId) Code() OptionCode { - return OPTION_INTERFACE_ID + return OptionInterfaceID } func (op *OptInterfaceId) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_INTERFACE_ID)) + 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 diff --git a/dhcpv6/option_nii.go b/dhcpv6/option_nii.go index 3a838ee..68e315e 100644 --- a/dhcpv6/option_nii.go +++ b/dhcpv6/option_nii.go @@ -33,12 +33,12 @@ type OptNetworkInterfaceId struct { } func (op *OptNetworkInterfaceId) Code() OptionCode { - return OPTION_NII + return OptionNII } func (op *OptNetworkInterfaceId) ToBytes() []byte { buf := make([]byte, 7) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_NII)) + 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 diff --git a/dhcpv6/option_nontemporaryaddress.go b/dhcpv6/option_nontemporaryaddress.go index 5debeec..62442c1 100644 --- a/dhcpv6/option_nontemporaryaddress.go +++ b/dhcpv6/option_nontemporaryaddress.go @@ -16,12 +16,12 @@ type OptIANA struct { } func (op *OptIANA) Code() OptionCode { - return OPTION_IA_NA + return OptionIANA } func (op *OptIANA) ToBytes() []byte { buf := make([]byte, 16) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_IA_NA)) + 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) diff --git a/dhcpv6/option_nontemporaryaddress_test.go b/dhcpv6/option_nontemporaryaddress_test.go index 54df8f0..39c3c36 100644 --- a/dhcpv6/option_nontemporaryaddress_test.go +++ b/dhcpv6/option_nontemporaryaddress_test.go @@ -47,7 +47,7 @@ func TestOptIANAGetOneOption(t *testing.T) { opt := OptIANA{ Options: []Option{&OptElapsedTime{}, oaddr}, } - require.Equal(t, oaddr, opt.GetOneOption(OPTION_IAADDR)) + require.Equal(t, oaddr, opt.GetOneOption(OptionIAAddr)) } func TestOptIANAGetOneOptionMissingOpt(t *testing.T) { @@ -57,7 +57,7 @@ func TestOptIANAGetOneOptionMissingOpt(t *testing.T) { opt := OptIANA{ Options: []Option{&OptElapsedTime{}, oaddr}, } - require.Equal(t, nil, opt.GetOneOption(DNS_RECURSIVE_NAME_SERVER)) + require.Equal(t, nil, opt.GetOneOption(OptionDNSRecursiveNameServer)) } func TestOptIANADelOption(t *testing.T) { @@ -69,13 +69,13 @@ func TestOptIANADelOption(t *testing.T) { optiana1.Options = append(optiana1.Options, &optsc) optiana1.Options = append(optiana1.Options, &optiaaddr) optiana1.Options = append(optiana1.Options, &optiaaddr) - optiana1.DelOption(OPTION_IAADDR) + optiana1.DelOption(OptionIAAddr) require.Equal(t, optiana1.Options, []Option{&optsc}) optiana2.Options = append(optiana2.Options, &optiaaddr) optiana2.Options = append(optiana2.Options, &optsc) optiana2.Options = append(optiana2.Options, &optiaaddr) - optiana2.DelOption(OPTION_IAADDR) + optiana2.DelOption(OptionIAAddr) require.Equal(t, optiana2.Options, []Option{&optsc}) } @@ -91,7 +91,7 @@ func TestOptIANAToBytes(t *testing.T) { }, } expected := []byte{ - 0, 3, // OPTION_IA_NA + 0, 3, // OptionIANA 0, 18, // length 1, 2, 3, 4, // IA ID 0, 0, 0x30, 0x39, // T1 = 12345 diff --git a/dhcpv6/option_prefixdelegation.go b/dhcpv6/option_prefixdelegation.go index 2a211c0..ecc7990 100644 --- a/dhcpv6/option_prefixdelegation.go +++ b/dhcpv6/option_prefixdelegation.go @@ -16,12 +16,12 @@ type OptIAForPrefixDelegation struct { } func (op *OptIAForPrefixDelegation) Code() OptionCode { - return OPTION_IA_PD + return OptionIAPD } func (op *OptIAForPrefixDelegation) ToBytes() []byte { buf := make([]byte, 16) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_IA_PD)) + 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) diff --git a/dhcpv6/option_relaymsg.go b/dhcpv6/option_relaymsg.go index 98b9e84..89621ec 100644 --- a/dhcpv6/option_relaymsg.go +++ b/dhcpv6/option_relaymsg.go @@ -13,12 +13,12 @@ type OptRelayMsg struct { } func (op *OptRelayMsg) Code() OptionCode { - return OPTION_RELAY_MSG + return OptionRelayMsg } func (op *OptRelayMsg) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_RELAY_MSG)) + 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 diff --git a/dhcpv6/option_relaymsg_test.go b/dhcpv6/option_relaymsg_test.go index 6376813..df2442c 100644 --- a/dhcpv6/option_relaymsg_test.go +++ b/dhcpv6/option_relaymsg_test.go @@ -16,9 +16,9 @@ func TestRelayMsgParseOptRelayMsg(t *testing.T) { if err != nil { t.Fatal(err) } - if code := opt.Code(); code != OPTION_RELAY_MSG { - t.Fatalf("Invalid option code. Expected OPTION_RELAY_MSG (%v), got %v", - OPTION_RELAY_MSG, code, + if code := opt.Code(); code != OptionRelayMsg { + t.Fatalf("Invalid option code. Expected OptionRelayMsg (%v), got %v", + OptionRelayMsg, code, ) } } @@ -40,9 +40,9 @@ func TestRelayMsgOptionsFromBytes(t *testing.T) { t.Fatalf("Invalid number of options. Expected 1, got %v", len(opts)) } opt := opts[0] - if code := opt.Code(); code != OPTION_RELAY_MSG { - t.Fatalf("Invalid option code. Expected OPTION_RELAY_MSG (%v), got %v", - OPTION_RELAY_MSG, code, + if code := opt.Code(); code != OptionRelayMsg { + t.Fatalf("Invalid option code. Expected OptionRelayMsg (%v), got %v", + OptionRelayMsg, code, ) } } @@ -76,9 +76,9 @@ func TestRelayMsgParseOptRelayMsgSingleEncapsulation(t *testing.T) { if len(r.options) != 1 { t.Fatalf("Invalid number of options. Expected 1, got %v", len(r.options)) } - if code := r.options[0].Code(); code != OPTION_RELAY_MSG { - t.Fatalf("Invalid option code. Expected OPTION_RELAY_MSG (%v), got %v", - OPTION_RELAY_MSG, code, + if code := r.options[0].Code(); code != OptionRelayMsg { + t.Fatalf("Invalid option code. Expected OptionRelayMsg (%v), got %v", + OptionRelayMsg, code, ) } opt := r.options[0] diff --git a/dhcpv6/option_remoteid.go b/dhcpv6/option_remoteid.go index 210363d..9d249a7 100644 --- a/dhcpv6/option_remoteid.go +++ b/dhcpv6/option_remoteid.go @@ -14,12 +14,12 @@ type OptRemoteId struct { } func (op *OptRemoteId) Code() OptionCode { - return OPTION_REMOTE_ID + return OptionRemoteID } func (op *OptRemoteId) ToBytes() []byte { buf := make([]byte, 8) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_REMOTE_ID)) + 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...) diff --git a/dhcpv6/option_requestedoption.go b/dhcpv6/option_requestedoption.go index f6327f9..a55c533 100644 --- a/dhcpv6/option_requestedoption.go +++ b/dhcpv6/option_requestedoption.go @@ -13,13 +13,13 @@ type OptRequestedOption struct { } func (op *OptRequestedOption) Code() OptionCode { - return OPTION_ORO + return OptionORO } func (op *OptRequestedOption) ToBytes() []byte { buf := make([]byte, 4) roBytes := make([]byte, 2) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_ORO)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionORO)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) for _, ro := range op.requestedOptions { binary.BigEndian.PutUint16(roBytes, uint16(ro)) diff --git a/dhcpv6/option_serverid.go b/dhcpv6/option_serverid.go index ffc1b69..dbc17f5 100644 --- a/dhcpv6/option_serverid.go +++ b/dhcpv6/option_serverid.go @@ -14,12 +14,12 @@ type OptServerId struct { } func (op *OptServerId) Code() OptionCode { - return OPTION_SERVERID + return OptionServerID } func (op *OptServerId) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_SERVERID)) + 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 diff --git a/dhcpv6/option_serverid_test.go b/dhcpv6/option_serverid_test.go index f420b78..e330fa8 100644 --- a/dhcpv6/option_serverid_test.go +++ b/dhcpv6/option_serverid_test.go @@ -30,7 +30,7 @@ func TestOptServerIdToBytes(t *testing.T) { }, } expected := []byte{ - 0, 2, // OPTION_SERVERID + 0, 2, // OptionServerID 0, 10, // length 0, 3, // DUID_LL 0, 1, // hwtype ethernet @@ -46,7 +46,7 @@ func TestOptServerIdDecodeEncode(t *testing.T) { 5, 4, 3, 2, 1, 0, // hw addr } expected := append([]byte{ - 0, 2, // OPTION_SERVERID + 0, 2, // OptionServerID 0, 10, // length }, data...) opt, err := ParseOptServerId(data) @@ -63,5 +63,5 @@ func TestOptionServerId(t *testing.T) { }, } require.Equal(t, opt.Length(), 10) - require.Equal(t, opt.Code(), OPTION_SERVERID) + require.Equal(t, opt.Code(), OptionServerID) } diff --git a/dhcpv6/option_statuscode.go b/dhcpv6/option_statuscode.go index e6bd8ae..11f1cbf 100644 --- a/dhcpv6/option_statuscode.go +++ b/dhcpv6/option_statuscode.go @@ -18,13 +18,13 @@ type OptStatusCode struct { // Code returns the option code func (op *OptStatusCode) Code() OptionCode { - return OPTION_STATUS_CODE + return OptionStatusCode } // 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(OPTION_STATUS_CODE)) + 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...) diff --git a/dhcpv6/option_statuscode_test.go b/dhcpv6/option_statuscode_test.go index 95c785e..6d2ba94 100644 --- a/dhcpv6/option_statuscode_test.go +++ b/dhcpv6/option_statuscode_test.go @@ -20,7 +20,7 @@ func TestParseOptStatusCode(t *testing.T) { func TestOptStatusCodeToBytes(t *testing.T) { expected := []byte{ - 0, 13, // OPTION_STATUS_CODE + 0, 13, // OptionStatusCode 0, 9, // length 0, 0, // StatusSuccess 's', 'u', 'c', 'c', 'e', 's', 's', diff --git a/dhcpv6/option_types.go b/dhcpv6/option_types.go index 946dcd0..6a852ad 100644 --- a/dhcpv6/option_types.go +++ b/dhcpv6/option_types.go @@ -1,152 +1,153 @@ package dhcpv6 -// FIXME: rename all the options to have a consistent name, e.g. OPT_ +// All DHCPv6 options. const ( - OPTION_CLIENTID OptionCode = 1 - OPTION_SERVERID OptionCode = 2 - OPTION_IA_NA OptionCode = 3 - OPTION_IA_TA OptionCode = 4 - OPTION_IAADDR OptionCode = 5 - OPTION_ORO OptionCode = 6 - OPTION_PREFERENCE OptionCode = 7 - OPTION_ELAPSED_TIME OptionCode = 8 - OPTION_RELAY_MSG OptionCode = 9 + OptionClientID OptionCode = 1 + OptionServerID OptionCode = 2 + OptionIANA OptionCode = 3 + OptionIATA OptionCode = 4 + OptionIAAddr OptionCode = 5 + OptionORO OptionCode = 6 + OptionPreference OptionCode = 7 + OptionElapsedTime OptionCode = 8 + OptionRelayMsg OptionCode = 9 // skip 10 - OPTION_AUTH OptionCode = 11 - OPTION_UNICAST OptionCode = 12 - OPTION_STATUS_CODE OptionCode = 13 - OPTION_RAPID_COMMIT OptionCode = 14 - OPTION_USER_CLASS OptionCode = 15 - OPTION_VENDOR_CLASS OptionCode = 16 - OPTION_VENDOR_OPTS OptionCode = 17 - OPTION_INTERFACE_ID OptionCode = 18 - OPTION_RECONF_MSG OptionCode = 19 - OPTION_RECONF_ACCEPT OptionCode = 20 - SIP_SERVERS_DOMAIN_NAME_LIST OptionCode = 21 - SIP_SERVERS_IPV6_ADDRESS_LIST OptionCode = 22 - DNS_RECURSIVE_NAME_SERVER OptionCode = 23 - DOMAIN_SEARCH_LIST OptionCode = 24 - OPTION_IA_PD OptionCode = 25 - OPTION_IAPREFIX OptionCode = 26 - OPTION_NIS_SERVERS OptionCode = 27 - OPTION_NISP_SERVERS OptionCode = 28 - OPTION_NIS_DOMAIN_NAME OptionCode = 29 - OPTION_NISP_DOMAIN_NAME OptionCode = 30 - SNTP_SERVER_LIST OptionCode = 31 - INFORMATION_REFRESH_TIME OptionCode = 32 - BCMCS_CONTROLLER_DOMAIN_NAME_LIST OptionCode = 33 - BCMCS_CONTROLLER_IPV6_ADDRESS_LIST OptionCode = 34 + OptionAuth OptionCode = 11 + OptionUnicast OptionCode = 12 + OptionStatusCode OptionCode = 13 + OptionRapidCommit OptionCode = 14 + OptionUserClass OptionCode = 15 + OptionVendorClass OptionCode = 16 + OptionVendorOpts OptionCode = 17 + OptionInterfaceID OptionCode = 18 + OptionReconfMessage OptionCode = 19 + OptionReconfAccept OptionCode = 20 + OptionSIPServersDomainNameList OptionCode = 21 + OptionSIPServersIPv6AddressList OptionCode = 22 + OptionDNSRecursiveNameServer OptionCode = 23 + OptionDomainSearchList OptionCode = 24 + OptionIAPD OptionCode = 25 + OptionIAPrefix OptionCode = 26 + OptionNISServers OptionCode = 27 + OptionNISPServers OptionCode = 28 + OptionNISDomainName OptionCode = 29 + OptionNISPDomainName OptionCode = 30 + OptionSNTPServerList OptionCode = 31 + OptionInformationRefreshTime OptionCode = 32 + OptionBCMCSControllerDomainNameList OptionCode = 33 + OptionBCMCSControllerIPv6AddressList OptionCode = 34 // skip 35 - OPTION_GEOCONF_CIVIC OptionCode = 36 - OPTION_REMOTE_ID OptionCode = 37 - RELAY_AGENT_SUBSCRIBER_ID OptionCode = 38 - FQDN OptionCode = 39 - PANA_AUTHENTICATION_AGENT OptionCode = 40 - OPTION_NEW_POSIX_TIMEZONE OptionCode = 41 - OPTION_NEW_TZDB_TIMEZONE OptionCode = 42 - ECHO_REQUEST OptionCode = 43 - OPTION_LQ_QUERY OptionCode = 44 - OPTION_CLIENT_DATA OptionCode = 45 - OPTION_CLT_TIME OptionCode = 46 - OPTION_LQ_RELAY_DATA OptionCode = 47 - OPTION_LQ_CLIENT_LINK OptionCode = 48 - MIPV6_HOME_NETWORK_ID_FQDN OptionCode = 49 - MIPV6_VISITED_HOME_NETWORK_INFORMATION OptionCode = 50 - LOST_SERVER OptionCode = 51 - CAPWAP_ACCESS_CONTROLLER_ADDRESSES OptionCode = 52 - RELAY_ID OptionCode = 53 - OPTION_IPV6_ADDRESS_MOS OptionCode = 54 - OPTION_IPV6_FQDN_MOS OptionCode = 55 - OPTION_NTP_SERVER OptionCode = 56 - OPTION_V6_ACCESS_DOMAIN OptionCode = 57 - OPTION_SIP_UA_CS_LIST OptionCode = 58 - OPT_BOOTFILE_URL OptionCode = 59 - OPT_BOOTFILE_PARAM OptionCode = 60 - OPTION_CLIENT_ARCH_TYPE OptionCode = 61 - OPTION_NII OptionCode = 62 - OPTION_GEOLOCATION OptionCode = 63 - OPTION_AFTR_NAME OptionCode = 64 - OPTION_ERP_LOCAL_DOMAIN_NAME OptionCode = 65 - OPTION_RSOO OptionCode = 66 - OPTION_PD_EXCLUDE OptionCode = 67 - VIRTUAL_SUBNET_SELECTION OptionCode = 68 - MIPV6_IDENTIFIED_HOME_NETWORK_INFORMATION OptionCode = 69 - MIPV6_UNRESTRICTED_HOME_NETWORK_INFORMATION OptionCode = 70 - MIPV6_HOME_NETWORK_PREFIX OptionCode = 71 - MIPV6_HOME_AGENT_ADDRESS OptionCode = 72 - MIPV6_HOME_AGENT_FQDN OptionCode = 73 + OptionGeoConfCivic OptionCode = 36 + OptionRemoteID OptionCode = 37 + OptionRelayAgentSubscriberID OptionCode = 38 + OptionFQDN OptionCode = 39 + OptionPANAAuthenticationAgent OptionCode = 40 + OptionNewPOSIXTimezone OptionCode = 41 + OptionNewTZDBTimezone OptionCode = 42 + OptionEchoRequest OptionCode = 43 + OptionLQQuery OptionCode = 44 + OptionClientData OptionCode = 45 + OptionCLTTime OptionCode = 46 + OptionLQRelayData OptionCode = 47 + OptionLQClientLink OptionCode = 48 + OptionMIPv6HomeNetworkIDFQDN OptionCode = 49 + OptionMIPv6VisitedHomeNetworkInformation OptionCode = 50 + OptionLoSTServer OptionCode = 51 + OptionCAPWAPAccessControllerAddresses OptionCode = 52 + OptionRelayID OptionCode = 53 + OptionIPv6AddressMOS OptionCode = 54 + OptionIPv6FQDNMOS OptionCode = 55 + OptionNTPServer OptionCode = 56 + OptionV6AccessDomain OptionCode = 57 + OptionSIPUACSList OptionCode = 58 + OptionBootfileURL OptionCode = 59 + OptionBootfileParam OptionCode = 60 + OptionClientArchType OptionCode = 61 + OptionNII OptionCode = 62 + OptionGeolocation OptionCode = 63 + OptionAFTRName OptionCode = 64 + OptionERPLocalDomainName OptionCode = 65 + OptionRSOO OptionCode = 66 + OptionPDExclude OptionCode = 67 + OptionVirtualSubnetSelection OptionCode = 68 + OptionMIPv6IdentifiedHomeNetworkInformation OptionCode = 69 + OptionMIPv6UnrestrictedHomeNetworkInformation OptionCode = 70 + OptionMIPv6HomeNetworkPrefix OptionCode = 71 + OptionMIPv6HomeAgentAddress OptionCode = 72 + OptionMIPv6HomeAgentFQDN OptionCode = 73 ) +// OptionCodeToString maps DHCPv6 OptionCodes to human-readable strings. var OptionCodeToString = map[OptionCode]string{ - OPTION_CLIENTID: "OPTION_CLIENTID", - OPTION_SERVERID: "OPTION_SERVERID", - OPTION_IA_NA: "OPTION_IA_NA", - OPTION_IA_TA: "OPTION_IA_TA", - OPTION_IAADDR: "OPTION_IAADDR", - OPTION_ORO: "OPTION_ORO", - OPTION_PREFERENCE: "OPTION_PREFERENCE", - OPTION_ELAPSED_TIME: "OPTION_ELAPSED_TIME", - OPTION_RELAY_MSG: "OPTION_RELAY_MSG", - OPTION_AUTH: "OPTION_AUTH", - OPTION_UNICAST: "OPTION_UNICAST", - OPTION_STATUS_CODE: "OPTION_STATUS_CODE", - OPTION_RAPID_COMMIT: "OPTION_RAPID_COMMIT", - OPTION_USER_CLASS: "OPTION_USER_CLASS", - OPTION_VENDOR_CLASS: "OPTION_VENDOR_CLASS", - OPTION_VENDOR_OPTS: "OPTION_VENDOR_OPTS", - OPTION_INTERFACE_ID: "OPTION_INTERFACE_ID", - OPTION_RECONF_MSG: "OPTION_RECONF_MSG", - OPTION_RECONF_ACCEPT: "OPTION_RECONF_ACCEPT", - SIP_SERVERS_DOMAIN_NAME_LIST: "SIP Servers Domain Name List", - SIP_SERVERS_IPV6_ADDRESS_LIST: "SIP Servers IPv6 Address List", - DNS_RECURSIVE_NAME_SERVER: "DNS Recursive Name Server", - DOMAIN_SEARCH_LIST: "Domain Search List", - OPTION_IA_PD: "OPTION_IA_PD", - OPTION_IAPREFIX: "OPTION_IAPREFIX", - OPTION_NIS_SERVERS: "OPTION_NIS_SERVERS", - OPTION_NISP_SERVERS: "OPTION_NISP_SERVERS", - OPTION_NIS_DOMAIN_NAME: "OPTION_NIS_DOMAIN_NAME", - OPTION_NISP_DOMAIN_NAME: "OPTION_NISP_DOMAIN_NAME", - SNTP_SERVER_LIST: "SNTP Server List", - INFORMATION_REFRESH_TIME: "Information Refresh Time", - BCMCS_CONTROLLER_DOMAIN_NAME_LIST: "BCMCS Controller Domain Name List", - BCMCS_CONTROLLER_IPV6_ADDRESS_LIST: "BCMCS Controller IPv6 Address List", - OPTION_GEOCONF_CIVIC: "OPTION_GEOCONF", - OPTION_REMOTE_ID: "OPTION_REMOTE_ID", - RELAY_AGENT_SUBSCRIBER_ID: "Relay-Agent Subscriber ID", - FQDN: "FQDN", - PANA_AUTHENTICATION_AGENT: "PANA Authentication Agent", - OPTION_NEW_POSIX_TIMEZONE: "OPTION_NEW_POSIX_TIME_ZONE", - OPTION_NEW_TZDB_TIMEZONE: "OPTION_NEW_TZDB_TIMEZONE", - ECHO_REQUEST: "Echo Request", - OPTION_LQ_QUERY: "OPTION_LQ_QUERY", - OPTION_CLIENT_DATA: "OPTION_CLIENT_DATA", - OPTION_CLT_TIME: "OPTION_CLT_TIME", - OPTION_LQ_RELAY_DATA: "OPTION_LQ_RELAY_DATA", - OPTION_LQ_CLIENT_LINK: "OPTION_LQ_CLIENT_LINK", - MIPV6_HOME_NETWORK_ID_FQDN: "MIPv6 Home Network ID FQDN", - MIPV6_VISITED_HOME_NETWORK_INFORMATION: "MIPv6 Visited Home Network Information", - LOST_SERVER: "LoST Server", - CAPWAP_ACCESS_CONTROLLER_ADDRESSES: "CAPWAP Access Controller Addresses", - RELAY_ID: "RELAY_ID", - OPTION_IPV6_ADDRESS_MOS: "OPTION-IPv6_Address-MoS", - OPTION_IPV6_FQDN_MOS: "OPTION-IPv6-FQDN-MoS", - OPTION_NTP_SERVER: "OPTION_NTP_SERVER", - OPTION_V6_ACCESS_DOMAIN: "OPTION_V6_ACCESS_DOMAIN", - OPTION_SIP_UA_CS_LIST: "OPTION_SIP_UA_CS_LIST", - OPT_BOOTFILE_URL: "OPT_BOOTFILE_URL", - OPT_BOOTFILE_PARAM: "OPT_BOOTFILE_PARAM", - OPTION_CLIENT_ARCH_TYPE: "OPTION_CLIENT_ARCH_TYPE", - OPTION_NII: "OPTION_NII", - OPTION_GEOLOCATION: "OPTION_GEOLOCATION", - OPTION_AFTR_NAME: "OPTION_AFTR_NAME", - OPTION_ERP_LOCAL_DOMAIN_NAME: "OPTION_ERP_LOCAL_DOMAIN_NAME", - OPTION_RSOO: "OPTION_RSOO", - OPTION_PD_EXCLUDE: "OPTION_PD_EXCLUDE", - VIRTUAL_SUBNET_SELECTION: "Virtual Subnet Selection", - MIPV6_IDENTIFIED_HOME_NETWORK_INFORMATION: "MIPv6 Identified Home Network Information", - MIPV6_UNRESTRICTED_HOME_NETWORK_INFORMATION: "MIPv6 Unrestricted Home Network Information", - MIPV6_HOME_NETWORK_PREFIX: "MIPv6 Home Network Prefix", - MIPV6_HOME_AGENT_ADDRESS: "MIPv6 Home Agent Address", - MIPV6_HOME_AGENT_FQDN: "MIPv6 Home Agent FQDN", + OptionClientID: "OPTION_CLIENTID", + OptionServerID: "OPTION_SERVERID", + OptionIANA: "OPTION_IA_NA", + OptionIATA: "OPTION_IA_TA", + OptionIAAddr: "OPTION_IAADDR", + OptionORO: "OPTION_ORO", + OptionPreference: "OPTION_PREFERENCE", + OptionElapsedTime: "OPTION_ELAPSED_TIME", + OptionRelayMsg: "OPTION_RELAY_MSG", + OptionAuth: "OPTION_AUTH", + OptionUnicast: "OPTION_UNICAST", + OptionStatusCode: "OPTION_STATUS_CODE", + OptionRapidCommit: "OPTION_RAPID_COMMIT", + OptionUserClass: "OPTION_USER_CLASS", + OptionVendorClass: "OPTION_VENDOR_CLASS", + OptionVendorOpts: "OPTION_VENDOR_OPTS", + OptionInterfaceID: "OPTION_INTERFACE_ID", + OptionReconfMessage: "OPTION_RECONF_MSG", + OptionReconfAccept: "OPTION_RECONF_ACCEPT", + OptionSIPServersDomainNameList: "SIP Servers Domain Name List", + OptionSIPServersIPv6AddressList: "SIP Servers IPv6 Address List", + OptionDNSRecursiveNameServer: "DNS Recursive Name Server", + OptionDomainSearchList: "Domain Search List", + OptionIAPD: "OPTION_IA_PD", + OptionIAPrefix: "OPTION_IAPREFIX", + OptionNISServers: "OPTION_NIS_SERVERS", + OptionNISPServers: "OPTION_NISP_SERVERS", + OptionNISDomainName: "OPTION_NIS_DOMAIN_NAME", + OptionNISPDomainName: "OPTION_NISP_DOMAIN_NAME", + OptionSNTPServerList: "SNTP Server List", + OptionInformationRefreshTime: "Information Refresh Time", + OptionBCMCSControllerDomainNameList: "BCMCS Controller Domain Name List", + OptionBCMCSControllerIPv6AddressList: "BCMCS Controller IPv6 Address List", + OptionGeoConfCivic: "OPTION_GEOCONF", + OptionRemoteID: "OPTION_REMOTE_ID", + OptionRelayAgentSubscriberID: "Relay-Agent Subscriber ID", + OptionFQDN: "FQDN", + OptionPANAAuthenticationAgent: "PANA Authentication Agent", + OptionNewPOSIXTimezone: "OPTION_NEW_POSIX_TIME_ZONE", + OptionNewTZDBTimezone: "OPTION_NEW_TZDB_TIMEZONE", + OptionEchoRequest: "Echo Request", + OptionLQQuery: "OPTION_LQ_QUERY", + OptionClientData: "OPTION_CLIENT_DATA", + OptionCLTTime: "OPTION_CLT_TIME", + OptionLQRelayData: "OPTION_LQ_RELAY_DATA", + OptionLQClientLink: "OPTION_LQ_CLIENT_LINK", + OptionMIPv6HomeNetworkIDFQDN: "MIPv6 Home Network ID FQDN", + OptionMIPv6VisitedHomeNetworkInformation: "MIPv6 Visited Home Network Information", + OptionLoSTServer: "LoST Server", + OptionCAPWAPAccessControllerAddresses: "CAPWAP Access Controller Addresses", + OptionRelayID: "RELAY_ID", + OptionIPv6AddressMOS: "OPTION-IPv6_Address-MoS", + OptionIPv6FQDNMOS: "OPTION-IPv6-FQDN-MoS", + OptionNTPServer: "OPTION_NTP_SERVER", + OptionV6AccessDomain: "OPTION_V6_ACCESS_DOMAIN", + OptionSIPUACSList: "OPTION_SIP_UA_CS_LIST", + OptionBootfileURL: "OPT_BOOTFILE_URL", + OptionBootfileParam: "OPT_BOOTFILE_PARAM", + OptionClientArchType: "OPTION_CLIENT_ARCH_TYPE", + OptionNII: "OPTION_NII", + OptionGeolocation: "OPTION_GEOLOCATION", + OptionAFTRName: "OPTION_AFTR_NAME", + OptionERPLocalDomainName: "OPTION_ERP_LOCAL_DOMAIN_NAME", + OptionRSOO: "OPTION_RSOO", + OptionPDExclude: "OPTION_PD_EXCLUDE", + OptionVirtualSubnetSelection: "Virtual Subnet Selection", + OptionMIPv6IdentifiedHomeNetworkInformation: "MIPv6 Identified Home Network Information", + OptionMIPv6UnrestrictedHomeNetworkInformation: "MIPv6 Unrestricted Home Network Information", + OptionMIPv6HomeNetworkPrefix: "MIPv6 Home Network Prefix", + OptionMIPv6HomeAgentAddress: "MIPv6 Home Agent Address", + OptionMIPv6HomeAgentFQDN: "MIPv6 Home Agent FQDN", } diff --git a/dhcpv6/option_userclass.go b/dhcpv6/option_userclass.go index c5ac4ca..652c216 100644 --- a/dhcpv6/option_userclass.go +++ b/dhcpv6/option_userclass.go @@ -17,13 +17,13 @@ type OptUserClass struct { // Code returns the option code func (op *OptUserClass) Code() OptionCode { - return OPTION_USER_CLASS + return OptionUserClass } // 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(OPTION_USER_CLASS)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionUserClass)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) u16 := make([]byte, 2) for _, uc := range op.UserClasses { diff --git a/dhcpv6/option_userclass_test.go b/dhcpv6/option_userclass_test.go index 92c3848..3ba65cf 100644 --- a/dhcpv6/option_userclass_test.go +++ b/dhcpv6/option_userclass_test.go @@ -40,7 +40,7 @@ func TestOptUserClassToBytes(t *testing.T) { } data := opt.ToBytes() expected := []byte{ - 0, 15, // OPTION_USER_CLASS + 0, 15, // OptionUserClass 0, 11, // length 0, 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', } @@ -56,7 +56,7 @@ func TestOptUserClassToBytesMultiple(t *testing.T) { } data := opt.ToBytes() expected := []byte{ - 0, 15, // OPTION_USER_CLASS + 0, 15, // OptionUserClass 0, 17, // length 0, 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', 0, 4, 't', 'e', 's', 't', diff --git a/dhcpv6/options.go b/dhcpv6/options.go index d64d1d9..0a0bdc1 100644 --- a/dhcpv6/options.go +++ b/dhcpv6/options.go @@ -68,41 +68,41 @@ func ParseOption(dataStart []byte) (Option, error) { ) optData := dataStart[4 : 4+length] switch code { - case OPTION_CLIENTID: + case OptionClientID: opt, err = ParseOptClientId(optData) - case OPTION_SERVERID: + case OptionServerID: opt, err = ParseOptServerId(optData) - case OPTION_ELAPSED_TIME: + case OptionElapsedTime: opt, err = ParseOptElapsedTime(optData) - case OPTION_ORO: + case OptionORO: opt, err = ParseOptRequestedOption(optData) - case DNS_RECURSIVE_NAME_SERVER: + case OptionDNSRecursiveNameServer: opt, err = ParseOptDNSRecursiveNameServer(optData) - case DOMAIN_SEARCH_LIST: + case OptionDomainSearchList: opt, err = ParseOptDomainSearchList(optData) - case OPTION_IA_NA: + case OptionIANA: opt, err = ParseOptIANA(optData) - case OPTION_IA_PD: + case OptionIAPD: opt, err = ParseOptIAForPrefixDelegation(optData) - case OPTION_IAADDR: + case OptionIAAddr: opt, err = ParseOptIAAddress(optData) - case OPTION_IAPREFIX: + case OptionIAPrefix: opt, err = ParseOptIAPrefix(optData) - case OPTION_STATUS_CODE: + case OptionStatusCode: opt, err = ParseOptStatusCode(optData) - case OPTION_RELAY_MSG: + case OptionRelayMsg: opt, err = ParseOptRelayMsg(optData) - case OPTION_REMOTE_ID: + case OptionRemoteID: opt, err = ParseOptRemoteId(optData) - case OPTION_INTERFACE_ID: + case OptionInterfaceID: opt, err = ParseOptInterfaceId(optData) - case OPTION_CLIENT_ARCH_TYPE: + case OptionClientArchType: opt, err = ParseOptClientArchType(optData) - case OPTION_NII: + case OptionNII: opt, err = ParseOptNetworkInterfaceId(optData) - case OPT_BOOTFILE_URL: + case OptionBootfileURL: opt, err = ParseOptBootFileURL(optData) - case OPTION_USER_CLASS: + case OptionUserClass: opt, err = ParseOptUserClass(optData) default: opt = &OptionGeneric{OptionCode: code, OptionData: optData} diff --git a/dhcpv6/utils.go b/dhcpv6/utils.go index b1c0b93..1681661 100644 --- a/dhcpv6/utils.go +++ b/dhcpv6/utils.go @@ -11,14 +11,14 @@ import ( // if the "boot file" option is included in the packet, which is useful for // ADVERTISE/REPLY packet. func IsNetboot(msg DHCPv6) bool { - for _, optoro := range msg.GetOption(OPTION_ORO) { + for _, optoro := range msg.GetOption(OptionORO) { for _, o := range optoro.(*OptRequestedOption).RequestedOptions() { - if o == OPT_BOOTFILE_URL { + if o == OptionBootfileURL { return true } } } - if optbf := msg.GetOneOption(OPT_BOOTFILE_URL); optbf != nil { + if optbf := msg.GetOneOption(OptionBootfileURL); optbf != nil { return true } return false @@ -42,14 +42,14 @@ func IsUsingUEFI(msg DHCPv6) bool { // 7 EFI BC // 8 EFI Xscale // 9 EFI x86-64 - if opt := msg.GetOneOption(OPTION_CLIENT_ARCH_TYPE); opt != nil { + if opt := msg.GetOneOption(OptionClientArchType); opt != nil { optat := opt.(*OptClientArchType) // TODO investigate if other types are appropriate if optat.ArchType == EFI_BC || optat.ArchType == EFI_X86_64 { return true } } - if opt := msg.GetOneOption(OPTION_USER_CLASS); opt != nil { + if opt := msg.GetOneOption(OptionUserClass); opt != nil { optuc := opt.(*OptUserClass) for _, uc := range optuc.UserClasses { if strings.Contains(string(uc), "EFI") { diff --git a/dhcpv6/utils_test.go b/dhcpv6/utils_test.go index 36b1683..f3b53f0 100644 --- a/dhcpv6/utils_test.go +++ b/dhcpv6/utils_test.go @@ -12,7 +12,7 @@ func TestIsNetboot(t *testing.T) { msg2 := DHCPv6Message{} optro := OptRequestedOption{} - optro.AddRequestedOption(OPT_BOOTFILE_URL) + optro.AddRequestedOption(OptionBootfileURL) msg2.AddOption(&optro) require.True(t, IsNetboot(&msg2)) diff --git a/netboot/netboot.go b/netboot/netboot.go index 643c5ec..81ba144 100644 --- a/netboot/netboot.go +++ b/netboot/netboot.go @@ -67,7 +67,7 @@ func ConversationToNetconf(conversation []dhcpv6.DHCPv6) (*NetConf, string, erro opt dhcpv6.Option bootfile string ) - opt = reply.GetOneOption(dhcpv6.OPT_BOOTFILE_URL) + opt = reply.GetOneOption(dhcpv6.OptionBootfileURL) if opt == nil { log.Printf("no bootfile URL option found in REPLY, looking for it in ADVERTISE") // as a fallback, look for bootfile URL in the advertise @@ -82,7 +82,7 @@ func ConversationToNetconf(conversation []dhcpv6.DHCPv6) (*NetConf, string, erro if advertise == nil { return nil, "", errors.New("no ADVERTISE found") } - opt = advertise.GetOneOption(dhcpv6.OPT_BOOTFILE_URL) + opt = advertise.GetOneOption(dhcpv6.OptionBootfileURL) if opt == nil { return nil, "", errors.New("no bootfile URL option found in ADVERTISE") } diff --git a/netboot/netconf.go b/netboot/netconf.go index f7a117c..84fb263 100644 --- a/netboot/netconf.go +++ b/netboot/netconf.go @@ -30,7 +30,7 @@ type NetConf struct { // GetNetConfFromPacketv6 extracts network configuration information from a DHCPv6 // Reply packet and returns a populated NetConf structure func GetNetConfFromPacketv6(d *dhcpv6.DHCPv6Message) (*NetConf, error) { - opt := d.GetOneOption(dhcpv6.OPTION_IA_NA) + opt := d.GetOneOption(dhcpv6.OptionIANA) if opt == nil { return nil, errors.New("No option IA NA found") } @@ -39,7 +39,7 @@ func GetNetConfFromPacketv6(d *dhcpv6.DHCPv6Message) (*NetConf, error) { oiana := opt.(*dhcpv6.OptIANA) iaaddrs := make([]*dhcpv6.OptIAAddress, 0) for _, o := range oiana.Options { - if o.Code() == dhcpv6.OPTION_IAADDR { + if o.Code() == dhcpv6.OptionIAAddr { iaaddrs = append(iaaddrs, o.(*dhcpv6.OptIAAddress)) } } @@ -55,7 +55,7 @@ func GetNetConfFromPacketv6(d *dhcpv6.DHCPv6Message) (*NetConf, error) { }) } // get DNS configuration - opt = d.GetOneOption(dhcpv6.DNS_RECURSIVE_NAME_SERVER) + opt = d.GetOneOption(dhcpv6.OptionDNSRecursiveNameServer) if opt == nil { return nil, errors.New("No option DNS Recursive Name Servers found ") } @@ -63,7 +63,7 @@ func GetNetConfFromPacketv6(d *dhcpv6.DHCPv6Message) (*NetConf, error) { // TODO should this be copied? netconf.DNSServers = odnsserv.NameServers - opt = d.GetOneOption(dhcpv6.DOMAIN_SEARCH_LIST) + opt = d.GetOneOption(dhcpv6.OptionDomainSearchList) if opt == nil { return nil, errors.New("No option DNS Domain Search List found") } -- cgit v1.2.3 From 4100f801b9bb7036e0b201c2bf42fa6030119cc5 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Tue, 31 Jul 2018 11:02:38 +0200 Subject: add OptDomainSearch --- dhcpv4/option_domain_search.go | 55 +++++++++++++++++++++++++++++++ dhcpv4/option_domain_search_test.go | 37 +++++++++++++++++++++ dhcpv4/option_rfc1035label.go | 53 +++++++++++++++++++++++++++++ dhcpv4/option_rfc1035label_test.go | 66 +++++++++++++++++++++++++++++++++++++ 4 files changed, 211 insertions(+) create mode 100644 dhcpv4/option_domain_search.go create mode 100644 dhcpv4/option_domain_search_test.go create mode 100644 dhcpv4/option_rfc1035label.go create mode 100644 dhcpv4/option_rfc1035label_test.go diff --git a/dhcpv4/option_domain_search.go b/dhcpv4/option_domain_search.go new file mode 100644 index 0000000..c30cbe1 --- /dev/null +++ b/dhcpv4/option_domain_search.go @@ -0,0 +1,55 @@ +package dhcpv4 + +// This module defines the OptDomainSearch structure. +// https://tools.ietf.org/html/rfc3397 + +import ( + "fmt" +) + +// OptDomainSearch represents an option encapsulating a domain search list. +type OptDomainSearch struct { + DomainSearch []string +} + +func (op *OptDomainSearch) Code() OptionCode { + return OptionDNSDomainSearchList +} + +func (op *OptDomainSearch) ToBytes() []byte { + buf := []byte{byte(op.Code()), byte(op.Length())} + buf = append(buf, LabelsToBytes(op.DomainSearch)...) + return buf +} + +func (op *OptDomainSearch) Length() int { + var length int + for _, label := range op.DomainSearch { + length += len(label) + 2 // add the first and the last length bytes + } + return length +} + +func (op *OptDomainSearch) String() string { + return fmt.Sprintf("DNS Domain Search List ->", op.DomainSearch) +} + +// build an OptDomainSearch structure from a sequence of bytes. +func ParseOptDomainSearch(data []byte) (*OptDomainSearch, error) { + if len(data) < 2 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionDNSDomainSearchList { + return nil, fmt.Errorf("expected code %v, got %v", OptionDNSDomainSearchList, code) + } + length := int(data[1]) + if len(data) < 2+length { + return nil, ErrShortByteStream + } + domainSearch, err := LabelsFromBytes(data[2:length+2]) + if err != nil { + return nil, err + } + return &OptDomainSearch{DomainSearch: domainSearch}, nil +} diff --git a/dhcpv4/option_domain_search_test.go b/dhcpv4/option_domain_search_test.go new file mode 100644 index 0000000..4848a83 --- /dev/null +++ b/dhcpv4/option_domain_search_test.go @@ -0,0 +1,37 @@ +package dhcpv4 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseOptDomainSearch(t *testing.T) { + data := []byte{ + 119, // OptionDNSDomainSearchList + 33, // length + 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, + 6, 's', 'u', 'b', 'n', 'e', 't', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'o', 'r', 'g', 0, + } + opt, err := ParseOptDomainSearch(data) + require.NoError(t, err) + require.Equal(t, len(opt.DomainSearch), 2) + require.Equal(t, opt.DomainSearch[0], "example.com") + require.Equal(t, opt.DomainSearch[1], "subnet.example.org") +} + +func TestOptDomainSearchToBytes(t *testing.T) { + expected := []byte{ + 119, // OptionDNSDomainSearchList + 33, // length + 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, + 6, 's', 'u', 'b', 'n', 'e', 't', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'o', 'r', 'g', 0, + } + opt := OptDomainSearch{ + DomainSearch: []string{ + "example.com", + "subnet.example.org", + }, + } + require.Equal(t, opt.ToBytes(), expected) +} diff --git a/dhcpv4/option_rfc1035label.go b/dhcpv4/option_rfc1035label.go new file mode 100644 index 0000000..b78d8da --- /dev/null +++ b/dhcpv4/option_rfc1035label.go @@ -0,0 +1,53 @@ +package dhcpv4 + +import ( + "fmt" + "strings" +) + +func LabelsFromBytes(buf []byte) ([]string, error) { + var ( + pos = 0 + domains = make([]string, 0) + label = "" + ) + for { + if pos >= len(buf) { + return domains, nil + } + length := int(buf[pos]) + pos++ + if length == 0 { + domains = append(domains, label) + label = "" + } + if len(buf)-pos < length { + return nil, fmt.Errorf("DomainNamesFromBytes: invalid short label length") + } + if label != "" { + label += "." + } + label += string(buf[pos : pos+length]) + pos += length + } +} + +func LabelToBytes(label string) []byte { + var encodedLabel []byte + if len(label) == 0 { + return []byte{0} + } + for _, part := range strings.Split(label, ".") { + encodedLabel = append(encodedLabel, byte(len(part))) + encodedLabel = append(encodedLabel, []byte(part)...) + } + return append(encodedLabel, 0) +} + +func LabelsToBytes(labels []string) []byte { + var encodedLabels []byte + for _, label := range labels { + encodedLabels = append(encodedLabels, LabelToBytes(label)...) + } + return encodedLabels +} diff --git a/dhcpv4/option_rfc1035label_test.go b/dhcpv4/option_rfc1035label_test.go new file mode 100644 index 0000000..30c87c8 --- /dev/null +++ b/dhcpv4/option_rfc1035label_test.go @@ -0,0 +1,66 @@ +package dhcpv4 + +import ( + "bytes" + "testing" +) + +func TestLabelsFromBytes(t *testing.T) { + labels, err := LabelsFromBytes([]byte{ + 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', + 0x2, 'i', 't', + 0x0, + }) + if err != nil { + t.Fatal(err) + } + if len(labels) != 1 { + t.Fatalf("Invalid labels length. Expected: 1, got: %v", len(labels)) + } + if labels[0] != "slackware.it" { + t.Fatalf("Invalid label. Expected: %v, got: %v'", "slackware.it", labels[0]) + } +} + +func TestLabelsFromBytesZeroLength(t *testing.T) { + labels, err := LabelsFromBytes([]byte{}) + if err != nil { + t.Fatal(err) + } + if len(labels) != 0 { + t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels)) + } +} + +func TestLabelsFromBytesInvalidLength(t *testing.T) { + labels, err := LabelsFromBytes([]byte{0x3, 0xaa, 0xbb}) // short length + if err == nil { + t.Fatal("Expected error, got nil") + } + if len(labels) != 0 { + t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels)) + } + if labels != nil { + t.Fatalf("Invalid label. Expected nil, got %v", labels) + } +} + +func TestLabelToBytes(t *testing.T) { + encodedLabel := LabelToBytes("slackware.it") + expected := []byte{ + 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', + 0x2, 'i', 't', + 0x0, + } + if !bytes.Equal(encodedLabel, expected) { + t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) + } +} + +func TestLabelToBytesZeroLength(t *testing.T) { + encodedLabel := LabelToBytes("") + expected := []byte{0} + if !bytes.Equal(encodedLabel, expected) { + t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) + } +} -- cgit v1.2.3 From 7452e8de6d21fbd1a6bbcaa1b63643f42f1f217c Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Tue, 31 Jul 2018 11:14:09 +0200 Subject: OptDomainSearch: add comments --- dhcpv4/option_domain_search.go | 12 +++++++++--- dhcpv4/option_rfc1035label.go | 8 ++++---- dhcpv4/option_rfc1035label_test.go | 10 +++++----- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/dhcpv4/option_domain_search.go b/dhcpv4/option_domain_search.go index c30cbe1..75626ed 100644 --- a/dhcpv4/option_domain_search.go +++ b/dhcpv4/option_domain_search.go @@ -12,16 +12,20 @@ type OptDomainSearch struct { DomainSearch []string } +// Code returns the option code. func (op *OptDomainSearch) Code() OptionCode { return OptionDNSDomainSearchList } +// ToBytes returns a serialized stream of bytes for this option. func (op *OptDomainSearch) ToBytes() []byte { buf := []byte{byte(op.Code()), byte(op.Length())} - buf = append(buf, LabelsToBytes(op.DomainSearch)...) + buf = append(buf, labelsToBytes(op.DomainSearch)...) return buf } +// Length returns the length of the data portion (excluding option code an byte +// length). func (op *OptDomainSearch) Length() int { var length int for _, label := range op.DomainSearch { @@ -30,11 +34,13 @@ func (op *OptDomainSearch) Length() int { return length } +// String returns a human-readable string. func (op *OptDomainSearch) String() string { return fmt.Sprintf("DNS Domain Search List ->", op.DomainSearch) } -// build an OptDomainSearch structure from a sequence of bytes. +// ParseOptDomainSearch returns a new OptDomainSearch from a byte stream, or +// error if any. func ParseOptDomainSearch(data []byte) (*OptDomainSearch, error) { if len(data) < 2 { return nil, ErrShortByteStream @@ -47,7 +53,7 @@ func ParseOptDomainSearch(data []byte) (*OptDomainSearch, error) { if len(data) < 2+length { return nil, ErrShortByteStream } - domainSearch, err := LabelsFromBytes(data[2:length+2]) + domainSearch, err := labelsFromBytes(data[2:length+2]) if err != nil { return nil, err } diff --git a/dhcpv4/option_rfc1035label.go b/dhcpv4/option_rfc1035label.go index b78d8da..d0972ed 100644 --- a/dhcpv4/option_rfc1035label.go +++ b/dhcpv4/option_rfc1035label.go @@ -5,7 +5,7 @@ import ( "strings" ) -func LabelsFromBytes(buf []byte) ([]string, error) { +func labelsFromBytes(buf []byte) ([]string, error) { var ( pos = 0 domains = make([]string, 0) @@ -32,7 +32,7 @@ func LabelsFromBytes(buf []byte) ([]string, error) { } } -func LabelToBytes(label string) []byte { +func labelToBytes(label string) []byte { var encodedLabel []byte if len(label) == 0 { return []byte{0} @@ -44,10 +44,10 @@ func LabelToBytes(label string) []byte { return append(encodedLabel, 0) } -func LabelsToBytes(labels []string) []byte { +func labelsToBytes(labels []string) []byte { var encodedLabels []byte for _, label := range labels { - encodedLabels = append(encodedLabels, LabelToBytes(label)...) + encodedLabels = append(encodedLabels, labelToBytes(label)...) } return encodedLabels } diff --git a/dhcpv4/option_rfc1035label_test.go b/dhcpv4/option_rfc1035label_test.go index 30c87c8..cd8189a 100644 --- a/dhcpv4/option_rfc1035label_test.go +++ b/dhcpv4/option_rfc1035label_test.go @@ -6,7 +6,7 @@ import ( ) func TestLabelsFromBytes(t *testing.T) { - labels, err := LabelsFromBytes([]byte{ + labels, err := labelsFromBytes([]byte{ 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', 0x2, 'i', 't', 0x0, @@ -23,7 +23,7 @@ func TestLabelsFromBytes(t *testing.T) { } func TestLabelsFromBytesZeroLength(t *testing.T) { - labels, err := LabelsFromBytes([]byte{}) + labels, err := labelsFromBytes([]byte{}) if err != nil { t.Fatal(err) } @@ -33,7 +33,7 @@ func TestLabelsFromBytesZeroLength(t *testing.T) { } func TestLabelsFromBytesInvalidLength(t *testing.T) { - labels, err := LabelsFromBytes([]byte{0x3, 0xaa, 0xbb}) // short length + labels, err := labelsFromBytes([]byte{0x3, 0xaa, 0xbb}) // short length if err == nil { t.Fatal("Expected error, got nil") } @@ -46,7 +46,7 @@ func TestLabelsFromBytesInvalidLength(t *testing.T) { } func TestLabelToBytes(t *testing.T) { - encodedLabel := LabelToBytes("slackware.it") + encodedLabel := labelToBytes("slackware.it") expected := []byte{ 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', 0x2, 'i', 't', @@ -58,7 +58,7 @@ func TestLabelToBytes(t *testing.T) { } func TestLabelToBytesZeroLength(t *testing.T) { - encodedLabel := LabelToBytes("") + encodedLabel := labelToBytes("") expected := []byte{0} if !bytes.Equal(encodedLabel, expected) { t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) -- cgit v1.2.3 From 6f8e78fd7f9fd9aa658246645d4f75fea43f344c Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Tue, 31 Jul 2018 11:17:31 +0200 Subject: OptDomainSearch --- dhcpv4/options.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dhcpv4/options.go b/dhcpv4/options.go index 760bc98..e7a50b9 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -72,6 +72,8 @@ func ParseOption(data []byte) (Option, error) { opt, err = ParseOptBootfileName(data) case OptionUserClassInformation: opt, err = ParseOptUserClass(data) + case OptionDNSDomainSearchList: + opt, err = ParseOptDomainSearch(data) default: opt, err = ParseOptionGeneric(data) } -- cgit v1.2.3 From 1f58265bc02e94b0c530719cda87da780a3813c1 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Tue, 31 Jul 2018 11:36:51 +0200 Subject: OptDomainSearch: fix String() --- dhcpv4/option_domain_search.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhcpv4/option_domain_search.go b/dhcpv4/option_domain_search.go index 75626ed..b845b59 100644 --- a/dhcpv4/option_domain_search.go +++ b/dhcpv4/option_domain_search.go @@ -36,7 +36,7 @@ func (op *OptDomainSearch) Length() int { // String returns a human-readable string. func (op *OptDomainSearch) String() string { - return fmt.Sprintf("DNS Domain Search List ->", op.DomainSearch) + return fmt.Sprintf("DNS Domain Search List -> %v", op.DomainSearch) } // ParseOptDomainSearch returns a new OptDomainSearch from a byte stream, or -- cgit v1.2.3 From 727a17d07bc2c8042bd882dd6fcc3001a47933c9 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Tue, 31 Jul 2018 17:02:36 +0200 Subject: add OptHostName --- dhcpv4/option_domain_name.go | 6 ++--- dhcpv4/option_host_name.go | 49 +++++++++++++++++++++++++++++++++++++++++ dhcpv4/option_host_name_test.go | 41 ++++++++++++++++++++++++++++++++++ dhcpv4/options.go | 2 ++ 4 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 dhcpv4/option_host_name.go create mode 100644 dhcpv4/option_host_name_test.go diff --git a/dhcpv4/option_domain_name.go b/dhcpv4/option_domain_name.go index 71d7ef1..e876b05 100644 --- a/dhcpv4/option_domain_name.go +++ b/dhcpv4/option_domain_name.go @@ -2,10 +2,10 @@ package dhcpv4 import "fmt" -// This option implements the server domani name option +// This option implements the domain name option // https://tools.ietf.org/html/rfc2132 -// OptDomainName represents an option encapsulating the server identifier. +// OptDomainName represents an option encapsulating the domain name. type OptDomainName struct { DomainName string } @@ -13,7 +13,7 @@ type OptDomainName struct { // ParseOptDomainName returns a new OptDomainName from a byte // stream, or error if any. func ParseOptDomainName(data []byte) (*OptDomainName, error) { - if len(data) < 2 { + if len(data) < 3 { return nil, ErrShortByteStream } code := OptionCode(data[0]) diff --git a/dhcpv4/option_host_name.go b/dhcpv4/option_host_name.go new file mode 100644 index 0000000..a922a2b --- /dev/null +++ b/dhcpv4/option_host_name.go @@ -0,0 +1,49 @@ +package dhcpv4 + +import "fmt" + +// This option implements the host name option +// https://tools.ietf.org/html/rfc2132 + +// OptHostName represents an option encapsulating the host name. +type OptHostName struct { + HostName string +} + +// ParseOptHostName returns a new OptHostName from a byte stream, or error if +// any. +func ParseOptHostName(data []byte) (*OptHostName, error) { + if len(data) < 3 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionHostName { + return nil, fmt.Errorf("expected code %v, got %v", OptionHostName, code) + } + length := int(data[1]) + if len(data) < 2+length { + return nil, ErrShortByteStream + } + return &OptHostName{HostName: string(data[2 : 2+length])}, nil +} + +// Code returns the option code. +func (o *OptHostName) Code() OptionCode { + return OptionHostName +} + +// ToBytes returns a serialized stream of bytes for this option. +func (o *OptHostName) ToBytes() []byte { + return append([]byte{byte(o.Code()), byte(o.Length())}, []byte(o.HostName)...) +} + +// String returns a human-readable string. +func (o *OptHostName) String() string { + return fmt.Sprintf("Host Name -> %v", o.HostName) +} + +// Length returns the length of the data portion (excluding option code an byte +// length). +func (o *OptHostName) Length() int { + return len(o.HostName) +} diff --git a/dhcpv4/option_host_name_test.go b/dhcpv4/option_host_name_test.go new file mode 100644 index 0000000..7f99100 --- /dev/null +++ b/dhcpv4/option_host_name_test.go @@ -0,0 +1,41 @@ +package dhcpv4 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOptHostNameInterfaceMethods(t *testing.T) { + o := OptHostName{HostName: "foo"} + require.Equal(t, OptionHostName, o.Code(), "Code") + require.Equal(t, 3, o.Length(), "Length") + require.Equal(t, []byte{byte(OptionHostName), 3, 'f', 'o', 'o'}, o.ToBytes(), "ToBytes") +} + +func TestParseOptHostName(t *testing.T) { + data := []byte{byte(OptionHostName), 4, 't', 'e', 's', 't'} + o, err := ParseOptHostName(data) + require.NoError(t, err) + require.Equal(t, &OptHostName{HostName: "test"}, o) + + // Short byte stream + data = []byte{byte(OptionHostName)} + _, err = ParseOptHostName(data) + require.Error(t, err, "should get error from short byte stream") + + // Wrong code + data = []byte{54, 2, 1, 1} + _, err = ParseOptHostName(data) + require.Error(t, err, "should get error from wrong code") + + // Bad length + data = []byte{byte(OptionHostName), 6, 1, 1, 1} + _, err = ParseOptHostName(data) + require.Error(t, err, "should get error from bad length") +} + +func TestOptHostNameString(t *testing.T) { + o := OptHostName{HostName: "testy test"} + require.Equal(t, "Host Name -> testy test", o.String()) +} diff --git a/dhcpv4/options.go b/dhcpv4/options.go index 760bc98..4903ab3 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -44,6 +44,8 @@ func ParseOption(data []byte) (Option, error) { opt, err = ParseOptSubnetMask(data) case OptionRouter: opt, err = ParseOptRouter(data) + case OptionHostName: + opt, err = ParseOptDomainName(data) case OptionRequestedIPAddress: opt, err = ParseOptRequestedIPAddress(data) case OptionIPAddressLeaseTime: -- cgit v1.2.3 From 857a1500e791f8d1b71e74f2e14e62b026cdb11d Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Tue, 31 Jul 2018 17:04:30 +0200 Subject: fix OptHostName --- dhcpv4/options.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhcpv4/options.go b/dhcpv4/options.go index 4903ab3..53b2ecf 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -45,7 +45,7 @@ func ParseOption(data []byte) (Option, error) { case OptionRouter: opt, err = ParseOptRouter(data) case OptionHostName: - opt, err = ParseOptDomainName(data) + opt, err = ParseOptHostName(data) case OptionRequestedIPAddress: opt, err = ParseOptRequestedIPAddress(data) case OptionIPAddressLeaseTime: -- cgit v1.2.3 From 0b939d11a4a11dcbc4f22d7fd0de52544bc4c51c Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Tue, 31 Jul 2018 19:48:00 +0200 Subject: OptHostName: update rfc url --- dhcpv4/option_host_name.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhcpv4/option_host_name.go b/dhcpv4/option_host_name.go index a922a2b..1630539 100644 --- a/dhcpv4/option_host_name.go +++ b/dhcpv4/option_host_name.go @@ -3,7 +3,7 @@ package dhcpv4 import "fmt" // This option implements the host name option -// https://tools.ietf.org/html/rfc2132 +// https://tools.ietf.org/html/rfc2132.txt // OptHostName represents an option encapsulating the host name. type OptHostName struct { -- cgit v1.2.3 From 1beb6bcd60050fb8192164bd9eee49c416e7cffa Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Tue, 31 Jul 2018 19:54:25 +0200 Subject: OptHostName: update rfc url --- dhcpv4/option_host_name.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhcpv4/option_host_name.go b/dhcpv4/option_host_name.go index 1630539..72c2c5d 100644 --- a/dhcpv4/option_host_name.go +++ b/dhcpv4/option_host_name.go @@ -3,7 +3,7 @@ package dhcpv4 import "fmt" // This option implements the host name option -// https://tools.ietf.org/html/rfc2132.txt +// https://tools.ietf.org/rfc/rfc2132.txt // OptHostName represents an option encapsulating the host name. type OptHostName struct { -- cgit v1.2.3 From 68394731198d4a68cbd5a7e95a5c84319d48eb97 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Tue, 31 Jul 2018 19:57:01 +0200 Subject: OptHostName: update rfc url --- dhcpv4/option_host_name.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhcpv4/option_host_name.go b/dhcpv4/option_host_name.go index 72c2c5d..a922a2b 100644 --- a/dhcpv4/option_host_name.go +++ b/dhcpv4/option_host_name.go @@ -3,7 +3,7 @@ package dhcpv4 import "fmt" // This option implements the host name option -// https://tools.ietf.org/rfc/rfc2132.txt +// https://tools.ietf.org/html/rfc2132 // OptHostName represents an option encapsulating the host name. type OptHostName struct { -- cgit v1.2.3 From 833497ba74dfdd0188176ae38a19de7a03a7858a Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Wed, 1 Aug 2018 18:25:54 +0200 Subject: extract rfc1035label --- dhcpv4/option_domain_search.go | 6 ++-- dhcpv4/option_rfc1035label.go | 53 ------------------------------ dhcpv4/option_rfc1035label_test.go | 66 -------------------------------------- dhcpv6/option_domainsearchlist.go | 6 ++-- dhcpv6/option_rfc1035label.go | 54 ------------------------------- dhcpv6/option_rfc1035label_test.go | 66 -------------------------------------- dnscompress/label.go | 60 ++++++++++++++++++++++++++++++++++ dnscompress/label_test.go | 66 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 134 insertions(+), 243 deletions(-) delete mode 100644 dhcpv4/option_rfc1035label.go delete mode 100644 dhcpv4/option_rfc1035label_test.go delete mode 100644 dhcpv6/option_rfc1035label.go delete mode 100644 dhcpv6/option_rfc1035label_test.go create mode 100644 dnscompress/label.go create mode 100644 dnscompress/label_test.go diff --git a/dhcpv4/option_domain_search.go b/dhcpv4/option_domain_search.go index b845b59..5ca17a8 100644 --- a/dhcpv4/option_domain_search.go +++ b/dhcpv4/option_domain_search.go @@ -5,6 +5,8 @@ package dhcpv4 import ( "fmt" + + "github.com/insomniacslk/dhcp/dnscompress" ) // OptDomainSearch represents an option encapsulating a domain search list. @@ -20,7 +22,7 @@ func (op *OptDomainSearch) Code() OptionCode { // ToBytes returns a serialized stream of bytes for this option. func (op *OptDomainSearch) ToBytes() []byte { buf := []byte{byte(op.Code()), byte(op.Length())} - buf = append(buf, labelsToBytes(op.DomainSearch)...) + buf = append(buf, dnscompress.LabelsToBytes(op.DomainSearch)...) return buf } @@ -53,7 +55,7 @@ func ParseOptDomainSearch(data []byte) (*OptDomainSearch, error) { if len(data) < 2+length { return nil, ErrShortByteStream } - domainSearch, err := labelsFromBytes(data[2:length+2]) + domainSearch, err := dnscompress.LabelsFromBytes(data[2:length+2]) if err != nil { return nil, err } diff --git a/dhcpv4/option_rfc1035label.go b/dhcpv4/option_rfc1035label.go deleted file mode 100644 index d0972ed..0000000 --- a/dhcpv4/option_rfc1035label.go +++ /dev/null @@ -1,53 +0,0 @@ -package dhcpv4 - -import ( - "fmt" - "strings" -) - -func labelsFromBytes(buf []byte) ([]string, error) { - var ( - pos = 0 - domains = make([]string, 0) - label = "" - ) - for { - if pos >= len(buf) { - return domains, nil - } - length := int(buf[pos]) - pos++ - if length == 0 { - domains = append(domains, label) - label = "" - } - if len(buf)-pos < length { - return nil, fmt.Errorf("DomainNamesFromBytes: invalid short label length") - } - if label != "" { - label += "." - } - label += string(buf[pos : pos+length]) - pos += length - } -} - -func labelToBytes(label string) []byte { - var encodedLabel []byte - if len(label) == 0 { - return []byte{0} - } - for _, part := range strings.Split(label, ".") { - encodedLabel = append(encodedLabel, byte(len(part))) - encodedLabel = append(encodedLabel, []byte(part)...) - } - return append(encodedLabel, 0) -} - -func labelsToBytes(labels []string) []byte { - var encodedLabels []byte - for _, label := range labels { - encodedLabels = append(encodedLabels, labelToBytes(label)...) - } - return encodedLabels -} diff --git a/dhcpv4/option_rfc1035label_test.go b/dhcpv4/option_rfc1035label_test.go deleted file mode 100644 index cd8189a..0000000 --- a/dhcpv4/option_rfc1035label_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package dhcpv4 - -import ( - "bytes" - "testing" -) - -func TestLabelsFromBytes(t *testing.T) { - labels, err := labelsFromBytes([]byte{ - 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', - 0x2, 'i', 't', - 0x0, - }) - if err != nil { - t.Fatal(err) - } - if len(labels) != 1 { - t.Fatalf("Invalid labels length. Expected: 1, got: %v", len(labels)) - } - if labels[0] != "slackware.it" { - t.Fatalf("Invalid label. Expected: %v, got: %v'", "slackware.it", labels[0]) - } -} - -func TestLabelsFromBytesZeroLength(t *testing.T) { - labels, err := labelsFromBytes([]byte{}) - if err != nil { - t.Fatal(err) - } - if len(labels) != 0 { - t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels)) - } -} - -func TestLabelsFromBytesInvalidLength(t *testing.T) { - labels, err := labelsFromBytes([]byte{0x3, 0xaa, 0xbb}) // short length - if err == nil { - t.Fatal("Expected error, got nil") - } - if len(labels) != 0 { - t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels)) - } - if labels != nil { - t.Fatalf("Invalid label. Expected nil, got %v", labels) - } -} - -func TestLabelToBytes(t *testing.T) { - encodedLabel := labelToBytes("slackware.it") - expected := []byte{ - 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', - 0x2, 'i', 't', - 0x0, - } - if !bytes.Equal(encodedLabel, expected) { - t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) - } -} - -func TestLabelToBytesZeroLength(t *testing.T) { - encodedLabel := labelToBytes("") - expected := []byte{0} - if !bytes.Equal(encodedLabel, expected) { - t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) - } -} diff --git a/dhcpv6/option_domainsearchlist.go b/dhcpv6/option_domainsearchlist.go index 402c68e..cc56ee0 100644 --- a/dhcpv6/option_domainsearchlist.go +++ b/dhcpv6/option_domainsearchlist.go @@ -6,6 +6,8 @@ package dhcpv6 import ( "encoding/binary" "fmt" + + "github.com/insomniacslk/dhcp/dnscompress" ) // OptDomainSearchList list implements a DOMAIN_SEARCH_LIST option @@ -21,7 +23,7 @@ func (op *OptDomainSearchList) ToBytes() []byte { buf := make([]byte, 4) binary.BigEndian.PutUint16(buf[0:2], uint16(DOMAIN_SEARCH_LIST)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) - buf = append(buf, LabelsToBytes(op.DomainSearchList)...) + buf = append(buf, dnscompress.LabelsToBytes(op.DomainSearchList)...) return buf } @@ -42,7 +44,7 @@ func (op *OptDomainSearchList) String() string { func ParseOptDomainSearchList(data []byte) (*OptDomainSearchList, error) { opt := OptDomainSearchList{} var err error - opt.DomainSearchList, err = LabelsFromBytes(data) + opt.DomainSearchList, err = dnscompress.LabelsFromBytes(data) if err != nil { return nil, err } diff --git a/dhcpv6/option_rfc1035label.go b/dhcpv6/option_rfc1035label.go deleted file mode 100644 index 06870b5..0000000 --- a/dhcpv6/option_rfc1035label.go +++ /dev/null @@ -1,54 +0,0 @@ -package dhcpv6 - -import ( - "fmt" - "strings" -) - -func LabelsFromBytes(buf []byte) ([]string, error) { - var ( - pos = 0 - domains = make([]string, 0) - label = "" - ) - for { - if pos >= len(buf) { - return domains, nil - } - length := int(buf[pos]) - pos++ - if length == 0 { - domains = append(domains, label) - label = "" - } - if len(buf)-pos < length { - return nil, fmt.Errorf("DomainNamesFromBytes: invalid short label length") - } - if label != "" { - label += "." - } - label += string(buf[pos : pos+length]) - pos += length - } - return domains, nil -} - -func LabelToBytes(label string) []byte { - var encodedLabel []byte - if len(label) == 0 { - return []byte{0} - } - for _, part := range strings.Split(label, ".") { - encodedLabel = append(encodedLabel, byte(len(part))) - encodedLabel = append(encodedLabel, []byte(part)...) - } - return append(encodedLabel, 0) -} - -func LabelsToBytes(labels []string) []byte { - var encodedLabels []byte - for _, label := range labels { - encodedLabels = append(encodedLabels, LabelToBytes(label)...) - } - return encodedLabels -} diff --git a/dhcpv6/option_rfc1035label_test.go b/dhcpv6/option_rfc1035label_test.go deleted file mode 100644 index f99c209..0000000 --- a/dhcpv6/option_rfc1035label_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package dhcpv6 - -import ( - "bytes" - "testing" -) - -func TestLabelsFromBytes(t *testing.T) { - labels, err := LabelsFromBytes([]byte{ - 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', - 0x2, 'i', 't', - 0x0, - }) - if err != nil { - t.Fatal(err) - } - if len(labels) != 1 { - t.Fatalf("Invalid labels length. Expected: 1, got: %v", len(labels)) - } - if labels[0] != "slackware.it" { - t.Fatalf("Invalid label. Expected: %v, got: %v'", "slackware.it", labels[0]) - } -} - -func TestLabelsFromBytesZeroLength(t *testing.T) { - labels, err := LabelsFromBytes([]byte{}) - if err != nil { - t.Fatal(err) - } - if len(labels) != 0 { - t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels)) - } -} - -func TestLabelsFromBytesInvalidLength(t *testing.T) { - labels, err := LabelsFromBytes([]byte{0x3, 0xaa, 0xbb}) // short length - if err == nil { - t.Fatal("Expected error, got nil") - } - if len(labels) != 0 { - t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels)) - } - if labels != nil { - t.Fatalf("Invalid label. Expected nil, got %v", labels) - } -} - -func TestLabelToBytes(t *testing.T) { - encodedLabel := LabelToBytes("slackware.it") - expected := []byte{ - 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', - 0x2, 'i', 't', - 0x0, - } - if !bytes.Equal(encodedLabel, expected) { - t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) - } -} - -func TestLabelToBytesZeroLength(t *testing.T) { - encodedLabel := LabelToBytes("") - expected := []byte{0} - if !bytes.Equal(encodedLabel, expected) { - t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) - } -} diff --git a/dnscompress/label.go b/dnscompress/label.go new file mode 100644 index 0000000..3d84708 --- /dev/null +++ b/dnscompress/label.go @@ -0,0 +1,60 @@ +package dnscompress + +import ( + "fmt" + "strings" +) + +// This implements the compression from RFC 1035, section 4.1.4 +// https://tools.ietf.org/html/rfc1035 + +// LabelsFromBytes decodes a serialized stream and returns a list of labels +func LabelsFromBytes(buf []byte) ([]string, error) { + var ( + pos = 0 + domains = make([]string, 0) + label = "" + ) + for { + if pos >= len(buf) { + return domains, nil + } + length := int(buf[pos]) + pos++ + if length == 0 { + domains = append(domains, label) + label = "" + } + if len(buf)-pos < length { + return nil, fmt.Errorf("DomainNamesFromBytes: invalid short label length") + } + if label != "" { + label += "." + } + label += string(buf[pos : pos+length]) + pos += length + } +} + +// LabelToBytes encodes a label and returns a serialized stream of bytes +func LabelToBytes(label string) []byte { + var encodedLabel []byte + if len(label) == 0 { + return []byte{0} + } + for _, part := range strings.Split(label, ".") { + encodedLabel = append(encodedLabel, byte(len(part))) + encodedLabel = append(encodedLabel, []byte(part)...) + } + return append(encodedLabel, 0) +} + +// LabelsToBytes encodes a list of labels and returns a serialized stream of +// bytes +func LabelsToBytes(labels []string) []byte { + var encodedLabels []byte + for _, label := range labels { + encodedLabels = append(encodedLabels, LabelToBytes(label)...) + } + return encodedLabels +} diff --git a/dnscompress/label_test.go b/dnscompress/label_test.go new file mode 100644 index 0000000..af8e405 --- /dev/null +++ b/dnscompress/label_test.go @@ -0,0 +1,66 @@ +package dnscompress + +import ( + "bytes" + "testing" +) + +func TestLabelsFromBytes(t *testing.T) { + labels, err := LabelsFromBytes([]byte{ + 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', + 0x2, 'i', 't', + 0x0, + }) + if err != nil { + t.Fatal(err) + } + if len(labels) != 1 { + t.Fatalf("Invalid labels length. Expected: 1, got: %v", len(labels)) + } + if labels[0] != "slackware.it" { + t.Fatalf("Invalid label. Expected: %v, got: %v'", "slackware.it", labels[0]) + } +} + +func TestLabelsFromBytesZeroLength(t *testing.T) { + labels, err := LabelsFromBytes([]byte{}) + if err != nil { + t.Fatal(err) + } + if len(labels) != 0 { + t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels)) + } +} + +func TestLabelsFromBytesInvalidLength(t *testing.T) { + labels, err := LabelsFromBytes([]byte{0x3, 0xaa, 0xbb}) // short length + if err == nil { + t.Fatal("Expected error, got nil") + } + if len(labels) != 0 { + t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels)) + } + if labels != nil { + t.Fatalf("Invalid label. Expected nil, got %v", labels) + } +} + +func TestLabelToBytes(t *testing.T) { + encodedLabel := LabelToBytes("slackware.it") + expected := []byte{ + 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', + 0x2, 'i', 't', + 0x0, + } + if !bytes.Equal(encodedLabel, expected) { + t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) + } +} + +func TestLabelToBytesZeroLength(t *testing.T) { + encodedLabel := LabelToBytes("") + expected := []byte{0} + if !bytes.Equal(encodedLabel, expected) { + t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) + } +} -- cgit v1.2.3 From 1ce17c65bf3f13919fa2fbb1a14ce8306a0eca04 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Wed, 1 Aug 2018 17:59:36 +0100 Subject: add OptNTPServers (#115) --- dhcpv4/option_ntp_servers.go | 70 +++++++++++++++++++++++++++++++++++++++ dhcpv4/option_ntp_servers_test.go | 65 ++++++++++++++++++++++++++++++++++++ dhcpv4/option_router.go | 2 +- dhcpv4/options.go | 2 ++ 4 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 dhcpv4/option_ntp_servers.go create mode 100644 dhcpv4/option_ntp_servers_test.go diff --git a/dhcpv4/option_ntp_servers.go b/dhcpv4/option_ntp_servers.go new file mode 100644 index 0000000..39881d6 --- /dev/null +++ b/dhcpv4/option_ntp_servers.go @@ -0,0 +1,70 @@ +package dhcpv4 + +import ( + "fmt" + "net" +) + +// This option implements the network time protocol servers option +// https://tools.ietf.org/html/rfc2132 + +// OptNTPServers represents an option encapsulating the NTP servers. +type OptNTPServers struct { + NTPServers []net.IP +} + +// ParseOptNTPServers returns a new OptNTPServers from a byte stream, or error if any. +func ParseOptNTPServers(data []byte) (*OptNTPServers, error) { + if len(data) < 2 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionNTPServers { + return nil, fmt.Errorf("expected code %v, got %v", OptionNTPServers, code) + } + length := int(data[1]) + if length == 0 || length%4 != 0 { + return nil, fmt.Errorf("Invalid length: expected multiple of 4 larger than 4, got %v", length) + } + if len(data) < 2+length { + return nil, ErrShortByteStream + } + ntpServers := make([]net.IP, 0, length%4) + for idx := 0; idx < length; idx += 4 { + b := data[2+idx : 2+idx+4] + ntpServers = append(ntpServers, net.IPv4(b[0], b[1], b[2], b[3])) + } + return &OptNTPServers{NTPServers: ntpServers}, nil +} + +// Code returns the option code. +func (o *OptNTPServers) Code() OptionCode { + return OptionNTPServers +} + +// ToBytes returns a serialized stream of bytes for this option. +func (o *OptNTPServers) ToBytes() []byte { + ret := []byte{byte(o.Code()), byte(o.Length())} + for _, ntp := range o.NTPServers { + ret = append(ret, ntp.To4()...) + } + return ret +} + +// String returns a human-readable string. +func (o *OptNTPServers) String() string { + var ntpServers string + for idx, ntp := range o.NTPServers { + ntpServers += ntp.String() + if idx < len(o.NTPServers)-1 { + ntpServers += ", " + } + } + return fmt.Sprintf("NTP Servers -> %v", ntpServers) +} + +// Length returns the length of the data portion (excluding option code an byte +// length). +func (o *OptNTPServers) Length() int { + return len(o.NTPServers) * 4 +} diff --git a/dhcpv4/option_ntp_servers_test.go b/dhcpv4/option_ntp_servers_test.go new file mode 100644 index 0000000..e7bcefd --- /dev/null +++ b/dhcpv4/option_ntp_servers_test.go @@ -0,0 +1,65 @@ +package dhcpv4 + +import ( + "net" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOptNTPServersInterfaceMethods(t *testing.T) { + ntpServers := []net.IP{ + net.IPv4(192, 168, 0, 10), + net.IPv4(192, 168, 0, 20), + } + o := OptNTPServers{NTPServers: ntpServers} + require.Equal(t, OptionNTPServers, o.Code(), "Code") + require.Equal(t, net.IPv4len*len(ntpServers), o.Length(), "Length") + require.Equal(t, ntpServers, o.NTPServers, "NTPServers") +} + +func TestParseOptNTPServers(t *testing.T) { + data := []byte{ + byte(OptionNTPServers), + 8, // Length + 192, 168, 0, 10, // NTP server #1 + 192, 168, 0, 20, // NTP server #2 + } + o, err := ParseOptNTPServers(data) + require.NoError(t, err) + ntpServers := []net.IP{ + net.IPv4(192, 168, 0, 10), + net.IPv4(192, 168, 0, 20), + } + require.Equal(t, &OptNTPServers{NTPServers: ntpServers}, o) + + // Short byte stream + data = []byte{byte(OptionNTPServers)} + _, err = ParseOptNTPServers(data) + require.Error(t, err, "should get error from short byte stream") + + // Wrong code + data = []byte{54, 2, 1, 1} + _, err = ParseOptNTPServers(data) + require.Error(t, err, "should get error from wrong code") + + // Bad length + data = []byte{byte(OptionNTPServers), 6, 1, 1, 1} + _, err = ParseOptNTPServers(data) + require.Error(t, err, "should get error from bad length") +} + +func TestParseOptNTPserversNoNTPServers(t *testing.T) { + // RFC2132 requires that at least one NTP server IP is specified + data := []byte{ + byte(OptionNTPServers), + 0, // Length + } + _, err := ParseOptNTPServers(data) + require.Error(t, err) +} + +func TestOptNTPServersString(t *testing.T) { + o := OptNTPServers{NTPServers: []net.IP{net.IPv4(192, 168, 0, 1), net.IPv4(192, 168, 0, 10)}} + require.Equal(t, "NTP Servers -> 192.168.0.1, 192.168.0.10", o.String()) +} diff --git a/dhcpv4/option_router.go b/dhcpv4/option_router.go index b9bdff1..3154edd 100644 --- a/dhcpv4/option_router.go +++ b/dhcpv4/option_router.go @@ -13,7 +13,7 @@ type OptRouter struct { Routers []net.IP } -// ParseOptRouter returns a new OptRouter from a byte stream, or error if any. +// ParseOptRouter returns a new OptRouter from a byte stream, or error if any. func ParseOptRouter(data []byte) (*OptRouter, error) { if len(data) < 2 { return nil, ErrShortByteStream diff --git a/dhcpv4/options.go b/dhcpv4/options.go index 53b2ecf..22e6eee 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -46,6 +46,8 @@ func ParseOption(data []byte) (Option, error) { opt, err = ParseOptRouter(data) case OptionHostName: opt, err = ParseOptHostName(data) + case OptionNTPServers: + opt, err = ParseOptNTPServers(data) case OptionRequestedIPAddress: opt, err = ParseOptRequestedIPAddress(data) case OptionIPAddressLeaseTime: -- cgit v1.2.3 From a606ffc4c7a61ae981450b9dcc5605f78bba7e07 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Wed, 1 Aug 2018 19:12:37 +0200 Subject: rename dnscompress to rfc1035label --- dhcpv4/option_domain_search.go | 6 ++-- dhcpv6/option_domainsearchlist.go | 6 ++-- dnscompress/label.go | 60 ----------------------------------- dnscompress/label_test.go | 66 --------------------------------------- rfc1035label/label.go | 60 +++++++++++++++++++++++++++++++++++ rfc1035label/label_test.go | 66 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 132 insertions(+), 132 deletions(-) delete mode 100644 dnscompress/label.go delete mode 100644 dnscompress/label_test.go create mode 100644 rfc1035label/label.go create mode 100644 rfc1035label/label_test.go diff --git a/dhcpv4/option_domain_search.go b/dhcpv4/option_domain_search.go index 5ca17a8..daade0a 100644 --- a/dhcpv4/option_domain_search.go +++ b/dhcpv4/option_domain_search.go @@ -6,7 +6,7 @@ package dhcpv4 import ( "fmt" - "github.com/insomniacslk/dhcp/dnscompress" + "github.com/insomniacslk/dhcp/rfc1035label" ) // OptDomainSearch represents an option encapsulating a domain search list. @@ -22,7 +22,7 @@ func (op *OptDomainSearch) Code() OptionCode { // ToBytes returns a serialized stream of bytes for this option. func (op *OptDomainSearch) ToBytes() []byte { buf := []byte{byte(op.Code()), byte(op.Length())} - buf = append(buf, dnscompress.LabelsToBytes(op.DomainSearch)...) + buf = append(buf, rfc1035label.LabelsToBytes(op.DomainSearch)...) return buf } @@ -55,7 +55,7 @@ func ParseOptDomainSearch(data []byte) (*OptDomainSearch, error) { if len(data) < 2+length { return nil, ErrShortByteStream } - domainSearch, err := dnscompress.LabelsFromBytes(data[2:length+2]) + domainSearch, err := rfc1035label.LabelsFromBytes(data[2:length+2]) if err != nil { return nil, err } diff --git a/dhcpv6/option_domainsearchlist.go b/dhcpv6/option_domainsearchlist.go index cc56ee0..54e08bd 100644 --- a/dhcpv6/option_domainsearchlist.go +++ b/dhcpv6/option_domainsearchlist.go @@ -7,7 +7,7 @@ import ( "encoding/binary" "fmt" - "github.com/insomniacslk/dhcp/dnscompress" + "github.com/insomniacslk/dhcp/rfc1035label" ) // OptDomainSearchList list implements a DOMAIN_SEARCH_LIST option @@ -23,7 +23,7 @@ func (op *OptDomainSearchList) ToBytes() []byte { buf := make([]byte, 4) binary.BigEndian.PutUint16(buf[0:2], uint16(DOMAIN_SEARCH_LIST)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) - buf = append(buf, dnscompress.LabelsToBytes(op.DomainSearchList)...) + buf = append(buf, rfc1035label.LabelsToBytes(op.DomainSearchList)...) return buf } @@ -44,7 +44,7 @@ func (op *OptDomainSearchList) String() string { func ParseOptDomainSearchList(data []byte) (*OptDomainSearchList, error) { opt := OptDomainSearchList{} var err error - opt.DomainSearchList, err = dnscompress.LabelsFromBytes(data) + opt.DomainSearchList, err = rfc1035label.LabelsFromBytes(data) if err != nil { return nil, err } diff --git a/dnscompress/label.go b/dnscompress/label.go deleted file mode 100644 index 3d84708..0000000 --- a/dnscompress/label.go +++ /dev/null @@ -1,60 +0,0 @@ -package dnscompress - -import ( - "fmt" - "strings" -) - -// This implements the compression from RFC 1035, section 4.1.4 -// https://tools.ietf.org/html/rfc1035 - -// LabelsFromBytes decodes a serialized stream and returns a list of labels -func LabelsFromBytes(buf []byte) ([]string, error) { - var ( - pos = 0 - domains = make([]string, 0) - label = "" - ) - for { - if pos >= len(buf) { - return domains, nil - } - length := int(buf[pos]) - pos++ - if length == 0 { - domains = append(domains, label) - label = "" - } - if len(buf)-pos < length { - return nil, fmt.Errorf("DomainNamesFromBytes: invalid short label length") - } - if label != "" { - label += "." - } - label += string(buf[pos : pos+length]) - pos += length - } -} - -// LabelToBytes encodes a label and returns a serialized stream of bytes -func LabelToBytes(label string) []byte { - var encodedLabel []byte - if len(label) == 0 { - return []byte{0} - } - for _, part := range strings.Split(label, ".") { - encodedLabel = append(encodedLabel, byte(len(part))) - encodedLabel = append(encodedLabel, []byte(part)...) - } - return append(encodedLabel, 0) -} - -// LabelsToBytes encodes a list of labels and returns a serialized stream of -// bytes -func LabelsToBytes(labels []string) []byte { - var encodedLabels []byte - for _, label := range labels { - encodedLabels = append(encodedLabels, LabelToBytes(label)...) - } - return encodedLabels -} diff --git a/dnscompress/label_test.go b/dnscompress/label_test.go deleted file mode 100644 index af8e405..0000000 --- a/dnscompress/label_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package dnscompress - -import ( - "bytes" - "testing" -) - -func TestLabelsFromBytes(t *testing.T) { - labels, err := LabelsFromBytes([]byte{ - 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', - 0x2, 'i', 't', - 0x0, - }) - if err != nil { - t.Fatal(err) - } - if len(labels) != 1 { - t.Fatalf("Invalid labels length. Expected: 1, got: %v", len(labels)) - } - if labels[0] != "slackware.it" { - t.Fatalf("Invalid label. Expected: %v, got: %v'", "slackware.it", labels[0]) - } -} - -func TestLabelsFromBytesZeroLength(t *testing.T) { - labels, err := LabelsFromBytes([]byte{}) - if err != nil { - t.Fatal(err) - } - if len(labels) != 0 { - t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels)) - } -} - -func TestLabelsFromBytesInvalidLength(t *testing.T) { - labels, err := LabelsFromBytes([]byte{0x3, 0xaa, 0xbb}) // short length - if err == nil { - t.Fatal("Expected error, got nil") - } - if len(labels) != 0 { - t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels)) - } - if labels != nil { - t.Fatalf("Invalid label. Expected nil, got %v", labels) - } -} - -func TestLabelToBytes(t *testing.T) { - encodedLabel := LabelToBytes("slackware.it") - expected := []byte{ - 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', - 0x2, 'i', 't', - 0x0, - } - if !bytes.Equal(encodedLabel, expected) { - t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) - } -} - -func TestLabelToBytesZeroLength(t *testing.T) { - encodedLabel := LabelToBytes("") - expected := []byte{0} - if !bytes.Equal(encodedLabel, expected) { - t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) - } -} diff --git a/rfc1035label/label.go b/rfc1035label/label.go new file mode 100644 index 0000000..c63b4af --- /dev/null +++ b/rfc1035label/label.go @@ -0,0 +1,60 @@ +package rfc1035label + +import ( + "fmt" + "strings" +) + +// This implements the compression from RFC 1035, section 4.1.4 +// https://tools.ietf.org/html/rfc1035 + +// LabelsFromBytes decodes a serialized stream and returns a list of labels +func LabelsFromBytes(buf []byte) ([]string, error) { + var ( + pos = 0 + domains = make([]string, 0) + label = "" + ) + for { + if pos >= len(buf) { + return domains, nil + } + length := int(buf[pos]) + pos++ + if length == 0 { + domains = append(domains, label) + label = "" + } + if len(buf)-pos < length { + return nil, fmt.Errorf("DomainNamesFromBytes: invalid short label length") + } + if label != "" { + label += "." + } + label += string(buf[pos : pos+length]) + pos += length + } +} + +// LabelToBytes encodes a label and returns a serialized stream of bytes +func LabelToBytes(label string) []byte { + var encodedLabel []byte + if len(label) == 0 { + return []byte{0} + } + for _, part := range strings.Split(label, ".") { + encodedLabel = append(encodedLabel, byte(len(part))) + encodedLabel = append(encodedLabel, []byte(part)...) + } + return append(encodedLabel, 0) +} + +// LabelsToBytes encodes a list of labels and returns a serialized stream of +// bytes +func LabelsToBytes(labels []string) []byte { + var encodedLabels []byte + for _, label := range labels { + encodedLabels = append(encodedLabels, LabelToBytes(label)...) + } + return encodedLabels +} diff --git a/rfc1035label/label_test.go b/rfc1035label/label_test.go new file mode 100644 index 0000000..f91110b --- /dev/null +++ b/rfc1035label/label_test.go @@ -0,0 +1,66 @@ +package rfc1035label + +import ( + "bytes" + "testing" +) + +func TestLabelsFromBytes(t *testing.T) { + labels, err := LabelsFromBytes([]byte{ + 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', + 0x2, 'i', 't', + 0x0, + }) + if err != nil { + t.Fatal(err) + } + if len(labels) != 1 { + t.Fatalf("Invalid labels length. Expected: 1, got: %v", len(labels)) + } + if labels[0] != "slackware.it" { + t.Fatalf("Invalid label. Expected: %v, got: %v'", "slackware.it", labels[0]) + } +} + +func TestLabelsFromBytesZeroLength(t *testing.T) { + labels, err := LabelsFromBytes([]byte{}) + if err != nil { + t.Fatal(err) + } + if len(labels) != 0 { + t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels)) + } +} + +func TestLabelsFromBytesInvalidLength(t *testing.T) { + labels, err := LabelsFromBytes([]byte{0x3, 0xaa, 0xbb}) // short length + if err == nil { + t.Fatal("Expected error, got nil") + } + if len(labels) != 0 { + t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels)) + } + if labels != nil { + t.Fatalf("Invalid label. Expected nil, got %v", labels) + } +} + +func TestLabelToBytes(t *testing.T) { + encodedLabel := LabelToBytes("slackware.it") + expected := []byte{ + 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e', + 0x2, 'i', 't', + 0x0, + } + if !bytes.Equal(encodedLabel, expected) { + t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) + } +} + +func TestLabelToBytesZeroLength(t *testing.T) { + encodedLabel := LabelToBytes("") + expected := []byte{0} + if !bytes.Equal(encodedLabel, expected) { + t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel) + } +} -- cgit v1.2.3 From 40474d58d9e4302b97e535e199e79ad1c63ade06 Mon Sep 17 00:00:00 2001 From: Owen Mooney Date: Wed, 1 Aug 2018 20:42:28 +0100 Subject: Parse non-RFC compliant user class (#114) --- dhcpv4/option_userclass.go | 17 +++++++++++++++++ dhcpv4/option_userclass_test.go | 28 ++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/dhcpv4/option_userclass.go b/dhcpv4/option_userclass.go index 307a128..fc088fa 100644 --- a/dhcpv4/option_userclass.go +++ b/dhcpv4/option_userclass.go @@ -66,6 +66,23 @@ func ParseOptUserClass(data []byte) (*OptUserClass, error) { totalLength, len(data)) } + // Check if option is Microsoft style instead of RFC compliant, issue #113 + + // User-class options are, according to RFC3004, supposed to contain a set + // of strings each with length UC_Len_i. Here we check that this is so, + // by seeing if all the UC_Len_i lengths are consistent with the overall + // option length. If the lengths don't add up, we assume that the option + // is a single string and non RFC3004 compliant + var counting int + for counting < totalLength { + // UC_Len_i does not include itself so add 1 + counting += int(data[counting]) + 1 + } + if counting != totalLength { + opt.UserClasses = append(opt.UserClasses, data[:totalLength]) + return &opt, nil + } + for i := 0; i < totalLength; { ucLen := int(data[i]) if ucLen == 0 { diff --git a/dhcpv4/option_userclass_test.go b/dhcpv4/option_userclass_test.go index 492cabd..02b6f94 100644 --- a/dhcpv4/option_userclass_test.go +++ b/dhcpv4/option_userclass_test.go @@ -38,6 +38,26 @@ func TestParseOptUserClassNone(t *testing.T) { require.Error(t, err) } +func TestParseOptUserClassMicrosoft(t *testing.T) { + expected := []byte{ + 77, 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', + } + opt, err := ParseOptUserClass(expected) + require.NoError(t, err) + require.Equal(t, 1, len(opt.UserClasses)) + require.Equal(t, []byte("linuxboot"), opt.UserClasses[0]) +} + +func TestParseOptUserClassMicrosoftLongerThanLength(t *testing.T) { + expected := []byte{ + 77, 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', 'X', + } + opt, err := ParseOptUserClass(expected) + require.NoError(t, err) + require.Equal(t, 1, len(opt.UserClasses)) + require.Equal(t, []byte("linuxboot"), opt.UserClasses[0]) +} + func TestParseOptUserClass(t *testing.T) { expected := []byte{ 77, 10, 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', @@ -75,14 +95,6 @@ func TestParseOptUserClassLongerThanLength(t *testing.T) { require.Equal(t, []byte("linuxboot"), opt.UserClasses[0]) } -func TestParseOptUserClassShorterThanLength(t *testing.T) { - expected := []byte{ - 77, 10, 10, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', - } - _, err := ParseOptUserClass(expected) - require.Error(t, err) -} - func TestParseOptUserClassShorterTotalLength(t *testing.T) { expected := []byte{ 77, 11, 10, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', -- cgit v1.2.3 From 5fd3f4fea8d73e29d931c0fabb4179b9e98ad275 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 11:01:48 +0200 Subject: fix OptUserClass --- dhcpv4/option_userclass.go | 9 ++++++++- dhcpv4/option_userclass_test.go | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/dhcpv4/option_userclass.go b/dhcpv4/option_userclass.go index fc088fa..f7e4d7f 100644 --- a/dhcpv4/option_userclass.go +++ b/dhcpv4/option_userclass.go @@ -12,6 +12,7 @@ import ( // OptUserClass represents an option encapsulating User Classes. type OptUserClass struct { UserClasses [][]byte + Rfc3004 bool } // Code returns the option code @@ -22,6 +23,9 @@ func (op *OptUserClass) Code() OptionCode { // ToBytes serializes the option and returns it as a sequence of bytes func (op *OptUserClass) ToBytes() []byte { buf := []byte{byte(op.Code()), byte(op.Length())} + if !op.Rfc3004 && len(op.UserClasses) == 1 { + return append(buf, op.UserClasses[0]...) + } for _, uc := range op.UserClasses { buf = append(buf, byte(len(uc))) buf = append(buf, uc...) @@ -32,6 +36,9 @@ func (op *OptUserClass) ToBytes() []byte { // Length returns the option length func (op *OptUserClass) Length() int { ret := 0 + if !op.Rfc3004 && len(op.UserClasses) == 1 { + return len(op.UserClasses[0]) + } for _, uc := range op.UserClasses { ret += 1 + len(uc) } @@ -82,7 +89,7 @@ func ParseOptUserClass(data []byte) (*OptUserClass, error) { opt.UserClasses = append(opt.UserClasses, data[:totalLength]) return &opt, nil } - + opt.Rfc3004 = true for i := 0; i < totalLength; { ucLen := int(data[i]) if ucLen == 0 { diff --git a/dhcpv4/option_userclass_test.go b/dhcpv4/option_userclass_test.go index 02b6f94..0e74ee1 100644 --- a/dhcpv4/option_userclass_test.go +++ b/dhcpv4/option_userclass_test.go @@ -9,6 +9,7 @@ import ( func TestOptUserClassToBytes(t *testing.T) { opt := OptUserClass{ UserClasses: [][]byte{[]byte("linuxboot")}, + Rfc3004: true, } data := opt.ToBytes() expected := []byte{ @@ -19,6 +20,19 @@ func TestOptUserClassToBytes(t *testing.T) { require.Equal(t, expected, data) } +func TestOptUserClassMicrosoftToBytes(t *testing.T) { + opt := OptUserClass{ + UserClasses: [][]byte{[]byte("linuxboot")}, + } + data := opt.ToBytes() + expected := []byte{ + 77, // OptionUserClass + 9, // length + 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', + } + require.Equal(t, expected, data) +} + func TestParseOptUserClassMultiple(t *testing.T) { expected := []byte{ 77, 15, -- cgit v1.2.3 From 4ceb1c8174876a7564bca419ab971cd63c10327f Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 11:13:54 +0200 Subject: OptUserClass: fix short length --- dhcpv4/option_userclass.go | 2 +- dhcpv4/option_userclass_test.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dhcpv4/option_userclass.go b/dhcpv4/option_userclass.go index f7e4d7f..f8a081f 100644 --- a/dhcpv4/option_userclass.go +++ b/dhcpv4/option_userclass.go @@ -58,7 +58,7 @@ func (op *OptUserClass) String() string { func ParseOptUserClass(data []byte) (*OptUserClass, error) { opt := OptUserClass{} - if len(data) < 4 { + if len(data) < 3 { return nil, ErrShortByteStream } code := OptionCode(data[0]) diff --git a/dhcpv4/option_userclass_test.go b/dhcpv4/option_userclass_test.go index 0e74ee1..72cf131 100644 --- a/dhcpv4/option_userclass_test.go +++ b/dhcpv4/option_userclass_test.go @@ -62,6 +62,16 @@ func TestParseOptUserClassMicrosoft(t *testing.T) { require.Equal(t, []byte("linuxboot"), opt.UserClasses[0]) } +func TestParseOptUserClassMicrosoftShort(t *testing.T) { + expected := []byte{ + 77, 1, 'l', + } + opt, err := ParseOptUserClass(expected) + require.NoError(t, err) + require.Equal(t, 1, len(opt.UserClasses)) + require.Equal(t, []byte("l"), opt.UserClasses[0]) +} + func TestParseOptUserClassMicrosoftLongerThanLength(t *testing.T) { expected := []byte{ 77, 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', 'X', -- cgit v1.2.3 From 5e00177fe0c35379c9e83da496d6527d932854ae Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 18:24:24 +0200 Subject: OptUserClass: use only the flag as authoritative --- dhcpv4/dhcpv4_test.go | 4 ++-- dhcpv4/modifiers_test.go | 4 ++-- dhcpv4/option_userclass.go | 14 +++++++++----- dhcpv4/option_userclass_test.go | 1 + 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/dhcpv4/dhcpv4_test.go b/dhcpv4/dhcpv4_test.go index 7e5f083..217c801 100644 --- a/dhcpv4/dhcpv4_test.go +++ b/dhcpv4/dhcpv4_test.go @@ -363,7 +363,7 @@ func TestDHCPv4RequestFromOfferWithModifier(t *testing.T) { require.NoError(t, err) require.NotEqual(t, (*MessageType)(nil), *req.MessageType()) require.Equal(t, MessageTypeRequest, *req.MessageType()) - require.Equal(t, "OptUserClass{userclass=[linuxboot]}", req.options[3].String()) + require.Equal(t, "User Class Information -> linuxboot", req.options[3].String()) } func TestNewReplyFromRequest(t *testing.T) { @@ -385,7 +385,7 @@ func TestNewReplyFromRequestWithModifier(t *testing.T) { require.NoError(t, err) require.Equal(t, discover.TransactionID(), reply.TransactionID()) require.Equal(t, discover.GatewayIPAddr(), reply.GatewayIPAddr()) - require.Equal(t, "OptUserClass{userclass=[linuxboot]}", reply.options[0].String()) + require.Equal(t, "User Class Information -> linuxboot", reply.options[0].String()) } func TestDHCPv4MessageTypeNil(t *testing.T) { diff --git a/dhcpv4/modifiers_test.go b/dhcpv4/modifiers_test.go index 415a4ea..2e249ee 100644 --- a/dhcpv4/modifiers_test.go +++ b/dhcpv4/modifiers_test.go @@ -10,5 +10,5 @@ func TestUserClassModifier(t *testing.T) { d, _ := New() userClass := WithUserClass([]byte("linuxboot")) d = userClass(d) - require.Equal(t, "OptUserClass{userclass=[linuxboot]}", d.options[0].String()) -} \ No newline at end of file + require.Equal(t, "User Class Information -> linuxboot", d.options[0].String()) +} diff --git a/dhcpv4/option_userclass.go b/dhcpv4/option_userclass.go index f8a081f..d6ddabc 100644 --- a/dhcpv4/option_userclass.go +++ b/dhcpv4/option_userclass.go @@ -23,7 +23,7 @@ func (op *OptUserClass) Code() OptionCode { // ToBytes serializes the option and returns it as a sequence of bytes func (op *OptUserClass) ToBytes() []byte { buf := []byte{byte(op.Code()), byte(op.Length())} - if !op.Rfc3004 && len(op.UserClasses) == 1 { + if !op.Rfc3004 { return append(buf, op.UserClasses[0]...) } for _, uc := range op.UserClasses { @@ -36,7 +36,7 @@ func (op *OptUserClass) ToBytes() []byte { // Length returns the option length func (op *OptUserClass) Length() int { ret := 0 - if !op.Rfc3004 && len(op.UserClasses) == 1 { + if !op.Rfc3004 { return len(op.UserClasses[0]) } for _, uc := range op.UserClasses { @@ -47,10 +47,14 @@ func (op *OptUserClass) Length() int { func (op *OptUserClass) String() string { ucStrings := make([]string, 0, len(op.UserClasses)) - for _, uc := range op.UserClasses { - ucStrings = append(ucStrings, string(uc)) + if !op.Rfc3004 { + ucStrings = append(ucStrings, string(op.UserClasses[0])) + } else { + for _, uc := range op.UserClasses { + ucStrings = append(ucStrings, string(uc)) + } } - return fmt.Sprintf("OptUserClass{userclass=[%s]}", strings.Join(ucStrings, ", ")) + return fmt.Sprintf("User Class Information -> %v", strings.Join(ucStrings, ", ")) } // ParseOptUserClass returns a new OptUserClass from a byte stream or diff --git a/dhcpv4/option_userclass_test.go b/dhcpv4/option_userclass_test.go index 72cf131..f6039df 100644 --- a/dhcpv4/option_userclass_test.go +++ b/dhcpv4/option_userclass_test.go @@ -98,6 +98,7 @@ func TestOptUserClassToBytesMultiple(t *testing.T) { []byte("linuxboot"), []byte("test"), }, + Rfc3004: true, } data := opt.ToBytes() expected := []byte{ -- cgit v1.2.3 From de11326c564574a39b8b70dacbb44347b2985795 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 15:16:39 +0200 Subject: add OptClientArchType --- dhcpv4/option_archtype.go | 104 +++++++++++++++++++++++++++++++++++++++++ dhcpv4/option_archtype_test.go | 68 +++++++++++++++++++++++++++ dhcpv4/options.go | 22 +++++---- dhcpv4/options_test.go | 16 +++---- 4 files changed, 192 insertions(+), 18 deletions(-) create mode 100644 dhcpv4/option_archtype.go create mode 100644 dhcpv4/option_archtype_test.go diff --git a/dhcpv4/option_archtype.go b/dhcpv4/option_archtype.go new file mode 100644 index 0000000..eb5b365 --- /dev/null +++ b/dhcpv4/option_archtype.go @@ -0,0 +1,104 @@ +package dhcpv4 + +// This option implements the Client System Architecture Type option +// https://tools.ietf.org/html/rfc4578 + +import ( + "encoding/binary" + "fmt" +) + +//ArchType encodes an architecture type in an uint16 +type ArchType uint16 + +// see rfc4578 +const ( + INTEL_X86PC ArchType = 0 + NEC_PC98 ArchType = 1 + EFI_ITANIUM ArchType = 2 + DEC_ALPHA ArchType = 3 + ARC_X86 ArchType = 4 + INTEL_LEAN_CLIENT ArchType = 5 + EFI_IA32 ArchType = 6 + EFI_BC ArchType = 7 + EFI_XSCALE ArchType = 8 + EFI_X86_64 ArchType = 9 +) + +// ArchTypeToStringMap maps an ArchType to a mnemonic name +var ArchTypeToStringMap = map[ArchType]string{ + INTEL_X86PC: "Intel x86PC", + NEC_PC98: "NEC/PC98", + EFI_ITANIUM: "EFI Itanium", + DEC_ALPHA: "DEC Alpha", + ARC_X86: "Arc x86", + INTEL_LEAN_CLIENT: "Intel Lean Client", + EFI_IA32: "EFI IA32", + EFI_BC: "EFI BC", + EFI_XSCALE: "EFI Xscale", + EFI_X86_64: "EFI x86-64", +} + +// OptClientArchType represents an option CLIENT_ARCH_TYPE +type OptClientArchType struct { + ArchTypes []ArchType +} + +func (o *OptClientArchType) Code() OptionCode { + return OptionClientSystemArchitectureType +} + +func (o *OptClientArchType) ToBytes() []byte { + ret := []byte{byte(o.Code()), byte(o.Length())} + for _, at := range o.ArchTypes { + buf := make([]byte, 2) + binary.BigEndian.PutUint16(buf[0:2], uint16(at)) + ret = append(ret, buf...) + } + return ret +} + +func (o *OptClientArchType) Length() int { + return 2*len(o.ArchTypes) +} + +func (o *OptClientArchType) String() string { + var archTypes string + for idx, at := range o.ArchTypes { + name, ok := ArchTypeToStringMap[at] + if !ok { + name = "Unknown" + } + archTypes += name + if idx < len(o.ArchTypes)-1 { + archTypes += ", " + } + } + return fmt.Sprintf("Client System Architecture Type -> %v", archTypes) +} + +// ParseOptClientArchType builds an OptClientArchType structure from +// a sequence of bytes The input data does not include option code and +// length bytes. +func ParseOptClientArchType(data []byte) (*OptClientArchType, error) { + if len(data) < 2 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionClientSystemArchitectureType { + return nil, fmt.Errorf("expected code %v, got %v", OptionClientSystemArchitectureType, code) + } + length := int(data[1]) + if length == 0 || length%2 != 0 { + return nil, fmt.Errorf("Invalid length: expected multiple of 2 larger than 2, got %v", length) + } + if len(data) < 2+length { + return nil, ErrShortByteStream + } + archTypes := make([]ArchType, 0, length%2) + for idx := 0; idx < length; idx += 2 { + b := data[2+idx : 2+idx+2] + archTypes = append(archTypes, ArchType(binary.BigEndian.Uint16(b))) + } + return &OptClientArchType{ArchTypes: archTypes}, nil +} diff --git a/dhcpv4/option_archtype_test.go b/dhcpv4/option_archtype_test.go new file mode 100644 index 0000000..3805d5b --- /dev/null +++ b/dhcpv4/option_archtype_test.go @@ -0,0 +1,68 @@ +package dhcpv4 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseOptClientArchType(t *testing.T) { + data := []byte{ + 93, // OptionClientSystemArchitectureType + 2, // Length + 0, 6, // EFI_IA32 + } + opt, err := ParseOptClientArchType(data) + require.NoError(t, err) + require.Equal(t, opt.ArchTypes[0], EFI_IA32) +} + +func TestParseOptClientArchTypeMultiple(t *testing.T) { + data := []byte{ + 93, // OptionClientSystemArchitectureType + 4, // Length + 0, 6, // EFI_IA32 + 0, 2, // EFI_ITANIUM + } + opt, err := ParseOptClientArchType(data) + require.NoError(t, err) + require.Equal(t, opt.ArchTypes[0], EFI_IA32) + require.Equal(t, opt.ArchTypes[1], EFI_ITANIUM) +} + +func TestParseOptClientArchTypeInvalid(t *testing.T) { + data := []byte{42} + _, err := ParseOptClientArchType(data) + require.Error(t, err) +} + +func TestOptClientArchTypeParseAndToBytes(t *testing.T) { + data := []byte{ + 93, // OptionClientSystemArchitectureType + 2, // Length + 0, 8, // EFI_XSCALE + } + opt, err := ParseOptClientArchType(data) + require.NoError(t, err) + require.Equal(t, opt.ToBytes(), data) +} + +func TestOptClientArchTypeParseAndToBytesMultiple(t *testing.T) { + data := []byte{ + 93, // OptionClientSystemArchitectureType + 4, // Length + 0, 8, // EFI_XSCALE + 0, 6, // EFI_XSCALE + } + opt, err := ParseOptClientArchType(data) + require.NoError(t, err) + require.Equal(t, opt.ToBytes(), data) +} + +func TestOptClientArchType(t *testing.T) { + opt := OptClientArchType{ + ArchTypes: []ArchType{EFI_ITANIUM}, + } + require.Equal(t, opt.Length(), 2) + require.Equal(t, opt.Code(), OptionClientSystemArchitectureType) +} diff --git a/dhcpv4/options.go b/dhcpv4/options.go index 0be494f..d869b7d 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -44,8 +44,14 @@ func ParseOption(data []byte) (Option, error) { opt, err = ParseOptSubnetMask(data) case OptionRouter: opt, err = ParseOptRouter(data) + case OptionDomainNameServer: + opt, err = ParseOptDomainNameServer(data) case OptionHostName: opt, err = ParseOptHostName(data) + case OptionDomainName: + opt, err = ParseOptDomainName(data) + case OptionBroadcastAddress: + opt, err = ParseOptBroadcastAddress(data) case OptionNTPServers: opt, err = ParseOptNTPServers(data) case OptionRequestedIPAddress: @@ -54,28 +60,24 @@ func ParseOption(data []byte) (Option, error) { opt, err = ParseOptIPAddressLeaseTime(data) case OptionDHCPMessageType: opt, err = ParseOptMessageType(data) - case OptionParameterRequestList: - opt, err = ParseOptParameterRequestList(data) case OptionServerIdentifier: opt, err = ParseOptServerIdentifier(data) - case OptionBroadcastAddress: - opt, err = ParseOptBroadcastAddress(data) + case OptionParameterRequestList: + opt, err = ParseOptParameterRequestList(data) case OptionMaximumDHCPMessageSize: opt, err = ParseOptMaximumDHCPMessageSize(data) case OptionClassIdentifier: opt, err = ParseOptClassIdentifier(data) - case OptionDomainName: - opt, err = ParseOptDomainName(data) - case OptionDomainNameServer: - 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) + case OptionClientSystemArchitectureType: + opt, err = ParseOptClientArchType(data) + case OptionVendorIdentifyingVendorClass: + opt, err = ParseOptVIVC(data) case OptionDNSDomainSearchList: opt, err = ParseOptDomainSearch(data) default: diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go index b3d7605..0617eb7 100644 --- a/dhcpv4/options_test.go +++ b/dhcpv4/options_test.go @@ -41,14 +41,6 @@ func TestParseOption(t *testing.T) { require.Equal(t, 1, opt.Length(), "Length") require.Equal(t, option, opt.ToBytes(), "ToBytes") - // Parameter request list - option = []byte{55, 3, 5, 53, 61} - opt, err = ParseOption(option) - require.NoError(t, err) - require.Equal(t, OptionParameterRequestList, opt.Code(), "Code") - require.Equal(t, 3, opt.Length(), "Length") - require.Equal(t, option, opt.ToBytes(), "ToBytes") - // Option server ID option = []byte{54, 4, 1, 2, 3, 4} opt, err = ParseOption(option) @@ -57,6 +49,14 @@ func TestParseOption(t *testing.T) { require.Equal(t, 4, opt.Length(), "Length") require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Parameter request list + option = []byte{55, 3, 5, 53, 61} + opt, err = ParseOption(option) + require.NoError(t, err) + require.Equal(t, OptionParameterRequestList, opt.Code(), "Code") + require.Equal(t, 3, opt.Length(), "Length") + require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Option max message size option = []byte{57, 2, 1, 2} opt, err = ParseOption(option) -- cgit v1.2.3 From 1f8480c7ad48bab220f830c9c3ec02e43dae1d6e Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 15:21:33 +0200 Subject: OptClientArchType: add comments --- dhcpv4/option_archtype.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/dhcpv4/option_archtype.go b/dhcpv4/option_archtype.go index eb5b365..3121d36 100644 --- a/dhcpv4/option_archtype.go +++ b/dhcpv4/option_archtype.go @@ -39,15 +39,18 @@ var ArchTypeToStringMap = map[ArchType]string{ EFI_X86_64: "EFI x86-64", } -// OptClientArchType represents an option CLIENT_ARCH_TYPE +// OptRouter represents an option encapsulating the Client System Architecture +// Type option Definition. type OptClientArchType struct { ArchTypes []ArchType } +// Code returns the option code. func (o *OptClientArchType) Code() OptionCode { return OptionClientSystemArchitectureType } +// ToBytes returns a serialized stream of bytes for this option. func (o *OptClientArchType) ToBytes() []byte { ret := []byte{byte(o.Code()), byte(o.Length())} for _, at := range o.ArchTypes { @@ -58,10 +61,13 @@ func (o *OptClientArchType) ToBytes() []byte { return ret } +// Length returns the length of the data portion (excluding option code an byte +// length). func (o *OptClientArchType) Length() int { return 2*len(o.ArchTypes) } +// String returns a human-readable string. func (o *OptClientArchType) String() string { var archTypes string for idx, at := range o.ArchTypes { @@ -77,9 +83,8 @@ func (o *OptClientArchType) String() string { return fmt.Sprintf("Client System Architecture Type -> %v", archTypes) } -// ParseOptClientArchType builds an OptClientArchType structure from -// a sequence of bytes The input data does not include option code and -// length bytes. +// ParseOptClientArchType returns a new OptClientArchType from a byte stream, +// or error if any. func ParseOptClientArchType(data []byte) (*OptClientArchType, error) { if len(data) < 2 { return nil, ErrShortByteStream -- cgit v1.2.3 From 1045a3d39ec8341a089ab45286871b599216b7dd Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 15:23:33 +0200 Subject: OptClientArchType --- dhcpv4/option_archtype.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dhcpv4/option_archtype.go b/dhcpv4/option_archtype.go index 3121d36..16ca98d 100644 --- a/dhcpv4/option_archtype.go +++ b/dhcpv4/option_archtype.go @@ -39,8 +39,8 @@ var ArchTypeToStringMap = map[ArchType]string{ EFI_X86_64: "EFI x86-64", } -// OptRouter represents an option encapsulating the Client System Architecture -// Type option Definition. +// OptClientArchType represents an option encapsulating the Client System +// Architecture Type option Definition. type OptClientArchType struct { ArchTypes []ArchType } -- cgit v1.2.3 From d4f878f583acae9d6c5f5301c1e857f115cfc8fb Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 15:25:51 +0200 Subject: OptClientArchType --- dhcpv4/option_archtype_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhcpv4/option_archtype_test.go b/dhcpv4/option_archtype_test.go index 3805d5b..cb9bc3f 100644 --- a/dhcpv4/option_archtype_test.go +++ b/dhcpv4/option_archtype_test.go @@ -52,7 +52,7 @@ func TestOptClientArchTypeParseAndToBytesMultiple(t *testing.T) { 93, // OptionClientSystemArchitectureType 4, // Length 0, 8, // EFI_XSCALE - 0, 6, // EFI_XSCALE + 0, 6, // EFI_IA32 } opt, err := ParseOptClientArchType(data) require.NoError(t, err) -- cgit v1.2.3 From 3a192e93923b2ce2fcb4046994d058e557259ced Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 17:44:28 +0200 Subject: OptClientArchType --- dhcpv4/options_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go index 0617eb7..73b344b 100644 --- a/dhcpv4/options_test.go +++ b/dhcpv4/options_test.go @@ -96,6 +96,14 @@ func TestParseOption(t *testing.T) { require.Equal(t, OptionUserClassInformation, opt.Code(), "Code") require.Equal(t, 5, opt.Length(), "Length") require.Equal(t, option, opt.ToBytes(), "ToBytes") + + // Option client system architecture type option + option = []byte{93, 4, 't', 'e', 's', 't'} + opt, err = ParseOption(option) + require.NoError(t, err) + require.Equal(t, OptionClientSystemArchitectureType, opt.Code(), "Code") + require.Equal(t, 4, opt.Length(), "Length") + require.Equal(t, option, opt.ToBytes(), "ToBytes") } func TestParseOptionZeroLength(t *testing.T) { -- cgit v1.2.3 From 93881b9caf158750b4d98e3ab10413bbe28fee02 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 17:53:59 +0200 Subject: OptClientArchType: increase coverage --- dhcpv4/options_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go index 73b344b..be2791c 100644 --- a/dhcpv4/options_test.go +++ b/dhcpv4/options_test.go @@ -25,6 +25,14 @@ func TestParseOption(t *testing.T) { require.Equal(t, 4, opt.Length(), "Length") require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Option router + option = []byte{3, 4, 192, 168, 1, 1} + opt, err = ParseOption(option) + require.NoError(t, err) + require.Equal(t, OptionRouter, opt.Code(), "Code") + require.Equal(t, 4, opt.Length(), "Length") + require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Requested IP address option = []byte{50, 4, 1, 2, 3, 4} opt, err = ParseOption(option) -- cgit v1.2.3 From 54efe77e5afa90329d78cf8302b8eb8e0af3e898 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 18:58:47 +0200 Subject: OptClientArchType: increase coverage --- dhcpv4/options_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go index be2791c..b2d671a 100644 --- a/dhcpv4/options_test.go +++ b/dhcpv4/options_test.go @@ -33,6 +33,14 @@ func TestParseOption(t *testing.T) { require.Equal(t, 4, opt.Length(), "Length") require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Option domain name server + option = []byte{6, 4, 192, 168, 1, 1} + opt, err = ParseOption(option) + require.NoError(t, err) + require.Equal(t, OptionDomainNameServer, opt.Code(), "Code") + require.Equal(t, 4, opt.Length(), "Length") + require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Requested IP address option = []byte{50, 4, 1, 2, 3, 4} opt, err = ParseOption(option) -- cgit v1.2.3 From 63d7f1ef09968bc3007f26eae4c7a6e1ef4272f6 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 19:21:59 +0200 Subject: OptClientArchType: increase coverage --- dhcpv4/options_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go index b2d671a..9d3e88a 100644 --- a/dhcpv4/options_test.go +++ b/dhcpv4/options_test.go @@ -41,6 +41,14 @@ func TestParseOption(t *testing.T) { require.Equal(t, 4, opt.Length(), "Length") require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Option host name + option = []byte{12, 4, 't', 'e', 's', 't'} + opt, err = ParseOption(option) + require.NoError(t, err) + require.Equal(t, OptionHostName, opt.Code(), "Code") + require.Equal(t, 4, opt.Length(), "Length") + require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Requested IP address option = []byte{50, 4, 1, 2, 3, 4} opt, err = ParseOption(option) -- cgit v1.2.3 From 020d95df3ba020e7ae7753d15cb6ad085cdcf849 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 19:28:14 +0200 Subject: OptClientArchType: increase coverage --- dhcpv4/options_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go index 9d3e88a..3121f51 100644 --- a/dhcpv4/options_test.go +++ b/dhcpv4/options_test.go @@ -49,6 +49,14 @@ func TestParseOption(t *testing.T) { require.Equal(t, 4, opt.Length(), "Length") require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Option domain name + option = []byte{15, 4, 't', 'e', 's', 't'} + opt, err = ParseOption(option) + require.NoError(t, err) + require.Equal(t, OptionDomainName, opt.Code(), "Code") + require.Equal(t, 4, opt.Length(), "Length") + require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Requested IP address option = []byte{50, 4, 1, 2, 3, 4} opt, err = ParseOption(option) -- cgit v1.2.3 From e5d59324bd89596d9a3d88d972936d691d96a96c Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Thu, 2 Aug 2018 19:37:37 +0200 Subject: OptClientArchType: increase coverage --- dhcpv4/options_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go index 3121f51..0268483 100644 --- a/dhcpv4/options_test.go +++ b/dhcpv4/options_test.go @@ -57,6 +57,14 @@ func TestParseOption(t *testing.T) { require.Equal(t, 4, opt.Length(), "Length") require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Option NTP servers + option = []byte{42, 4, 10, 10, 10, 10} + opt, err = ParseOption(option) + require.NoError(t, err) + require.Equal(t, OptionNTPServers, opt.Code(), "Code") + require.Equal(t, 4, opt.Length(), "Length") + require.Equal(t, option, opt.ToBytes(), "ToBytes") + // Requested IP address option = []byte{50, 4, 1, 2, 3, 4} opt, err = ParseOption(option) -- cgit v1.2.3 From 5207d76712250f33111c546d9ace98336d616bfc Mon Sep 17 00:00:00 2001 From: Owen Mooney Date: Fri, 10 Aug 2018 13:33:46 +0100 Subject: Added modifier for netbootv4 (#124) --- dhcpv4/dhcpv4_test.go | 4 ++-- dhcpv4/modifiers.go | 46 ++++++++++++++++++++++++++++++++++---- dhcpv4/modifiers_test.go | 57 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 7 deletions(-) diff --git a/dhcpv4/dhcpv4_test.go b/dhcpv4/dhcpv4_test.go index 217c801..1bce05d 100644 --- a/dhcpv4/dhcpv4_test.go +++ b/dhcpv4/dhcpv4_test.go @@ -358,7 +358,7 @@ func TestDHCPv4RequestFromOfferWithModifier(t *testing.T) { require.NoError(t, err) offer.AddOption(&OptMessageType{MessageType: MessageTypeOffer}) offer.AddOption(&OptServerIdentifier{ServerID: net.IPv4(192, 168, 0, 1)}) - userClass := WithUserClass([]byte("linuxboot")) + userClass := WithUserClass([]byte("linuxboot"), false) req, err := RequestFromOffer(*offer, userClass) require.NoError(t, err) require.NotEqual(t, (*MessageType)(nil), *req.MessageType()) @@ -380,7 +380,7 @@ func TestNewReplyFromRequestWithModifier(t *testing.T) { discover, err := New() require.NoError(t, err) discover.SetGatewayIPAddr(net.IPv4(192, 168, 0, 1)) - userClass := WithUserClass([]byte("linuxboot")) + userClass := WithUserClass([]byte("linuxboot"), false) reply, err := NewReplyFromRequest(discover, userClass) require.NoError(t, err) require.Equal(t, discover.TransactionID(), reply.TransactionID()) diff --git a/dhcpv4/modifiers.go b/dhcpv4/modifiers.go index bc19219..3b6ce70 100644 --- a/dhcpv4/modifiers.go +++ b/dhcpv4/modifiers.go @@ -1,11 +1,49 @@ package dhcpv4 -// WithUserClass adds a user class option to the packet -func WithUserClass(uc []byte) Modifier { +// WithUserClass adds a user class option to the packet. +// The rfc parameter allows you to specify if the userclass should be +// rfc compliant or not. More details in issue #113 +func WithUserClass(uc []byte, rfc bool) Modifier { // TODO let the user specify multiple user classes return func(d *DHCPv4) *DHCPv4 { - ouc := OptUserClass{UserClasses: [][]byte{uc}} + ouc := OptUserClass{ + UserClasses: [][]byte{uc}, + Rfc3004: rfc, + } d.AddOption(&ouc) return d } -} \ No newline at end of file +} + +// WithNetboot adds bootfile URL and bootfile param options to a DHCPv4 packet. +func WithNetboot(d *DHCPv4) *DHCPv4 { + params := d.GetOneOption(OptionParameterRequestList) + + var ( + OptParams *OptParameterRequestList + foundOptionTFTPServerName bool + foundOptionBootfileName bool + ) + if params != nil { + OptParams = params.(*OptParameterRequestList) + for _, option := range OptParams.RequestedOpts { + if option == OptionTFTPServerName { + foundOptionTFTPServerName = true + } else if option == OptionBootfileName { + foundOptionBootfileName = true + } + } + if !foundOptionTFTPServerName { + OptParams.RequestedOpts = append(OptParams.RequestedOpts, OptionTFTPServerName) + } + if !foundOptionBootfileName { + OptParams.RequestedOpts = append(OptParams.RequestedOpts, OptionBootfileName) + } + } else { + OptParams = &OptParameterRequestList{ + RequestedOpts: []OptionCode{OptionTFTPServerName, OptionBootfileName}, + } + d.AddOption(OptParams) + } + return d +} diff --git a/dhcpv4/modifiers_test.go b/dhcpv4/modifiers_test.go index 2e249ee..2f0e1ed 100644 --- a/dhcpv4/modifiers_test.go +++ b/dhcpv4/modifiers_test.go @@ -8,7 +8,62 @@ import ( func TestUserClassModifier(t *testing.T) { d, _ := New() - userClass := WithUserClass([]byte("linuxboot")) + userClass := WithUserClass([]byte("linuxboot"), false) d = userClass(d) + expected := []byte{ + 77, // OptionUserClass + 9, // length + 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', + } require.Equal(t, "User Class Information -> linuxboot", d.options[0].String()) + require.Equal(t, expected, d.options[0].ToBytes()) +} + +func TestUserClassModifierRFC(t *testing.T) { + d, _ := New() + userClass := WithUserClass([]byte("linuxboot"), true) + d = userClass(d) + expected := []byte{ + 77, // OptionUserClass + 10, // length + 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', + } + require.Equal(t, "User Class Information -> linuxboot", d.options[0].String()) + require.Equal(t, expected, d.options[0].ToBytes()) +} + +func TestWithNetboot(t *testing.T) { + d, _ := New() + d = WithNetboot(d) + require.Equal(t, "Parameter Request List -> [TFTP Server Name, Bootfile Name]", d.options[0].String()) +} + +func TestWithNetbootExistingTFTP(t *testing.T) { + d, _ := New() + OptParams := &OptParameterRequestList{ + RequestedOpts: []OptionCode{OptionTFTPServerName}, + } + d.AddOption(OptParams) + d = WithNetboot(d) + require.Equal(t, "Parameter Request List -> [TFTP Server Name, Bootfile Name]", d.options[0].String()) +} + +func TestWithNetbootExistingBootfileName(t *testing.T) { + d, _ := New() + OptParams := &OptParameterRequestList{ + RequestedOpts: []OptionCode{OptionBootfileName}, + } + d.AddOption(OptParams) + d = WithNetboot(d) + require.Equal(t, "Parameter Request List -> [Bootfile Name, TFTP Server Name]", d.options[0].String()) +} + +func TestWithNetbootExistingBoth(t *testing.T) { + d, _ := New() + OptParams := &OptParameterRequestList{ + RequestedOpts: []OptionCode{OptionBootfileName, OptionTFTPServerName}, + } + d.AddOption(OptParams) + d = WithNetboot(d) + require.Equal(t, "Parameter Request List -> [Bootfile Name, TFTP Server Name]", d.options[0].String()) } -- cgit v1.2.3 From 45d999fb7b4230c0fa99b94136dfe13a06d924c8 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Fri, 3 Aug 2018 11:02:46 +0200 Subject: rename RequestFromOffer to NewRequestFromOffer --- dhcpv4/dhcpv4.go | 4 ++-- dhcpv4/dhcpv4_test.go | 8 ++++---- dhcpv4/option_bootfile_name.go | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dhcpv4/dhcpv4.go b/dhcpv4/dhcpv4.go index d670de2..5005375 100644 --- a/dhcpv4/dhcpv4.go +++ b/dhcpv4/dhcpv4.go @@ -187,8 +187,8 @@ func NewInformForInterface(ifname string, needsBroadcast bool) (*DHCPv4, error) return d, nil } -// RequestFromOffer builds a DHCPv4 request from an offer. -func RequestFromOffer(offer DHCPv4, modifiers ...Modifier) (*DHCPv4, error) { +// NewRequestFromOffer builds a DHCPv4 request from an offer. +func NewRequestFromOffer(offer *DHCPv4, modifiers ...Modifier) (*DHCPv4, error) { d, err := New() if err != nil { return nil, err diff --git a/dhcpv4/dhcpv4_test.go b/dhcpv4/dhcpv4_test.go index 1bce05d..6e3394c 100644 --- a/dhcpv4/dhcpv4_test.go +++ b/dhcpv4/dhcpv4_test.go @@ -342,24 +342,24 @@ func TestAddOption(t *testing.T) { require.Equal(t, options[3].Code(), OptionEnd) } -func TestDHCPv4RequestFromOffer(t *testing.T) { +func TestDHCPv4NewRequestFromOffer(t *testing.T) { offer, err := New() require.NoError(t, err) offer.AddOption(&OptMessageType{MessageType: MessageTypeOffer}) offer.AddOption(&OptServerIdentifier{ServerID: net.IPv4(192, 168, 0, 1)}) - req, err := RequestFromOffer(*offer) + req, err := NewRequestFromOffer(offer) require.NoError(t, err) require.NotEqual(t, (*MessageType)(nil), *req.MessageType()) require.Equal(t, MessageTypeRequest, *req.MessageType()) } -func TestDHCPv4RequestFromOfferWithModifier(t *testing.T) { +func TestDHCPv4NewRequestFromOfferWithModifier(t *testing.T) { offer, err := New() require.NoError(t, err) offer.AddOption(&OptMessageType{MessageType: MessageTypeOffer}) offer.AddOption(&OptServerIdentifier{ServerID: net.IPv4(192, 168, 0, 1)}) userClass := WithUserClass([]byte("linuxboot"), false) - req, err := RequestFromOffer(*offer, userClass) + req, err := NewRequestFromOffer(offer, userClass) require.NoError(t, err) require.NotEqual(t, (*MessageType)(nil), *req.MessageType()) require.Equal(t, MessageTypeRequest, *req.MessageType()) diff --git a/dhcpv4/option_bootfile_name.go b/dhcpv4/option_bootfile_name.go index 73ea625..5f1020a 100644 --- a/dhcpv4/option_bootfile_name.go +++ b/dhcpv4/option_bootfile_name.go @@ -28,7 +28,7 @@ func (op *OptBootfileName) Length() int { } func (op *OptBootfileName) String() string { - return fmt.Sprintf("OptBootfileName{BootfileName=%s}", op.BootfileName) + return fmt.Sprintf("Bootfile Name -> %v", op.BootfileName) } -- cgit v1.2.3 From 6539e106ea55e534d7afcc04df6828c7de9d4e8f Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Fri, 3 Aug 2018 11:09:40 +0200 Subject: OptTFTPServerName: fix String() --- dhcpv4/option_tftp_server_name.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhcpv4/option_tftp_server_name.go b/dhcpv4/option_tftp_server_name.go index b4029e1..886208d 100644 --- a/dhcpv4/option_tftp_server_name.go +++ b/dhcpv4/option_tftp_server_name.go @@ -28,7 +28,7 @@ func (op *OptTFTPServerName) Length() int { } func (op *OptTFTPServerName) String() string { - return fmt.Sprintf("OptTFTPServerName{TFTPServerName=%s}", op.TFTPServerName) + return fmt.Sprintf("TFTP Server Name -> %v", op.TFTPServerName) } // ParseOptTFTPServerName returns a new OptTFTPServerName fomr a byte stream or error if any -- cgit v1.2.3 From 782ff7524ce2fce9b098ad72c020a489735557a1 Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Fri, 3 Aug 2018 11:21:52 +0200 Subject: NewRequestFromOffer: update client --- dhcpv4/client.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dhcpv4/client.go b/dhcpv4/client.go index db7e71a..36ff60b 100644 --- a/dhcpv4/client.go +++ b/dhcpv4/client.go @@ -124,8 +124,8 @@ func MakeListeningSocket(ifname string) (int, error) { // ordered as Discovery, Offer, Request and Acknowledge. In case of errors, an // error is returned, and the list of DHCPv4 objects will be shorted than 4, // containing all the sent and received DHCPv4 messages. -func (c *Client) Exchange(ifname string, discover *DHCPv4, modifiers ...Modifier) ([]DHCPv4, error) { - conversation := make([]DHCPv4, 1) +func (c *Client) Exchange(ifname string, discover *DHCPv4, modifiers ...Modifier) ([]*DHCPv4, error) { + conversation := make([]*DHCPv4, 1) var err error // Get our file descriptor for the broadcast socket. @@ -155,21 +155,21 @@ func (c *Client) Exchange(ifname string, discover *DHCPv4, modifiers ...Modifier if err != nil { return conversation, err } - conversation = append(conversation, *offer) + conversation = append(conversation, offer) // Request - request, err := RequestFromOffer(*offer, modifiers...) + request, err := NewRequestFromOffer(offer, modifiers...) if err != nil { return conversation, err } - conversation = append(conversation, *request) + conversation = append(conversation, request) // Ack ack, err := BroadcastSendReceive(sfd, rfd, request, c.ReadTimeout, c.WriteTimeout, MessageTypeAck) if err != nil { return conversation, err } - conversation = append(conversation, *ack) + conversation = append(conversation, ack) return conversation, nil } -- cgit v1.2.3 From ba823f457ef09139f01168013de529ede7caf76b Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Fri, 3 Aug 2018 11:30:07 +0200 Subject: NewRequestFromOffer: update client --- dhcpv4/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dhcpv4/client.go b/dhcpv4/client.go index 36ff60b..9fd8501 100644 --- a/dhcpv4/client.go +++ b/dhcpv4/client.go @@ -148,7 +148,7 @@ func (c *Client) Exchange(ifname string, discover *DHCPv4, modifiers ...Modifier for _, mod := range modifiers { discover = mod(discover) } - conversation[0] = *discover + conversation[0] = discover // Offer offer, err := BroadcastSendReceive(sfd, rfd, discover, c.ReadTimeout, c.WriteTimeout, MessageTypeOffer) -- cgit v1.2.3 From 0edcc4a6721ffa792bbdc095134ac77e473510ee Mon Sep 17 00:00:00 2001 From: Pablo Mazzini Date: Fri, 3 Aug 2018 11:44:57 +0200 Subject: OptTFTPServerName and OptBootfileName test String --- dhcpv4/option_bootfile_name.go | 2 +- dhcpv4/option_bootfile_name_test.go | 5 +++++ dhcpv4/option_tftp_server_name.go | 2 +- dhcpv4/option_tftp_server_name_test.go | 5 +++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/dhcpv4/option_bootfile_name.go b/dhcpv4/option_bootfile_name.go index 5f1020a..ca9317b 100644 --- a/dhcpv4/option_bootfile_name.go +++ b/dhcpv4/option_bootfile_name.go @@ -28,7 +28,7 @@ func (op *OptBootfileName) Length() int { } func (op *OptBootfileName) String() string { - return fmt.Sprintf("Bootfile Name -> %v", op.BootfileName) + return fmt.Sprintf("Bootfile Name -> %s", op.BootfileName) } diff --git a/dhcpv4/option_bootfile_name_test.go b/dhcpv4/option_bootfile_name_test.go index 1f66807..0c7c200 100644 --- a/dhcpv4/option_bootfile_name_test.go +++ b/dhcpv4/option_bootfile_name_test.go @@ -58,3 +58,8 @@ func TestParseOptBootfileNameShortLength(t *testing.T) { require.NoError(t, err) require.Equal(t, []byte("linu"), opt.BootfileName) } + +func TestOptBootfileNameString(t *testing.T) { + o := OptBootfileName{BootfileName: []byte("testy test")} + require.Equal(t, "Bootfile Name -> testy test", o.String()) +} diff --git a/dhcpv4/option_tftp_server_name.go b/dhcpv4/option_tftp_server_name.go index 886208d..19dde21 100644 --- a/dhcpv4/option_tftp_server_name.go +++ b/dhcpv4/option_tftp_server_name.go @@ -28,7 +28,7 @@ func (op *OptTFTPServerName) Length() int { } func (op *OptTFTPServerName) String() string { - return fmt.Sprintf("TFTP Server Name -> %v", op.TFTPServerName) + return fmt.Sprintf("TFTP Server Name -> %s", op.TFTPServerName) } // ParseOptTFTPServerName returns a new OptTFTPServerName fomr a byte stream or error if any diff --git a/dhcpv4/option_tftp_server_name_test.go b/dhcpv4/option_tftp_server_name_test.go index caddf95..812210f 100644 --- a/dhcpv4/option_tftp_server_name_test.go +++ b/dhcpv4/option_tftp_server_name_test.go @@ -58,3 +58,8 @@ func TestParseOptTFTPServerNameShortLength(t *testing.T) { require.NoError(t, err) require.Equal(t, []byte("linu"), opt.TFTPServerName) } + +func TestOptTFTPServerNameString(t *testing.T) { + o := OptTFTPServerName{TFTPServerName: []byte("testy test")} + require.Equal(t, "TFTP Server Name -> testy test", o.String()) +} -- cgit v1.2.3