mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-26 09:33:02 +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:
89
network/pcap/writer.go
Normal file
89
network/pcap/writer.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package pcap
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Writer writes .mhfr capture files.
|
||||
type Writer struct {
|
||||
bw *bufio.Writer
|
||||
}
|
||||
|
||||
// NewWriter creates a Writer, immediately writing the file header and metadata block.
|
||||
func NewWriter(w io.Writer, header FileHeader, meta SessionMetadata) (*Writer, error) {
|
||||
metaBytes, err := json.Marshal(&meta)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pcap: marshal metadata: %w", err)
|
||||
}
|
||||
header.MetadataLen = uint32(len(metaBytes))
|
||||
|
||||
bw := bufio.NewWriter(w)
|
||||
|
||||
// Write 32-byte file header.
|
||||
if _, err := bw.WriteString(Magic); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Write(bw, binary.BigEndian, header.Version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := bw.WriteByte(byte(header.ServerType)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := bw.WriteByte(header.ClientMode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Write(bw, binary.BigEndian, header.SessionStartNs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 4 bytes reserved
|
||||
if _, err := bw.Write(make([]byte, 4)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Write(bw, binary.BigEndian, header.MetadataLen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 8 bytes reserved
|
||||
if _, err := bw.Write(make([]byte, 8)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Write metadata JSON block.
|
||||
if _, err := bw.Write(metaBytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := bw.Flush(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Writer{bw: bw}, nil
|
||||
}
|
||||
|
||||
// WritePacket appends a single packet record.
|
||||
func (w *Writer) WritePacket(rec PacketRecord) error {
|
||||
if err := binary.Write(w.bw, binary.BigEndian, rec.TimestampNs); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.bw.WriteByte(byte(rec.Direction)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(w.bw, binary.BigEndian, rec.Opcode); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := binary.Write(w.bw, binary.BigEndian, uint32(len(rec.Payload))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.bw.Write(rec.Payload); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush flushes the buffered writer.
|
||||
func (w *Writer) Flush() error {
|
||||
return w.bw.Flush()
|
||||
}
|
||||
Reference in New Issue
Block a user