mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
feat(pcap): complete replay system with filtering, metadata, and live replay
Wire ExcludeOpcodes config into RecordingConn so configured opcodes (e.g. ping, nop, position) are filtered at record time. Add padded metadata with in-place PatchMetadata to populate CharID/UserID after login. Implement --mode replay using protbot's encrypted connection with timing-aware packet sending, auto-ping response, concurrent S→C collection, and byte-level payload diff reporting.
This commit is contained in:
@@ -76,6 +76,10 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
s.userID = userID
|
||||
|
||||
if s.captureConn != nil {
|
||||
s.captureConn.SetSessionInfo(s.charID, s.userID)
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(uint32(TimeAdjusted().Unix())) // Unix timestamp
|
||||
|
||||
|
||||
@@ -14,25 +14,26 @@ import (
|
||||
)
|
||||
|
||||
// startCapture wraps a network.Conn with a RecordingConn if capture is enabled.
|
||||
// Returns the (possibly wrapped) conn and a cleanup function that must be called on session close.
|
||||
func startCapture(server *Server, conn network.Conn, remoteAddr net.Addr, serverType pcap.ServerType) (network.Conn, func()) {
|
||||
// Returns the (possibly wrapped) conn, the RecordingConn (nil if capture disabled),
|
||||
// and a cleanup function that must be called on session close.
|
||||
func startCapture(server *Server, conn network.Conn, remoteAddr net.Addr, serverType pcap.ServerType) (network.Conn, *pcap.RecordingConn, func()) {
|
||||
capCfg := server.erupeConfig.Capture
|
||||
if !capCfg.Enabled {
|
||||
return conn, func() {}
|
||||
return conn, nil, func() {}
|
||||
}
|
||||
|
||||
switch serverType {
|
||||
case pcap.ServerTypeSign:
|
||||
if !capCfg.CaptureSign {
|
||||
return conn, func() {}
|
||||
return conn, nil, func() {}
|
||||
}
|
||||
case pcap.ServerTypeEntrance:
|
||||
if !capCfg.CaptureEntrance {
|
||||
return conn, func() {}
|
||||
return conn, nil, func() {}
|
||||
}
|
||||
case pcap.ServerTypeChannel:
|
||||
if !capCfg.CaptureChannel {
|
||||
return conn, func() {}
|
||||
return conn, nil, func() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +43,7 @@ func startCapture(server *Server, conn network.Conn, remoteAddr net.Addr, server
|
||||
}
|
||||
if err := os.MkdirAll(outputDir, 0o755); err != nil {
|
||||
server.logger.Warn("Failed to create capture directory", zap.Error(err))
|
||||
return conn, func() {}
|
||||
return conn, nil, func() {}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
@@ -56,7 +57,7 @@ func startCapture(server *Server, conn network.Conn, remoteAddr net.Addr, server
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
server.logger.Warn("Failed to create capture file", zap.Error(err), zap.String("path", path))
|
||||
return conn, func() {}
|
||||
return conn, nil, func() {}
|
||||
}
|
||||
|
||||
startNs := now.UnixNano()
|
||||
@@ -75,12 +76,13 @@ func startCapture(server *Server, conn network.Conn, remoteAddr net.Addr, server
|
||||
if err != nil {
|
||||
server.logger.Warn("Failed to initialize capture writer", zap.Error(err))
|
||||
_ = f.Close()
|
||||
return conn, func() {}
|
||||
return conn, nil, func() {}
|
||||
}
|
||||
|
||||
server.logger.Info("Capture started", zap.String("file", path))
|
||||
|
||||
rc := pcap.NewRecordingConn(conn, w, startNs)
|
||||
rc := pcap.NewRecordingConn(conn, w, startNs, capCfg.ExcludeOpcodes)
|
||||
rc.SetCaptureFile(f, &meta)
|
||||
cleanup := func() {
|
||||
if err := w.Flush(); err != nil {
|
||||
server.logger.Warn("Failed to flush capture", zap.Error(err))
|
||||
@@ -91,7 +93,7 @@ func startCapture(server *Server, conn network.Conn, remoteAddr net.Addr, server
|
||||
server.logger.Info("Capture saved", zap.String("file", path))
|
||||
}
|
||||
|
||||
return rc, cleanup
|
||||
return rc, rc, cleanup
|
||||
}
|
||||
|
||||
// sanitizeAddr replaces characters that are problematic in filenames.
|
||||
|
||||
@@ -74,14 +74,15 @@ type Session struct {
|
||||
Name string
|
||||
closed atomic.Bool
|
||||
ackStart map[uint32]time.Time
|
||||
captureCleanup func() // Called on session close to flush/close capture file
|
||||
captureConn *pcap.RecordingConn // non-nil when capture is active
|
||||
captureCleanup func() // Called on session close to flush/close capture file
|
||||
}
|
||||
|
||||
// NewSession creates a new Session type.
|
||||
func NewSession(server *Server, conn net.Conn) *Session {
|
||||
var cryptConn network.Conn = network.NewCryptConn(conn, server.erupeConfig.RealClientMode, server.logger.Named(conn.RemoteAddr().String()))
|
||||
|
||||
cryptConn, captureCleanup := startCapture(server, cryptConn, conn.RemoteAddr(), pcap.ServerTypeChannel)
|
||||
cryptConn, captureConn, captureCleanup := startCapture(server, cryptConn, conn.RemoteAddr(), pcap.ServerTypeChannel)
|
||||
|
||||
s := &Session{
|
||||
logger: server.logger.Named(conn.RemoteAddr().String()),
|
||||
@@ -96,6 +97,7 @@ func NewSession(server *Server, conn net.Conn) *Session {
|
||||
stageMoveStack: stringstack.New(),
|
||||
ackStart: make(map[uint32]time.Time),
|
||||
semaphoreID: make([]uint16, 2),
|
||||
captureConn: captureConn,
|
||||
captureCleanup: captureCleanup,
|
||||
}
|
||||
return s
|
||||
|
||||
@@ -64,7 +64,7 @@ func startEntranceCapture(s *Server, conn network.Conn, remoteAddr net.Addr) (ne
|
||||
|
||||
s.logger.Info("Capture started", zap.String("file", path))
|
||||
|
||||
rc := pcap.NewRecordingConn(conn, w, startNs)
|
||||
rc := pcap.NewRecordingConn(conn, w, startNs, capCfg.ExcludeOpcodes)
|
||||
cleanup := func() {
|
||||
if err := w.Flush(); err != nil {
|
||||
s.logger.Warn("Failed to flush capture", zap.Error(err))
|
||||
|
||||
@@ -64,7 +64,7 @@ func startSignCapture(s *Server, conn network.Conn, remoteAddr net.Addr) (networ
|
||||
|
||||
s.logger.Info("Capture started", zap.String("file", path))
|
||||
|
||||
rc := pcap.NewRecordingConn(conn, w, startNs)
|
||||
rc := pcap.NewRecordingConn(conn, w, startNs, capCfg.ExcludeOpcodes)
|
||||
cleanup := func() {
|
||||
if err := w.Flush(); err != nil {
|
||||
s.logger.Warn("Failed to flush capture", zap.Error(err))
|
||||
|
||||
Reference in New Issue
Block a user