Files
Erupe/network/pcap/format.go
Houmgaor 7ef5efc549 feat(network): add protocol packet capture and replay system
Add a recording and replay foundation for the MHF network protocol.
A RecordingConn decorator wraps network.Conn to transparently capture
all decrypted packets to binary .mhfr files, with zero handler changes
and zero overhead when disabled.

- network/pcap: binary capture format (writer, reader, filters)
- RecordingConn: thread-safe Conn decorator with direction tracking
- CaptureOptions in config (disabled by default)
- Capture wired into all three server types (sign, entrance, channel)
- cmd/replay: CLI tool with dump, json, stats, and compare modes
- 19 new tests, all passing with -race
2026-02-23 18:50:44 +01:00

104 lines
2.4 KiB
Go

package pcap
import "encoding/json"
// Capture file format constants.
const (
// Magic is the 4-byte magic number for .mhfr capture files.
Magic = "MHFR"
// FormatVersion is the current capture format version.
FormatVersion uint16 = 1
// HeaderSize is the fixed size of the file header in bytes.
HeaderSize = 32
)
// Direction indicates whether a packet was sent or received.
type Direction byte
const (
DirClientToServer Direction = 0x01
DirServerToClient Direction = 0x02
)
func (d Direction) String() string {
switch d {
case DirClientToServer:
return "C→S"
case DirServerToClient:
return "S→C"
default:
return "???"
}
}
// ServerType identifies which server a capture originated from.
type ServerType byte
const (
ServerTypeSign ServerType = 0x01
ServerTypeEntrance ServerType = 0x02
ServerTypeChannel ServerType = 0x03
)
func (st ServerType) String() string {
switch st {
case ServerTypeSign:
return "sign"
case ServerTypeEntrance:
return "entrance"
case ServerTypeChannel:
return "channel"
default:
return "unknown"
}
}
// FileHeader is the fixed 32-byte header at the start of a .mhfr file.
//
// [4B] Magic "MHFR"
// [2B] Version
// [1B] ServerType
// [1B] ClientMode
// [8B] SessionStartNs
// [4B] Reserved
// [4B] MetadataLen
// [8B] Reserved
type FileHeader struct {
Version uint16
ServerType ServerType
ClientMode byte
SessionStartNs int64
MetadataLen uint32
}
// SessionMetadata is the JSON-encoded metadata block following the file header.
type SessionMetadata struct {
ServerVersion string `json:"server_version,omitempty"`
Host string `json:"host,omitempty"`
Port int `json:"port,omitempty"`
CharID uint32 `json:"char_id,omitempty"`
UserID uint32 `json:"user_id,omitempty"`
RemoteAddr string `json:"remote_addr,omitempty"`
}
// MarshalJSON serializes the metadata to JSON.
func (m *SessionMetadata) MarshalJSON() ([]byte, error) {
type Alias SessionMetadata
return json.Marshal((*Alias)(m))
}
// PacketRecord is a single captured packet.
//
// [8B] TimestampNs [1B] Direction [2B] Opcode [4B] PayloadLen [NB] Payload
type PacketRecord struct {
TimestampNs int64
Direction Direction
Opcode uint16
Payload []byte // Full decrypted packet bytes (includes the 2-byte opcode prefix)
}
// PacketRecordHeaderSize is the fixed overhead per packet record (before payload).
const PacketRecordHeaderSize = 8 + 1 + 2 + 4 // 15 bytes