package dhcp import ( "bytes" "encoding/binary" "fmt" "io" "net" "strings" log "git.wntrmute.dev/kyle/goutils/syslog" ) 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 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 }