package settings import ( "fmt" "path/filepath" "github.com/qdm12/gluetun/internal/constants/providers" "github.com/qdm12/gosettings" "github.com/qdm12/gosettings/validate" "github.com/qdm12/gotree" ) // PortForwarding contains settings for port forwarding. type PortForwarding struct { // Enabled is true if port forwarding should be activated. // It cannot be nil for the internal state. Enabled *bool `json:"enabled"` // Provider is set to specify which custom port forwarding code // should be used. This is especially necessary for the custom // provider using Wireguard for a provider where Wireguard is not // natively supported but custom port forwading code is available. // It defaults to the empty string, meaning the current provider // should be the one used for port forwarding. // It cannot be nil for the internal state. Provider *string `json:"provider"` // Filepath is the port forwarding status file path // to use. It can be the empty string to indicate not // to write to a file. It cannot be nil for the // internal state Filepath *string `json:"status_file_path"` // ListeningPort is the port traffic would be redirected to from the // forwarded port. The redirection is disabled if it is set to 0, which // is its default as well. ListeningPort *uint16 `json:"listening_port"` } func (p PortForwarding) Validate(vpnProvider string) (err error) { if !*p.Enabled { return nil } // Validate current provider or custom provider specified providerSelected := vpnProvider if *p.Provider != "" { providerSelected = *p.Provider } validProviders := []string{ providers.PrivateInternetAccess, providers.Protonvpn, } if err = validate.IsOneOf(providerSelected, validProviders...); err != nil { return fmt.Errorf("%w: %w", ErrPortForwardingEnabled, err) } // Validate Filepath if *p.Filepath != "" { // optional _, err := filepath.Abs(*p.Filepath) if err != nil { return fmt.Errorf("filepath is not valid: %w", err) } } return nil } func (p *PortForwarding) Copy() (copied PortForwarding) { return PortForwarding{ Enabled: gosettings.CopyPointer(p.Enabled), Provider: gosettings.CopyPointer(p.Provider), Filepath: gosettings.CopyPointer(p.Filepath), ListeningPort: gosettings.CopyPointer(p.ListeningPort), } } func (p *PortForwarding) mergeWith(other PortForwarding) { p.Enabled = gosettings.MergeWithPointer(p.Enabled, other.Enabled) p.Provider = gosettings.MergeWithPointer(p.Provider, other.Provider) p.Filepath = gosettings.MergeWithPointer(p.Filepath, other.Filepath) p.ListeningPort = gosettings.MergeWithPointer(p.ListeningPort, other.ListeningPort) } func (p *PortForwarding) OverrideWith(other PortForwarding) { p.Enabled = gosettings.OverrideWithPointer(p.Enabled, other.Enabled) p.Provider = gosettings.OverrideWithPointer(p.Provider, other.Provider) p.Filepath = gosettings.OverrideWithPointer(p.Filepath, other.Filepath) p.ListeningPort = gosettings.OverrideWithPointer(p.ListeningPort, other.ListeningPort) } func (p *PortForwarding) setDefaults() { p.Enabled = gosettings.DefaultPointer(p.Enabled, false) p.Provider = gosettings.DefaultPointer(p.Provider, "") p.Filepath = gosettings.DefaultPointer(p.Filepath, "/tmp/gluetun/forwarded_port") p.ListeningPort = gosettings.DefaultPointer(p.ListeningPort, 0) } func (p PortForwarding) String() string { return p.toLinesNode().String() } func (p PortForwarding) toLinesNode() (node *gotree.Node) { if !*p.Enabled { return nil } node = gotree.New("Automatic port forwarding settings:") listeningPort := "disabled" if *p.ListeningPort != 0 { listeningPort = fmt.Sprintf("%d", *p.ListeningPort) } node.Appendf("Redirection listening port: %s", listeningPort) if *p.Provider == "" { node.Appendf("Use port forwarding code for current provider") } else { node.Appendf("Use code for provider: %s", *p.Provider) } filepath := *p.Filepath if filepath == "" { filepath = "[not set]" } node.Appendf("Forwarded port file path: %s", filepath) return node }