kdhcp/dhcp/packet.go

172 lines
3.1 KiB
Go

package dhcp
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
"strings"
log "git.wntrmute.dev/kyle/goutils/log"
)
const (
maxHardwareAddrLen = 16
maxServerName = 64
maxFileName = 128
)
var anyAddr = net.IP([]byte{0, 0, 0, 0})
func formatMAC(mac []byte) string {
s := []string{}
for i := 0; i < len(mac); i++ {
s = append(s, fmt.Sprintf("%0x", mac[i:i+1]))
}
return strings.Join(s, ":")
}
type BootRequest struct {
MessageType uint8
HardwareType uint8
HardwareAddress []byte
Hops uint8
TransactionID uint32
SecondsElapsed uint16
Flags uint16
ServerName string
FileName string
ClientIP net.IP
YourIP net.IP
NextIP net.IP
RelayIP net.IP
DHCPType DHCPMessageType // option 53
HostName string // option 12
ClientID string // option 61
ParameterRequests []OptionTag
endOptions bool
}
func newPacketReaderFunc(r io.Reader) func(v any) error {
return func(v any) error {
return binary.Read(r, binary.BigEndian, v)
}
}
func (req *BootRequest) Read(packet []byte) error {
buf := bytes.NewBuffer(packet)
read := newPacketReaderFunc(buf)
if err := read(&req.MessageType); err != nil {
return err
}
if err := read(&req.HardwareType); err != nil {
return err
}
var hwaLength uint8
if err := read(&hwaLength); err != nil {
return err
}
if err := read(&req.Hops); err != nil {
return err
}
if err := read(&req.TransactionID); err != nil {
return err
}
if err := read(&req.SecondsElapsed); err != nil {
return err
}
if err := read(&req.Flags); err != nil {
return err
}
req.ClientIP = anyAddr[:]
if _, err := buf.Read(req.ClientIP); err != nil {
return err
}
req.YourIP = anyAddr[:]
if _, err := buf.Read(req.YourIP); err != nil {
return err
}
req.NextIP = anyAddr[:]
if _, err := buf.Read(req.NextIP); err != nil {
return err
}
req.RelayIP = anyAddr[:]
if _, err := buf.Read(req.RelayIP); err != nil {
return err
}
req.HardwareAddress = make([]byte, int(hwaLength))
if _, err := buf.Read(req.HardwareAddress); err != nil {
return err
}
hwaPad := make([]byte, maxHardwareAddrLen-hwaLength)
if _, err := buf.Read(hwaPad); err != nil {
return err
}
tempBuf := make([]byte, maxServerName)
if _, err := buf.Read(tempBuf); err != nil {
return err
}
req.ServerName = string(bytes.Trim(tempBuf, "\x00"))
tempBuf = make([]byte, maxFileName)
if _, err := buf.Read(tempBuf); err != nil {
return err
}
req.FileName = string(bytes.Trim(tempBuf, "\x00"))
if err := ReadMagicCookie(buf); err != nil {
return err
}
for {
if req.endOptions {
break
}
tag, err := buf.ReadByte()
if err != nil {
if err == io.EOF {
break
}
return err
}
err = ReadOption(req, tag, buf)
if err != nil {
log.Spew(*req)
log.Spew(packet)
return err
}
}
return nil
}
func ReadRequest(pkt []byte) (*BootRequest, error) {
req := &BootRequest{}
err := req.Read(pkt)
if err != nil {
return nil, err
}
log.Debugf("dhcp: BOOTP request with txid %d for %s", req.TransactionID, formatMAC(req.HardwareAddress))
return req, nil
}