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/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-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-sam-go v0.0.0-20251016194809-b6ead96fdc39
|
||||
github.com/go-i2p/i2pkeys v0.33.92
|
||||
github.com/go-i2p/onramp v0.33.93-0.20251016200402-d3ac8f5353c5
|
||||
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301
|
||||
@@ -20,7 +21,6 @@ require (
|
||||
github.com/cretz/bine v0.2.0 // 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/go-sam-go v0.0.0-20251016194809-b6ead96fdc39 // indirect
|
||||
github.com/go-i2p/logger v0.0.0-20241123010126-3050657e5d0c // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/oklog/ulid/v2 v2.1.1 // indirect
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
@@ -192,8 +193,44 @@ func (h *HTTPClient) SetOptions(opts map[string]string) error {
|
||||
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 {
|
||||
// For now, return an error indicating this method needs configuration file support
|
||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
||||
// Prevent config changes while tunnel is running to avoid race conditions
|
||||
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"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
httpinspector "github.com/go-i2p/go-connfilter/http"
|
||||
@@ -201,8 +202,50 @@ func (h *HTTPServer) SetOptions(opts map[string]string) error {
|
||||
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 {
|
||||
// For now, return an error indicating this method needs configuration file support
|
||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
||||
// Prevent config changes while tunnel is running to avoid race conditions
|
||||
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"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
ircinspector "github.com/go-i2p/go-connfilter/irc"
|
||||
@@ -180,8 +181,47 @@ func (i *IRCClient) SetOptions(opts map[string]string) error {
|
||||
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 {
|
||||
// For now, return an error indicating this method needs configuration file support
|
||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
||||
// Prevent config changes while tunnel is running to avoid race conditions
|
||||
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"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
ircinspector "github.com/go-i2p/go-connfilter/irc"
|
||||
@@ -191,8 +192,47 @@ func (i *IRCServer) SetOptions(opts map[string]string) error {
|
||||
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 {
|
||||
// For now, return an error indicating this method needs configuration file support
|
||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
||||
// Prevent config changes while tunnel is running to avoid race conditions
|
||||
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"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
@@ -204,8 +205,44 @@ func (s *SOCKS) SetOptions(opts map[string]string) error {
|
||||
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 {
|
||||
// For now, return an error indicating this method needs configuration file support
|
||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
||||
// Prevent config changes while tunnel is running to avoid race conditions
|
||||
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"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-i2p/go-forward/config"
|
||||
@@ -183,8 +184,56 @@ func (t *TCPClient) SetOptions(opts map[string]string) error {
|
||||
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 {
|
||||
// For now, return an error indicating this method needs configuration file support
|
||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
||||
// Prevent config changes while tunnel is running to avoid race conditions
|
||||
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"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-i2p/go-forward/config"
|
||||
@@ -191,8 +192,50 @@ func (t *TCPServer) SetOptions(opts map[string]string) error {
|
||||
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 {
|
||||
// For now, return an error indicating this method needs configuration file support
|
||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
||||
// Prevent config changes while tunnel is running to avoid race conditions
|
||||
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"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-i2p/go-forward/config"
|
||||
@@ -184,8 +185,47 @@ func (u *UDPClient) SetOptions(opts map[string]string) error {
|
||||
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 {
|
||||
// For now, return an error indicating this method needs configuration file support
|
||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
||||
// Prevent config changes while tunnel is running to avoid race conditions
|
||||
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"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-i2p/go-forward/config"
|
||||
@@ -176,8 +177,47 @@ func (u *UDPServer) SetOptions(opts map[string]string) error {
|
||||
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 {
|
||||
// For now, return an error indicating this method needs configuration file support
|
||||
return fmt.Errorf("LoadConfig not yet implemented: would load configuration from %s", path)
|
||||
// Prevent config changes while tunnel is running to avoid race conditions
|
||||
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