<commit_before>// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 <NAME>. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package layers

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"math/rand"
	"net"

	"github.com/vtolstov/gopacket"
)

var LayerTypeDHCPv4 = gopacket.RegisterLayerType(6768, gopacket.LayerTypeMetadata{"DHCPv4", gopacket.DecodeFunc(decodeDHCPv4)})

var opTypes = [3]string{
	0: "UNSPEC",
	1: "REQUEST",
	2: "RESPONSE",
}

type DHCPOperation uint8

const (
	DHCP_MSG_UNSPEC DHCPOperation = iota
	DHCP_MSG_DISCOVER
	DHCP_MSG_OFFER
	DHCP_MSG_REQUEST
	DHCP_MSG_DECLINE
	DHCP_MSG_ACK
	DHCP_MSG_NAK
	DHCP_MSG_RELEASE
	DHCP_MSG_INFORM
)

var messageTypes = map[DHCPOperation]string{
	0: "UNSPEC",
	1: "DISCOVER",
	2: "OFFER",
	3: "REQUEST",
	4: "DECLINE",
	5: "ACK",
	6: "NAK",
	7: "RELEASE",
	8: "INFORM",
}

func (o DHCPOperation) String() string {
	return opTypes[o]
}

const (
	_ = iota
	DHCP_MSG_REQ
	DHCP_MSG_RES
)

var dhcpMagic uint32 = 0x63825363

type DHCPv4 struct {
	BaseLayer
	Operation    DHCPOperation
	HardwareType byte
	HardwareLen  uint8
	HardwareOpts uint8
	Xid          uint32
	Secs         uint16
	Flags        uint16
	ClientIP     net.IP
	YourIP       net.IP
	ServerIP     net.IP
	GatewayIP    net.IP
	ClientHWAddr net.HardwareAddr
	ServerName   []byte
	File         []byte
	Options      []DHCPOption
}

const (
	DHCP_OPT_REQUEST_IP     byte = iota + 50 // 0x32, 4, net.IP
	DHCP_OPT_LEASE_TIME                      // 0x33, 4, uint32
	DHCP_OPT_EXT_OPTS                        // 0x34, 1, 1/2/3
	DHCP_OPT_MESSAGE_TYPE                    // 0x35, 1, 1-7
	DHCP_OPT_SERVER_ID                       // 0x36, 4, net.IP
	DHCP_OPT_PARAMS_REQUEST                  // 0x37, n, []byte
	DHCP_OPT_MESSAGE                         // 0x38, n, string
	DHCP_OPT_MAX_DHCP_SIZE                   // 0x39, 2, uint16
	DHCP_OPT_T1                              // 0x3a, 4, uint32
	DHCP_OPT_T2                              // 0x3b, 4, uint32
	DHCP_OPT_CLASS_ID                        // 0x3c, n, []byte
	DHCP_OPT_CLIENT_ID                       // 0x3d, n >=  2, []byte

)

const (
	DHCP_HW_TYPE_ETHERNET byte = 0x01
)

const (
	DHCP_FLAG_BROADCAST uint16 = 0x80

	//      FLAG_BROADCAST_MASK uint16 = (1 << FLAG_BROADCAST)
)

// LayerType returns gopacket.LayerTypeUDP
func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 }

func (dhcp *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
	dhcp.Operation = DHCPOperation(data[0])
	dhcp.HardwareType = data[1]
	dhcp.HardwareLen = data[2]
	dhcp.HardwareOpts = data[3]
	dhcp.Xid = binary.BigEndian.Uint32(data[4:8])
	dhcp.Secs = binary.BigEndian.Uint16(data[8:10])
	dhcp.Flags = binary.BigEndian.Uint16(data[10:12])
	dhcp.ClientIP = net.IP(data[12:16])
	dhcp.YourIP = net.IP(data[16:20])
	dhcp.ServerIP = net.IP(data[20:24])
	dhcp.GatewayIP = net.IP(data[24:28])
	dhcp.ClientHWAddr = net.HardwareAddr(data[28 : 28+dhcp.HardwareLen])
	dhcp.ServerName = data[44:108]
	dhcp.File = data[108:236]
	if binary.BigEndian.Uint32(data[236:240]) != dhcpMagic {
		return errors.New("Bad DHCP header")
	}

	if len(data) > 240 {
		options := make([]byte, len(data)-240)
		if binary.Read(bytes.NewBuffer(data[240:]), binary.BigEndian, &options) != nil {
			return errors.New("failed to unmarshal options")
		}

		stop := len(options)
		start := 0
		for start < stop {
			o := DHCPOption{}
			if err := o.Unmarshal(options[start:]); err != nil {
				return err
			}
			if o.Type == DHCP_OPT_END {
				return nil
			}
			dhcp.Options = append(dhcp.Options, o)
			start += int(o.Length) + 2
		}
	}
	return nil

}

func (dhcp *DHCPv4) Len() uint16 {
	n := uint16(240)
	for _, o := range dhcp.Options {
		n += uint16(o.Length) + 2
	}
	n += 1 // for opt end
	return n
}

// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (dhcp *DHCPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
	plen := int(dhcp.Len())
	if plen < 300 {
		plen = 300
	}

	data, err := b.PrependBytes(plen)
	if err != nil {
		return err
	}

	data[0] = byte(dhcp.Operation)
	data[1] = dhcp.HardwareType
	data[2] = dhcp.HardwareLen
	data[3] = dhcp.HardwareOpts
	binary.BigEndian.PutUint32(data[4:8], dhcp.Xid)
	binary.BigEndian.PutUint16(data[8:10], dhcp.Secs)
	binary.BigEndian.PutUint16(data[10:12], dhcp.Flags)
	copy(data[12:16], dhcp.ClientIP.To4())
	copy(data[16:20], dhcp.YourIP.To4())
	copy(data[20:24], dhcp.ServerIP.To4())
	copy(data[24:28], dhcp.GatewayIP.To4())
	copy(data[28:44], dhcp.ClientHWAddr)
	copy(data[44:108], dhcp.ServerName)
	copy(data[108:236], dhcp.File)
	binary.BigEndian.PutUint32(data[236:240], dhcpMagic)

	if len(dhcp.Options) > 0 {
		options := make([]byte, plen-240)
		start := 0
		for _, o := range dhcp.Options {
			buffer, err := o.Marshal()
			if err != nil {
				return err
			}
			copy(options[start:], buffer)
			start += len(buffer)
		}
		optend := NewDHCPOption(DHCP_OPT_END, nil)
		buffer, err := (&optend).Marshal()
		if err != nil {
			return err
		}
		copy(options[start:], buffer)
		buf := new(bytes.Buffer)
		if err := binary.Write(buf, binary.BigEndian, options); err != nil {
			return err
		}
		copy(data[240:], buf.Bytes())
	}

	return nil
}

func (dhcp *DHCPv4) CanDecode() gopacket.LayerClass {
	return LayerTypeDHCPv4
}

func (dhcp *DHCPv4) NextLayerType() gopacket.LayerType {
	return gopacket.LayerTypePayload
}

func decodeDHCPv4(data []byte, p gopacket.PacketBuilder) error {
	dhcp := &DHCPv4{}
	err := dhcp.DecodeFromBytes(data, p)
	if err != nil {
		return err
	}
	p.AddLayer(dhcp)
	return p.NextDecoder(gopacket.LayerTypePayload)
}

func NewDHCP(xid uint32, op DHCPOperation, hwtype byte) (*DHCPv4, error) {
	if xid == 0 {
		xid = rand.Uint32()
	}
	switch hwtype {
	case DHCP_HW_TYPE_ETHERNET:
		break
	default:
		return nil, errors.New("Bad HardwareType")
	}
	d := &DHCPv4{
		Operation:    op,
		HardwareType: hwtype,
		HardwareLen:  6,
		Xid:          xid,
		ClientIP:     make([]byte, 4),
		YourIP:       make([]byte, 4),
		ServerIP:     make([]byte, 4),
		GatewayIP:    make([]byte, 4),
		ClientHWAddr: make([]byte, 16),
	}
	return d, nil
}

func (p *DHCPv4) String() string {
	if p == nil {
		return "<nil>"
	}
	pFile := string(p.File)
	if pFile == "" {
		pFile = "<nil>"
	}
	pServerName := string(p.ServerName)
	if pServerName == "" {
		pServerName = "<nil>"
	}
	return fmt.Sprintf("op: %s, htype: %v, hlen: %v, hopts: %v, xid: %#x, secs: %v, flags: %v, ciaddr: %v, yiaddr: %v, siaddr: %v, giaddr: %v, chaddr: %s, sname: %v, file: %v, options: %v", p.Operation, p.HardwareType, p.HardwareLen, p.HardwareOpts, p.Xid, p.Secs, p.Flags, p.ClientIP, p.YourIP, p.ServerIP, p.GatewayIP, p.ClientHWAddr, pServerName, pFile, p.Options)
}

func (p *DHCPv4) SetBroadcast(broadcast bool) {
	p.Flags ^= 128
}

const (
	DHCP_OPT_PAD                      byte = iota
	DHCP_OPT_SUBNET_MASK                   // 0x01, 4, net.IP
	DHCP_OPT_TIME_OFFSET                   // 0x02, 4, int32 (signed seconds from UTC)
	DHCP_OPT_DEFAULT_GATEWAY               // 0x03, n*4, [n]net.IP
	DHCP_OPT_TIME_SERVER                   // 0x04, n*4, [n]net.IP
	DHCP_OPT_NAME_SERVER                   // 0x05, n*4, [n]net.IP
	DHCP_OPT_DOMAIN_NAME_SERVERS           // 0x06, n*4, [n]net.IP
	DHCP_OPT_LOG_SERVER                    // 0x07, n*4, [n]net.IP<commit_msg>minimize cruft<commit_after>// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 <NAME>. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package layers

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"math/rand"
	"net"

	"github.com/vtolstov/gopacket"
)

var LayerTypeDHCPv4 = gopacket.RegisterLayerType(6768, gopacket.LayerTypeMetadata{"DHCPv4", gopacket.DecodeFunc(decodeDHCPv4)})

var opTypes = [3]string{
	0: "UNSPEC",
	1: "REQUEST",
	2: "RESPONSE",
}

type DHCPOperation uint8

const (
	DHCP_MSG_UNSPEC DHCPOperation = iota
	DHCP_MSG_DISCOVER
	DHCP_MSG_OFFER
	DHCP_MSG_REQUEST
	DHCP_MSG_DECLINE
	DHCP_MSG_ACK
	DHCP_MSG_NAK
	DHCP_MSG_RELEASE
	DHCP_MSG_INFORM
)

var messageTypes = map[DHCPOperation]string{
	0: "UNSPEC",
	1: "DISCOVER",
	2: "OFFER",
	3: "REQUEST",
	4: "DECLINE",
	5: "ACK",
	6: "NAK",
	7: "RELEASE",
	8: "INFORM",
}

func (o DHCPOperation) String() string {
	return opTypes[o]
}

const (
	_ = iota
	DHCP_MSG_REQ
	DHCP_MSG_RES
)

var dhcpMagic uint32 = 0x63825363

type DHCPv4 struct {
	BaseLayer
	Operation    DHCPOperation
	HardwareType byte
	HardwareLen  uint8
	HardwareOpts uint8
	Xid          uint32
	Secs         uint16
	Flags        uint16
	ClientIP     net.IP
	YourIP       net.IP
	ServerIP     net.IP
	GatewayIP    net.IP
	ClientHWAddr net.HardwareAddr
	ServerName   []byte
	File         []byte
	Options      []DHCPOption
}

const (
	DHCP_OPT_REQUEST_IP     byte = iota + 50 // 0x32, 4, net.IP
	DHCP_OPT_LEASE_TIME                      // 0x33, 4, uint32
	DHCP_OPT_EXT_OPTS                        // 0x34, 1, 1/2/3
	DHCP_OPT_MESSAGE_TYPE                    // 0x35, 1, 1-7
	DHCP_OPT_SERVER_ID                       // 0x36, 4, net.IP
	DHCP_OPT_PARAMS_REQUEST                  // 0x37, n, []byte
	DHCP_OPT_MESSAGE                         // 0x38, n, string
	DHCP_OPT_MAX_DHCP_SIZE                   // 0x39, 2, uint16
	DHCP_OPT_T1                              // 0x3a, 4, uint32
	DHCP_OPT_T2                              // 0x3b, 4, uint32
	DHCP_OPT_CLASS_ID                        // 0x3c, n, []byte
	DHCP_OPT_CLIENT_ID                       // 0x3d, n >=  2, []byte

)

const (
	DHCP_HW_TYPE_ETHERNET byte = 0x01
)

const (
	DHCP_FLAG_BROADCAST uint16 = 0x80

	//      FLAG_BROADCAST_MASK uint16 = (1 << FLAG_BROADCAST)
)

// LayerType returns gopacket.LayerTypeUDP
func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 }

func (dhcp *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
	dhcp.Operation = DHCPOperation(data[0])
	dhcp.HardwareType = data[1]
	dhcp.HardwareLen = data[2]
	dhcp.HardwareOpts = data[3]
	dhcp.Xid = binary.BigEndian.Uint32(data[4:8])
	dhcp.Secs = binary.BigEndian.Uint16(data[8:10])
	dhcp.Flags = binary.BigEndian.Uint16(data[10:12])
	dhcp.ClientIP = net.IP(data[12:16])
	dhcp.YourIP = net.IP(data[16:20])
	dhcp.ServerIP = net.IP(data[20:24])
	dhcp.GatewayIP = net.IP(data[24:28])
	dhcp.ClientHWAddr = net.HardwareAddr(data[28 : 28+dhcp.HardwareLen])
	dhcp.ServerName = data[44:108]
	dhcp.File = data[108:236]
	if binary.BigEndian.Uint32(data[236:240]) != dhcpMagic {
		return errors.New("Bad DHCP header")
	}

	if len(data) > 240 {
		options := make([]byte, len(data)-240)
		if binary.Read(bytes.NewBuffer(data[240:]), binary.BigEndian, &options) != nil {
			return errors.New("failed to unmarshal options")
		}

		stop := len(options)
		start := 0
		for start < stop {
			o := DHCPOption{}
			if err := o.Unmarshal(options[start:]); err != nil {
				return err
			}
			if o.Type == DHCP_OPT_END {
				return nil
			}
			dhcp.Options = append(dhcp.Options, o)
			start += int(o.Length) + 2
		}
	}
	return nil

}

func (dhcp *DHCPv4) Len() uint16 {
	n := uint16(240)
	for _, o := range dhcp.Options {
		n += uint16(o.Length) + 2
	}
	n += 1 // for opt end
	return n
}

// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (dhcp *DHCPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
	plen := int(dhcp.Len())
	if plen < 300 {
		plen = 300
	}

	data, err := b.PrependBytes(plen)
	if err != nil {
		return err
	}

	data[0] = byte(dhcp.Operation)
	data[1] = dhcp.HardwareType
	data[2] = dhcp.HardwareLen
	data[3] = dhcp.HardwareOpts
	binary.BigEndian.PutUint32(data[4:8], dhcp.Xid)
	binary.BigEndian.PutUint16(data[8:10], dhcp.Secs)
	binary.BigEndian.PutUint16(data[10:12], dhcp.Flags)
	copy(data[12:16], dhcp.ClientIP.To4())
	copy(data[16:20], dhcp.YourIP.To4())
	copy(data[20:24], dhcp.ServerIP.To4())
	copy(data[24:28], dhcp.GatewayIP.To4())
	copy(data[28:44], dhcp.ClientHWAddr)
	copy(data[44:108], dhcp.ServerName)
	copy(data[108:236], dhcp.File)
	binary.BigEndian.PutUint32(data[236:240], dhcpMagic)

	if len(dhcp.Options) > 0 {
		options := make([]byte, plen-240)
		start := 0
		for _, o := range dhcp.Options {
			buffer, err := o.Marshal()
			if err != nil {
				return err
			}
			copy(options[start:], buffer)
			start += len(buffer)
		}
		optend := NewDHCPOption(DHCP_OPT_END, nil)
		buffer, err := (&optend).Marshal()
		if err != nil {
			return err
		}
		copy(options[start:], buffer)
		buf := new(bytes.Buffer)
		if err := binary.Write(buf, binary.BigEndian, options); err != nil {
			return err
		}
		copy(data[240:], buf.Bytes())
	}

	return nil
}

func (dhcp *DHCPv4) CanDecode() gopacket.LayerClass {
	return LayerTypeDHCPv4
}

func (dhcp *DHCPv4) NextLayerType() gopacket.LayerType {
	return gopacket.LayerTypePayload
}

func decodeDHCPv4(data []byte, p gopacket.PacketBuilder) error {
	dhcp := &DHCPv4{}
	err := dhcp.DecodeFromBytes(data, p)
	if err != nil {
		return err
	}
	p.AddLayer(dhcp)
	return p.NextDecoder(gopacket.LayerTypePayload)
}

func NewDHCP(xid uint32, op DHCPOperation, hwtype byte) (*DHCPv4, error) {
	if xid == 0 {
		xid = rand.Uint32()
	}
	switch hwtype {
	case DHCP_HW_TYPE_ETHERNET:
		break
	default:
		return nil, errors.New("Bad HardwareType")
	}
	d := &DHCPv4{
		Operation:    op,
		HardwareType: hwtype,
		HardwareLen:  6,
		Xid:          xid,
		ClientIP:     make([]byte, 4),
		YourIP:       make([]byte, 4),
		ServerIP:     make([]byte, 4),
		GatewayIP:    make([]byte, 4),
		ClientHWAddr: make([]byte, 16),
	}
	return d, nil
}

func (p *DHCPv4) String() string {
	if p == nil {
		return "<nil>"
	}
	pFile := string(p.File)
	if pFile == "" {
		pFile = "<nil>"
	}
	pServerName := string(p.ServerName)
	if pServerName == "" {
		pServerName = "<nil>"
	}
	return fmt.Sprintf("op: %s, htype: %v, hlen: %v, hopts: %v, xid: %#x, secs: %v, flags: %v, ciaddr: %v, yiaddr: %v, siaddr: %v, giaddr: %v, chaddr: %s, options: %v", p.Operation, p.HardwareType, p.HardwareLen, p.HardwareOpts, p.Xid, p.Secs, p.Flags, p.ClientIP, p.YourIP, p.ServerIP, p.GatewayIP, p.ClientHWAddr, p.Options)
}

func (p *DHCPv4) SetBroadcast(broadcast bool) {
	p.Flags ^= 128
}

const (
	DHCP_OPT_PAD                      byte = iota
	DHCP_OPT_SUBNET_MASK                   // 0x01, 4, net.IP
	DHCP_OPT_TIME_OFFSET                   // 0x02, 4, int32 (signed seconds from UTC)
	DHCP_OPT_DEFAULT_GATEWAY               // 0x03, n*4, [n]net.IP
	DHCP_OPT_TIME_SERVER                   // 0x04, n*4, [n]net.IP
	DHCP_OPT_NAME_SERVER                   // 0x05, n*4, [n]net.IP
	DHCP_OPT_DOMAIN_NAME_SERVERS           // 0x06, n*4, [n]net.IP
	DHCP_OPT_LOG_SERVER                    // 0x07, n*4, [n]net.IP
{'fraction_non_alphanumeric': 0.13555054988737247, 'fraction_numerical': 0.04465350470385584, 'mean_word_length': 3.057795698924731, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5895765', 'n_tokens_mistral': 6823, 'n_tokens_neox': 6057, 'n_words': 1306}