1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
package bsdp
import (
"errors"
"fmt"
"strings"
"github.com/insomniacslk/dhcp/dhcpv4"
)
// OptVendorSpecificInformation encapsulates the BSDP-specific options used for
// the protocol.
type OptVendorSpecificInformation struct {
Options []dhcpv4.Option
}
// parseOption is similar to dhcpv4.ParseOption, except that it switches based
// on the BSDP specific options.
func parseOption(data []byte) (dhcpv4.Option, error) {
if len(data) == 0 {
return nil, dhcpv4.ErrZeroLengthByteStream
}
var (
opt dhcpv4.Option
err error
)
switch dhcpv4.OptionCode(data[0]) {
case OptionBootImageList:
opt, err = ParseOptBootImageList(data)
case OptionDefaultBootImageID:
opt, err = ParseOptDefaultBootImageID(data)
case OptionMachineName:
opt, err = ParseOptMachineName(data)
case OptionMessageType:
opt, err = ParseOptMessageType(data)
case OptionReplyPort:
opt, err = ParseOptReplyPort(data)
case OptionSelectedBootImageID:
opt, err = ParseOptSelectedBootImageID(data)
case OptionServerIdentifier:
opt, err = ParseOptServerIdentifier(data)
case OptionServerPriority:
opt, err = ParseOptServerPriority(data)
case OptionVersion:
opt, err = ParseOptVersion(data)
default:
opt, err = ParseOptGeneric(data)
}
if err != nil {
return nil, err
}
return opt, nil
}
// ParseOptVendorSpecificInformation constructs an OptVendorSpecificInformation struct from a sequence of
// bytes and returns it, or an error.
func ParseOptVendorSpecificInformation(data []byte) (*OptVendorSpecificInformation, error) {
// Should at least have code + length
if len(data) < 2 {
return nil, dhcpv4.ErrShortByteStream
}
code := dhcpv4.OptionCode(data[0])
if code != dhcpv4.OptionVendorSpecificInformation {
return nil, fmt.Errorf("expected option %v, got %v instead", dhcpv4.OptionVendorSpecificInformation, code)
}
length := int(data[1])
if len(data) < length+2 {
return nil, fmt.Errorf("expected length 2, got %d instead", length)
}
options := make([]dhcpv4.Option, 0, 10)
idx := 2
for {
if idx == len(data) {
break
}
// This should never happen.
if idx > len(data) {
return nil, errors.New("read past the end of options")
}
opt, err := parseOption(data[idx:])
if err != nil {
return nil, err
}
options = append(options, opt)
// Account for code + length bytes
idx += 2 + opt.Length()
}
return &OptVendorSpecificInformation{options}, nil
}
// Code returns the option code.
func (o *OptVendorSpecificInformation) Code() dhcpv4.OptionCode {
return dhcpv4.OptionVendorSpecificInformation
}
// ToBytes returns a serialized stream of bytes for this option.
func (o *OptVendorSpecificInformation) ToBytes() []byte {
bs := []byte{byte(o.Code()), byte(o.Length())}
// Append data section
for _, opt := range o.Options {
bs = append(bs, opt.ToBytes()...)
}
return bs
}
// String returns a human-readable string for this option.
func (o *OptVendorSpecificInformation) String() string {
s := "Vendor Specific Information ->"
for _, opt := range o.Options {
optString := opt.String()
// If this option has sub-structures, offset them accordingly.
if strings.Contains(optString, "\n") {
optString = strings.Replace(optString, "\n ", "\n ", -1)
}
s += "\n " + optString
}
return s
}
// Length returns the length of the data portion of this option. Take into
// account code + data length bytes for each sub option.
func (o *OptVendorSpecificInformation) Length() int {
var length int
for _, opt := range o.Options {
length += 2 + opt.Length()
}
return length
}
// GetOption returns all suboptions that match the given OptionCode code.
func (o *OptVendorSpecificInformation) GetOption(code dhcpv4.OptionCode) []dhcpv4.Option {
var opts []dhcpv4.Option
for _, opt := range o.Options {
if opt.Code() == code {
opts = append(opts, opt)
}
}
return opts
}
// GetOneOption returns the first suboption that matches the OptionCode code.
func (o *OptVendorSpecificInformation) GetOneOption(code dhcpv4.OptionCode) dhcpv4.Option {
opts := o.GetOption(code)
if len(opts) == 0 {
return nil
}
return opts[0]
}
|