From 72283a6f755826142750f19accf06cb6b2e7ad4f Mon Sep 17 00:00:00 2001 From: Michael Haro Date: Tue, 30 Jul 2024 23:26:38 -0700 Subject: [PATCH 1/9] Decode more options into their appropriate types Signed-off-by: Michael Haro --- dhcpv4/options.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dhcpv4/options.go b/dhcpv4/options.go index 1e5fcbdb..b31a14b1 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -344,7 +344,8 @@ func getOption(code OptionCode, data []byte, vendorDecoder OptionDecoder) fmt.St d = &OptionCodeList{} case OptionHostName, OptionDomainName, OptionRootPath, - OptionClassIdentifier, OptionTFTPServerName, OptionBootfileName: + OptionClassIdentifier, OptionTFTPServerName, OptionBootfileName, + OptionMessage, OptionReferenceToTZDatabase: var s String d = &s @@ -354,7 +355,9 @@ func getOption(code OptionCode, data []byte, vendorDecoder OptionDecoder) fmt.St case OptionDNSDomainSearchList: d = &rfc1035label.Labels{} - case OptionIPAddressLeaseTime, OptionRenewTimeValue, OptionRebindingTimeValue, OptionIPv6OnlyPreferred: + case OptionIPAddressLeaseTime, OptionRenewTimeValue, + OptionRebindingTimeValue, OptionIPv6OnlyPreferred, OptionArpCacheTimeout, + OptionTimeOffset: var dur Duration d = &dur From 175cf2aa7c9eca6f4bfb7a34552159fff192ba72 Mon Sep 17 00:00:00 2001 From: Michael Haro Date: Tue, 30 Jul 2024 23:27:23 -0700 Subject: [PATCH 2/9] Humanize option82 suboptions Signed-off-by: Michael Haro --- dhcpv4/option_relay_agent_information.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dhcpv4/option_relay_agent_information.go b/dhcpv4/option_relay_agent_information.go index 4f974dd9..d26367ad 100644 --- a/dhcpv4/option_relay_agent_information.go +++ b/dhcpv4/option_relay_agent_information.go @@ -12,6 +12,14 @@ type RelayOptions struct { var relayHumanizer = OptionHumanizer{ ValueHumanizer: func(code OptionCode, data []byte) fmt.Stringer { + var d OptionDecoder + switch code { + case LinkSelectionSubOption, ServerIdentifierOverrideSubOption: + d = &IPs{} + } + if d != nil && d.FromBytes(data) == nil { + return d + } return raiSubOptionValue{data} }, CodeHumanizer: func(c uint8) OptionCode { From 4322ec4a67779a28508d6437b0fc11989e9846a5 Mon Sep 17 00:00:00 2001 From: Michael Haro Date: Tue, 30 Jul 2024 23:50:03 -0700 Subject: [PATCH 3/9] Add a test case for opt82 humanization Signed-off-by: Michael Haro --- dhcpv4/option_relay_agent_information_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dhcpv4/option_relay_agent_information_test.go b/dhcpv4/option_relay_agent_information_test.go index a875d14e..a0e12bcb 100644 --- a/dhcpv4/option_relay_agent_information_test.go +++ b/dhcpv4/option_relay_agent_information_test.go @@ -36,12 +36,14 @@ func TestOptRelayAgentInfo(t *testing.T) { opt := OptRelayAgentInfo( OptGeneric(GenericOptionCode(1), []byte("linux")), OptGeneric(GenericOptionCode(2), []byte("boot")), + OptGeneric(GenericOptionCode(LinkSelectionSubOption), []byte{192, 0, 2, 1}), ) wantBytes := []byte{ 1, 5, 'l', 'i', 'n', 'u', 'x', 2, 4, 'b', 'o', 'o', 't', + 5, 4, 192, 0, 2, 1, } - wantString := "Relay Agent Information:\n\n Agent Circuit ID Sub-option: linux ([108 105 110 117 120])\n Agent Remote ID Sub-option: boot ([98 111 111 116])\n" + wantString := "Relay Agent Information:\n\n Agent Circuit ID Sub-option: linux ([108 105 110 117 120])\n Agent Remote ID Sub-option: boot ([98 111 111 116])\n Link Selection Sub-option: 192.0.2.1\n" require.Equal(t, wantBytes, opt.Value.ToBytes()) require.Equal(t, OptionRelayAgentInformation, opt.Code) require.Equal(t, wantString, opt.String()) From add14894cac60520f2d08d723bc14012cbe4e6c3 Mon Sep 17 00:00:00 2001 From: Bogdan-Denisenko Date: Mon, 26 Aug 2024 10:12:57 +0000 Subject: [PATCH 4/9] Remove zero padding check Signed-off-by: Bogdan-Denisenko --- dhcpv4/options.go | 13 ------------- dhcpv4/options_test.go | 6 +++--- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/dhcpv4/options.go b/dhcpv4/options.go index b31a14b1..18668291 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -21,11 +21,6 @@ var ( // ErrZeroLengthByteStream is an error that is thrown any time a zero-length // byte stream is encountered. ErrZeroLengthByteStream = errors.New("zero-length byte stream") - - // ErrInvalidOptions is returned when invalid options data is - // encountered during parsing. The data could report an incorrect - // length or have trailing bytes which are not part of the option. - ErrInvalidOptions = errors.New("invalid options data") ) // OptionValue is an interface that all DHCP v4 options adhere to. @@ -161,14 +156,6 @@ func (o Options) fromBytesCheckEnd(data []byte, checkEndOption bool) error { return io.ErrUnexpectedEOF } - // Any bytes left must be padding. - var pad uint8 - for buf.Len() >= 1 { - pad = buf.Read8() - if pad != optPad && pad != optEnd { - return ErrInvalidOptions - } - } return nil } diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go index ff11f888..c748c2e9 100644 --- a/dhcpv4/options_test.go +++ b/dhcpv4/options_test.go @@ -255,9 +255,9 @@ func TestOptionsUnmarshal(t *testing.T) { wantError: true, }, { - // Option present after the End is a nono. - input: []byte{byte(OptionEnd), 3}, - wantError: true, + // Option present after the End. + input: []byte{byte(OptionEnd), 3}, + want: Options{}, }, { input: []byte{byte(OptionEnd)}, From a3a4c1f04475c0dbe037fd87d5363c2ea13005b8 Mon Sep 17 00:00:00 2001 From: Michael Haro Date: Tue, 27 Aug 2024 21:29:13 -0700 Subject: [PATCH 5/9] Escape agent options containing binary when stringifying Printing strings that contain binary messes up my terminal. Using %q instead of %s will result in the binary being escaped. Signed-off-by: Michael Haro --- dhcpv4/option_relay_agent_information.go | 2 +- dhcpv4/option_relay_agent_information_test.go | 4 +++- dhcpv4/options_test.go | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dhcpv4/option_relay_agent_information.go b/dhcpv4/option_relay_agent_information.go index d26367ad..451402f5 100644 --- a/dhcpv4/option_relay_agent_information.go +++ b/dhcpv4/option_relay_agent_information.go @@ -50,7 +50,7 @@ type raiSubOptionValue struct { } func (rv raiSubOptionValue) String() string { - return fmt.Sprintf("%s (%v)", string(rv.val), rv.val) + return fmt.Sprintf("%q (%v)", string(rv.val), rv.val) } type raiSubOptionCode uint8 diff --git a/dhcpv4/option_relay_agent_information_test.go b/dhcpv4/option_relay_agent_information_test.go index a0e12bcb..eef5b9d1 100644 --- a/dhcpv4/option_relay_agent_information_test.go +++ b/dhcpv4/option_relay_agent_information_test.go @@ -36,14 +36,16 @@ func TestOptRelayAgentInfo(t *testing.T) { opt := OptRelayAgentInfo( OptGeneric(GenericOptionCode(1), []byte("linux")), OptGeneric(GenericOptionCode(2), []byte("boot")), + OptGeneric(GenericOptionCode(3), []byte{2, 30, 99}), OptGeneric(GenericOptionCode(LinkSelectionSubOption), []byte{192, 0, 2, 1}), ) wantBytes := []byte{ 1, 5, 'l', 'i', 'n', 'u', 'x', 2, 4, 'b', 'o', 'o', 't', + 3, 3, 2, 30, 99, 5, 4, 192, 0, 2, 1, } - wantString := "Relay Agent Information:\n\n Agent Circuit ID Sub-option: linux ([108 105 110 117 120])\n Agent Remote ID Sub-option: boot ([98 111 111 116])\n Link Selection Sub-option: 192.0.2.1\n" + wantString := "Relay Agent Information:\n\n Agent Circuit ID Sub-option: \"linux\" ([108 105 110 117 120])\n Agent Remote ID Sub-option: \"boot\" ([98 111 111 116])\n unknown (3): \"\\x02\\x1ec\" ([2 30 99])\n Link Selection Sub-option: 192.0.2.1\n" require.Equal(t, wantBytes, opt.Value.ToBytes()) require.Equal(t, OptionRelayAgentInformation, opt.Code) require.Equal(t, wantString, opt.String()) diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go index c748c2e9..6d731fb6 100644 --- a/dhcpv4/options_test.go +++ b/dhcpv4/options_test.go @@ -119,7 +119,7 @@ func TestParseOption(t *testing.T) { { code: OptionRelayAgentInformation, value: []byte{1, 12, 99, 105, 114, 99, 117, 105, 116, 45, 105, 100, 45, 49}, - want: "\n Agent Circuit ID Sub-option: circuit-id-1 ([99 105 114 99 117 105 116 45 105 100 45 49])\n", + want: "\n Agent Circuit ID Sub-option: \"circuit-id-1\" ([99 105 114 99 117 105 116 45 105 100 45 49])\n", }, { code: OptionClientSystemArchitectureType, From 8aeff6ebe500f5029fa7a5baad63ae193cbb5a1a Mon Sep 17 00:00:00 2001 From: Gonzalo Fernandez-Victorio Date: Sat, 30 Nov 2024 21:41:31 +0000 Subject: [PATCH 6/9] Basic windows listening A very basic implementation of listening UDP connections in Windows. It is based on https://github.com/insomniacslk/dhcp/issues/521#issuecomment-1937470810 It works good enough. There is probably something missing, but a windows binary using it is capable at listening. Signed-off-by: Gonzalo Fernandez-Victorio --- dhcpv4/server4/conn_windows.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/dhcpv4/server4/conn_windows.go b/dhcpv4/server4/conn_windows.go index cbe9d71b..ce4f8688 100644 --- a/dhcpv4/server4/conn_windows.go +++ b/dhcpv4/server4/conn_windows.go @@ -1,11 +1,21 @@ +//go:build windows + package server4 import ( - "errors" + "fmt" "net" ) -// NewIPv4UDPConn fails on Windows. Use WithConn() to pass the connection. +// NewIPv4UDPConn returns an UDPv4 connection bound to the IP and port provider func NewIPv4UDPConn(iface string, addr *net.UDPAddr) (*net.UDPConn, error) { - return nil, errors.New("not implemented on Windows") + connection, err := net.ListenPacket("udp4", addr.String()) + if err != nil { + return nil, fmt.Errorf("We cannot listen on %s and port %d: %v", addr.IP, addr.Port, err) + } + udpConn, ok := connection.(*net.UDPConn) + if !ok { + return nil, fmt.Errorf("The connection is not of the proper type") + } + return udpConn, nil } From ffcd2d25cd3dbbcfacd2e7d7b1001bfdcf6de3ba Mon Sep 17 00:00:00 2001 From: Andrea Barberio Date: Thu, 19 Dec 2024 09:19:02 +0100 Subject: [PATCH 7/9] Update golang.org/x/net Addresses https://github.com/insomniacslk/dhcp/security/dependabot/14 Signed-off-by: Andrea Barberio --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index f93fee90..45914480 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,8 @@ require ( github.com/mdlayher/packet v1.1.2 github.com/stretchr/testify v1.6.1 github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 - golang.org/x/net v0.23.0 - golang.org/x/sys v0.18.0 + golang.org/x/net v0.33.0 + golang.org/x/sys v0.28.0 ) require ( diff --git a/go.sum b/go.sum index 6b08a535..3e4399f3 100644 --- a/go.sum +++ b/go.sum @@ -27,13 +27,13 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 5d1eceae760f29e4b629a7c1bd5d11527ebceee8 Mon Sep 17 00:00:00 2001 From: Nikita Usatov Date: Mon, 23 Dec 2024 18:00:17 +0300 Subject: [PATCH 8/9] dhcpv4: Add Opt function for NetBIOS Name Servers Signed-off-by: Nikita Usatov --- dhcpv4/dhcpv4.go | 7 +++++++ dhcpv4/option_ips.go | 10 ++++++++++ dhcpv4/option_ips_test.go | 19 +++++++++++++++++++ dhcpv4/types.go | 4 ++-- 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/dhcpv4/dhcpv4.go b/dhcpv4/dhcpv4.go index a8754312..2b9a2fc9 100644 --- a/dhcpv4/dhcpv4.go +++ b/dhcpv4/dhcpv4.go @@ -602,6 +602,13 @@ func (d *DHCPv4) NTPServers() []net.IP { return GetIPs(OptionNTPServers, d.Options) } +// NetBIOSNameServers parses the DHCPv4 NetBIOS Name Servers option if present. +// +// The NetBIOS over TCP/IP Name Server option is described by RFC 2132, Section 8.5. +func (d *DHCPv4) NetBIOSNameServers() []net.IP { + return GetIPs(OptionNetBIOSOverTCPIPNameServers, d.Options) +} + // DNS parses the DHCPv4 Domain Name Server option if present. // // The DNS server option is described by RFC 2132, Section 3.8. diff --git a/dhcpv4/option_ips.go b/dhcpv4/option_ips.go index e0ee4cd0..2778e64d 100644 --- a/dhcpv4/option_ips.go +++ b/dhcpv4/option_ips.go @@ -87,6 +87,16 @@ func OptNTPServers(ntpServers ...net.IP) Option { } } +// OptNetBIOSNameServers returns a new DHCPv4 NetBIOS Name Server option. +// +// The NetBIOS over TCP/IP Name Server option is described by RFC 2132, Section 8.5. +func OptNetBIOSNameServers(netBIOSNameServers ...net.IP) Option { + return Option{ + Code: OptionNetBIOSOverTCPIPNameServers, + Value: IPs(netBIOSNameServers), + } +} + // OptDNS returns a new DHCPv4 Domain Name Server option. // // The DNS server option is described by RFC 2132, Section 3.8. diff --git a/dhcpv4/option_ips_test.go b/dhcpv4/option_ips_test.go index 05b29393..2d20a40b 100644 --- a/dhcpv4/option_ips_test.go +++ b/dhcpv4/option_ips_test.go @@ -69,6 +69,25 @@ func TestGetNTPServers(t *testing.T) { require.Nil(t, m.NTPServers()) } +func TestOptNetBIOSNameServers(t *testing.T) { + o := OptNetBIOSNameServers(net.IPv4(192, 168, 0, 1), net.IPv4(192, 168, 0, 10)) + require.Equal(t, OptionNetBIOSOverTCPIPNameServers, o.Code) + require.Equal(t, []byte{192, 168, 0, 1, 192, 168, 0, 10}, o.Value.ToBytes()) + require.Equal(t, "NetBIOS over TCP/IP Name Servers: 192.168.0.1, 192.168.0.10", o.String()) +} + +func TestGetNetBIOSNameServers(t *testing.T) { + ips := []net.IP{ + net.IP{192, 168, 0, 1}, + net.IP{192, 168, 0, 10}, + } + m, _ := New(WithOption(OptNetBIOSNameServers(ips...))) + require.Equal(t, ips, m.NetBIOSNameServers()) + + m, _ = New() + require.Nil(t, m.NetBIOSNameServers()) +} + func TestOptRouter(t *testing.T) { o := OptRouter(net.IPv4(192, 168, 0, 1), net.IPv4(192, 168, 0, 10)) require.Equal(t, OptionRouter, o.Code) diff --git a/dhcpv4/types.go b/dhcpv4/types.go index 80ea49cf..35f323d1 100644 --- a/dhcpv4/types.go +++ b/dhcpv4/types.go @@ -180,7 +180,7 @@ const ( OptionNetworkInformationServers optionCode = 41 OptionNTPServers optionCode = 42 OptionVendorSpecificInformation optionCode = 43 - OptionNetBIOSOverTCPIPNameServer optionCode = 44 + OptionNetBIOSOverTCPIPNameServers optionCode = 44 OptionNetBIOSOverTCPIPDatagramDistributionServer optionCode = 45 OptionNetBIOSOverTCPIPNodeType optionCode = 46 OptionNetBIOSOverTCPIPScope optionCode = 47 @@ -345,7 +345,7 @@ var optionCodeToString = map[OptionCode]string{ OptionNetworkInformationServers: "Network Information Servers", OptionNTPServers: "NTP Servers", OptionVendorSpecificInformation: "Vendor Specific Information", - OptionNetBIOSOverTCPIPNameServer: "NetBIOS over TCP/IP Name Server", + OptionNetBIOSOverTCPIPNameServers: "NetBIOS over TCP/IP Name Servers", OptionNetBIOSOverTCPIPDatagramDistributionServer: "NetBIOS over TCP/IP Datagram Distribution Server", OptionNetBIOSOverTCPIPNodeType: "NetBIOS over TCP/IP Node Type", OptionNetBIOSOverTCPIPScope: "NetBIOS over TCP/IP Scope", From a2d20506697ef2c7feba84c0ca4d3d7ffce6e069 Mon Sep 17 00:00:00 2001 From: Nikita Usatov Date: Mon, 23 Dec 2024 19:19:56 +0300 Subject: [PATCH 9/9] Remove backward incompatible change Signed-off-by: Nikita Usatov --- dhcpv4/dhcpv4.go | 2 +- dhcpv4/option_ips.go | 2 +- dhcpv4/option_ips_test.go | 4 ++-- dhcpv4/types.go | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dhcpv4/dhcpv4.go b/dhcpv4/dhcpv4.go index 2b9a2fc9..36c726f6 100644 --- a/dhcpv4/dhcpv4.go +++ b/dhcpv4/dhcpv4.go @@ -606,7 +606,7 @@ func (d *DHCPv4) NTPServers() []net.IP { // // The NetBIOS over TCP/IP Name Server option is described by RFC 2132, Section 8.5. func (d *DHCPv4) NetBIOSNameServers() []net.IP { - return GetIPs(OptionNetBIOSOverTCPIPNameServers, d.Options) + return GetIPs(OptionNetBIOSOverTCPIPNameServer, d.Options) } // DNS parses the DHCPv4 Domain Name Server option if present. diff --git a/dhcpv4/option_ips.go b/dhcpv4/option_ips.go index 2778e64d..cbb81a22 100644 --- a/dhcpv4/option_ips.go +++ b/dhcpv4/option_ips.go @@ -92,7 +92,7 @@ func OptNTPServers(ntpServers ...net.IP) Option { // The NetBIOS over TCP/IP Name Server option is described by RFC 2132, Section 8.5. func OptNetBIOSNameServers(netBIOSNameServers ...net.IP) Option { return Option{ - Code: OptionNetBIOSOverTCPIPNameServers, + Code: OptionNetBIOSOverTCPIPNameServer, Value: IPs(netBIOSNameServers), } } diff --git a/dhcpv4/option_ips_test.go b/dhcpv4/option_ips_test.go index 2d20a40b..3fb91a9a 100644 --- a/dhcpv4/option_ips_test.go +++ b/dhcpv4/option_ips_test.go @@ -71,9 +71,9 @@ func TestGetNTPServers(t *testing.T) { func TestOptNetBIOSNameServers(t *testing.T) { o := OptNetBIOSNameServers(net.IPv4(192, 168, 0, 1), net.IPv4(192, 168, 0, 10)) - require.Equal(t, OptionNetBIOSOverTCPIPNameServers, o.Code) + require.Equal(t, OptionNetBIOSOverTCPIPNameServer, o.Code) require.Equal(t, []byte{192, 168, 0, 1, 192, 168, 0, 10}, o.Value.ToBytes()) - require.Equal(t, "NetBIOS over TCP/IP Name Servers: 192.168.0.1, 192.168.0.10", o.String()) + require.Equal(t, "NetBIOS over TCP/IP Name Server: 192.168.0.1, 192.168.0.10", o.String()) } func TestGetNetBIOSNameServers(t *testing.T) { diff --git a/dhcpv4/types.go b/dhcpv4/types.go index 35f323d1..80ea49cf 100644 --- a/dhcpv4/types.go +++ b/dhcpv4/types.go @@ -180,7 +180,7 @@ const ( OptionNetworkInformationServers optionCode = 41 OptionNTPServers optionCode = 42 OptionVendorSpecificInformation optionCode = 43 - OptionNetBIOSOverTCPIPNameServers optionCode = 44 + OptionNetBIOSOverTCPIPNameServer optionCode = 44 OptionNetBIOSOverTCPIPDatagramDistributionServer optionCode = 45 OptionNetBIOSOverTCPIPNodeType optionCode = 46 OptionNetBIOSOverTCPIPScope optionCode = 47 @@ -345,7 +345,7 @@ var optionCodeToString = map[OptionCode]string{ OptionNetworkInformationServers: "Network Information Servers", OptionNTPServers: "NTP Servers", OptionVendorSpecificInformation: "Vendor Specific Information", - OptionNetBIOSOverTCPIPNameServers: "NetBIOS over TCP/IP Name Servers", + OptionNetBIOSOverTCPIPNameServer: "NetBIOS over TCP/IP Name Server", OptionNetBIOSOverTCPIPDatagramDistributionServer: "NetBIOS over TCP/IP Datagram Distribution Server", OptionNetBIOSOverTCPIPNodeType: "NetBIOS over TCP/IP Node Type", OptionNetBIOSOverTCPIPScope: "NetBIOS over TCP/IP Scope",