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  )