summaryrefslogtreecommitdiffstats
path: root/transport/failover.go
diff options
context:
space:
mode:
authorgabrix73 <gabriel1@frozenstar.info>2026-04-10 16:10:48 +0200
committergabrix73 <gabriel1@frozenstar.info>2026-04-10 16:10:48 +0200
commit88375da30ea667ec25c2000874557fcc4785a558 (patch)
tree71364a43230977481c6d3280246aa8bb48562bdc /transport/failover.go
downloadkhimera-88375da30ea667ec25c2000874557fcc4785a558.tar.gz
khimera-88375da30ea667ec25c2000874557fcc4785a558.tar.xz
khimera-88375da30ea667ec25c2000874557fcc4785a558.zip
Initial commit: Veilith v0.9 - P2P Encrypted Messenger
Military-grade secure messenger with: - End-to-End Encryption (Noise Protocol XX) - Forward Secrecy (automatic session rotation) - Anti-Traffic Analysis (padding + cover traffic) - Secure Deletion (DOD 5220.22-M) - Tor Integration (embedded with bridge support) - Portable Mode (no installation required) Generated with Claude Code
Diffstat (limited to 'transport/failover.go')
-rw-r--r--transport/failover.go284
1 files changed, 284 insertions, 0 deletions
diff --git a/transport/failover.go b/transport/failover.go
new file mode 100644
index 0000000..77129a3
--- /dev/null
+++ b/transport/failover.go
@@ -0,0 +1,284 @@
+// Package transport - Multi-transport failover for war zones
+package transport
+
+import (
+ "fmt"
+ "net"
+ "sync"
+ "time"
+)
+
+// FailoverTransport implements automatic failover between multiple transports
+// Critical for war zones: Tor → Mesh → Bluetooth → Direct
+type FailoverTransport struct {
+ transports []Transport // Ordered list of transports to try
+ currentIndex int // Currently active transport
+ mu sync.RWMutex
+ healthCheck map[TransportType]time.Time // Last successful connection
+ retryInterval time.Duration
+ maxRetries int
+ onFailover func(from, to TransportType) // Callback when failing over
+}
+
+// FailoverConfig holds failover configuration
+type FailoverConfig struct {
+ RetryInterval time.Duration // Time between retry attempts
+ MaxRetries int // Max retries per transport before failing over
+ HealthCheckInterval time.Duration // How often to health check transports
+}
+
+// NewFailoverTransport creates a new failover transport
+func NewFailoverTransport(transports []Transport, config *FailoverConfig) *FailoverTransport {
+ if config == nil {
+ config = &FailoverConfig{
+ RetryInterval: 5 * time.Second,
+ MaxRetries: 3,
+ HealthCheckInterval: 30 * time.Second,
+ }
+ }
+
+ ft := &FailoverTransport{
+ transports: transports,
+ currentIndex: 0,
+ healthCheck: make(map[TransportType]time.Time),
+ retryInterval: config.RetryInterval,
+ maxRetries: config.MaxRetries,
+ }
+
+ // Start health checking
+ go ft.healthCheckLoop(config.HealthCheckInterval)
+
+ return ft
+}
+
+// Connect attempts to connect using transports in priority order
+func (ft *FailoverTransport) Connect(address string) (net.Conn, error) {
+ ft.mu.Lock()
+ startIndex := ft.currentIndex
+ ft.mu.Unlock()
+
+ // Try each transport in order
+ for attempt := 0; attempt < len(ft.transports)*ft.maxRetries; attempt++ {
+ ft.mu.Lock()
+ index := ft.currentIndex
+ transport := ft.transports[index]
+ ft.mu.Unlock()
+
+ transportType := transport.Type()
+
+ // Attempt connection
+ conn, err := transport.Connect(address)
+ if err == nil {
+ // Success!
+ ft.mu.Lock()
+ ft.healthCheck[transportType] = time.Now()
+ ft.mu.Unlock()
+
+ fmt.Printf("✓ Connected via %s\n", transportType)
+ return conn, nil
+ }
+
+ fmt.Printf("✗ %s connection failed: %v\n", transportType, err)
+
+ // Move to next transport
+ ft.mu.Lock()
+ oldIndex := ft.currentIndex
+ ft.currentIndex = (ft.currentIndex + 1) % len(ft.transports)
+ newTransport := ft.transports[ft.currentIndex]
+ ft.mu.Unlock()
+
+ // Notify failover
+ if ft.onFailover != nil && oldIndex != ft.currentIndex {
+ go ft.onFailover(transport.Type(), newTransport.Type())
+ }
+
+ // Wait before retry
+ time.Sleep(ft.retryInterval)
+ }
+
+ return nil, fmt.Errorf("all transports failed after %d attempts", len(ft.transports)*ft.maxRetries)
+}
+
+// Listen starts listening on the highest priority available transport
+func (ft *FailoverTransport) Listen(address string) (net.Listener, error) {
+ ft.mu.RLock()
+ defer ft.mu.RUnlock()
+
+ // Try each transport
+ for _, transport := range ft.transports {
+ listener, err := transport.Listen(address)
+ if err == nil {
+ fmt.Printf("✓ Listening on %s\n", transport.Type())
+ return listener, nil
+ }
+ fmt.Printf("✗ Listen failed on %s: %v\n", transport.Type(), err)
+ }
+
+ return nil, fmt.Errorf("all transports failed to listen")
+}
+
+// Type returns the currently active transport type
+func (ft *FailoverTransport) Type() TransportType {
+ ft.mu.RLock()
+ defer ft.mu.RUnlock()
+
+ if ft.currentIndex < len(ft.transports) {
+ return ft.transports[ft.currentIndex].Type()
+ }
+ return TransportType(-1)
+}
+
+// Close closes all transports
+func (ft *FailoverTransport) Close() error {
+ ft.mu.Lock()
+ defer ft.mu.Unlock()
+
+ var lastErr error
+ for _, transport := range ft.transports {
+ if err := transport.Close(); err != nil {
+ lastErr = err
+ }
+ }
+ return lastErr
+}
+
+// GetCurrentTransport returns the currently active transport
+func (ft *FailoverTransport) GetCurrentTransport() Transport {
+ ft.mu.RLock()
+ defer ft.mu.RUnlock()
+
+ if ft.currentIndex < len(ft.transports) {
+ return ft.transports[ft.currentIndex]
+ }
+ return nil
+}
+
+// GetTransports returns all available transports
+func (ft *FailoverTransport) GetTransports() []Transport {
+ ft.mu.RLock()
+ defer ft.mu.RUnlock()
+
+ return ft.transports
+}
+
+// SetTransportPriority reorders transports by priority
+func (ft *FailoverTransport) SetTransportPriority(types []TransportType) error {
+ ft.mu.Lock()
+ defer ft.mu.Unlock()
+
+ if len(types) != len(ft.transports) {
+ return fmt.Errorf("priority list length mismatch")
+ }
+
+ // Create new ordered list
+ newTransports := make([]Transport, 0, len(ft.transports))
+ for _, targetType := range types {
+ found := false
+ for _, transport := range ft.transports {
+ if transport.Type() == targetType {
+ newTransports = append(newTransports, transport)
+ found = true
+ break
+ }
+ }
+ if !found {
+ return fmt.Errorf("transport type %s not found", targetType)
+ }
+ }
+
+ ft.transports = newTransports
+ ft.currentIndex = 0
+
+ return nil
+}
+
+// healthCheckLoop periodically checks transport health
+func (ft *FailoverTransport) healthCheckLoop(interval time.Duration) {
+ ticker := time.NewTicker(interval)
+ defer ticker.Stop()
+
+ for range ticker.C {
+ ft.performHealthCheck()
+ }
+}
+
+// performHealthCheck checks if current transport is still healthy
+func (ft *FailoverTransport) performHealthCheck() {
+ ft.mu.RLock()
+ current := ft.transports[ft.currentIndex]
+ lastSuccess, exists := ft.healthCheck[current.Type()]
+ ft.mu.RUnlock()
+
+ // If no successful connection in last 5 minutes, try next transport
+ if !exists || time.Since(lastSuccess) > 5*time.Minute {
+ ft.mu.Lock()
+ oldIndex := ft.currentIndex
+ ft.currentIndex = (ft.currentIndex + 1) % len(ft.transports)
+ ft.mu.Unlock()
+
+ if ft.onFailover != nil && oldIndex != ft.currentIndex {
+ ft.mu.RLock()
+ go ft.onFailover(ft.transports[oldIndex].Type(), ft.transports[ft.currentIndex].Type())
+ ft.mu.RUnlock()
+ }
+ }
+}
+
+// SetOnFailover sets callback for failover events
+func (ft *FailoverTransport) SetOnFailover(callback func(from, to TransportType)) {
+ ft.mu.Lock()
+ defer ft.mu.Unlock()
+ ft.onFailover = callback
+}
+
+// GetHealthStatus returns health status of all transports
+func (ft *FailoverTransport) GetHealthStatus() map[TransportType]time.Time {
+ ft.mu.RLock()
+ defer ft.mu.RUnlock()
+
+ status := make(map[TransportType]time.Time)
+ for k, v := range ft.healthCheck {
+ status[k] = v
+ }
+ return status
+}
+
+// String returns a string representation
+func (ft *FailoverTransport) String() string {
+ ft.mu.RLock()
+ defer ft.mu.RUnlock()
+
+ currentType := "none"
+ if ft.currentIndex < len(ft.transports) {
+ currentType = ft.transports[ft.currentIndex].Type().String()
+ }
+
+ return fmt.Sprintf("FailoverTransport(current=%s, transports=%d)",
+ currentType, len(ft.transports))
+}
+
+// ConnectWithRetry attempts connection with exponential backoff
+func (ft *FailoverTransport) ConnectWithRetry(address string, maxAttempts int) (net.Conn, error) {
+ var conn net.Conn
+ var err error
+
+ backoff := 1 * time.Second
+
+ for attempt := 1; attempt <= maxAttempts; attempt++ {
+ conn, err = ft.Connect(address)
+ if err == nil {
+ return conn, nil
+ }
+
+ fmt.Printf("Attempt %d/%d failed, retrying in %v...\n", attempt, maxAttempts, backoff)
+ time.Sleep(backoff)
+
+ // Exponential backoff with jitter
+ backoff *= 2
+ if backoff > 60*time.Second {
+ backoff = 60 * time.Second
+ }
+ }
+
+ return nil, fmt.Errorf("failed after %d attempts: %w", maxAttempts, err)
+}