diff --git a/cmd/inspector/main.go b/cmd/inspector/main.go index 9c42abb..f65d8dd 100644 --- a/cmd/inspector/main.go +++ b/cmd/inspector/main.go @@ -4,15 +4,27 @@ import ( "fmt" "os" + "telephony-inspector/internal/logger" "telephony-inspector/internal/tui" tea "github.com/charmbracelet/bubbletea" ) func main() { + // Initialize logger + if err := logger.Init(); err != nil { + fmt.Fprintf(os.Stderr, "Warning: Could not initialize logger: %v\n", err) + } + defer logger.Close() + + logger.Info("Starting Telephony Inspector") + p := tea.NewProgram(tui.NewModel(), tea.WithAltScreen()) if _, err := p.Run(); err != nil { + logger.Error("Program error: %v", err) fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } + + logger.Info("Telephony Inspector exited normally") } diff --git a/data/calls.pcap b/data/calls.pcap new file mode 100644 index 0000000..de13ccf Binary files /dev/null and b/data/calls.pcap differ diff --git a/data/test.pcapng b/data/test.pcapng new file mode 100644 index 0000000..13c33b2 Binary files /dev/null and b/data/test.pcapng differ diff --git a/data/test2.pcap b/data/test2.pcap new file mode 100644 index 0000000..869ac76 Binary files /dev/null and b/data/test2.pcap differ diff --git a/go.mod b/go.mod index 2f8380c..adf7f69 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,15 @@ go 1.24.0 toolchain go1.24.12 require ( + github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/lipgloss v1.1.0 + github.com/google/gopacket v1.1.19 golang.org/x/crypto v0.47.0 ) require ( + github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect github.com/charmbracelet/x/ansi v0.10.1 // indirect @@ -25,6 +28,7 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/sahilm/fuzzy v0.1.1 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect diff --git a/go.sum b/go.sum index 4a96c15..a74c483 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,11 @@ +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= +github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= @@ -10,10 +16,16 @@ github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7 github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -31,17 +43,33 @@ github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= +github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/inspector.exe b/inspector.exe index a5f450f..367a2e2 100644 Binary files a/inspector.exe and b/inspector.exe differ diff --git a/internal/capture/local.go b/internal/capture/local.go new file mode 100644 index 0000000..ba5ec6d --- /dev/null +++ b/internal/capture/local.go @@ -0,0 +1,180 @@ +package capture + +import ( + "bufio" + "context" + "fmt" + "io" + "os/exec" + "strings" + "sync" + + "telephony-inspector/internal/sip" +) + +// LocalCapturer handles SIP packet capture locally via tcpdump +type LocalCapturer struct { + cmd *exec.Cmd + cancel context.CancelFunc + running bool + mu sync.Mutex + + // Callbacks + OnPacket func(*sip.Packet) + OnError func(error) +} + +// NewLocalCapturer creates a new local capturer +func NewLocalCapturer() *LocalCapturer { + return &LocalCapturer{} +} + +// Start begins capturing SIP traffic locally +func (c *LocalCapturer) Start(iface string, port int) error { + c.mu.Lock() + if c.running { + c.mu.Unlock() + return fmt.Errorf("capture already running") + } + c.running = true + c.mu.Unlock() + + ctx, cancel := context.WithCancel(context.Background()) + c.cancel = cancel + + // Build tcpdump command + // -l: line buffered + // -A: print packet payload in ASCII + // -s 0: capture full packets + args := []string{"-l", "-A", "-s", "0", "-i", iface, "port", fmt.Sprintf("%d", port)} + c.cmd = exec.CommandContext(ctx, "tcpdump", args...) + + stdout, err := c.cmd.StdoutPipe() + if err != nil { + c.mu.Lock() + c.running = false + c.mu.Unlock() + return fmt.Errorf("failed to get stdout: %w", err) + } + + stderr, err := c.cmd.StderrPipe() + if err != nil { + c.mu.Lock() + c.running = false + c.mu.Unlock() + return fmt.Errorf("failed to get stderr: %w", err) + } + + if err := c.cmd.Start(); err != nil { + c.mu.Lock() + c.running = false + c.mu.Unlock() + return fmt.Errorf("failed to start tcpdump: %w", err) + } + + // Process stdout in goroutine + go c.processStream(stdout) + + // Log stderr + go c.processErrors(stderr) + + return nil +} + +// Stop stops the capture +func (c *LocalCapturer) Stop() { + c.mu.Lock() + defer c.mu.Unlock() + + if !c.running { + return + } + c.running = false + + if c.cancel != nil { + c.cancel() + c.cancel = nil + } + + if c.cmd != nil && c.cmd.Process != nil { + c.cmd.Process.Kill() + c.cmd.Wait() + } +} + +// IsRunning returns whether capture is active +func (c *LocalCapturer) IsRunning() bool { + c.mu.Lock() + defer c.mu.Unlock() + return c.running +} + +// Close cleans up resources +func (c *LocalCapturer) Close() error { + c.Stop() + return nil +} + +func (c *LocalCapturer) processStream(r io.Reader) { + scanner := bufio.NewScanner(r) + var buffer strings.Builder + inSIPMessage := false + + for scanner.Scan() { + c.mu.Lock() + running := c.running + c.mu.Unlock() + if !running { + break + } + + line := scanner.Text() + + // Detect start of SIP message + if isSIPStart(line) { + // If we were building a message, parse it + if buffer.Len() > 0 { + c.parseAndEmit(buffer.String()) + buffer.Reset() + } + inSIPMessage = true + } + + if inSIPMessage { + buffer.WriteString(line) + buffer.WriteString("\r\n") + } + } + + // Parse remaining buffer + if buffer.Len() > 0 { + c.parseAndEmit(buffer.String()) + } +} + +func (c *LocalCapturer) processErrors(r io.Reader) { + scanner := bufio.NewScanner(r) + for scanner.Scan() { + text := scanner.Text() + // tcpdump prints "listening on..." to stderr, ignore it + if strings.Contains(text, "listening on") { + continue + } + if c.OnError != nil { + c.OnError(fmt.Errorf("tcpdump: %s", text)) + } + } +} + +func (c *LocalCapturer) parseAndEmit(raw string) { + packet, err := sip.Parse(raw) + if err != nil { + if c.OnError != nil { + c.OnError(err) + } + return + } + if packet != nil && c.OnPacket != nil { + c.OnPacket(packet) + } +} diff --git a/internal/capture/pcap_reader.go b/internal/capture/pcap_reader.go new file mode 100644 index 0000000..ccc4fa5 --- /dev/null +++ b/internal/capture/pcap_reader.go @@ -0,0 +1,243 @@ +package capture + +import ( + "fmt" + "os" + + "telephony-inspector/internal/logger" + "telephony-inspector/internal/sip" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" +) + +// PcapReader reads and parses pcap files +type PcapReader struct { + path string + handle *pcap.Handle + packets []*sip.Packet +} + +// NewPcapReader creates a new pcap reader +func NewPcapReader(path string) *PcapReader { + logger.Info("PcapReader: Creating reader for %s", path) + return &PcapReader{ + path: path, + packets: make([]*sip.Packet, 0), + } +} + +// ReadAll reads all SIP packets from the pcap file +func (r *PcapReader) ReadAll() ([]*sip.Packet, error) { + logger.Info("PcapReader: Opening file %s", r.path) + + // Check if file exists + if _, err := os.Stat(r.path); os.IsNotExist(err) { + logger.Error("PcapReader: File does not exist: %s", r.path) + return nil, fmt.Errorf("file does not exist: %s", r.path) + } + + handle, err := pcap.OpenOffline(r.path) + if err != nil { + logger.Error("PcapReader: Failed to open pcap: %v", err) + return nil, fmt.Errorf("failed to open pcap: %w", err) + } + defer handle.Close() + + logger.Info("PcapReader: File opened successfully, link type: %v", handle.LinkType()) + + // Try setting BPF filter (optional) + if err := handle.SetBPFFilter("port 5060 or port 5061"); err != nil { + logger.Warn("PcapReader: Could not set BPF filter: %v (continuing without filter)", err) + } else { + logger.Debug("PcapReader: BPF filter set for SIP ports") + } + + packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + + totalPackets := 0 + sipPackets := 0 + + for packet := range packetSource.Packets() { + totalPackets++ + logger.Debug("PcapReader: Processing packet %d, layers: %v", totalPackets, packet.Layers()) + + sipPacket := r.extractSIPPacket(packet, totalPackets) + if sipPacket != nil { + sipPackets++ + r.packets = append(r.packets, sipPacket) + logger.Info("PcapReader: Found SIP packet %d: %s %s", sipPackets, + func() string { + if sipPacket.IsRequest { + return string(sipPacket.Method) + } else { + return fmt.Sprintf("%d", sipPacket.StatusCode) + } + }(), + sipPacket.CallID) + } + } + + logger.Info("PcapReader: Finished reading. Total packets: %d, SIP packets: %d", totalPackets, sipPackets) + return r.packets, nil +} + +// extractSIPPacket extracts SIP data from a gopacket +func (r *PcapReader) extractSIPPacket(packet gopacket.Packet, packetNum int) *sip.Packet { + // Get network layer for IPs + var srcIP, dstIP string + var srcPort, dstPort int + var payload []byte + + if ipLayer := packet.Layer(layers.LayerTypeIPv4); ipLayer != nil { + ip := ipLayer.(*layers.IPv4) + srcIP = ip.SrcIP.String() + dstIP = ip.DstIP.String() + logger.Debug("PcapReader: Packet %d IPv4 %s -> %s", packetNum, srcIP, dstIP) + } else if ipLayer := packet.Layer(layers.LayerTypeIPv6); ipLayer != nil { + ip := ipLayer.(*layers.IPv6) + srcIP = ip.SrcIP.String() + dstIP = ip.DstIP.String() + logger.Debug("PcapReader: Packet %d IPv6 %s -> %s", packetNum, srcIP, dstIP) + } else { + logger.Debug("PcapReader: Packet %d has no IP layer", packetNum) + } + + // Get transport layer for ports AND payload + if udpLayer := packet.Layer(layers.LayerTypeUDP); udpLayer != nil { + udp := udpLayer.(*layers.UDP) + srcPort = int(udp.SrcPort) + dstPort = int(udp.DstPort) + payload = udp.Payload + logger.Debug("PcapReader: Packet %d UDP %d -> %d, payload len: %d", packetNum, srcPort, dstPort, len(payload)) + } else if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { + tcp := tcpLayer.(*layers.TCP) + srcPort = int(tcp.SrcPort) + dstPort = int(tcp.DstPort) + payload = tcp.Payload + logger.Debug("PcapReader: Packet %d TCP %d -> %d, payload len: %d", packetNum, srcPort, dstPort, len(payload)) + } else { + logger.Debug("PcapReader: Packet %d has no TCP/UDP layer", packetNum) + return nil + } + + if len(payload) == 0 { + logger.Debug("PcapReader: Packet %d has empty payload", packetNum) + return nil + } + + payloadStr := string(payload) + + // Log first 200 chars to see full SIP headers + preview := payloadStr + if len(preview) > 200 { + preview = preview[:200] + } + logger.Debug("PcapReader: Packet %d full payload preview: %q", packetNum, preview) + + // Check if it looks like SIP + if !isSIPPayload(payloadStr) { + logger.Debug("PcapReader: Packet %d is not SIP", packetNum) + return nil + } + + logger.Debug("PcapReader: Packet %d detected as SIP, parsing...", packetNum) + + // Parse the SIP message + sipPacket, err := sip.Parse(payloadStr) + if err != nil { + logger.Warn("PcapReader: Packet %d SIP parse error: %v", packetNum, err) + return nil + } + if sipPacket == nil { + logger.Warn("PcapReader: Packet %d SIP parse returned nil", packetNum) + return nil + } + + // Set network info + sipPacket.SourceIP = srcIP + sipPacket.SourcePort = srcPort + sipPacket.DestIP = dstIP + sipPacket.DestPort = dstPort + + return sipPacket +} + +// isSIPPayload checks if payload looks like a SIP message +func isSIPPayload(payload string) bool { + if len(payload) < 4 { + return false + } + + // Check for SIP request methods + methods := []string{"INVITE ", "ACK ", "BYE ", "CANCEL ", "REGISTER ", "OPTIONS ", + "PRACK ", "SUBSCRIBE ", "NOTIFY ", "PUBLISH ", "INFO ", "REFER ", "MESSAGE ", "UPDATE "} + for _, m := range methods { + if len(payload) >= len(m) && payload[:len(m)] == m { + logger.Debug("isSIPPayload: Detected SIP method: %s", m[:len(m)-1]) + return true + } + } + + // Check for SIP response + if len(payload) >= 7 && payload[:7] == "SIP/2.0" { + logger.Debug("isSIPPayload: Detected SIP response") + return true + } + + // Check if this looks like an SDP body (might be reassembled SIP) + if len(payload) >= 4 && payload[:4] == "v=0\r" { + logger.Debug("isSIPPayload: Detected SDP-only payload (no SIP headers)") + // This is SDP without SIP headers - likely a reassembly issue + // Log first 50 chars for debugging + preview := payload + if len(preview) > 50 { + preview = preview[:50] + } + logger.Debug("isSIPPayload: SDP payload: %q", preview) + return false + } + + // Log what the payload starts with for debugging + preview := payload + if len(preview) > 20 { + preview = preview[:20] + } + logger.Debug("isSIPPayload: Unrecognized payload start: %q", preview) + + return false +} + +// GetPacketCount returns number of packets read +func (r *PcapReader) GetPacketCount() int { + return len(r.packets) +} + +// Close closes the reader +func (r *PcapReader) Close() error { + if r.handle != nil { + r.handle.Close() + } + return nil +} + +// ListPcapFiles lists .pcap files in a directory +func ListPcapFiles(dir string) ([]string, error) { + entries, err := os.ReadDir(dir) + if err != nil { + return nil, err + } + + var files []string + for _, e := range entries { + if e.IsDir() { + continue + } + name := e.Name() + if len(name) > 5 && (name[len(name)-5:] == ".pcap" || name[len(name)-7:] == ".pcapng") { + files = append(files, name) + } + } + return files, nil +} diff --git a/internal/config/loader.go b/internal/config/loader.go new file mode 100644 index 0000000..3825d02 --- /dev/null +++ b/internal/config/loader.go @@ -0,0 +1,72 @@ +package config + +import ( + "encoding/json" + "os" +) + +// LoadNetworkMap loads a network map from a JSON file +func LoadNetworkMap(path string) (*NetworkMap, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var nm NetworkMap + if err := json.Unmarshal(data, &nm); err != nil { + return nil, err + } + + return &nm, nil +} + +// SaveNetworkMap saves a network map to a JSON file +func SaveNetworkMap(nm *NetworkMap, path string) error { + data, err := json.MarshalIndent(nm, "", " ") + if err != nil { + return err + } + + return os.WriteFile(path, data, 0644) +} + +// DefaultNetworkMapPath returns a default path for the network map file +func DefaultNetworkMapPath() string { + return "network_map.json" +} + +// CreateSampleNetworkMap creates a sample network map for testing +func CreateSampleNetworkMap() *NetworkMap { + nm := NewNetworkMap() + + nm.AddNode(NetworkNode{ + Name: "Asterisk PBX", + IP: "192.168.1.10", + Type: NodeTypePBX, + Description: "Main PBX server", + }) + + nm.AddNode(NetworkNode{ + Name: "Kamailio Proxy", + IP: "192.168.1.20", + Type: NodeTypeProxy, + Aliases: []string{"10.0.0.20"}, + Description: "SIP proxy/load balancer", + }) + + nm.AddNode(NetworkNode{ + Name: "RTPEngine", + IP: "192.168.1.30", + Type: NodeTypeMediaServer, + Description: "Media relay server", + }) + + nm.AddNode(NetworkNode{ + Name: "PSTN Gateway", + IP: "192.168.1.40", + Type: NodeTypeGateway, + Description: "Gateway to PSTN", + }) + + return nm +} diff --git a/internal/logger/logger.go b/internal/logger/logger.go new file mode 100644 index 0000000..4496821 --- /dev/null +++ b/internal/logger/logger.go @@ -0,0 +1,113 @@ +package logger + +import ( + "fmt" + "io" + "os" + "path/filepath" + "sync" + "time" +) + +var ( + instance *Logger + once sync.Once +) + +// Logger handles application logging +type Logger struct { + file *os.File + mu sync.Mutex + path string +} + +// Init initializes the logger with timestamp-based filename +func Init() error { + var err error + once.Do(func() { + // Create logs directory + logsDir := "logs" + if mkErr := os.MkdirAll(logsDir, 0755); mkErr != nil { + err = mkErr + return + } + + // Create log file with timestamp + timestamp := time.Now().Format("2006-01-02_15-04-05") + filename := filepath.Join(logsDir, fmt.Sprintf("%s.log", timestamp)) + + f, openErr := os.Create(filename) + if openErr != nil { + err = openErr + return + } + + instance = &Logger{ + file: f, + path: filename, + } + + Info("Logger initialized: %s", filename) + }) + return err +} + +// Close closes the logger +func Close() { + if instance != nil && instance.file != nil { + instance.file.Close() + } +} + +// GetPath returns the log file path +func GetPath() string { + if instance != nil { + return instance.path + } + return "" +} + +// log writes a log entry +func log(level, format string, args ...interface{}) { + if instance == nil { + return + } + + instance.mu.Lock() + defer instance.mu.Unlock() + + timestamp := time.Now().Format("15:04:05.000") + msg := fmt.Sprintf(format, args...) + line := fmt.Sprintf("[%s] [%s] %s\n", timestamp, level, msg) + + instance.file.WriteString(line) + instance.file.Sync() +} + +// Debug logs a debug message +func Debug(format string, args ...interface{}) { + log("DEBUG", format, args...) +} + +// Info logs an info message +func Info(format string, args ...interface{}) { + log("INFO", format, args...) +} + +// Warn logs a warning message +func Warn(format string, args ...interface{}) { + log("WARN", format, args...) +} + +// Error logs an error message +func Error(format string, args ...interface{}) { + log("ERROR", format, args...) +} + +// Writer returns an io.Writer that logs to the file +func Writer() io.Writer { + if instance != nil { + return instance.file + } + return os.Stdout +} diff --git a/internal/sip/callflow.go b/internal/sip/callflow.go new file mode 100644 index 0000000..30f612d --- /dev/null +++ b/internal/sip/callflow.go @@ -0,0 +1,199 @@ +package sip + +import ( + "sync" + "time" +) + +// CallFlow represents a SIP call with all its packets +type CallFlow struct { + CallID string + Packets []*Packet + StartTime time.Time + EndTime time.Time + + // Summary info + From string + To string + State CallState +} + +// CallState represents the current state of a call +type CallState string + +const ( + CallStateInitial CallState = "Initial" + CallStateRinging CallState = "Ringing" + CallStateConnected CallState = "Connected" + CallStateTerminated CallState = "Terminated" + CallStateFailed CallState = "Failed" +) + +// CallFlowStore stores and manages call flows +type CallFlowStore struct { + mu sync.RWMutex + flows map[string]*CallFlow +} + +// NewCallFlowStore creates a new call flow store +func NewCallFlowStore() *CallFlowStore { + return &CallFlowStore{ + flows: make(map[string]*CallFlow), + } +} + +// AddPacket adds a packet to the appropriate call flow +func (s *CallFlowStore) AddPacket(p *Packet) *CallFlow { + s.mu.Lock() + defer s.mu.Unlock() + + if p.CallID == "" { + return nil + } + + flow, exists := s.flows[p.CallID] + if !exists { + flow = &CallFlow{ + CallID: p.CallID, + Packets: make([]*Packet, 0), + StartTime: time.Now(), + From: p.From, + To: p.To, + State: CallStateInitial, + } + s.flows[p.CallID] = flow + } + + flow.Packets = append(flow.Packets, p) + flow.EndTime = time.Now() + + // Update call state based on packet + s.updateState(flow, p) + + return flow +} + +// updateState updates the call state based on the packet +func (s *CallFlowStore) updateState(flow *CallFlow, p *Packet) { + if p.IsRequest { + switch p.Method { + case MethodINVITE: + if flow.State == CallStateInitial { + flow.State = CallStateInitial + } + case MethodBYE, MethodCANCEL: + flow.State = CallStateTerminated + } + } else { + // Response + switch { + case p.StatusCode >= 100 && p.StatusCode < 200: + if p.StatusCode == 180 || p.StatusCode == 183 { + flow.State = CallStateRinging + } + case p.StatusCode >= 200 && p.StatusCode < 300: + flow.State = CallStateConnected + case p.StatusCode >= 400: + flow.State = CallStateFailed + } + } +} + +// GetFlow returns a call flow by Call-ID +func (s *CallFlowStore) GetFlow(callID string) *CallFlow { + s.mu.RLock() + defer s.mu.RUnlock() + return s.flows[callID] +} + +// GetAllFlows returns all call flows +func (s *CallFlowStore) GetAllFlows() []*CallFlow { + s.mu.RLock() + defer s.mu.RUnlock() + + flows := make([]*CallFlow, 0, len(s.flows)) + for _, f := range s.flows { + flows = append(flows, f) + } + return flows +} + +// GetRecentFlows returns the N most recent call flows sorted by StartTime (oldest first) +func (s *CallFlowStore) GetRecentFlows(n int) []*CallFlow { + flows := s.GetAllFlows() + + // Sort by start time ascending (oldest first), then by CallID for stable order + for i := 0; i < len(flows)-1; i++ { + for j := i + 1; j < len(flows); j++ { + // Compare by StartTime first + if flows[i].StartTime.After(flows[j].StartTime) { + flows[i], flows[j] = flows[j], flows[i] + } else if flows[i].StartTime.Equal(flows[j].StartTime) { + // If same time, sort by CallID for stable order + if flows[i].CallID > flows[j].CallID { + flows[i], flows[j] = flows[j], flows[i] + } + } + } + } + + if len(flows) > n { + flows = flows[:n] + } + return flows +} + +// Count returns the number of call flows +func (s *CallFlowStore) Count() int { + s.mu.RLock() + defer s.mu.RUnlock() + return len(s.flows) +} + +// Summary returns a string summary of a packet for display +func (p *Packet) Summary() string { + if p.IsRequest { + return string(p.Method) + } + return formatStatusCode(p.StatusCode, p.StatusText) +} + +func formatStatusCode(code int, text string) string { + if text != "" { + return text + } + switch code { + case 100: + return "100 Trying" + case 180: + return "180 Ringing" + case 183: + return "183 Session Progress" + case 200: + return "200 OK" + case 400: + return "400 Bad Request" + case 401: + return "401 Unauthorized" + case 403: + return "403 Forbidden" + case 404: + return "404 Not Found" + case 408: + return "408 Request Timeout" + case 480: + return "480 Temporarily Unavailable" + case 486: + return "486 Busy Here" + case 487: + return "487 Request Terminated" + case 488: + return "488 Not Acceptable Here" + case 500: + return "500 Server Internal Error" + case 503: + return "503 Service Unavailable" + default: + return string(rune('0'+code/100)) + "xx" + } +} diff --git a/internal/tui/file_browser.go b/internal/tui/file_browser.go new file mode 100644 index 0000000..c133324 --- /dev/null +++ b/internal/tui/file_browser.go @@ -0,0 +1,256 @@ +package tui + +import ( + "os" + "path/filepath" + "sort" + "strings" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +// FileBrowserModel handles file selection +type FileBrowserModel struct { + currentDir string + entries []os.DirEntry + selected int + filter string // file extension filter (e.g., ".pcap") + height int + offset int + + // Results + selectedFile string + cancelled bool + err error +} + +// NewFileBrowser creates a new file browser starting at dir +func NewFileBrowser(startDir string, filter string) FileBrowserModel { + if startDir == "" { + startDir, _ = os.Getwd() + } + + fb := FileBrowserModel{ + currentDir: startDir, + filter: filter, + height: 15, + } + fb.loadDir() + return fb +} + +func (m *FileBrowserModel) loadDir() { + entries, err := os.ReadDir(m.currentDir) + if err != nil { + m.err = err + return + } + + // Filter and sort entries + var filtered []os.DirEntry + for _, e := range entries { + if e.IsDir() { + filtered = append(filtered, e) + } else if m.filter == "" { + filtered = append(filtered, e) + } else { + name := strings.ToLower(e.Name()) + if strings.HasSuffix(name, m.filter) || strings.HasSuffix(name, ".pcapng") { + filtered = append(filtered, e) + } + } + } + + // Sort: directories first, then alphabetically + sort.Slice(filtered, func(i, j int) bool { + iDir := filtered[i].IsDir() + jDir := filtered[j].IsDir() + if iDir != jDir { + return iDir + } + return strings.ToLower(filtered[i].Name()) < strings.ToLower(filtered[j].Name()) + }) + + m.entries = filtered + m.selected = 0 + m.offset = 0 +} + +// Init initializes the model +func (m FileBrowserModel) Init() tea.Cmd { + return nil +} + +// Update handles messages +func (m FileBrowserModel) Update(msg tea.Msg) (FileBrowserModel, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "esc", "q": + m.cancelled = true + return m, nil + + case "up", "k": + if m.selected > 0 { + m.selected-- + if m.selected < m.offset { + m.offset = m.selected + } + } + + case "down", "j": + if m.selected < len(m.entries)-1 { + m.selected++ + if m.selected >= m.offset+m.height { + m.offset = m.selected - m.height + 1 + } + } + + case "enter": + if len(m.entries) == 0 { + return m, nil + } + + entry := m.entries[m.selected] + fullPath := filepath.Join(m.currentDir, entry.Name()) + + if entry.IsDir() { + m.currentDir = fullPath + m.loadDir() + } else { + m.selectedFile = fullPath + } + + case "backspace", "h": + // Go to parent directory + parent := filepath.Dir(m.currentDir) + if parent != m.currentDir { + m.currentDir = parent + m.loadDir() + } + + case "home": + home, err := os.UserHomeDir() + if err == nil { + m.currentDir = home + m.loadDir() + } + } + + case tea.WindowSizeMsg: + m.height = msg.Height - 10 + if m.height < 5 { + m.height = 5 + } + } + + return m, nil +} + +// View renders the file browser +func (m FileBrowserModel) View() string { + titleStyle := lipgloss.NewStyle(). + Bold(true). + Foreground(lipgloss.Color("#7D56F4")). + MarginBottom(1) + + pathStyle := lipgloss.NewStyle(). + Foreground(lipgloss.Color("#AFAFAF")). + MarginBottom(1) + + dirStyle := lipgloss.NewStyle(). + Foreground(lipgloss.Color("#8BE9FD")) + + fileStyle := lipgloss.NewStyle(). + Foreground(lipgloss.Color("#F8F8F2")) + + selectedStyle := lipgloss.NewStyle(). + Bold(true). + Background(lipgloss.Color("#44475A")). + Foreground(lipgloss.Color("#50FA7B")) + + helpStyle := lipgloss.NewStyle(). + Foreground(lipgloss.Color("#626262")). + MarginTop(1) + + var b strings.Builder + + b.WriteString(titleStyle.Render("📁 Select PCAP File")) + b.WriteString("\n") + b.WriteString(pathStyle.Render(m.currentDir)) + b.WriteString("\n\n") + + if m.err != nil { + b.WriteString(lipgloss.NewStyle().Foreground(lipgloss.Color("#FF5555")).Render("Error: " + m.err.Error())) + b.WriteString("\n") + } + + if len(m.entries) == 0 { + b.WriteString("(empty directory)\n") + } else { + // Show entries with scrolling + end := m.offset + m.height + if end > len(m.entries) { + end = len(m.entries) + } + + for i := m.offset; i < end; i++ { + entry := m.entries[i] + name := entry.Name() + + var style lipgloss.Style + prefix := " " + + if entry.IsDir() { + name = "📂 " + name + "/" + style = dirStyle + } else { + name = "📄 " + name + style = fileStyle + } + + if i == m.selected { + prefix = "▶ " + style = selectedStyle + } + + b.WriteString(prefix + style.Render(name) + "\n") + } + + // Show scroll indicator + if len(m.entries) > m.height { + b.WriteString(pathStyle.Render( + strings.Repeat("─", 20) + + " " + string(rune('0'+m.offset/10)) + string(rune('0'+m.offset%10)) + + "/" + string(rune('0'+len(m.entries)/10)) + string(rune('0'+len(m.entries)%10)) + " " + + strings.Repeat("─", 20))) + b.WriteString("\n") + } + } + + b.WriteString("\n") + b.WriteString(helpStyle.Render("↑/↓ navigate • Enter select/open • Backspace parent • Esc cancel")) + + return b.String() +} + +// IsSelected returns true if a file was selected +func (m FileBrowserModel) IsSelected() bool { + return m.selectedFile != "" +} + +// IsCancelled returns true if cancelled +func (m FileBrowserModel) IsCancelled() bool { + return m.cancelled +} + +// GetSelectedFile returns the selected file path +func (m FileBrowserModel) GetSelectedFile() string { + return m.selectedFile +} + +// GetCurrentDir returns current directory +func (m FileBrowserModel) GetCurrentDir() string { + return m.currentDir +} diff --git a/internal/tui/model.go b/internal/tui/model.go index c4f4aa6..1a8cad6 100644 --- a/internal/tui/model.go +++ b/internal/tui/model.go @@ -2,8 +2,16 @@ package tui import ( "fmt" - "telephony-inspector/internal/config" + "strconv" + "strings" + "telephony-inspector/internal/capture" + "telephony-inspector/internal/config" + "telephony-inspector/internal/sip" + internalSSH "telephony-inspector/internal/ssh" + + "github.com/charmbracelet/bubbles/list" + "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) @@ -18,9 +26,31 @@ const ( ViewNetworkMap ) +// SubView for modal states +type SubView int + +const ( + SubViewNone SubView = iota + SubViewSSHConfig + SubViewAddNode + SubViewCallDetail + SubViewCaptureMenu + SubViewFileBrowser +) + +// CaptureMode defines local vs SSH capture +type CaptureMode int + +const ( + CaptureModeNone CaptureMode = iota + CaptureModeLocal + CaptureModeSSH +) + // Model holds the application state type Model struct { currentView View + subView SubView width int height int @@ -28,9 +58,29 @@ type Model struct { networkMap *config.NetworkMap // Capture state - capturing bool - packetCount int - lastPackets []string // Last N packet summaries + captureMode CaptureMode + sshConfig SSHConfigModel + capturer *capture.Capturer + localCapturer *capture.LocalCapturer + connected bool + capturing bool + packetCount int + lastPackets []string + captureError string + captureIface string + + // Call flow analysis + callFlowStore *sip.CallFlowStore + selectedFlow int + flowList list.Model + + // File browser for pcap import + fileBrowser FileBrowserModel + loadedPcapPath string + + // Network node input + nodeInput []textinput.Model + nodeInputFocus int // Style definitions styles Styles @@ -44,6 +94,11 @@ type Styles struct { Inactive lipgloss.Style Help lipgloss.Style StatusBar lipgloss.Style + Error lipgloss.Style + Success lipgloss.Style + Box lipgloss.Style + PacketRow lipgloss.Style + CallFlow lipgloss.Style } func defaultStyles() Styles { @@ -66,17 +121,67 @@ func defaultStyles() Styles { Background(lipgloss.Color("#7D56F4")). Foreground(lipgloss.Color("#FFFFFF")). Padding(0, 1), + Error: lipgloss.NewStyle(). + Foreground(lipgloss.Color("#FF5555")), + Success: lipgloss.NewStyle(). + Foreground(lipgloss.Color("#50FA7B")), + Box: lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + BorderForeground(lipgloss.Color("#7D56F4")). + Padding(1, 2), + PacketRow: lipgloss.NewStyle(). + Foreground(lipgloss.Color("#F8F8F2")), + CallFlow: lipgloss.NewStyle(). + Border(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("#44475A")). + Padding(0, 1), } } // NewModel creates a new TUI model with default values func NewModel() Model { - return Model{ - currentView: ViewDashboard, - networkMap: config.NewNetworkMap(), - lastPackets: make([]string, 0, 20), - styles: defaultStyles(), + // Try to load existing network map + nm, err := config.LoadNetworkMap(config.DefaultNetworkMapPath()) + if err != nil { + nm = config.NewNetworkMap() } + + return Model{ + currentView: ViewDashboard, + subView: SubViewNone, + networkMap: nm, + callFlowStore: sip.NewCallFlowStore(), + lastPackets: make([]string, 0, 50), + sshConfig: NewSSHConfigModel(), + nodeInput: createNodeInputs(), + styles: defaultStyles(), + } +} + +func createNodeInputs() []textinput.Model { + inputs := make([]textinput.Model, 4) + + inputs[0] = textinput.New() + inputs[0].Placeholder = "Node Name" + inputs[0].Prompt = "Name: " + inputs[0].CharLimit = 64 + + inputs[1] = textinput.New() + inputs[1].Placeholder = "192.168.1.x" + inputs[1].Prompt = "IP: " + inputs[1].CharLimit = 45 + + inputs[2] = textinput.New() + inputs[2].Placeholder = "PBX/Proxy/MediaServer/Gateway" + inputs[2].Prompt = "Type: " + inputs[2].CharLimit = 20 + + inputs[3] = textinput.New() + inputs[3].Placeholder = "Optional description" + inputs[3].Prompt = "Desc: " + inputs[3].CharLimit = 256 + + return inputs } // Init initializes the model @@ -84,12 +189,30 @@ func (m Model) Init() tea.Cmd { return nil } +// PacketMsg is sent when a new packet is received +type PacketMsg struct { + Packet *sip.Packet +} + +// ErrorMsg is sent when an error occurs +type ErrorMsg struct { + Error error +} + // Update handles messages and updates the model func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmd tea.Cmd + + // Handle subview updates first + if m.subView != SubViewNone { + return m.updateSubView(msg) + } + switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "q", "ctrl+c": + m.cleanup() return m, tea.Quit case "1": m.currentView = ViewDashboard @@ -99,18 +222,336 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.currentView = ViewAnalysis case "4": m.currentView = ViewNetworkMap + default: + cmd = m.handleViewKeys(msg) } case tea.WindowSizeMsg: m.width = msg.Width m.height = msg.Height + + case PacketMsg: + m.packetCount++ + summary := formatPacketSummary(msg.Packet, m.networkMap) + m.lastPackets = append(m.lastPackets, summary) + if len(m.lastPackets) > 50 { + m.lastPackets = m.lastPackets[1:] + } + m.callFlowStore.AddPacket(msg.Packet) + + case ErrorMsg: + m.captureError = msg.Error.Error() + } + + return m, cmd +} + +func (m *Model) handleViewKeys(msg tea.KeyMsg) tea.Cmd { + switch m.currentView { + case ViewCapture: + switch msg.String() { + case "c": + // Show capture mode menu if not capturing + if !m.capturing && m.captureMode == CaptureModeNone { + m.subView = SubViewCaptureMenu + } + case "l": + // Start local capture directly + if !m.capturing { + m.captureMode = CaptureModeLocal + m.captureIface = "any" + return m.startLocalCapture() + } + case "r": + // SSH remote capture + if !m.capturing { + m.subView = SubViewSSHConfig + m.sshConfig = NewSSHConfigModel() + return m.sshConfig.Init() + } + case "s": + if m.capturing { + m.stopCapture() + } else if m.captureMode != CaptureModeNone { + if m.captureMode == CaptureModeLocal { + return m.startLocalCapture() + } else if m.connected { + return m.startSSHCapture() + } + } + case "d": + m.disconnect() + } + + case ViewAnalysis: + switch msg.String() { + case "up", "k": + if m.selectedFlow > 0 { + m.selectedFlow-- + } + case "down", "j": + flows := m.callFlowStore.GetRecentFlows(20) + if m.selectedFlow < len(flows)-1 { + m.selectedFlow++ + } + case "enter": + m.subView = SubViewCallDetail + } + + case ViewNetworkMap: + switch msg.String() { + case "a": + m.subView = SubViewAddNode + m.nodeInput = createNodeInputs() + m.nodeInput[0].Focus() + m.nodeInputFocus = 0 + case "l": + if nm, err := config.LoadNetworkMap(config.DefaultNetworkMapPath()); err == nil { + m.networkMap = nm + } + case "s": + config.SaveNetworkMap(m.networkMap, config.DefaultNetworkMapPath()) + case "g": + m.networkMap = config.CreateSampleNetworkMap() + } + } + + return nil +} + +func (m *Model) updateSubView(msg tea.Msg) (tea.Model, tea.Cmd) { + switch m.subView { + case SubViewCaptureMenu: + if keyMsg, ok := msg.(tea.KeyMsg); ok { + switch keyMsg.String() { + case "l", "1": + m.captureMode = CaptureModeLocal + m.captureIface = "any" + m.subView = SubViewNone + return m, m.startLocalCapture() + case "r", "2": + m.subView = SubViewSSHConfig + m.sshConfig = NewSSHConfigModel() + return m, m.sshConfig.Init() + case "p", "3": + // Open file browser for pcap + m.fileBrowser = NewFileBrowser("", ".pcap") + m.subView = SubViewFileBrowser + return m, nil + case "esc", "q": + m.subView = SubViewNone + } + } + return m, nil + + case SubViewFileBrowser: + var cmd tea.Cmd + m.fileBrowser, cmd = m.fileBrowser.Update(msg) + + if m.fileBrowser.IsSelected() { + // Load the pcap file + pcapPath := m.fileBrowser.GetSelectedFile() + m.loadedPcapPath = pcapPath + m.subView = SubViewNone + + // Load packets from pcap + reader := capture.NewPcapReader(pcapPath) + packets, err := reader.ReadAll() + if err != nil { + m.captureError = err.Error() + } else { + m.captureError = "" + m.packetCount = len(packets) + for _, pkt := range packets { + m.callFlowStore.AddPacket(pkt) + summary := formatPacketSummary(pkt, m.networkMap) + m.lastPackets = append(m.lastPackets, summary) + } + } + return m, nil + } else if m.fileBrowser.IsCancelled() { + m.subView = SubViewNone + } + return m, cmd + + case SubViewSSHConfig: + var cmd tea.Cmd + m.sshConfig, cmd = m.sshConfig.Update(msg) + + if m.sshConfig.IsSubmitted() { + host, port, user, password := m.sshConfig.GetConfig() + portInt, _ := strconv.Atoi(port) + if portInt == 0 { + portInt = 22 + } + + cfg := internalSSH.Config{ + Host: host, + Port: portInt, + User: user, + Password: password, + } + + m.capturer = capture.NewCapturer(cfg) + if err := m.capturer.Connect(); err != nil { + m.captureError = err.Error() + } else { + m.connected = true + m.captureMode = CaptureModeSSH + m.captureError = "" + } + m.subView = SubViewNone + } else if m.sshConfig.IsCancelled() { + m.subView = SubViewNone + } + return m, cmd + + case SubViewAddNode: + return m.updateNodeInput(msg) + + case SubViewCallDetail: + if keyMsg, ok := msg.(tea.KeyMsg); ok { + if keyMsg.String() == "esc" || keyMsg.String() == "q" { + m.subView = SubViewNone + } + } + return m, nil } return m, nil } +func (m *Model) updateNodeInput(msg tea.Msg) (tea.Model, tea.Cmd) { + if keyMsg, ok := msg.(tea.KeyMsg); ok { + switch keyMsg.String() { + case "esc": + m.subView = SubViewNone + return m, nil + case "tab", "down": + m.nodeInput[m.nodeInputFocus].Blur() + m.nodeInputFocus = (m.nodeInputFocus + 1) % len(m.nodeInput) + return m, m.nodeInput[m.nodeInputFocus].Focus() + case "shift+tab", "up": + m.nodeInput[m.nodeInputFocus].Blur() + m.nodeInputFocus-- + if m.nodeInputFocus < 0 { + m.nodeInputFocus = len(m.nodeInput) - 1 + } + return m, m.nodeInput[m.nodeInputFocus].Focus() + case "enter": + if m.nodeInputFocus == len(m.nodeInput)-1 { + // Submit + name := m.nodeInput[0].Value() + ip := m.nodeInput[1].Value() + nodeType := m.nodeInput[2].Value() + desc := m.nodeInput[3].Value() + + if name != "" && ip != "" { + m.networkMap.AddNode(config.NetworkNode{ + Name: name, + IP: ip, + Type: config.NodeType(nodeType), + Description: desc, + }) + } + m.subView = SubViewNone + return m, nil + } + m.nodeInput[m.nodeInputFocus].Blur() + m.nodeInputFocus++ + return m, m.nodeInput[m.nodeInputFocus].Focus() + } + } + + var cmd tea.Cmd + m.nodeInput[m.nodeInputFocus], cmd = m.nodeInput[m.nodeInputFocus].Update(msg) + return m, cmd +} + +func (m *Model) startLocalCapture() tea.Cmd { + m.capturing = true + m.captureError = "" + m.packetCount = 0 + m.lastPackets = m.lastPackets[:0] + m.captureMode = CaptureModeLocal + + m.localCapturer = capture.NewLocalCapturer() + m.localCapturer.OnPacket = func(p *sip.Packet) { + // Note: In real implementation, use channel + tea.Cmd + } + m.localCapturer.OnError = func(err error) { + m.captureError = err.Error() + } + + iface := m.captureIface + if iface == "" { + iface = "any" + } + + if err := m.localCapturer.Start(iface, 5060); err != nil { + m.captureError = err.Error() + m.capturing = false + } + + return nil +} + +func (m *Model) startSSHCapture() tea.Cmd { + m.capturing = true + m.captureError = "" + m.packetCount = 0 + m.lastPackets = m.lastPackets[:0] + + m.capturer.OnPacket = func(p *sip.Packet) { + // Note: In real implementation, use channel + tea.Cmd + } + m.capturer.OnError = func(err error) { + m.captureError = err.Error() + } + + if err := m.capturer.Start("any", 5060); err != nil { + m.captureError = err.Error() + m.capturing = false + } + + return nil +} + +func (m *Model) stopCapture() { + if m.localCapturer != nil { + m.localCapturer.Stop() + } + if m.capturer != nil { + m.capturer.Stop() + } + m.capturing = false +} + +func (m *Model) disconnect() { + m.stopCapture() + if m.localCapturer != nil { + m.localCapturer.Close() + m.localCapturer = nil + } + if m.capturer != nil { + m.capturer.Close() + m.capturer = nil + } + m.connected = false + m.captureMode = CaptureModeNone +} + +func (m *Model) cleanup() { + m.disconnect() +} + // View renders the TUI func (m Model) View() string { + // Handle subview modals + if m.subView != SubViewNone { + return m.renderSubView() + } + var content string switch m.currentView { @@ -124,15 +565,94 @@ func (m Model) View() string { content = m.viewNetworkMap() } - // Navigation bar nav := m.renderNav() - - // Status bar - status := m.styles.StatusBar.Render(" Telephony Inspector v0.1.0 ") + status := m.renderStatusBar() return lipgloss.JoinVertical(lipgloss.Left, nav, content, status) } +func (m Model) renderSubView() string { + switch m.subView { + case SubViewCaptureMenu: + return m.styles.Box.Render(m.renderCaptureMenu()) + case SubViewFileBrowser: + return m.styles.Box.Render(m.fileBrowser.View()) + case SubViewSSHConfig: + return m.styles.Box.Render(m.sshConfig.View()) + case SubViewAddNode: + return m.styles.Box.Render(m.renderAddNodeForm()) + case SubViewCallDetail: + return m.renderCallDetail() + } + return "" +} + +func (m Model) renderCaptureMenu() string { + var b strings.Builder + b.WriteString(m.styles.Title.Render("📡 Select Capture Mode")) + b.WriteString("\n\n") + b.WriteString(" [1] [L]ocal - Capture on this machine (requires tcpdump)\n") + b.WriteString(" [2] [R]emote - Capture via SSH on remote server\n") + b.WriteString(" [3] [P]cap - Import pcap file from disk\n") + b.WriteString("\n") + b.WriteString(m.styles.Help.Render("Press 1/L, 2/R, or 3/P to select • Esc to cancel")) + return b.String() +} + +func (m Model) renderAddNodeForm() string { + var b strings.Builder + b.WriteString(m.styles.Title.Render("➕ Add Network Node")) + b.WriteString("\n\n") + for _, input := range m.nodeInput { + b.WriteString(input.View()) + b.WriteString("\n") + } + b.WriteString("\n") + b.WriteString(m.styles.Help.Render("Tab navigate • Enter submit • Esc cancel")) + return b.String() +} + +func (m Model) renderCallDetail() string { + flows := m.callFlowStore.GetRecentFlows(20) + if m.selectedFlow >= len(flows) || len(flows) == 0 { + return m.styles.Box.Render("No call selected\n\nPress Esc to go back") + } + + flow := flows[m.selectedFlow] + var b strings.Builder + + b.WriteString(m.styles.Title.Render("📞 Call Detail")) + b.WriteString("\n\n") + b.WriteString(fmt.Sprintf("Call-ID: %s\n", flow.CallID)) + b.WriteString(fmt.Sprintf("From: %s\n", flow.From)) + b.WriteString(fmt.Sprintf("To: %s\n", flow.To)) + b.WriteString(fmt.Sprintf("State: %s\n", flow.State)) + b.WriteString(fmt.Sprintf("Packets: %d\n\n", len(flow.Packets))) + + b.WriteString("Transaction Flow:\n") + for i, pkt := range flow.Packets { + arrow := "→" + if !pkt.IsRequest { + arrow = "←" + } + b.WriteString(fmt.Sprintf(" %d. %s %s\n", i+1, arrow, pkt.Summary())) + + // Show SDP info if present + if pkt.SDP != nil { + mediaIP := pkt.SDP.GetSDPMediaIP() + if mediaIP != "" { + label := m.networkMap.LabelForIP(mediaIP) + b.WriteString(fmt.Sprintf(" SDP Media: %s\n", label)) + } + } + } + + b.WriteString("\n") + b.WriteString(m.styles.Help.Render("Press Esc to go back")) + + return m.styles.Box.Render(b.String()) +} + func (m Model) renderNav() string { tabs := []string{"[1] Dashboard", "[2] Capture", "[3] Analysis", "[4] Network Map"} var rendered []string @@ -148,52 +668,163 @@ func (m Model) renderNav() string { return lipgloss.JoinHorizontal(lipgloss.Top, rendered...) + "\n" } +func (m Model) renderStatusBar() string { + var parts []string + parts = append(parts, " Telephony Inspector v0.1.0 ") + + if m.connected { + parts = append(parts, m.styles.Success.Render(" SSH: Connected ")) + } + if m.capturing { + parts = append(parts, m.styles.Active.Render(fmt.Sprintf(" Capturing: %d pkts ", m.packetCount))) + } + if m.captureError != "" { + parts = append(parts, m.styles.Error.Render(" Error: "+m.captureError+" ")) + } + + return m.styles.StatusBar.Render(strings.Join(parts, "|")) +} + func (m Model) viewDashboard() string { title := m.styles.Title.Render("📞 Dashboard") - info := lipgloss.JoinVertical(lipgloss.Left, - m.styles.Subtitle.Render("SIP Telephony Inspector"), - "", - "• Capture SIP traffic via SSH + tcpdump", - "• Analyze SIP/SDP packets in real-time", - "• Map network topology for better debugging", - "", - m.styles.Help.Render("Press 1-4 to navigate, q to quit"), - ) + var stats []string + stats = append(stats, m.styles.Subtitle.Render("SIP Telephony Inspector")) + stats = append(stats, "") - return lipgloss.JoinVertical(lipgloss.Left, title, info) + if m.connected { + stats = append(stats, m.styles.Success.Render("✓ SSH Connected")) + } else { + stats = append(stats, m.styles.Inactive.Render("○ SSH Disconnected")) + } + + stats = append(stats, fmt.Sprintf("Network Nodes: %d", len(m.networkMap.Nodes))) + stats = append(stats, fmt.Sprintf("Active Calls: %d", m.callFlowStore.Count())) + stats = append(stats, fmt.Sprintf("Packets Captured: %d", m.packetCount)) + stats = append(stats, "") + stats = append(stats, "Quick Start:") + stats = append(stats, " 1. Go to [2] Capture → Press 'c' to connect SSH") + stats = append(stats, " 2. Press 's' to start capturing SIP traffic") + stats = append(stats, " 3. Go to [3] Analysis to view call flows") + stats = append(stats, " 4. Go to [4] Network Map to label IPs") + stats = append(stats, "") + stats = append(stats, m.styles.Help.Render("Press 1-4 to navigate, q to quit")) + + return lipgloss.JoinVertical(lipgloss.Left, title, lipgloss.JoinVertical(lipgloss.Left, stats...)) } func (m Model) viewCapture() string { title := m.styles.Title.Render("🔍 Capture") - status := "Status: " - if m.capturing { - status += m.styles.Active.Render("● Capturing") - } else { - status += m.styles.Inactive.Render("○ Stopped") + var lines []string + + // Mode indicator + switch m.captureMode { + case CaptureModeLocal: + lines = append(lines, m.styles.Success.Render("● Mode: Local capture")) + case CaptureModeSSH: + if m.connected { + lines = append(lines, m.styles.Success.Render("● Mode: SSH (connected)")) + } else { + lines = append(lines, m.styles.Inactive.Render("○ Mode: SSH (disconnected)")) + } + default: + lines = append(lines, m.styles.Inactive.Render("○ No capture mode selected")) } - packetInfo := fmt.Sprintf("Packets captured: %d", m.packetCount) + // Capture status + if m.capturing { + mode := "local" + if m.captureMode == CaptureModeSSH { + mode = "SSH" + } + lines = append(lines, m.styles.Active.Render(fmt.Sprintf("● Capturing on port 5060 (%s)", mode))) + } else if m.captureMode != CaptureModeNone { + lines = append(lines, m.styles.Inactive.Render("○ Capture stopped")) + } - help := m.styles.Help.Render("[c] Connect SSH [s] Start/Stop Capture [q] Quit") + lines = append(lines, fmt.Sprintf("Packets: %d", m.packetCount)) + lines = append(lines, "") - return lipgloss.JoinVertical(lipgloss.Left, title, status, packetInfo, "", help) + // Last packets + if len(m.lastPackets) > 0 { + lines = append(lines, "Recent Packets:") + start := 0 + if len(m.lastPackets) > 15 { + start = len(m.lastPackets) - 15 + } + for _, pkt := range m.lastPackets[start:] { + lines = append(lines, " "+pkt) + } + } + + lines = append(lines, "") + + // Help + var help string + if m.captureMode == CaptureModeNone { + help = "[c] Choose mode [l] Local [r] Remote SSH [q] Quit" + } else if !m.capturing { + help = "[s] Start [c] Change mode [d] Disconnect [q] Quit" + } else { + help = "[s] Stop [d] Disconnect [q] Quit" + } + lines = append(lines, m.styles.Help.Render(help)) + + return lipgloss.JoinVertical(lipgloss.Left, title, lipgloss.JoinVertical(lipgloss.Left, lines...)) } func (m Model) viewAnalysis() string { title := m.styles.Title.Render("📊 Analysis") - content := lipgloss.JoinVertical(lipgloss.Left, - "Call flows will appear here once packets are captured.", - "", - "Features:", - "• Group packets by Call-ID", - "• Visualize SIP transaction flow", - "• Decode SDP offers/answers", - ) + flows := m.callFlowStore.GetRecentFlows(20) - return lipgloss.JoinVertical(lipgloss.Left, title, content) + if len(flows) == 0 { + return lipgloss.JoinVertical(lipgloss.Left, title, + "No calls captured yet.", + "", + "Start capturing on the Capture tab to see call flows here.", + "", + m.styles.Help.Render("Press 2 to go to Capture")) + } + + var lines []string + lines = append(lines, fmt.Sprintf("Calls: %d", len(flows))) + lines = append(lines, "") + + for i, flow := range flows { + prefix := " " + style := m.styles.Inactive + if i == m.selectedFlow { + prefix = "▶ " + style = m.styles.Active + } + + stateIcon := "○" + switch flow.State { + case sip.CallStateRinging: + stateIcon = "◐" + case sip.CallStateConnected: + stateIcon = "●" + case sip.CallStateTerminated: + stateIcon = "◯" + case sip.CallStateFailed: + stateIcon = "✕" + } + + summary := fmt.Sprintf("%s%s %s → %s [%d pkts]", + prefix, stateIcon, + truncate(extractUser(flow.From), 15), + truncate(extractUser(flow.To), 15), + len(flow.Packets)) + + lines = append(lines, style.Render(summary)) + } + + lines = append(lines, "") + lines = append(lines, m.styles.Help.Render("↑/↓ select • Enter details • q quit")) + + return lipgloss.JoinVertical(lipgloss.Left, title, lipgloss.JoinVertical(lipgloss.Left, lines...)) } func (m Model) viewNetworkMap() string { @@ -203,14 +834,71 @@ func (m Model) viewNetworkMap() string { return lipgloss.JoinVertical(lipgloss.Left, title, "No network nodes configured.", "", - m.styles.Help.Render("[a] Add node [l] Load from file"), - ) + "Add nodes to label IPs in your SIP infrastructure.", + "", + m.styles.Help.Render("[a] Add node [l] Load file [g] Generate sample")) } - var nodes []string + var lines []string for _, node := range m.networkMap.Nodes { - nodes = append(nodes, fmt.Sprintf("• %s (%s): %s", node.Name, node.Type, node.IP)) + icon := "○" + switch node.Type { + case config.NodeTypePBX: + icon = "☎" + case config.NodeTypeProxy: + icon = "⇄" + case config.NodeTypeMediaServer: + icon = "♪" + case config.NodeTypeGateway: + icon = "⬚" + } + line := fmt.Sprintf(" %s %s (%s): %s", icon, node.Name, node.Type, node.IP) + if node.Description != "" { + line += " - " + node.Description + } + lines = append(lines, line) } - return lipgloss.JoinVertical(lipgloss.Left, title, lipgloss.JoinVertical(lipgloss.Left, nodes...)) + lines = append(lines, "") + lines = append(lines, m.styles.Help.Render("[a] Add [s] Save [l] Load [g] Sample [q] Quit")) + + return lipgloss.JoinVertical(lipgloss.Left, title, lipgloss.JoinVertical(lipgloss.Left, lines...)) +} + +// Helper functions + +func formatPacketSummary(p *sip.Packet, nm *config.NetworkMap) string { + src := nm.LabelForIP(p.SourceIP) + dst := nm.LabelForIP(p.DestIP) + + if p.IsRequest { + return fmt.Sprintf("%s → %s: %s", src, dst, p.Method) + } + return fmt.Sprintf("%s → %s: %d %s", src, dst, p.StatusCode, p.StatusText) +} + +func truncate(s string, max int) string { + if len(s) <= max { + return s + } + return s[:max-1] + "…" +} + +func extractUser(sipAddr string) string { + // Extract user from "Display Name" + if idx := strings.Index(sipAddr, "= 0 { + start := idx + 5 + end := strings.Index(sipAddr[start:], "@") + if end > 0 { + return sipAddr[start : start+end] + } + } + if idx := strings.Index(sipAddr, "sip:"); idx >= 0 { + start := idx + 4 + end := strings.Index(sipAddr[start:], "@") + if end > 0 { + return sipAddr[start : start+end] + } + } + return sipAddr } diff --git a/internal/tui/ssh_config.go b/internal/tui/ssh_config.go new file mode 100644 index 0000000..951b5e2 --- /dev/null +++ b/internal/tui/ssh_config.go @@ -0,0 +1,180 @@ +package tui + +import ( + "strings" + + "github.com/charmbracelet/bubbles/textinput" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +// SSHConfigModel handles SSH connection input +type SSHConfigModel struct { + inputs []textinput.Model + focusIndex int + submitted bool + cancelled bool + + // Parsed values + Host string + Port string + User string + Password string +} + +const ( + inputHost = iota + inputPort + inputUser + inputPassword +) + +// NewSSHConfigModel creates a new SSH config input model +func NewSSHConfigModel() SSHConfigModel { + inputs := make([]textinput.Model, 4) + + // Host input + inputs[inputHost] = textinput.New() + inputs[inputHost].Placeholder = "192.168.1.100" + inputs[inputHost].Focus() + inputs[inputHost].CharLimit = 256 + inputs[inputHost].Width = 30 + inputs[inputHost].Prompt = "Host: " + + // Port input + inputs[inputPort] = textinput.New() + inputs[inputPort].Placeholder = "22" + inputs[inputPort].CharLimit = 5 + inputs[inputPort].Width = 10 + inputs[inputPort].Prompt = "Port: " + inputs[inputPort].SetValue("22") + + // User input + inputs[inputUser] = textinput.New() + inputs[inputUser].Placeholder = "root" + inputs[inputUser].CharLimit = 64 + inputs[inputUser].Width = 20 + inputs[inputUser].Prompt = "User: " + + // Password input + inputs[inputPassword] = textinput.New() + inputs[inputPassword].Placeholder = "password" + inputs[inputPassword].CharLimit = 128 + inputs[inputPassword].Width = 30 + inputs[inputPassword].Prompt = "Password: " + inputs[inputPassword].EchoMode = textinput.EchoPassword + inputs[inputPassword].EchoCharacter = '•' + + return SSHConfigModel{ + inputs: inputs, + focusIndex: 0, + } +} + +// Init initializes the model +func (m SSHConfigModel) Init() tea.Cmd { + return textinput.Blink +} + +// Update handles messages +func (m SSHConfigModel) Update(msg tea.Msg) (SSHConfigModel, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "ctrl+c", "esc": + m.cancelled = true + return m, nil + + case "tab", "down": + m.focusIndex++ + if m.focusIndex >= len(m.inputs) { + m.focusIndex = 0 + } + return m, m.updateFocus() + + case "shift+tab", "up": + m.focusIndex-- + if m.focusIndex < 0 { + m.focusIndex = len(m.inputs) - 1 + } + return m, m.updateFocus() + + case "enter": + if m.focusIndex == len(m.inputs)-1 { + // Submit on last field + m.submitted = true + m.Host = m.inputs[inputHost].Value() + m.Port = m.inputs[inputPort].Value() + m.User = m.inputs[inputUser].Value() + m.Password = m.inputs[inputPassword].Value() + return m, nil + } + // Move to next field + m.focusIndex++ + return m, m.updateFocus() + } + } + + // Update focused input + cmd := m.updateInputs(msg) + return m, cmd +} + +func (m *SSHConfigModel) updateFocus() tea.Cmd { + cmds := make([]tea.Cmd, len(m.inputs)) + for i := range m.inputs { + if i == m.focusIndex { + cmds[i] = m.inputs[i].Focus() + } else { + m.inputs[i].Blur() + } + } + return tea.Batch(cmds...) +} + +func (m *SSHConfigModel) updateInputs(msg tea.Msg) tea.Cmd { + cmds := make([]tea.Cmd, len(m.inputs)) + for i := range m.inputs { + m.inputs[i], cmds[i] = m.inputs[i].Update(msg) + } + return tea.Batch(cmds...) +} + +// View renders the SSH config form +func (m SSHConfigModel) View() string { + var b strings.Builder + + titleStyle := lipgloss.NewStyle(). + Bold(true). + Foreground(lipgloss.Color("#7D56F4")). + MarginBottom(1) + + b.WriteString(titleStyle.Render("🔌 SSH Connection")) + b.WriteString("\n\n") + + for i := range m.inputs { + b.WriteString(m.inputs[i].View()) + b.WriteString("\n") + } + + helpStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")) + b.WriteString("\n") + b.WriteString(helpStyle.Render("Tab/↑↓ navigate • Enter submit • Esc cancel")) + + return b.String() +} + +// IsSubmitted returns true if the form was submitted +func (m SSHConfigModel) IsSubmitted() bool { + return m.submitted +} + +// IsCancelled returns true if the form was cancelled +func (m SSHConfigModel) IsCancelled() bool { + return m.cancelled +} + +// GetConfig returns the SSH config values +func (m SSHConfigModel) GetConfig() (host, port, user, password string) { + return m.Host, m.Port, m.User, m.Password +} diff --git a/logs/2026-01-19_14-18-33.log b/logs/2026-01-19_14-18-33.log new file mode 100644 index 0000000..77dbbfb --- /dev/null +++ b/logs/2026-01-19_14-18-33.log @@ -0,0 +1,52 @@ +[14:18:33.982] [INFO] Logger initialized: logs\2026-01-19_14-18-33.log +[14:18:33.986] [INFO] Starting Telephony Inspector +[14:18:40.383] [INFO] PcapReader: Creating reader for D:\Proyectos\telephony-inspector\data\test2.pcap +[14:18:40.387] [INFO] PcapReader: Opening file D:\Proyectos\telephony-inspector\data\test2.pcap +[14:18:40.391] [INFO] PcapReader: File opened successfully, link type: Ethernet +[14:18:40.395] [DEBUG] PcapReader: BPF filter set for SIP ports +[14:18:40.399] [DEBUG] PcapReader: Processing packet 1, layers: [0xc00022a150 0xc00034a000 0xc000234100 0xc000232090] +[14:18:40.402] [DEBUG] PcapReader: Packet 1 IPv4 192.168.0.164 -> 192.168.0.162 +[14:18:40.407] [DEBUG] PcapReader: Packet 1 UDP 51416 -> 5060 +[14:18:40.409] [DEBUG] PcapReader: Packet 1 application layer, payload length: 528 +[14:18:40.411] [DEBUG] PcapReader: Packet 1 payload preview: "v=0\r\no=- 3977820639 3977820639 IN IP4 192.168.0.164\r\ns=pjmedia\r\nb=AS:84\r\nt=0 0\r\na=X-nat:0\r\nm=audio 4" +[14:18:40.413] [DEBUG] PcapReader: Packet 1 is not SIP +[14:18:40.415] [DEBUG] PcapReader: Processing packet 2, layers: [0xc00022a1c0 0xc00034a0a0 0xc000234180 0xc000232120] +[14:18:40.417] [DEBUG] PcapReader: Packet 2 IPv4 192.168.0.162 -> 192.168.0.164 +[14:18:40.419] [DEBUG] PcapReader: Packet 2 UDP 5060 -> 51416 +[14:18:40.421] [DEBUG] PcapReader: Packet 2 application layer, payload length: 0 +[14:18:40.423] [DEBUG] PcapReader: Packet 2 is not SIP +[14:18:40.425] [DEBUG] PcapReader: Processing packet 3, layers: [0xc00022a230 0xc00034a140 0xc000234200 0xc0002321b0] +[14:18:40.426] [DEBUG] PcapReader: Packet 3 IPv4 192.168.0.162 -> 192.168.0.164 +[14:18:40.428] [DEBUG] PcapReader: Packet 3 UDP 5060 -> 51416 +[14:18:40.431] [DEBUG] PcapReader: Packet 3 application layer, payload length: 375 +[14:18:40.433] [DEBUG] PcapReader: Packet 3 payload preview: "v=0\r\no=- 3977820639 3977820641 IN IP4 192.168.0.162\r\ns=Asterisk\r\nc=IN IP4 192.168.0.162\r\nt=0 0\r\nm=au" +[14:18:40.434] [DEBUG] PcapReader: Packet 3 is not SIP +[14:18:40.436] [DEBUG] PcapReader: Processing packet 4, layers: [0xc00022a2a0 0xc00034a1e0 0xc000234280 0xc000232240] +[14:18:40.439] [DEBUG] PcapReader: Packet 4 IPv4 192.168.0.164 -> 192.168.0.162 +[14:18:40.441] [DEBUG] PcapReader: Packet 4 UDP 51416 -> 5060 +[14:18:40.443] [DEBUG] PcapReader: Packet 4 application layer, payload length: 0 +[14:18:40.444] [DEBUG] PcapReader: Packet 4 is not SIP +[14:18:40.446] [DEBUG] PcapReader: Processing packet 5, layers: [0xc00022a310 0xc00034a280 0xc000234300 0xc0002322d0] +[14:18:40.448] [DEBUG] PcapReader: Packet 5 IPv4 192.168.0.164 -> 192.168.0.162 +[14:18:40.450] [DEBUG] PcapReader: Packet 5 UDP 51416 -> 5060 +[14:18:40.451] [DEBUG] PcapReader: Packet 5 application layer, payload length: 319 +[14:18:40.454] [DEBUG] PcapReader: Packet 5 payload preview: "v=0\r\no=- 3977820639 3977820640 IN IP4 192.168.0.164\r\ns=pjmedia\r\nb=AS:84\r\nt=0 0\r\na=X-nat:0\r\nm=audio 4" +[14:18:40.456] [DEBUG] PcapReader: Packet 5 is not SIP +[14:18:40.457] [DEBUG] PcapReader: Processing packet 6, layers: [0xc00022a380 0xc00034a320 0xc000234380 0xc000232360] +[14:18:40.459] [DEBUG] PcapReader: Packet 6 IPv4 192.168.0.162 -> 192.168.0.164 +[14:18:40.461] [DEBUG] PcapReader: Packet 6 UDP 5060 -> 51416 +[14:18:40.462] [DEBUG] PcapReader: Packet 6 application layer, payload length: 239 +[14:18:40.465] [DEBUG] PcapReader: Packet 6 payload preview: "v=0\r\no=- 3977820639 3977820642 IN IP4 192.168.0.162\r\ns=Asterisk\r\nc=IN IP4 192.168.0.162\r\nt=0 0\r\nm=au" +[14:18:40.467] [DEBUG] PcapReader: Packet 6 is not SIP +[14:18:40.468] [DEBUG] PcapReader: Processing packet 7, layers: [0xc00022a3f0 0xc00034a3c0 0xc000234400 0xc0002323f0] +[14:18:40.470] [DEBUG] PcapReader: Packet 7 IPv4 192.168.0.164 -> 192.168.0.162 +[14:18:40.472] [DEBUG] PcapReader: Packet 7 UDP 51416 -> 5060 +[14:18:40.474] [DEBUG] PcapReader: Packet 7 application layer, payload length: 0 +[14:18:40.476] [DEBUG] PcapReader: Packet 7 is not SIP +[14:18:40.478] [DEBUG] PcapReader: Processing packet 8, layers: [0xc00022a460 0xc00034a460 0xc000234480 0xc000232480] +[14:18:40.480] [DEBUG] PcapReader: Packet 8 IPv4 192.168.0.162 -> 192.168.0.164 +[14:18:40.481] [DEBUG] PcapReader: Packet 8 UDP 5060 -> 51416 +[14:18:40.483] [DEBUG] PcapReader: Packet 8 application layer, payload length: 0 +[14:18:40.485] [DEBUG] PcapReader: Packet 8 is not SIP +[14:18:40.487] [INFO] PcapReader: Finished reading. Total packets: 8, SIP packets: 0 +[14:23:18.995] [INFO] Telephony Inspector exited normally diff --git a/logs/2026-01-19_14-23-22.log b/logs/2026-01-19_14-23-22.log new file mode 100644 index 0000000..5f808b0 --- /dev/null +++ b/logs/2026-01-19_14-23-22.log @@ -0,0 +1,237 @@ +[14:23:22.124] [INFO] Logger initialized: logs\2026-01-19_14-23-22.log +[14:23:22.128] [INFO] Starting Telephony Inspector +[14:23:26.188] [INFO] PcapReader: Creating reader for D:\Proyectos\telephony-inspector\data\test.pcapng +[14:23:26.193] [INFO] PcapReader: Opening file D:\Proyectos\telephony-inspector\data\test.pcapng +[14:23:26.197] [INFO] PcapReader: File opened successfully, link type: Ethernet +[14:23:26.200] [DEBUG] PcapReader: BPF filter set for SIP ports +[14:23:26.204] [DEBUG] PcapReader: Processing packet 1, layers: [0xc00048c0e0 0xc00049c000 0xc000332100 0xc00049e000] +[14:23:26.208] [DEBUG] PcapReader: Packet 1 IPv4 192.168.0.164 -> 192.168.0.162 +[14:23:26.212] [DEBUG] PcapReader: Packet 1 UDP 51416 -> 5060, payload len: 1154 +[14:23:26.214] [DEBUG] PcapReader: Packet 1 full payload preview: "INVITE sip:145632@192.168.0.162 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj2b9d528911164b1795466ba22bc7e1e0\r\nMax-Forwards: 70\r\nFrom: ;tag=d158c77860094" +[14:23:26.215] [DEBUG] isSIPPayload: Detected SIP method: INVITE +[14:23:26.218] [DEBUG] PcapReader: Packet 1 detected as SIP, parsing... +[14:23:26.220] [INFO] PcapReader: Found SIP packet 1: INVITE b8060bf9ee0d43cbb60d714e22046659 +[14:23:26.221] [DEBUG] PcapReader: Processing packet 2, layers: [0xc00048c150 0xc00049c0a0 0xc000332180 0xc00049e090] +[14:23:26.223] [DEBUG] PcapReader: Packet 2 IPv4 192.168.0.162 -> 192.168.0.164 +[14:23:26.225] [DEBUG] PcapReader: Packet 2 UDP 5060 -> 51416, payload len: 382 +[14:23:26.227] [DEBUG] PcapReader: Packet 2 full payload preview: "SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj2b9d528911164b1795466ba22bc7e1e0\r\nCall-ID: b8060bf9ee0d43cbb60d714e22046659\r\nFrom: 192.168.0.164 +[14:23:26.238] [DEBUG] PcapReader: Packet 3 UDP 5060 -> 51416, payload len: 1075 +[14:23:26.240] [DEBUG] PcapReader: Packet 3 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj2b9d528911164b1795466ba22bc7e1e0\r\nCall-ID: b8060bf9ee0d43cbb60d714e22046659\r\nFrom: 192.168.0.162 +[14:23:26.251] [DEBUG] PcapReader: Packet 4 UDP 51416 -> 5060, payload len: 371 +[14:23:26.253] [DEBUG] PcapReader: Packet 4 full payload preview: "ACK sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj5a53b67ee71748bf944b029063d2d8b4\r\nMax-Forwards: 70\r\nFrom: ;tag=d158c77860094c3dbd" +[14:23:26.255] [DEBUG] isSIPPayload: Detected SIP method: ACK +[14:23:26.257] [DEBUG] PcapReader: Packet 4 detected as SIP, parsing... +[14:23:26.259] [INFO] PcapReader: Found SIP packet 4: ACK b8060bf9ee0d43cbb60d714e22046659 +[14:23:26.261] [DEBUG] PcapReader: Processing packet 5, layers: [0xc00048c2a0 0xc00049c280 0xc000332300 0xc00049e240] +[14:23:26.263] [DEBUG] PcapReader: Packet 5 IPv4 192.168.0.164 -> 192.168.0.162 +[14:23:26.264] [DEBUG] PcapReader: Packet 5 UDP 51416 -> 5060, payload len: 871 +[14:23:26.267] [DEBUG] PcapReader: Packet 5 full payload preview: "UPDATE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPje6cb582aed174802a817688d4d8b7f0d\r\nMax-Forwards: 70\r\nFrom: ;tag=d158c77860094c3" +[14:23:26.269] [DEBUG] isSIPPayload: Detected SIP method: UPDATE +[14:23:26.271] [DEBUG] PcapReader: Packet 5 detected as SIP, parsing... +[14:23:26.273] [INFO] PcapReader: Found SIP packet 5: UPDATE b8060bf9ee0d43cbb60d714e22046659 +[14:23:26.275] [DEBUG] PcapReader: Processing packet 6, layers: [0xc00048c310 0xc00049c320 0xc000332380 0xc00049e2d0] +[14:23:26.276] [DEBUG] PcapReader: Packet 6 IPv4 192.168.0.162 -> 192.168.0.164 +[14:23:26.278] [DEBUG] PcapReader: Packet 6 UDP 5060 -> 51416, payload len: 939 +[14:23:26.280] [DEBUG] PcapReader: Packet 6 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPje6cb582aed174802a817688d4d8b7f0d\r\nCall-ID: b8060bf9ee0d43cbb60d714e22046659\r\nFrom: 192.168.0.162 +[14:23:26.291] [DEBUG] PcapReader: Packet 7 UDP 51416 -> 5060, payload len: 400 +[14:23:26.293] [DEBUG] PcapReader: Packet 7 full payload preview: "BYE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj8e548a072435464c8b97c3930df0cbca\r\nMax-Forwards: 70\r\nFrom: ;tag=d158c77860094c3dbd" +[14:23:26.295] [DEBUG] isSIPPayload: Detected SIP method: BYE +[14:23:26.297] [DEBUG] PcapReader: Packet 7 detected as SIP, parsing... +[14:23:26.299] [INFO] PcapReader: Found SIP packet 7: BYE b8060bf9ee0d43cbb60d714e22046659 +[14:23:26.301] [DEBUG] PcapReader: Processing packet 8, layers: [0xc00048c3f0 0xc00049c460 0xc000332480 0xc00049e3f0] +[14:23:26.303] [DEBUG] PcapReader: Packet 8 IPv4 192.168.0.162 -> 192.168.0.164 +[14:23:26.305] [DEBUG] PcapReader: Packet 8 UDP 5060 -> 51416, payload len: 416 +[14:23:26.306] [DEBUG] PcapReader: Packet 8 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj8e548a072435464c8b97c3930df0cbca\r\nCall-ID: b8060bf9ee0d43cbb60d714e22046659\r\nFrom: 192.168.0.162 +[14:25:08.963] [DEBUG] PcapReader: Packet 1 UDP 51416 -> 5060, payload len: 1153 +[14:25:08.965] [DEBUG] PcapReader: Packet 1 full payload preview: "INVITE sip:123456@192.168.0.162 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjc988f0aca12d47aa900f472ea93fc0d6\r\nMax-Forwards: 70\r\nFrom: ;tag=d715109fcadd4" +[14:25:08.967] [DEBUG] isSIPPayload: Detected SIP method: INVITE +[14:25:08.969] [DEBUG] PcapReader: Packet 1 detected as SIP, parsing... +[14:25:08.971] [INFO] PcapReader: Found SIP packet 1: INVITE e06cb346194a4f9295d3a325b185912f +[14:25:08.973] [DEBUG] PcapReader: Processing packet 2, layers: [0xc000532150 0xc000112460 0xc000154100 0xc000232120] +[14:25:08.975] [DEBUG] PcapReader: Packet 2 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:08.976] [DEBUG] PcapReader: Packet 2 UDP 5060 -> 51416, payload len: 382 +[14:25:08.978] [DEBUG] PcapReader: Packet 2 full payload preview: "SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjc988f0aca12d47aa900f472ea93fc0d6\r\nCall-ID: e06cb346194a4f9295d3a325b185912f\r\nFrom: 192.168.0.164 +[14:25:08.990] [DEBUG] PcapReader: Packet 3 UDP 5060 -> 51416, payload len: 1075 +[14:25:08.991] [DEBUG] PcapReader: Packet 3 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjc988f0aca12d47aa900f472ea93fc0d6\r\nCall-ID: e06cb346194a4f9295d3a325b185912f\r\nFrom: 192.168.0.162 +[14:25:09.006] [DEBUG] PcapReader: Packet 4 UDP 51416 -> 5060, payload len: 371 +[14:25:09.008] [DEBUG] PcapReader: Packet 4 full payload preview: "ACK sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjf0cf112e8b5d4759a3417bc63cd5481e\r\nMax-Forwards: 70\r\nFrom: ;tag=d715109fcadd492b8f" +[14:25:09.010] [DEBUG] isSIPPayload: Detected SIP method: ACK +[14:25:09.012] [DEBUG] PcapReader: Packet 4 detected as SIP, parsing... +[14:25:09.014] [INFO] PcapReader: Found SIP packet 4: ACK e06cb346194a4f9295d3a325b185912f +[14:25:09.016] [DEBUG] PcapReader: Processing packet 5, layers: [0xc0005322a0 0xc000112780 0xc000154280 0xc0002322d0] +[14:25:09.018] [DEBUG] PcapReader: Packet 5 IPv4 192.168.0.164 -> 192.168.0.162 +[14:25:09.019] [DEBUG] PcapReader: Packet 5 UDP 51416 -> 5060, payload len: 870 +[14:25:09.021] [DEBUG] PcapReader: Packet 5 full payload preview: "UPDATE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj1453a8f281f04023b1a9d246fb587ca3\r\nMax-Forwards: 70\r\nFrom: ;tag=d715109fcadd492" +[14:25:09.023] [DEBUG] isSIPPayload: Detected SIP method: UPDATE +[14:25:09.025] [DEBUG] PcapReader: Packet 5 detected as SIP, parsing... +[14:25:09.027] [INFO] PcapReader: Found SIP packet 5: UPDATE e06cb346194a4f9295d3a325b185912f +[14:25:09.029] [DEBUG] PcapReader: Processing packet 6, layers: [0xc000532310 0xc000112820 0xc000154300 0xc000232360] +[14:25:09.031] [DEBUG] PcapReader: Packet 6 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:09.032] [DEBUG] PcapReader: Packet 6 UDP 5060 -> 51416, payload len: 939 +[14:25:09.034] [DEBUG] PcapReader: Packet 6 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj1453a8f281f04023b1a9d246fb587ca3\r\nCall-ID: e06cb346194a4f9295d3a325b185912f\r\nFrom: 192.168.0.162 +[14:25:09.045] [DEBUG] PcapReader: Packet 7 UDP 51416 -> 5060, payload len: 400 +[14:25:09.047] [DEBUG] PcapReader: Packet 7 full payload preview: "BYE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj15f5d8bca6884d6794f27c43437f1201\r\nMax-Forwards: 70\r\nFrom: ;tag=d715109fcadd492b8f" +[14:25:09.048] [DEBUG] isSIPPayload: Detected SIP method: BYE +[14:25:09.051] [DEBUG] PcapReader: Packet 7 detected as SIP, parsing... +[14:25:09.052] [INFO] PcapReader: Found SIP packet 7: BYE e06cb346194a4f9295d3a325b185912f +[14:25:09.054] [DEBUG] PcapReader: Processing packet 8, layers: [0xc0005323f0 0xc000112960 0xc000154400 0xc000232480] +[14:25:09.056] [DEBUG] PcapReader: Packet 8 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:09.058] [DEBUG] PcapReader: Packet 8 UDP 5060 -> 51416, payload len: 416 +[14:25:09.059] [DEBUG] PcapReader: Packet 8 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj15f5d8bca6884d6794f27c43437f1201\r\nCall-ID: e06cb346194a4f9295d3a325b185912f\r\nFrom: 192.168.0.162 +[14:25:09.071] [DEBUG] PcapReader: Packet 9 UDP 51416 -> 5060, payload len: 1160 +[14:25:09.073] [DEBUG] PcapReader: Packet 9 full payload preview: "INVITE sip:962791077@192.168.0.162 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj25f0f67dd6cb411ab3b6d48b1c0f9ca9\r\nMax-Forwards: 70\r\nFrom: ;tag=820adc6fe0" +[14:25:09.074] [DEBUG] isSIPPayload: Detected SIP method: INVITE +[14:25:09.076] [DEBUG] PcapReader: Packet 9 detected as SIP, parsing... +[14:25:09.078] [INFO] PcapReader: Found SIP packet 9: INVITE a15e53524b304ebf9dd16209f9c95d18 +[14:25:09.080] [DEBUG] PcapReader: Processing packet 10, layers: [0xc0005324d0 0xc000112aa0 0xc000154580 0xc0002325a0] +[14:25:09.081] [DEBUG] PcapReader: Packet 10 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:09.083] [DEBUG] PcapReader: Packet 10 UDP 5060 -> 51416, payload len: 385 +[14:25:09.085] [DEBUG] PcapReader: Packet 10 full payload preview: "SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj25f0f67dd6cb411ab3b6d48b1c0f9ca9\r\nCall-ID: a15e53524b304ebf9dd16209f9c95d18\r\nFrom: 192.168.0.164 +[14:25:09.096] [DEBUG] PcapReader: Packet 11 UDP 5060 -> 51416, payload len: 1078 +[14:25:09.098] [DEBUG] PcapReader: Packet 11 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj25f0f67dd6cb411ab3b6d48b1c0f9ca9\r\nCall-ID: a15e53524b304ebf9dd16209f9c95d18\r\nFrom: 192.168.0.162 +[14:25:09.108] [DEBUG] PcapReader: Packet 12 UDP 51416 -> 5060, payload len: 374 +[14:25:09.110] [DEBUG] PcapReader: Packet 12 full payload preview: "ACK sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj70a4a90d59254a4885b807deca2166fc\r\nMax-Forwards: 70\r\nFrom: ;tag=820adc6fe0694f93bc" +[14:25:09.112] [DEBUG] isSIPPayload: Detected SIP method: ACK +[14:25:09.114] [DEBUG] PcapReader: Packet 12 detected as SIP, parsing... +[14:25:09.116] [INFO] PcapReader: Found SIP packet 12: ACK a15e53524b304ebf9dd16209f9c95d18 +[14:25:09.118] [DEBUG] PcapReader: Processing packet 13, layers: [0xc000532620 0xc000112c80 0xc000154700 0xc000232750] +[14:25:09.120] [DEBUG] PcapReader: Packet 13 IPv4 192.168.0.164 -> 192.168.0.162 +[14:25:09.121] [DEBUG] PcapReader: Packet 13 UDP 51416 -> 5060, payload len: 874 +[14:25:09.123] [DEBUG] PcapReader: Packet 13 full payload preview: "UPDATE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjda3c200bd4664b39b5d59177c5f53017\r\nMax-Forwards: 70\r\nFrom: ;tag=820adc6fe0694f9" +[14:25:09.125] [DEBUG] isSIPPayload: Detected SIP method: UPDATE +[14:25:09.127] [DEBUG] PcapReader: Packet 13 detected as SIP, parsing... +[14:25:09.129] [INFO] PcapReader: Found SIP packet 13: UPDATE a15e53524b304ebf9dd16209f9c95d18 +[14:25:09.131] [DEBUG] PcapReader: Processing packet 14, layers: [0xc000532690 0xc000112d20 0xc000154780 0xc0002327e0] +[14:25:09.132] [DEBUG] PcapReader: Packet 14 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:09.134] [DEBUG] PcapReader: Packet 14 UDP 5060 -> 51416, payload len: 942 +[14:25:09.136] [DEBUG] PcapReader: Packet 14 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjda3c200bd4664b39b5d59177c5f53017\r\nCall-ID: a15e53524b304ebf9dd16209f9c95d18\r\nFrom: 192.168.0.162 +[14:25:09.147] [DEBUG] PcapReader: Packet 15 UDP 51416 -> 5060, payload len: 403 +[14:25:09.148] [DEBUG] PcapReader: Packet 15 full payload preview: "BYE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjcca874595f7840409818bf98eb6f7629\r\nMax-Forwards: 70\r\nFrom: ;tag=820adc6fe0694f93bc" +[14:25:09.150] [DEBUG] isSIPPayload: Detected SIP method: BYE +[14:25:09.152] [DEBUG] PcapReader: Packet 15 detected as SIP, parsing... +[14:25:09.154] [INFO] PcapReader: Found SIP packet 15: BYE a15e53524b304ebf9dd16209f9c95d18 +[14:25:09.156] [DEBUG] PcapReader: Processing packet 16, layers: [0xc000532770 0xc000112e60 0xc000154880 0xc000232900] +[14:25:09.157] [DEBUG] PcapReader: Packet 16 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:09.159] [DEBUG] PcapReader: Packet 16 UDP 5060 -> 51416, payload len: 419 +[14:25:09.162] [DEBUG] PcapReader: Packet 16 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjcca874595f7840409818bf98eb6f7629\r\nCall-ID: a15e53524b304ebf9dd16209f9c95d18\r\nFrom: 192.168.0.162 +[14:25:09.173] [DEBUG] PcapReader: Packet 17 UDP 51416 -> 5060, payload len: 1150 +[14:25:09.175] [DEBUG] PcapReader: Packet 17 full payload preview: "INVITE sip:3333@192.168.0.162 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjb6b83a62e76f41c58fe9b71e82af61a5\r\nMax-Forwards: 70\r\nFrom: ;tag=c2ffb0450ac24fb" +[14:25:09.176] [DEBUG] isSIPPayload: Detected SIP method: INVITE +[14:25:09.178] [DEBUG] PcapReader: Packet 17 detected as SIP, parsing... +[14:25:09.180] [INFO] PcapReader: Found SIP packet 17: INVITE e9270b7c047542ef9af488d27b82162e +[14:25:09.181] [DEBUG] PcapReader: Processing packet 18, layers: [0xc000532850 0xc000112fa0 0xc000154980 0xc000232a20] +[14:25:09.184] [DEBUG] PcapReader: Packet 18 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:09.186] [DEBUG] PcapReader: Packet 18 UDP 5060 -> 51416, payload len: 380 +[14:25:09.187] [DEBUG] PcapReader: Packet 18 full payload preview: "SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjb6b83a62e76f41c58fe9b71e82af61a5\r\nCall-ID: e9270b7c047542ef9af488d27b82162e\r\nFrom: 192.168.0.164 +[14:25:09.198] [DEBUG] PcapReader: Packet 19 UDP 5060 -> 51416, payload len: 1073 +[14:25:09.201] [DEBUG] PcapReader: Packet 19 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjb6b83a62e76f41c58fe9b71e82af61a5\r\nCall-ID: e9270b7c047542ef9af488d27b82162e\r\nFrom: 192.168.0.162 +[14:25:09.212] [DEBUG] PcapReader: Packet 20 UDP 51416 -> 5060, payload len: 369 +[14:25:09.213] [DEBUG] PcapReader: Packet 20 full payload preview: "ACK sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj1ec8fc81ae324187a6feecaf02f04ff1\r\nMax-Forwards: 70\r\nFrom: ;tag=c2ffb0450ac24fb098" +[14:25:09.215] [DEBUG] isSIPPayload: Detected SIP method: ACK +[14:25:09.217] [DEBUG] PcapReader: Packet 20 detected as SIP, parsing... +[14:25:09.219] [INFO] PcapReader: Found SIP packet 20: ACK e9270b7c047542ef9af488d27b82162e +[14:25:09.221] [DEBUG] PcapReader: Processing packet 21, layers: [0xc0005329a0 0xc000113180 0xc000154b00 0xc000232bd0] +[14:25:09.223] [DEBUG] PcapReader: Packet 21 IPv4 192.168.0.164 -> 192.168.0.162 +[14:25:09.224] [DEBUG] PcapReader: Packet 21 UDP 51416 -> 5060, payload len: 869 +[14:25:09.226] [DEBUG] PcapReader: Packet 21 full payload preview: "UPDATE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPja17c7b2ea9da425d85da051e336794c9\r\nMax-Forwards: 70\r\nFrom: ;tag=c2ffb0450ac24fb" +[14:25:09.228] [DEBUG] isSIPPayload: Detected SIP method: UPDATE +[14:25:09.230] [DEBUG] PcapReader: Packet 21 detected as SIP, parsing... +[14:25:09.231] [INFO] PcapReader: Found SIP packet 21: UPDATE e9270b7c047542ef9af488d27b82162e +[14:25:09.233] [DEBUG] PcapReader: Processing packet 22, layers: [0xc000532a10 0xc000113220 0xc000154b80 0xc000232c60] +[14:25:09.235] [DEBUG] PcapReader: Packet 22 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:09.237] [DEBUG] PcapReader: Packet 22 UDP 5060 -> 51416, payload len: 937 +[14:25:09.239] [DEBUG] PcapReader: Packet 22 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPja17c7b2ea9da425d85da051e336794c9\r\nCall-ID: e9270b7c047542ef9af488d27b82162e\r\nFrom: 192.168.0.162 +[14:25:09.250] [DEBUG] PcapReader: Packet 23 UDP 51416 -> 5060, payload len: 398 +[14:25:09.252] [DEBUG] PcapReader: Packet 23 full payload preview: "BYE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjc372eee10c754ece9a873f8e36a6a0e2\r\nMax-Forwards: 70\r\nFrom: ;tag=c2ffb0450ac24fb098" +[14:25:09.253] [DEBUG] isSIPPayload: Detected SIP method: BYE +[14:25:09.255] [DEBUG] PcapReader: Packet 23 detected as SIP, parsing... +[14:25:09.257] [INFO] PcapReader: Found SIP packet 23: BYE e9270b7c047542ef9af488d27b82162e +[14:25:09.259] [DEBUG] PcapReader: Processing packet 24, layers: [0xc000532af0 0xc000113360 0xc000154c80 0xc000232d80] +[14:25:09.261] [DEBUG] PcapReader: Packet 24 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:09.263] [DEBUG] PcapReader: Packet 24 UDP 5060 -> 51416, payload len: 414 +[14:25:09.264] [DEBUG] PcapReader: Packet 24 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjc372eee10c754ece9a873f8e36a6a0e2\r\nCall-ID: e9270b7c047542ef9af488d27b82162e\r\nFrom: 192.168.0.162 +[14:25:35.335] [DEBUG] PcapReader: Packet 1 UDP 51416 -> 5060, payload len: 1153 +[14:25:35.337] [DEBUG] PcapReader: Packet 1 full payload preview: "INVITE sip:123456@192.168.0.162 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjc988f0aca12d47aa900f472ea93fc0d6\r\nMax-Forwards: 70\r\nFrom: ;tag=d715109fcadd4" +[14:25:35.338] [DEBUG] isSIPPayload: Detected SIP method: INVITE +[14:25:35.340] [DEBUG] PcapReader: Packet 1 detected as SIP, parsing... +[14:25:35.342] [INFO] PcapReader: Found SIP packet 1: INVITE e06cb346194a4f9295d3a325b185912f +[14:25:35.344] [DEBUG] PcapReader: Processing packet 2, layers: [0xc00043a2a0 0xc00044e0a0 0xc00031a300 0xc000450090] +[14:25:35.346] [DEBUG] PcapReader: Packet 2 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:35.348] [DEBUG] PcapReader: Packet 2 UDP 5060 -> 51416, payload len: 382 +[14:25:35.350] [DEBUG] PcapReader: Packet 2 full payload preview: "SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjc988f0aca12d47aa900f472ea93fc0d6\r\nCall-ID: e06cb346194a4f9295d3a325b185912f\r\nFrom: 192.168.0.164 +[14:25:35.361] [DEBUG] PcapReader: Packet 3 UDP 5060 -> 51416, payload len: 1075 +[14:25:35.363] [DEBUG] PcapReader: Packet 3 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjc988f0aca12d47aa900f472ea93fc0d6\r\nCall-ID: e06cb346194a4f9295d3a325b185912f\r\nFrom: 192.168.0.162 +[14:25:35.374] [DEBUG] PcapReader: Packet 4 UDP 51416 -> 5060, payload len: 371 +[14:25:35.376] [DEBUG] PcapReader: Packet 4 full payload preview: "ACK sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjf0cf112e8b5d4759a3417bc63cd5481e\r\nMax-Forwards: 70\r\nFrom: ;tag=d715109fcadd492b8f" +[14:25:35.378] [DEBUG] isSIPPayload: Detected SIP method: ACK +[14:25:35.379] [DEBUG] PcapReader: Packet 4 detected as SIP, parsing... +[14:25:35.381] [INFO] PcapReader: Found SIP packet 4: ACK e06cb346194a4f9295d3a325b185912f +[14:25:35.383] [DEBUG] PcapReader: Processing packet 5, layers: [0xc00043a3f0 0xc00044e280 0xc00031a480 0xc000450240] +[14:25:35.385] [DEBUG] PcapReader: Packet 5 IPv4 192.168.0.164 -> 192.168.0.162 +[14:25:35.387] [DEBUG] PcapReader: Packet 5 UDP 51416 -> 5060, payload len: 870 +[14:25:35.389] [DEBUG] PcapReader: Packet 5 full payload preview: "UPDATE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj1453a8f281f04023b1a9d246fb587ca3\r\nMax-Forwards: 70\r\nFrom: ;tag=d715109fcadd492" +[14:25:35.390] [DEBUG] isSIPPayload: Detected SIP method: UPDATE +[14:25:35.392] [DEBUG] PcapReader: Packet 5 detected as SIP, parsing... +[14:25:35.394] [INFO] PcapReader: Found SIP packet 5: UPDATE e06cb346194a4f9295d3a325b185912f +[14:25:35.396] [DEBUG] PcapReader: Processing packet 6, layers: [0xc00043a460 0xc00044e320 0xc00031a500 0xc0004502d0] +[14:25:35.398] [DEBUG] PcapReader: Packet 6 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:35.399] [DEBUG] PcapReader: Packet 6 UDP 5060 -> 51416, payload len: 939 +[14:25:35.401] [DEBUG] PcapReader: Packet 6 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj1453a8f281f04023b1a9d246fb587ca3\r\nCall-ID: e06cb346194a4f9295d3a325b185912f\r\nFrom: 192.168.0.162 +[14:25:35.412] [DEBUG] PcapReader: Packet 7 UDP 51416 -> 5060, payload len: 400 +[14:25:35.414] [DEBUG] PcapReader: Packet 7 full payload preview: "BYE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj15f5d8bca6884d6794f27c43437f1201\r\nMax-Forwards: 70\r\nFrom: ;tag=d715109fcadd492b8f" +[14:25:35.415] [DEBUG] isSIPPayload: Detected SIP method: BYE +[14:25:35.417] [DEBUG] PcapReader: Packet 7 detected as SIP, parsing... +[14:25:35.419] [INFO] PcapReader: Found SIP packet 7: BYE e06cb346194a4f9295d3a325b185912f +[14:25:35.421] [DEBUG] PcapReader: Processing packet 8, layers: [0xc00043a540 0xc00044e460 0xc00031a600 0xc0004503f0] +[14:25:35.439] [DEBUG] PcapReader: Packet 8 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:35.440] [DEBUG] PcapReader: Packet 8 UDP 5060 -> 51416, payload len: 416 +[14:25:35.442] [DEBUG] PcapReader: Packet 8 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj15f5d8bca6884d6794f27c43437f1201\r\nCall-ID: e06cb346194a4f9295d3a325b185912f\r\nFrom: 192.168.0.162 +[14:25:35.453] [DEBUG] PcapReader: Packet 9 UDP 51416 -> 5060, payload len: 1160 +[14:25:35.454] [DEBUG] PcapReader: Packet 9 full payload preview: "INVITE sip:962791077@192.168.0.162 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj25f0f67dd6cb411ab3b6d48b1c0f9ca9\r\nMax-Forwards: 70\r\nFrom: ;tag=820adc6fe0" +[14:25:35.457] [DEBUG] isSIPPayload: Detected SIP method: INVITE +[14:25:35.459] [DEBUG] PcapReader: Packet 9 detected as SIP, parsing... +[14:25:35.461] [INFO] PcapReader: Found SIP packet 9: INVITE a15e53524b304ebf9dd16209f9c95d18 +[14:25:35.462] [DEBUG] PcapReader: Processing packet 10, layers: [0xc00043a620 0xc00044e5a0 0xc00031a700 0xc000450510] +[14:25:35.465] [DEBUG] PcapReader: Packet 10 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:35.467] [DEBUG] PcapReader: Packet 10 UDP 5060 -> 51416, payload len: 385 +[14:25:35.468] [DEBUG] PcapReader: Packet 10 full payload preview: "SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj25f0f67dd6cb411ab3b6d48b1c0f9ca9\r\nCall-ID: a15e53524b304ebf9dd16209f9c95d18\r\nFrom: 192.168.0.164 +[14:25:35.480] [DEBUG] PcapReader: Packet 11 UDP 5060 -> 51416, payload len: 1078 +[14:25:35.481] [DEBUG] PcapReader: Packet 11 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj25f0f67dd6cb411ab3b6d48b1c0f9ca9\r\nCall-ID: a15e53524b304ebf9dd16209f9c95d18\r\nFrom: 192.168.0.162 +[14:25:35.492] [DEBUG] PcapReader: Packet 12 UDP 51416 -> 5060, payload len: 374 +[14:25:35.494] [DEBUG] PcapReader: Packet 12 full payload preview: "ACK sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj70a4a90d59254a4885b807deca2166fc\r\nMax-Forwards: 70\r\nFrom: ;tag=820adc6fe0694f93bc" +[14:25:35.496] [DEBUG] isSIPPayload: Detected SIP method: ACK +[14:25:35.498] [DEBUG] PcapReader: Packet 12 detected as SIP, parsing... +[14:25:35.500] [INFO] PcapReader: Found SIP packet 12: ACK a15e53524b304ebf9dd16209f9c95d18 +[14:25:35.502] [DEBUG] PcapReader: Processing packet 13, layers: [0xc00043a770 0xc00044e780 0xc00031a880 0xc0004506c0] +[14:25:35.504] [DEBUG] PcapReader: Packet 13 IPv4 192.168.0.164 -> 192.168.0.162 +[14:25:35.505] [DEBUG] PcapReader: Packet 13 UDP 51416 -> 5060, payload len: 874 +[14:25:35.507] [DEBUG] PcapReader: Packet 13 full payload preview: "UPDATE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjda3c200bd4664b39b5d59177c5f53017\r\nMax-Forwards: 70\r\nFrom: ;tag=820adc6fe0694f9" +[14:25:35.509] [DEBUG] isSIPPayload: Detected SIP method: UPDATE +[14:25:35.511] [DEBUG] PcapReader: Packet 13 detected as SIP, parsing... +[14:25:35.513] [INFO] PcapReader: Found SIP packet 13: UPDATE a15e53524b304ebf9dd16209f9c95d18 +[14:25:35.515] [DEBUG] PcapReader: Processing packet 14, layers: [0xc00043a7e0 0xc00044e820 0xc00031a900 0xc000450750] +[14:25:35.517] [DEBUG] PcapReader: Packet 14 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:35.519] [DEBUG] PcapReader: Packet 14 UDP 5060 -> 51416, payload len: 942 +[14:25:35.521] [DEBUG] PcapReader: Packet 14 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjda3c200bd4664b39b5d59177c5f53017\r\nCall-ID: a15e53524b304ebf9dd16209f9c95d18\r\nFrom: 192.168.0.162 +[14:25:35.532] [DEBUG] PcapReader: Packet 15 UDP 51416 -> 5060, payload len: 403 +[14:25:35.534] [DEBUG] PcapReader: Packet 15 full payload preview: "BYE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjcca874595f7840409818bf98eb6f7629\r\nMax-Forwards: 70\r\nFrom: ;tag=820adc6fe0694f93bc" +[14:25:35.535] [DEBUG] isSIPPayload: Detected SIP method: BYE +[14:25:35.537] [DEBUG] PcapReader: Packet 15 detected as SIP, parsing... +[14:25:35.539] [INFO] PcapReader: Found SIP packet 15: BYE a15e53524b304ebf9dd16209f9c95d18 +[14:25:35.541] [DEBUG] PcapReader: Processing packet 16, layers: [0xc00043a8c0 0xc00044e960 0xc00031aa00 0xc000450870] +[14:25:35.543] [DEBUG] PcapReader: Packet 16 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:35.545] [DEBUG] PcapReader: Packet 16 UDP 5060 -> 51416, payload len: 419 +[14:25:35.546] [DEBUG] PcapReader: Packet 16 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjcca874595f7840409818bf98eb6f7629\r\nCall-ID: a15e53524b304ebf9dd16209f9c95d18\r\nFrom: 192.168.0.162 +[14:25:35.557] [DEBUG] PcapReader: Packet 17 UDP 51416 -> 5060, payload len: 1150 +[14:25:35.559] [DEBUG] PcapReader: Packet 17 full payload preview: "INVITE sip:3333@192.168.0.162 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjb6b83a62e76f41c58fe9b71e82af61a5\r\nMax-Forwards: 70\r\nFrom: ;tag=c2ffb0450ac24fb" +[14:25:35.561] [DEBUG] isSIPPayload: Detected SIP method: INVITE +[14:25:35.563] [DEBUG] PcapReader: Packet 17 detected as SIP, parsing... +[14:25:35.565] [INFO] PcapReader: Found SIP packet 17: INVITE e9270b7c047542ef9af488d27b82162e +[14:25:35.567] [DEBUG] PcapReader: Processing packet 18, layers: [0xc00043a9a0 0xc00044eaa0 0xc00031ab00 0xc000450990] +[14:25:35.568] [DEBUG] PcapReader: Packet 18 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:35.570] [DEBUG] PcapReader: Packet 18 UDP 5060 -> 51416, payload len: 380 +[14:25:35.572] [DEBUG] PcapReader: Packet 18 full payload preview: "SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjb6b83a62e76f41c58fe9b71e82af61a5\r\nCall-ID: e9270b7c047542ef9af488d27b82162e\r\nFrom: 192.168.0.164 +[14:25:35.583] [DEBUG] PcapReader: Packet 19 UDP 5060 -> 51416, payload len: 1073 +[14:25:35.585] [DEBUG] PcapReader: Packet 19 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjb6b83a62e76f41c58fe9b71e82af61a5\r\nCall-ID: e9270b7c047542ef9af488d27b82162e\r\nFrom: 192.168.0.162 +[14:25:35.596] [DEBUG] PcapReader: Packet 20 UDP 51416 -> 5060, payload len: 369 +[14:25:35.598] [DEBUG] PcapReader: Packet 20 full payload preview: "ACK sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj1ec8fc81ae324187a6feecaf02f04ff1\r\nMax-Forwards: 70\r\nFrom: ;tag=c2ffb0450ac24fb098" +[14:25:35.600] [DEBUG] isSIPPayload: Detected SIP method: ACK +[14:25:35.602] [DEBUG] PcapReader: Packet 20 detected as SIP, parsing... +[14:25:35.604] [INFO] PcapReader: Found SIP packet 20: ACK e9270b7c047542ef9af488d27b82162e +[14:25:35.605] [DEBUG] PcapReader: Processing packet 21, layers: [0xc00043aaf0 0xc00044ec80 0xc00031ac80 0xc000450b40] +[14:25:35.607] [DEBUG] PcapReader: Packet 21 IPv4 192.168.0.164 -> 192.168.0.162 +[14:25:35.609] [DEBUG] PcapReader: Packet 21 UDP 51416 -> 5060, payload len: 869 +[14:25:35.611] [DEBUG] PcapReader: Packet 21 full payload preview: "UPDATE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPja17c7b2ea9da425d85da051e336794c9\r\nMax-Forwards: 70\r\nFrom: ;tag=c2ffb0450ac24fb" +[14:25:35.613] [DEBUG] isSIPPayload: Detected SIP method: UPDATE +[14:25:35.615] [DEBUG] PcapReader: Packet 21 detected as SIP, parsing... +[14:25:35.617] [INFO] PcapReader: Found SIP packet 21: UPDATE e9270b7c047542ef9af488d27b82162e +[14:25:35.618] [DEBUG] PcapReader: Processing packet 22, layers: [0xc00043ab60 0xc00044ed20 0xc00031ad00 0xc000450bd0] +[14:25:35.620] [DEBUG] PcapReader: Packet 22 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:35.622] [DEBUG] PcapReader: Packet 22 UDP 5060 -> 51416, payload len: 937 +[14:25:35.625] [DEBUG] PcapReader: Packet 22 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPja17c7b2ea9da425d85da051e336794c9\r\nCall-ID: e9270b7c047542ef9af488d27b82162e\r\nFrom: 192.168.0.162 +[14:25:35.636] [DEBUG] PcapReader: Packet 23 UDP 51416 -> 5060, payload len: 398 +[14:25:35.638] [DEBUG] PcapReader: Packet 23 full payload preview: "BYE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjc372eee10c754ece9a873f8e36a6a0e2\r\nMax-Forwards: 70\r\nFrom: ;tag=c2ffb0450ac24fb098" +[14:25:35.640] [DEBUG] isSIPPayload: Detected SIP method: BYE +[14:25:35.642] [DEBUG] PcapReader: Packet 23 detected as SIP, parsing... +[14:25:35.644] [INFO] PcapReader: Found SIP packet 23: BYE e9270b7c047542ef9af488d27b82162e +[14:25:35.645] [DEBUG] PcapReader: Processing packet 24, layers: [0xc00043ac40 0xc00044ee60 0xc00031ae00 0xc000450cf0] +[14:25:35.647] [DEBUG] PcapReader: Packet 24 IPv4 192.168.0.162 -> 192.168.0.164 +[14:25:35.649] [DEBUG] PcapReader: Packet 24 UDP 5060 -> 51416, payload len: 414 +[14:25:35.651] [DEBUG] PcapReader: Packet 24 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjc372eee10c754ece9a873f8e36a6a0e2\r\nCall-ID: e9270b7c047542ef9af488d27b82162e\r\nFrom: 192.168.0.162 +[14:28:12.275] [DEBUG] PcapReader: Packet 1 UDP 51416 -> 5060, payload len: 1153 +[14:28:12.277] [DEBUG] PcapReader: Packet 1 full payload preview: "INVITE sip:123456@192.168.0.162 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjc988f0aca12d47aa900f472ea93fc0d6\r\nMax-Forwards: 70\r\nFrom: ;tag=d715109fcadd4" +[14:28:12.280] [DEBUG] isSIPPayload: Detected SIP method: INVITE +[14:28:12.281] [DEBUG] PcapReader: Packet 1 detected as SIP, parsing... +[14:28:12.283] [INFO] PcapReader: Found SIP packet 1: INVITE e06cb346194a4f9295d3a325b185912f +[14:28:12.285] [DEBUG] PcapReader: Processing packet 2, layers: [0xc0000e3b90 0xc0000b86e0 0xc0000e0300 0xc00037e090] +[14:28:12.286] [DEBUG] PcapReader: Packet 2 IPv4 192.168.0.162 -> 192.168.0.164 +[14:28:12.289] [DEBUG] PcapReader: Packet 2 UDP 5060 -> 51416, payload len: 382 +[14:28:12.290] [DEBUG] PcapReader: Packet 2 full payload preview: "SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjc988f0aca12d47aa900f472ea93fc0d6\r\nCall-ID: e06cb346194a4f9295d3a325b185912f\r\nFrom: 192.168.0.164 +[14:28:12.302] [DEBUG] PcapReader: Packet 3 UDP 5060 -> 51416, payload len: 1075 +[14:28:12.304] [DEBUG] PcapReader: Packet 3 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjc988f0aca12d47aa900f472ea93fc0d6\r\nCall-ID: e06cb346194a4f9295d3a325b185912f\r\nFrom: 192.168.0.162 +[14:28:12.315] [DEBUG] PcapReader: Packet 4 UDP 51416 -> 5060, payload len: 371 +[14:28:12.317] [DEBUG] PcapReader: Packet 4 full payload preview: "ACK sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjf0cf112e8b5d4759a3417bc63cd5481e\r\nMax-Forwards: 70\r\nFrom: ;tag=d715109fcadd492b8f" +[14:28:12.319] [DEBUG] isSIPPayload: Detected SIP method: ACK +[14:28:12.320] [DEBUG] PcapReader: Packet 4 detected as SIP, parsing... +[14:28:12.322] [INFO] PcapReader: Found SIP packet 4: ACK e06cb346194a4f9295d3a325b185912f +[14:28:12.324] [DEBUG] PcapReader: Processing packet 5, layers: [0xc0000e3ce0 0xc0000b88c0 0xc0000e0480 0xc00037e240] +[14:28:12.326] [DEBUG] PcapReader: Packet 5 IPv4 192.168.0.164 -> 192.168.0.162 +[14:28:12.328] [DEBUG] PcapReader: Packet 5 UDP 51416 -> 5060, payload len: 870 +[14:28:12.330] [DEBUG] PcapReader: Packet 5 full payload preview: "UPDATE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj1453a8f281f04023b1a9d246fb587ca3\r\nMax-Forwards: 70\r\nFrom: ;tag=d715109fcadd492" +[14:28:12.332] [DEBUG] isSIPPayload: Detected SIP method: UPDATE +[14:28:12.334] [DEBUG] PcapReader: Packet 5 detected as SIP, parsing... +[14:28:12.335] [INFO] PcapReader: Found SIP packet 5: UPDATE e06cb346194a4f9295d3a325b185912f +[14:28:12.337] [DEBUG] PcapReader: Processing packet 6, layers: [0xc0000e3d50 0xc0000b8960 0xc0000e0500 0xc00037e2d0] +[14:28:12.339] [DEBUG] PcapReader: Packet 6 IPv4 192.168.0.162 -> 192.168.0.164 +[14:28:12.341] [DEBUG] PcapReader: Packet 6 UDP 5060 -> 51416, payload len: 939 +[14:28:12.342] [DEBUG] PcapReader: Packet 6 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj1453a8f281f04023b1a9d246fb587ca3\r\nCall-ID: e06cb346194a4f9295d3a325b185912f\r\nFrom: 192.168.0.162 +[14:28:12.354] [DEBUG] PcapReader: Packet 7 UDP 51416 -> 5060, payload len: 400 +[14:28:12.356] [DEBUG] PcapReader: Packet 7 full payload preview: "BYE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj15f5d8bca6884d6794f27c43437f1201\r\nMax-Forwards: 70\r\nFrom: ;tag=d715109fcadd492b8f" +[14:28:12.358] [DEBUG] isSIPPayload: Detected SIP method: BYE +[14:28:12.360] [DEBUG] PcapReader: Packet 7 detected as SIP, parsing... +[14:28:12.361] [INFO] PcapReader: Found SIP packet 7: BYE e06cb346194a4f9295d3a325b185912f +[14:28:12.363] [DEBUG] PcapReader: Processing packet 8, layers: [0xc0000e3e30 0xc0000b8aa0 0xc0000e0600 0xc00037e3f0] +[14:28:12.365] [DEBUG] PcapReader: Packet 8 IPv4 192.168.0.162 -> 192.168.0.164 +[14:28:12.367] [DEBUG] PcapReader: Packet 8 UDP 5060 -> 51416, payload len: 416 +[14:28:12.369] [DEBUG] PcapReader: Packet 8 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj15f5d8bca6884d6794f27c43437f1201\r\nCall-ID: e06cb346194a4f9295d3a325b185912f\r\nFrom: 192.168.0.162 +[14:28:12.380] [DEBUG] PcapReader: Packet 9 UDP 51416 -> 5060, payload len: 1160 +[14:28:12.382] [DEBUG] PcapReader: Packet 9 full payload preview: "INVITE sip:962791077@192.168.0.162 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj25f0f67dd6cb411ab3b6d48b1c0f9ca9\r\nMax-Forwards: 70\r\nFrom: ;tag=820adc6fe0" +[14:28:12.384] [DEBUG] isSIPPayload: Detected SIP method: INVITE +[14:28:12.386] [DEBUG] PcapReader: Packet 9 detected as SIP, parsing... +[14:28:12.388] [INFO] PcapReader: Found SIP packet 9: INVITE a15e53524b304ebf9dd16209f9c95d18 +[14:28:12.390] [DEBUG] PcapReader: Processing packet 10, layers: [0xc0000e3f10 0xc0000b8be0 0xc0000e0700 0xc00037e510] +[14:28:12.391] [DEBUG] PcapReader: Packet 10 IPv4 192.168.0.162 -> 192.168.0.164 +[14:28:12.394] [DEBUG] PcapReader: Packet 10 UDP 5060 -> 51416, payload len: 385 +[14:28:12.395] [DEBUG] PcapReader: Packet 10 full payload preview: "SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj25f0f67dd6cb411ab3b6d48b1c0f9ca9\r\nCall-ID: a15e53524b304ebf9dd16209f9c95d18\r\nFrom: 192.168.0.164 +[14:28:12.407] [DEBUG] PcapReader: Packet 11 UDP 5060 -> 51416, payload len: 1078 +[14:28:12.408] [DEBUG] PcapReader: Packet 11 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPj25f0f67dd6cb411ab3b6d48b1c0f9ca9\r\nCall-ID: a15e53524b304ebf9dd16209f9c95d18\r\nFrom: 192.168.0.162 +[14:28:12.419] [DEBUG] PcapReader: Packet 12 UDP 51416 -> 5060, payload len: 374 +[14:28:12.421] [DEBUG] PcapReader: Packet 12 full payload preview: "ACK sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj70a4a90d59254a4885b807deca2166fc\r\nMax-Forwards: 70\r\nFrom: ;tag=820adc6fe0694f93bc" +[14:28:12.423] [DEBUG] isSIPPayload: Detected SIP method: ACK +[14:28:12.425] [DEBUG] PcapReader: Packet 12 detected as SIP, parsing... +[14:28:12.427] [INFO] PcapReader: Found SIP packet 12: ACK a15e53524b304ebf9dd16209f9c95d18 +[14:28:12.429] [DEBUG] PcapReader: Processing packet 13, layers: [0xc0004980e0 0xc0000b8dc0 0xc0000e0880 0xc00037e6c0] +[14:28:12.431] [DEBUG] PcapReader: Packet 13 IPv4 192.168.0.164 -> 192.168.0.162 +[14:28:12.433] [DEBUG] PcapReader: Packet 13 UDP 51416 -> 5060, payload len: 874 +[14:28:12.434] [DEBUG] PcapReader: Packet 13 full payload preview: "UPDATE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjda3c200bd4664b39b5d59177c5f53017\r\nMax-Forwards: 70\r\nFrom: ;tag=820adc6fe0694f9" +[14:28:12.436] [DEBUG] isSIPPayload: Detected SIP method: UPDATE +[14:28:12.438] [DEBUG] PcapReader: Packet 13 detected as SIP, parsing... +[14:28:12.440] [INFO] PcapReader: Found SIP packet 13: UPDATE a15e53524b304ebf9dd16209f9c95d18 +[14:28:12.442] [DEBUG] PcapReader: Processing packet 14, layers: [0xc000498150 0xc0000b8e60 0xc0000e0900 0xc00037e750] +[14:28:12.444] [DEBUG] PcapReader: Packet 14 IPv4 192.168.0.162 -> 192.168.0.164 +[14:28:12.446] [DEBUG] PcapReader: Packet 14 UDP 5060 -> 51416, payload len: 942 +[14:28:12.448] [DEBUG] PcapReader: Packet 14 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjda3c200bd4664b39b5d59177c5f53017\r\nCall-ID: a15e53524b304ebf9dd16209f9c95d18\r\nFrom: 192.168.0.162 +[14:28:12.459] [DEBUG] PcapReader: Packet 15 UDP 51416 -> 5060, payload len: 403 +[14:28:12.461] [DEBUG] PcapReader: Packet 15 full payload preview: "BYE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjcca874595f7840409818bf98eb6f7629\r\nMax-Forwards: 70\r\nFrom: ;tag=820adc6fe0694f93bc" +[14:28:12.462] [DEBUG] isSIPPayload: Detected SIP method: BYE +[14:28:12.464] [DEBUG] PcapReader: Packet 15 detected as SIP, parsing... +[14:28:12.466] [INFO] PcapReader: Found SIP packet 15: BYE a15e53524b304ebf9dd16209f9c95d18 +[14:28:12.468] [DEBUG] PcapReader: Processing packet 16, layers: [0xc000498230 0xc0000b8fa0 0xc0000e0a00 0xc00037e870] +[14:28:12.470] [DEBUG] PcapReader: Packet 16 IPv4 192.168.0.162 -> 192.168.0.164 +[14:28:12.472] [DEBUG] PcapReader: Packet 16 UDP 5060 -> 51416, payload len: 419 +[14:28:12.474] [DEBUG] PcapReader: Packet 16 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjcca874595f7840409818bf98eb6f7629\r\nCall-ID: a15e53524b304ebf9dd16209f9c95d18\r\nFrom: 192.168.0.162 +[14:28:12.489] [DEBUG] PcapReader: Packet 17 UDP 51416 -> 5060, payload len: 1150 +[14:28:12.492] [DEBUG] PcapReader: Packet 17 full payload preview: "INVITE sip:3333@192.168.0.162 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjb6b83a62e76f41c58fe9b71e82af61a5\r\nMax-Forwards: 70\r\nFrom: ;tag=c2ffb0450ac24fb" +[14:28:12.493] [DEBUG] isSIPPayload: Detected SIP method: INVITE +[14:28:12.495] [DEBUG] PcapReader: Packet 17 detected as SIP, parsing... +[14:28:12.497] [INFO] PcapReader: Found SIP packet 17: INVITE e9270b7c047542ef9af488d27b82162e +[14:28:12.499] [DEBUG] PcapReader: Processing packet 18, layers: [0xc000498310 0xc0000b90e0 0xc0000e0b00 0xc00037e990] +[14:28:12.501] [DEBUG] PcapReader: Packet 18 IPv4 192.168.0.162 -> 192.168.0.164 +[14:28:12.504] [DEBUG] PcapReader: Packet 18 UDP 5060 -> 51416, payload len: 380 +[14:28:12.505] [DEBUG] PcapReader: Packet 18 full payload preview: "SIP/2.0 100 Trying\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjb6b83a62e76f41c58fe9b71e82af61a5\r\nCall-ID: e9270b7c047542ef9af488d27b82162e\r\nFrom: 192.168.0.164 +[14:28:12.517] [DEBUG] PcapReader: Packet 19 UDP 5060 -> 51416, payload len: 1073 +[14:28:12.518] [DEBUG] PcapReader: Packet 19 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjb6b83a62e76f41c58fe9b71e82af61a5\r\nCall-ID: e9270b7c047542ef9af488d27b82162e\r\nFrom: 192.168.0.162 +[14:28:12.530] [DEBUG] PcapReader: Packet 20 UDP 51416 -> 5060, payload len: 369 +[14:28:12.532] [DEBUG] PcapReader: Packet 20 full payload preview: "ACK sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPj1ec8fc81ae324187a6feecaf02f04ff1\r\nMax-Forwards: 70\r\nFrom: ;tag=c2ffb0450ac24fb098" +[14:28:12.534] [DEBUG] isSIPPayload: Detected SIP method: ACK +[14:28:12.535] [DEBUG] PcapReader: Packet 20 detected as SIP, parsing... +[14:28:12.537] [INFO] PcapReader: Found SIP packet 20: ACK e9270b7c047542ef9af488d27b82162e +[14:28:12.539] [DEBUG] PcapReader: Processing packet 21, layers: [0xc000498460 0xc0000b92c0 0xc0000e0c80 0xc00037eb40] +[14:28:12.541] [DEBUG] PcapReader: Packet 21 IPv4 192.168.0.164 -> 192.168.0.162 +[14:28:12.543] [DEBUG] PcapReader: Packet 21 UDP 51416 -> 5060, payload len: 869 +[14:28:12.545] [DEBUG] PcapReader: Packet 21 full payload preview: "UPDATE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPja17c7b2ea9da425d85da051e336794c9\r\nMax-Forwards: 70\r\nFrom: ;tag=c2ffb0450ac24fb" +[14:28:12.547] [DEBUG] isSIPPayload: Detected SIP method: UPDATE +[14:28:12.548] [DEBUG] PcapReader: Packet 21 detected as SIP, parsing... +[14:28:12.550] [INFO] PcapReader: Found SIP packet 21: UPDATE e9270b7c047542ef9af488d27b82162e +[14:28:12.552] [DEBUG] PcapReader: Processing packet 22, layers: [0xc0004984d0 0xc0000b9360 0xc0000e0d00 0xc00037ebd0] +[14:28:12.554] [DEBUG] PcapReader: Packet 22 IPv4 192.168.0.162 -> 192.168.0.164 +[14:28:12.555] [DEBUG] PcapReader: Packet 22 UDP 5060 -> 51416, payload len: 937 +[14:28:12.558] [DEBUG] PcapReader: Packet 22 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPja17c7b2ea9da425d85da051e336794c9\r\nCall-ID: e9270b7c047542ef9af488d27b82162e\r\nFrom: 192.168.0.162 +[14:28:12.568] [DEBUG] PcapReader: Packet 23 UDP 51416 -> 5060, payload len: 398 +[14:28:12.570] [DEBUG] PcapReader: Packet 23 full payload preview: "BYE sip:192.168.0.162:5060 SIP/2.0\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport;branch=z9hG4bKPjc372eee10c754ece9a873f8e36a6a0e2\r\nMax-Forwards: 70\r\nFrom: ;tag=c2ffb0450ac24fb098" +[14:28:12.572] [DEBUG] isSIPPayload: Detected SIP method: BYE +[14:28:12.574] [DEBUG] PcapReader: Packet 23 detected as SIP, parsing... +[14:28:12.576] [INFO] PcapReader: Found SIP packet 23: BYE e9270b7c047542ef9af488d27b82162e +[14:28:12.578] [DEBUG] PcapReader: Processing packet 24, layers: [0xc0004985b0 0xc0000b94a0 0xc0000e0e00 0xc00037ecf0] +[14:28:12.579] [DEBUG] PcapReader: Packet 24 IPv4 192.168.0.162 -> 192.168.0.164 +[14:28:12.581] [DEBUG] PcapReader: Packet 24 UDP 5060 -> 51416, payload len: 414 +[14:28:12.583] [DEBUG] PcapReader: Packet 24 full payload preview: "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 192.168.0.164:51416;rport=51416;received=192.168.0.164;branch=z9hG4bKPjc372eee10c754ece9a873f8e36a6a0e2\r\nCall-ID: e9270b7c047542ef9af488d27b82162e\r\nFrom: