packet interceptor

This commit is contained in:
Sudo-Ivan
2025-01-04 18:37:53 -06:00
parent 79e1caa815
commit 77729e07e1
2 changed files with 196 additions and 20 deletions

View File

@@ -20,10 +20,13 @@ import (
"github.com/Sudo-Ivan/reticulum-go/pkg/interfaces" "github.com/Sudo-Ivan/reticulum-go/pkg/interfaces"
"github.com/Sudo-Ivan/reticulum-go/pkg/packet" "github.com/Sudo-Ivan/reticulum-go/pkg/packet"
"github.com/Sudo-Ivan/reticulum-go/pkg/transport" "github.com/Sudo-Ivan/reticulum-go/pkg/transport"
testutils "github.com/Sudo-Ivan/reticulum-go/test-utilities"
) )
var ( 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{}) { func debugLog(level int, format string, v ...interface{}) {
@@ -123,6 +126,31 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
destination: dest, 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 // Initialize interfaces from config
for name, ifaceConfig := range cfg.Interfaces { for name, ifaceConfig := range cfg.Interfaces {
if !ifaceConfig.Enabled { if !ifaceConfig.Enabled {
@@ -164,8 +192,19 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
continue 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) debugLog(2, "Configuring interface %s (type=%s)...", name, ifaceConfig.Type)
r.interfaces = append(r.interfaces, iface) r.interfaces = append(r.interfaces, iface)
debugLog(3, "Interface %s started successfully", name)
} }
return r, nil return r, nil
@@ -176,7 +215,12 @@ func (r *Reticulum) handleInterface(iface common.NetworkInterface) {
ch := channel.NewChannel(&transportWrapper{r.transport}) ch := channel.NewChannel(&transportWrapper{r.transport})
r.channels[iface.GetName()] = ch 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( rw := buffer.CreateBidirectionalBuffer(
1, 1,
@@ -188,28 +232,22 @@ func (r *Reticulum) handleInterface(iface common.NetworkInterface) {
iface.ProcessIncoming(data) iface.ProcessIncoming(data)
if len(data) > 0 { 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]) debugLog(DEBUG_TRACE, "Interface %s: Received packet type 0x%02x", iface.GetName(), data[0])
r.transport.HandlePacket(data, iface) r.transport.HandlePacket(data, iface)
} }
debugLog(5, "Processed %d bytes from interface %s", size, iface.GetName())
}, },
) )
r.buffers[iface.GetName()] = &buffer.Buffer{ r.buffers[iface.GetName()] = &buffer.Buffer{
ReadWriter: rw, 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() { func (r *Reticulum) monitorInterfaces() {
@@ -286,9 +324,7 @@ func main() {
go r.monitorInterfaces() go r.monitorInterfaces()
// Register announce handler // Register announce handler
handler := &AnnounceHandler{ handler := NewAnnounceHandler(r, []string{"*"})
aspectFilter: []string{"*"},
}
r.transport.RegisterAnnounceHandler(handler) r.transport.RegisterAnnounceHandler(handler)
// Start Reticulum // Start Reticulum
@@ -538,6 +574,14 @@ func (r *Reticulum) Stop() error {
type AnnounceHandler struct { type AnnounceHandler struct {
aspectFilter []string aspectFilter []string
reticulum *Reticulum
}
func NewAnnounceHandler(r *Reticulum, aspectFilter []string) *AnnounceHandler {
return &AnnounceHandler{
aspectFilter: aspectFilter,
reticulum: r,
}
} }
func (h *AnnounceHandler) AspectFilter() []string { 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 { func (h *AnnounceHandler) ReceivedAnnounce(destHash []byte, id interface{}, appData []byte) error {
debugLog(DEBUG_INFO, "Received announce from %x", destHash) debugLog(DEBUG_INFO, "Received announce from %x", destHash)
debugLog(DEBUG_PACKETS, "Raw announce data: %x", appData)
// Parse msgpack array
if len(appData) > 0 { 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 { if identity, ok := id.(*identity.Identity); ok {
debugLog(DEBUG_ALL, "Identity details:") debugLog(DEBUG_ALL, "Identity details:")
debugLog(DEBUG_ALL, " Hash: %s", identity.GetHexHash()) 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) 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 return nil

View File

@@ -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
}