From 77729e07e15c18990dc84546854f4ff218cc26d7 Mon Sep 17 00:00:00 2001 From: Sudo-Ivan Date: Sat, 4 Jan 2025 18:37:53 -0600 Subject: [PATCH] packet interceptor --- cmd/reticulum-go/main.go | 112 ++++++++++++++++++++++----- test-utilities/packet-interceptor.go | 104 +++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 20 deletions(-) create mode 100644 test-utilities/packet-interceptor.go diff --git a/cmd/reticulum-go/main.go b/cmd/reticulum-go/main.go index 758749a..05ee9f8 100644 --- a/cmd/reticulum-go/main.go +++ b/cmd/reticulum-go/main.go @@ -20,10 +20,13 @@ import ( "github.com/Sudo-Ivan/reticulum-go/pkg/interfaces" "github.com/Sudo-Ivan/reticulum-go/pkg/packet" "github.com/Sudo-Ivan/reticulum-go/pkg/transport" + testutils "github.com/Sudo-Ivan/reticulum-go/test-utilities" ) var ( - debugLevel = flag.Int("debug", 7, "Debug level (0-7)") + debugLevel = flag.Int("debug", 7, "Debug level (0-7)") + interceptPackets = flag.Bool("intercept-packets", false, "Enable packet interception") + interceptOutput = flag.String("intercept-output", "packets.log", "Output file for intercepted packets") ) func debugLog(level int, format string, v ...interface{}) { @@ -123,6 +126,31 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) { destination: dest, } + // Initialize packet interceptor if enabled + var interceptor *testutils.PacketInterceptor + if *interceptPackets { + var err error + interceptor, err = testutils.NewPacketInterceptor(*interceptOutput) + if err != nil { + debugLog(DEBUG_ERROR, "Failed to initialize packet interceptor: %v", err) + } else { + debugLog(DEBUG_INFO, "Packet interception enabled") + } + } + + // Create a wrapper for the packet callback that includes interception + packetCallbackWrapper := func(data []byte, iface common.NetworkInterface) { + if interceptor != nil { + if err := interceptor.InterceptIncoming(data, iface); err != nil { + debugLog(DEBUG_ERROR, "Failed to intercept incoming packet: %v", err) + } + } + // Call original callback + if r.transport != nil { + r.transport.HandlePacket(data, iface) + } + } + // Initialize interfaces from config for name, ifaceConfig := range cfg.Interfaces { if !ifaceConfig.Enabled { @@ -164,8 +192,19 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) { continue } + // Set the wrapped packet callback + iface.SetPacketCallback(packetCallbackWrapper) + + // Wrap interface for outgoing packet interception + if interceptor != nil { + iface = interfaces.NewInterceptedInterface(iface, func(data []byte, ni common.NetworkInterface) error { + return interceptor.InterceptOutgoing(data, ni) + }) + } + debugLog(2, "Configuring interface %s (type=%s)...", name, ifaceConfig.Type) r.interfaces = append(r.interfaces, iface) + debugLog(3, "Interface %s started successfully", name) } return r, nil @@ -176,7 +215,12 @@ func (r *Reticulum) handleInterface(iface common.NetworkInterface) { ch := channel.NewChannel(&transportWrapper{r.transport}) r.channels[iface.GetName()] = ch - debugLog(DEBUG_VERBOSE, "Created channel for interface %s with transport wrapper", iface.GetName()) + + // Get interceptor if enabled + var interceptor *testutils.PacketInterceptor + if *interceptPackets { + interceptor, _ = testutils.NewPacketInterceptor(*interceptOutput) + } rw := buffer.CreateBidirectionalBuffer( 1, @@ -188,28 +232,22 @@ func (r *Reticulum) handleInterface(iface common.NetworkInterface) { iface.ProcessIncoming(data) if len(data) > 0 { + // Intercept incoming packet before processing + if interceptor != nil { + if err := interceptor.InterceptIncoming(data, iface); err != nil { + debugLog(DEBUG_ERROR, "Failed to intercept incoming packet: %v", err) + } + } + debugLog(DEBUG_TRACE, "Interface %s: Received packet type 0x%02x", iface.GetName(), data[0]) r.transport.HandlePacket(data, iface) } - - debugLog(5, "Processed %d bytes from interface %s", size, iface.GetName()) }, ) r.buffers[iface.GetName()] = &buffer.Buffer{ ReadWriter: rw, } - debugLog(DEBUG_VERBOSE, "Created bidirectional buffer for interface %s", iface.GetName()) - - iface.SetPacketCallback(func(data []byte, ni common.NetworkInterface) { - if buf, ok := r.buffers[ni.GetName()]; ok { - if _, err := buf.Write(data); err != nil { - debugLog(1, "Error writing to buffer for interface %s: %v", ni.GetName(), err) - } - debugLog(6, "Written %d bytes to interface %s buffer", len(data), ni.GetName()) - } - r.transport.HandlePacket(data, ni) - }) } func (r *Reticulum) monitorInterfaces() { @@ -286,9 +324,7 @@ func main() { go r.monitorInterfaces() // Register announce handler - handler := &AnnounceHandler{ - aspectFilter: []string{"*"}, - } + handler := NewAnnounceHandler(r, []string{"*"}) r.transport.RegisterAnnounceHandler(handler) // Start Reticulum @@ -538,6 +574,14 @@ func (r *Reticulum) Stop() error { type AnnounceHandler struct { aspectFilter []string + reticulum *Reticulum +} + +func NewAnnounceHandler(r *Reticulum, aspectFilter []string) *AnnounceHandler { + return &AnnounceHandler{ + aspectFilter: aspectFilter, + reticulum: r, + } } func (h *AnnounceHandler) AspectFilter() []string { @@ -546,12 +590,31 @@ func (h *AnnounceHandler) AspectFilter() []string { func (h *AnnounceHandler) ReceivedAnnounce(destHash []byte, id interface{}, appData []byte) error { debugLog(DEBUG_INFO, "Received announce from %x", destHash) + debugLog(DEBUG_PACKETS, "Raw announce data: %x", appData) + // Parse msgpack array if len(appData) > 0 { - debugLog(DEBUG_VERBOSE, "Announce app data: %s", string(appData)) + if appData[0] == 0x92 { // msgpack array of 2 elements + var pos = 1 + + // Parse first element (name) + if appData[pos] == 0xc4 { // bin 8 format + nameLen := int(appData[pos+1]) + name := string(appData[pos+2 : pos+2+nameLen]) + pos += 2 + nameLen + debugLog(DEBUG_VERBOSE, "Announce name: %s", name) + + // Parse second element (app data) + if pos < len(appData) && appData[pos] == 0xc4 { // bin 8 format + dataLen := int(appData[pos+1]) + data := appData[pos+2 : pos+2+dataLen] + debugLog(DEBUG_VERBOSE, "Announce app data: %s", string(data)) + } + } + } } - // Type assert using the package path + // Type assert and log identity details if identity, ok := id.(*identity.Identity); ok { debugLog(DEBUG_ALL, "Identity details:") debugLog(DEBUG_ALL, " Hash: %s", identity.GetHexHash()) @@ -567,6 +630,15 @@ func (h *AnnounceHandler) ReceivedAnnounce(destHash []byte, id interface{}, appD debugLog(DEBUG_ALL, " Current Ratchet ID: %x", ratchetID) } } + + // Store announce in history + h.reticulum.announceHistoryMu.Lock() + h.reticulum.announceHistory[identity.GetHexHash()] = announceRecord{ + // You can add fields here to store relevant announce data + } + h.reticulum.announceHistoryMu.Unlock() + + debugLog(DEBUG_VERBOSE, "Stored announce in history for identity %s", identity.GetHexHash()) } return nil diff --git a/test-utilities/packet-interceptor.go b/test-utilities/packet-interceptor.go new file mode 100644 index 0000000..d122a4b --- /dev/null +++ b/test-utilities/packet-interceptor.go @@ -0,0 +1,104 @@ +package testutils + +import ( + "encoding/hex" + "fmt" + "os" + "sync" + "time" + + "github.com/Sudo-Ivan/reticulum-go/pkg/common" +) + +type PacketInterceptor struct { + mutex sync.Mutex + outputFile *os.File + isEnabled bool + packetCount uint64 +} + +func NewPacketInterceptor(outputPath string) (*PacketInterceptor, error) { + file, err := os.OpenFile(outputPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return nil, fmt.Errorf("failed to open output file: %v", err) + } + + pi := &PacketInterceptor{ + outputFile: file, + isEnabled: true, + } + + // Write header + header := fmt.Sprintf("=== Packet Capture Started at %s ===\n\n", + time.Now().UTC().Format("2006-01-02 15:04:05")) + if _, err := file.WriteString(header); err != nil { + file.Close() + return nil, fmt.Errorf("failed to write header: %v", err) + } + + return pi, nil +} + +func (pi *PacketInterceptor) Close() error { + pi.mutex.Lock() + defer pi.mutex.Unlock() + + if pi.outputFile != nil { + return pi.outputFile.Close() + } + return nil +} + +func (pi *PacketInterceptor) InterceptPacket(data []byte, iface common.NetworkInterface, direction string) error { + pi.mutex.Lock() + defer pi.mutex.Unlock() + + if !pi.isEnabled || pi.outputFile == nil { + return nil + } + + timestamp := time.Now().UTC().Format("2006-01-02 15:04:05.000") + pi.packetCount++ + + // Format packet info + logEntry := fmt.Sprintf("[%s] %s packet #%d on interface %s\n", + timestamp, + direction, + pi.packetCount, + iface.GetName(), + ) + + // Add hex dump of packet data + logEntry += fmt.Sprintf("Data (%d bytes):\n%s\n\n", + len(data), + hex.Dump(data), + ) + + // Write to file + if _, err := pi.outputFile.WriteString(logEntry); err != nil { + return fmt.Errorf("failed to write to log file: %v", err) + } + + // Ensure data is written to disk + return pi.outputFile.Sync() +} + +func (pi *PacketInterceptor) InterceptOutgoing(data []byte, iface common.NetworkInterface) error { + return pi.InterceptPacket(data, iface, "OUTGOING") +} + +func (pi *PacketInterceptor) InterceptIncoming(data []byte, iface common.NetworkInterface) error { + return pi.InterceptPacket(data, iface, "INCOMING") +} + +func (pi *PacketInterceptor) Enable() { + pi.mutex.Lock() + defer pi.mutex.Unlock() + pi.isEnabled = true +} + +func (pi *PacketInterceptor) Disable() { + pi.mutex.Lock() + defer pi.mutex.Unlock() + pi.isEnabled = false +}