packet interceptor
This commit is contained in:
@@ -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
|
||||||
|
|||||||
104
test-utilities/packet-interceptor.go
Normal file
104
test-utilities/packet-interceptor.go
Normal 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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user