update 0.2.0

This commit is contained in:
Sudo-Ivan
2024-12-30 02:26:51 -06:00
parent 668d7c56b5
commit 2e01fa565d
14 changed files with 509 additions and 217 deletions

View File

@@ -2,3 +2,4 @@
Reticulum Network Stack in Go.
See [To-Do](./To-Do.md) for the current state of the project.

22
To-Do
View File

@@ -66,7 +66,7 @@ Basic Features
[✓] Basic UDP transport
[✓] TCP transport
[ ] Interface discovery
[ ] Connection management
[] Connection management
[✓] Packet framing
[✓] Transport integration
@@ -89,14 +89,14 @@ Basic Features
Next Immediate Tasks:
1. [✓] Fix import cycles by creating common package
2. [ ] Implement Interface discovery
3. [ ] Implement Connection management
4. [ ] Test network layer integration end-to-end
5. [ ] Add error handling for network failures
6. [ ] Implement interface auto-configuration
7. [ ] Complete NetworkInterface implementation
8. [ ] Add comprehensive interface tests
9. [ ] Implement connection retry logic
2. [] Complete NetworkInterface implementation
3. [✓] Add comprehensive interface tests
4. [] Implement connection retry logic
5. [] Add client reconnection handling
6. [ ] Implement Interface discovery
7. [ ] Test network layer integration end-to-end
8. [ ] Add error handling for network failures
9. [ ] Implement interface auto-configuration
10. [ ] Add metrics collection for interfaces
11. [ ] Add client reconnection handling
12. [ ] Implement client-side path caching
11. [ ] Implement client-side path caching
12. [ ] Add support for additional transport types

View File

@@ -14,14 +14,104 @@ import (
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
"github.com/Sudo-Ivan/reticulum-go/pkg/identity"
"github.com/Sudo-Ivan/reticulum-go/pkg/transport"
"github.com/Sudo-Ivan/reticulum-go/pkg/destination"
"github.com/Sudo-Ivan/reticulum-go/pkg/interfaces"
)
var (
configPath = flag.String("config", "", "Path to config file")
targetHash = flag.String("target", "", "Target destination hash")
generateIdentity = flag.Bool("generate-identity", false, "Generate a new identity and print its hash")
)
type Client struct {
config *common.ReticulumConfig
transport *transport.Transport
interfaces []common.NetworkInterface
}
func NewClient(cfg *common.ReticulumConfig) (*Client, error) {
if cfg == nil {
var err error
cfg, err = config.InitConfig()
if err != nil {
return nil, fmt.Errorf("failed to initialize config: %v", err)
}
}
t, err := transport.NewTransport(cfg)
if err != nil {
return nil, fmt.Errorf("failed to initialize transport: %v", err)
}
return &Client{
config: cfg,
transport: t,
interfaces: make([]common.NetworkInterface, 0),
}, nil
}
func (c *Client) Start() error {
for _, ifaceConfig := range c.config.Interfaces {
var iface common.NetworkInterface
switch ifaceConfig.Type {
case "tcp":
client, err := interfaces.NewTCPClient(
ifaceConfig.Name,
ifaceConfig.Address,
ifaceConfig.Port,
ifaceConfig.KISSFraming,
ifaceConfig.I2PTunneled,
)
if err != nil {
log.Printf("Failed to create TCP interface %s: %v", ifaceConfig.Name, err)
continue
}
// Convert callback type to match interface
callback := func(data []byte, iface interface{}) {
c.transport.HandlePacket(data, iface)
}
client.SetPacketCallback(common.PacketCallback(callback))
iface = client
case "udp":
addr := fmt.Sprintf("%s:%d", ifaceConfig.Address, ifaceConfig.Port)
udp, err := interfaces.NewUDPInterface(
ifaceConfig.Name,
addr,
"", // No target address for client initially
)
if err != nil {
log.Printf("Failed to create UDP interface %s: %v", ifaceConfig.Name, err)
continue
}
// Convert callback type to match interface
callback := func(data []byte, iface interface{}) {
c.transport.HandlePacket(data, iface)
}
udp.SetPacketCallback(common.PacketCallback(callback))
iface = udp
default:
log.Printf("Unknown interface type: %s", ifaceConfig.Type)
continue
}
c.interfaces = append(c.interfaces, iface)
}
return nil
}
func (c *Client) Stop() {
for _, iface := range c.interfaces {
iface.Detach()
}
c.transport.Close()
}
func main() {
flag.Parse()
@@ -30,85 +120,32 @@ func main() {
if *configPath == "" {
cfg, err = config.InitConfig()
if err != nil {
log.Fatalf("Failed to initialize config: %v", err)
}
} else {
cfg, err = config.LoadConfig(*configPath)
}
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
}
// Enable transport by default for client
cfg.EnableTransport = true
// Initialize transport
transport, err := transport.NewTransport(cfg)
if *generateIdentity {
id, err := identity.New()
if err != nil {
log.Fatalf("Failed to initialize transport: %v", err)
log.Fatalf("Failed to generate identity: %v", err)
}
defer transport.Close()
// If target specified, establish connection
if *targetHash != "" {
destHash, err := identity.HashFromHex(*targetHash)
if err != nil {
log.Fatalf("Invalid destination hash: %v", err)
}
// Request path if needed
if !transport.HasPath(destHash) {
fmt.Println("Requesting path to destination...")
if err := transport.RequestPath(destHash, "", nil, true); err != nil {
log.Fatalf("Failed to request path: %v", err)
}
}
// Get destination identity
destIdentity, err := identity.Recall(destHash)
if err != nil {
log.Fatalf("Failed to recall identity: %v", err)
}
// Create destination
dest, err := destination.New(
destIdentity,
destination.OUT,
destination.SINGLE,
"client",
"direct",
)
if err != nil {
log.Fatalf("Failed to create destination: %v", err)
}
// Enable and configure ratchets
dest.SetRetainedRatchets(destination.RATCHET_COUNT)
dest.SetRatchetInterval(destination.RATCHET_INTERVAL)
dest.EnforceRatchets()
// Create link
link := transport.NewLink(dest.Hash(), func() {
fmt.Println("Link established")
}, func() {
fmt.Println("Link closed")
})
defer link.Teardown()
// Set packet callback
link.SetPacketCallback(func(data []byte) {
fmt.Printf("Received: %s\n", string(data))
})
// Start interactive loop
go interactiveLoop(link)
} else {
fmt.Println("No target specified. Use -target <hash> to connect to a destination")
fmt.Printf("Identity hash: %s\n", id.Hex())
return
}
client, err := NewClient(cfg)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer client.Stop()
if err := client.Start(); err != nil {
log.Fatalf("Failed to start client: %v", err)
}
// Wait for interrupt
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

View File

@@ -6,17 +6,18 @@ import (
"os/signal"
"syscall"
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
"github.com/Sudo-Ivan/reticulum-go/internal/config"
"github.com/Sudo-Ivan/reticulum-go/pkg/transport"
"github.com/Sudo-Ivan/reticulum-go/pkg/interfaces"
)
type Reticulum struct {
config *config.ReticulumConfig
config *common.ReticulumConfig
transport *transport.Transport
}
func NewReticulum(cfg *config.ReticulumConfig) (*Reticulum, error) {
func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
if cfg == nil {
cfg = config.DefaultConfig()
}

View File

@@ -22,7 +22,7 @@ func DefaultConfig() *common.ReticulumConfig {
InstanceControlPort: DefaultInstanceControlPort,
PanicOnInterfaceErr: false,
LogLevel: DefaultLogLevel,
Interfaces: make(map[string]common.InterfaceConfig),
Interfaces: make(map[string]*common.InterfaceConfig),
}
}
@@ -75,13 +75,13 @@ func CreateDefaultConfig(path string) error {
cfg := DefaultConfig()
// Add default interface
cfg.Interfaces["Default Interface"] = common.InterfaceConfig{
cfg.Interfaces["Default Interface"] = &common.InterfaceConfig{
Type: "AutoInterface",
Enabled: false,
}
// Add default quad4net interface
cfg.Interfaces["quad4net tcp"] = common.InterfaceConfig{
cfg.Interfaces["quad4net tcp"] = &common.InterfaceConfig{
Type: "TCPClientInterface",
Enabled: true,
TargetHost: "rns.quad4.io",

View File

@@ -10,20 +10,26 @@ type ConfigProvider interface {
// InterfaceConfig represents interface configuration
type InterfaceConfig struct {
Type string `toml:"type"`
Name string `toml:"name"`
Enabled bool `toml:"enabled"`
TargetHost string `toml:"target_host,omitempty"`
TargetPort int `toml:"target_port,omitempty"`
Interface string `toml:"interface,omitempty"`
Address string `toml:"address,omitempty"`
Port int `toml:"port,omitempty"`
KISSFraming bool `toml:"kiss_framing,omitempty"`
I2PTunneled bool `toml:"i2p_tunneled,omitempty"`
PreferIPv6 bool `toml:"prefer_ipv6,omitempty"`
}
// ReticulumConfig represents the main configuration structure
type ReticulumConfig struct {
ConfigPath string `toml:"-"`
EnableTransport bool `toml:"enable_transport"`
ShareInstance bool `toml:"share_instance"`
SharedInstancePort int `toml:"shared_instance_port"`
InstanceControlPort int `toml:"instance_control_port"`
PanicOnInterfaceErr bool `toml:"panic_on_interface_error"`
LogLevel int `toml:"loglevel"`
ConfigPath string `toml:"-"`
Interfaces map[string]InterfaceConfig
Interfaces map[string]*InterfaceConfig `toml:"interfaces"`
}

60
pkg/common/interface.go Normal file
View File

@@ -0,0 +1,60 @@
package common
import (
"net"
"sync"
"time"
)
type InterfaceMode byte
type InterfaceType byte
type PacketCallback func([]byte, interface{})
// NetworkInterface combines both low-level and high-level interface requirements
type NetworkInterface interface {
// Low-level network operations
Start() error
Stop() error
Send(data []byte, address string) error
Receive() ([]byte, string, error)
GetType() InterfaceType
GetMode() InterfaceMode
GetMTU() int
// High-level packet operations
ProcessIncoming([]byte)
ProcessOutgoing([]byte) error
SendPathRequest([]byte) error
SendLinkPacket([]byte, []byte, time.Time) error
Detach()
SetPacketCallback(PacketCallback)
// Additional required fields
GetName() string
GetConn() net.Conn
IsEnabled() bool
}
// BaseInterface provides common implementation
type BaseInterface struct {
Name string
Mode InterfaceMode
Type InterfaceType
Online bool
Detached bool
IN bool
OUT bool
MTU int
Bitrate int64
TxBytes uint64
RxBytes uint64
Mutex sync.RWMutex
Owner interface{}
PacketCallback PacketCallback
}

View File

@@ -1,57 +0,0 @@
package common
import (
"net"
"sync"
"time"
)
// NetworkInterface combines both low-level and high-level interface requirements
type NetworkInterface interface {
// Low-level network operations
Start() error
Stop() error
Send(data []byte, address string) error
Receive() ([]byte, string, error)
GetType() InterfaceType
GetMode() InterfaceMode
GetMTU() int
// High-level packet operations
ProcessIncoming([]byte)
ProcessOutgoing([]byte) error
SendPathRequest([]byte) error
SendLinkPacket([]byte, []byte, time.Time) error
Detach()
SetPacketCallback(PacketCallback)
// Additional required fields
GetName() string
GetConn() net.Conn
IsEnabled() bool
}
type PacketCallback func([]byte, interface{})
// BaseInterface provides common implementation
type BaseInterface struct {
Name string
Mode InterfaceMode
Type InterfaceType
Online bool
Detached bool
IN bool
OUT bool
MTU int
Bitrate int64
TxBytes uint64
RxBytes uint64
mutex sync.RWMutex
owner interface{}
packetCallback PacketCallback
}

View File

@@ -4,10 +4,6 @@ import (
"time"
)
// Interface related types
type InterfaceMode byte
type InterfaceType byte
// Transport related types
type TransportMode byte
type PathStatus byte
@@ -15,8 +11,6 @@ type PathStatus byte
// Common structs
type Path struct {
Interface NetworkInterface
Address string
Status PathStatus
LastSeen time.Time
NextHop []byte
Hops uint8

View File

@@ -428,3 +428,11 @@ func (i *Identity) DecryptSymmetric(ciphertext []byte) ([]byte, error) {
return plaintext, nil
}
func (i *Identity) Hash() []byte {
return TruncatedHash(i.publicKey)
}
func (i *Identity) Hex() string {
return hex.EncodeToString(i.Hash())
}

View File

@@ -2,32 +2,40 @@ package interfaces
import (
"fmt"
"sync"
"time"
"encoding/binary"
"net"
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
)
const (
BITRATE_MINIMUM = 5 // Minimum required bitrate in bits/sec
MODE_FULL = 0x01
)
// BaseInterface embeds common.BaseInterface and implements common.Interface
type Interface interface {
common.NetworkInterface
Send(data []byte, target string) error
Detach()
IsEnabled() bool
GetName() string
}
type BaseInterface struct {
common.BaseInterface
}
func (i *BaseInterface) SetPacketCallback(callback common.PacketCallback) {
i.mutex.Lock()
defer i.mutex.Unlock()
i.packetCallback = callback
i.Mutex.Lock()
defer i.Mutex.Unlock()
i.PacketCallback = callback
}
func (i *BaseInterface) ProcessIncoming(data []byte) {
i.mutex.RLock()
callback := i.packetCallback
i.mutex.RUnlock()
i.Mutex.RLock()
callback := i.PacketCallback
i.Mutex.RUnlock()
if callback != nil {
callback(data, i)
@@ -42,8 +50,8 @@ func (i *BaseInterface) ProcessOutgoing(data []byte) error {
}
func (i *BaseInterface) Detach() {
i.mutex.Lock()
defer i.mutex.Unlock()
i.Mutex.Lock()
defer i.Mutex.Unlock()
i.Detached = true
i.Online = false
}
@@ -77,3 +85,43 @@ func (i *BaseInterface) SendLinkPacket(dest []byte, data []byte, timestamp time.
return i.ProcessOutgoing(frame)
}
func (i *BaseInterface) Start() error {
return nil
}
func (i *BaseInterface) Stop() error {
return nil
}
func (i *BaseInterface) Send(data []byte, address string) error {
return i.ProcessOutgoing(data)
}
func (i *BaseInterface) Receive() ([]byte, string, error) {
return nil, "", nil
}
func (i *BaseInterface) GetType() common.InterfaceType {
return i.Type
}
func (i *BaseInterface) GetMode() common.InterfaceMode {
return i.Mode
}
func (i *BaseInterface) GetMTU() int {
return i.MTU
}
func (i *BaseInterface) GetName() string {
return i.Name
}
func (i *BaseInterface) GetConn() net.Conn {
return nil
}
func (i *BaseInterface) IsEnabled() bool {
return i.Online && !i.Detached
}

View File

@@ -5,6 +5,7 @@ import (
"net"
"sync"
"time"
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
)
const (
@@ -26,7 +27,7 @@ const (
)
type TCPClientInterface struct {
Interface
BaseInterface
conn net.Conn
targetAddr string
targetPort int
@@ -39,16 +40,19 @@ type TCPClientInterface struct {
maxReconnectTries int
packetBuffer []byte
packetType byte
packetCallback func([]byte, interface{})
}
func NewTCPClient(name string, targetAddr string, targetPort int, kissFraming bool, i2pTunneled bool) (*TCPClientInterface, error) {
tc := &TCPClientInterface{
Interface: Interface{
BaseInterface: BaseInterface{
BaseInterface: common.BaseInterface{
Name: name,
Mode: MODE_FULL,
Mode: common.IF_MODE_FULL,
MTU: 1064,
Bitrate: 10000000, // 10Mbps estimate
},
},
targetAddr: targetAddr,
targetPort: targetPort,
kissFraming: kissFraming,
@@ -195,12 +199,12 @@ func (tc *TCPClientInterface) handlePacket(data []byte) {
switch packetType {
case 0x01: // Path request
tc.Interface.ProcessIncoming(payload)
tc.BaseInterface.ProcessIncoming(payload)
case 0x02: // Link packet
if len(payload) < 40 { // minimum size for link packet
return
}
tc.Interface.ProcessIncoming(payload)
tc.BaseInterface.ProcessIncoming(payload)
default:
// Unknown packet type
return
@@ -229,7 +233,7 @@ func (tc *TCPClientInterface) ProcessOutgoing(data []byte) error {
return fmt.Errorf("write failed: %v", err)
}
tc.Interface.ProcessOutgoing(data)
tc.BaseInterface.ProcessOutgoing(data)
return nil
}
@@ -269,29 +273,45 @@ func escapeKISS(data []byte) []byte {
return escaped
}
func (tc *TCPClientInterface) SetPacketCallback(cb func([]byte, interface{})) {
tc.packetCallback = cb
}
func (tc *TCPClientInterface) IsEnabled() bool {
return tc.Online
}
func (tc *TCPClientInterface) GetName() string {
return tc.Name
}
type TCPServerInterface struct {
Interface
BaseInterface
server net.Listener
bindAddr string
bindPort int
i2pTunneled bool
preferIPv6 bool
i2pTunneled bool
spawned []*TCPClientInterface
spawnedMutex sync.RWMutex
packetCallback func([]byte, interface{})
}
func NewTCPServer(name string, bindAddr string, bindPort int, i2pTunneled bool, preferIPv6 bool) (*TCPServerInterface, error) {
func NewTCPServer(name string, bindAddr string, bindPort int, preferIPv6 bool, i2pTunneled bool) (*TCPServerInterface, error) {
ts := &TCPServerInterface{
Interface: Interface{
BaseInterface: BaseInterface{
BaseInterface: common.BaseInterface{
Name: name,
Mode: MODE_FULL,
Mode: common.IF_MODE_FULL,
Type: common.IF_TYPE_TCP,
MTU: 1064,
Bitrate: 10000000, // 10Mbps estimate
Bitrate: 10000000,
},
},
bindAddr: bindAddr,
bindPort: bindPort,
i2pTunneled: i2pTunneled,
preferIPv6: preferIPv6,
i2pTunneled: i2pTunneled,
spawned: make([]*TCPClientInterface, 0),
}
@@ -328,7 +348,6 @@ func (ts *TCPServerInterface) acceptLoop() {
conn, err := ts.server.Accept()
if err != nil {
if !ts.Detached {
// Log error and continue accepting
continue
}
return
@@ -336,10 +355,14 @@ func (ts *TCPServerInterface) acceptLoop() {
// Create new client interface for this connection
client := &TCPClientInterface{
Interface: Interface{
BaseInterface: BaseInterface{
BaseInterface: common.BaseInterface{
Name: fmt.Sprintf("Client-%s-%s", ts.Name, conn.RemoteAddr()),
Mode: ts.Mode,
Type: common.IF_TYPE_TCP,
MTU: ts.MTU,
Bitrate: ts.Bitrate,
},
},
conn: conn,
i2pTunneled: ts.i2pTunneled,
@@ -367,7 +390,7 @@ func (ts *TCPServerInterface) acceptLoop() {
}
func (ts *TCPServerInterface) Detach() {
ts.Interface.Detach()
ts.BaseInterface.Detach()
if ts.server != nil {
ts.server.Close()
@@ -406,3 +429,15 @@ func (ts *TCPServerInterface) String() string {
}
return fmt.Sprintf("TCPServerInterface[%s/%s:%d]", ts.Name, addr, ts.bindPort)
}
func (ts *TCPServerInterface) SetPacketCallback(cb func([]byte, interface{})) {
ts.packetCallback = cb
}
func (ts *TCPServerInterface) IsEnabled() bool {
return ts.Online
}
func (ts *TCPServerInterface) GetName() string {
return ts.Name
}

View File

@@ -3,11 +3,11 @@ package interfaces
import (
"fmt"
"net"
"sync"
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
)
type UDPInterface struct {
Interface
BaseInterface
conn *net.UDPConn
listenAddr *net.UDPAddr
targetAddr *net.UDPAddr
@@ -16,11 +16,14 @@ type UDPInterface struct {
func NewUDPInterface(name string, listenAddr string, targetAddr string) (*UDPInterface, error) {
ui := &UDPInterface{
Interface: Interface{
BaseInterface: BaseInterface{
BaseInterface: common.BaseInterface{
Name: name,
Mode: MODE_FULL,
Mode: common.IF_MODE_FULL,
Type: common.IF_TYPE_UDP,
MTU: 1500,
Bitrate: 100000000, // 100Mbps estimate for UDP
Bitrate: 100000000, // 100Mbps estimate
},
},
readBuffer: make([]byte, 65535),
}
@@ -39,7 +42,7 @@ func NewUDPInterface(name string, listenAddr string, targetAddr string) (*UDPInt
return nil, fmt.Errorf("invalid target address: %v", err)
}
ui.targetAddr = taddr
ui.OUT = true
ui.BaseInterface.OUT = true
}
// Create UDP connection
@@ -48,8 +51,8 @@ func NewUDPInterface(name string, listenAddr string, targetAddr string) (*UDPInt
return nil, fmt.Errorf("failed to listen on UDP: %v", err)
}
ui.conn = conn
ui.IN = true
ui.Online = true
ui.BaseInterface.IN = true
ui.BaseInterface.Online = true
// Start read loop
go ui.readLoop()
@@ -59,17 +62,23 @@ func NewUDPInterface(name string, listenAddr string, targetAddr string) (*UDPInt
func (ui *UDPInterface) readLoop() {
for {
if !ui.Online {
if !ui.BaseInterface.Online {
return
}
n, addr, err := ui.conn.ReadFromUDP(ui.readBuffer)
n, remoteAddr, err := ui.conn.ReadFromUDP(ui.readBuffer)
if err != nil {
if !ui.Detached {
// Log error
}
if !ui.BaseInterface.Detached {
continue
}
return
}
// If no target address is set, use the first sender's address
if ui.targetAddr == nil {
ui.targetAddr = remoteAddr
ui.BaseInterface.OUT = true
}
// Copy received data
data := make([]byte, n)
@@ -81,7 +90,7 @@ func (ui *UDPInterface) readLoop() {
}
func (ui *UDPInterface) ProcessOutgoing(data []byte) error {
if !ui.Online || ui.targetAddr == nil {
if !ui.BaseInterface.Online || ui.targetAddr == nil {
return fmt.Errorf("interface offline or no target address configured")
}
@@ -90,13 +99,17 @@ func (ui *UDPInterface) ProcessOutgoing(data []byte) error {
return fmt.Errorf("UDP write failed: %v", err)
}
ui.Interface.ProcessOutgoing(data)
ui.BaseInterface.ProcessOutgoing(data)
return nil
}
func (ui *UDPInterface) Detach() {
ui.Interface.Detach()
ui.BaseInterface.Detach()
if ui.conn != nil {
ui.conn.Close()
}
}
func (ui *UDPInterface) GetConn() net.Conn {
return ui.conn
}

View File

@@ -457,3 +457,149 @@ func SendAnnounce(packet []byte) error {
return lastErr
}
func (t *Transport) HandlePacket(data []byte, iface interface{}) {
if len(data) < 1 {
return
}
packetType := data[0]
switch packetType {
case 0x01: // Path Request
t.handlePathRequest(data[1:], iface)
case 0x02: // Link Packet
t.handleLinkPacket(data[1:], iface)
case 0x03: // Path Response
t.handlePathResponse(data[1:], iface)
case 0x04: // Announce
t.handleAnnouncePacket(data[1:], iface)
}
}
func (t *Transport) handlePathRequest(data []byte, iface interface{}) {
if len(data) < 33 { // 32 bytes hash + 1 byte TTL minimum
return
}
destHash := data[:32]
ttl := data[32]
var tag []byte
recursive := false
if len(data) > 33 {
tag = data[33:len(data)-1]
recursive = data[len(data)-1] == 0x01
}
// Check if we have a path to the destination
if t.HasPath(destHash) {
// Create and send path response
hops := t.HopsTo(destHash)
nextHop := t.NextHop(destHash)
response := make([]byte, 0, 64)
response = append(response, 0x03) // Path Response type
response = append(response, destHash...)
response = append(response, byte(hops))
response = append(response, nextHop...)
if len(tag) > 0 {
response = append(response, tag...)
}
if i, ok := iface.(common.NetworkInterface); ok {
i.Send(response, "")
}
} else if recursive && ttl > 0 {
// Forward path request to other interfaces
newData := make([]byte, len(data))
copy(newData, data)
newData[32] = ttl - 1 // Decrease TTL
for name, otherIface := range t.interfaces {
if name != iface.(common.NetworkInterface).GetName() && otherIface.IsEnabled() {
otherIface.Send(newData, "")
}
}
}
}
func (t *Transport) handleLinkPacket(data []byte, iface interface{}) {
if len(data) < 40 { // 32 bytes dest + 8 bytes timestamp minimum
return
}
dest := data[:32]
timestamp := binary.BigEndian.Uint64(data[32:40])
payload := data[40:]
// Check if we're the destination
if t.HasPath(dest) {
nextHop := t.NextHop(dest)
nextIface := t.NextHopInterface(dest)
if iface, ok := t.interfaces[nextIface]; ok {
iface.Send(data, string(nextHop))
}
}
// Update timing information
if link := t.findLink(dest); link != nil {
link.lastInbound = time.Unix(int64(timestamp), 0)
if link.packetCb != nil {
link.packetCb(payload)
}
}
}
func (t *Transport) handlePathResponse(data []byte, iface interface{}) {
if len(data) < 33 { // 32 bytes hash + 1 byte hops minimum
return
}
destHash := data[:32]
hops := data[32]
var nextHop []byte
if len(data) > 33 {
nextHop = data[33:]
}
// Update path information
if i, ok := iface.(common.NetworkInterface); ok {
t.UpdatePath(destHash, nextHop, i.GetName(), hops)
}
}
func (t *Transport) handleAnnouncePacket(data []byte, iface interface{}) {
if len(data) < 32 { // 32 bytes minimum for hash
return
}
destHash := data[:32]
var identity, appData []byte
if len(data) > 32 {
splitPoint := 32
for i := 32; i < len(data); i++ {
if data[i] == 0x00 {
splitPoint = i
break
}
}
identity = data[32:splitPoint]
if splitPoint < len(data)-1 {
appData = data[splitPoint+1:]
}
}
t.HandleAnnounce(destHash, identity, appData)
}
func (t *Transport) findLink(dest []byte) *Link {
t.mutex.RLock()
defer t.mutex.RUnlock()
// This is a simplified version - you might want to maintain a map of active links
// in the Transport struct for better performance
return nil
}