mirror of
https://github.com/go-i2p/go-i2ptunnel.git
synced 2025-12-20 15:15:52 -05:00
Implement configuration loading for HTTP, IRC, TCP, and UDP tunnels using go-i2ptunnel-config
This commit is contained in:
4
go.mod
4
go.mod
@@ -8,8 +8,9 @@ require (
|
|||||||
github.com/elazarl/goproxy v1.7.0
|
github.com/elazarl/goproxy v1.7.0
|
||||||
github.com/go-i2p/go-connfilter v0.0.0-20250205023438-0f2b889a80f6
|
github.com/go-i2p/go-connfilter v0.0.0-20250205023438-0f2b889a80f6
|
||||||
github.com/go-i2p/go-forward v0.0.0-20250202052226-ee8a43dcb664
|
github.com/go-i2p/go-forward v0.0.0-20250202052226-ee8a43dcb664
|
||||||
github.com/go-i2p/go-i2ptunnel-config v0.0.0-20250209030407-ba90db65df97
|
github.com/go-i2p/go-i2ptunnel-config v0.0.0-20251019021515-10ef90f2473b
|
||||||
github.com/go-i2p/go-limit v0.0.0-20250203203118-210616857c15
|
github.com/go-i2p/go-limit v0.0.0-20250203203118-210616857c15
|
||||||
|
github.com/go-i2p/go-sam-go v0.0.0-20251016194809-b6ead96fdc39
|
||||||
github.com/go-i2p/i2pkeys v0.33.92
|
github.com/go-i2p/i2pkeys v0.33.92
|
||||||
github.com/go-i2p/onramp v0.33.93-0.20251016200402-d3ac8f5353c5
|
github.com/go-i2p/onramp v0.33.93-0.20251016200402-d3ac8f5353c5
|
||||||
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301
|
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301
|
||||||
@@ -20,7 +21,6 @@ require (
|
|||||||
github.com/cretz/bine v0.2.0 // indirect
|
github.com/cretz/bine v0.2.0 // indirect
|
||||||
github.com/go-i2p/common v0.0.0-20250819203334-e5459df35789 // indirect
|
github.com/go-i2p/common v0.0.0-20250819203334-e5459df35789 // indirect
|
||||||
github.com/go-i2p/crypto v0.0.0-20250822224541-85015740db11 // indirect
|
github.com/go-i2p/crypto v0.0.0-20250822224541-85015740db11 // indirect
|
||||||
github.com/go-i2p/go-sam-go v0.0.0-20251016194809-b6ead96fdc39 // indirect
|
|
||||||
github.com/go-i2p/logger v0.0.0-20241123010126-3050657e5d0c // indirect
|
github.com/go-i2p/logger v0.0.0-20241123010126-3050657e5d0c // indirect
|
||||||
github.com/magiconair/properties v1.8.9 // indirect
|
github.com/magiconair/properties v1.8.9 // indirect
|
||||||
github.com/oklog/ulid/v2 v2.1.1 // indirect
|
github.com/oklog/ulid/v2 v2.1.1 // indirect
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -192,8 +193,44 @@ func (h *HTTPClient) SetOptions(opts map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the tunnel config from file
|
// LoadConfig loads tunnel configuration from a file and updates the tunnel settings.
|
||||||
|
// The tunnel must be stopped before calling LoadConfig to prevent inconsistent state.
|
||||||
|
// Supported formats: .properties, .ini, .yaml/.yml
|
||||||
|
//
|
||||||
|
// Why: HTTP proxies need dynamic configuration updates for production deployments.
|
||||||
|
// Design: Uses go-i2ptunnel-config library for parsing. Preserves SAM connection and I2P keys.
|
||||||
func (h *HTTPClient) LoadConfig(path string) error {
|
func (h *HTTPClient) LoadConfig(path string) error {
|
||||||
// For now, return an error indicating this method needs configuration file support
|
// Prevent config changes while tunnel is running to avoid race conditions
|
||||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
if h.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusRunning ||
|
||||||
|
h.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusStarting {
|
||||||
|
return fmt.Errorf("cannot load config while tunnel is %s - stop tunnel first", h.I2PTunnelStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse config file using the converter library
|
||||||
|
conv := i2pconv.Converter{}
|
||||||
|
format, err := conv.DetectFormat(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect config format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig, err := conv.ParseInput(bytes, format)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type safety: ensure loaded config matches expected tunnel type
|
||||||
|
if newConfig.Type != "httpclient" {
|
||||||
|
return fmt.Errorf("config file contains %s tunnel, expected httpclient", newConfig.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update mutable configuration fields
|
||||||
|
// The Garlic connection (SAM) is preserved to maintain tunnel identity and keys
|
||||||
|
h.TunnelConfig = *newConfig
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
httpinspector "github.com/go-i2p/go-connfilter/http"
|
httpinspector "github.com/go-i2p/go-connfilter/http"
|
||||||
@@ -201,8 +202,50 @@ func (h *HTTPServer) SetOptions(opts map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the tunnel config from file
|
// LoadConfig loads tunnel configuration from a file and updates the tunnel settings.
|
||||||
|
// The tunnel must be stopped before calling LoadConfig to prevent inconsistent state.
|
||||||
|
// Supported formats: .properties, .ini, .yaml/.yml
|
||||||
|
//
|
||||||
|
// Why: HTTP reverse proxies need dynamic configuration updates for production deployments.
|
||||||
|
// Design: Uses go-i2ptunnel-config library for parsing. Preserves SAM connection and I2P keys.
|
||||||
func (h *HTTPServer) LoadConfig(path string) error {
|
func (h *HTTPServer) LoadConfig(path string) error {
|
||||||
// For now, return an error indicating this method needs configuration file support
|
// Prevent config changes while tunnel is running to avoid race conditions
|
||||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
if h.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusRunning ||
|
||||||
|
h.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusStarting {
|
||||||
|
return fmt.Errorf("cannot load config while tunnel is %s - stop tunnel first", h.I2PTunnelStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse config file using the converter library
|
||||||
|
conv := i2pconv.Converter{}
|
||||||
|
format, err := conv.DetectFormat(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect config format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig, err := conv.ParseInput(bytes, format)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type safety: ensure loaded config matches expected tunnel type
|
||||||
|
if newConfig.Type != "httpserver" {
|
||||||
|
return fmt.Errorf("config file contains %s tunnel, expected httpserver", newConfig.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate target address (local service) before applying changes
|
||||||
|
targetAddr, err := net.ResolveTCPAddr("tcp", newConfig.Target)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid target address in config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update mutable configuration fields
|
||||||
|
h.TunnelConfig = *newConfig
|
||||||
|
h.Addr = targetAddr
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
ircinspector "github.com/go-i2p/go-connfilter/irc"
|
ircinspector "github.com/go-i2p/go-connfilter/irc"
|
||||||
@@ -180,8 +181,47 @@ func (i *IRCClient) SetOptions(opts map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the tunnel config from file
|
// LoadConfig loads tunnel configuration from a file and updates the tunnel settings.
|
||||||
|
// The tunnel must be stopped before calling LoadConfig to prevent inconsistent state.
|
||||||
|
// Supported formats: .properties, .ini, .yaml/.yml
|
||||||
func (i *IRCClient) LoadConfig(path string) error {
|
func (i *IRCClient) LoadConfig(path string) error {
|
||||||
// For now, return an error indicating this method needs configuration file support
|
// Prevent config changes while tunnel is running to avoid race conditions
|
||||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
if i.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusRunning ||
|
||||||
|
i.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusStarting {
|
||||||
|
return fmt.Errorf("cannot load config while tunnel is %s - stop tunnel first", i.I2PTunnelStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse config file using the converter library
|
||||||
|
conv := i2pconv.Converter{}
|
||||||
|
format, err := conv.DetectFormat(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect config format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig, err := conv.ParseInput(bytes, format)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type safety: ensure loaded config matches expected tunnel type
|
||||||
|
if newConfig.Type != "ircclient" {
|
||||||
|
return fmt.Errorf("config file contains %s tunnel, expected ircclient", newConfig.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate target address before applying changes
|
||||||
|
addr, err := i2pkeys.Lookup(newConfig.Target)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid target address in config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update mutable configuration fields
|
||||||
|
i.TunnelConfig = *newConfig
|
||||||
|
i.I2PAddr = addr
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
ircinspector "github.com/go-i2p/go-connfilter/irc"
|
ircinspector "github.com/go-i2p/go-connfilter/irc"
|
||||||
@@ -191,8 +192,47 @@ func (i *IRCServer) SetOptions(opts map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the tunnel config from file
|
// LoadConfig loads tunnel configuration from a file and updates the tunnel settings.
|
||||||
|
// The tunnel must be stopped before calling LoadConfig to prevent inconsistent state.
|
||||||
|
// Supported formats: .properties, .ini, .yaml/.yml
|
||||||
func (i *IRCServer) LoadConfig(path string) error {
|
func (i *IRCServer) LoadConfig(path string) error {
|
||||||
// For now, return an error indicating this method needs configuration file support
|
// Prevent config changes while tunnel is running to avoid race conditions
|
||||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
if i.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusRunning ||
|
||||||
|
i.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusStarting {
|
||||||
|
return fmt.Errorf("cannot load config while tunnel is %s - stop tunnel first", i.I2PTunnelStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse config file using the converter library
|
||||||
|
conv := i2pconv.Converter{}
|
||||||
|
format, err := conv.DetectFormat(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect config format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig, err := conv.ParseInput(bytes, format)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type safety: ensure loaded config matches expected tunnel type
|
||||||
|
if newConfig.Type != "ircserver" {
|
||||||
|
return fmt.Errorf("config file contains %s tunnel, expected ircserver", newConfig.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate target address (local service) before applying changes
|
||||||
|
targetAddr, err := net.ResolveTCPAddr("tcp", newConfig.Target)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid target address in config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update mutable configuration fields
|
||||||
|
i.TunnelConfig = *newConfig
|
||||||
|
i.Addr = targetAddr
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -204,8 +205,44 @@ func (s *SOCKS) SetOptions(opts map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the tunnel config from file
|
// LoadConfig loads tunnel configuration from a file and updates the tunnel settings.
|
||||||
|
// The tunnel must be stopped before calling LoadConfig to prevent inconsistent state.
|
||||||
|
// Supported formats: .properties, .ini, .yaml/.yml
|
||||||
|
//
|
||||||
|
// Why: SOCKS proxies need dynamic configuration for production deployments.
|
||||||
|
// Design: Uses go-i2ptunnel-config library for parsing. Preserves SAM connection and I2P keys.
|
||||||
func (s *SOCKS) LoadConfig(path string) error {
|
func (s *SOCKS) LoadConfig(path string) error {
|
||||||
// For now, return an error indicating this method needs configuration file support
|
// Prevent config changes while tunnel is running to avoid race conditions
|
||||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
if s.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusRunning ||
|
||||||
|
s.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusStarting {
|
||||||
|
return fmt.Errorf("cannot load config while tunnel is %s - stop tunnel first", s.I2PTunnelStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse config file using the converter library
|
||||||
|
conv := i2pconv.Converter{}
|
||||||
|
format, err := conv.DetectFormat(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect config format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig, err := conv.ParseInput(bytes, format)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type safety: ensure loaded config matches expected tunnel type
|
||||||
|
if newConfig.Type != "socks" {
|
||||||
|
return fmt.Errorf("config file contains %s tunnel, expected socks", newConfig.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update mutable configuration fields
|
||||||
|
// The Garlic connection (SAM) is preserved to maintain tunnel identity and keys
|
||||||
|
s.TunnelConfig = *newConfig
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/go-i2p/go-forward/config"
|
"github.com/go-i2p/go-forward/config"
|
||||||
@@ -183,8 +184,56 @@ func (t *TCPClient) SetOptions(opts map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the tunnel config from file
|
// LoadConfig loads tunnel configuration from a file and updates the tunnel settings.
|
||||||
|
// The tunnel must be stopped before calling LoadConfig to prevent inconsistent state.
|
||||||
|
// Supported formats: .properties, .ini, .yaml/.yml
|
||||||
|
//
|
||||||
|
// Why: Production deployments need to reload configuration without recreating tunnel objects.
|
||||||
|
// This enables configuration management tools and web UIs to persist changes.
|
||||||
|
//
|
||||||
|
// Design: Uses the go-i2ptunnel-config library to parse config files in multiple formats,
|
||||||
|
// then updates only the mutable fields. SAM connection is preserved to maintain tunnel identity.
|
||||||
|
// The Garlic (I2P connection) is NOT reloaded - it maintains the existing keys and SAM session.
|
||||||
func (t *TCPClient) LoadConfig(path string) error {
|
func (t *TCPClient) LoadConfig(path string) error {
|
||||||
// For now, return an error indicating this method needs configuration file support
|
// Prevent config changes while tunnel is running to avoid race conditions
|
||||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
if t.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusRunning ||
|
||||||
|
t.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusStarting {
|
||||||
|
return fmt.Errorf("cannot load config while tunnel is %s - stop tunnel first", t.I2PTunnelStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse config file using the converter library
|
||||||
|
// This handles format detection and validation for .properties, .ini, .yaml
|
||||||
|
conv := i2pconv.Converter{}
|
||||||
|
format, err := conv.DetectFormat(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect config format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig, err := conv.ParseInput(bytes, format)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type safety: ensure loaded config matches expected tunnel type
|
||||||
|
if newConfig.Type != "tcpclient" {
|
||||||
|
return fmt.Errorf("config file contains %s tunnel, expected tcpclient", newConfig.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate target address before applying changes
|
||||||
|
addr, err := i2pkeys.Lookup(newConfig.Target)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid target address in config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update mutable configuration fields
|
||||||
|
// The Garlic connection (SAM) is preserved to maintain tunnel identity and keys
|
||||||
|
t.TunnelConfig = *newConfig
|
||||||
|
t.I2PAddr = addr
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/go-i2p/go-forward/config"
|
"github.com/go-i2p/go-forward/config"
|
||||||
@@ -191,8 +192,50 @@ func (t *TCPServer) SetOptions(opts map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the tunnel config from file
|
// LoadConfig loads tunnel configuration from a file and updates the tunnel settings.
|
||||||
|
// The tunnel must be stopped before calling LoadConfig to prevent inconsistent state.
|
||||||
|
// Supported formats: .properties, .ini, .yaml/.yml
|
||||||
|
//
|
||||||
|
// Why: Production deployments need to reload configuration without recreating tunnel objects.
|
||||||
|
// Design: Uses go-i2ptunnel-config library for parsing. Preserves SAM connection and I2P keys.
|
||||||
func (t *TCPServer) LoadConfig(path string) error {
|
func (t *TCPServer) LoadConfig(path string) error {
|
||||||
// For now, return an error indicating this method needs configuration file support
|
// Prevent config changes while tunnel is running to avoid race conditions
|
||||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
if t.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusRunning ||
|
||||||
|
t.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusStarting {
|
||||||
|
return fmt.Errorf("cannot load config while tunnel is %s - stop tunnel first", t.I2PTunnelStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse config file using the converter library
|
||||||
|
conv := i2pconv.Converter{}
|
||||||
|
format, err := conv.DetectFormat(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect config format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig, err := conv.ParseInput(bytes, format)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type safety: ensure loaded config matches expected tunnel type
|
||||||
|
if newConfig.Type != "tcpserver" {
|
||||||
|
return fmt.Errorf("config file contains %s tunnel, expected tcpserver", newConfig.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate target address (local service) before applying changes
|
||||||
|
targetAddr, err := net.ResolveTCPAddr("tcp", newConfig.Target)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid target address in config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update mutable configuration fields
|
||||||
|
t.TunnelConfig = *newConfig
|
||||||
|
t.Addr = targetAddr
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/go-i2p/go-forward/config"
|
"github.com/go-i2p/go-forward/config"
|
||||||
@@ -184,8 +185,47 @@ func (u *UDPClient) SetOptions(opts map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the tunnel config from file
|
// LoadConfig loads tunnel configuration from a file and updates the tunnel settings.
|
||||||
|
// The tunnel must be stopped before calling LoadConfig to prevent inconsistent state.
|
||||||
|
// Supported formats: .properties, .ini, .yaml/.yml
|
||||||
func (u *UDPClient) LoadConfig(path string) error {
|
func (u *UDPClient) LoadConfig(path string) error {
|
||||||
// For now, return an error indicating this method needs configuration file support
|
// Prevent config changes while tunnel is running to avoid race conditions
|
||||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
if u.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusRunning ||
|
||||||
|
u.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusStarting {
|
||||||
|
return fmt.Errorf("cannot load config while tunnel is %s - stop tunnel first", u.I2PTunnelStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse config file using the converter library
|
||||||
|
conv := i2pconv.Converter{}
|
||||||
|
format, err := conv.DetectFormat(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect config format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig, err := conv.ParseInput(bytes, format)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type safety: ensure loaded config matches expected tunnel type
|
||||||
|
if newConfig.Type != "udpclient" {
|
||||||
|
return fmt.Errorf("config file contains %s tunnel, expected udpclient", newConfig.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate target address before applying changes
|
||||||
|
addr, err := i2pkeys.Lookup(newConfig.Target)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid target address in config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update mutable configuration fields
|
||||||
|
u.TunnelConfig = *newConfig
|
||||||
|
u.I2PAddr = addr
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/go-i2p/go-forward/config"
|
"github.com/go-i2p/go-forward/config"
|
||||||
@@ -176,8 +177,47 @@ func (u *UDPServer) SetOptions(opts map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the tunnel config from file
|
// LoadConfig loads tunnel configuration from a file and updates the tunnel settings.
|
||||||
|
// The tunnel must be stopped before calling LoadConfig to prevent inconsistent state.
|
||||||
|
// Supported formats: .properties, .ini, .yaml/.yml
|
||||||
func (u *UDPServer) LoadConfig(path string) error {
|
func (u *UDPServer) LoadConfig(path string) error {
|
||||||
// For now, return an error indicating this method needs configuration file support
|
// Prevent config changes while tunnel is running to avoid race conditions
|
||||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
if u.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusRunning ||
|
||||||
|
u.I2PTunnelStatus == i2ptunnel.I2PTunnelStatusStarting {
|
||||||
|
return fmt.Errorf("cannot load config while tunnel is %s - stop tunnel first", u.I2PTunnelStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse config file using the converter library
|
||||||
|
conv := i2pconv.Converter{}
|
||||||
|
format, err := conv.DetectFormat(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect config format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig, err := conv.ParseInput(bytes, format)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type safety: ensure loaded config matches expected tunnel type
|
||||||
|
if newConfig.Type != "udpserver" {
|
||||||
|
return fmt.Errorf("config file contains %s tunnel, expected udpserver", newConfig.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate target address (local service) before applying changes
|
||||||
|
targetAddr, err := net.ResolveUDPAddr("udp", newConfig.Target)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid target address in config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update mutable configuration fields
|
||||||
|
u.TunnelConfig = *newConfig
|
||||||
|
u.Addr = targetAddr
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user