mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-24 16:43:37 +01:00
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
This commit is contained in:
103
network/pcap/format.go
Normal file
103
network/pcap/format.go
Normal file
@@ -0,0 +1,103 @@
|
||||
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
|
||||
Reference in New Issue
Block a user