github.com/transparency-dev/armored-witness-applet@v0.1.1/third_party/dhcp/dhcp.go (about) 1 // Copyright 2019 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 //go:build !lint 6 // +build !lint 7 8 // Package dhcp implements a DHCP client and server as described in RFC 2131. 9 package dhcp 10 11 import ( 12 "bytes" 13 "encoding/binary" 14 "fmt" 15 "strings" 16 "time" 17 18 "gvisor.dev/gvisor/pkg/tcpip" 19 ) 20 21 // Seconds represents a time duration in seconds. 22 // 23 // RFC 2131 Section 3.3 24 // https://tools.ietf.org/html/rfc2131#section-3.3 25 // 26 // Throughout the protocol, times are to be represented in units of seconds. 27 // 28 // Representing relative times in units of seconds in an unsigned 32 bit 29 // word gives a range of relative times from 0 to approximately 100 years, 30 // which is sufficient for the relative times to be measured using DHCP. 31 type Seconds uint32 32 33 func (s Seconds) String() string { 34 return s.Duration().String() 35 } 36 37 func (s Seconds) Duration() time.Duration { 38 return time.Duration(s) * time.Second 39 } 40 41 // Config is standard DHCP configuration. 42 type Config struct { 43 ServerAddress tcpip.Address // address of the server 44 SubnetMask tcpip.AddressMask // client address subnet mask 45 Router []tcpip.Address // client router addresses 46 DNS []tcpip.Address // client DNS server addresses 47 LeaseLength Seconds // length of the address lease 48 RenewTime Seconds // time until client enters RENEWING state 49 RebindTime Seconds // time until client enters REBINDING state 50 } 51 52 func (cfg *Config) decode(opts []option) error { 53 *cfg = Config{} 54 for _, opt := range opts { 55 b := opt.body 56 if !opt.code.lenValid(len(b)) { 57 return fmt.Errorf("%s: bad length: %d", opt.code, len(b)) 58 } 59 switch opt.code { 60 case optLeaseTime: 61 cfg.LeaseLength = Seconds(binary.BigEndian.Uint32(b)) 62 case optRenewalTime: 63 cfg.RenewTime = Seconds(binary.BigEndian.Uint32(b)) 64 case optRebindingTime: 65 cfg.RebindTime = Seconds(binary.BigEndian.Uint32(b)) 66 case optSubnetMask: 67 cfg.SubnetMask = tcpip.MaskFromBytes(b) 68 case optDHCPServer: 69 cfg.ServerAddress = tcpip.AddrFromSlice(b) 70 case optRouter: 71 for len(b) != 0 { 72 cfg.Router = append(cfg.Router, tcpip.AddrFromSlice(b[:4])) 73 b = b[4:] 74 } 75 case optDomainNameServer: 76 for len(b) != 0 { 77 cfg.DNS = append(cfg.DNS, tcpip.AddrFromSlice(b[:4])) 78 b = b[4:] 79 } 80 } 81 } 82 return nil 83 } 84 85 func (cfg Config) encode() (opts []option) { 86 if cfg.ServerAddress.Len() > 0 { 87 opts = append(opts, option{optDHCPServer, []byte(cfg.ServerAddress.AsSlice())}) 88 } 89 if cfg.SubnetMask.Len() > 0 { 90 opts = append(opts, option{optSubnetMask, []byte(cfg.SubnetMask.AsSlice())}) 91 } 92 if len(cfg.Router) > 0 { 93 router := make([]byte, 0, 4*len(cfg.Router)) 94 for _, addr := range cfg.Router { 95 router = append(router, addr.AsSlice()...) 96 } 97 opts = append(opts, option{optRouter, router}) 98 } 99 if len(cfg.DNS) > 0 { 100 dns := make([]byte, 0, 4*len(cfg.DNS)) 101 for _, addr := range cfg.DNS { 102 dns = append(dns, addr.AsSlice()...) 103 } 104 opts = append(opts, option{optDomainNameServer, dns}) 105 } 106 if l := cfg.LeaseLength; l != 0 { 107 opts = append(opts, serializeLeaseOption(l, optLeaseTime)) 108 } 109 if r := cfg.RebindTime; r != 0 { 110 opts = append(opts, serializeLeaseOption(r, optRebindingTime)) 111 } 112 if r := cfg.RenewTime; r != 0 { 113 opts = append(opts, serializeLeaseOption(r, optRenewalTime)) 114 } 115 return opts 116 } 117 118 func serializeLeaseOption(s Seconds, o optionCode) option { 119 v := make([]byte, 4) 120 binary.BigEndian.PutUint32(v, uint32(s)) 121 return option{o, v} 122 } 123 124 const ( 125 // ServerPort is the well-known UDP port number for a DHCP server. 126 ServerPort = 67 127 // ClientPort is the well-known UDP port number for a DHCP client. 128 ClientPort = 68 129 130 dhcpMinimumSize = 241 131 ) 132 133 var magicCookie = []byte{99, 130, 83, 99} // RFC 1497 134 135 type xid uint32 136 137 type hdr []byte 138 139 func (h hdr) init() { 140 h[1] = 0x01 // htype 141 h[2] = 0x06 // hlen 142 h[3] = 0x00 // hops 143 h[8], h[9] = 0, 0 // secs 144 copy(h[236:240], magicCookie) 145 } 146 147 func (h hdr) isValid() bool { 148 if len(h) < dhcpMinimumSize { 149 return false 150 } 151 if o := h.op(); o != opRequest && o != opReply { 152 return false 153 } 154 if !bytes.Equal(h[1:3], []byte{0x1, 0x6}) { 155 return false 156 } 157 return bytes.Equal(h[236:240], magicCookie) 158 } 159 160 func (h hdr) op() op { return op(h[0]) } 161 func (h hdr) setOp(o op) { h[0] = byte(o) } 162 func (h hdr) xidbytes() []byte { return h[4:8] } 163 func (h hdr) xid() xid { return xid(h[4])<<24 | xid(h[5])<<16 | xid(h[6])<<8 | xid(h[7]) } 164 func (h hdr) setBroadcast() { h[10] |= 1 << 7 } 165 func (h hdr) broadcast() bool { return h[10]&1<<7 != 0 } 166 func (h hdr) ciaddr() []byte { return h[12:16] } 167 func (h hdr) yiaddr() []byte { return h[16:20] } 168 func (h hdr) siaddr() []byte { return h[20:24] } 169 func (h hdr) giaddr() []byte { return h[24:28] } 170 func (h hdr) chaddr() []byte { return h[28:44] } 171 func (h hdr) sname() []byte { return h[44:108] } 172 func (h hdr) file() []byte { return h[108:236] } 173 174 func (h hdr) options() (opts options, err error) { 175 i := headerBaseSize 176 for i < len(h) { 177 if h[i] == 0 { 178 i++ 179 continue 180 } 181 if h[i] == 255 { 182 break 183 } 184 code := optionCode(h[i]) 185 i++ 186 if len(h) < i+1 { 187 return nil, fmt.Errorf("option %s missing length i=%d", code, i) 188 } 189 optlen := int(h[i]) 190 i++ 191 if len(h) < i+optlen { 192 return nil, fmt.Errorf("option %s too long i=%d, optlen=%d", code, i, optlen) 193 } 194 opts = append(opts, option{ 195 code: code, 196 body: h[i:][:optlen], 197 }) 198 i += optlen 199 } 200 return opts, nil 201 } 202 203 func (h hdr) setOptions(opts []option) { 204 i := headerBaseSize 205 for _, opt := range opts { 206 h[i] = byte(opt.code) 207 h[i+1] = byte(len(opt.body)) 208 copy(h[i+2:i+2+len(opt.body)], opt.body) 209 i += 2 + len(opt.body) 210 } 211 h[i] = 255 // End option 212 i++ 213 for ; i < len(h); i++ { 214 h[i] = 0 215 } 216 } 217 218 func (h hdr) String() string { 219 var buf strings.Builder 220 if _, err := fmt.Fprintf(&buf, "len=%d", len(h)); err != nil { 221 panic(err) 222 } 223 if !h.isValid() { 224 return fmt.Sprintf("DHCP invalid; %s; %x", buf.String(), []byte(h)) 225 } 226 opts, err := h.options() 227 if err != nil { 228 return fmt.Sprintf("DHCP options=%s; %s; %x", err, buf.String(), []byte(h)) 229 } 230 buf.WriteString(";options=") 231 for i, opt := range opts { 232 if i > 0 { 233 buf.WriteByte(',') 234 } 235 buf.WriteString(opt.String()) 236 } 237 msgType, err := opts.dhcpMsgType() 238 if err != nil { 239 return fmt.Sprintf("DHCP type=%s; %s; %x", err, buf.String(), []byte(h)) 240 } 241 if _, err := fmt.Fprintf( 242 &buf, 243 "type=%s;ciaddr=%s;yiaddr=%s;siaddr=%s;giaddr=%s;chaddr=%x", 244 msgType, 245 tcpip.AddrFromSlice(h.ciaddr()), 246 tcpip.AddrFromSlice(h.yiaddr()), 247 tcpip.AddrFromSlice(h.siaddr()), 248 tcpip.AddrFromSlice(h.giaddr()), 249 h.chaddr(), 250 ); err != nil { 251 panic(err) 252 } 253 return buf.String() 254 } 255 256 // headerBaseSize is the size of a DHCP packet, including the magic cookie. 257 // 258 // Note that a DHCP packet is required to have an 'end' option that takes 259 // up an extra byte, so the minimum DHCP packet size is headerBaseSize + 1. 260 const headerBaseSize = 240 261 262 type option struct { 263 code optionCode 264 body []byte 265 } 266 267 func (opt option) String() string { 268 return fmt.Sprintf("%s: %x", opt.code, opt.body) 269 } 270 271 type optionCode byte 272 273 const ( 274 optSubnetMask optionCode = 1 275 // RFC 2132 section 3.5: 276 // 3.5. Router Option 277 // 278 // The router option specifies a list of IP addresses for routers on the 279 // client's subnet. Routers SHOULD be listed in order of preference. 280 // 281 // The code for the router option is 3. The minimum length for the 282 // router option is 4 octets, and the length MUST always be a multiple 283 // of 4. 284 optRouter optionCode = 3 285 optDomainNameServer optionCode = 6 286 optHostname optionCode = 12 287 optDomainName optionCode = 15 288 optReqIPAddr optionCode = 50 289 optLeaseTime optionCode = 51 290 optDHCPMsgType optionCode = 53 // dhcpMsgType 291 optDHCPServer optionCode = 54 292 optParamReq optionCode = 55 293 optMessage optionCode = 56 294 optRenewalTime optionCode = 58 295 optRebindingTime optionCode = 59 296 optClientID optionCode = 61 297 ) 298 299 func (code optionCode) lenValid(l int) bool { 300 switch code { 301 case optSubnetMask, 302 optReqIPAddr, 303 optLeaseTime, 304 optDHCPServer, 305 optRenewalTime, 306 optRebindingTime: 307 return l == 4 308 case optDHCPMsgType: 309 return l == 1 310 case optRouter, optDomainNameServer: 311 return l%4 == 0 312 case optMessage, optDomainName, optClientID, optHostname: 313 return l >= 1 314 case optParamReq: 315 return true // no fixed length 316 default: 317 return true // unknown option, assume ok 318 } 319 } 320 321 type options []option 322 323 func (opts options) dhcpMsgType() (dhcpMsgType, error) { 324 for _, opt := range opts { 325 if opt.code == optDHCPMsgType { 326 if len(opt.body) != 1 { 327 return 0, fmt.Errorf("%s: bad length: %d", opt.code, len(opt.body)) 328 } 329 v := opt.body[0] 330 if v <= 0 || v >= 8 { 331 return 0, fmt.Errorf("DHCP bad length: %d", len(opt.body)) 332 } 333 return dhcpMsgType(v), nil 334 } 335 } 336 return 0, nil 337 } 338 339 func (opts options) message() string { 340 for _, opt := range opts { 341 if opt.code == optMessage { 342 return string(opt.body) 343 } 344 } 345 return "" 346 } 347 348 func (opts options) len() int { 349 l := 0 350 for _, opt := range opts { 351 l += 1 + 1 + len(opt.body) // code + len + body 352 } 353 return l + 1 // extra byte for 'pad' option 354 } 355 356 type op byte 357 358 const ( 359 opRequest op = 0x01 360 opReply op = 0x02 361 ) 362 363 // dhcpMsgType is the DHCP Message Type from RFC 1533, section 9.4. 364 type dhcpMsgType byte 365 366 const ( 367 dhcpDISCOVER dhcpMsgType = 1 368 dhcpOFFER dhcpMsgType = 2 369 dhcpREQUEST dhcpMsgType = 3 370 dhcpDECLINE dhcpMsgType = 4 371 dhcpACK dhcpMsgType = 5 372 dhcpNAK dhcpMsgType = 6 373 dhcpRELEASE dhcpMsgType = 7 374 )