fix: Resolve IP direction mismatch by latching metadata and robustifying tcpdump header parsing

This commit is contained in:
Jose Luis Montañes Ojados
2026-01-19 16:00:59 +01:00
parent cd9b0db44d
commit 0c61db8f5b
2 changed files with 115 additions and 35 deletions

View File

@@ -127,6 +127,7 @@ func (c *LocalCapturer) processStream(r io.Reader) {
scanner := bufio.NewScanner(r)
var buffer strings.Builder
inSIPMessage := false
var msgNetInfo *NetInfo
for scanner.Scan() {
c.mu.Lock()
@@ -137,26 +138,60 @@ func (c *LocalCapturer) processStream(r io.Reader) {
}
line := scanner.Text()
// logger.Debug("Stdout: %s", line) // Commented out to reduce noise, enable if needed
// Check for tcpdump header
if netInfo := parseTcpdumpHeader(line); netInfo != nil {
c.currentNetInfo = netInfo
// Proceed to next line
continue
}
// Detect start of SIP message
if idx := findSIPStart(line); idx != -1 {
logger.Debug("SIP Start detected: %s", line)
// Latch the current network info for this message
if c.currentNetInfo != nil {
// Make a copy
info := *c.currentNetInfo
msgNetInfo = &info
}
// Clean the line (remove prefix garbage)
line = line[idx:]
// If we were building a message, parse it
// If we were building a message, parse it with its OWN net info (which was latched previously)
// Note: This edge case (buffer > 0 but new start) means previous message ended implicitly.
// But wait, the msgNetInfo we just latched is for the NEW message.
// The OLD message should have already been emitted or we are in a weird state.
// Use the PREVIOUS msgNetInfo for the existing buffer if any.
// Actually, single buffer logic is simple: emit what we have.
if buffer.Len() > 0 {
c.parseAndEmit(buffer.String())
// We need to pass the net info that belongs to the buffered content.
// But we just overwrote msgNetInfo.
// Realistically, we should emit before latching new info.
// But tcpdump header comes BEFORE the message.
// So c.currentNetInfo is already the NEW info.
// And the buffer contains the OLD message.
// So when we started the OLD message, we latched OLD info.
// We should persist that OLD info until emit.
// This implies we need `pendingNetInfo` vs `currentNetInfo`.
// Simplified approach: msgNetInfo stores the info for the message currently being built in buffer.
// When we start a NEW message, the buffer contains the PREVIOUS message.
// So we emit the buffer with the OLD msgNetInfo.
// THEN we start the new message and update msgNetInfo to the NEW c.currentNetInfo.
c.parseAndEmit(buffer.String(), msgNetInfo)
buffer.Reset()
}
// NOW update msgNetInfo for the new message
if c.currentNetInfo != nil {
info := *c.currentNetInfo
msgNetInfo = &info
} else {
msgNetInfo = nil
}
inSIPMessage = true
}
@@ -168,7 +203,7 @@ func (c *LocalCapturer) processStream(r io.Reader) {
// Parse remaining buffer
if buffer.Len() > 0 {
c.parseAndEmit(buffer.String())
c.parseAndEmit(buffer.String(), msgNetInfo)
}
}
@@ -188,10 +223,11 @@ func (c *LocalCapturer) processErrors(r io.Reader) {
}
}
func (c *LocalCapturer) parseAndEmit(raw string) {
func (c *LocalCapturer) parseAndEmit(raw string, netInfo *NetInfo) {
packet, err := sip.Parse(raw)
if err != nil {
logger.Error("Error parsing SIP packet: %v", err)
// Suppress verbose error logging for partial packets unless debug
// logger.Error("Error parsing SIP packet: %v", err)
if c.OnError != nil {
c.OnError(err)
}
@@ -199,12 +235,12 @@ func (c *LocalCapturer) parseAndEmit(raw string) {
}
if packet != nil {
// Attach network info if available
if c.currentNetInfo != nil {
packet.Timestamp = c.currentNetInfo.Timestamp
packet.SourceIP = c.currentNetInfo.SourceIP
packet.SourcePort = c.currentNetInfo.SourcePort
packet.DestIP = c.currentNetInfo.DestIP
packet.DestPort = c.currentNetInfo.DestPort
if netInfo != nil {
packet.Timestamp = netInfo.Timestamp
packet.SourceIP = netInfo.SourceIP
packet.SourcePort = netInfo.SourcePort
packet.DestIP = netInfo.DestIP
packet.DestPort = netInfo.DestPort
}
logger.Debug("Packet parsed: %s %s -> %s", packet.Method, packet.SourceIP, packet.DestIP)
if c.OnPacket != nil {