diff --git a/.golangci.yml b/.golangci.yml index d24f8fed..7b2a7f27 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -68,6 +68,9 @@ linters: - err113 - mnd path: ci\/.+\.go + - linters: + - err113 + text: "do not define dynamic errors, use wrapped static errors instead" paths: - third_party$ diff --git a/cmd/gluetun/main.go b/cmd/gluetun/main.go index 8d5e5e0a..779c7fab 100644 --- a/cmd/gluetun/main.go +++ b/cmd/gluetun/main.go @@ -142,8 +142,6 @@ func main() { } } -var errCommandUnknown = errors.New("command is unknown") - //nolint:gocognit,gocyclo,maintidx func _main(ctx context.Context, buildInfo models.BuildInformation, args []string, logger log.LoggerInterface, reader *reader.Reader, @@ -165,7 +163,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation, case "genkey": return cli.GenKey(args[2:]) default: - return fmt.Errorf("%w: %s", errCommandUnknown, args[1]) + return fmt.Errorf("command is unknown: %s", args[1]) } } diff --git a/internal/alpine/users.go b/internal/alpine/users.go index 537974e3..7e22269a 100644 --- a/internal/alpine/users.go +++ b/internal/alpine/users.go @@ -1,7 +1,6 @@ package alpine import ( - "errors" "fmt" "io/fs" "os" @@ -9,8 +8,6 @@ import ( "strconv" ) -var ErrUserAlreadyExists = errors.New("user already exists") - // CreateUser creates a user in Alpine with the given UID. func (a *Alpine) CreateUser(username string, uid int) (createdUsername string, err error) { UIDStr := strconv.Itoa(uid) @@ -34,8 +31,8 @@ func (a *Alpine) CreateUser(username string, uid int) (createdUsername string, e } if u != nil { - return "", fmt.Errorf("%w: with name %s for ID %s instead of %d", - ErrUserAlreadyExists, username, u.Uid, uid) + return "", fmt.Errorf("user already exists: with name %s for ID %s instead of %d", + username, u.Uid, uid) } const permission = fs.FileMode(0o644) diff --git a/internal/amneziawg/constructor_test.go b/internal/amneziawg/constructor_test.go index 856af3ad..f799373e 100644 --- a/internal/amneziawg/constructor_test.go +++ b/internal/amneziawg/constructor_test.go @@ -1,6 +1,7 @@ package amneziawg import ( + "errors" "net/netip" "testing" @@ -28,7 +29,7 @@ func Test_New(t *testing.T) { PrivateKey: "", }, }, - err: wireguard.ErrPrivateKeyMissing, + err: errors.New("private key is missing"), }, "minimal valid settings": { settings: Settings{ diff --git a/internal/amneziawg/run.go b/internal/amneziawg/run.go index e85e7ecc..5d703c27 100644 --- a/internal/amneziawg/run.go +++ b/internal/amneziawg/run.go @@ -13,11 +13,6 @@ import ( "github.com/qdm12/gluetun/internal/wireguard" ) -var ( - errTunNameMismatch = errors.New("TUN device name is mismatching") - errDeviceWaited = errors.New("device waited for") -) - // Run runs the amneziawg interface and waits until the context is done, then it cleans up the // interface and returns any error that occurred during setup or waiting. It sends an error to // waitError if any error occurs during setup or waiting, otherwise it sends nil when the context @@ -52,8 +47,7 @@ func setupUserspace(ctx context.Context, if err != nil { return 0, nil, fmt.Errorf("getting created TUN device name: %w", err) } else if tunName != interfaceName { - return 0, nil, fmt.Errorf("%w: expected %q and got %q", - errTunNameMismatch, interfaceName, tunName) + return 0, nil, fmt.Errorf("TUN device name is mismatching: expected %q and got %q", interfaceName, tunName) } link, err := netLinker.LinkByName(interfaceName) @@ -106,7 +100,7 @@ func setupUserspace(ctx context.Context, case err = <-uapiAcceptErrorCh: close(uapiAcceptErrorCh) case <-device.Wait(): - err = errDeviceWaited + err = errors.New("device waited for") } cleanups.Cleanup(logger) diff --git a/internal/cli/formatservers.go b/internal/cli/formatservers.go index 254c0451..fbb70a79 100644 --- a/internal/cli/formatservers.go +++ b/internal/cli/formatservers.go @@ -16,11 +16,6 @@ import ( "golang.org/x/text/language" ) -var ( - ErrProviderUnspecified = errors.New("VPN provider to format was not specified") - ErrMultipleProvidersToFormat = errors.New("more than one VPN provider to format were specified") -) - func addProviderFlag(flagSet *flag.FlagSet, providerToFormat map[string]*bool, provider string, titleCaser cases.Caser, ) { @@ -65,11 +60,10 @@ func (c *CLI) FormatServers(args []string) error { } switch len(providers) { case 0: - return fmt.Errorf("%w", ErrProviderUnspecified) + return errors.New("VPN provider to format was not specified") case 1: default: - return fmt.Errorf("%w: %d specified: %s", - ErrMultipleProvidersToFormat, len(providers), + return fmt.Errorf("more than one VPN provider to format were specified: %d specified: %s", len(providers), strings.Join(providers, ", ")) } diff --git a/internal/cli/update.go b/internal/cli/update.go index 85a1a211..3a065d68 100644 --- a/internal/cli/update.go +++ b/internal/cli/update.go @@ -24,13 +24,6 @@ import ( "github.com/qdm12/gluetun/internal/updater/unzip" ) -var ( - ErrModeUnspecified = errors.New("at least one of -enduser or -maintainer must be specified") - ErrNoProviderSpecified = errors.New("no provider was specified") - ErrUsernameMissing = errors.New("username is required for this provider") - ErrPasswordMissing = errors.New("password is required for this provider") -) - type UpdaterLogger interface { Info(s string) Warn(s string) @@ -65,14 +58,14 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e } if !endUserMode && !maintainerMode { - return fmt.Errorf("%w", ErrModeUnspecified) + return errors.New("at least one of -enduser or -maintainer must be specified") } if updateAll { options.Providers = providers.All() } else { if csvProviders == "" { - return fmt.Errorf("%w", ErrNoProviderSpecified) + return errors.New("no provider was specified") } options.Providers = strings.Split(csvProviders, ",") } diff --git a/internal/command/split.go b/internal/command/split.go index e1e8d223..8a661c34 100644 --- a/internal/command/split.go +++ b/internal/command/split.go @@ -8,13 +8,6 @@ import ( "unicode/utf8" ) -var ( - errCommandEmpty = errors.New("command is empty") - errSingleQuoteUnterminated = errors.New("unterminated single-quoted string") - errDoubleQuoteUnterminated = errors.New("unterminated double-quoted string") - errEscapeUnterminated = errors.New("unterminated backslash-escape") -) - // split splits a command string into a slice of arguments. // This is especially important for commands such as: // /bin/sh -c "echo hello" @@ -25,7 +18,7 @@ var ( // - expansion (brace, shell or pathname). func split(command string) (words []string, err error) { if command == "" { - return nil, fmt.Errorf("%w", errCommandEmpty) + return nil, errors.New("command is empty") } const bufferSize = 1024 @@ -42,7 +35,7 @@ func split(command string) (words []string, err error) { case character == '\\': // Look ahead to eventually skip an escaped newline if command[startIndex+runeSize:] == "" { - return nil, fmt.Errorf("%w: %q", errEscapeUnterminated, command) + return nil, fmt.Errorf("unterminated backslash-escape: %q", command) } character, runeSize := utf8.DecodeRuneInString(command[startIndex+runeSize:]) if character == '\n' { @@ -119,7 +112,7 @@ func handleDoubleQuoted(input string, startIndex int, buffer *bytes.Buffer) ( startIndex = cursor } } - return "", 0, fmt.Errorf("%w", errDoubleQuoteUnterminated) + return "", 0, errors.New("unterminated double-quoted string") } func handleSingleQuoted(input string, startIndex int, buffer *bytes.Buffer) ( @@ -127,7 +120,7 @@ func handleSingleQuoted(input string, startIndex int, buffer *bytes.Buffer) ( ) { closingQuoteIndex := strings.IndexRune(input[startIndex:], '\'') if closingQuoteIndex == -1 { - return "", 0, fmt.Errorf("%w", errSingleQuoteUnterminated) + return "", 0, errors.New("unterminated single-quoted string") } buffer.WriteString(input[startIndex : startIndex+closingQuoteIndex]) const singleQuoteRuneLength = 1 @@ -139,7 +132,7 @@ func handleEscaped(input string, startIndex int, buffer *bytes.Buffer) ( word string, newStartIndex int, err error, ) { if input[startIndex:] == "" { - return "", 0, fmt.Errorf("%w", errEscapeUnterminated) + return "", 0, errors.New("unterminated backslash-escape") } character, runeLength := utf8.DecodeRuneInString(input[startIndex:]) if character != '\n' { // backslash-escaped newline is ignored diff --git a/internal/command/split_test.go b/internal/command/split_test.go index 8eb14cbb..31aef642 100644 --- a/internal/command/split_test.go +++ b/internal/command/split_test.go @@ -12,12 +12,10 @@ func Test_split(t *testing.T) { testCases := map[string]struct { command string words []string - errWrapped error errMessage string }{ "empty": { command: "", - errWrapped: errCommandEmpty, errMessage: "command is empty", }, "concrete_sh_command": { @@ -74,22 +72,18 @@ func Test_split(t *testing.T) { }, "unterminated_single_quote": { command: "'abc'\\''def", - errWrapped: errSingleQuoteUnterminated, errMessage: `splitting word in "'abc'\\''def": unterminated single-quoted string`, }, "unterminated_double_quote": { command: "\"abc'def", - errWrapped: errDoubleQuoteUnterminated, errMessage: `splitting word in "\"abc'def": unterminated double-quoted string`, }, "unterminated_escape": { command: "abc\\", - errWrapped: errEscapeUnterminated, errMessage: `splitting word in "abc\\": unterminated backslash-escape`, }, "unterminated_escape_only": { command: " \\", - errWrapped: errEscapeUnterminated, errMessage: `unterminated backslash-escape: " \\"`, }, } @@ -101,9 +95,10 @@ func Test_split(t *testing.T) { words, err := split(testCase.command) assert.Equal(t, testCase.words, words) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { - assert.EqualError(t, err, testCase.errMessage) + if testCase.errMessage != "" { + assert.ErrorContains(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/configuration/settings/amneziawg.go b/internal/configuration/settings/amneziawg.go index 107d32d1..8c58d4c7 100644 --- a/internal/configuration/settings/amneziawg.go +++ b/internal/configuration/settings/amneziawg.go @@ -1,7 +1,6 @@ package settings import ( - "errors" "fmt" "strconv" "strings" @@ -177,14 +176,6 @@ func (a AmneziaWg) toLinesNode() (node *gotree.Node) { return node } -var ( - ErrAmenziawgImplementationNotValid = errors.New("AmneziaWG implementation is not valid") - ErrJunkPacketBounds = errors.New("junk packet minimum must be lower than or equal to maximum") - ErrJunkPacketMinMaxNotSet = errors.New("junk packet min and max must be set when junk packet count is set") - ErrJunkPacketCountNotSet = errors.New("junk packet count must be set when junk packet min or max is set") - ErrHeaderRangeMalformed = errors.New("header range is malformed") -) - func (a AmneziaWg) validate(vpnProvider string, ipv6Supported bool) error { const amneziaWG = true err := a.Wireguard.validate(vpnProvider, ipv6Supported, amneziaWG) @@ -194,16 +185,16 @@ func (a AmneziaWg) validate(vpnProvider string, ipv6Supported bool) error { if *a.JunkPacketCount == 0 { if *a.JunkPacketMin != 0 || *a.JunkPacketMax != 0 { - return fmt.Errorf("%w: jc=%d and jmin=%d and jmax=%d", - ErrJunkPacketCountNotSet, a.JunkPacketCount, *a.JunkPacketMin, *a.JunkPacketMax) + return fmt.Errorf("junk packet count must be set when junk packet min or max is set: "+ + "jc=%d and jmin=%d and jmax=%d", a.JunkPacketCount, *a.JunkPacketMin, *a.JunkPacketMax) } } else { if *a.JunkPacketMin == 0 || *a.JunkPacketMax == 0 { - return fmt.Errorf("%w: jc=%d and jmin=%d and jmax=%d", - ErrJunkPacketMinMaxNotSet, a.JunkPacketCount, *a.JunkPacketMin, *a.JunkPacketMax) + return fmt.Errorf("junk packet min and max must be set when junk packet count is set: "+ + "jc=%d and jmin=%d and jmax=%d", a.JunkPacketCount, *a.JunkPacketMin, *a.JunkPacketMax) } else if *a.JunkPacketMin > *a.JunkPacketMax { - return fmt.Errorf("%w: jmin=%d and jmax=%d", - ErrJunkPacketBounds, *a.JunkPacketMin, *a.JunkPacketMax) + return fmt.Errorf("junk packet minimum must be lower than or equal to maximum: "+ + "jmin=%d and jmax=%d", *a.JunkPacketMin, *a.JunkPacketMax) } } @@ -222,20 +213,20 @@ func (a AmneziaWg) validate(vpnProvider string, ipv6Supported bool) error { case 1: _, err := strconv.Atoi(fields[0]) if err != nil { - return fmt.Errorf("%w: %s value %s is not a number", - ErrHeaderRangeMalformed, name, headerRange) + return fmt.Errorf("header range is malformed: "+ + "%s value %s is not a number", name, headerRange) } case 2: //nolint:mnd for _, field := range fields { _, err := strconv.Atoi(field) if err != nil { - return fmt.Errorf("%w: %s value %s is not a valid range", - ErrHeaderRangeMalformed, name, headerRange) + return fmt.Errorf("header range is malformed: "+ + "%s value %s is not a valid range", name, headerRange) } } default: - return fmt.Errorf("%w: %s value %s must be in the form n or n-m", - ErrHeaderRangeMalformed, name, headerRange) + return fmt.Errorf("header range is malformed: "+ + "%s value %s must be in the form n or n-m", name, headerRange) } } diff --git a/internal/configuration/settings/dns.go b/internal/configuration/settings/dns.go index 7649112f..789e5324 100644 --- a/internal/configuration/settings/dns.go +++ b/internal/configuration/settings/dns.go @@ -1,7 +1,6 @@ package settings import ( - "errors" "fmt" "net/netip" "time" @@ -48,22 +47,15 @@ type DNS struct { UpstreamPlainAddresses []netip.AddrPort } -var ( - ErrDNSUpstreamTypeNotValid = errors.New("DNS upstream type is not valid") - ErrDNSUpdatePeriodTooShort = errors.New("update period is too short") - ErrDNSUpstreamPlainNoIPv6 = errors.New("upstream plain addresses do not contain any IPv6 address") - ErrDNSUpstreamPlainNoIPv4 = errors.New("upstream plain addresses do not contain any IPv4 address") -) - func (d DNS) validate() (err error) { if !helpers.IsOneOf(d.UpstreamType, DNSUpstreamTypeDot, DNSUpstreamTypeDoh, DNSUpstreamTypePlain) { - return fmt.Errorf("%w: %s", ErrDNSUpstreamTypeNotValid, d.UpstreamType) + return fmt.Errorf("DNS upstream type is not valid: %s", d.UpstreamType) } const minUpdatePeriod = 30 * time.Second if *d.UpdatePeriod != 0 && *d.UpdatePeriod < minUpdatePeriod { - return fmt.Errorf("%w: %s must be bigger than %s", - ErrDNSUpdatePeriodTooShort, *d.UpdatePeriod, minUpdatePeriod) + return fmt.Errorf("update period is too short: %s must be bigger than %s", + *d.UpdatePeriod, minUpdatePeriod) } if d.UpstreamType == DNSUpstreamTypePlain { @@ -81,9 +73,11 @@ func (d DNS) validate() (err error) { } switch { case *d.IPv6 && !selectedHasPlainIPv6: - return fmt.Errorf("%w: in %d addresses", ErrDNSUpstreamPlainNoIPv6, len(d.UpstreamPlainAddresses)) + return fmt.Errorf("upstream plain addresses do not contain any IPv6 address: "+ + "in %d addresses", len(d.UpstreamPlainAddresses)) case !*d.IPv6 && !selectedHasPlainIPv4: - return fmt.Errorf("%w: in %d addresses", ErrDNSUpstreamPlainNoIPv4, len(d.UpstreamPlainAddresses)) + return fmt.Errorf("upstream plain addresses do not contain any IPv4 address: "+ + "in %d addresses", len(d.UpstreamPlainAddresses)) } } // Note: all DNS built in providers have both IPv4 and IPv6 addresses for all modes diff --git a/internal/configuration/settings/dnsblacklist.go b/internal/configuration/settings/dnsblacklist.go index bf380cfe..c15704e8 100644 --- a/internal/configuration/settings/dnsblacklist.go +++ b/internal/configuration/settings/dnsblacklist.go @@ -1,7 +1,6 @@ package settings import ( - "errors" "fmt" "net/http" "net/netip" @@ -37,22 +36,16 @@ func (b *DNSBlacklist) setDefaults() { var hostRegex = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9_])(\.([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9]))*$`) //nolint:lll -var ( - ErrAllowedHostNotValid = errors.New("allowed host is not valid") - ErrBlockedHostNotValid = errors.New("blocked host is not valid") - ErrRebindingProtectionExemptHostNotValid = errors.New("rebinding protection exempt host is not valid") -) - func (b DNSBlacklist) validate() (err error) { for _, host := range b.AllowedHosts { if !hostRegex.MatchString(host) { - return fmt.Errorf("%w: %s", ErrAllowedHostNotValid, host) + return fmt.Errorf("allowed host is not valid: %s", host) } } for _, host := range b.AddBlockedHosts { if !hostRegex.MatchString(host) { - return fmt.Errorf("%w: %s", ErrBlockedHostNotValid, host) + return fmt.Errorf("blocked host is not valid: %s", host) } } @@ -61,7 +54,7 @@ func (b DNSBlacklist) validate() (err error) { host = host[2:] } if !hostRegex.MatchString(host) { - return fmt.Errorf("%w: %s", ErrRebindingProtectionExemptHostNotValid, host) + return fmt.Errorf("rebinding protection exempt host is not valid: %s", host) } } @@ -209,8 +202,6 @@ func readDNSBlockedIPs(r *reader.Reader) (ips []netip.Addr, return ips, ipPrefixes, nil } -var ErrPrivateAddressNotValid = errors.New("private address is not a valid IP or CIDR range") - func readDNSPrivateAddresses(r *reader.Reader) (ips []netip.Addr, ipPrefixes []netip.Prefix, err error, ) { @@ -236,8 +227,9 @@ func readDNSPrivateAddresses(r *reader.Reader) (ips []netip.Addr, } return nil, nil, fmt.Errorf( - "environment variable DOT_PRIVATE_ADDRESS: %w: %s", - ErrPrivateAddressNotValid, privateAddress) + "environment variable DOT_PRIVATE_ADDRESS: "+ + "private address is not a valid IP or CIDR range: %s", + privateAddress) } return ips, ipPrefixes, nil diff --git a/internal/configuration/settings/errors.go b/internal/configuration/settings/errors.go deleted file mode 100644 index b3329a5b..00000000 --- a/internal/configuration/settings/errors.go +++ /dev/null @@ -1,58 +0,0 @@ -package settings - -import "errors" - -var ( - ErrValueUnknown = errors.New("value is unknown") - ErrCityNotValid = errors.New("the city specified is not valid") - ErrControlServerPrivilegedPort = errors.New("cannot use privileged port without running as root") - ErrCategoryNotValid = errors.New("the category specified is not valid") - ErrCountryNotValid = errors.New("the country specified is not valid") - ErrFilepathMissing = errors.New("filepath is missing") - ErrFirewallZeroPort = errors.New("cannot have a zero port") - ErrFirewallPublicOutboundSubnet = errors.New("outbound subnet has an unspecified address") - ErrHostnameNotValid = errors.New("the hostname specified is not valid") - ErrISPNotValid = errors.New("the ISP specified is not valid") - ErrMinRatioNotValid = errors.New("minimum ratio is not valid") - ErrMissingValue = errors.New("missing value") - ErrNameNotValid = errors.New("the server name specified is not valid") - ErrOpenVPNClientKeyMissing = errors.New("client key is missing") - ErrOpenVPNCustomPortNotAllowed = errors.New("custom endpoint port is not allowed") - ErrOpenVPNEncryptionPresetNotValid = errors.New("PIA encryption preset is not valid") - ErrOpenVPNInterfaceNotValid = errors.New("interface name is not valid") - ErrOpenVPNKeyPassphraseIsEmpty = errors.New("key passphrase is empty") - ErrOpenVPNMSSFixIsTooHigh = errors.New("mssfix option value is too high") - ErrOpenVPNPasswordIsEmpty = errors.New("password is empty") - ErrOpenVPNTCPNotSupported = errors.New("TCP protocol is not supported") - ErrOpenVPNUserIsEmpty = errors.New("user is empty") - ErrOpenVPNVerbosityIsOutOfBounds = errors.New("verbosity value is out of bounds") - ErrOpenVPNVersionIsNotValid = errors.New("version is not valid") - ErrPortForwardingEnabled = errors.New("port forwarding cannot be enabled") - ErrPortForwardingUserEmpty = errors.New("port forwarding username is empty") - ErrPortForwardingPasswordEmpty = errors.New("port forwarding password is empty") - ErrRegionNotValid = errors.New("the region specified is not valid") - ErrServerAddressNotValid = errors.New("server listening address is not valid") - ErrSystemPGIDNotValid = errors.New("process group id is not valid") - ErrSystemPUIDNotValid = errors.New("process user id is not valid") - ErrSystemTimezoneNotValid = errors.New("timezone is not valid") - ErrUpdaterPeriodTooSmall = errors.New("VPN server data updater period is too small") - ErrUpdaterProtonPasswordMissing = errors.New("proton password is missing") - ErrUpdaterProtonEmailMissing = errors.New("proton email is missing") - ErrVPNProviderNameNotValid = errors.New("VPN provider name is not valid") - ErrVPNTypeNotValid = errors.New("VPN type is not valid") - ErrWireguardAllowedIPNotSet = errors.New("allowed IP is not set") - ErrWireguardAllowedIPsNotSet = errors.New("allowed IPs is not set") - ErrWireguardEndpointIPNotSet = errors.New("endpoint IP is not set") - ErrWireguardEndpointPortNotAllowed = errors.New("endpoint port is not allowed") - ErrWireguardEndpointPortNotSet = errors.New("endpoint port is not set") - ErrWireguardEndpointPortSet = errors.New("endpoint port is set") - ErrWireguardInterfaceAddressNotSet = errors.New("interface address is not set") - ErrWireguardInterfaceAddressIPv6 = errors.New("interface address is IPv6 but IPv6 is not supported") - ErrWireguardInterfaceNotValid = errors.New("interface name is not valid") - ErrWireguardPreSharedKeyNotSet = errors.New("pre-shared key is not set") - ErrWireguardPrivateKeyNotSet = errors.New("private key is not set") - ErrWireguardPublicKeyNotSet = errors.New("public key is not set") - ErrWireguardPublicKeyNotValid = errors.New("public key is not valid") - ErrWireguardKeepAliveNegative = errors.New("persistent keep alive interval is negative") - ErrWireguardImplementationNotValid = errors.New("implementation is not valid") -) diff --git a/internal/configuration/settings/firewall.go b/internal/configuration/settings/firewall.go index 7f94d989..13b7b04d 100644 --- a/internal/configuration/settings/firewall.go +++ b/internal/configuration/settings/firewall.go @@ -1,6 +1,7 @@ package settings import ( + "errors" "fmt" "net/netip" @@ -20,16 +21,16 @@ type Firewall struct { func (f Firewall) validate() (err error) { if hasZeroPort(f.VPNInputPorts) { - return fmt.Errorf("VPN input ports: %w", ErrFirewallZeroPort) + return errors.New("VPN input ports: cannot have a zero port") } if hasZeroPort(f.InputPorts) { - return fmt.Errorf("input ports: %w", ErrFirewallZeroPort) + return errors.New("input ports: cannot have a zero port") } for _, subnet := range f.OutboundSubnets { if subnet.Addr().IsUnspecified() { - return fmt.Errorf("%w: %s", ErrFirewallPublicOutboundSubnet, subnet) + return fmt.Errorf("outbound subnet has an unspecified address: %s", subnet) } } diff --git a/internal/configuration/settings/firewall_test.go b/internal/configuration/settings/firewall_test.go index 825db124..1895ba0e 100644 --- a/internal/configuration/settings/firewall_test.go +++ b/internal/configuration/settings/firewall_test.go @@ -13,25 +13,21 @@ func Test_Firewall_validate(t *testing.T) { testCases := map[string]struct { firewall Firewall - errWrapped error errMessage string }{ "empty": { - errWrapped: log.ErrLevelNotRecognized, errMessage: "iptables settings: log level: level is not recognized: ", }, "zero_vpn_input_port": { firewall: Firewall{ VPNInputPorts: []uint16{0}, }, - errWrapped: ErrFirewallZeroPort, errMessage: "VPN input ports: cannot have a zero port", }, "zero_input_port": { firewall: Firewall{ InputPorts: []uint16{0}, }, - errWrapped: ErrFirewallZeroPort, errMessage: "input ports: cannot have a zero port", }, "unspecified_outbound_subnet": { @@ -40,7 +36,6 @@ func Test_Firewall_validate(t *testing.T) { netip.MustParsePrefix("0.0.0.0/0"), }, }, - errWrapped: ErrFirewallPublicOutboundSubnet, errMessage: "outbound subnet has an unspecified address: 0.0.0.0/0", }, "public_outbound_subnet": { @@ -70,9 +65,10 @@ func Test_Firewall_validate(t *testing.T) { err := testCase.firewall.validate() - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/configuration/settings/health.go b/internal/configuration/settings/health.go index 55756780..4ff54bcd 100644 --- a/internal/configuration/settings/health.go +++ b/internal/configuration/settings/health.go @@ -38,12 +38,6 @@ type Health struct { RestartVPN *bool } -var ( - ErrICMPTargetIPNotValid = errors.New("ICMP target IP address is not valid") - ErrICMPTargetIPsNotCompatible = errors.New("ICMP target IP addresses are not compatible") - ErrSmallCheckTypeNotValid = errors.New("small check type is not valid") -) - func (h Health) Validate() (err error) { err = validate.ListeningAddress(h.ServerAddress, os.Getuid()) if err != nil { @@ -53,16 +47,16 @@ func (h Health) Validate() (err error) { for _, ip := range h.ICMPTargetIPs { switch { case !ip.IsValid(): - return fmt.Errorf("%w: %s", ErrICMPTargetIPNotValid, ip) + return fmt.Errorf("ICMP target IP address is not valid: %s", ip) case ip.IsUnspecified() && len(h.ICMPTargetIPs) > 1: - return fmt.Errorf("%w: only a single IP address must be set if it is to be unspecified", - ErrICMPTargetIPsNotCompatible) + return errors.New("ICMP target IP addresses are not compatible: " + + "only a single IP address must be set if it is to be unspecified") } } err = validate.IsOneOf(h.SmallCheckType, "icmp", "dns") if err != nil { - return fmt.Errorf("%w: %s", ErrSmallCheckTypeNotValid, err) + return fmt.Errorf("small check type is not valid: %w", err) } return nil diff --git a/internal/configuration/settings/httpproxy.go b/internal/configuration/settings/httpproxy.go index 64d9dcbf..75091c75 100644 --- a/internal/configuration/settings/httpproxy.go +++ b/internal/configuration/settings/httpproxy.go @@ -48,7 +48,7 @@ func (h HTTPProxy) validate() (err error) { // Do not validate user and password err = validate.ListeningAddress(h.ListeningAddress, os.Getuid()) if err != nil { - return fmt.Errorf("%w: %s", ErrServerAddressNotValid, h.ListeningAddress) + return fmt.Errorf("server listening address is not valid: %w", err) } return nil @@ -176,7 +176,6 @@ func readHTTProxyLog(r *reader.Reader) (enabled *bool, err error) { case "disabled", "no", "off": return ptrTo(false), nil default: - return nil, fmt.Errorf("HTTP retro-compatible proxy log setting: %w: %s", - ErrValueUnknown, value) + return nil, fmt.Errorf("HTTP retro-compatible proxy log setting: value is unknown: %s", value) } } diff --git a/internal/configuration/settings/openvpn.go b/internal/configuration/settings/openvpn.go index 7cffd4d3..a638cf1f 100644 --- a/internal/configuration/settings/openvpn.go +++ b/internal/configuration/settings/openvpn.go @@ -2,6 +2,7 @@ package settings import ( "encoding/base64" + "errors" "fmt" "regexp" "strings" @@ -92,7 +93,7 @@ func (o OpenVPN) validate(vpnProvider string) (err error) { // Validate version validVersions := []string{openvpn.Openvpn25, openvpn.Openvpn26} if err = validate.IsOneOf(o.Version, validVersions...); err != nil { - return fmt.Errorf("%w: %w", ErrOpenVPNVersionIsNotValid, err) + return fmt.Errorf("version is not valid: %w", err) } isCustom := vpnProvider == providers.Custom @@ -101,14 +102,14 @@ func (o OpenVPN) validate(vpnProvider string) (err error) { vpnProvider != providers.VPNSecure if isUserRequired && *o.User == "" { - return fmt.Errorf("%w", ErrOpenVPNUserIsEmpty) + return errors.New("user is empty") } passwordRequired := isUserRequired && (vpnProvider != providers.Ivpn || !ivpnAccountID.MatchString(*o.User)) if passwordRequired && *o.Password == "" { - return fmt.Errorf("%w", ErrOpenVPNPasswordIsEmpty) + return errors.New("password is empty") } err = validateOpenVPNConfigFilepath(isCustom, *o.ConfFile) @@ -132,23 +133,20 @@ func (o OpenVPN) validate(vpnProvider string) (err error) { } if *o.EncryptedKey != "" && *o.KeyPassphrase == "" { - return fmt.Errorf("%w", ErrOpenVPNKeyPassphraseIsEmpty) + return errors.New("key passphrase is empty") } const maxMSSFix = 10000 if *o.MSSFix > maxMSSFix { - return fmt.Errorf("%w: %d is over the maximum value of %d", - ErrOpenVPNMSSFixIsTooHigh, *o.MSSFix, maxMSSFix) + return fmt.Errorf("mssfix option value is too high: %d is over the maximum value of %d", *o.MSSFix, maxMSSFix) } if !regexpInterfaceName.MatchString(o.Interface) { - return fmt.Errorf("%w: '%s' does not match regex '%s'", - ErrOpenVPNInterfaceNotValid, o.Interface, regexpInterfaceName) + return fmt.Errorf("interface name is not valid: '%s' does not match regex '%s'", o.Interface, regexpInterfaceName) } if *o.Verbosity < 0 || *o.Verbosity > 6 { - return fmt.Errorf("%w: %d can only be between 0 and 5", - ErrOpenVPNVerbosityIsOutOfBounds, o.Verbosity) + return fmt.Errorf("verbosity value is out of bounds: %d can only be between 0 and 5", o.Verbosity) } return nil @@ -162,7 +160,7 @@ func validateOpenVPNConfigFilepath(isCustom bool, } if confFile == "" { - return fmt.Errorf("%w", ErrFilepathMissing) + return errors.New("filepath is missing") } err = validate.FileExists(confFile) @@ -189,7 +187,7 @@ func validateOpenVPNClientCertificate(vpnProvider, providers.VPNSecure, providers.VPNUnlimited: if clientCert == "" { - return fmt.Errorf("%w", ErrMissingValue) + return errors.New("missing value") } } @@ -211,7 +209,7 @@ func validateOpenVPNClientKey(vpnProvider, clientKey string) (err error) { providers.Cyberghost, providers.VPNUnlimited: if clientKey == "" { - return fmt.Errorf("%w", ErrMissingValue) + return errors.New("missing value") } } @@ -230,7 +228,7 @@ func validateOpenVPNEncryptedKey(vpnProvider, encryptedPrivateKey string, ) (err error) { if vpnProvider == providers.VPNSecure && encryptedPrivateKey == "" { - return fmt.Errorf("%w", ErrMissingValue) + return errors.New("missing value") } if encryptedPrivateKey == "" { diff --git a/internal/configuration/settings/openvpnselection.go b/internal/configuration/settings/openvpnselection.go index c72f62d2..13339cc0 100644 --- a/internal/configuration/settings/openvpnselection.go +++ b/internal/configuration/settings/openvpnselection.go @@ -62,8 +62,7 @@ func (o OpenVPNSelection) validate(vpnProvider string) (err error) { providers.Perfectprivacy, providers.Vyprvpn, ) { - return fmt.Errorf("%w: for VPN service provider %s", - ErrOpenVPNTCPNotSupported, vpnProvider) + return fmt.Errorf("TCP protocol is not supported: for VPN service provider %s", vpnProvider) } // Validate CustomPort @@ -78,8 +77,7 @@ func (o OpenVPNSelection) validate(vpnProvider string) (err error) { providers.Nordvpn, providers.Purevpn, providers.Surfshark, providers.VPNSecure, providers.VPNUnlimited, providers.Vyprvpn: - return fmt.Errorf("%w: for VPN service provider %s", - ErrOpenVPNCustomPortNotAllowed, vpnProvider) + return fmt.Errorf("custom endpoint port is not allowed: for VPN service provider %s", vpnProvider) default: var allowedTCP, allowedUDP []uint16 switch vpnProvider { @@ -123,8 +121,7 @@ func (o OpenVPNSelection) validate(vpnProvider string) (err error) { } err = validate.IsOneOf(*o.CustomPort, allowedPorts...) if err != nil { - return fmt.Errorf("%w: for VPN service provider %s: %w", - ErrOpenVPNCustomPortNotAllowed, vpnProvider, err) + return fmt.Errorf("custom endpoint port is not allowed: for VPN service provider %s: %w", vpnProvider, err) } } } @@ -136,7 +133,7 @@ func (o OpenVPNSelection) validate(vpnProvider string) (err error) { presets.Strong, } if err = validate.IsOneOf(*o.PIAEncPreset, validEncryptionPresets...); err != nil { - return fmt.Errorf("%w: %w", ErrOpenVPNEncryptionPresetNotValid, err) + return fmt.Errorf("PIA encryption preset is not valid: %w", err) } } diff --git a/internal/configuration/settings/pmtud.go b/internal/configuration/settings/pmtud.go index e447aa70..37366503 100644 --- a/internal/configuration/settings/pmtud.go +++ b/internal/configuration/settings/pmtud.go @@ -1,7 +1,6 @@ package settings import ( - "errors" "fmt" "net/netip" @@ -24,21 +23,16 @@ type PMTUD struct { TCPAddresses []netip.AddrPort `json:"tcp_addresses"` } -var ( - ErrPMTUDICMPAddressNotValid = errors.New("PMTUD ICMP address is not valid") - ErrPMTUDTCPAddressNotValid = errors.New("PMTUD TCP address is not valid") -) - // Validate validates PMTUD settings. func (p PMTUD) validate() (err error) { for i, addr := range p.ICMPAddresses { if !addr.IsValid() { - return fmt.Errorf("%w: at index %d", ErrPMTUDICMPAddressNotValid, i) + return fmt.Errorf("PMTUD ICMP address is not valid: at index %d", i) } } for i, addr := range p.TCPAddresses { if !addr.IsValid() { - return fmt.Errorf("%w: at index %d", ErrPMTUDTCPAddressNotValid, i) + return fmt.Errorf("PMTUD TCP address is not valid: at index %d", i) } } return nil diff --git a/internal/configuration/settings/portforward.go b/internal/configuration/settings/portforward.go index 9e7d9202..a08277e6 100644 --- a/internal/configuration/settings/portforward.go +++ b/internal/configuration/settings/portforward.go @@ -55,12 +55,6 @@ type PortForwarding struct { Password string `json:"password"` } -var ( - ErrPortsCountTooHigh = errors.New("ports count too high") - ErrListeningPortsLen = errors.New("listening ports length must be equal to ports count") - ErrListeningPortZero = errors.New("listening port cannot be 0") -) - func (p PortForwarding) Validate(vpnProvider string) (err error) { if !*p.Enabled { return nil @@ -78,7 +72,7 @@ func (p PortForwarding) Validate(vpnProvider string) (err error) { providers.Protonvpn, } if err = validate.IsOneOf(providerSelected, validProviders...); err != nil { - return fmt.Errorf("%w: %w", ErrPortForwardingEnabled, err) + return fmt.Errorf("port forwarding cannot be enabled: %w", err) } // Validate Filepath @@ -94,30 +88,31 @@ func (p PortForwarding) Validate(vpnProvider string) (err error) { const maxPortsCount = 1 switch { case p.PortsCount > maxPortsCount: - return fmt.Errorf("%w: %d > %d", ErrPortsCountTooHigh, p.PortsCount, maxPortsCount) + return fmt.Errorf("ports count too high: %d > %d", p.PortsCount, maxPortsCount) case p.Username == "": - return fmt.Errorf("%w", ErrPortForwardingUserEmpty) + return errors.New("port forwarding username is empty") case p.Password == "": - return fmt.Errorf("%w", ErrPortForwardingPasswordEmpty) + return errors.New("port forwarding password is empty") } case providers.Protonvpn: const maxPortsCount = 4 if p.PortsCount > maxPortsCount { - return fmt.Errorf("%w: %d > %d", ErrPortsCountTooHigh, p.PortsCount, maxPortsCount) + return fmt.Errorf("ports count too high: %d > %d", p.PortsCount, maxPortsCount) } default: const maxPortsCount = 1 if p.PortsCount > maxPortsCount { - return fmt.Errorf("%w: %d > %d", ErrPortsCountTooHigh, p.PortsCount, maxPortsCount) + return fmt.Errorf("ports count too high: %d > %d", p.PortsCount, maxPortsCount) } } if !slices.Equal(p.ListeningPorts, []uint16{0}) { switch { case len(p.ListeningPorts) != int(p.PortsCount): - return fmt.Errorf("%w: %d != %d", ErrListeningPortsLen, len(p.ListeningPorts), p.PortsCount) + return fmt.Errorf("listening ports length must be equal to ports count: "+ + "%d != %d", len(p.ListeningPorts), p.PortsCount) case slices.Contains(p.ListeningPorts, 0): - return fmt.Errorf("%w: in %v", ErrListeningPortZero, p.ListeningPorts) + return fmt.Errorf("listening port cannot be 0: in %v", p.ListeningPorts) } } diff --git a/internal/configuration/settings/provider.go b/internal/configuration/settings/provider.go index e4a18e77..ccc3acac 100644 --- a/internal/configuration/settings/provider.go +++ b/internal/configuration/settings/provider.go @@ -55,7 +55,7 @@ func (p *Provider) validate(vpnType string, filterChoicesGetter FilterChoicesGet } } if err = validate.IsOneOf(p.Name, validNames...); err != nil { - return fmt.Errorf("%w for %s: %w", ErrVPNProviderNameNotValid, vpnType, err) + return fmt.Errorf("VPN provider name is not valid for %s: %w", vpnType, err) } err = p.ServerSelection.validate(p.Name, filterChoicesGetter, warner) diff --git a/internal/configuration/settings/publicip_test.go b/internal/configuration/settings/publicip_test.go index 1195af5d..a3c278c2 100644 --- a/internal/configuration/settings/publicip_test.go +++ b/internal/configuration/settings/publicip_test.go @@ -15,7 +15,6 @@ func Test_PublicIP_read(t *testing.T) { makeReader func(ctrl *gomock.Controller) *reader.Reader makeWarner func(ctrl *gomock.Controller) Warner settings PublicIP - errWrapped error errMessage string }{ "nothing_read": { @@ -152,9 +151,10 @@ func Test_PublicIP_read(t *testing.T) { err := settings.read(reader, warner) assert.Equal(t, testCase.settings, settings) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/configuration/settings/server.go b/internal/configuration/settings/server.go index a99c8f8f..dfee5672 100644 --- a/internal/configuration/settings/server.go +++ b/internal/configuration/settings/server.go @@ -46,8 +46,7 @@ func (c ControlServer) validate() (err error) { uid := os.Getuid() const maxPrivilegedPort = 1023 if uid != 0 && port != 0 && port <= maxPrivilegedPort { - return fmt.Errorf("%w: %d when running with user ID %d", - ErrControlServerPrivilegedPort, port, uid) + return fmt.Errorf("cannot use privileged port without running as root: %d when running with user ID %d", port, uid) } jsonDecoder := json.NewDecoder(bytes.NewBufferString(c.AuthDefaultRole)) diff --git a/internal/configuration/settings/serverselection.go b/internal/configuration/settings/serverselection.go index 3781e818..c3e6a6d6 100644 --- a/internal/configuration/settings/serverselection.go +++ b/internal/configuration/settings/serverselection.go @@ -71,25 +71,13 @@ type ServerSelection struct { Wireguard WireguardSelection `json:"wireguard"` } -var ( - ErrOwnedOnlyNotSupported = errors.New("owned only filter is not supported") - ErrFreeOnlyNotSupported = errors.New("free only filter is not supported") - ErrPremiumOnlyNotSupported = errors.New("premium only filter is not supported") - ErrStreamOnlyNotSupported = errors.New("stream only filter is not supported") - ErrMultiHopOnlyNotSupported = errors.New("multi hop only filter is not supported") - ErrPortForwardOnlyNotSupported = errors.New("port forwarding only filter is not supported") - ErrFreePremiumBothSet = errors.New("free only and premium only filters are both set") - ErrSecureCoreOnlyNotSupported = errors.New("secure core only filter is not supported") - ErrTorOnlyNotSupported = errors.New("tor only filter is not supported") -) - func (ss *ServerSelection) validate(vpnServiceProvider string, filterChoicesGetter FilterChoicesGetter, warner Warner, ) (err error) { switch ss.VPN { case vpn.AmneziaWg, vpn.OpenVPN, vpn.Wireguard: default: - return fmt.Errorf("%w: %s", ErrVPNTypeNotValid, ss.VPN) + return fmt.Errorf("VPN type is not valid: %s", ss.VPN) } filterChoices, err := getLocationFilterChoices(vpnServiceProvider, ss, filterChoicesGetter, warner) @@ -150,7 +138,7 @@ func getLocationFilterChoices(vpnServiceProvider string, // Only return error comparing with newer regions, we don't want to confuse the user // with the retro regions in the error message. err = atLeastOneIsOneOfCaseInsensitive(ss.Regions, filterChoices.Regions, warner) - return models.FilterChoices{}, fmt.Errorf("%w: %w", ErrRegionNotValid, err) + return models.FilterChoices{}, fmt.Errorf("the region specified is not valid: %w", err) } } @@ -164,27 +152,27 @@ func validateServerFilters(settings ServerSelection, filterChoices models.Filter ) (err error) { err = atLeastOneIsOneOfCaseInsensitive(settings.Countries, filterChoices.Countries, warner) if err != nil { - return fmt.Errorf("%w: %w", ErrCountryNotValid, err) + return fmt.Errorf("the country specified is not valid: %w", err) } err = atLeastOneIsOneOfCaseInsensitive(settings.Regions, filterChoices.Regions, warner) if err != nil { - return fmt.Errorf("%w: %w", ErrRegionNotValid, err) + return fmt.Errorf("the region specified is not valid: %w", err) } err = atLeastOneIsOneOfCaseInsensitive(settings.Cities, filterChoices.Cities, warner) if err != nil { - return fmt.Errorf("%w: %w", ErrCityNotValid, err) + return fmt.Errorf("the city specified is not valid: %w", err) } err = atLeastOneIsOneOfCaseInsensitive(settings.ISPs, filterChoices.ISPs, warner) if err != nil { - return fmt.Errorf("%w: %w", ErrISPNotValid, err) + return fmt.Errorf("the ISP specified is not valid: %w", err) } err = atLeastOneIsOneOfCaseInsensitive(settings.Hostnames, filterChoices.Hostnames, warner) if err != nil { - return fmt.Errorf("%w: %w", ErrHostnameNotValid, err) + return fmt.Errorf("the hostname specified is not valid: %w", err) } if vpnServiceProvider == providers.Custom { @@ -196,19 +184,19 @@ func validateServerFilters(settings ServerSelection, filterChoices models.Filter // which requires a server name for TLS verification. filterChoices.Names = settings.Names default: - return fmt.Errorf("%w: %d names specified instead of "+ - "0 or 1 for the custom provider", - ErrNameNotValid, len(settings.Names)) + return fmt.Errorf("name is not valid: "+ + "%d names specified instead of 0 or 1 for the custom provider", + len(settings.Names)) } } err = atLeastOneIsOneOfCaseInsensitive(settings.Names, filterChoices.Names, warner) if err != nil { - return fmt.Errorf("%w: %w", ErrNameNotValid, err) + return fmt.Errorf("the server name specified is not valid: %w", err) } err = atLeastOneIsOneOfCaseInsensitive(settings.Categories, filterChoices.Categories, warner) if err != nil { - return fmt.Errorf("%w: %w", ErrCategoryNotValid, err) + return fmt.Errorf("the category specified is not valid: %w", err) } return nil @@ -255,12 +243,12 @@ func validateSubscriptionTierFilters(settings ServerSelection, vpnServiceProvide switch { case *settings.FreeOnly && !helpers.IsOneOf(vpnServiceProvider, providers.Protonvpn, providers.VPNUnlimited): - return fmt.Errorf("%w", ErrFreeOnlyNotSupported) + return errors.New("free only filter is not supported") case *settings.PremiumOnly && !helpers.IsOneOf(vpnServiceProvider, providers.VPNSecure): - return fmt.Errorf("%w", ErrPremiumOnlyNotSupported) + return errors.New("premium only filter is not supported") case *settings.FreeOnly && *settings.PremiumOnly: - return fmt.Errorf("%w", ErrFreePremiumBothSet) + return errors.New("free only and premium only filters are both set") default: return nil } @@ -269,21 +257,21 @@ func validateSubscriptionTierFilters(settings ServerSelection, vpnServiceProvide func validateFeatureFilters(settings ServerSelection, vpnServiceProvider string) error { switch { case *settings.OwnedOnly && vpnServiceProvider != providers.Mullvad: - return fmt.Errorf("%w", ErrOwnedOnlyNotSupported) + return errors.New("owned only filter is not supported") case vpnServiceProvider == providers.Protonvpn && *settings.FreeOnly && *settings.PortForwardOnly: - return fmt.Errorf("%w: together with free only filter", ErrPortForwardOnlyNotSupported) + return errors.New("port forwarding only filter is not supported: together with free only filter") case *settings.StreamOnly && !helpers.IsOneOf(vpnServiceProvider, providers.Protonvpn, providers.VPNUnlimited): - return fmt.Errorf("%w", ErrStreamOnlyNotSupported) + return errors.New("stream only filter is not supported") case *settings.MultiHopOnly && vpnServiceProvider != providers.Surfshark: - return fmt.Errorf("%w", ErrMultiHopOnlyNotSupported) + return errors.New("multi hop only filter is not supported") case *settings.PortForwardOnly && !helpers.IsOneOf(vpnServiceProvider, providers.PrivateInternetAccess, providers.Protonvpn): - return fmt.Errorf("%w", ErrPortForwardOnlyNotSupported) + return errors.New("port forwarding only filter is not supported") case *settings.SecureCoreOnly && vpnServiceProvider != providers.Protonvpn: - return fmt.Errorf("%w", ErrSecureCoreOnlyNotSupported) + return errors.New("secure core only filter is not supported") case *settings.TorOnly && vpnServiceProvider != providers.Protonvpn: - return fmt.Errorf("%w", ErrTorOnlyNotSupported) + return errors.New("tor only filter is not supported") default: return nil } diff --git a/internal/configuration/settings/updater.go b/internal/configuration/settings/updater.go index 8b0f55e3..c9f32250 100644 --- a/internal/configuration/settings/updater.go +++ b/internal/configuration/settings/updater.go @@ -1,6 +1,7 @@ package settings import ( + "errors" "fmt" "slices" "strings" @@ -37,20 +38,20 @@ type Updater struct { func (u Updater) Validate() (err error) { const minPeriod = time.Minute if *u.Period > 0 && *u.Period < minPeriod { - return fmt.Errorf("%w: %d must be larger than %s", - ErrUpdaterPeriodTooSmall, *u.Period, minPeriod) + return fmt.Errorf("VPN server data updater period is too small: "+ + "%d must be larger than %s", *u.Period, minPeriod) } if u.MinRatio <= 0 || u.MinRatio > 1 { - return fmt.Errorf("%w: %.2f must be between 0+ and 1", - ErrMinRatioNotValid, u.MinRatio) + return fmt.Errorf("minimum ratio is not valid: "+ + "%.2f must be between 0+ and 1", u.MinRatio) } validProviders := providers.All() for _, provider := range u.Providers { err = validate.IsOneOf(provider, validProviders...) if err != nil { - return fmt.Errorf("%w: %w", ErrVPNProviderNameNotValid, err) + return fmt.Errorf("VPN provider name is not valid: %w", err) } if provider == providers.Protonvpn { @@ -58,9 +59,9 @@ func (u Updater) Validate() (err error) { if authenticatedAPI { switch { case *u.ProtonEmail == "": - return fmt.Errorf("%w", ErrUpdaterProtonEmailMissing) + return errors.New("proton email is missing") case *u.ProtonPassword == "": - return fmt.Errorf("%w", ErrUpdaterProtonPasswordMissing) + return errors.New("proton password is missing") } } } diff --git a/internal/configuration/settings/vpn.go b/internal/configuration/settings/vpn.go index f0c894b6..26fcdf20 100644 --- a/internal/configuration/settings/vpn.go +++ b/internal/configuration/settings/vpn.go @@ -37,7 +37,7 @@ func (v *VPN) Validate(filterChoicesGetter FilterChoicesGetter, ipv6Supported bo // Validate Type validVPNTypes := []string{vpn.AmneziaWg, vpn.OpenVPN, vpn.Wireguard} if err = validate.IsOneOf(v.Type, validVPNTypes...); err != nil { - return fmt.Errorf("%w: %w", ErrVPNTypeNotValid, err) + return fmt.Errorf("VPN type is not valid: %w", err) } err = v.Provider.validate(v.Type, filterChoicesGetter, warner) diff --git a/internal/configuration/settings/wireguard.go b/internal/configuration/settings/wireguard.go index f3ecf3cb..8bd7326d 100644 --- a/internal/configuration/settings/wireguard.go +++ b/internal/configuration/settings/wireguard.go @@ -1,6 +1,7 @@ package settings import ( + "errors" "fmt" "net/netip" "regexp" @@ -54,7 +55,7 @@ var regexpInterfaceName = regexp.MustCompile(`^[a-zA-Z0-9_]+$`) func (w Wireguard) validate(vpnProvider string, ipv6Supported, amneziawg bool) (err error) { // Validate PrivateKey if *w.PrivateKey == "" { - return fmt.Errorf("%w", ErrWireguardPrivateKeyNotSet) + return errors.New("private key is not set") } _, err = wgtypes.ParseKey(*w.PrivateKey) if err != nil { @@ -68,7 +69,7 @@ func (w Wireguard) validate(vpnProvider string, ipv6Supported, amneziawg bool) ( if vpnProvider == providers.Airvpn { if *w.PreSharedKey == "" { - return fmt.Errorf("%w", ErrWireguardPreSharedKeyNotSet) + return errors.New("pre-shared key is not set") } } @@ -82,17 +83,15 @@ func (w Wireguard) validate(vpnProvider string, ipv6Supported, amneziawg bool) ( // Validate Addresses if len(w.Addresses) == 0 { - return fmt.Errorf("%w", ErrWireguardInterfaceAddressNotSet) + return errors.New("interface address is not set") } for i, ipNet := range w.Addresses { if !ipNet.IsValid() { - return fmt.Errorf("%w: for address at index %d", - ErrWireguardInterfaceAddressNotSet, i) + return fmt.Errorf("interface address is not set: for address at index %d", i) } if !ipv6Supported && ipNet.Addr().Is6() { - return fmt.Errorf("%w: address %s", - ErrWireguardInterfaceAddressIPv6, ipNet.String()) + return fmt.Errorf("interface address is IPv6 but IPv6 is not supported: address %s", ipNet.String()) } } @@ -100,30 +99,27 @@ func (w Wireguard) validate(vpnProvider string, ipv6Supported, amneziawg bool) ( // WARNING: do not check for IPv6 networks in the allowed IPs, // the wireguard code will take care to ignore it. if len(w.AllowedIPs) == 0 { - return fmt.Errorf("%w", ErrWireguardAllowedIPsNotSet) + return errors.New("allowed IPs is not set") } for i, allowedIP := range w.AllowedIPs { if !allowedIP.IsValid() { - return fmt.Errorf("%w: for allowed ip %d of %d", - ErrWireguardAllowedIPNotSet, i+1, len(w.AllowedIPs)) + return fmt.Errorf("allowed IP is not set: for allowed ip %d of %d", i+1, len(w.AllowedIPs)) } } if *w.PersistentKeepaliveInterval < 0 { - return fmt.Errorf("%w: %s", ErrWireguardKeepAliveNegative, - *w.PersistentKeepaliveInterval) + return fmt.Errorf("persistent keep alive interval is negative: %s", *w.PersistentKeepaliveInterval) } // Validate interface if !regexpInterfaceName.MatchString(w.Interface) { - return fmt.Errorf("%w: '%s' does not match regex '%s'", - ErrWireguardInterfaceNotValid, w.Interface, regexpInterfaceName) + return fmt.Errorf("interface name is not valid: '%s' does not match regex '%s'", w.Interface, regexpInterfaceName) } if !amneziawg { // amneziawg should have its own Implementation field and ignore this one validImplementations := []string{"auto", "userspace", "kernelspace"} if err := validate.IsOneOf(w.Implementation, validImplementations...); err != nil { - return fmt.Errorf("%w: %w", ErrWireguardImplementationNotValid, err) + return fmt.Errorf("implementation is not valid: %w", err) } } diff --git a/internal/configuration/settings/wireguardselection.go b/internal/configuration/settings/wireguardselection.go index 44bd28d1..301893cf 100644 --- a/internal/configuration/settings/wireguardselection.go +++ b/internal/configuration/settings/wireguardselection.go @@ -1,6 +1,7 @@ package settings import ( + "errors" "fmt" "net/netip" @@ -44,7 +45,7 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) { // endpoint IP addresses are baked in case providers.Custom: if !w.EndpointIP.IsValid() || w.EndpointIP.IsUnspecified() { - return fmt.Errorf("%w", ErrWireguardEndpointIPNotSet) + return errors.New("endpoint IP is not set") } default: // Providers not supporting Wireguard } @@ -54,13 +55,13 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) { // EndpointPort is required case providers.Custom: if *w.EndpointPort == 0 { - return fmt.Errorf("%w", ErrWireguardEndpointPortNotSet) + return errors.New("endpoint port is not set") } // EndpointPort cannot be set case providers.Fastestvpn, providers.Nordvpn, providers.Protonvpn, providers.Surfshark: if *w.EndpointPort != 0 { - return fmt.Errorf("%w", ErrWireguardEndpointPortSet) + return errors.New("endpoint port is set") } case providers.Airvpn, providers.Ivpn, providers.Mullvad, providers.Windscribe: // EndpointPort is optional and can be 0 @@ -84,8 +85,7 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) { if err == nil { break } - return fmt.Errorf("%w: for VPN service provider %s: %w", - ErrWireguardEndpointPortNotAllowed, vpnProvider, err) + return fmt.Errorf("endpoint port is not allowed: for VPN service provider %s: %w", vpnProvider, err) default: // Providers not supporting Wireguard } @@ -96,15 +96,14 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) { // public keys are baked in case providers.Custom: if w.PublicKey == "" { - return fmt.Errorf("%w", ErrWireguardPublicKeyNotSet) + return errors.New("public key is not set") } default: // Providers not supporting Wireguard } if w.PublicKey != "" { _, err := wgtypes.ParseKey(w.PublicKey) if err != nil { - return fmt.Errorf("%w: %s: %s", - ErrWireguardPublicKeyNotValid, w.PublicKey, err) + return fmt.Errorf("public key is not valid: %s: %s", w.PublicKey, err) } } diff --git a/internal/configuration/sources/files/wireguard.go b/internal/configuration/sources/files/wireguard.go index 3c3f1519..7bc7905d 100644 --- a/internal/configuration/sources/files/wireguard.go +++ b/internal/configuration/sources/files/wireguard.go @@ -74,8 +74,6 @@ func parseWireguardInterfaceSection(interfaceSection *ini.Section) ( return privateKey, addresses } -var ErrEndpointHostNotIP = errors.New("endpoint host is not an IP") - func parseWireguardPeerSection(peerSection *ini.Section) ( preSharedKey, publicKey, endpointIP, endpointPort *string, ) { diff --git a/internal/dns/leak.go b/internal/dns/leak.go index 92b157dd..8b9dbb02 100644 --- a/internal/dns/leak.go +++ b/internal/dns/leak.go @@ -3,7 +3,6 @@ package dns import ( "context" "encoding/json" - "errors" "fmt" "math" "math/rand/v2" @@ -63,8 +62,6 @@ func generateRandomString(length uint) string { return string(b) } -var errIPLeakSessionMismatch = errors.New("ipleak.net session mismatch") - func triggerDNSQuery(ctx context.Context, client *http.Client, session string) ( dnsToCount map[string]uint, err error, ) { @@ -93,7 +90,7 @@ func triggerDNSQuery(ctx context.Context, client *http.Client, session string) ( if err != nil { return nil, fmt.Errorf("decoding response: %w", err) } else if data.Session != session { - return nil, fmt.Errorf("%w: expected %s, got %s", errIPLeakSessionMismatch, session, data.Session) + return nil, fmt.Errorf("ipleak.net session mismatch: expected %s, got %s", session, data.Session) } return data.IP, nil diff --git a/internal/firewall/iptables/delete_test.go b/internal/firewall/iptables/delete_test.go index de7176f6..a68de634 100644 --- a/internal/firewall/iptables/delete_test.go +++ b/internal/firewall/iptables/delete_test.go @@ -57,18 +57,15 @@ func Test_deleteIPTablesRule(t *testing.T) { t.Parallel() const iptablesBinary = "/sbin/iptables" - errTest := errors.New("test error") testCases := map[string]struct { instruction string makeRunner func(ctrl *gomock.Controller) *MockCmdRunner makeLogger func(ctrl *gomock.Controller) *MockLogger - errWrapped error errMessage string }{ "invalid_instruction": { instruction: "invalid", - errWrapped: ErrIptablesCommandMalformed, errMessage: "parsing iptables command: parsing \"invalid\": " + "iptables command is malformed: flag \"invalid\" requires a value, but got none", }, @@ -78,7 +75,7 @@ func Test_deleteIPTablesRule(t *testing.T) { runner := NewMockCmdRunner(ctrl) runner.EXPECT(). Run(newCmdMatcherListRules(iptablesBinary, "nat", "PREROUTING")). - Return("", errTest) + Return("", errors.New("test error")) return runner }, makeLogger: func(ctrl *gomock.Controller) *MockLogger { @@ -86,7 +83,6 @@ func Test_deleteIPTablesRule(t *testing.T) { logger.EXPECT().Debug("/sbin/iptables -t nat -L PREROUTING --line-numbers -n -v") return logger }, - errWrapped: errTest, errMessage: `finding iptables chain rule line number: command failed: ` + `"/sbin/iptables -t nat -L PREROUTING --line-numbers -n -v": test error`, }, @@ -120,7 +116,7 @@ func Test_deleteIPTablesRule(t *testing.T) { "2 0 0 REDIRECT 6 -- tun0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:43716 redir ports 5678\n", //nolint:lll nil) runner.EXPECT().Run(newCmdMatcher(iptablesBinary, "^-t$", "^nat$", - "^-D$", "^PREROUTING$", "^2$")).Return("details", errTest) + "^-D$", "^PREROUTING$", "^2$")).Return("details", errors.New("test error")) return runner }, makeLogger: func(ctrl *gomock.Controller) *MockLogger { @@ -131,7 +127,6 @@ func Test_deleteIPTablesRule(t *testing.T) { logger.EXPECT().Debug("/sbin/iptables -t nat -D PREROUTING 2") return logger }, - errWrapped: errTest, errMessage: "command failed: \"/sbin/iptables -t nat -D PREROUTING 2\": test error: details", }, "rule_found_delete_success": { @@ -177,9 +172,10 @@ func Test_deleteIPTablesRule(t *testing.T) { err := deleteIPTablesRule(ctx, iptablesBinary, instruction, runner, logger) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/firewall/iptables/ip6tables.go b/internal/firewall/iptables/ip6tables.go index 33b1865a..0e4e69a4 100644 --- a/internal/firewall/iptables/ip6tables.go +++ b/internal/firewall/iptables/ip6tables.go @@ -82,13 +82,11 @@ func (c *Config) runIP6tablesInstructionNoSave(ctx context.Context, instruction return nil } -var ErrPolicyNotValid = errors.New("policy is not valid") - func (c *Config) SetIPv6AllPolicies(ctx context.Context, policy string) error { switch policy { case "ACCEPT", "DROP": default: - return fmt.Errorf("%w: %s", ErrPolicyNotValid, policy) + return fmt.Errorf("policy is not valid: %s", policy) } return c.runIP6tablesInstructions(ctx, []string{ "--policy INPUT " + policy, diff --git a/internal/firewall/iptables/iptables.go b/internal/firewall/iptables/iptables.go index ee372c68..7690d8e8 100644 --- a/internal/firewall/iptables/iptables.go +++ b/internal/firewall/iptables/iptables.go @@ -2,7 +2,6 @@ package iptables import ( "context" - "errors" "fmt" "io" "net/netip" @@ -13,10 +12,8 @@ import ( "github.com/qdm12/gluetun/internal/models" ) -var ( - ErrIPTablesVersionTooShort = errors.New("iptables version string is too short") - ErrPolicyUnknown = errors.New("unknown policy") - ErrNeedIP6Tables = errors.New("ip6tables is required, please upgrade your kernel to support it") +const ( + needIP6Tables = "ip6tables is required, please upgrade your kernel" ) func appendOrDelete(remove bool) string { @@ -36,7 +33,7 @@ func (c *Config) Version(ctx context.Context) (string, error) { words := strings.Fields(output) const minWords = 2 if len(words) < minWords { - return "", fmt.Errorf("%w: %s", ErrIPTablesVersionTooShort, output) + return "", fmt.Errorf("iptables version string is too short: %s", output) } return "iptables " + words[1], nil } @@ -102,7 +99,7 @@ func (c *Config) SetIPv4AllPolicies(ctx context.Context, policy string) error { switch policy { case "ACCEPT", "DROP": default: - return fmt.Errorf("%w: %s", ErrPolicyUnknown, policy) + return fmt.Errorf("unknown policy: %s", policy) } return c.runIptablesInstructions(ctx, []string{ "--policy INPUT " + policy, @@ -129,7 +126,7 @@ func (c *Config) AcceptInputToSubnet(ctx context.Context, intf string, destinati return c.runIptablesInstruction(ctx, instruction) } if c.ip6Tables == "" { - return fmt.Errorf("accept input to subnet %s: %w", destination, ErrNeedIP6Tables) + return fmt.Errorf("accept input to subnet %s: %s", destination, needIP6Tables) } return c.runIP6tablesInstruction(ctx, instruction) } @@ -157,7 +154,7 @@ func (c *Config) AcceptOutputTrafficToVPN(ctx context.Context, if connection.IP.Is4() { return c.runIptablesInstruction(ctx, instruction) } else if c.ip6Tables == "" { - return fmt.Errorf("accept output to VPN server: %w", ErrNeedIP6Tables) + return fmt.Errorf("accept output to VPN server %s: %s", connection.IP, needIP6Tables) } return c.runIP6tablesInstruction(ctx, instruction) } @@ -175,7 +172,7 @@ func (c *Config) AcceptOutput(ctx context.Context, if ip.Is4() { return c.runIptablesInstruction(ctx, instruction) } else if c.ip6Tables == "" { - return fmt.Errorf("accept output to VPN server: %w", ErrNeedIP6Tables) + return fmt.Errorf("accept output to VPN server %s: %s", ip, needIP6Tables) } return c.runIP6tablesInstruction(ctx, instruction) } @@ -200,7 +197,7 @@ func (c *Config) AcceptOutputFromIPToSubnet(ctx context.Context, if doIPv4 { return c.runIptablesInstruction(ctx, instruction) } else if c.ip6Tables == "" { - return fmt.Errorf("accept output from %s to %s: %w", sourceIP, destinationSubnet, ErrNeedIP6Tables) + return fmt.Errorf("accept output from %s to %s: %s", sourceIP, destinationSubnet, needIP6Tables) } return c.runIP6tablesInstruction(ctx, instruction) } @@ -350,7 +347,7 @@ func (c *Config) RunUserPostRules(ctx context.Context, filepath string) error { case ipv4: err = c.runIptablesInstructionNoSave(ctx, rule) case c.ip6Tables == "": - err = fmt.Errorf("running user ip6tables rule: %w", ErrNeedIP6Tables) + err = fmt.Errorf("running user ip6tables rule: %s", needIP6Tables) default: // ipv6 err = c.runIP6tablesInstructionNoSave(ctx, rule) } diff --git a/internal/firewall/iptables/list.go b/internal/firewall/iptables/list.go index 49f855fe..1ef89b49 100644 --- a/internal/firewall/iptables/list.go +++ b/internal/firewall/iptables/list.go @@ -40,8 +40,6 @@ type mark struct { value uint } -var ErrChainListMalformed = errors.New("iptables chain list output is malformed") - func parseChain(iptablesOutput string) (c chain, err error) { // Text example: // Chain INPUT (policy ACCEPT 140K packets, 226M bytes) @@ -63,8 +61,8 @@ func parseChain(iptablesOutput string) (c chain, err error) { const minLines = 2 // chain general information line + legend line if len(lines) < minLines { - return chain{}, fmt.Errorf("%w: not enough lines to process in: %s", - ErrChainListMalformed, iptablesOutput) + return chain{}, fmt.Errorf("iptables chain list output is malformed: not enough lines to process in: %s", + iptablesOutput) } c, err = parseChainGeneralDataLine(lines[0]) @@ -77,8 +75,8 @@ func parseChain(iptablesOutput string) (c chain, err error) { legendLine := strings.TrimSpace(lines[1]) legendFields := strings.Fields(legendLine) if !slices.Equal(expectedLegendFields, legendFields) { - return chain{}, fmt.Errorf("%w: legend %q is not the expected %q", - ErrChainListMalformed, legendLine, strings.Join(expectedLegendFields, " ")) + return chain{}, fmt.Errorf("iptables chain list output is malformed: legend %q is not the expected %q", + legendLine, strings.Join(expectedLegendFields, " ")) } lines = lines[2:] // remove chain general information line and legend line @@ -111,8 +109,8 @@ func parseChainGeneralDataLine(line string) (base chain, err error) { fields := strings.Fields(line) const expectedNumberOfFields = 8 if len(fields) != expectedNumberOfFields { - return chain{}, fmt.Errorf("%w: expected %d fields in %q", - ErrChainListMalformed, expectedNumberOfFields, line) + return chain{}, fmt.Errorf("iptables chain list output is malformed: expected %d fields in %q", + expectedNumberOfFields, line) } // Sanity checks @@ -126,8 +124,8 @@ func parseChainGeneralDataLine(line string) (base chain, err error) { if fields[index] == expectedValue { continue } - return chain{}, fmt.Errorf("%w: expected %q for field %d in %q", - ErrChainListMalformed, expectedValue, index, line) + return chain{}, fmt.Errorf("iptables chain list output is malformed: expected %q for field %d in %q", + expectedValue, index, line) } base.name = fields[1] // chain name could be custom @@ -152,19 +150,17 @@ func parseChainGeneralDataLine(line string) (base chain, err error) { return base, nil } -var ErrChainRuleMalformed = errors.New("chain rule is malformed") - func parseChainRuleLine(line string) (rule chainRule, err error) { line = strings.TrimSpace(line) if line == "" { - return chainRule{}, fmt.Errorf("%w: empty line", ErrChainRuleMalformed) + return chainRule{}, errors.New("chain rule is malformed: empty line") } fields := strings.Fields(line) const minFields = 10 if len(fields) < minFields { - return chainRule{}, fmt.Errorf("%w: not enough fields", ErrChainRuleMalformed) + return chainRule{}, errors.New("chain rule is malformed: not enough fields") } for fieldIndex, field := range fields[:minFields] { @@ -186,7 +182,7 @@ func parseChainRuleLine(line string) (rule chainRule, err error) { func parseChainRuleField(fieldIndex int, field string, rule *chainRule) (err error) { if field == "" { - return fmt.Errorf("%w: empty field at index %d", ErrChainRuleMalformed, fieldIndex) + return fmt.Errorf("chain rule is malformed: empty field at index %d", fieldIndex) } const ( @@ -278,8 +274,8 @@ func parseChainRuleOptionalFields(optionalFields []string, rule *chainRule) (err rule.redirPorts = ports i++ default: - return fmt.Errorf("%w: unexpected %q after redir", - ErrChainRuleMalformed, optionalFields[1]) + return fmt.Errorf("chain rule is malformed: unexpected %q after redir", + optionalFields[1]) } case "ctstate": i++ @@ -294,15 +290,13 @@ func parseChainRuleOptionalFields(optionalFields []string, rule *chainRule) (err rule.mark = mark i += consumed default: - return fmt.Errorf("%w: unexpected optional field: %s", - ErrChainRuleMalformed, optionalFields[i]) + return fmt.Errorf("chain rule is malformed: unexpected optional field: %s", + optionalFields[i]) } } return nil } -var errUDPOptionalUnknown = errors.New("unknown UDP optional field") - func parseUDPOptional(optionalFields []string, rule *chainRule) (consumed int, err error) { for _, value := range optionalFields { if !strings.ContainsRune(value, ':') { @@ -323,14 +317,12 @@ func parseUDPOptional(optionalFields []string, rule *chainRule) (consumed int, e } consumed++ default: - return 0, fmt.Errorf("%w: %s", errUDPOptionalUnknown, value) + return 0, fmt.Errorf("unknown UDP optional field: %s", value) } } return consumed, nil } -var errTCPOptionalUnknown = errors.New("unknown TCP optional field") - func parseTCPOptional(optionalFields []string, rule *chainRule) (consumed int, err error) { for _, value := range optionalFields { if !strings.ContainsRune(value, ':') { @@ -357,7 +349,7 @@ func parseTCPOptional(optionalFields []string, rule *chainRule) (consumed int, e } consumed++ default: - return 0, fmt.Errorf("%w: %s", errTCPOptionalUnknown, value) + return 0, fmt.Errorf("unknown TCP optional field: %s", value) } } return consumed, nil @@ -373,15 +365,13 @@ func parseSourcePort(value string) (port uint16, err error) { return parsePort(value) } -var errTCPFlagsMalformed = errors.New("TCP flags are malformed") - func parseTCPFlags(value string) (tcpFlags, error) { value = strings.TrimPrefix(value, "flags:") fields := strings.Split(value, "/") const expectedFields = 2 if len(fields) != expectedFields { - return tcpFlags{}, fmt.Errorf("%w: expected format 'flags:/' in %q", - errTCPFlagsMalformed, value) + return tcpFlags{}, fmt.Errorf("TCP flags are malformed: expected format 'flags:/' in %q", + value) } maskFlags := strings.Split(fields[0], ",") mask := make([]tcpFlag, len(maskFlags)) @@ -422,8 +412,6 @@ func parsePortsCSV(s string) (ports []uint16, err error) { return ports, nil } -var errMarkValueMalformed = errors.New("mark value is malformed") - func parseMark(optionalFields []string) (m mark, consumed int, err error) { switch optionalFields[consumed] { case "match": @@ -437,42 +425,36 @@ func parseMark(optionalFields []string) (m mark, consumed int, err error) { const bits = 32 value, err := strconv.ParseUint(optionalFields[consumed], base, bits) if err != nil { - return mark{}, 0, fmt.Errorf("%w: %s", errMarkValueMalformed, optionalFields[consumed]) + return mark{}, 0, fmt.Errorf("mark value is malformed: %s", optionalFields[consumed]) } m.value = uint(value) consumed++ default: - return mark{}, 0, fmt.Errorf("%w: unexpected mark mode field: %s", - ErrChainRuleMalformed, optionalFields[consumed]) + return mark{}, 0, fmt.Errorf("chain rule is malformed: unexpected mark mode field: %s", + optionalFields[consumed]) } return m, consumed, nil } -var ErrLineNumberIsZero = errors.New("line number is zero") - func parseLineNumber(s string) (n uint16, err error) { const base, bitLength = 10, 16 lineNumber, err := strconv.ParseUint(s, base, bitLength) if err != nil { return 0, err } else if lineNumber == 0 { - return 0, fmt.Errorf("%w", ErrLineNumberIsZero) + return 0, errors.New("line number is zero") } return uint16(lineNumber), nil } -var ErrTargetUnknown = errors.New("unknown target") - func checkTarget(target string) (err error) { switch target { case "ACCEPT", "DROP", "REJECT", "REDIRECT": return nil } - return fmt.Errorf("%w: %s", ErrTargetUnknown, target) + return fmt.Errorf("unknown target: %s", target) } -var ErrProtocolUnknown = errors.New("unknown protocol") - func parseProtocol(s string) (protocol string, err error) { switch s { case "0", "all": @@ -483,18 +465,16 @@ func parseProtocol(s string) (protocol string, err error) { case "17", "udp": protocol = "udp" default: - return "", fmt.Errorf("%w: %s", ErrProtocolUnknown, s) + return "", fmt.Errorf("unknown protocol: %s", s) } return protocol, nil } -var ErrMetricSizeMalformed = errors.New("metric size is malformed") - // parseMetricSize parses a metric size string like 140K or 226M and // returns the raw integer matching it. func parseMetricSize(size string) (n uint64, err error) { if size == "" { - return n, fmt.Errorf("%w: empty string", ErrMetricSizeMalformed) + return n, errors.New("metric size is malformed: empty string") } //nolint:mnd @@ -516,7 +496,7 @@ func parseMetricSize(size string) (n uint64, err error) { const base, bitLength = 10, 64 n, err = strconv.ParseUint(size, base, bitLength) if err != nil { - return n, fmt.Errorf("%w: %w", ErrMetricSizeMalformed, err) + return n, fmt.Errorf("metric size is malformed: %w", err) } n *= multiplier return n, nil diff --git a/internal/firewall/iptables/list_test.go b/internal/firewall/iptables/list_test.go index 38828784..d4f93bb8 100644 --- a/internal/firewall/iptables/list_test.go +++ b/internal/firewall/iptables/list_test.go @@ -13,30 +13,25 @@ func Test_parseChain(t *testing.T) { testCases := map[string]struct { iptablesOutput string table chain - errWrapped error errMessage string }{ "no_output": { - errWrapped: ErrChainListMalformed, errMessage: "iptables chain list output is malformed: not enough lines to process in: ", }, "single_line_only": { iptablesOutput: `Chain INPUT (policy ACCEPT 140K packets, 226M bytes)`, - errWrapped: ErrChainListMalformed, errMessage: "iptables chain list output is malformed: not enough lines to process in: " + "Chain INPUT (policy ACCEPT 140K packets, 226M bytes)", }, "malformed_general_data_line": { iptablesOutput: `Chain INPUT num pkts bytes target prot opt in out source destination`, - errWrapped: ErrChainListMalformed, errMessage: "parsing chain general data line: iptables chain list output is malformed: " + "expected 8 fields in \"Chain INPUT\"", }, "malformed_legend": { iptablesOutput: `Chain INPUT (policy ACCEPT 140K packets, 226M bytes) num pkts bytes target prot opt in out source`, - errWrapped: ErrChainListMalformed, errMessage: "iptables chain list output is malformed: legend " + "\"num pkts bytes target prot opt in out source\" " + "is not the expected \"num pkts bytes target prot opt in out source destination\"", @@ -135,9 +130,10 @@ num pkts bytes target prot opt in out source destinati table, err := parseChain(testCase.iptablesOutput) assert.Equal(t, testCase.table, table) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/firewall/iptables/parse.go b/internal/firewall/iptables/parse.go index a18d7714..929af05c 100644 --- a/internal/firewall/iptables/parse.go +++ b/internal/firewall/iptables/parse.go @@ -80,11 +80,9 @@ func ipPrefixesEqual(instruction, chainRule netip.Prefix) bool { (!instruction.IsValid() && chainRule.Bits() == 0 && chainRule.Addr().IsUnspecified()) } -var ErrIptablesCommandMalformed = errors.New("iptables command is malformed") - func parseIptablesInstruction(s string) (instruction iptablesInstruction, err error) { if s == "" { - return iptablesInstruction{}, fmt.Errorf("%w: empty instruction", ErrIptablesCommandMalformed) + return iptablesInstruction{}, errors.New("iptables command is malformed: empty instruction") } fields := strings.Fields(s) @@ -173,7 +171,7 @@ func parseInstructionFlag(fields []string, instruction *iptablesInstruction) (co return 0, fmt.Errorf("parsing TCP flags: %w", err) } default: - return 0, fmt.Errorf("%w: unknown key %q", ErrIptablesCommandMalformed, flag) + return 0, fmt.Errorf("iptables command is malformed: unknown key %q", flag) } return consumed, nil } @@ -185,15 +183,15 @@ func preCheckInstructionFields(fields []string) (consumed int, err error) { case "--tcp-flags": // -m can have 1 or 2 values const expected = 3 if len(fields) < expected { - return 0, fmt.Errorf("%w: flag %q requires at least 2 values, but got %s", - ErrIptablesCommandMalformed, flag, strings.Join(fields, " ")) + return 0, fmt.Errorf("iptables command is malformed: flag %q requires at least 2 values, but got %s", + flag, strings.Join(fields, " ")) } return expected, nil default: const expected = 2 if len(fields) < expected { - return 0, fmt.Errorf("%w: flag %q requires a value, but got none", - ErrIptablesCommandMalformed, flag) + return 0, fmt.Errorf("iptables command is malformed: flag %q requires a value, but got none", + flag) } return expected, nil } @@ -239,12 +237,12 @@ func parseMatchModule(fields []string, instruction *iptablesInstruction) ( consumed++ instruction.mark.invert = true default: - return consumed, fmt.Errorf("%w: unsupported match mark with value: %s", - ErrIptablesCommandMalformed, fields[2]) + return consumed, fmt.Errorf("iptables command is malformed: unsupported match mark with value: %s", + fields[2]) } default: - return 0, fmt.Errorf("%w: unknown match value: %s", - ErrIptablesCommandMalformed, fields[consumed]) + return 0, fmt.Errorf("iptables command is malformed: unknown match value: %s", + fields[consumed]) } return consumed, nil } diff --git a/internal/firewall/iptables/parse_test.go b/internal/firewall/iptables/parse_test.go index 51c5ab7c..0928c2f4 100644 --- a/internal/firewall/iptables/parse_test.go +++ b/internal/firewall/iptables/parse_test.go @@ -13,21 +13,17 @@ func Test_parseIptablesInstruction(t *testing.T) { testCases := map[string]struct { s string instruction iptablesInstruction - errWrapped error errMessage string }{ "no_instruction": { - errWrapped: ErrIptablesCommandMalformed, errMessage: "iptables command is malformed: empty instruction", }, "uneven_fields": { s: "-A", - errWrapped: ErrIptablesCommandMalformed, errMessage: "parsing \"-A\": iptables command is malformed: flag \"-A\" requires a value, but got none", }, "unknown_key": { s: "-x something", - errWrapped: ErrIptablesCommandMalformed, errMessage: "parsing \"-x something\": iptables command is malformed: unknown key \"-x\"", }, "one_pair": { @@ -74,9 +70,10 @@ func Test_parseIptablesInstruction(t *testing.T) { rule, err := parseIptablesInstruction(testCase.s) assert.Equal(t, testCase.instruction, rule) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/firewall/iptables/support.go b/internal/firewall/iptables/support.go index 33245228..5896d743 100644 --- a/internal/firewall/iptables/support.go +++ b/internal/firewall/iptables/support.go @@ -10,12 +10,7 @@ import ( "strings" ) -var ( - ErrNetAdminMissing = errors.New("NET_ADMIN capability is missing") - ErrTestRuleCleanup = errors.New("failed cleaning up test rule") - ErrInputPolicyNotFound = errors.New("input policy not found") - ErrNotSupported = errors.New("no iptables supported found") -) +var ErrNotSupported = errors.New("no iptables supported found") func checkIptablesSupport(ctx context.Context, runner CmdRunner, iptablesPathsToTry ...string, @@ -53,7 +48,7 @@ func checkIptablesSupport(ctx context.Context, runner CmdRunner, if allArePermissionDenied { // If the error is related to a denied permission for all iptables path, // return an error describing what to do from an end-user perspective. - return "", fmt.Errorf("%w: %s", ErrNetAdminMissing, strings.Join(allUnsupportedMessages, "; ")) + return "", fmt.Errorf("NET_ADMIN capability is missing: %s", strings.Join(allUnsupportedMessages, "; ")) } return "", fmt.Errorf("%w: errors encountered are: %s", @@ -85,7 +80,7 @@ func testIptablesPath(ctx context.Context, path string, output, err = runner.Run(cmd) if err != nil { // this is a critical error, we want to make sure our test rule gets removed. - criticalErr = fmt.Errorf("%w: %s (%s)", ErrTestRuleCleanup, output, err) + criticalErr = fmt.Errorf("failed cleaning up test rule: %s (%s)", output, err) return false, "", criticalErr } @@ -108,7 +103,7 @@ func testIptablesPath(ctx context.Context, path string, } if inputPolicy == "" { - criticalErr = fmt.Errorf("%w: in INPUT rules: %s", ErrInputPolicyNotFound, output) + criticalErr = fmt.Errorf("input policy not found: in INPUT rules: %s", output) return false, "", criticalErr } diff --git a/internal/firewall/iptables/support_test.go b/internal/firewall/iptables/support_test.go index 9df4ed21..6740d55d 100644 --- a/internal/firewall/iptables/support_test.go +++ b/internal/firewall/iptables/support_test.go @@ -7,7 +7,6 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func newAppendTestRuleMatcher(path string) *cmdMatcher { @@ -43,7 +42,6 @@ func Test_checkIptablesSupport(t *testing.T) { buildRunner func(ctrl *gomock.Controller) CmdRunner iptablesPathsToTry []string iptablesPath string - errSentinel error errMessage string }{ "critical error when checking": { @@ -56,7 +54,6 @@ func Test_checkIptablesSupport(t *testing.T) { return runner }, iptablesPathsToTry: []string{"path1", "path2"}, - errSentinel: ErrTestRuleCleanup, errMessage: "for path1: failed cleaning up test rule: " + "output (exit code 4)", }, @@ -86,7 +83,6 @@ func Test_checkIptablesSupport(t *testing.T) { return runner }, iptablesPathsToTry: []string{"path1", "path2"}, - errSentinel: ErrNetAdminMissing, errMessage: "NET_ADMIN capability is missing: " + "path1: Permission denied (you must be root) more context (exit code 4); " + "path2: context: Permission denied (you must be root) (exit code 4)", @@ -101,7 +97,6 @@ func Test_checkIptablesSupport(t *testing.T) { return runner }, iptablesPathsToTry: []string{"path1", "path2"}, - errSentinel: ErrNotSupported, errMessage: "no iptables supported found: " + "errors encountered are: " + "path1: output 1 (exit code 4); " + @@ -118,9 +113,10 @@ func Test_checkIptablesSupport(t *testing.T) { iptablesPath, err := checkIptablesSupport(ctx, runner, testCase.iptablesPathsToTry...) - require.ErrorIs(t, err, testCase.errSentinel) - if testCase.errSentinel != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } assert.Equal(t, testCase.iptablesPath, iptablesPath) }) @@ -139,7 +135,6 @@ func Test_testIptablesPath(t *testing.T) { buildRunner func(ctrl *gomock.Controller) CmdRunner ok bool unsupportedMessage string - criticalErrWrapped error criticalErrMessage string }{ "append test rule permission denied": { @@ -168,7 +163,6 @@ func Test_testIptablesPath(t *testing.T) { Return("some output", errDummy) return runner }, - criticalErrWrapped: ErrTestRuleCleanup, criticalErrMessage: "failed cleaning up test rule: some output (exit code 4)", }, "list input rules permission denied": { @@ -202,7 +196,6 @@ func Test_testIptablesPath(t *testing.T) { Return("some\noutput", nil) return runner }, - criticalErrWrapped: ErrInputPolicyNotFound, criticalErrMessage: "input policy not found: in INPUT rules: some\noutput", }, "set policy permission denied": { @@ -257,9 +250,10 @@ func Test_testIptablesPath(t *testing.T) { assert.Equal(t, testCase.ok, ok) assert.Equal(t, testCase.unsupportedMessage, unsupportedMessage) - assert.ErrorIs(t, criticalErr, testCase.criticalErrWrapped) - if testCase.criticalErrWrapped != nil { + if testCase.criticalErrMessage != "" { assert.EqualError(t, criticalErr, testCase.criticalErrMessage) + } else { + assert.NoError(t, criticalErr) } }) } diff --git a/internal/firewall/iptables/tcp.go b/internal/firewall/iptables/tcp.go index 77e5c5f2..8ed42fc7 100644 --- a/internal/firewall/iptables/tcp.go +++ b/internal/firewall/iptables/tcp.go @@ -45,12 +45,10 @@ func (f tcpFlag) String() string { case tcpFlagCWR: return "CWR" default: - panic(fmt.Sprintf("%s: %d", errTCPFlagUnknown, f)) + panic(fmt.Sprintf("unknown TCP flag: %d", f)) } } -var errTCPFlagUnknown = errors.New("unknown TCP flag") - func parseTCPFlag(s string) (tcpFlag, error) { allFlags := []tcpFlag{ tcpFlagFIN, tcpFlagSYN, tcpFlagRST, tcpFlagPSH, @@ -61,7 +59,7 @@ func parseTCPFlag(s string) (tcpFlag, error) { return flag, nil } } - return 0, fmt.Errorf("%w: %s", errTCPFlagUnknown, s) + return 0, fmt.Errorf("unknown TCP flag: %s", s) } var ErrMarkMatchModuleMissing = errors.New("kernel is missing the mark module libxt_mark.so") diff --git a/internal/healthcheck/checker.go b/internal/healthcheck/checker.go index c396ecba..5d36f58d 100644 --- a/internal/healthcheck/checker.go +++ b/internal/healthcheck/checker.go @@ -266,8 +266,6 @@ func makeAddressToDial(address string) (addressToDial string, err error) { return address, nil } -var ErrAllCheckTriesFailed = errors.New("all check tries failed") - func withRetries(ctx context.Context, tryTimeouts []time.Duration, logger Logger, checkName string, check func(ctx context.Context, try int) error, ) error { @@ -297,7 +295,7 @@ func withRetries(ctx context.Context, tryTimeouts []time.Duration, for i, err := range errs { errStrings[i] = fmt.Sprintf("attempt %d (%dms): %s", i+1, err.durationMS, err.err) } - return fmt.Errorf("%w:\n\t%s", ErrAllCheckTriesFailed, strings.Join(errStrings, "\n\t")) + return fmt.Errorf("all check tries failed:\n\t%s", strings.Join(errStrings, "\n\t")) } func (c *Checker) startupCheck(ctx context.Context) error { @@ -342,7 +340,7 @@ func (c *Checker) startupCheck(ctx context.Context) error { for i, err := range errs { errStrings[i] = fmt.Sprintf("parallel attempt %d/%d failed: %s", i+1, len(errs), err) } - return fmt.Errorf("%w: %s", ErrAllCheckTriesFailed, strings.Join(errStrings, ", ")) + return fmt.Errorf("all check tries failed: %s", strings.Join(errStrings, ", ")) } const ( diff --git a/internal/healthcheck/checker_test.go b/internal/healthcheck/checker_test.go index 4f9419ed..f6241f76 100644 --- a/internal/healthcheck/checker_test.go +++ b/internal/healthcheck/checker_test.go @@ -2,7 +2,6 @@ package healthcheck import ( "context" - "fmt" "net" "testing" "time" @@ -68,7 +67,7 @@ func Test_makeAddressToDial(t *testing.T) { testCases := map[string]struct { address string addressToDial string - err error + errMessage string }{ "host without port": { address: "test.com", @@ -79,8 +78,8 @@ func Test_makeAddressToDial(t *testing.T) { addressToDial: "test.com:80", }, "bad address": { - address: "test.com::", - err: fmt.Errorf("splitting host and port from address: address test.com::: too many colons in address"), //nolint:lll + address: "test.com::", + errMessage: "splitting host and port from address: address test.com::: too many colons in address", }, } @@ -91,8 +90,8 @@ func Test_makeAddressToDial(t *testing.T) { addressToDial, err := makeAddressToDial(testCase.address) assert.Equal(t, testCase.addressToDial, addressToDial) - if testCase.err != nil { - assert.EqualError(t, err, testCase.err.Error()) + if testCase.errMessage != "" { + assert.EqualError(t, err, testCase.errMessage) } else { assert.NoError(t, err) } diff --git a/internal/healthcheck/client.go b/internal/healthcheck/client.go index d52a58fd..8d6d229e 100644 --- a/internal/healthcheck/client.go +++ b/internal/healthcheck/client.go @@ -2,15 +2,12 @@ package healthcheck import ( "context" - "errors" "fmt" "io" "net/http" "time" ) -var ErrHTTPStatusNotOK = errors.New("HTTP response status is not OK") - type Client struct { httpClient *http.Client } @@ -41,6 +38,6 @@ func (c *Client) Check(ctx context.Context, url string) error { if err != nil { return err } - return fmt.Errorf("%w: %d %s: %s", ErrHTTPStatusNotOK, + return fmt.Errorf("HTTP response status is not OK: %d %s: %s", response.StatusCode, response.Status, string(b)) } diff --git a/internal/healthcheck/dns/dns.go b/internal/healthcheck/dns/dns.go index 27fe0bea..e98bba75 100644 --- a/internal/healthcheck/dns/dns.go +++ b/internal/healthcheck/dns/dns.go @@ -2,7 +2,6 @@ package dns import ( "context" - "errors" "fmt" "net" "net/netip" @@ -41,8 +40,6 @@ func concatAddrPorts(addrs [][]netip.AddrPort) []netip.AddrPort { return result } -var ErrLookupNoIPs = errors.New("no IPs found from DNS lookup") - func (c *Client) Check(ctx context.Context) error { dnsAddr := c.serverAddrs[c.dnsIPIndex].String() resolver := &net.Resolver{ @@ -59,7 +56,7 @@ func (c *Client) Check(ctx context.Context) error { return fmt.Errorf("with DNS server %s: %w", dnsAddr, err) case len(ips) == 0: c.dnsIPIndex = (c.dnsIPIndex + 1) % len(c.serverAddrs) - return fmt.Errorf("with DNS server %s: %w", dnsAddr, ErrLookupNoIPs) + return fmt.Errorf("with DNS server %s: no IPs found from DNS lookup", dnsAddr) default: return nil } diff --git a/internal/healthcheck/handler.go b/internal/healthcheck/handler.go index 2b9ee99b..3e49c41b 100644 --- a/internal/healthcheck/handler.go +++ b/internal/healthcheck/handler.go @@ -12,11 +12,9 @@ type handler struct { logger Logger } -var errHealthcheckNotRunYet = errors.New("healthcheck did not run yet") - func newHandler(logger Logger) *handler { return &handler{ - healthErr: errHealthcheckNotRunYet, + healthErr: errors.New("healthcheck did not run yet"), logger: logger, } } diff --git a/internal/healthcheck/icmp/echo.go b/internal/healthcheck/icmp/echo.go index 6b19d261..b147bad7 100644 --- a/internal/healthcheck/icmp/echo.go +++ b/internal/healthcheck/icmp/echo.go @@ -19,11 +19,6 @@ import ( "golang.org/x/net/ipv6" ) -var ( - ErrICMPBodyUnsupported = errors.New("ICMP body type is not supported") - ErrICMPEchoDataMismatch = errors.New("ICMP data mismatch") -) - type Echoer struct { buffer []byte randomSource io.Reader @@ -60,10 +55,7 @@ func (e *Echoer) Reset() { e.seqStart = time.Now() } -var ( - ErrTimedOut = errors.New("timed out waiting for ICMP echo reply") - ErrNotPermitted = errors.New("not permitted") -) +var ErrNotPermitted = errors.New("not permitted") func (e *Echoer) Echo(ctx context.Context, ip netip.Addr) (err error) { var ipVersion string @@ -114,14 +106,14 @@ func (e *Echoer) Echo(ctx context.Context, ip netip.Addr) (err error) { receivedData, err := receiveEchoReply(conn, e.id, e.seq, e.buffer, ipVersion, e.logger) if err != nil { if errors.Is(err, net.ErrClosed) && ctx.Err() != nil { - return fmt.Errorf("%w from %s", ErrTimedOut, ip) + return fmt.Errorf("timed out waiting for ICMP echo reply from %s", ip) } return fmt.Errorf("receiving ICMP echo reply from %s: %w", ip, err) } sentData := message.Body.(*icmp.Echo).Data //nolint:forcetypeassert if !bytes.Equal(receivedData, sentData) { - return fmt.Errorf("%w: sent %x to %s and received %x", ErrICMPEchoDataMismatch, sentData, ip, receivedData) + return fmt.Errorf("ICMP data mismatch: sent %x to %s and received %x", sentData, ip, receivedData) } return nil @@ -216,8 +208,9 @@ func receiveEchoReply(conn net.PacketConn, id, seq int, buffer []byte, ipVersion message.Code, returnAddr, id, seq) continue default: - return nil, fmt.Errorf("%w: %T (type %d, code %d, return address %s, expected id %d and seq %d)", - ErrICMPBodyUnsupported, body, message.Type, message.Code, returnAddr, id, seq) + return nil, fmt.Errorf("ICMP body type is not supported: "+ + "%T (type %d, code %d, return address %s, expected id %d and seq %d)", + body, message.Type, message.Code, returnAddr, id, seq) } } } diff --git a/internal/httpserver/server_test.go b/internal/httpserver/server_test.go index 0523db7e..88cba4be 100644 --- a/internal/httpserver/server_test.go +++ b/internal/httpserver/server_test.go @@ -6,7 +6,6 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) //go:generate mockgen -destination=logger_mock_test.go -package $GOPACKAGE . Logger @@ -20,11 +19,9 @@ func Test_New(t *testing.T) { testCases := map[string]struct { settings Settings expected *Server - errWrapped error errMessage string }{ "empty settings": { - errWrapped: ErrHandlerIsNotSet, errMessage: "http server settings validation failed: HTTP handler cannot be left unset", }, "filled settings": { @@ -52,9 +49,10 @@ func Test_New(t *testing.T) { t.Parallel() server, err := New(testCase.settings) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { - require.EqualError(t, err, testCase.errMessage) + if testCase.errMessage != "" { + assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } if server != nil { diff --git a/internal/httpserver/settings.go b/internal/httpserver/settings.go index 9b67c533..14e3d7e0 100644 --- a/internal/httpserver/settings.go +++ b/internal/httpserver/settings.go @@ -64,14 +64,6 @@ func (s *Settings) OverrideWith(other Settings) { s.ShutdownTimeout = gosettings.OverrideWithComparable(s.ShutdownTimeout, other.ShutdownTimeout) } -var ( - ErrHandlerIsNotSet = errors.New("HTTP handler cannot be left unset") - ErrLoggerIsNotSet = errors.New("logger cannot be left unset") - ErrReadHeaderTimeoutTooSmall = errors.New("read header timeout is too small") - ErrReadTimeoutTooSmall = errors.New("read timeout is too small") - ErrShutdownTimeoutTooSmall = errors.New("shutdown timeout is too small") -) - func (s Settings) Validate() (err error) { err = validate.ListeningAddress(s.Address, os.Getuid()) if err != nil { @@ -79,31 +71,25 @@ func (s Settings) Validate() (err error) { } if s.Handler == nil { - return fmt.Errorf("%w", ErrHandlerIsNotSet) + return errors.New("HTTP handler cannot be left unset") } if s.Logger == nil { - return fmt.Errorf("%w", ErrLoggerIsNotSet) + return errors.New("logger cannot be left unset") } const minReadTimeout = time.Millisecond if s.ReadHeaderTimeout < minReadTimeout { - return fmt.Errorf("%w: %s must be at least %s", - ErrReadHeaderTimeoutTooSmall, - s.ReadHeaderTimeout, minReadTimeout) + return fmt.Errorf("read header timeout is too small: %s must be at least %s", s.ReadHeaderTimeout, minReadTimeout) } if s.ReadTimeout < minReadTimeout { - return fmt.Errorf("%w: %s must be at least %s", - ErrReadTimeoutTooSmall, - s.ReadTimeout, minReadTimeout) + return fmt.Errorf("read timeout is too small: %s must be at least %s", s.ReadTimeout, minReadTimeout) } const minShutdownTimeout = 5 * time.Millisecond if s.ShutdownTimeout < minShutdownTimeout { - return fmt.Errorf("%w: %s must be at least %s", - ErrShutdownTimeoutTooSmall, - s.ShutdownTimeout, minShutdownTimeout) + return fmt.Errorf("shutdown timeout is too small: %s must be at least %s", s.ShutdownTimeout, minShutdownTimeout) } return nil diff --git a/internal/httpserver/settings_test.go b/internal/httpserver/settings_test.go index 3c817f3c..65437b54 100644 --- a/internal/httpserver/settings_test.go +++ b/internal/httpserver/settings_test.go @@ -5,7 +5,6 @@ import ( "testing" "time" - "github.com/qdm12/gosettings/validate" "github.com/stretchr/testify/assert" ) @@ -189,30 +188,26 @@ func Test_Settings_Validate(t *testing.T) { testCases := map[string]struct { settings Settings - errWrapped error errMessage string }{ "bad_address": { settings: Settings{ Address: "address:notanint", }, - errWrapped: validate.ErrPortNotAnInteger, errMessage: "port value is not an integer: notanint", }, "nil handler": { settings: Settings{ Address: ":8000", }, - errWrapped: ErrHandlerIsNotSet, - errMessage: ErrHandlerIsNotSet.Error(), + errMessage: "HTTP handler cannot be left unset", }, "nil logger": { settings: Settings{ Address: ":8000", Handler: someHandler, }, - errWrapped: ErrLoggerIsNotSet, - errMessage: ErrLoggerIsNotSet.Error(), + errMessage: "logger cannot be left unset", }, "read header timeout too small": { settings: Settings{ @@ -221,7 +216,6 @@ func Test_Settings_Validate(t *testing.T) { Logger: someLogger, ReadHeaderTimeout: time.Nanosecond, }, - errWrapped: ErrReadHeaderTimeoutTooSmall, errMessage: "read header timeout is too small: 1ns must be at least 1ms", }, "read timeout too small": { @@ -232,7 +226,6 @@ func Test_Settings_Validate(t *testing.T) { ReadHeaderTimeout: time.Millisecond, ReadTimeout: time.Nanosecond, }, - errWrapped: ErrReadTimeoutTooSmall, errMessage: "read timeout is too small: 1ns must be at least 1ms", }, "shutdown timeout too small": { @@ -244,7 +237,6 @@ func Test_Settings_Validate(t *testing.T) { ReadTimeout: time.Millisecond, ShutdownTimeout: time.Millisecond, }, - errWrapped: ErrShutdownTimeoutTooSmall, errMessage: "shutdown timeout is too small: 1ms must be at least 5ms", }, "valid settings": { @@ -265,9 +257,10 @@ func Test_Settings_Validate(t *testing.T) { err := testCase.settings.Validate() - assert.ErrorIs(t, err, testCase.errWrapped) if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/loopstate/apply.go b/internal/loopstate/apply.go index 4f9cc23d..411832f7 100644 --- a/internal/loopstate/apply.go +++ b/internal/loopstate/apply.go @@ -2,15 +2,12 @@ package loopstate import ( "context" - "errors" "fmt" "github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/models" ) -var ErrInvalidStatus = errors.New("invalid status") - // ApplyStatus sends signals to the running loop depending on the // current status and status requested, such that its next status // matches the requested one. It is thread safe and a synchronous call @@ -73,7 +70,7 @@ func (s *State) ApplyStatus(ctx context.Context, status models.LoopStatus) ( return newStatus.String(), nil default: s.statusMu.Unlock() - return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s", - ErrInvalidStatus, status, constants.Running, constants.Stopped) + return "", fmt.Errorf("invalid status: %s: it can only be one of: %s, %s", + status, constants.Running, constants.Stopped) } } diff --git a/internal/mod/configgz_linux.go b/internal/mod/configgz_linux.go index 0e118ab7..37416faa 100644 --- a/internal/mod/configgz_linux.go +++ b/internal/mod/configgz_linux.go @@ -3,19 +3,11 @@ package mod import ( "bufio" "compress/gzip" - "errors" "fmt" "os" "strings" ) -var ( - errModuleNameUnknown = errors.New("unknown module name") - errKernelFeatureIsModule = errors.New("kernel feature is a module, not built-in") - errKernelFeatureNotSet = errors.New("kernel feature not set") - errKernelFeatureNotFound = errors.New("kernel feature not found") -) - // checkProcConfig checks /proc/config.gz for a the kernel feature corresponding // to the given module name. If the kernel feature is found and set to "y", it returns nil. // If the kernel feature is found and set to "m", it returns an error indicating that the kernel @@ -39,7 +31,7 @@ func checkProcConfig(moduleName string) error { // If any group of kernel features is satisfied, then the module is considered supported. kernelFeatureGroups, ok := moduleNameToKernelFeatureGroups(moduleName) if !ok { - return fmt.Errorf("%w: %s", errModuleNameUnknown, moduleName) + return fmt.Errorf("unknown module name: %s", moduleName) } groups := make([]map[string]bool, len(kernelFeatureGroups)) for i, group := range kernelFeatureGroups { @@ -58,20 +50,20 @@ func checkProcConfig(moduleName string) error { switch { case ok: case strings.HasPrefix(line, name+"=m"): - return fmt.Errorf("%w: %s", errKernelFeatureIsModule, name) + return fmt.Errorf("kernel feature is a module, not built-in: %s", name) case strings.HasPrefix(line, name+"=y"): featureToOK[name] = true if allFeaturesOK(featureToOK) { return nil } case strings.HasPrefix(line, "# "+name+" is not set"): - return fmt.Errorf("%w: %s", errKernelFeatureNotSet, name) + return fmt.Errorf("kernel feature not set: %s", name) } } } } - return fmt.Errorf("%w: for module name %s", errKernelFeatureNotFound, moduleName) + return fmt.Errorf("kernel feature not found: for module name %s", moduleName) } func moduleNameToKernelFeatureGroups(moduleName string) (featureGroups [][]string, ok bool) { diff --git a/internal/mod/info_linux.go b/internal/mod/info_linux.go index 25fea26e..f43fb780 100644 --- a/internal/mod/info_linux.go +++ b/internal/mod/info_linux.go @@ -181,8 +181,6 @@ func getLoadedModules(modulesInfo map[string]moduleInfo) (err error) { return nil } -var ErrModulePathNotFound = errors.New("module path not found") - func findModulePath(moduleName string, modulesInfo map[string]moduleInfo) (modulePath string, err error) { // Kernel module names can have underscores or hyphens in their names, // but only one or the other in one particular name. @@ -205,5 +203,5 @@ func findModulePath(moduleName string, modulesInfo map[string]moduleInfo) (modul } } - return "", fmt.Errorf("%w: for %q", ErrModulePathNotFound, moduleName) + return "", fmt.Errorf("module path not found: for %q", moduleName) } diff --git a/internal/mod/load_linux.go b/internal/mod/load_linux.go index e5ea58f8..236d105a 100644 --- a/internal/mod/load_linux.go +++ b/internal/mod/load_linux.go @@ -1,7 +1,6 @@ package mod import ( - "errors" "fmt" "io" "os" @@ -14,15 +13,10 @@ import ( "golang.org/x/sys/unix" ) -var ( - ErrModuleInfoNotFound = errors.New("module info not found") - ErrCircularDependency = errors.New("circular dependency") -) - func initDependencies(path string, modulesInfo map[string]moduleInfo) (err error) { info, ok := modulesInfo[path] if !ok { - return fmt.Errorf("%w: %s", ErrModuleInfoNotFound, path) + return fmt.Errorf("module info not found: %s", path) } switch info.state { @@ -30,8 +24,7 @@ func initDependencies(path string, modulesInfo map[string]moduleInfo) (err error case loaded, builtin: return nil case loading: - return fmt.Errorf("%w: %s is already in the loading state", - ErrCircularDependency, path) + return fmt.Errorf("circular dependency: %s is already in the loading state", path) } info.state = loading diff --git a/internal/models/markdown.go b/internal/models/markdown.go index efa6812f..27d6b274 100644 --- a/internal/models/markdown.go +++ b/internal/models/markdown.go @@ -1,7 +1,6 @@ package models import ( - "errors" "fmt" "strings" @@ -109,8 +108,6 @@ func (s *Servers) toMarkdown(vpnProvider string) (formatted string, err error) { return formatted, nil } -var ErrMarkdownHeadersNotDefined = errors.New("markdown headers not defined") - func getMarkdownHeaders(vpnProvider string) (headers []string, err error) { switch vpnProvider { case providers.Airvpn: @@ -169,6 +166,6 @@ func getMarkdownHeaders(vpnProvider string) (headers []string, err error) { case providers.Windscribe: return []string{regionHeader, cityHeader, hostnameHeader, vpnHeader}, nil default: - return nil, fmt.Errorf("%w: for %s", ErrMarkdownHeadersNotDefined, vpnProvider) + return nil, fmt.Errorf("markdown headers not defined: for %s", vpnProvider) } } diff --git a/internal/models/markdown_test.go b/internal/models/markdown_test.go index 16d7cee4..4b0dbfd7 100644 --- a/internal/models/markdown_test.go +++ b/internal/models/markdown_test.go @@ -15,12 +15,10 @@ func Test_Servers_ToMarkdown(t *testing.T) { provider string servers Servers formatted string - errWrapped error errMessage string }{ "unsupported_provider": { provider: "unsupported", - errWrapped: ErrMarkdownHeadersNotDefined, errMessage: "getting markdown headers: markdown headers not defined: for unsupported", }, providers.Cyberghost: { @@ -58,9 +56,10 @@ func Test_Servers_ToMarkdown(t *testing.T) { markdown, err := testCase.servers.toMarkdown(testCase.provider) assert.Equal(t, testCase.formatted, markdown) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/models/server.go b/internal/models/server.go index 1ae92fb6..8c52ad66 100644 --- a/internal/models/server.go +++ b/internal/models/server.go @@ -38,27 +38,18 @@ type Server struct { IPs []netip.Addr `json:"ips,omitempty"` } -var ( - ErrVPNFieldEmpty = errors.New("vpn field is empty") - ErrHostnameFieldEmpty = errors.New("hostname field is empty") - ErrIPsFieldEmpty = errors.New("ips field is empty") - ErrNoNetworkProtocol = errors.New("both TCP and UDP fields are false for OpenVPN") - ErrNetworkProtocolSet = errors.New("no network protocol should be set") - ErrWireguardPublicKeyEmpty = errors.New("wireguard public key field is empty") -) - func (s *Server) HasMinimumInformation() (err error) { switch { case s.VPN == "": - return fmt.Errorf("%w", ErrVPNFieldEmpty) + return errors.New("vpn field is empty") case len(s.IPs) == 0: - return fmt.Errorf("%w", ErrIPsFieldEmpty) + return errors.New("ips field is empty") case s.VPN == vpn.Wireguard && (s.TCP || s.UDP): - return fmt.Errorf("%w", ErrNetworkProtocolSet) + return errors.New("no network protocol should be set") case s.VPN == vpn.OpenVPN && !s.TCP && !s.UDP: - return fmt.Errorf("%w", ErrNoNetworkProtocol) + return errors.New("both TCP and UDP fields are false for OpenVPN") case s.VPN == vpn.Wireguard && s.WgPubKey == "": - return fmt.Errorf("%w", ErrWireguardPublicKeyEmpty) + return errors.New("wireguard public key field is empty") default: return nil } diff --git a/internal/models/servers.go b/internal/models/servers.go index 4aba19a9..6ae47c48 100644 --- a/internal/models/servers.go +++ b/internal/models/servers.go @@ -3,7 +3,6 @@ package models import ( "bytes" "encoding/json" - "errors" "fmt" "math" "reflect" @@ -158,8 +157,6 @@ type Servers struct { Servers []Server `json:"servers,omitempty"` } -var ErrServersFormatNotSupported = errors.New("servers format not supported") - func (s *Servers) Format(vpnProvider, format string) (formatted string, err error) { switch format { case "markdown": @@ -167,7 +164,7 @@ func (s *Servers) Format(vpnProvider, format string) (formatted string, err erro case "json": return s.toJSON() default: - return "", fmt.Errorf("%w: %s", ErrServersFormatNotSupported, format) + return "", fmt.Errorf("servers format not supported: %s", format) } } diff --git a/internal/models/servers_test.go b/internal/models/servers_test.go index 9cf7dbfc..dc96592a 100644 --- a/internal/models/servers_test.go +++ b/internal/models/servers_test.go @@ -16,7 +16,6 @@ func Test_AllServers_MarshalJSON(t *testing.T) { testCases := map[string]struct { allServers *AllServers dataString string - errWrapped error errMessage string }{ "no provider": { @@ -58,16 +57,18 @@ func Test_AllServers_MarshalJSON(t *testing.T) { t.Parallel() data, err := testCase.allServers.MarshalJSON() - assert.ErrorIs(t, err, testCase.errWrapped) - if err != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } require.Equal(t, testCase.dataString, string(data)) data, err = json.Marshal(testCase.allServers) - assert.ErrorIs(t, err, testCase.errWrapped) - if err != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } require.Equal(t, testCase.dataString, string(data)) @@ -87,7 +88,6 @@ func Test_AllServers_UnmarshalJSON(t *testing.T) { testCases := map[string]struct { dataString string allServers AllServers - errWrapped error errMessage string }{ "empty": { @@ -131,9 +131,10 @@ func Test_AllServers_UnmarshalJSON(t *testing.T) { err := json.Unmarshal(data, &allServers) - assert.ErrorIs(t, err, testCase.errWrapped) - if err != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } assert.Equal(t, testCase.allServers, allServers) }) diff --git a/internal/natpmp/checks.go b/internal/natpmp/checks.go index f5cc4edb..31d3f0c7 100644 --- a/internal/natpmp/checks.go +++ b/internal/natpmp/checks.go @@ -6,48 +6,40 @@ import ( "fmt" ) -var ErrRequestSizeTooSmall = errors.New("message size is too small") - func checkRequest(request []byte) (err error) { const minMessageSize = 2 // version number + operation code if len(request) < minMessageSize { - return fmt.Errorf("%w: need at least %d bytes and got %d byte(s)", - ErrRequestSizeTooSmall, minMessageSize, len(request)) + return fmt.Errorf("message size is too small: need at least %d bytes and got %d byte(s)", + minMessageSize, len(request)) } return nil } -var ( - ErrResponseSizeTooSmall = errors.New("response size is too small") - ErrResponseSizeUnexpected = errors.New("response size is unexpected") - ErrProtocolVersionUnknown = errors.New("protocol version is unknown") - ErrOperationCodeUnexpected = errors.New("operation code is unexpected") -) - func checkResponse(response []byte, expectedOperationCode byte, expectedResponseSize uint, ) (err error) { const minResponseSize = 4 if len(response) < minResponseSize { - return fmt.Errorf("%w: need at least %d bytes and got %d byte(s)", - ErrResponseSizeTooSmall, minResponseSize, len(response)) + return fmt.Errorf("response size is too small: "+ + "need at least %d bytes and got %d byte(s)", + minResponseSize, len(response)) } if uint(len(response)) != expectedResponseSize { - return fmt.Errorf("%w: expected %d bytes and got %d byte(s)", - ErrResponseSizeUnexpected, expectedResponseSize, len(response)) + return fmt.Errorf("response size is unexpected: "+ + "expected %d bytes and got %d byte(s)", + expectedResponseSize, len(response)) } protocolVersion := response[0] if protocolVersion != 0 { - return fmt.Errorf("%w: %d", ErrProtocolVersionUnknown, protocolVersion) + return fmt.Errorf("protocol version is unknown: %d", protocolVersion) } operationCode := response[1] if operationCode != expectedOperationCode { - return fmt.Errorf("%w: expected 0x%x and got 0x%x", - ErrOperationCodeUnexpected, expectedOperationCode, operationCode) + return fmt.Errorf("operation code is unexpected: expected 0x%x and got 0x%x", expectedOperationCode, operationCode) } resultCode := binary.BigEndian.Uint16(response[2:4]) @@ -59,15 +51,6 @@ func checkResponse(response []byte, expectedOperationCode byte, return nil } -var ( - ErrVersionNotSupported = errors.New("version is not supported") - ErrNotAuthorized = errors.New("not authorized") - ErrNetworkFailure = errors.New("network failure") - ErrOutOfResources = errors.New("out of resources") - ErrOperationCodeNotSupported = errors.New("operation code is not supported") - ErrResultCodeUnknown = errors.New("result code is unknown") -) - // checkResultCode checks the result code and returns an error // if the result code is not a success (0). // See https://www.ietf.org/rfc/rfc6886.html#section-3.5 @@ -78,16 +61,16 @@ func checkResultCode(resultCode uint16) (err error) { case 0: return nil case 1: - return fmt.Errorf("%w", ErrVersionNotSupported) + return errors.New("version is not supported") case 2: - return fmt.Errorf("%w", ErrNotAuthorized) + return errors.New("not authorized") case 3: - return fmt.Errorf("%w", ErrNetworkFailure) + return errors.New("network failure") case 4: - return fmt.Errorf("%w", ErrOutOfResources) + return errors.New("out of resources") case 5: - return fmt.Errorf("%w", ErrOperationCodeNotSupported) + return errors.New("operation code is not supported") default: - return fmt.Errorf("%w: %d", ErrResultCodeUnknown, resultCode) + return fmt.Errorf("result code is unknown: %d", resultCode) } } diff --git a/internal/natpmp/checks_test.go b/internal/natpmp/checks_test.go index 8efc6463..84f44aec 100644 --- a/internal/natpmp/checks_test.go +++ b/internal/natpmp/checks_test.go @@ -1,6 +1,7 @@ package natpmp import ( + "errors" "testing" "github.com/stretchr/testify/assert" @@ -11,12 +12,10 @@ func Test_checkRequest(t *testing.T) { testCases := map[string]struct { request []byte - err error errMessage string }{ "too_short": { request: []byte{1}, - err: ErrRequestSizeTooSmall, errMessage: "message size is too small: need at least 2 bytes and got 1 byte(s)", }, "success": { @@ -30,9 +29,10 @@ func Test_checkRequest(t *testing.T) { err := checkRequest(testCase.request) - assert.ErrorIs(t, err, testCase.err) - if testCase.err != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } @@ -50,33 +50,33 @@ func Test_checkResponse(t *testing.T) { }{ "too_short": { response: []byte{1}, - err: ErrResponseSizeTooSmall, + err: errors.New("response size is too small"), errMessage: "response size is too small: need at least 4 bytes and got 1 byte(s)", }, "size_mismatch": { response: []byte{0, 0, 0, 0}, expectedResponseSize: 5, - err: ErrResponseSizeUnexpected, + err: errors.New("response size is unexpected"), errMessage: "response size is unexpected: expected 5 bytes and got 4 byte(s)", }, "protocol_unknown": { response: []byte{1, 0, 0, 0}, expectedResponseSize: 4, - err: ErrProtocolVersionUnknown, + err: errors.New("protocol version is unknown"), errMessage: "protocol version is unknown: 1", }, "operation_code_unexpected": { response: []byte{0, 2, 0, 0}, expectedOperationCode: 1, expectedResponseSize: 4, - err: ErrOperationCodeUnexpected, + err: errors.New("operation code is unexpected"), errMessage: "operation code is unexpected: expected 0x1 and got 0x2", }, "result_code_failure": { response: []byte{0, 1, 0, 1}, expectedOperationCode: 1, expectedResponseSize: 4, - err: ErrVersionNotSupported, + err: errors.New("version is not supported"), errMessage: "result code: version is not supported", }, "success": { @@ -94,9 +94,11 @@ func Test_checkResponse(t *testing.T) { testCase.expectedOperationCode, testCase.expectedResponseSize) - assert.ErrorIs(t, err, testCase.err) if testCase.err != nil { + assert.ErrorContains(t, err, testCase.err.Error()) assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } @@ -113,32 +115,32 @@ func Test_checkResultCode(t *testing.T) { "success": {}, "version_unsupported": { resultCode: 1, - err: ErrVersionNotSupported, + err: errors.New("version is not supported"), errMessage: "version is not supported", }, "not_authorized": { resultCode: 2, - err: ErrNotAuthorized, + err: errors.New("not authorized"), errMessage: "not authorized", }, "network_failure": { resultCode: 3, - err: ErrNetworkFailure, + err: errors.New("network failure"), errMessage: "network failure", }, "out_of_resources": { resultCode: 4, - err: ErrOutOfResources, + err: errors.New("out of resources"), errMessage: "out of resources", }, "unsupported_operation_code": { resultCode: 5, - err: ErrOperationCodeNotSupported, + err: errors.New("operation code is not supported"), errMessage: "operation code is not supported", }, "unknown": { resultCode: 6, - err: ErrResultCodeUnknown, + err: errors.New("result code is unknown"), errMessage: "result code is unknown: 6", }, } @@ -149,9 +151,11 @@ func Test_checkResultCode(t *testing.T) { err := checkResultCode(testCase.resultCode) - assert.ErrorIs(t, err, testCase.err) if testCase.err != nil { + assert.ErrorContains(t, err, testCase.err.Error()) assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/natpmp/portmapping.go b/internal/natpmp/portmapping.go index 52d0ee1b..c902cda7 100644 --- a/internal/natpmp/portmapping.go +++ b/internal/natpmp/portmapping.go @@ -3,17 +3,11 @@ package natpmp import ( "context" "encoding/binary" - "errors" "fmt" "net/netip" "time" ) -var ( - ErrNetworkProtocolUnknown = errors.New("network protocol is unknown") - ErrLifetimeTooLong = errors.New("lifetime is too long") -) - // Add or delete a port mapping. To delete a mapping, set both the // requestedExternalPort and lifetime to 0. // See https://www.ietf.org/rfc/rfc6886.html#section-3.3 @@ -26,8 +20,9 @@ func (c *Client) AddPortMapping(ctx context.Context, gateway netip.Addr, lifetimeSecondsFloat := lifetime.Seconds() const maxLifetimeSeconds = uint64(^uint32(0)) if uint64(lifetimeSecondsFloat) > maxLifetimeSeconds { - return 0, 0, 0, 0, fmt.Errorf("%w: %d seconds must at most %d seconds", - ErrLifetimeTooLong, uint64(lifetimeSecondsFloat), maxLifetimeSeconds) + return 0, 0, 0, 0, fmt.Errorf("lifetime is too long: "+ + "%d seconds must at most %d seconds", + uint64(lifetimeSecondsFloat), maxLifetimeSeconds) } const messageSize = 12 message := make([]byte, messageSize) @@ -38,7 +33,7 @@ func (c *Client) AddPortMapping(ctx context.Context, gateway netip.Addr, case "tcp": message[1] = 2 // operationCode 2 default: - return 0, 0, 0, 0, fmt.Errorf("%w: %s", ErrNetworkProtocolUnknown, protocol) + return 0, 0, 0, 0, fmt.Errorf("network protocol is unknown: %s", protocol) } // [2:3] are reserved. binary.BigEndian.PutUint16(message[4:6], internalPort) diff --git a/internal/natpmp/portmapping_test.go b/internal/natpmp/portmapping_test.go index b89e3d84..a8c48f7a 100644 --- a/internal/natpmp/portmapping_test.go +++ b/internal/natpmp/portmapping_test.go @@ -25,18 +25,15 @@ func Test_Client_AddPortMapping(t *testing.T) { assignedInternalPort uint16 assignedExternalPort uint16 assignedLifetime time.Duration - err error errMessage string }{ "lifetime_too_long": { lifetime: time.Duration(uint64(^uint32(0))+1) * time.Second, - err: ErrLifetimeTooLong, errMessage: "lifetime is too long: 4294967296 seconds must at most 4294967295 seconds", }, "protocol_unknown": { lifetime: time.Second, protocol: "xyz", - err: ErrNetworkProtocolUnknown, errMessage: "network protocol is unknown: xyz", }, "rpc_error": { @@ -48,7 +45,6 @@ func Test_Client_AddPortMapping(t *testing.T) { lifetime: 1200 * time.Second, initialConnectionDuration: time.Millisecond, exchanges: []udpExchange{{close: true}}, - err: ErrConnectionTimeout, errMessage: "executing remote procedure call: connection timeout: failed attempts: " + "read udp 127.0.0.1:[1-9][0-9]{0,4}->127.0.0.1:[1-9][0-9]{0,4}: i/o timeout \\(try 1\\)", }, @@ -136,9 +132,6 @@ func Test_Client_AddPortMapping(t *testing.T) { assert.Equal(t, testCase.assignedExternalPort, assignedExternalPort) assert.Equal(t, testCase.assignedLifetime, assignedLifetime) if testCase.errMessage != "" { - if testCase.err != nil { - assert.ErrorIs(t, err, testCase.err) - } assert.Regexp(t, "^"+testCase.errMessage+"$", err.Error()) } else { assert.NoError(t, err) diff --git a/internal/natpmp/rpc.go b/internal/natpmp/rpc.go index d0f6598d..aa87b3ee 100644 --- a/internal/natpmp/rpc.go +++ b/internal/natpmp/rpc.go @@ -11,17 +11,12 @@ import ( "time" ) -var ( - ErrGatewayIPUnspecified = errors.New("gateway IP is unspecified") - ErrConnectionTimeout = errors.New("connection timeout") -) - func (c *Client) rpc(ctx context.Context, gateway netip.Addr, request []byte, responseSize uint) ( response []byte, err error, ) { if gateway.IsUnspecified() || !gateway.IsValid() { - return nil, fmt.Errorf("%w", ErrGatewayIPUnspecified) + return nil, errors.New("gateway IP is unspecified") } err = checkRequest(request) @@ -114,8 +109,7 @@ func (c *Client) rpc(ctx context.Context, gateway netip.Addr, } if retryCount == c.maxRetries { - return nil, fmt.Errorf("%w: failed attempts: %s", - ErrConnectionTimeout, dedupFailedAttempts(failedAttempts)) + return nil, fmt.Errorf("connection timeout: failed attempts: %s", dedupFailedAttempts(failedAttempts)) } // Opcodes between 0 and 127 are client requests. Opcodes from 128 to diff --git a/internal/natpmp/rpc_test.go b/internal/natpmp/rpc_test.go index 3019d012..f9a13e43 100644 --- a/internal/natpmp/rpc_test.go +++ b/internal/natpmp/rpc_test.go @@ -20,20 +20,17 @@ func Test_Client_rpc(t *testing.T) { initialConnectionDuration time.Duration exchanges []udpExchange expectedResponse []byte - err error errMessage string }{ "gateway_ip_unspecified": { gateway: netip.IPv6Unspecified(), request: []byte{0, 0}, - err: ErrGatewayIPUnspecified, errMessage: "gateway IP is unspecified", }, "request_too_small": { gateway: netip.AddrFrom4([4]byte{127, 0, 0, 1}), request: []byte{0}, initialConnectionDuration: time.Nanosecond, // doesn't matter - err: ErrRequestSizeTooSmall, errMessage: `checking request: message size is too small: ` + `need at least 2 bytes and got 1 byte\(s\)`, }, @@ -53,7 +50,6 @@ func Test_Client_rpc(t *testing.T) { exchanges: []udpExchange{ {request: []byte{0, 1}, close: true}, }, - err: ErrConnectionTimeout, errMessage: "connection timeout: failed attempts: " + "read udp 127.0.0.1:[1-9][0-9]{0,4}->127.0.0.1:[1-9][0-9]{0,4}: i/o timeout \\(try 1\\)", }, @@ -66,7 +62,6 @@ func Test_Client_rpc(t *testing.T) { request: []byte{0, 0}, response: []byte{1}, }}, - err: ErrResponseSizeTooSmall, errMessage: `checking response: response size is too small: ` + `need at least 4 bytes and got 1 byte\(s\)`, }, @@ -80,7 +75,6 @@ func Test_Client_rpc(t *testing.T) { request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, response: []byte{0, 1, 2, 3}, // size 4 }}, - err: ErrResponseSizeUnexpected, errMessage: `checking response: response size is unexpected: ` + `expected 5 bytes and got 4 byte\(s\)`, }, @@ -94,7 +88,6 @@ func Test_Client_rpc(t *testing.T) { request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, response: []byte{0x1, 0x82, 0x0, 0x0, 0x0, 0x14, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, }}, - err: ErrProtocolVersionUnknown, errMessage: "checking response: protocol version is unknown: 1", }, "unexpected_operation_code": { @@ -107,7 +100,6 @@ func Test_Client_rpc(t *testing.T) { request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, response: []byte{0x0, 0x88, 0x0, 0x0, 0x0, 0x14, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, }}, - err: ErrOperationCodeUnexpected, errMessage: "checking response: operation code is unexpected: expected 0x82 and got 0x88", }, "failure_result_code": { @@ -120,7 +112,6 @@ func Test_Client_rpc(t *testing.T) { request: []byte{0x0, 0x2, 0x0, 0x0, 0x0, 0x7b, 0x1, 0xc8, 0x0, 0x0, 0x4, 0xb0}, response: []byte{0x0, 0x82, 0x0, 0x11, 0x0, 0x14, 0x4, 0x96, 0x0, 0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, }}, - err: ErrResultCodeUnknown, errMessage: "checking response: result code: result code is unknown: 17", }, "success": { @@ -153,9 +144,6 @@ func Test_Client_rpc(t *testing.T) { testCase.request, testCase.responseSize) if testCase.errMessage != "" { - if testCase.err != nil { - assert.ErrorIs(t, err, testCase.err) - } assert.Regexp(t, "^"+testCase.errMessage+"$", err.Error()) } else { assert.NoError(t, err) diff --git a/internal/netlink/ipv6_test.go b/internal/netlink/ipv6_test.go index 295984f5..d04d1f97 100644 --- a/internal/netlink/ipv6_test.go +++ b/internal/netlink/ipv6_test.go @@ -41,8 +41,6 @@ func findAvailableTCPPort(t *testing.T) (port uint16) { func Test_dialAddrThroughFirewall(t *testing.T) { t.Parallel() - errTest := errors.New("test error") - const ipv6InternetWorks = false testCases := map[string]struct { @@ -102,7 +100,7 @@ func Test_dialAddrThroughFirewall(t *testing.T) { }, }, "firewall_add_error": { - firewallAddErr: errTest, + firewallAddErr: errors.New("test error"), errMessageRegex: func() string { return "accepting output traffic: test error" }, @@ -122,7 +120,7 @@ func Test_dialAddrThroughFirewall(t *testing.T) { addrPort := netip.MustParseAddrPort(listener.Addr().String()) return netip.AddrPortFrom(loopback, addrPort.Port()) }, - firewallRemoveErr: errTest, + firewallRemoveErr: errors.New("test error"), errMessageRegex: func() string { return "removing output traffic rule: test error" }, diff --git a/internal/netlink/link.go b/internal/netlink/link.go index 5e103747..821a4423 100644 --- a/internal/netlink/link.go +++ b/internal/netlink/link.go @@ -1,7 +1,6 @@ package netlink import ( - "errors" "fmt" "github.com/jsimonetti/rtnetlink" @@ -47,8 +46,6 @@ func (n *NetLink) LinkList() (links []Link, err error) { return links, nil } -var ErrLinkNotFound = errors.New("link not found") - func (n *NetLink) LinkByName(name string) (link Link, err error) { links, err := n.LinkList() if err != nil { @@ -61,7 +58,7 @@ func (n *NetLink) LinkByName(name string) (link Link, err error) { } } - return Link{}, fmt.Errorf("%w: for name %s", ErrLinkNotFound, name) + return Link{}, fmt.Errorf("link not found: for name %s", name) } func (n *NetLink) LinkByIndex(index uint32) (link Link, err error) { @@ -76,7 +73,7 @@ func (n *NetLink) LinkByIndex(index uint32) (link Link, err error) { } } - return Link{}, fmt.Errorf("%w: for index %d", ErrLinkNotFound, index) + return Link{}, fmt.Errorf("link not found: for index %d", index) } func (n *NetLink) LinkAdd(link Link) (linkIndex uint32, err error) { @@ -114,7 +111,7 @@ func (n *NetLink) LinkAdd(link Link) (linkIndex uint32, err error) { } } - return 0, fmt.Errorf("%w: matching name %s", ErrLinkNotFound, link.Name) + return 0, fmt.Errorf("link not found: matching name %s", link.Name) } func (n *NetLink) LinkDel(linkIndex uint32) (err error) { diff --git a/internal/openvpn/extract/data.go b/internal/openvpn/extract/data.go index bc658889..9cf42a94 100644 --- a/internal/openvpn/extract/data.go +++ b/internal/openvpn/extract/data.go @@ -1,17 +1,11 @@ package extract import ( - "errors" "fmt" "github.com/qdm12/gluetun/internal/models" ) -var ( - ErrRead = errors.New("cannot read file") - ErrExtractConnection = errors.New("cannot extract connection from file") -) - // Data extracts the lines and connection from the OpenVPN configuration file. func (e *Extractor) Data(filepath string) (lines []string, connection models.Connection, err error, diff --git a/internal/openvpn/extract/extract.go b/internal/openvpn/extract/extract.go index eaa90888..b5557e46 100644 --- a/internal/openvpn/extract/extract.go +++ b/internal/openvpn/extract/extract.go @@ -11,8 +11,6 @@ import ( "github.com/qdm12/gluetun/internal/models" ) -var errRemoteLineNotFound = errors.New("remote line not found") - func extractDataFromLines(lines []string) ( connection models.Connection, err error, ) { @@ -35,7 +33,7 @@ func extractDataFromLines(lines []string) ( } if !connection.IP.IsValid() { - return connection, errRemoteLineNotFound + return connection, errors.New("remote line not found") } if connection.Protocol == "" { @@ -81,19 +79,15 @@ func extractDataFromLine(line string) ( return ip, 0, "", nil } -var errProtoLineFieldsCount = errors.New("proto line has not 2 fields as expected") - func extractProto(line string) (protocol string, err error) { fields := strings.Fields(line) if len(fields) != 2 { //nolint:mnd - return "", fmt.Errorf("%w: %s", errProtoLineFieldsCount, line) + return "", fmt.Errorf("proto line has not 2 fields as expected: %s", line) } return parseProto(fields[1]) } -var errProtocolNotSupported = errors.New("network protocol not supported") - func parseProto(field string) (protocol string, err error) { switch field { case "tcp", "tcp4", "tcp6", "tcp-client": @@ -106,16 +100,10 @@ func parseProto(field string) (protocol string, err error) { // determined by the remote IP address version. return constants.UDP, nil default: - return "", fmt.Errorf("%w: %s", errProtocolNotSupported, field) + return "", fmt.Errorf("network protocol not supported: %s", field) } } -var ( - errRemoteLineFieldsCount = errors.New("remote line has not 2 fields as expected") - errHostNotIP = errors.New("host is not an IP address") - errPortNotValid = errors.New("port is not valid") -) - func extractRemote(line string) (ip netip.Addr, port uint16, protocol string, err error, ) { @@ -123,13 +111,13 @@ func extractRemote(line string) (ip netip.Addr, port uint16, n := len(fields) if n < 2 || n > 4 { - return netip.Addr{}, 0, "", fmt.Errorf("%w: %s", errRemoteLineFieldsCount, line) + return netip.Addr{}, 0, "", fmt.Errorf("remote line has not 2 fields as expected: %s", line) } host := fields[1] ip, err = netip.ParseAddr(host) if err != nil { - return netip.Addr{}, 0, "", fmt.Errorf("%w: %s", errHostNotIP, host) + return netip.Addr{}, 0, "", fmt.Errorf("host is not an IP address: %s", host) // TODO resolve hostname once there is an option to allow it through // the firewall before the VPN is up. } @@ -137,9 +125,9 @@ func extractRemote(line string) (ip netip.Addr, port uint16, if n > 2 { //nolint:mnd portInt, err := strconv.Atoi(fields[2]) if err != nil { - return netip.Addr{}, 0, "", fmt.Errorf("%w: %s", errPortNotValid, line) + return netip.Addr{}, 0, "", fmt.Errorf("port is not valid: %s", line) } else if portInt < 1 || portInt > 65535 { - return netip.Addr{}, 0, "", fmt.Errorf("%w: %d must be between 1 and 65535", errPortNotValid, portInt) + return netip.Addr{}, 0, "", fmt.Errorf("port is not valid: %d must be between 1 and 65535", portInt) } port = uint16(portInt) } @@ -154,20 +142,18 @@ func extractRemote(line string) (ip netip.Addr, port uint16, return ip, port, protocol, nil } -var errPostLineFieldsCount = errors.New("post line has not 2 fields as expected") - func extractPort(line string) (port uint16, err error) { fields := strings.Fields(line) const expectedFieldsCount = 2 if len(fields) != expectedFieldsCount { - return 0, fmt.Errorf("%w: %s", errPostLineFieldsCount, line) + return 0, fmt.Errorf("post line has not 2 fields as expected: %s", line) } portInt, err := strconv.Atoi(fields[1]) if err != nil { - return 0, fmt.Errorf("%w: %s", errPortNotValid, line) + return 0, fmt.Errorf("port is not valid: %s", line) } else if portInt < 1 || portInt > 65535 { - return 0, fmt.Errorf("%w: %d must be between 1 and 65535", errPortNotValid, portInt) + return 0, fmt.Errorf("port is not valid: %d must be between 1 and 65535", portInt) } port = uint16(portInt) diff --git a/internal/openvpn/extract/extract_test.go b/internal/openvpn/extract/extract_test.go index 77e122ec..200bfdb8 100644 --- a/internal/openvpn/extract/extract_test.go +++ b/internal/openvpn/extract/extract_test.go @@ -17,7 +17,7 @@ func Test_extractDataFromLines(t *testing.T) { testCases := map[string]struct { lines []string connection models.Connection - err error + errMessage string }{ "success": { lines: []string{"bla", "proto tcp", "remote 1.2.3.4 1194 tcp", "dev tun6"}, @@ -28,8 +28,8 @@ func Test_extractDataFromLines(t *testing.T) { }, }, "extraction error": { - lines: []string{"bla", "proto bad", "remote 1.2.3.4 1194 tcp"}, - err: errors.New("on line 2: extracting protocol from proto line: network protocol not supported: bad"), + lines: []string{"bla", "proto bad", "remote 1.2.3.4 1194 tcp"}, + errMessage: "on line 2: extracting protocol from proto line: network protocol not supported: bad", }, "only use first values found": { lines: []string{"proto udp", "proto tcp", "remote 1.2.3.4 443 tcp", "remote 5.2.3.4 1194 udp"}, @@ -44,7 +44,7 @@ func Test_extractDataFromLines(t *testing.T) { connection: models.Connection{ Protocol: constants.TCP, }, - err: errRemoteLineNotFound, + errMessage: "remote line not found", }, "default TCP port": { lines: []string{"remote 1.2.3.4", "proto tcp"}, @@ -70,9 +70,8 @@ func Test_extractDataFromLines(t *testing.T) { connection, err := extractDataFromLines(testCase.lines) - if testCase.err != nil { - require.Error(t, err) - assert.Equal(t, testCase.err.Error(), err.Error()) + if testCase.errMessage != "" { + assert.EqualError(t, err, testCase.errMessage) } else { assert.NoError(t, err) } @@ -86,18 +85,18 @@ func Test_extractDataFromLine(t *testing.T) { t.Parallel() testCases := map[string]struct { - line string - ip netip.Addr - port uint16 - protocol string - isErr error + line string + ip netip.Addr + port uint16 + protocol string + errMessage string }{ "irrelevant line": { line: "bla", }, "extract proto error": { - line: "proto bad", - isErr: errProtocolNotSupported, + line: "proto bad", + errMessage: "network protocol not supported", }, "extract proto success": { line: "proto tcp", @@ -108,8 +107,8 @@ func Test_extractDataFromLine(t *testing.T) { protocol: constants.TCP, }, "extract remote error": { - line: "remote bad", - isErr: errHostNotIP, + line: "remote bad", + errMessage: "host is not an IP address", }, "extract remote success": { line: "remote 1.2.3.4 1194 udp", @@ -118,8 +117,8 @@ func Test_extractDataFromLine(t *testing.T) { protocol: constants.UDP, }, "extract_port_fail": { - line: "port a", - isErr: errPortNotValid, + line: "port a", + errMessage: "port is not valid", }, "extract_port_success": { line: "port 1194", @@ -133,8 +132,8 @@ func Test_extractDataFromLine(t *testing.T) { ip, port, protocol, err := extractDataFromLine(testCase.line) - if testCase.isErr != nil { - assert.ErrorIs(t, err, testCase.isErr) + if testCase.errMessage != "" { + assert.ErrorContains(t, err, testCase.errMessage) } else { assert.NoError(t, err) } diff --git a/internal/openvpn/extract/pem.go b/internal/openvpn/extract/pem.go index 5126a0b7..09ee27a4 100644 --- a/internal/openvpn/extract/pem.go +++ b/internal/openvpn/extract/pem.go @@ -4,15 +4,12 @@ import ( "encoding/base64" "encoding/pem" "errors" - "fmt" ) -var errPEMDecode = errors.New("cannot decode PEM encoded block") - func PEM(b []byte) (encodedData string, err error) { pemBlock, _ := pem.Decode(b) if pemBlock == nil { - return "", fmt.Errorf("%w", errPEMDecode) + return "", errors.New("cannot decode PEM encoded block") } der := pemBlock.Bytes diff --git a/internal/openvpn/extract/pem_test.go b/internal/openvpn/extract/pem_test.go index 2a4680b6..e9bb2146 100644 --- a/internal/openvpn/extract/pem_test.go +++ b/internal/openvpn/extract/pem_test.go @@ -13,16 +13,13 @@ func Test_PEM(t *testing.T) { testCases := map[string]struct { b []byte encodedData string - errWrapped error errMessage string }{ "no input": { - errWrapped: errPEMDecode, errMessage: "cannot decode PEM encoded block", }, "bad input": { b: []byte{1, 2, 3}, - errWrapped: errPEMDecode, errMessage: "cannot decode PEM encoded block", }, "valid data with extras": { @@ -46,9 +43,10 @@ func Test_PEM(t *testing.T) { encodedData, err := PEM(testCase.b) assert.Equal(t, testCase.encodedData, encodedData) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/openvpn/pkcs8/algorithms.go b/internal/openvpn/pkcs8/algorithms.go index 3514b88a..39097389 100644 --- a/internal/openvpn/pkcs8/algorithms.go +++ b/internal/openvpn/pkcs8/algorithms.go @@ -3,7 +3,6 @@ package pkcs8 import ( "crypto/x509/pkix" "encoding/asn1" - "errors" "fmt" ) @@ -11,8 +10,6 @@ import ( // https://www.ibm.com/docs/en/zos/2.3.0?topic=programming-object-identifiers var oidDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7} //nolint:gochecknoglobals -var ErrEncryptionAlgorithmNotPBES2 = errors.New("encryption algorithm is not PBES2") - type encryptedPrivateKey struct { EncryptionAlgorithm pkix.AlgorithmIdentifier EncryptedData []byte @@ -35,8 +32,8 @@ func getEncryptionAlgorithmOid(der []byte) ( oidPBES2 := asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13} oidAlgorithm := encryptedPrivateKeyData.EncryptionAlgorithm.Algorithm if !oidAlgorithm.Equal(oidPBES2) { - return nil, fmt.Errorf("%w: %s instead of PBES2 %s", - ErrEncryptionAlgorithmNotPBES2, oidAlgorithm, oidPBES2) + return nil, fmt.Errorf("encryption algorithm is not PBES2: %s instead of PBES2 %s", + oidAlgorithm, oidPBES2) } var encryptionAlgorithmParams encryptedAlgorithmParams diff --git a/internal/openvpn/pkcs8/upgrade.go b/internal/openvpn/pkcs8/upgrade.go index 2b0b9664..9c0b962b 100644 --- a/internal/openvpn/pkcs8/upgrade.go +++ b/internal/openvpn/pkcs8/upgrade.go @@ -2,14 +2,11 @@ package pkcs8 import ( "encoding/base64" - "errors" "fmt" pkcs8lib "github.com/youmark/pkcs8" ) -var ErrUnsupportedKeyType = errors.New("unsupported key type") - // UpgradeEncryptedKey eventually upgrades an encrypted key to a newer encryption // if its encryption is too weak for Openvpn/Openssl. // If the key is encrypted using DES-CBC, it is decrypted and re-encrypted using AES-256-CBC. diff --git a/internal/openvpn/start.go b/internal/openvpn/start.go index 01e7e9b2..f78082a7 100644 --- a/internal/openvpn/start.go +++ b/internal/openvpn/start.go @@ -2,15 +2,12 @@ package openvpn import ( "context" - "errors" "fmt" "os/exec" "github.com/qdm12/gluetun/internal/constants/openvpn" ) -var ErrVersionUnknown = errors.New("OpenVPN version is unknown") - const ( binOpenvpn25 = "openvpn2.5" binOpenvpn26 = "openvpn2.6" @@ -26,7 +23,7 @@ func start(ctx context.Context, starter CmdStarter, version string, flags []stri case openvpn.Openvpn26: bin = binOpenvpn26 default: - return nil, nil, nil, fmt.Errorf("%w: %s", ErrVersionUnknown, version) + return nil, nil, nil, fmt.Errorf("OpenVPN version is unknown: %s", version) } args := []string{"--config", configPath} diff --git a/internal/openvpn/version.go b/internal/openvpn/version.go index dd9d5493..a78c84f1 100644 --- a/internal/openvpn/version.go +++ b/internal/openvpn/version.go @@ -2,7 +2,6 @@ package openvpn import ( "context" - "errors" "fmt" "os/exec" "strings" @@ -16,8 +15,6 @@ func (c *Configurator) Version26(ctx context.Context) (version string, err error return c.version(ctx, binOpenvpn26) } -var ErrVersionTooShort = errors.New("version output is too short") - func (c *Configurator) version(ctx context.Context, binName string) (version string, err error) { cmd := exec.CommandContext(ctx, binName, "--version") output, err := c.cmder.Run(cmd) @@ -28,7 +25,7 @@ func (c *Configurator) version(ctx context.Context, binName string) (version str words := strings.Fields(firstLine) const minWords = 2 if len(words) < minWords { - return "", fmt.Errorf("%w: %s", ErrVersionTooShort, firstLine) + return "", fmt.Errorf("version output is too short: %s", firstLine) } return words[1], nil } diff --git a/internal/pmtud/icmp/check.go b/internal/pmtud/icmp/check.go index 72e6dc3d..84ebad44 100644 --- a/internal/pmtud/icmp/check.go +++ b/internal/pmtud/icmp/check.go @@ -2,24 +2,17 @@ package icmp import ( "bytes" - "errors" "fmt" "golang.org/x/net/icmp" ) -var ( - ErrNextHopMTUTooLow = errors.New("ICMP Next Hop MTU is too low") - ErrNextHopMTUTooHigh = errors.New("ICMP Next Hop MTU is too high") -) - func checkMTU(mtu, minMTU, physicalLinkMTU uint32) (err error) { switch { case mtu < minMTU: - return fmt.Errorf("%w: %d", ErrNextHopMTUTooLow, mtu) + return fmt.Errorf("ICMP Next Hop MTU is too low: %d", mtu) case mtu > physicalLinkMTU: - return fmt.Errorf("%w: %d is larger than physical link MTU %d", - ErrNextHopMTUTooHigh, mtu, physicalLinkMTU) + return fmt.Errorf("ICMP Next Hop MTU is too high: %d is larger than physical link MTU %d", mtu, physicalLinkMTU) default: return nil } @@ -34,14 +27,12 @@ func checkInvokingReplyIDMatch(icmpProtocol int, received []byte, } inboundBody, ok := inboundMessage.Body.(*icmp.Echo) if !ok { - return false, fmt.Errorf("%w: %T", ErrBodyUnsupported, inboundMessage.Body) + return false, fmt.Errorf("ICMP body type is not supported: %T", inboundMessage.Body) } outboundBody := outboundMessage.Body.(*icmp.Echo) //nolint:forcetypeassert return inboundBody.ID == outboundBody.ID, nil } -var ErrIDMismatch = errors.New("ICMP id mismatch") - func checkEchoReply(icmpProtocol int, received []byte, outboundMessage *icmp.Message, truncatedBody bool, ) (err error) { @@ -51,12 +42,12 @@ func checkEchoReply(icmpProtocol int, received []byte, } inboundBody, ok := inboundMessage.Body.(*icmp.Echo) if !ok { - return fmt.Errorf("%w: %T", ErrBodyUnsupported, inboundMessage.Body) + return fmt.Errorf("ICMP body type is not supported: %T", inboundMessage.Body) } outboundBody := outboundMessage.Body.(*icmp.Echo) //nolint:forcetypeassert if inboundBody.ID != outboundBody.ID { - return fmt.Errorf("%w: sent id %d and received id %d", - ErrIDMismatch, outboundBody.ID, inboundBody.ID) + return fmt.Errorf("ICMP id mismatch: sent id %d and received id %d", + outboundBody.ID, inboundBody.ID) } err = checkEchoBodies(outboundBody.Data, inboundBody.Data, truncatedBody) if err != nil { @@ -65,19 +56,17 @@ func checkEchoReply(icmpProtocol int, received []byte, return nil } -var ErrEchoDataMismatch = errors.New("ICMP data mismatch") - func checkEchoBodies(sent, received []byte, receivedTruncated bool) (err error) { if len(received) > len(sent) { - return fmt.Errorf("%w: sent %d bytes and received %d bytes", - ErrEchoDataMismatch, len(sent), len(received)) + return fmt.Errorf("ICMP data mismatch: sent %d bytes and received %d bytes", + len(sent), len(received)) } if receivedTruncated { sent = sent[:len(received)] } if !bytes.Equal(received, sent) { - return fmt.Errorf("%w: sent %x and received %x", - ErrEchoDataMismatch, sent, received) + return fmt.Errorf("ICMP data mismatch: sent %x and received %x", + sent, received) } return nil } diff --git a/internal/pmtud/icmp/errors.go b/internal/pmtud/icmp/errors.go index 4a9c3e97..eb098221 100644 --- a/internal/pmtud/icmp/errors.go +++ b/internal/pmtud/icmp/errors.go @@ -10,9 +10,7 @@ import ( var ( ErrNotPermitted = errors.New("ICMP not permitted") - ErrDestinationUnreachable = errors.New("ICMP destination unreachable") - ErrCommunicationAdministrativelyProhibited = errors.New("communication administratively prohibited") - ErrBodyUnsupported = errors.New("ICMP body type is not supported") + errCommunicationAdministrativelyProhibited = errors.New("communication administratively prohibited") ErrMTUNotFound = errors.New("MTU not found") errTimeout = errors.New("operation timed out") ) diff --git a/internal/pmtud/icmp/icmp.go b/internal/pmtud/icmp/icmp.go index a713f77a..91a2be27 100644 --- a/internal/pmtud/icmp/icmp.go +++ b/internal/pmtud/icmp/icmp.go @@ -25,7 +25,7 @@ func PathMTUDiscover(ctx context.Context, ip netip.Addr, switch { case err == nil: return mtu, nil - case errors.Is(err, errTimeout) || errors.Is(err, ErrCommunicationAdministrativelyProhibited): // blackhole + case errors.Is(err, errTimeout) || errors.Is(err, errCommunicationAdministrativelyProhibited): // blackhole default: return 0, fmt.Errorf("finding IPv4 next hop MTU to %s: %w", ip, err) } diff --git a/internal/pmtud/icmp/ipv4.go b/internal/pmtud/icmp/ipv4.go index bc1fa186..2185698d 100644 --- a/internal/pmtud/icmp/ipv4.go +++ b/internal/pmtud/icmp/ipv4.go @@ -117,13 +117,10 @@ func findIPv4NextHopMTU(ctx context.Context, ip netip.Addr, case portUnreachable: // triggered by TCP or UDP from applications continue // ignore and wait for the next message case communicationAdministrativelyProhibitedCode: - return 0, fmt.Errorf("%w: %w (code %d)", - ErrDestinationUnreachable, - ErrCommunicationAdministrativelyProhibited, + return 0, fmt.Errorf("ICMP destination unreachable: %w (code %d)", errCommunicationAdministrativelyProhibited, inboundMessage.Code) default: - return 0, fmt.Errorf("%w: code %d", - ErrDestinationUnreachable, inboundMessage.Code) + return 0, fmt.Errorf("ICMP destination unreachable: code %d", inboundMessage.Code) } // See https://datatracker.ietf.org/doc/html/rfc1191#section-4 @@ -158,7 +155,7 @@ func findIPv4NextHopMTU(ctx context.Context, ip netip.Addr, inboundID, outboundID) continue default: - return 0, fmt.Errorf("%w: %T", ErrBodyUnsupported, typedBody) + return 0, fmt.Errorf("ICMP body type is not supported: %T", typedBody) } } } diff --git a/internal/pmtud/icmp/ipv6.go b/internal/pmtud/icmp/ipv6.go index c14f499c..cd480761 100644 --- a/internal/pmtud/icmp/ipv6.go +++ b/internal/pmtud/icmp/ipv6.go @@ -2,6 +2,7 @@ package icmp import ( "context" + "errors" "fmt" "net" "net/netip" @@ -115,7 +116,7 @@ func getIPv6PacketTooBig(ctx context.Context, ip netip.Addr, if err != nil { return 0, fmt.Errorf("checking invoking message id: %w", err) } else if idMatch { - return 0, fmt.Errorf("%w", ErrDestinationUnreachable) + return 0, errors.New("ICMP destination unreachable") } logger.Debug("discarding received ICMP destination unreachable reply with an unknown id") continue @@ -128,7 +129,7 @@ func getIPv6PacketTooBig(ctx context.Context, ip netip.Addr, inboundID, outboundID) continue default: - return 0, fmt.Errorf("%w: %T", ErrBodyUnsupported, typedBody) + return 0, fmt.Errorf("ICMP body type %T is not supported", typedBody) } } } diff --git a/internal/pmtud/icmp/multi.go b/internal/pmtud/icmp/multi.go index b4612d41..3b3903c6 100644 --- a/internal/pmtud/icmp/multi.go +++ b/internal/pmtud/icmp/multi.go @@ -71,7 +71,7 @@ func pmtudMultiSizes(ctx context.Context, ip netip.Addr, _, err = conn.WriteTo(encodedMessage, &net.IPAddr{IP: ip.AsSlice()}) if err != nil { if strings.HasSuffix(err.Error(), "sendto: operation not permitted") { - err = fmt.Errorf("%w", ErrNotPermitted) + err = ErrNotPermitted } return 0, fmt.Errorf("writing ICMP message: %w", err) } @@ -157,7 +157,7 @@ func collectReplies(conn net.PacketConn, ipVersion string, logger.Debugf("ignoring ICMP message (type: %d, code: %d)", message.Type, message.Code) continue default: - return fmt.Errorf("%w: %T", ErrBodyUnsupported, message.Body) + return fmt.Errorf("ICMP body type is not supported: %T", message.Body) } echoBody, _ := message.Body.(*icmp.Echo) @@ -183,8 +183,8 @@ func collectReplies(conn net.PacketConn, ipVersion string, ipPacketLength == conservativeReplyLength // Check the packet size is the same if the reply is not truncated if !truncated && sentBytes != ipPacketLength { - return fmt.Errorf("%w: sent %dB and received %dB", - ErrEchoDataMismatch, sentBytes, ipPacketLength) + return fmt.Errorf("ICMP data mismatch: sent %dB and received %dB", + sentBytes, ipPacketLength) } // Truncated reply or matching reply size tests[testIndex].ok = true diff --git a/internal/pmtud/ip/source.go b/internal/pmtud/ip/source.go index 8d1c5a2c..8fa0f3ce 100644 --- a/internal/pmtud/ip/source.go +++ b/internal/pmtud/ip/source.go @@ -29,7 +29,7 @@ func SrcAddr(dst netip.AddrPort, proto int) (src netip.AddrPort, cleanup func(), } var ( - errNoRoute = fmt.Errorf("no route to destination") + errNoRoute = errors.New("no route to destination") ErrNetworkUnreachable = errors.New("network unreachable") ) diff --git a/internal/pmtud/pmtud.go b/internal/pmtud/pmtud.go index 4505c0e9..4a714f24 100644 --- a/internal/pmtud/pmtud.go +++ b/internal/pmtud/pmtud.go @@ -13,11 +13,6 @@ import ( "github.com/qdm12/gluetun/internal/pmtud/tcp" ) -var ( - ErrICMPOkTCPFail = errors.New("PMTUD succeeded with ICMP but failed with TCP") - ErrICMPFailTCPFail = errors.New("PMTUD failed with both ICMP and TCP") -) - // PathMTUDiscover discovers the maximum MTU using both ICMP and TCP. // Multiple ICMP addresses and TCP addresses can be specified for redundancy. // ICMP PMTUD is run first. If successful, the range of possible MTU values to @@ -81,10 +76,10 @@ func PathMTUDiscover(ctx context.Context, icmpAddrs []netip.Addr, tcpAddrs []net } } if icmpSuccess { - return 0, fmt.Errorf("%w - discarding ICMP obtained MTU %d", - ErrICMPOkTCPFail, maxPossibleMTU) + return 0, fmt.Errorf("PMTUD succeeded with ICMP but failed with TCP "+ + "- discarding ICMP obtained MTU %d", maxPossibleMTU) } - return 0, fmt.Errorf("%w", ErrICMPFailTCPFail) + return 0, errors.New("PMTUD failed with both ICMP and TCP") } logger.Debugf("TCP path MTU discovery found maximum valid MTU %d", mtu) return mtu, nil diff --git a/internal/pmtud/tcp/helpers_test.go b/internal/pmtud/tcp/helpers_test.go index 24ed3e35..f97bf419 100644 --- a/internal/pmtud/tcp/helpers_test.go +++ b/internal/pmtud/tcp/helpers_test.go @@ -57,8 +57,6 @@ func (l *noopLogger) Warn(_ string) {} func (l *noopLogger) Warnf(_ string, _ ...any) {} func (l *noopLogger) Error(_ string) {} -var errRouteNotFound = errors.New("route not found") - func findLoopbackMTU(netlinker *netlink.NetLink) (mtu uint32, err error) { routes, err := netlinker.RouteList(netlink.FamilyV4) if err != nil { @@ -76,7 +74,7 @@ func findLoopbackMTU(netlinker *netlink.NetLink) (mtu uint32, err error) { return min(link.MTU, maxMTU), nil } } - return 0, fmt.Errorf("%w: no loopback route found", errRouteNotFound) + return 0, errors.New("route not found: no loopback route found") } func findDefaultRouteMTU(netlinker *netlink.NetLink) (mtu uint32, err error) { @@ -100,7 +98,7 @@ func findDefaultRouteMTU(netlinker *netlink.NetLink) (mtu uint32, err error) { } } if mtu == 0 { - return 0, fmt.Errorf("%w: no default route found", errRouteNotFound) + return 0, errors.New("route not found: no default route found") } return mtu, nil } diff --git a/internal/pmtud/tcp/mss.go b/internal/pmtud/tcp/mss.go index bedc2a5d..e149d408 100644 --- a/internal/pmtud/tcp/mss.go +++ b/internal/pmtud/tcp/mss.go @@ -12,8 +12,6 @@ import ( "github.com/qdm12/gluetun/internal/pmtud/ip" ) -var errTCPServersUnreachable = errors.New("all TCP servers are unreachable") - // findHighestMSSDestination finds the destination with the highest // MSS amongst the provided destinations. func findHighestMSSDestination(ctx context.Context, familyToFD map[int]fileDescriptor, @@ -68,7 +66,7 @@ func findHighestMSSDestination(ctx context.Context, familyToFD map[int]fileDescr } if mss == 0 { // no MSS found for any destination - return netip.AddrPort{}, 0, fmt.Errorf("%w (%d servers)", errTCPServersUnreachable, len(dsts)) + return netip.AddrPort{}, 0, fmt.Errorf("all %d TCP servers are unreachable", len(dsts)) } maxPossibleMTU = ip.HeaderLength(dst.Addr().Is4()) + constants.BaseTCPHeaderLength + mss @@ -77,8 +75,6 @@ func findHighestMSSDestination(ctx context.Context, familyToFD map[int]fileDescr return dst, mss, nil } -var errMSSNotFound = errors.New("MSS option not found in reply") - func findMSS(ctx context.Context, fd fileDescriptor, dst netip.AddrPort, excludeMark int, tracker *tracker, firewall Firewall, logger Logger) ( mss uint32, err error, @@ -132,11 +128,12 @@ func findMSS(ctx context.Context, fd fileDescriptor, dst netip.AddrPort, case err != nil: return 0, fmt.Errorf("parsing reply TCP header: %w", err) case replyHeader.typ != packetTypeSYNACK: - return 0, fmt.Errorf("%w: unexpected packet type %s", errTCPPacketNotSynAck, replyHeader.typ) + return 0, fmt.Errorf("TCP packet is not a SYN-ACK: unexpected packet type %s", replyHeader.typ) case replyHeader.ack != synSeq+1: - return 0, fmt.Errorf("%w: expected %d, got %d", errTCPSynAckAckMismatch, synSeq+1, replyHeader.ack) + return 0, fmt.Errorf("TCP SYN-ACK ACK number %d does not match expected value %d", + replyHeader.ack, synSeq+1) case replyHeader.options.mss == 0: - return 0, fmt.Errorf("%w: MSS option not found in reply", errMSSNotFound) + return 0, errors.New("MSS option not found in reply") } err = sendRST(fd, src, dst, replyHeader.ack) diff --git a/internal/pmtud/tcp/multi.go b/internal/pmtud/tcp/multi.go index f4f6b8a2..d291e565 100644 --- a/internal/pmtud/tcp/multi.go +++ b/internal/pmtud/tcp/multi.go @@ -12,11 +12,6 @@ import ( "github.com/qdm12/gluetun/internal/pmtud/test" ) -var ( - ErrMTUNotFound = errors.New("MTU not found") - ErrMSSTooSmall = errors.New("TCP MSS is too small to find the MTU") -) - type testUnit struct { mtu uint32 ok bool @@ -178,5 +173,5 @@ func pathMTUDiscover(ctx context.Context, fd fileDescriptor, } } - return 0, fmt.Errorf("%w: your connection might not be working at all", ErrMTUNotFound) + return 0, errors.New("MTU not found: your connection might not be working at all") } diff --git a/internal/pmtud/tcp/tcp.go b/internal/pmtud/tcp/tcp.go index d5b585a2..00638a42 100644 --- a/internal/pmtud/tcp/tcp.go +++ b/internal/pmtud/tcp/tcp.go @@ -75,13 +75,6 @@ func startRawSocket(family, excludeMark int) (fd fileDescriptor, stop func(), er return fileDescriptor(fdPlatform), stop, nil } -var ( - errTCPPacketNotSynAck = errors.New("TCP packet is not a SYN-ACK") - errTCPSynAckAckMismatch = errors.New("TCP SYN-ACK ACK number does not match expected value") - errFinalPacketTypeUnexpected = errors.New("final TCP packet type is unexpected") - errTCPPacketLost = errors.New("TCP packet was lost") -) - // Craft and send a raw TCP packet to test the MTU. // It expects either an RST reply (if no server is listening) // or a SYN-ACK/ACK reply (if a server is listening). @@ -142,9 +135,10 @@ func runTest(ctx context.Context, dst netip.AddrPort, mtu uint32, // server actively closed the connection, try sending a SYN with data return handleRSTReply(ctx, fd, ch, src, dst, mtu) case firstReplyHeader.typ != packetTypeSYNACK: - return fmt.Errorf("%w: unexpected packet type %s", errTCPPacketNotSynAck, firstReplyHeader.typ) + return fmt.Errorf("TCP packet is not a SYN-ACK: unexpected packet type %s", firstReplyHeader.typ) case firstReplyHeader.ack != synSeq+1: - return fmt.Errorf("%w: expected %d, got %d", errTCPSynAckAckMismatch, synSeq+1, firstReplyHeader.ack) + return fmt.Errorf("TCP SYN-ACK ACK number does not match expected value: "+ + "expected %d, got %d", synSeq+1, firstReplyHeader.ack) } if firstReplyHeader.options.mss != 0 { @@ -191,15 +185,13 @@ func runTest(ctx context.Context, dst netip.AddrPort, mtu uint32, } return nil case packetTypeSYNACK: // server never received our MTU-test ACK packet - return fmt.Errorf("%w: server responded with second SYN-ACK packet", errTCPPacketLost) + return errors.New("TCP packet was lost: server responded with second SYN-ACK packet") default: _ = sendRST(fd, src, dst, finalPacketHeader.ack) - return fmt.Errorf("%w: %s", errFinalPacketTypeUnexpected, finalPacketHeader.typ) + return fmt.Errorf("final TCP packet type is unexpected: %s", finalPacketHeader.typ) } } -var errTCPPacketNotRST = errors.New("TCP packet is not an RST") - func handleRSTReply(ctx context.Context, fd fileDescriptor, ch <-chan []byte, src, dst netip.AddrPort, mtu uint32, ) error { @@ -223,7 +215,7 @@ func handleRSTReply(ctx context.Context, fd fileDescriptor, ch <-chan []byte, return fmt.Errorf("parsing reply TCP header: %w", err) } else if replyPacketHeader.typ != packetTypeRST && replyPacketHeader.typ != packetTypeRSTACK { - return fmt.Errorf("%w: %s", errTCPPacketNotRST, replyPacketHeader.typ) + return fmt.Errorf("TCP packet is not an RST: %s", replyPacketHeader.typ) } return nil } diff --git a/internal/pmtud/tcp/tcpheader.go b/internal/pmtud/tcp/tcpheader.go index 83a4ae38..aa1ab70f 100644 --- a/internal/pmtud/tcp/tcpheader.go +++ b/internal/pmtud/tcp/tcpheader.go @@ -2,7 +2,6 @@ package tcp import ( "encoding/binary" - "errors" "fmt" "github.com/qdm12/gluetun/internal/pmtud/constants" @@ -120,17 +119,11 @@ type tcpHeader struct { options options } -var ( - errTCPHeaderTooShort = errors.New("TCP header is too short") - errTCPHeaderDataOffset = errors.New("TCP header data offset is invalid") - errTCPPacketTypeUnknown = errors.New("TCP packet type is unknown") -) - // parseTCPHeader parses the TCP header from b. // b should be the entire TCP packet bytes. func parseTCPHeader(b []byte) (header tcpHeader, err error) { if len(b) < int(constants.BaseTCPHeaderLength) { - return tcpHeader{}, fmt.Errorf("%w: %d bytes", errTCPHeaderTooShort, len(b)) + return tcpHeader{}, fmt.Errorf("TCP header is too short: %d bytes", len(b)) } header.srcPort = binary.BigEndian.Uint16(b[0:2]) @@ -146,11 +139,11 @@ func parseTCPHeader(b []byte) (header tcpHeader, err error) { switch { case uint32(header.dataOffset) < constants.BaseTCPHeaderLength: - return tcpHeader{}, fmt.Errorf("%w: data offset is %d bytes, expected at least %d bytes", - errTCPHeaderDataOffset, header.dataOffset, constants.BaseTCPHeaderLength) + return tcpHeader{}, fmt.Errorf("TCP header data offset is invalid: "+ + "data offset is %d bytes, expected at least %d bytes", header.dataOffset, constants.BaseTCPHeaderLength) case int(header.dataOffset) > len(b): - return tcpHeader{}, fmt.Errorf("%w: data offset is %d bytes, but packet is only %d bytes", - errTCPHeaderDataOffset, header.dataOffset, len(b)) + return tcpHeader{}, fmt.Errorf("TCP header data offset is invalid: "+ + "data offset is %d bytes, but packet is only %d bytes", header.dataOffset, len(b)) } if uint32(header.dataOffset) > constants.BaseTCPHeaderLength { @@ -186,7 +179,7 @@ func parseTCPHeader(b []byte) (header tcpHeader, err error) { case flags&ackFlag != 0: header.typ = packetTypeACK default: - return tcpHeader{}, fmt.Errorf("%w: flags are 0x%02x", errTCPPacketTypeUnknown, flags) + return tcpHeader{}, fmt.Errorf("TCP packet type is unknown: flags are 0x%02x", flags) } header.seq = binary.BigEndian.Uint32(b[4:8]) @@ -206,15 +199,6 @@ type optionTimestamps struct { echo uint32 } -var ( - errTCPOptionLengthTruncated = errors.New("TCP option length is truncated") - ErrTCPOptionLengthInvalid = errors.New("TCP option length is invalid") - ErrTCPOptionMSSInvalid = errors.New("TCP option MSS value is invalid") - ErrTCPOptionWindowScaleInvalid = errors.New("TCP option Window Scale value is invalid") - ErrTCPOptionTimestampsInvalid = errors.New("TCP option Timestamps value is invalid") - errTCPOptionTypeUnknown = errors.New("TCP option type is unknown") -) - func parseTCPOptions(b []byte) (parsed options, err error) { i := 0 for i < len(b) { @@ -232,7 +216,7 @@ func parseTCPOptions(b []byte) (parsed options, err error) { // Handle TLV (Type-Length-Value) options if i+1 >= len(b) { // This should not happen for DF packets. - return options{}, fmt.Errorf("%w: at offset %d", errTCPOptionLengthTruncated, i) + return options{}, fmt.Errorf("TCP option length is truncated: at offset %d", i) } length := int(b[i+1]) @@ -240,11 +224,11 @@ func parseTCPOptions(b []byte) (parsed options, err error) { maxLength := len(b) - i switch { case length < minLength: - return options{}, fmt.Errorf("%w: type %d at offset %d has length %d < %d", - ErrTCPOptionLengthInvalid, optionType, i, length, minLength) + return options{}, fmt.Errorf("TCP option length is invalid: "+ + "type %d at offset %d has length %d < %d", optionType, i, length, minLength) case length > maxLength: - return options{}, fmt.Errorf("%w: type %d at offset %d has length %d > %d", - ErrTCPOptionLengthInvalid, optionType, i, length, maxLength) + return options{}, fmt.Errorf("TCP option length is invalid: "+ + "type %d at offset %d has length %d > %d", optionType, i, length, maxLength) } data := b[i+2 : i+length] @@ -259,15 +243,15 @@ func parseTCPOptions(b []byte) (parsed options, err error) { case optionTypeMSS: const expectedLength = 4 if length != expectedLength { - return options{}, fmt.Errorf("%w: MSS option at offset %d has length %d, expected %d", - ErrTCPOptionMSSInvalid, i, length, expectedLength) + return options{}, fmt.Errorf("TCP option MSS value is invalid: "+ + "MSS option at offset %d has length %d, expected %d", i, length, expectedLength) } parsed.mss = uint32(binary.BigEndian.Uint16(data)) case optionTypeWindowScale: const expectedLength = 3 if length != expectedLength { - return options{}, fmt.Errorf("%w: window scale option at offset %d has length %d, expected %d", - ErrTCPOptionWindowScaleInvalid, i, length, expectedLength) + return options{}, fmt.Errorf("TCP option Window Scale value is invalid: "+ + "window scale option at offset %d has length %d, expected %d", i, length, expectedLength) } windowScale := data[0] parsed.windowScale = &windowScale @@ -276,15 +260,15 @@ func parseTCPOptions(b []byte) (parsed options, err error) { case optionTypeTimestamps: const expectedLength = 10 if length != expectedLength { - return options{}, fmt.Errorf("%w: timestamps option at offset %d has length %d, expected %d", - ErrTCPOptionTimestampsInvalid, i, length, expectedLength) + return options{}, fmt.Errorf("TCP option Timestamps value is invalid: "+ + "timestamps option at offset %d has length %d, expected %d", i, length, expectedLength) } parsed.timestamps = &optionTimestamps{ value: binary.BigEndian.Uint32(data[:4]), echo: binary.BigEndian.Uint32(data[4:]), } default: - return options{}, fmt.Errorf("%w: type %d", errTCPOptionTypeUnknown, optionType) + return options{}, fmt.Errorf("TCP option type is unknown: type %d", optionType) } i += length diff --git a/internal/portforward/loop.go b/internal/portforward/loop.go index 93e114bb..fd0a7d4e 100644 --- a/internal/portforward/loop.go +++ b/internal/portforward/loop.go @@ -177,11 +177,9 @@ func (l *Loop) GetPortsForwarded() (ports []uint16) { return l.service.GetPortsForwarded() } -var ErrServiceNotStarted = errors.New("port forwarding service not started") - func (l *Loop) SetPortsForwarded(ports []uint16) (err error) { if l.service == nil { - return fmt.Errorf("%w", ErrServiceNotStarted) + return errors.New("port forwarding service not started") } return l.service.SetPortsForwarded(l.runCtx, ports) diff --git a/internal/portforward/service/settings.go b/internal/portforward/service/settings.go index 5ec89ae9..df1645eb 100644 --- a/internal/portforward/service/settings.go +++ b/internal/portforward/service/settings.go @@ -55,23 +55,10 @@ func (s *Settings) OverrideWith(update Settings) { s.Password = gosettings.OverrideWithComparable(s.Password, update.Password) } -var ( - ErrPortForwarderNotSet = errors.New("port forwarder not set") - ErrServerNameNotSet = errors.New("server name not set") - ErrUsernameNotSet = errors.New("username not set") - ErrPasswordNotSet = errors.New("password not set") - ErrFilepathNotSet = errors.New("file path not set") - ErrInterfaceNotSet = errors.New("interface not set") - ErrPortsCountZero = errors.New("ports count cannot be zero") - ErrPortsCountTooHigh = errors.New("ports count too high") - ErrListeningPortsLen = errors.New("listening ports length must be equal to ports count") - ErrListeningPortZero = errors.New("listening port cannot be 0") -) - func (s *Settings) Validate(forStartup bool) (err error) { // Minimal validation if s.Filepath == "" { - return fmt.Errorf("%w", ErrFilepathNotSet) + return errors.New("file path not set") } if !forStartup { @@ -83,41 +70,42 @@ func (s *Settings) Validate(forStartup bool) (err error) { // Startup validation requires additional fields set. switch { case s.PortForwarder == nil: - return fmt.Errorf("%w", ErrPortForwarderNotSet) + return errors.New("port forwarder not set") case s.Interface == "": - return fmt.Errorf("%w", ErrInterfaceNotSet) + return errors.New("interface not set") case s.PortsCount == 0: - return fmt.Errorf("%w", ErrPortsCountZero) + return errors.New("ports count cannot be zero") } switch s.PortForwarder.Name() { case providers.PrivateInternetAccess: switch { case s.ServerName == "": - return fmt.Errorf("%w", ErrServerNameNotSet) + return errors.New("server name not set") case s.Username == "": - return fmt.Errorf("%w", ErrUsernameNotSet) + return errors.New("username not set") case s.Password == "": - return fmt.Errorf("%w", ErrPasswordNotSet) + return errors.New("password not set") } case providers.Protonvpn: const maxPortsCount = 4 if s.PortsCount > maxPortsCount { - return fmt.Errorf("%w: %d > %d", ErrPortsCountTooHigh, s.PortsCount, maxPortsCount) + return fmt.Errorf("ports count too high: %d > %d", s.PortsCount, maxPortsCount) } default: const maxPortsCount = 1 if s.PortsCount > maxPortsCount { - return fmt.Errorf("%w: %d > %d", ErrPortsCountTooHigh, s.PortsCount, maxPortsCount) + return fmt.Errorf("ports count too high: %d > %d", s.PortsCount, maxPortsCount) } } if !slices.Equal(s.ListeningPorts, []uint16{0}) { switch { case len(s.ListeningPorts) != int(s.PortsCount): - return fmt.Errorf("%w: %d != %d", ErrListeningPortsLen, len(s.ListeningPorts), s.PortsCount) + return fmt.Errorf("listening ports length must be equal to ports count: %d != %d", + len(s.ListeningPorts), s.PortsCount) case slices.Contains(s.ListeningPorts, 0): - return fmt.Errorf("%w: in %v", ErrListeningPortZero, s.ListeningPorts) + return fmt.Errorf("listening port cannot be 0: in %v", s.ListeningPorts) } } diff --git a/internal/pprof/server_test.go b/internal/pprof/server_test.go index 0a2d43bc..83d3fb9b 100644 --- a/internal/pprof/server_test.go +++ b/internal/pprof/server_test.go @@ -120,7 +120,7 @@ func Test_Server_BadSettings(t *testing.T) { server, err := New(settings) assert.Nil(t, server) - assert.ErrorIs(t, err, ErrBlockProfileRateNegative) + assert.ErrorContains(t, err, "block profile rate cannot be negative") const expectedErrMessage = "pprof settings failed validation: block profile rate cannot be negative" assert.EqualError(t, err, expectedErrMessage) } diff --git a/internal/pprof/settings.go b/internal/pprof/settings.go index 8f7cef37..22f8cf07 100644 --- a/internal/pprof/settings.go +++ b/internal/pprof/settings.go @@ -2,7 +2,6 @@ package pprof import ( "errors" - "fmt" "time" "github.com/qdm12/gluetun/internal/httpserver" @@ -51,18 +50,13 @@ func (s *Settings) OverrideWith(other Settings) { s.HTTPServer.OverrideWith(other.HTTPServer) } -var ( - ErrBlockProfileRateNegative = errors.New("block profile rate cannot be negative") - ErrMutexProfileRateNegative = errors.New("mutex profile rate cannot be negative") -) - func (s Settings) Validate() (err error) { if *s.BlockProfileRate < 0 { - return fmt.Errorf("%w", ErrBlockProfileRateNegative) + return errors.New("block profile rate cannot be negative") } if *s.MutexProfileRate < 0 { - return fmt.Errorf("%w", ErrMutexProfileRateNegative) + return errors.New("mutex profile rate cannot be negative") } return s.HTTPServer.Validate() diff --git a/internal/pprof/settings_test.go b/internal/pprof/settings_test.go index d3155531..53aa22a7 100644 --- a/internal/pprof/settings_test.go +++ b/internal/pprof/settings_test.go @@ -6,7 +6,6 @@ import ( "time" "github.com/qdm12/gluetun/internal/httpserver" - "github.com/qdm12/gosettings/validate" "github.com/stretchr/testify/assert" ) @@ -195,7 +194,6 @@ func Test_Settings_Validate(t *testing.T) { testCases := map[string]struct { settings Settings - errWrapped error errMessage string }{ "negative block profile rate": { @@ -203,16 +201,14 @@ func Test_Settings_Validate(t *testing.T) { BlockProfileRate: intPtr(-1), MutexProfileRate: intPtr(0), }, - errWrapped: ErrBlockProfileRateNegative, - errMessage: ErrBlockProfileRateNegative.Error(), + errMessage: "block profile rate cannot be negative", }, "negative mutex profile rate": { settings: Settings{ BlockProfileRate: intPtr(0), MutexProfileRate: intPtr(-1), }, - errWrapped: ErrMutexProfileRateNegative, - errMessage: ErrMutexProfileRateNegative.Error(), + errMessage: "mutex profile rate cannot be negative", }, "http server validation error": { settings: Settings{ @@ -222,7 +218,6 @@ func Test_Settings_Validate(t *testing.T) { Address: ":x", }, }, - errWrapped: validate.ErrPortNotAnInteger, errMessage: "port value is not an integer: x", }, "valid settings": { @@ -247,9 +242,10 @@ func Test_Settings_Validate(t *testing.T) { err := testCase.settings.Validate() - assert.ErrorIs(t, err, testCase.errWrapped) if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/provider/airvpn/updater/api.go b/internal/provider/airvpn/updater/api.go index e5596d8f..39635a6d 100644 --- a/internal/provider/airvpn/updater/api.go +++ b/internal/provider/airvpn/updater/api.go @@ -6,8 +6,6 @@ import ( "fmt" "net/http" "net/netip" - - "github.com/qdm12/gluetun/internal/provider/common" ) type apiData struct { @@ -48,8 +46,8 @@ func fetchAPI(ctx context.Context, client *http.Client) ( if response.StatusCode != http.StatusOK { _ = response.Body.Close() - return data, fmt.Errorf("%w: %d %s", - common.ErrHTTPStatusCodeNotOK, response.StatusCode, response.Status) + return data, fmt.Errorf("HTTP status code not OK: %d %s", + response.StatusCode, response.Status) } decoder := json.NewDecoder(response.Body) diff --git a/internal/provider/common/portforward.go b/internal/provider/common/portforward.go deleted file mode 100644 index db1385af..00000000 --- a/internal/provider/common/portforward.go +++ /dev/null @@ -1,5 +0,0 @@ -package common - -import "errors" - -var ErrPortForwardNotSupported = errors.New("port forwarding not supported") diff --git a/internal/provider/common/updater.go b/internal/provider/common/updater.go index c86c98b5..3cc72391 100644 --- a/internal/provider/common/updater.go +++ b/internal/provider/common/updater.go @@ -10,10 +10,8 @@ import ( ) var ( - ErrNotEnoughServers = errors.New("not enough servers found") - ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK") - ErrIPFetcherUnsupported = errors.New("IP fetcher not supported") - ErrCredentialsMissing = errors.New("credentials missing") + ErrNotEnoughServers = errors.New("not enough servers found") + ErrCredentialsMissing = errors.New("credentials are missing") ) type Fetcher interface { diff --git a/internal/provider/custom/connection.go b/internal/provider/custom/connection.go index cf06e0bd..820a8bab 100644 --- a/internal/provider/custom/connection.go +++ b/internal/provider/custom/connection.go @@ -1,7 +1,6 @@ package custom import ( - "errors" "fmt" "github.com/qdm12/gluetun/internal/configuration/settings" @@ -10,8 +9,6 @@ import ( "github.com/qdm12/gluetun/internal/models" ) -var ErrVPNTypeNotSupported = errors.New("VPN type not supported for custom provider") - // GetConnection gets the connection from the OpenVPN configuration file. func (p *Provider) GetConnection(selection settings.ServerSelection, _ bool) ( connection models.Connection, err error, @@ -22,7 +19,7 @@ func (p *Provider) GetConnection(selection settings.ServerSelection, _ bool) ( case vpn.Wireguard, vpn.AmneziaWg: return getWireguardConnection(selection), nil default: - return connection, fmt.Errorf("%w: %s", ErrVPNTypeNotSupported, selection.VPN) + return connection, fmt.Errorf("VPN type not supported for custom provider: %s", selection.VPN) } } diff --git a/internal/provider/custom/openvpnconf.go b/internal/provider/custom/openvpnconf.go index 934996ac..dae0a02f 100644 --- a/internal/provider/custom/openvpnconf.go +++ b/internal/provider/custom/openvpnconf.go @@ -1,7 +1,6 @@ package custom import ( - "errors" "fmt" "strconv" "strings" @@ -12,8 +11,6 @@ import ( "github.com/qdm12/gluetun/internal/provider/utils" ) -var ErrExtractData = errors.New("failed extracting information from custom configuration file") - func (p *Provider) OpenVPNConfig(connection models.Connection, settings settings.OpenVPN, ipv6Supported bool, ) (lines []string) { diff --git a/internal/provider/example/updater/api.go b/internal/provider/example/updater/api.go index 4087db30..3d89df09 100644 --- a/internal/provider/example/updater/api.go +++ b/internal/provider/example/updater/api.go @@ -3,13 +3,10 @@ package updater import ( "context" "encoding/json" - "errors" "fmt" "net/http" ) -var errHTTPStatusCodeNotOK = errors.New("HTTP status code not OK") - type apiData struct { Servers []apiServer `json:"servers"` } @@ -42,8 +39,8 @@ func fetchAPI(ctx context.Context, client *http.Client) ( if response.StatusCode != http.StatusOK { _ = response.Body.Close() - return data, fmt.Errorf("%w: %d %s", - errHTTPStatusCodeNotOK, response.StatusCode, response.Status) + return data, fmt.Errorf("HTTP status code not OK: %d %s", + response.StatusCode, response.Status) } decoder := json.NewDecoder(response.Body) diff --git a/internal/provider/expressvpn/connection_test.go b/internal/provider/expressvpn/connection_test.go index fdf45ec0..42559dc3 100644 --- a/internal/provider/expressvpn/connection_test.go +++ b/internal/provider/expressvpn/connection_test.go @@ -21,21 +21,17 @@ func Test_Provider_GetConnection(t *testing.T) { const provider = providers.Expressvpn - errTest := errors.New("test error") - testCases := map[string]struct { filteredServers []models.Server storageErr error selection settings.ServerSelection ipv6Supported bool connection models.Connection - errWrapped error errMessage string panicMessage string }{ "error": { - storageErr: errTest, - errWrapped: errTest, + storageErr: errors.New("test error"), errMessage: "filtering servers: test error", }, "default OpenVPN TCP port": { @@ -100,9 +96,10 @@ func Test_Provider_GetConnection(t *testing.T) { connection, err := provider.GetConnection(testCase.selection, testCase.ipv6Supported) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } assert.Equal(t, testCase.connection, connection) diff --git a/internal/provider/fastestvpn/updater/api.go b/internal/provider/fastestvpn/updater/api.go index 6776d120..ca832cbd 100644 --- a/internal/provider/fastestvpn/updater/api.go +++ b/internal/provider/fastestvpn/updater/api.go @@ -3,14 +3,11 @@ package updater import ( "bytes" "context" - "errors" "fmt" "io" "net/http" "net/url" "strings" - - "github.com/qdm12/gluetun/internal/provider/common" ) type apiServer struct { @@ -19,8 +16,6 @@ type apiServer struct { hostname string } -var ErrDataMalformed = errors.New("data is malformed") - const apiURL = "https://support.fastestvpn.com/wp-admin/admin-ajax.php" // The API URL and requests are shamelessly taken from network operations @@ -49,7 +44,7 @@ func fetchAPIServers(ctx context.Context, client *http.Client, protocol string) if response.StatusCode != http.StatusOK { _ = response.Body.Close() - return nil, fmt.Errorf("%w: %d", common.ErrHTTPStatusCodeNotOK, response.StatusCode) + return nil, fmt.Errorf("HTTP status code not OK: %d", response.StatusCode) } data, err := io.ReadAll(response.Body) @@ -79,8 +74,8 @@ func fetchAPIServers(ctx context.Context, client *http.Client, protocol string) for i := range numberOfTDBlocks { tdBlock := getNextTDBlock(trBlock) if tdBlock == nil { - return nil, fmt.Errorf("%w: expected 3 blocks in block %q", - ErrDataMalformed, string(trBlock)) + return nil, fmt.Errorf("data is malformed: expected 3 blocks in block %q", + string(trBlock)) } trBlock = trBlock[len(tdBlock):] diff --git a/internal/provider/fastestvpn/updater/api_test.go b/internal/provider/fastestvpn/updater/api_test.go index 9c31ed86..81918c2b 100644 --- a/internal/provider/fastestvpn/updater/api_test.go +++ b/internal/provider/fastestvpn/updater/api_test.go @@ -8,7 +8,6 @@ import ( "strings" "testing" - "github.com/qdm12/gluetun/internal/provider/common" "github.com/stretchr/testify/assert" ) @@ -21,8 +20,6 @@ func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { func Test_fechAPIServers(t *testing.T) { t.Parallel() - errTest := errors.New("test error") - testCases := map[string]struct { ctx context.Context protocol string @@ -31,7 +28,6 @@ func Test_fechAPIServers(t *testing.T) { responseBody io.ReadCloser transportErr error servers []apiServer - errWrapped error errMessage string }{ "transport_error": { @@ -39,8 +35,7 @@ func Test_fechAPIServers(t *testing.T) { protocol: "tcp", requestBody: "action=vpn_servers&protocol=tcp", responseStatus: http.StatusOK, - transportErr: errTest, - errWrapped: errTest, + transportErr: errors.New("test error"), errMessage: `sending request: Post ` + `"https://support.fastestvpn.com/wp-admin/admin-ajax.php": ` + `test error`, @@ -50,7 +45,6 @@ func Test_fechAPIServers(t *testing.T) { protocol: "tcp", requestBody: "action=vpn_servers&protocol=tcp", responseStatus: http.StatusNotFound, - errWrapped: common.ErrHTTPStatusCodeNotOK, errMessage: "HTTP status code not OK: 404", }, "empty_data": { @@ -110,9 +104,10 @@ func Test_fechAPIServers(t *testing.T) { entries, err := fetchAPIServers(testCase.ctx, client, testCase.protocol) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } assert.Equal(t, testCase.servers, entries) }) diff --git a/internal/provider/giganews/updater/filename.go b/internal/provider/giganews/updater/filename.go index a2fd3f6e..4f017431 100644 --- a/internal/provider/giganews/updater/filename.go +++ b/internal/provider/giganews/updater/filename.go @@ -1,19 +1,16 @@ package updater import ( - "errors" "fmt" "strings" ) -var errNotOvpnExt = errors.New("filename does not have the openvpn file extension") - func parseFilename(fileName string) ( region string, err error, ) { const suffix = ".ovpn" if !strings.HasSuffix(fileName, suffix) { - return "", fmt.Errorf("%w: %s", errNotOvpnExt, fileName) + return "", fmt.Errorf("filename does not have the openvpn file extension: %s", fileName) } region = strings.TrimSuffix(fileName, suffix) diff --git a/internal/provider/ipvanish/updater/filename.go b/internal/provider/ipvanish/updater/filename.go index f95e386f..1cada331 100644 --- a/internal/provider/ipvanish/updater/filename.go +++ b/internal/provider/ipvanish/updater/filename.go @@ -1,7 +1,6 @@ package updater import ( - "errors" "fmt" "strings" @@ -9,8 +8,6 @@ import ( "golang.org/x/text/cases" ) -var errCountryCodeUnknown = errors.New("country code is unknown") - func parseFilename(fileName, hostname string, titleCaser cases.Caser) ( country, city string, err error, ) { @@ -28,7 +25,7 @@ func parseFilename(fileName, hostname string, titleCaser cases.Caser) ( countryCode := strings.ToLower(parts[0]) country, ok := countryCodes[countryCode] if !ok { - return "", "", fmt.Errorf("%w: %s", errCountryCodeUnknown, countryCode) + return "", "", fmt.Errorf("country code is unknown: %s", countryCode) } country = titleCaser.String(country) diff --git a/internal/provider/ivpn/connection_test.go b/internal/provider/ivpn/connection_test.go index f854b7ee..a952c663 100644 --- a/internal/provider/ivpn/connection_test.go +++ b/internal/provider/ivpn/connection_test.go @@ -22,20 +22,16 @@ func Test_Provider_GetConnection(t *testing.T) { const provider = providers.Ivpn - errTest := errors.New("test error") - testCases := map[string]struct { filteredServers []models.Server storageErr error selection settings.ServerSelection ipv6Supported bool connection models.Connection - errWrapped error errMessage string }{ "error": { - storageErr: errTest, - errWrapped: errTest, + storageErr: errors.New("test error"), errMessage: "filtering servers: test error", }, "default OpenVPN TCP port": { @@ -104,9 +100,10 @@ func Test_Provider_GetConnection(t *testing.T) { connection, err := provider.GetConnection(testCase.selection, testCase.ipv6Supported) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } assert.Equal(t, testCase.connection, connection) diff --git a/internal/provider/ivpn/updater/api.go b/internal/provider/ivpn/updater/api.go index 54d8d5d9..a45f2685 100644 --- a/internal/provider/ivpn/updater/api.go +++ b/internal/provider/ivpn/updater/api.go @@ -3,13 +3,10 @@ package updater import ( "context" "encoding/json" - "errors" "fmt" "net/http" ) -var errHTTPStatusCodeNotOK = errors.New("HTTP status code not OK") - type apiData struct { Servers []apiServer `json:"servers"` } @@ -45,8 +42,8 @@ func fetchAPI(ctx context.Context, client *http.Client) ( if response.StatusCode != http.StatusOK { _ = response.Body.Close() - return data, fmt.Errorf("%w: %d %s", - errHTTPStatusCodeNotOK, response.StatusCode, response.Status) + return data, fmt.Errorf("HTTP status code not OK: %d %s", + response.StatusCode, response.Status) } decoder := json.NewDecoder(response.Body) diff --git a/internal/provider/mullvad/connection_test.go b/internal/provider/mullvad/connection_test.go index beed5d74..5b821c01 100644 --- a/internal/provider/mullvad/connection_test.go +++ b/internal/provider/mullvad/connection_test.go @@ -22,20 +22,16 @@ func Test_Provider_GetConnection(t *testing.T) { const provider = providers.Mullvad - errTest := errors.New("test error") - testCases := map[string]struct { filteredServers []models.Server storageErr error selection settings.ServerSelection ipv6Supported bool connection models.Connection - errWrapped error errMessage string }{ "error": { - storageErr: errTest, - errWrapped: errTest, + storageErr: errors.New("test error"), errMessage: "filtering servers: test error", }, "default Wireguard port": { @@ -70,9 +66,10 @@ func Test_Provider_GetConnection(t *testing.T) { connection, err := provider.GetConnection(testCase.selection, testCase.ipv6Supported) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } assert.Equal(t, testCase.connection, connection) diff --git a/internal/provider/mullvad/updater/api.go b/internal/provider/mullvad/updater/api.go index a8631c26..ee41ed8f 100644 --- a/internal/provider/mullvad/updater/api.go +++ b/internal/provider/mullvad/updater/api.go @@ -3,16 +3,10 @@ package updater import ( "context" "encoding/json" - "errors" "fmt" "net/http" ) -var ( - ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK") - ErrDecodeResponseBody = errors.New("failed decoding response body") -) - type serverData struct { Hostname string `json:"hostname"` Country string `json:"country_name"` @@ -41,13 +35,12 @@ func fetchAPI(ctx context.Context, client *http.Client) (data []serverData, err defer response.Body.Close() if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%w: %d %s", ErrHTTPStatusCodeNotOK, - response.StatusCode, response.Status) + return nil, fmt.Errorf("HTTP status code not OK: %d %s", response.StatusCode, response.Status) } decoder := json.NewDecoder(response.Body) if err := decoder.Decode(&data); err != nil { - return nil, fmt.Errorf("%w: %s", ErrDecodeResponseBody, err) + return nil, fmt.Errorf("failed decoding response body: %s", err) } if err := response.Body.Close(); err != nil { diff --git a/internal/provider/mullvad/updater/hosttoserver.go b/internal/provider/mullvad/updater/hosttoserver.go index 33e98f20..eb616b8a 100644 --- a/internal/provider/mullvad/updater/hosttoserver.go +++ b/internal/provider/mullvad/updater/hosttoserver.go @@ -12,20 +12,13 @@ import ( type hostToServer map[string]models.Server -var ( - ErrNoIP = errors.New("no IP address for VPN server") - ErrIPIsNotV4 = errors.New("IP address is not IPv4") - ErrIPIsNotV6 = errors.New("IP address is not IPv6") - ErrVPNTypeNotSupported = errors.New("VPN type not supported") -) - func (hts hostToServer) add(data serverData) (err error) { if !data.Active { return nil } if data.IPv4 == "" && data.IPv6 == "" { - return fmt.Errorf("%w", ErrNoIP) + return errors.New("no IP address for VPN server") } server, ok := hts[data.Hostname] @@ -40,7 +33,7 @@ func (hts hostToServer) add(data serverData) (err error) { // ignore bridge servers return nil default: - return fmt.Errorf("%w: %s", ErrVPNTypeNotSupported, data.Type) + return fmt.Errorf("VPN type not supported: %s", data.Type) } if data.IPv4 != "" { @@ -48,7 +41,7 @@ func (hts hostToServer) add(data serverData) (err error) { if err != nil { return fmt.Errorf("parsing IPv4 address: %w", err) } else if !ipv4.Is4() { - return fmt.Errorf("%w: %s", ErrIPIsNotV4, data.IPv4) + return fmt.Errorf("IP address is not IPv4: %s", data.IPv4) } server.IPs = append(server.IPs, ipv4) } @@ -58,7 +51,7 @@ func (hts hostToServer) add(data serverData) (err error) { if err != nil { return fmt.Errorf("parsing IPv6 address: %w", err) } else if !ipv6.Is6() { - return fmt.Errorf("%w: %s", ErrIPIsNotV6, data.IPv6) + return fmt.Errorf("IP address is not IPv6: %s", data.IPv6) } server.IPs = append(server.IPs, ipv6) } diff --git a/internal/provider/nordvpn/updater/api.go b/internal/provider/nordvpn/updater/api.go index 3d122a8e..096e29ad 100644 --- a/internal/provider/nordvpn/updater/api.go +++ b/internal/provider/nordvpn/updater/api.go @@ -3,13 +3,10 @@ package updater import ( "context" "encoding/json" - "errors" "fmt" "net/http" ) -var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK") - func fetchAPI(ctx context.Context, client *http.Client, limit uint, ) (data serversData, err error) { @@ -28,7 +25,7 @@ func fetchAPI(ctx context.Context, client *http.Client, defer response.Body.Close() if response.StatusCode != http.StatusOK { - return serversData{}, fmt.Errorf("%w: %s", ErrHTTPStatusCodeNotOK, response.Status) + return serversData{}, fmt.Errorf("HTTP status code not OK: %s", response.Status) } decoder := json.NewDecoder(response.Body) diff --git a/internal/provider/nordvpn/updater/models.go b/internal/provider/nordvpn/updater/models.go index ea2296b2..8aea49a3 100644 --- a/internal/provider/nordvpn/updater/models.go +++ b/internal/provider/nordvpn/updater/models.go @@ -168,11 +168,6 @@ func (s *serverData) ips() (ips []netip.Addr) { return ips } -var ( - ErrWireguardPublicKeyMalformed = errors.New("wireguard public key is malformed") - ErrWireguardPublicKeyNotFound = errors.New("wireguard public key not found") -) - // wireguardPublicKey returns the Wireguard public key for the server. func (s *serverData) wireguardPublicKey(technologies map[uint32]technologyData) ( wgPubKey string, err error, @@ -189,11 +184,10 @@ func (s *serverData) wireguardPublicKey(technologies map[uint32]technologyData) wgPubKey = metadata.Value _, err = base64.StdEncoding.DecodeString(wgPubKey) if err != nil { - return "", fmt.Errorf("%w: %s cannot be decoded: %s", - ErrWireguardPublicKeyMalformed, wgPubKey, err) + return "", fmt.Errorf("wireguard public key is malformed: %s cannot be decoded: %s", wgPubKey, err) } return metadata.Value, nil } } - return "", fmt.Errorf("%w", ErrWireguardPublicKeyNotFound) + return "", errors.New("wireguard public key not found") } diff --git a/internal/provider/nordvpn/updater/name.go b/internal/provider/nordvpn/updater/name.go index cf51eb26..55464c46 100644 --- a/internal/provider/nordvpn/updater/name.go +++ b/internal/provider/nordvpn/updater/name.go @@ -7,10 +7,7 @@ import ( "strings" ) -var ( - ErrNoIDInServerName = errors.New("no ID in server name") - ErrInvalidIDInServerName = errors.New("invalid ID in server name") -) +var ErrNoIDInServerName = errors.New("no ID in server name") func parseServerName(serverName string) (number uint16, err error) { i := strings.IndexRune(serverName, '#') @@ -21,7 +18,7 @@ func parseServerName(serverName string) (number uint16, err error) { idString := serverName[i+1:] idUint64, err := strconv.ParseUint(idString, 10, 16) if err != nil { - return 0, fmt.Errorf("%w: %s", ErrInvalidIDInServerName, serverName) + return 0, fmt.Errorf("invalid ID in server name: %s", serverName) } number = uint16(idUint64) diff --git a/internal/provider/nordvpn/updater/servers.go b/internal/provider/nordvpn/updater/servers.go index d939700b..826f78b4 100644 --- a/internal/provider/nordvpn/updater/servers.go +++ b/internal/provider/nordvpn/updater/servers.go @@ -11,8 +11,6 @@ import ( "github.com/qdm12/gluetun/internal/provider/common" ) -var ErrNotIPv4 = errors.New("IP address is not IPv4") - func (u *Updater) FetchServers(ctx context.Context, minServers int) ( servers []models.Server, err error, ) { diff --git a/internal/provider/privateinternetaccess/portforward.go b/internal/provider/privateinternetaccess/portforward.go index 7fa2ca4c..0d2dcd7a 100644 --- a/internal/provider/privateinternetaccess/portforward.go +++ b/internal/provider/privateinternetaccess/portforward.go @@ -21,8 +21,6 @@ import ( "github.com/qdm12/gluetun/internal/provider/utils" ) -var ErrServerNameNotFound = errors.New("server name not found in servers") - // PortForward obtains a VPN server side port forwarded from PIA. func (p *Provider) PortForward(ctx context.Context, objects utils.PortForwardObjects, @@ -42,7 +40,7 @@ func (p *Provider) PortForward(ctx context.Context, logger := objects.Logger if !objects.CanPortForward { - return nil, fmt.Errorf("%w: for server %s", ErrServerNameNotFound, serverName) + return nil, fmt.Errorf("server name %s not found in servers", serverName) } privateIPClient, err := newHTTPClient(serverName) @@ -91,8 +89,6 @@ func (p *Provider) PortForward(ctx context.Context, return map[uint16]uint16{data.Port: data.Port}, nil } -var ErrPortForwardedExpired = errors.New("port forwarded data expired") - func (p *Provider) KeepPortForward(ctx context.Context, objects utils.PortForwardObjects, ) (err error) { @@ -136,14 +132,12 @@ func (p *Provider) KeepPortForward(ctx context.Context, } keepAliveTimer.Reset(keepAlivePeriod) case <-expiryTimer.C: - return fmt.Errorf("%w: on %s", ErrPortForwardedExpired, + return fmt.Errorf("port forwarded data expired on %s", data.Expiration.Format(time.RFC1123)) } } } -var errAPIIPNotFound = errors.New("API IP address not found") - func findAPIIP(ctx context.Context, client *http.Client, gateway netip.Addr) ( apiIP netip.Addr, err error, ) { @@ -188,7 +182,7 @@ func findAPIIP(ctx context.Context, client *http.Client, gateway netip.Addr) ( return ip, nil } - return netip.Addr{}, fmt.Errorf("%w: %w", errAPIIPNotFound, errors.Join(errs...)) + return netip.Addr{}, fmt.Errorf("API IP address not found: %w", errors.Join(errs...)) } func refreshPIAPortForwardData(ctx context.Context, client, privateIPClient *http.Client, @@ -290,8 +284,6 @@ func packPayload(port uint16, token string, expiration time.Time) (payload strin return payload, nil } -var errEmptyToken = errors.New("token received is empty") - func fetchToken(ctx context.Context, client *http.Client, username, password string, ) (token string, err error) { @@ -340,7 +332,7 @@ func fetchToken(ctx context.Context, client *http.Client, } if result.Token == "" { - return "", errEmptyToken + return "", errors.New("token received is empty") } return result.Token, nil } @@ -391,7 +383,7 @@ func fetchPortForwardData(ctx context.Context, client *http.Client, apiIP netip. } if data.Status != "OK" { - return 0, "", expiration, fmt.Errorf("%w: status is: %s", ErrBadResponse, data.Status) + return 0, "", expiration, fmt.Errorf("bad response received with status %s", data.Status) } port, _, expiration, err = unpackPayload(data.Payload) @@ -401,8 +393,6 @@ func fetchPortForwardData(ctx context.Context, client *http.Client, apiIP netip. return port, data.Signature, expiration, err } -var ErrBadResponse = errors.New("bad response received") - func bindPort(ctx context.Context, client *http.Client, apiIPAddress netip.Addr, data piaPortForwardData) (err error) { // Define a timeout since the default client has a large timeout and we don't // want to wait too long. @@ -455,7 +445,7 @@ func bindPort(ctx context.Context, client *http.Client, apiIPAddress netip.Addr, } if responseData.Status != "OK" { - return fmt.Errorf("%w: %s: %s", ErrBadResponse, responseData.Status, responseData.Message) + return fmt.Errorf("bad response received with status %q and message %q", responseData.Status, responseData.Message) } return nil @@ -464,7 +454,7 @@ func bindPort(ctx context.Context, client *http.Client, apiIPAddress netip.Addr, // replaceInErr is used to remove sensitive information from errors. func replaceInErr(err error, substitutions map[string]string) error { s := replaceInString(err.Error(), substitutions) - return errors.New(s) //nolint:err113 + return errors.New(s) } // replaceInString is used to remove sensitive information. @@ -475,8 +465,6 @@ func replaceInString(s string, substitutions map[string]string) string { return s } -var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code is not OK") - func makeNOKStatusError(response *http.Response, substitutions map[string]string) (err error) { url := response.Request.URL.String() url = replaceInString(url, substitutions) @@ -487,7 +475,6 @@ func makeNOKStatusError(response *http.Response, substitutions map[string]string shortenMessage = strings.ReplaceAll(shortenMessage, " ", " ") shortenMessage = replaceInString(shortenMessage, substitutions) - return fmt.Errorf("%w: %s: %d %s: response received: %s", - ErrHTTPStatusCodeNotOK, url, response.StatusCode, - response.Status, shortenMessage) + return fmt.Errorf("HTTP status code not OK: %s: %d %s: response received: %s", + url, response.StatusCode, response.Status, shortenMessage) } diff --git a/internal/provider/privateinternetaccess/updater/api.go b/internal/provider/privateinternetaccess/updater/api.go index 672bb7eb..584a470f 100644 --- a/internal/provider/privateinternetaccess/updater/api.go +++ b/internal/provider/privateinternetaccess/updater/api.go @@ -4,15 +4,12 @@ import ( "bytes" "context" "encoding/json" - "errors" "fmt" "io" "net/http" "net/netip" ) -var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK") - type apiData struct { Regions []regionData `json:"regions"` } @@ -50,7 +47,7 @@ func fetchAPI(ctx context.Context, client *http.Client) ( defer response.Body.Close() if response.StatusCode != http.StatusOK { - return data, fmt.Errorf("%w: %d %s", ErrHTTPStatusCodeNotOK, + return data, fmt.Errorf("HTTP status code not OK: %d %s", response.StatusCode, response.Status) } diff --git a/internal/provider/privatevpn/portforward.go b/internal/provider/privatevpn/portforward.go index 9982dc5a..383b7e0e 100644 --- a/internal/provider/privatevpn/portforward.go +++ b/internal/provider/privatevpn/portforward.go @@ -11,14 +11,11 @@ import ( "strconv" "time" - "github.com/qdm12/gluetun/internal/provider/common" "github.com/qdm12/gluetun/internal/provider/utils" ) var regexPort = regexp.MustCompile(`[1-9][0-9]{0,4}`) -var ErrPortForwardedNotFound = errors.New("port forwarded not found") - // PortForward obtains a VPN server side port forwarded from the PrivateVPN API. // It returns 0 if all ports are to forwarded on a dedicated server IP. func (p *Provider) PortForward(ctx context.Context, objects utils.PortForwardObjects) ( @@ -42,8 +39,7 @@ func (p *Provider) PortForward(ctx context.Context, objects utils.PortForwardObj } if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%w: %d %s", common.ErrHTTPStatusCodeNotOK, - response.StatusCode, response.Status) + return nil, fmt.Errorf("HTTP status code not OK: %d %s", response.StatusCode, response.Status) } defer response.Body.Close() @@ -62,12 +58,12 @@ func (p *Provider) PortForward(ctx context.Context, objects utils.PortForwardObj return nil, fmt.Errorf("decoding JSON response: %w; data is: %s", err, string(bytes)) } else if !data.Supported { - return nil, fmt.Errorf("%w for this VPN server", common.ErrPortForwardNotSupported) + return nil, errors.New("port forwarding not supported for this VPN server") } portString := regexPort.FindString(data.Status) if portString == "" { - return nil, fmt.Errorf("%w: in status %q", ErrPortForwardedNotFound, data.Status) + return nil, fmt.Errorf("port forwarded not found in status %q", data.Status) } const base, bitSize = 10, 16 diff --git a/internal/provider/privatevpn/portforward_test.go b/internal/provider/privatevpn/portforward_test.go index bb8f69ee..89f2361c 100644 --- a/internal/provider/privatevpn/portforward_test.go +++ b/internal/provider/privatevpn/portforward_test.go @@ -22,8 +22,6 @@ func (s roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { func Test_Provider_PortForward(t *testing.T) { t.Parallel() - errTest := errors.New("test error") - canceledCtx, cancel := context.WithCancel(context.Background()) cancel() @@ -59,7 +57,7 @@ func Test_Provider_PortForward(t *testing.T) { assert.Equal(t, "https://connect.pvdatanet.com/v3/Api/port?ip[]=10.10.10.10", r.URL.String()) - return nil, errTest + return nil, errors.New("test error") }), }, }, @@ -156,7 +154,7 @@ func Test_Provider_PortForward(t *testing.T) { }), }, }, - errMessage: "port forwarded not found: in status \"no port here\"", + errMessage: "port forwarded not found in status \"no port here\"", }, "port_too_big": { ctx: context.Background(), diff --git a/internal/provider/privatevpn/updater/filename.go b/internal/provider/privatevpn/updater/filename.go index 50d58de9..4ba8b844 100644 --- a/internal/provider/privatevpn/updater/filename.go +++ b/internal/provider/privatevpn/updater/filename.go @@ -1,7 +1,6 @@ package updater import ( - "errors" "fmt" "regexp" "strings" @@ -9,12 +8,6 @@ import ( var trailingNumber = regexp.MustCompile(` [0-9]+$`) -var ( - errBadPrefix = errors.New("bad prefix in file name") - errBadSuffix = errors.New("bad suffix in file name") - errNotEnoughParts = errors.New("not enough parts in file name") -) - func parseFilename(fileName string) ( countryCode, city string, err error, ) { @@ -22,7 +15,7 @@ func parseFilename(fileName string) ( const prefix = "PrivateVPN-" if !strings.HasPrefix(fileName, prefix) { - return "", "", fmt.Errorf("%w: %s", errBadPrefix, fileName) + return "", "", fmt.Errorf("bad prefix in file name %s", fileName) } s := strings.TrimPrefix(fileName, prefix) @@ -34,7 +27,7 @@ func parseFilename(fileName string) ( case strings.HasSuffix(fileName, udpSuffix): s = strings.TrimSuffix(s, udpSuffix) default: - return "", "", fmt.Errorf("%w: %s", errBadSuffix, fileName) + return "", "", fmt.Errorf("bad suffix in file name %s", fileName) } s = trailingNumber.ReplaceAllString(s, "") @@ -42,7 +35,7 @@ func parseFilename(fileName string) ( parts := strings.Split(s, "-") const minParts = 2 if len(parts) < minParts { - return "", "", fmt.Errorf("%w: %s", errNotEnoughParts, fileName) + return "", "", fmt.Errorf("not enough parts in file name %s", fileName) } countryCode, city = parts[0], parts[1] diff --git a/internal/provider/protonvpn/portforward.go b/internal/provider/protonvpn/portforward.go index 5479d65a..e3d24ff6 100644 --- a/internal/provider/protonvpn/portforward.go +++ b/internal/provider/protonvpn/portforward.go @@ -12,8 +12,6 @@ import ( "github.com/qdm12/gluetun/internal/provider/utils" ) -var ErrServerPortForwardNotSupported = errors.New("server does not support port forwarding") - const nonSymmetricPortStart uint16 = 56789 // PortForward obtains a VPN server side port forwarded from ProtonVPN gateway. @@ -21,7 +19,7 @@ func (p *Provider) PortForward(ctx context.Context, objects utils.PortForwardObj internalToExternalPorts map[uint16]uint16, err error, ) { if !objects.CanPortForward { - return nil, fmt.Errorf("%w", ErrServerPortForwardNotSupported) + return nil, errors.New("server does not support port forwarding") } client := natpmp.New() @@ -104,11 +102,6 @@ func checkExternalPorts(logger utils.Logger, udpPort, tcpPort uint16) { } } -var ( - ErrInternalPortChanged = errors.New("internal port changed") - ErrExternalPortChanged = errors.New("external port changed") -) - func (p *Provider) KeepPortForward(ctx context.Context, objects utils.PortForwardObjects, ) (err error) { @@ -135,11 +128,10 @@ func (p *Provider) KeepPortForward(ctx context.Context, } checkLifetime(logger, networkProtocol, lifetime, assignedLiftetime) if externalPort != assignedExternalPort { - return fmt.Errorf("%w: %d changed to %d", - ErrExternalPortChanged, externalPort, assignedExternalPort) + return fmt.Errorf("external port changed from %d to %d", externalPort, assignedExternalPort) } else if internalPort != assignedInternalPort { - return fmt.Errorf("%w: %d (for external port %d) changed to %d", - ErrInternalPortChanged, internalPort, externalPort, assignedInternalPort) + return fmt.Errorf("internal port changed from %d (for external port %d) to %d", + internalPort, externalPort, assignedInternalPort) } } objects.Logger.Debug(fmt.Sprintf("port forwarded %d maintained", externalPort)) diff --git a/internal/provider/protonvpn/updater/api.go b/internal/provider/protonvpn/updater/api.go index fb8c3016..dab78eb4 100644 --- a/internal/provider/protonvpn/updater/api.go +++ b/internal/provider/protonvpn/updater/api.go @@ -60,8 +60,6 @@ func newAPIClient(ctx context.Context, httpClient *http.Client) (client *apiClie }, nil } -var ErrCodeNotSuccess = errors.New("response code is not success") - // setHeaders sets the minimal necessary headers for Proton API requests // to succeed without being blocked by their "security" measures. // See for example [getMostRecentStableTag] on how the app version must @@ -126,8 +124,6 @@ func (c *apiClient) authenticate(ctx context.Context, email, password string, return authCookie, nil } -var ErrSessionIDNotFound = errors.New("session ID not found in cookies") - func (c *apiClient) getSessionID(ctx context.Context) (sessionID string, err error) { const url = "https://account.proton.me/vpn" request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) @@ -150,11 +146,9 @@ func (c *apiClient) getSessionID(ctx context.Context) (sessionID string, err err } } - return "", fmt.Errorf("%w", ErrSessionIDNotFound) + return "", errors.New("session ID not found in cookies") } -var ErrDataFieldMissing = errors.New("data field missing in response") - func (c *apiClient) getUnauthSession(ctx context.Context, sessionID string) ( tokenType, accessToken, refreshToken, uid string, err error, ) { @@ -198,24 +192,22 @@ func (c *apiClient) getUnauthSession(ctx context.Context, sessionID string) ( const successCode = 1000 switch { case data.Code != successCode: - return "", "", "", "", fmt.Errorf("%w: expected %d got %d", - ErrCodeNotSuccess, successCode, data.Code) + return "", "", "", "", fmt.Errorf("response code %d is not expected success code %d", + data.Code, successCode) case data.AccessToken == "": - return "", "", "", "", fmt.Errorf("%w: access token is empty", ErrDataFieldMissing) + return "", "", "", "", errors.New("access token is empty in response") case data.RefreshToken == "": - return "", "", "", "", fmt.Errorf("%w: refresh token is empty", ErrDataFieldMissing) + return "", "", "", "", errors.New("refresh token is empty in response") case data.TokenType == "": - return "", "", "", "", fmt.Errorf("%w: token type is empty", ErrDataFieldMissing) + return "", "", "", "", errors.New("token type is empty in response") case data.UID == "": - return "", "", "", "", fmt.Errorf("%w: UID is empty", ErrDataFieldMissing) + return "", "", "", "", errors.New("UID is empty in response") } // Ignore Scopes and LocalID fields, we don't use them. return data.TokenType, data.AccessToken, data.RefreshToken, data.UID, nil } -var ErrUIDMismatch = errors.New("UID in response does not match request UID") - func (c *apiClient) cookieToken(ctx context.Context, sessionID, tokenType, accessToken, refreshToken, uid string, ) (cookieToken string, err error) { @@ -282,11 +274,11 @@ func (c *apiClient) cookieToken(ctx context.Context, sessionID, tokenType, acces const successCode = 1000 switch { case cookies.Code != successCode: - return "", fmt.Errorf("%w: expected %d got %d", - ErrCodeNotSuccess, successCode, cookies.Code) + return "", fmt.Errorf("response code %d is not expected success code %d", + cookies.Code, successCode) case cookies.UID != requestBody.UID: - return "", fmt.Errorf("%w: expected %s got %s", - ErrUIDMismatch, requestBody.UID, cookies.UID) + return "", fmt.Errorf("UID %s in response does not match request UID %s", + cookies.UID, requestBody.UID) } // Ignore LocalID and RefreshCounter fields, we don't use them. @@ -296,11 +288,9 @@ func (c *apiClient) cookieToken(ctx context.Context, sessionID, tokenType, acces } } - return "", fmt.Errorf("%w", ErrAuthCookieNotFound) + return "", errors.New("auth cookie not found") } -var ErrUsernameDoesNotExist = errors.New("username does not exist") - // authInfo fetches SRP parameters for the account. func (c *apiClient) authInfo(ctx context.Context, email string, unauthCookie cookie) ( username, modulusPGPClearSigned, serverEphemeralBase64, saltBase64, srpSessionHex string, @@ -358,20 +348,20 @@ func (c *apiClient) authInfo(ctx context.Context, email string, unauthCookie coo const successCode = 1000 switch { case info.Code != successCode: - return "", "", "", "", "", 0, fmt.Errorf("%w: expected %d got %d", - ErrCodeNotSuccess, successCode, info.Code) + return "", "", "", "", "", 0, fmt.Errorf("response code %d is not expected success code %d", + info.Code, successCode) case info.Modulus == "": - return "", "", "", "", "", 0, fmt.Errorf("%w: modulus is empty", ErrDataFieldMissing) + return "", "", "", "", "", 0, errors.New("modulus is empty in response") case info.ServerEphemeral == "": - return "", "", "", "", "", 0, fmt.Errorf("%w: server ephemeral is empty", ErrDataFieldMissing) + return "", "", "", "", "", 0, errors.New("server ephemeral is empty in response") case info.Salt == "": - return "", "", "", "", "", 0, fmt.Errorf("%w (salt data field is empty)", ErrUsernameDoesNotExist) + return "", "", "", "", "", 0, errors.New("salt is empty in response") case info.SRPSession == "": - return "", "", "", "", "", 0, fmt.Errorf("%w: SRP session is empty", ErrDataFieldMissing) + return "", "", "", "", "", 0, errors.New("SRP session is empty in response") case info.Username == "": - return "", "", "", "", "", 0, fmt.Errorf("%w: username is empty", ErrDataFieldMissing) + return "", "", "", "", "", 0, errors.New("username is empty in response") case info.Version == nil: - return "", "", "", "", "", 0, fmt.Errorf("%w: version is missing", ErrDataFieldMissing) + return "", "", "", "", "", 0, errors.New("version is missing in response") } version = int(*info.Version) //nolint:gosec @@ -399,13 +389,7 @@ func (c *cookie) String() string { return s } -var ( - // ErrServerProofNotValid indicates the M2 from the server didn't match the expected proof. - ErrServerProofNotValid = errors.New("server proof from server is not valid") - ErrVPNScopeNotFound = errors.New("VPN scope not found in scopes") - ErrTwoFANotSupported = errors.New("two factor authentication not supported in this client") - ErrAuthCookieNotFound = errors.New("auth cookie not found") -) +// ErrServerProofNotValid indicates the M2 from the server didn't match the expected proof. // auth performs the SRP proof submission (and optionally TOTP) to obtain tokens. func (c *apiClient) auth(ctx context.Context, unauthCookie cookie, @@ -495,22 +479,22 @@ func (c *apiClient) auth(ctx context.Context, unauthCookie cookie, return cookie{}, fmt.Errorf("decoding server proof: %w", err) } if !bytes.Equal(m2, proofs.ExpectedServerProof) { - return cookie{}, fmt.Errorf("%w: expected %x got %x", - ErrServerProofNotValid, proofs.ExpectedServerProof, m2) + return cookie{}, fmt.Errorf("server proof from server %x is not expected proof %x", + m2, proofs.ExpectedServerProof) } const successCode = 1000 switch { case auth.Code != successCode: - return cookie{}, fmt.Errorf("%w: expected %d got %d", - ErrCodeNotSuccess, successCode, auth.Code) + return cookie{}, fmt.Errorf("response code %d is not expected success code %d", + auth.Code, successCode) case auth.UID != unauthCookie.uid: - return cookie{}, fmt.Errorf("%w: expected %s got %s", - ErrUIDMismatch, unauthCookie.uid, auth.UID) + return cookie{}, fmt.Errorf("UID %s in response does not match request UID %s", + auth.UID, unauthCookie.uid) case auth.TwoFactor != 0: - return cookie{}, fmt.Errorf("%w", ErrTwoFANotSupported) + return cookie{}, errors.New("two factor authentication not supported in this client") case !slices.Contains(auth.Scopes, "vpn"): - return cookie{}, fmt.Errorf("%w: in %v", ErrVPNScopeNotFound, auth.Scopes) + return cookie{}, fmt.Errorf("VPN scope not found in scopes %v", auth.Scopes) } for _, setCookieHeader := range response.Header.Values("Set-Cookie") { @@ -524,8 +508,7 @@ func (c *apiClient) auth(ctx context.Context, unauthCookie cookie, } } - return cookie{}, fmt.Errorf("%w: in HTTP headers %s", - ErrAuthCookieNotFound, httpHeadersToString(response.Header)) + return cookie{}, fmt.Errorf("auth cookie not found in HTTP headers %s", httpHeadersToString(response.Header)) } // generateLettersDigits mimicing Proton's own random string generator: @@ -611,8 +594,6 @@ func (c *apiClient) fetchServers(ctx context.Context, cookie cookie) ( return data, nil } -var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK") - func buildError(httpCode int, body []byte) error { prettyCode := http.StatusText(httpCode) var protonError struct { @@ -624,8 +605,8 @@ func buildError(httpCode int, body []byte) error { decoder.DisallowUnknownFields() err := decoder.Decode(&protonError) if err != nil || protonError.Error == nil || protonError.Code == nil { - return fmt.Errorf("%w: %s: %s", - ErrHTTPStatusCodeNotOK, prettyCode, body) + return fmt.Errorf("HTTP status code not OK: %s: %s", + prettyCode, body) } details := make([]string, 0, len(protonError.Details)) @@ -633,6 +614,6 @@ func buildError(httpCode int, body []byte) error { details = append(details, fmt.Sprintf("%s: %s", key, value)) } - return fmt.Errorf("%w: %s: %s (code %d with details: %s)", - ErrHTTPStatusCodeNotOK, prettyCode, *protonError.Error, *protonError.Code, strings.Join(details, ", ")) + return fmt.Errorf("HTTP status code not OK: %s: %s (code %d with details: %s)", + prettyCode, *protonError.Error, *protonError.Code, strings.Join(details, ", ")) } diff --git a/internal/provider/protonvpn/updater/version.go b/internal/provider/protonvpn/updater/version.go index bdf0587f..1d305304 100644 --- a/internal/provider/protonvpn/updater/version.go +++ b/internal/provider/protonvpn/updater/version.go @@ -45,7 +45,7 @@ func getMostRecentStableTag(ctx context.Context, client *http.Client) (version s } if response.StatusCode != http.StatusOK { - return "", fmt.Errorf("%w: %s: %s", ErrHTTPStatusCodeNotOK, response.Status, data) + return "", fmt.Errorf("HTTP status code not OK: %s: %s", response.Status, data) } var tags []struct { diff --git a/internal/provider/purevpn/updater/servers.go b/internal/provider/purevpn/updater/servers.go index 3d40ec83..5735fde7 100644 --- a/internal/provider/purevpn/updater/servers.go +++ b/internal/provider/purevpn/updater/servers.go @@ -17,7 +17,7 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) ( servers []models.Server, err error, ) { if !u.ipFetcher.CanFetchAnyIP() { - return nil, fmt.Errorf("%w: %s", common.ErrIPFetcherUnsupported, u.ipFetcher.String()) + return nil, fmt.Errorf("IP fetcher %s does not support fetching any IP", u.ipFetcher.String()) } const url = "https://d11a57lttb2ffq.cloudfront.net/heartbleed/router/Recommended-CA2.zip" diff --git a/internal/provider/surfshark/updater/api.go b/internal/provider/surfshark/updater/api.go index 2b5aab09..9b326d14 100644 --- a/internal/provider/surfshark/updater/api.go +++ b/internal/provider/surfshark/updater/api.go @@ -3,7 +3,6 @@ package updater import ( "context" "encoding/json" - "errors" "fmt" "net/http" @@ -38,8 +37,6 @@ func addServersFromAPI(ctx context.Context, client *http.Client, return nil } -var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK") - type serverData struct { Host string `json:"connectionName"` Region string `json:"region"` @@ -66,7 +63,7 @@ func fetchAPI(ctx context.Context, client *http.Client) ( defer response.Body.Close() if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%w: %d %s", ErrHTTPStatusCodeNotOK, + return nil, fmt.Errorf("HTTP status code not OK: %d %s", response.StatusCode, response.Status) } diff --git a/internal/provider/surfshark/updater/location.go b/internal/provider/surfshark/updater/location.go index 5ecadc40..72fb3dd6 100644 --- a/internal/provider/surfshark/updater/location.go +++ b/internal/provider/surfshark/updater/location.go @@ -1,20 +1,17 @@ package updater import ( - "errors" "fmt" "github.com/qdm12/gluetun/internal/provider/surfshark/servers" ) -var errHostnameNotFound = errors.New("hostname not found in hostname to location mapping") - func getHostInformation(host string, hostnameToLocation map[string]servers.ServerLocation) ( data servers.ServerLocation, err error, ) { locationData, ok := hostnameToLocation[host] if !ok { - return locationData, fmt.Errorf("%w: %s", errHostnameNotFound, host) + return locationData, fmt.Errorf("hostname %s not found in hostname to location mapping", host) } return locationData, nil diff --git a/internal/provider/utils/connection_test.go b/internal/provider/utils/connection_test.go index 37fb66f2..beccbe2b 100644 --- a/internal/provider/utils/connection_test.go +++ b/internal/provider/utils/connection_test.go @@ -19,8 +19,6 @@ import ( func Test_GetConnection(t *testing.T) { t.Parallel() - errTest := errors.New("test error") - testCases := map[string]struct { provider string filteredServers []models.Server @@ -30,12 +28,10 @@ func Test_GetConnection(t *testing.T) { ipv6Supported bool randSource rand.Source connection models.Connection - errWrapped error errMessage string }{ "storage filter error": { - filterError: errTest, - errWrapped: errTest, + filterError: errors.New("test error"), errMessage: "filtering servers: test error", }, "server without IPs": { @@ -50,7 +46,6 @@ func Test_GetConnection(t *testing.T) { OpenVPNUDPPort: 1, WireguardPort: 1, }, - errWrapped: ErrNoConnectionToPickFrom, errMessage: "no connection to pick from", }, "OpenVPN server with hostname": { @@ -199,9 +194,10 @@ func Test_GetConnection(t *testing.T) { testCase.randSource) assert.Equal(t, testCase.connection, connection) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/provider/utils/nofetcher.go b/internal/provider/utils/nofetcher.go index 275808e9..f4ba3ecf 100644 --- a/internal/provider/utils/nofetcher.go +++ b/internal/provider/utils/nofetcher.go @@ -2,7 +2,6 @@ package utils import ( "context" - "errors" "fmt" "github.com/qdm12/gluetun/internal/models" @@ -18,10 +17,8 @@ func NewNoFetcher(providerName string) *NoFetcher { } } -var ErrFetcherNotSupported = errors.New("fetching of servers is not supported") - func (n *NoFetcher) FetchServers(context.Context, int) ( servers []models.Server, err error, ) { - return nil, fmt.Errorf("%w: for %s", ErrFetcherNotSupported, n.providerName) + return nil, fmt.Errorf("fetching of servers is not supported for %s", n.providerName) } diff --git a/internal/provider/utils/pick.go b/internal/provider/utils/pick.go index 20926b08..de3135ac 100644 --- a/internal/provider/utils/pick.go +++ b/internal/provider/utils/pick.go @@ -11,8 +11,6 @@ import ( "github.com/qdm12/gluetun/internal/models" ) -var ErrNoConnectionToPickFrom = errors.New("no connection to pick from") - // pickConnection picks a connection from a pool of connections. // If the VPN protocol is Wireguard and the target IP is set, // it finds the connection corresponding to this target IP. @@ -23,7 +21,7 @@ func pickConnection(connections []models.Connection, connection models.Connection, err error, ) { if len(connections) == 0 { - return connection, ErrNoConnectionToPickFrom + return connection, errors.New("no connection to pick from") } var targetIP netip.Addr @@ -56,8 +54,6 @@ func pickRandomConnection(connections []models.Connection, return connections[rand.New(source).Intn(len(connections))] //nolint:gosec } -var errTargetIPNotFound = errors.New("target IP address not found") - func getTargetIPConnection(connections []models.Connection, targetIP netip.Addr, ) (connection models.Connection, err error) { @@ -66,6 +62,6 @@ func getTargetIPConnection(connections []models.Connection, return connection, nil } } - return connection, fmt.Errorf("%w: in %d filtered connections", - errTargetIPNotFound, len(connections)) + return connection, fmt.Errorf("target IP address not found: in %d filtered connections", + len(connections)) } diff --git a/internal/provider/vpnsecure/updater/website.go b/internal/provider/vpnsecure/updater/website.go index e25ffd19..652306bb 100644 --- a/internal/provider/vpnsecure/updater/website.go +++ b/internal/provider/vpnsecure/updater/website.go @@ -33,8 +33,6 @@ func fetchServers(ctx context.Context, client *http.Client, return servers, nil } -var ErrHTMLServersDivNotFound = errors.New("HTML servers container div not found") - const divString = "div" func parseHTML(rootNode *html.Node) (servers []models.Server, @@ -43,7 +41,7 @@ func parseHTML(rootNode *html.Node) (servers []models.Server, // Find div container for all servers, searching with BFS. serversDiv := findServersDiv(rootNode) if serversDiv == nil { - return nil, nil, htmlutils.WrapError(ErrHTMLServersDivNotFound, rootNode) + return nil, nil, htmlutils.WrapError(errors.New("HTML servers container div not found"), rootNode) } for countryNode := serversDiv.FirstChild; countryNode != nil; countryNode = countryNode.NextSibling { diff --git a/internal/provider/vpnsecure/updater/website_test.go b/internal/provider/vpnsecure/updater/website_test.go index 7c937032..ac4c0aea 100644 --- a/internal/provider/vpnsecure/updater/website_test.go +++ b/internal/provider/vpnsecure/updater/website_test.go @@ -31,12 +31,10 @@ func Test_fetchServers(t *testing.T) { responseStatus int responseBody io.ReadCloser servers []models.Server - errWrapped error errMessage string }{ "context canceled": { ctx: canceledCtx, - errWrapped: context.Canceled, errMessage: `fetching HTML code: Get "https://www.vpnsecure.me/vpn-locations/": context canceled`, }, "success": { @@ -105,9 +103,10 @@ func Test_fetchServers(t *testing.T) { servers, err := fetchServers(testCase.ctx, client, warner) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } assert.Equal(t, testCase.servers, servers) }) @@ -121,12 +120,10 @@ func Test_parseHTML(t *testing.T) { rootNode *html.Node servers []models.Server warnings []string - errWrapped error errMessage string }{ "empty html": { rootNode: parseTestHTML(t, ""), - errWrapped: ErrHTMLServersDivNotFound, errMessage: `HTML servers container div not found: in HTML code: `, }, "test data": { @@ -223,9 +220,10 @@ func Test_parseHTML(t *testing.T) { assert.Equal(t, testCase.servers, servers) assert.Equal(t, testCase.warnings, warnings) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/provider/vyprvpn/updater/filename.go b/internal/provider/vyprvpn/updater/filename.go index a2fd3f6e..4f017431 100644 --- a/internal/provider/vyprvpn/updater/filename.go +++ b/internal/provider/vyprvpn/updater/filename.go @@ -1,19 +1,16 @@ package updater import ( - "errors" "fmt" "strings" ) -var errNotOvpnExt = errors.New("filename does not have the openvpn file extension") - func parseFilename(fileName string) ( region string, err error, ) { const suffix = ".ovpn" if !strings.HasSuffix(fileName, suffix) { - return "", fmt.Errorf("%w: %s", errNotOvpnExt, fileName) + return "", fmt.Errorf("filename does not have the openvpn file extension: %s", fileName) } region = strings.TrimSuffix(fileName, suffix) diff --git a/internal/provider/windscribe/connection_test.go b/internal/provider/windscribe/connection_test.go index a4115dfe..bff15edc 100644 --- a/internal/provider/windscribe/connection_test.go +++ b/internal/provider/windscribe/connection_test.go @@ -22,21 +22,17 @@ func Test_Provider_GetConnection(t *testing.T) { const provider = providers.Windscribe - errTest := errors.New("test error") - testCases := map[string]struct { filteredServers []models.Server storageErr error selection settings.ServerSelection ipv6Supported bool connection models.Connection - errWrapped error errMessage string panicMessage string }{ "error": { - storageErr: errTest, - errWrapped: errTest, + storageErr: errors.New("test error"), errMessage: "filtering servers: test error", }, "default OpenVPN TCP port": { @@ -111,9 +107,10 @@ func Test_Provider_GetConnection(t *testing.T) { connection, err := provider.GetConnection(testCase.selection, testCase.ipv6Supported) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } assert.Equal(t, testCase.connection, connection) diff --git a/internal/provider/windscribe/updater/api.go b/internal/provider/windscribe/updater/api.go index 9ea83823..3891457f 100644 --- a/internal/provider/windscribe/updater/api.go +++ b/internal/provider/windscribe/updater/api.go @@ -3,7 +3,6 @@ package updater import ( "context" "encoding/json" - "errors" "fmt" "net/http" "net/netip" @@ -11,8 +10,6 @@ import ( "time" ) -var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK") - type apiData struct { Data []regionData `json:"data"` } @@ -55,7 +52,7 @@ func fetchAPI(ctx context.Context, client *http.Client) ( defer response.Body.Close() if response.StatusCode != http.StatusOK { - return data, fmt.Errorf("%w: %d %s", ErrHTTPStatusCodeNotOK, + return data, fmt.Errorf("HTTP status code not OK: %d %s", response.StatusCode, response.Status) } diff --git a/internal/provider/windscribe/updater/servers.go b/internal/provider/windscribe/updater/servers.go index 586f9821..3f07edfa 100644 --- a/internal/provider/windscribe/updater/servers.go +++ b/internal/provider/windscribe/updater/servers.go @@ -2,7 +2,6 @@ package updater import ( "context" - "errors" "fmt" "net/netip" "sort" @@ -12,8 +11,6 @@ import ( "github.com/qdm12/gluetun/internal/provider/common" ) -var ErrNoWireguardKey = errors.New("no wireguard public key found") - func (u *Updater) FetchServers(ctx context.Context, minServers int) ( servers []models.Server, err error, ) { @@ -51,7 +48,7 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) ( if !node.IP3.IsValid() { // Wireguard + Stealth continue } else if wgPubKey == "" { - return nil, fmt.Errorf("%w: for node %s", ErrNoWireguardKey, node.Hostname) + return nil, fmt.Errorf("no wireguard public key found: for node %s", node.Hostname) } server.VPN = vpn.Wireguard diff --git a/internal/publicip/api/api.go b/internal/publicip/api/api.go index c44f580e..999d49ed 100644 --- a/internal/publicip/api/api.go +++ b/internal/publicip/api/api.go @@ -1,7 +1,6 @@ package api import ( - "errors" "fmt" "maps" "net/http" @@ -58,8 +57,6 @@ func New(nameTokenPairs []NameToken, client *http.Client) ( var regexEchoipURL = regexp.MustCompile(`^http(s|):\/\/.+$`) -var ErrProviderNotValid = errors.New("API name is not valid") - func ParseProvider(s string) (provider Provider, err error) { possibleProviders := []Provider{ Cloudflare, @@ -97,12 +94,10 @@ func ParseProvider(s string) (provider Provider, err error) { providerStrings = append(providerStrings, "a custom "+prefix+" url") } - return "", fmt.Errorf(`%w: %q can only be %s`, - ErrProviderNotValid, s, orStrings(providerStrings)) + return "", fmt.Errorf("API name is not valid: %q can only be %s", + s, orStrings(providerStrings)) } -var ErrCustomURLNotValid = errors.New("custom URL is not valid") - func checkCustomURL(s, prefix string, regex *regexp.Regexp) (match bool, err error) { if !strings.HasPrefix(s, prefix) { return false, nil @@ -110,15 +105,15 @@ func checkCustomURL(s, prefix string, regex *regexp.Regexp) (match bool, err err s = strings.TrimPrefix(s, prefix) _, err = url.Parse(s) if err != nil { - return true, fmt.Errorf("%s %w: %w", prefix, ErrCustomURLNotValid, err) + return true, fmt.Errorf("%s custom URL is not valid: %w", prefix, err) } if regex.MatchString(s) { return true, nil } - return true, fmt.Errorf("%s %w: %q does not match regular expression: %s", - prefix, ErrCustomURLNotValid, s, regex) + return true, fmt.Errorf("%s custom URL is not valid: "+ + "%q does not match regular expression: %s", prefix, s, regex) } func orStrings(strings []string) (result string) { diff --git a/internal/publicip/api/api_test.go b/internal/publicip/api/api_test.go index 5d4ddedc..56b1bfc9 100644 --- a/internal/publicip/api/api_test.go +++ b/internal/publicip/api/api_test.go @@ -12,17 +12,14 @@ func Test_ParseProvider(t *testing.T) { testCases := map[string]struct { s string provider Provider - errWrapped error errMessage string }{ "empty": { - errWrapped: ErrProviderNotValid, errMessage: `API name is not valid: "" can only be ` + `"cloudflare", "ifconfigco", "ip2location", "ipinfo" or a custom echoip# url`, }, "invalid": { - s: "xyz", - errWrapped: ErrProviderNotValid, + s: "xyz", errMessage: `API name is not valid: "xyz" can only be ` + `"cloudflare", "ifconfigco", "ip2location", "ipinfo" or a custom echoip# url`, }, @@ -35,14 +32,12 @@ func Test_ParseProvider(t *testing.T) { provider: IPInfo, }, "echoip_url_empty": { - s: "echoip#", - errWrapped: ErrCustomURLNotValid, + s: "echoip#", errMessage: `echoip# custom URL is not valid: "" ` + `does not match regular expression: ^http(s|):\/\/.+$`, }, "echoip_url_invalid": { - s: "echoip#postgres://localhost:3451", - errWrapped: ErrCustomURLNotValid, + s: "echoip#postgres://localhost:3451", errMessage: `echoip# custom URL is not valid: "postgres://localhost:3451" ` + `does not match regular expression: ^http(s|):\/\/.+$`, }, @@ -59,9 +54,10 @@ func Test_ParseProvider(t *testing.T) { provider, err := ParseProvider(testCase.s) assert.Equal(t, testCase.provider, provider) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/publicip/api/cloudflare.go b/internal/publicip/api/cloudflare.go index 80dbba00..26439f28 100644 --- a/internal/publicip/api/cloudflare.go +++ b/internal/publicip/api/cloudflare.go @@ -50,8 +50,8 @@ func (c *cloudflare) FetchInfo(ctx context.Context, ip netip.Addr) ( urlBase := "https://speed.cloudflare.com" url := urlBase + "/meta" if ip.IsValid() { - return result, fmt.Errorf("%w: cloudflare cannot provide information on the arbitrary IP address %s", - ErrServiceLimited, ip) + return result, fmt.Errorf("service is limited: "+ + "cloudflare cannot provide information on the arbitrary IP address %s", ip) } request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) @@ -72,8 +72,8 @@ func (c *cloudflare) FetchInfo(ctx context.Context, ip netip.Addr) ( return result, fmt.Errorf("%w from %s: %d %s", ErrTooManyRequests, url, response.StatusCode, response.Status) default: - return result, fmt.Errorf("%w from %s: %d %s", - ErrBadHTTPStatus, url, response.StatusCode, response.Status) + return result, fmt.Errorf("bad HTTP status received from %s: %d %s", + url, response.StatusCode, response.Status) } decoder := json.NewDecoder(response.Body) diff --git a/internal/publicip/api/echoip.go b/internal/publicip/api/echoip.go index 7be70520..a52f40e6 100644 --- a/internal/publicip/api/echoip.go +++ b/internal/publicip/api/echoip.go @@ -70,11 +70,9 @@ func (e *echoip) FetchInfo(ctx context.Context, ip netip.Addr) ( switch response.StatusCode { case http.StatusOK: case http.StatusTooManyRequests: - return result, fmt.Errorf("%w from %s: %s", - ErrTooManyRequests, url, response.Status) + return result, fmt.Errorf("%w from %s: %s", ErrTooManyRequests, url, response.Status) default: - return result, fmt.Errorf("%w from %s: %s", - ErrBadHTTPStatus, url, response.Status) + return result, fmt.Errorf("bad HTTP status received from %s: %s", url, response.Status) } decoder := json.NewDecoder(response.Body) diff --git a/internal/publicip/api/errors.go b/internal/publicip/api/errors.go index 0b479551..42da6205 100644 --- a/internal/publicip/api/errors.go +++ b/internal/publicip/api/errors.go @@ -2,9 +2,4 @@ package api import "errors" -var ( - ErrTokenNotValid = errors.New("token is not valid") - ErrTooManyRequests = errors.New("too many requests sent for this month") - ErrBadHTTPStatus = errors.New("bad HTTP status received") - ErrServiceLimited = errors.New("service is limited") -) +var ErrTooManyRequests = errors.New("too many requests sent for this month") diff --git a/internal/publicip/api/ip2location.go b/internal/publicip/api/ip2location.go index 4bf29304..d1e8290f 100644 --- a/internal/publicip/api/ip2location.go +++ b/internal/publicip/api/ip2location.go @@ -75,7 +75,7 @@ func (i *ip2Location) FetchInfo(ctx context.Context, ip netip.Addr) ( defer response.Body.Close() if i.token != "" && response.StatusCode == http.StatusUnauthorized { - return result, fmt.Errorf("%w: %s", ErrTokenNotValid, response.Status) + return result, fmt.Errorf("token is not valid: %s", response.Status) } switch response.StatusCode { @@ -84,8 +84,8 @@ func (i *ip2Location) FetchInfo(ctx context.Context, ip netip.Addr) ( return result, fmt.Errorf("%w from %s: %d %s", ErrTooManyRequests, url, response.StatusCode, response.Status) default: - return result, fmt.Errorf("%w from %s: %d %s", - ErrBadHTTPStatus, url, response.StatusCode, response.Status) + return result, fmt.Errorf("bad HTTP status received from %s: %d %s", + url, response.StatusCode, response.Status) } decoder := json.NewDecoder(response.Body) diff --git a/internal/publicip/api/ipinfo.go b/internal/publicip/api/ipinfo.go index e5651f47..48bf9a30 100644 --- a/internal/publicip/api/ipinfo.go +++ b/internal/publicip/api/ipinfo.go @@ -70,7 +70,7 @@ func (i *ipInfo) FetchInfo(ctx context.Context, ip netip.Addr) ( defer response.Body.Close() if i.token != "" && response.StatusCode == http.StatusUnauthorized { - return result, fmt.Errorf("%w: %s", ErrTokenNotValid, response.Status) + return result, fmt.Errorf("token is not valid: %s", response.Status) } switch response.StatusCode { @@ -79,8 +79,8 @@ func (i *ipInfo) FetchInfo(ctx context.Context, ip netip.Addr) ( return result, fmt.Errorf("%w from %s: %d %s", ErrTooManyRequests, url, response.StatusCode, response.Status) default: - return result, fmt.Errorf("%w from %s: %d %s", - ErrBadHTTPStatus, url, response.StatusCode, response.Status) + return result, fmt.Errorf("bad HTTP status received from %s: %d %s", + url, response.StatusCode, response.Status) } decoder := json.NewDecoder(response.Body) diff --git a/internal/routing/default.go b/internal/routing/default.go index 3027bd6f..20f11ef8 100644 --- a/internal/routing/default.go +++ b/internal/routing/default.go @@ -1,15 +1,12 @@ package routing import ( - "errors" "fmt" "net/netip" "github.com/qdm12/gluetun/internal/netlink" ) -var ErrRouteDefaultNotFound = errors.New("default route not found") - type DefaultRoute struct { NetInterface string Gateway netip.Addr @@ -60,7 +57,7 @@ func (r *Routing) DefaultRoutes() (defaultRoutes []DefaultRoute, err error) { } if len(defaultRoutes) == 0 { - return nil, fmt.Errorf("%w: in %d route(s)", ErrRouteDefaultNotFound, len(routes)) + return nil, fmt.Errorf("default route not found: in %d route(s)", len(routes)) } return defaultRoutes, nil diff --git a/internal/routing/errors.go b/internal/routing/errors.go deleted file mode 100644 index 0750db36..00000000 --- a/internal/routing/errors.go +++ /dev/null @@ -1,7 +0,0 @@ -package routing - -import ( - "errors" -) - -var ErrLinkDefaultNotFound = errors.New("default link not found") diff --git a/internal/routing/ip.go b/internal/routing/ip.go index a43ced75..97011906 100644 --- a/internal/routing/ip.go +++ b/internal/routing/ip.go @@ -1,7 +1,6 @@ package routing import ( - "errors" "fmt" "net" "net/netip" @@ -14,8 +13,6 @@ func ipIsPrivate(ip netip.Addr) bool { ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() } -var errInterfaceIPNotFound = errors.New("IP address not found for interface") - func ipMatchesFamily(ip netip.Addr, family uint8) bool { return (family == netlink.FamilyV4 && ip.Is4()) || (family == netlink.FamilyV6 && ip.Is6()) @@ -46,6 +43,6 @@ func (r *Routing) AssignedIP(interfaceName string, family uint8) (ip netip.Addr, return ip, nil } - return ip, fmt.Errorf("%w: interface %s in %d addresses", - errInterfaceIPNotFound, interfaceName, len(addresses)) + return ip, fmt.Errorf("IP address not found for interface: interface %s in %d addresses", + interfaceName, len(addresses)) } diff --git a/internal/routing/local.go b/internal/routing/local.go index 564ec120..aa29d439 100644 --- a/internal/routing/local.go +++ b/internal/routing/local.go @@ -1,19 +1,12 @@ package routing import ( - "errors" "fmt" "net/netip" "github.com/qdm12/gluetun/internal/netlink" ) -var ( - ErrLinkLocalNotFound = errors.New("local link not found") - ErrSubnetDefaultNotFound = errors.New("default subnet not found") - ErrSubnetLocalNotFound = errors.New("local subnet not found") -) - type LocalNetwork struct { IPNet netip.Prefix InterfaceName string @@ -38,7 +31,7 @@ func (r *Routing) LocalNetworks() (localNetworks []LocalNetwork, err error) { } if len(localLinks) == 0 { - return localNetworks, fmt.Errorf("%w: in %d links", ErrLinkLocalNotFound, len(links)) + return localNetworks, fmt.Errorf("local link not found: in %d links", len(links)) } routes, err := r.netLinker.RouteList(netlink.FamilyAll) @@ -82,7 +75,7 @@ func (r *Routing) LocalNetworks() (localNetworks []LocalNetwork, err error) { } if len(localNetworks) == 0 { - return localNetworks, fmt.Errorf("%w: in %d routes", ErrSubnetLocalNotFound, len(routes)) + return localNetworks, fmt.Errorf("local subnet not found: in %d routes", len(routes)) } return localNetworks, nil diff --git a/internal/routing/vpn.go b/internal/routing/vpn.go index d83039ee..1775c038 100644 --- a/internal/routing/vpn.go +++ b/internal/routing/vpn.go @@ -1,18 +1,12 @@ package routing import ( - "errors" "fmt" "net/netip" "github.com/qdm12/gluetun/internal/netlink" ) -var ( - ErrVPNLocalGatewayIPNotFound = errors.New("VPN local gateway IP address not found") - ErrVPNLocalGatewayIPv6NotSupported = errors.New("VPN local gateway IPv6 address not supported") -) - func (r *Routing) VPNLocalGatewayIP(vpnIntf string) (ip netip.Addr, err error) { vpnLink, err := r.netLinker.LinkByName(vpnIntf) if err != nil { @@ -36,7 +30,7 @@ func (r *Routing) VPNLocalGatewayIP(vpnIntf string) (ip netip.Addr, err error) { route.Dst.Addr().Compare(route.Src.Addr()) == 0 && route.Table == tableLocal: // Wireguard if route.Src.Addr().Is6() { - return netip.Addr{}, fmt.Errorf("%w: %s", ErrVPNLocalGatewayIPv6NotSupported, route.Src) + return netip.Addr{}, fmt.Errorf("VPN local gateway IPv6 address not supported: %s", route.Src) } bytes := route.Src.Addr().As4() // force last byte to 1 to get the VPN gateway IP @@ -45,11 +39,9 @@ func (r *Routing) VPNLocalGatewayIP(vpnIntf string) (ip netip.Addr, err error) { return netip.AddrFrom4(bytes), nil } } - return ip, fmt.Errorf("%w: in %d routes", ErrVPNLocalGatewayIPNotFound, len(routes)) + return ip, fmt.Errorf("VPN local gateway IP address not found: in %d routes", len(routes)) } -var ErrVPNRouteNotFound = errors.New("VPN route not found") - // VPNRoutes returns the routes that are using the VPN interface, excluding local routes // and link-local multicast and unicast routes. func (r *Routing) VPNRoutes(vpnIntf string) (routes []netlink.Route, err error) { @@ -80,8 +72,8 @@ func (r *Routing) VPNRoutes(vpnIntf string) (routes []netlink.Route, err error) } if len(routes) == 0 { - return nil, fmt.Errorf("%w: for interface %s in %d routes", - ErrVPNRouteNotFound, vpnIntf, len(allRoutes)) + return nil, fmt.Errorf("VPN route not found: for interface %s in %d routes", + vpnIntf, len(allRoutes)) } return routes, nil diff --git a/internal/server/middlewares/auth/lookup.go b/internal/server/middlewares/auth/lookup.go index d02c433b..abbb287e 100644 --- a/internal/server/middlewares/auth/lookup.go +++ b/internal/server/middlewares/auth/lookup.go @@ -21,7 +21,7 @@ func settingsToLookupMap(settings Settings) (routeToRoles map[string][]internalR case AuthBasic: checker = newBasicAuthMethod(role.Username, role.Password) default: - return nil, fmt.Errorf("%w: %s", ErrMethodNotSupported, role.Auth) + return nil, fmt.Errorf("authentication method not supported: %s", role.Auth) } iRole := internalRole{ diff --git a/internal/server/middlewares/auth/lookup_test.go b/internal/server/middlewares/auth/lookup_test.go index e3e07525..3744a144 100644 --- a/internal/server/middlewares/auth/lookup_test.go +++ b/internal/server/middlewares/auth/lookup_test.go @@ -13,7 +13,6 @@ func Test_settingsToLookupMap(t *testing.T) { testCases := map[string]struct { settings Settings routeToRoles map[string][]internalRole - errWrapped error errMessage string }{ "empty_settings": { @@ -23,7 +22,6 @@ func Test_settingsToLookupMap(t *testing.T) { settings: Settings{ Roles: []Role{{Name: "a", Auth: "bad"}}, }, - errWrapped: ErrMethodNotSupported, errMessage: "authentication method not supported: bad", }, "success": { @@ -51,9 +49,10 @@ func Test_settingsToLookupMap(t *testing.T) { routeToRoles, err := settingsToLookupMap(testCase.settings) assert.Equal(t, testCase.routeToRoles, routeToRoles) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) } diff --git a/internal/server/middlewares/auth/settings.go b/internal/server/middlewares/auth/settings.go index eaaf45ef..108400d8 100644 --- a/internal/server/middlewares/auth/settings.go +++ b/internal/server/middlewares/auth/settings.go @@ -3,7 +3,6 @@ package auth import ( "bytes" "encoding/json" - "errors" "fmt" "net/http" "slices" @@ -106,28 +105,19 @@ type Role struct { Routes []string `json:"-"` } -var ( - ErrMethodNotSupported = errors.New("authentication method not supported") - ErrAPIKeyEmpty = errors.New("api key is empty") - ErrBasicUsernameEmpty = errors.New("username is empty") - ErrBasicPasswordEmpty = errors.New("password is empty") - ErrRoutePathNotSupported = errors.New("route path not supported by the control server") - ErrRouteMethodNotSupported = errors.New("route method not supported for the path") -) - func (r Role) Validate() (err error) { err = validate.IsOneOf(r.Auth, AuthNone, AuthAPIKey, AuthBasic) if err != nil { - return fmt.Errorf("%w: %s", ErrMethodNotSupported, r.Auth) + return fmt.Errorf("authentication method not supported: %s", r.Auth) } switch { case r.Auth == AuthAPIKey && r.APIKey == "": - return fmt.Errorf("for role %s: %w", r.Name, ErrAPIKeyEmpty) + return fmt.Errorf("for role %s: api key is empty", r.Name) case r.Auth == AuthBasic && r.Username == "": - return fmt.Errorf("for role %s: %w", r.Name, ErrBasicUsernameEmpty) + return fmt.Errorf("for role %s: username is empty", r.Name) case r.Auth == AuthBasic && r.Password == "": - return fmt.Errorf("for role %s: %w", r.Name, ErrBasicPasswordEmpty) + return fmt.Errorf("for role %s: password is empty", r.Name) } for i, route := range r.Routes { @@ -136,11 +126,11 @@ func (r Role) Validate() (err error) { method, path := parts[0], parts[1] methods, ok := validRoutes[path] if !ok { - return fmt.Errorf("route %d of %d: %w: %s", - i+1, len(r.Routes), ErrRoutePathNotSupported, path) + return fmt.Errorf("route %d of %d: route path not supported by the control server: %s", + i+1, len(r.Routes), path) } else if !slices.Contains(methods, method) { - return fmt.Errorf("route %d of %d: %w: %s for path %s", - i+1, len(r.Routes), ErrRouteMethodNotSupported, method, path) + return fmt.Errorf("route %d of %d: route method not supported for the path: %s for path %s", + i+1, len(r.Routes), method, path) } } diff --git a/internal/server/wrappers.go b/internal/server/wrappers.go index 5bc5e049..07c61bd4 100644 --- a/internal/server/wrappers.go +++ b/internal/server/wrappers.go @@ -1,7 +1,6 @@ package server import ( - "errors" "fmt" "github.com/qdm12/gluetun/internal/constants" @@ -12,16 +11,14 @@ type statusWrapper struct { Status string `json:"status"` } -var errInvalidStatus = errors.New("invalid status") - func (sw *statusWrapper) getStatus() (status models.LoopStatus, err error) { status = models.LoopStatus(sw.Status) switch status { case constants.Stopped, constants.Running: return status, nil default: - return "", fmt.Errorf("%w: %s: possible values are: %s, %s", - errInvalidStatus, sw.Status, constants.Stopped, constants.Running) + return "", fmt.Errorf("invalid status: %s: possible values are: %s, %s", + sw.Status, constants.Stopped, constants.Running) } } diff --git a/internal/shadowsocks/state.go b/internal/shadowsocks/state.go index 03d5b79c..49148467 100644 --- a/internal/shadowsocks/state.go +++ b/internal/shadowsocks/state.go @@ -2,7 +2,6 @@ package shadowsocks import ( "context" - "errors" "fmt" "reflect" "sync" @@ -31,8 +30,6 @@ func (l *Loop) GetStatus() (status models.LoopStatus) { return l.state.status } -var ErrInvalidStatus = errors.New("invalid status") - func (l *Loop) SetStatus(ctx context.Context, status models.LoopStatus) ( outcome string, err error, ) { @@ -80,8 +77,8 @@ func (l *Loop) SetStatus(ctx context.Context, status models.LoopStatus) ( l.state.status = newStatus return status.String(), nil default: - return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s", - ErrInvalidStatus, status, constants.Running, constants.Stopped) + return "", fmt.Errorf("invalid status: %s: it can only be one of: %s, %s", + status, constants.Running, constants.Stopped) } } diff --git a/internal/storage/filter.go b/internal/storage/filter.go index 4d74b6bc..81d6cdbc 100644 --- a/internal/storage/filter.go +++ b/internal/storage/filter.go @@ -1,6 +1,7 @@ package storage import ( + "errors" "fmt" "strings" @@ -28,7 +29,7 @@ func (s *Storage) FilterServers(provider string, selection settings.ServerSelect allServers := serversObject.Servers if len(allServers) == 0 { - return nil, ErrNoServerFound + return nil, errors.New("no server found") } for _, server := range allServers { diff --git a/internal/storage/formatting.go b/internal/storage/formatting.go index 3bf71b93..44865a5b 100644 --- a/internal/storage/formatting.go +++ b/internal/storage/formatting.go @@ -1,7 +1,6 @@ package storage import ( - "errors" "fmt" "strconv" "strings" @@ -15,8 +14,6 @@ func commaJoin(slice []string) string { return strings.Join(slice, ", ") } -var ErrNoServerFound = errors.New("no server found") - func noServerFoundError(selection settings.ServerSelection) (err error) { var messageParts []string @@ -160,5 +157,5 @@ func noServerFoundError(selection settings.ServerSelection) (err error) { message := "for " + strings.Join(messageParts, "; ") - return fmt.Errorf("%w: %s", ErrNoServerFound, message) + return fmt.Errorf("no server found: %s", message) } diff --git a/internal/tun/check.go b/internal/tun/check.go index 98d07284..14be916c 100644 --- a/internal/tun/check.go +++ b/internal/tun/check.go @@ -9,11 +9,6 @@ import ( "syscall" ) -var ( - ErrTUNInfo = errors.New("cannot get syscall stat info of TUN file") - ErrTUNBadRdev = errors.New("TUN file has an unexpected rdev") -) - // Check checks the tunnel device specified by path is present and accessible. func (t *Tun) Check(path string) error { f, err := os.OpenFile(path, os.O_RDWR, 0) @@ -29,13 +24,13 @@ func (t *Tun) Check(path string) error { sys, ok := info.Sys().(*syscall.Stat_t) if !ok { - return fmt.Errorf("%w", ErrTUNInfo) + return errors.New("cannot get syscall stat info of TUN file") } const expectedRdev = 2760 // corresponds to major 10 and minor 200 if sys.Rdev != expectedRdev { - return fmt.Errorf("%w: %d instead of expected %d", - ErrTUNBadRdev, sys.Rdev, expectedRdev) + return fmt.Errorf("TUN file has an unexpected rdev: %d instead of expected %d", + sys.Rdev, expectedRdev) } if err := f.Close(); err != nil { diff --git a/internal/updater/html/fetch.go b/internal/updater/html/fetch.go index 39603c1c..2883fd2c 100644 --- a/internal/updater/html/fetch.go +++ b/internal/updater/html/fetch.go @@ -2,15 +2,12 @@ package html import ( "context" - "errors" "fmt" "net/http" "golang.org/x/net/html" ) -var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code is not OK") - func Fetch(ctx context.Context, client *http.Client, url string) ( rootNode *html.Node, err error, ) { @@ -25,7 +22,7 @@ func Fetch(ctx context.Context, client *http.Client, url string) ( } if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%w: %d %s", ErrHTTPStatusCodeNotOK, + return nil, fmt.Errorf("HTTP status code not OK: %d %s", response.StatusCode, response.Status) } diff --git a/internal/updater/html/fetch_test.go b/internal/updater/html/fetch_test.go index a78d06d3..454de322 100644 --- a/internal/updater/html/fetch_test.go +++ b/internal/updater/html/fetch_test.go @@ -37,21 +37,18 @@ func Test_Fetch(t *testing.T) { responseStatus int responseBody io.ReadCloser rootNode *html.Node - errWrapped error errMessage string }{ "context canceled": { ctx: canceledCtx, url: "https://example.com/path", - errWrapped: context.Canceled, errMessage: `Get "https://example.com/path": context canceled`, }, "response status not ok": { ctx: context.Background(), url: "https://example.com/path", responseStatus: http.StatusNotFound, - errWrapped: ErrHTTPStatusCodeNotOK, - errMessage: `HTTP status code is not OK: 404 Not Found`, + errMessage: `HTTP status code not OK: 404 Not Found`, }, "success": { ctx: context.Background(), @@ -86,9 +83,10 @@ func Test_Fetch(t *testing.T) { rootNode, err := Fetch(testCase.ctx, client, testCase.url) - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } assert.Equal(t, testCase.rootNode, rootNode) }) diff --git a/internal/updater/loop/state.go b/internal/updater/loop/state.go index 551a1112..80962e93 100644 --- a/internal/updater/loop/state.go +++ b/internal/updater/loop/state.go @@ -2,7 +2,6 @@ package loop import ( "context" - "errors" "fmt" "reflect" "sync" @@ -31,8 +30,6 @@ func (l *Loop) GetStatus() (status models.LoopStatus) { return l.state.status } -var ErrInvalidStatus = errors.New("invalid status") - func (l *Loop) SetStatus(ctx context.Context, status models.LoopStatus) (outcome string, err error) { l.state.statusMu.Lock() defer l.state.statusMu.Unlock() @@ -79,8 +76,8 @@ func (l *Loop) SetStatus(ctx context.Context, status models.LoopStatus) (outcome l.state.status = newStatus return status.String(), nil default: - return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s", - ErrInvalidStatus, status, constants.Running, constants.Stopped) + return "", fmt.Errorf("invalid status: %s: it can only be one of: %s, %s", + status, constants.Running, constants.Stopped) } } diff --git a/internal/updater/openvpn/extract.go b/internal/updater/openvpn/extract.go index 00ed9ce8..d62ea9e7 100644 --- a/internal/updater/openvpn/extract.go +++ b/internal/updater/openvpn/extract.go @@ -8,12 +8,6 @@ import ( "strings" ) -var ( - ErrUnknownProto = errors.New("unknown protocol") - ErrNoRemoteHost = errors.New("remote host not found") - ErrNoRemoteIP = errors.New("remote IP not found") -) - func ExtractProto(b []byte) (tcp, udp bool, err error) { lines := strings.Split(string(b), "\n") const protoPrefix = "proto " @@ -30,7 +24,7 @@ func ExtractProto(b []byte) (tcp, udp bool, err error) { case "udp", "udp4", "udp6": return false, true, nil default: - return false, false, fmt.Errorf("%w: %s", ErrUnknownProto, s) + return false, false, fmt.Errorf("unknown protocol: %s", s) } } @@ -45,7 +39,7 @@ func ExtractHost(b []byte) (host, warning string, err error) { ) hosts := extractRemoteHosts(b, rejectIP, rejectDomain) if len(hosts) == 0 { - return "", "", ErrNoRemoteHost + return "", "", errors.New("remote host not found") } else if len(hosts) > 1 { warning = fmt.Sprintf( "only using the first host %q and discarding %d other hosts", @@ -58,7 +52,7 @@ func ExtractIPs(b []byte) (ips []netip.Addr, err error) { const rejectIP, rejectDomain = false, true ipStrings := extractRemoteHosts(b, rejectIP, rejectDomain) if len(ipStrings) == 0 { - return nil, ErrNoRemoteIP + return nil, errors.New("remote IP not found") } sort.Slice(ipStrings, func(i, j int) bool { diff --git a/internal/updater/openvpn/fetch.go b/internal/updater/openvpn/fetch.go index 61a4315a..0b8dd0cf 100644 --- a/internal/updater/openvpn/fetch.go +++ b/internal/updater/openvpn/fetch.go @@ -19,7 +19,7 @@ func FetchFile(ctx context.Context, client *http.Client, url string) ( const rejectDomain = false hosts := extractRemoteHosts(b, rejectIP, rejectDomain) if len(hosts) == 0 { - return "", fmt.Errorf("%w for url %s", ErrNoRemoteHost, url) + return "", fmt.Errorf("remote host not found for url %s", url) } return hosts[0], nil diff --git a/internal/updater/providers.go b/internal/updater/providers.go index 5222f961..237e843c 100644 --- a/internal/updater/providers.go +++ b/internal/updater/providers.go @@ -15,8 +15,6 @@ type Provider interface { FetchServers(ctx context.Context, minServers int) (servers []models.Server, err error) } -var ErrServerHasNotEnoughInformation = errors.New("server has not enough information") - func (u *Updater) updateProvider(ctx context.Context, provider Provider, minRatio float64, ) (err error) { diff --git a/internal/updater/resolver/parallel.go b/internal/updater/resolver/parallel.go index fed660e4..2e048d0f 100644 --- a/internal/updater/resolver/parallel.go +++ b/internal/updater/resolver/parallel.go @@ -2,7 +2,6 @@ package resolver import ( "context" - "errors" "fmt" "net/netip" ) @@ -34,11 +33,6 @@ type parallelResult struct { IPs []netip.Addr } -var ( - ErrMinFound = errors.New("not enough hosts found") - ErrMaxFailRatio = errors.New("maximum failure ratio reached") -) - func (pr *Parallel) Resolve(ctx context.Context, settings ParallelSettings) ( hostToIPs map[string][]netip.Addr, warnings []string, err error, ) { @@ -90,7 +84,7 @@ func (pr *Parallel) Resolve(ctx context.Context, settings ParallelSettings) ( failureRatio := float64(len(warnings)) / float64(len(settings.Hosts)) if failureRatio > settings.MaxFailRatio { return hostToIPs, warnings, - fmt.Errorf("%w: %.2f failure ratio reached", ErrMaxFailRatio, failureRatio) + fmt.Errorf("maximum failure ratio reached: %.2f failure ratio reached", failureRatio) } return hostToIPs, warnings, nil diff --git a/internal/updater/resolver/repeat.go b/internal/updater/resolver/repeat.go index 038d2105..000adb2e 100644 --- a/internal/updater/resolver/repeat.go +++ b/internal/updater/resolver/repeat.go @@ -2,7 +2,6 @@ package resolver import ( "context" - "errors" "fmt" "net" "net/netip" @@ -62,11 +61,6 @@ func (r *Repeat) Resolve(ctx context.Context, host string, settings RepeatSettin return ips, nil } -var ( - ErrMaxNoNew = errors.New("reached the maximum number of no new update") - ErrMaxFails = errors.New("reached the maximum number of consecutive failures") -) - func (r *Repeat) resolveOnce(ctx, timedCtx context.Context, host string, settings RepeatSettings, uniqueIPs map[string]struct{}, noNewCounter, failCounter int) ( newNoNewCounter, newFailCounter int, err error, @@ -75,8 +69,8 @@ func (r *Repeat) resolveOnce(ctx, timedCtx context.Context, host string, if err != nil { failCounter++ if settings.MaxFails > 0 && failCounter == settings.MaxFails { - return noNewCounter, failCounter, fmt.Errorf("%w: %d failed attempts resolving %s: %s", - ErrMaxFails, settings.MaxFails, host, err) + return noNewCounter, failCounter, fmt.Errorf("reached the maximum number of consecutive failures: "+ + "%d failed attempts resolving %s: %s", settings.MaxFails, host, err) } // it's fine to fail some of the resolutions return noNewCounter, failCounter, nil @@ -100,8 +94,9 @@ func (r *Repeat) resolveOnce(ctx, timedCtx context.Context, host string, // we reached the maximum number of resolutions without // finding any new IP address to our unique IP addresses set. return noNewCounter, failCounter, - fmt.Errorf("%w: %d times no updated for %d IP addresses found", - ErrMaxNoNew, noNewCounter, len(uniqueIPs)) + fmt.Errorf("reached the maximum number of no new update: "+ + "%d times no updated for %d IP addresses found", + noNewCounter, len(uniqueIPs)) } timer := time.NewTimer(settings.BetweenDuration) diff --git a/internal/updater/unzip/fetch.go b/internal/updater/unzip/fetch.go index be9efa3a..ba928e1a 100644 --- a/internal/updater/unzip/fetch.go +++ b/internal/updater/unzip/fetch.go @@ -2,14 +2,11 @@ package unzip import ( "context" - "errors" "fmt" "io" "net/http" ) -var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK") - func (u *Unzipper) FetchAndExtract(ctx context.Context, url string) ( contents map[string][]byte, err error, ) { @@ -26,7 +23,7 @@ func (u *Unzipper) FetchAndExtract(ctx context.Context, url string) ( defer response.Body.Close() if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%w: %s: %d %s", ErrHTTPStatusCodeNotOK, + return nil, fmt.Errorf("HTTP status code not OK: %s: %d %s", url, response.StatusCode, response.Status) } diff --git a/internal/version/github.go b/internal/version/github.go index 15aa4ee2..ad249ed7 100644 --- a/internal/version/github.go +++ b/internal/version/github.go @@ -3,7 +3,6 @@ package version import ( "context" "encoding/json" - "errors" "fmt" "net/http" "time" @@ -25,8 +24,6 @@ type githubCommit struct { } `json:"commit"` } -var errHTTPStatusCode = errors.New("bad response HTTP status code") - func getGithubReleases(ctx context.Context, client *http.Client) (releases []githubRelease, err error) { // Define a timeout since the default client has a large timeout and we don't // want to wait too long. @@ -47,7 +44,7 @@ func getGithubReleases(ctx context.Context, client *http.Client) (releases []git defer response.Body.Close() if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%w: %d %s", errHTTPStatusCode, + return nil, fmt.Errorf("bad response HTTP status code: %d %s", response.StatusCode, response.Status) } diff --git a/internal/version/version.go b/internal/version/version.go index 010cab1f..ba154fb2 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -44,8 +44,6 @@ func GetMessage(ctx context.Context, buildInfo models.BuildInformation, nil } -var errReleaseNotFound = errors.New("release not found") - func getLatestRelease(ctx context.Context, client *http.Client) (tagName, name string, time time.Time, err error) { releases, err := getGithubReleases(ctx, client) if err != nil { @@ -61,11 +59,9 @@ func getLatestRelease(ctx context.Context, client *http.Client) (tagName, name s } return release.TagName, release.Name, release.PublishedAt, nil } - return "", "", time, fmt.Errorf("%w", errReleaseNotFound) + return "", "", time, errors.New("release not found") } -var errCommitNotFound = errors.New("commit not found") - func getCommitsSince(ctx context.Context, client *http.Client, commitShort string) (n int, err error) { commits, err := getGithubCommits(ctx, client) if err != nil { @@ -77,5 +73,5 @@ func getCommitsSince(ctx context.Context, client *http.Client, commitShort strin } n++ } - return 0, fmt.Errorf("%w: %s", errCommitNotFound, commitShort) + return 0, fmt.Errorf("commit not found: %s", commitShort) } diff --git a/internal/vpn/portforward.go b/internal/vpn/portforward.go index 584a8225..ec8a7862 100644 --- a/internal/vpn/portforward.go +++ b/internal/vpn/portforward.go @@ -2,7 +2,6 @@ package vpn import ( "context" - "errors" "fmt" "github.com/qdm12/gluetun/internal/portforward" @@ -55,8 +54,6 @@ func newNoPortForwarder(providerName string) *noPortForwarder { } } -var ErrPortForwardingNotSupported = errors.New("custom port forwarding obtention is not supported") - func (n *noPortForwarder) Name() string { return n.providerName } @@ -64,9 +61,9 @@ func (n *noPortForwarder) Name() string { func (n *noPortForwarder) PortForward(context.Context, pfutils.PortForwardObjects) ( internalToExternalPorts map[uint16]uint16, err error, ) { - return nil, fmt.Errorf("%w: for %s", ErrPortForwardingNotSupported, n.providerName) + return nil, fmt.Errorf("custom port forwarding obtention is not supported: for %s", n.providerName) } func (n *noPortForwarder) KeepPortForward(context.Context, pfutils.PortForwardObjects) (err error) { - return fmt.Errorf("%w: for %s", ErrPortForwardingNotSupported, n.providerName) + return fmt.Errorf("custom port forwarding obtention is not supported: for %s", n.providerName) } diff --git a/internal/wireguard/config.go b/internal/wireguard/config.go index a0752cd0..9dd3de74 100644 --- a/internal/wireguard/config.go +++ b/internal/wireguard/config.go @@ -1,6 +1,7 @@ package wireguard import ( + "errors" "fmt" "net" "net/netip" @@ -27,19 +28,19 @@ func ConfigureDevice(client *wgctrl.Client, settings Settings) (err error) { func makeDeviceConfig(settings Settings) (config wgtypes.Config, err error) { privateKey, err := wgtypes.ParseKey(settings.PrivateKey) if err != nil { - return config, ErrPrivateKeyInvalid + return config, errors.New("cannot parse private key") } publicKey, err := wgtypes.ParseKey(settings.PublicKey) if err != nil { - return config, fmt.Errorf("%w: %s", ErrPublicKeyInvalid, settings.PublicKey) + return config, fmt.Errorf("cannot parse public key: %s", settings.PublicKey) } var preSharedKey *wgtypes.Key if settings.PreSharedKey != "" { preSharedKeyValue, err := wgtypes.ParseKey(settings.PreSharedKey) if err != nil { - return config, ErrPreSharedKeyInvalid + return config, errors.New("cannot parse pre-shared key") } preSharedKey = &preSharedKeyValue } diff --git a/internal/wireguard/config_test.go b/internal/wireguard/config_test.go index dfa24620..cff564d2 100644 --- a/internal/wireguard/config_test.go +++ b/internal/wireguard/config_test.go @@ -38,7 +38,7 @@ func Test_makeDeviceConfig(t *testing.T) { settings: Settings{ PrivateKey: "bad key", }, - err: ErrPrivateKeyInvalid, + err: errors.New("cannot parse private key"), }, "bad public key": { settings: Settings{ diff --git a/internal/wireguard/constructor_test.go b/internal/wireguard/constructor_test.go index 8ca2b89c..b80e64fc 100644 --- a/internal/wireguard/constructor_test.go +++ b/internal/wireguard/constructor_test.go @@ -1,6 +1,7 @@ package wireguard import ( + "errors" "net/netip" "testing" @@ -25,7 +26,7 @@ func Test_New(t *testing.T) { settings: Settings{ PrivateKey: "", }, - err: ErrPrivateKeyMissing, + err: errors.New("private key is missing"), }, "minimal valid settings": { settings: Settings{ diff --git a/internal/wireguard/run.go b/internal/wireguard/run.go index db23161b..deecc756 100644 --- a/internal/wireguard/run.go +++ b/internal/wireguard/run.go @@ -14,12 +14,6 @@ import ( "golang.zx2c4.com/wireguard/wgctrl" ) -var ( - errKernelSupport = errors.New("kernel does not support Wireguard") - errTunNameMismatch = errors.New("TUN device name is mismatching") - errDeviceWaited = errors.New("device waited for") -) - // Run runs the wireguard interface and waits until the context is done, then it cleans up the // interface and returns any error that occurred during setup or waiting. It sends an error to // waitError if any error occurs during setup or waiting, otherwise it sends nil when the context @@ -44,7 +38,7 @@ func (w *Wireguard) Run(ctx context.Context, waitError chan<- error, ready chan< case "userspace": case "kernelspace": if !kernelSupported { - waitError <- fmt.Errorf("%w", errKernelSupport) + waitError <- errors.New("kernel does not support Wireguard") return } setupFunction = setupKernelSpace @@ -199,8 +193,7 @@ func setupUserSpace(ctx context.Context, if err != nil { return 0, nil, fmt.Errorf("getting created TUN device name: %w", err) } else if tunName != interfaceName { - return 0, nil, fmt.Errorf("%w: expected %q and got %q", - errTunNameMismatch, interfaceName, tunName) + return 0, nil, fmt.Errorf("TUN device name is mismatching: expected %q and got %q", interfaceName, tunName) } link, err := netLinker.LinkByName(interfaceName) @@ -247,7 +240,7 @@ func setupUserSpace(ctx context.Context, case err = <-uapiAcceptErrorCh: close(uapiAcceptErrorCh) case <-device.Wait(): - err = errDeviceWaited + err = errors.New("device waited for") } cleanups.Cleanup(logger) diff --git a/internal/wireguard/settings.go b/internal/wireguard/settings.go index 66eee765..0d290ed6 100644 --- a/internal/wireguard/settings.go +++ b/internal/wireguard/settings.go @@ -88,99 +88,77 @@ func (s *Settings) SetDefaults() { } } -var ( - ErrInterfaceNameInvalid = errors.New("invalid interface name") - ErrPrivateKeyMissing = errors.New("private key is missing") - ErrPrivateKeyInvalid = errors.New("cannot parse private key") - ErrPublicKeyMissing = errors.New("public key is missing") - ErrPublicKeyInvalid = errors.New("cannot parse public key") - ErrPreSharedKeyInvalid = errors.New("cannot parse pre-shared key") - ErrEndpointAddrMissing = errors.New("endpoint address is missing") - ErrEndpointPortMissing = errors.New("endpoint port is missing") - ErrAddressMissing = errors.New("interface address is missing") - ErrAddressNotValid = errors.New("interface address is not valid") - ErrAllowedIPsMissing = errors.New("allowed IPs are missing") - ErrAllowedIPNotValid = errors.New("allowed IP is not valid") - ErrAllowedIPv6NotSupported = errors.New("allowed IPv6 address not supported") - ErrKeepaliveIsNegative = errors.New("keep alive interval is negative") - ErrFirewallMarkMissing = errors.New("firewall mark is missing") - ErrMTUMissing = errors.New("MTU is missing") - ErrImplementationInvalid = errors.New("invalid implementation") -) - var interfaceNameRegexp = regexp.MustCompile(`^[a-zA-Z0-9_]+$`) func (s *Settings) Check() (err error) { if !interfaceNameRegexp.MatchString(s.InterfaceName) { - return fmt.Errorf("%w: %s", ErrInterfaceNameInvalid, s.InterfaceName) + return fmt.Errorf("invalid interface name: %s", s.InterfaceName) } if s.PrivateKey == "" { - return fmt.Errorf("%w", ErrPrivateKeyMissing) + return errors.New("private key is missing") } else if _, err := wgtypes.ParseKey(s.PrivateKey); err != nil { - return fmt.Errorf("%w", ErrPrivateKeyInvalid) + return errors.New("cannot parse private key") } if s.PublicKey == "" { - return fmt.Errorf("%w", ErrPublicKeyMissing) + return errors.New("public key is missing") } else if _, err := wgtypes.ParseKey(s.PublicKey); err != nil { - return fmt.Errorf("%w: %s", ErrPublicKeyInvalid, s.PublicKey) + return fmt.Errorf("cannot parse public key: %s", s.PublicKey) } if s.PreSharedKey != "" { if _, err := wgtypes.ParseKey(s.PreSharedKey); err != nil { - return fmt.Errorf("%w", ErrPreSharedKeyInvalid) + return errors.New("cannot parse pre-shared key") } } switch { case !s.Endpoint.Addr().IsValid(): - return fmt.Errorf("%w", ErrEndpointAddrMissing) + return errors.New("endpoint address is missing") case s.Endpoint.Port() == 0: - return fmt.Errorf("%w", ErrEndpointPortMissing) + return errors.New("endpoint port is missing") } if len(s.Addresses) == 0 { - return fmt.Errorf("%w", ErrAddressMissing) + return errors.New("interface address is missing") } for i, addr := range s.Addresses { if !addr.IsValid() { - return fmt.Errorf("%w: for address %d of %d", - ErrAddressNotValid, i+1, len(s.Addresses)) + return fmt.Errorf("interface address is not valid: for address %d of %d", + i+1, len(s.Addresses)) } } if len(s.AllowedIPs) == 0 { - return fmt.Errorf("%w", ErrAllowedIPsMissing) + return errors.New("allowed IPs are missing") } for i, allowedIP := range s.AllowedIPs { switch { case !allowedIP.IsValid(): - return fmt.Errorf("%w: for allowed IP %d of %d", - ErrAllowedIPNotValid, i+1, len(s.AllowedIPs)) + return fmt.Errorf("allowed IP is not valid: for allowed IP %d of %d", + i+1, len(s.AllowedIPs)) case allowedIP.Addr().Is6() && !*s.IPv6: - return fmt.Errorf("%w: for allowed IP %s", - ErrAllowedIPv6NotSupported, allowedIP) + return fmt.Errorf("allowed IPv6 address not supported: for allowed IP %s", allowedIP) } } if s.PersistentKeepaliveInterval < 0 { - return fmt.Errorf("%w: %s", ErrKeepaliveIsNegative, - s.PersistentKeepaliveInterval) + return fmt.Errorf("keep alive interval is negative: %s", s.PersistentKeepaliveInterval) } if s.FirewallMark == 0 { - return fmt.Errorf("%w", ErrFirewallMarkMissing) + return errors.New("firewall mark is missing") } if s.MTU == 0 { - return fmt.Errorf("%w", ErrMTUMissing) + return errors.New("MTU is missing") } switch s.Implementation { case "auto", "kernelspace", "userspace": default: - return fmt.Errorf("%w: %s", ErrImplementationInvalid, s.Implementation) + return fmt.Errorf("invalid implementation: %s", s.Implementation) } return nil diff --git a/internal/wireguard/settings_test.go b/internal/wireguard/settings_test.go index a0fb4923..1ea48e98 100644 --- a/internal/wireguard/settings_test.go +++ b/internal/wireguard/settings_test.go @@ -84,25 +84,21 @@ func Test_Settings_Check(t *testing.T) { testCases := map[string]struct { settings Settings - errWrapped error errMessage string }{ "empty settings": { - errWrapped: ErrInterfaceNameInvalid, errMessage: "invalid interface name: ", }, "bad interface name": { settings: Settings{ InterfaceName: "$H1T", }, - errWrapped: ErrInterfaceNameInvalid, errMessage: "invalid interface name: $H1T", }, "empty private key": { settings: Settings{ InterfaceName: "wg0", }, - errWrapped: ErrPrivateKeyMissing, errMessage: "private key is missing", }, "bad private key": { @@ -110,7 +106,6 @@ func Test_Settings_Check(t *testing.T) { InterfaceName: "wg0", PrivateKey: "bad key", }, - errWrapped: ErrPrivateKeyInvalid, errMessage: "cannot parse private key", }, "empty public key": { @@ -118,7 +113,6 @@ func Test_Settings_Check(t *testing.T) { InterfaceName: "wg0", PrivateKey: validKey1, }, - errWrapped: ErrPublicKeyMissing, errMessage: "public key is missing", }, "bad public key": { @@ -127,7 +121,6 @@ func Test_Settings_Check(t *testing.T) { PrivateKey: validKey1, PublicKey: "bad key", }, - errWrapped: ErrPublicKeyInvalid, errMessage: "cannot parse public key: bad key", }, "bad preshared key": { @@ -137,7 +130,6 @@ func Test_Settings_Check(t *testing.T) { PublicKey: validKey2, PreSharedKey: "bad key", }, - errWrapped: ErrPreSharedKeyInvalid, errMessage: "cannot parse pre-shared key", }, "invalid endpoint address": { @@ -146,7 +138,6 @@ func Test_Settings_Check(t *testing.T) { PrivateKey: validKey1, PublicKey: validKey2, }, - errWrapped: ErrEndpointAddrMissing, errMessage: "endpoint address is missing", }, "zero endpoint port": { @@ -156,7 +147,6 @@ func Test_Settings_Check(t *testing.T) { PublicKey: validKey2, Endpoint: netip.AddrPortFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 0), }, - errWrapped: ErrEndpointPortMissing, errMessage: "endpoint port is missing", }, "no address": { @@ -166,7 +156,6 @@ func Test_Settings_Check(t *testing.T) { PublicKey: validKey2, Endpoint: netip.AddrPortFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 51820), }, - errWrapped: ErrAddressMissing, errMessage: "interface address is missing", }, "invalid address": { @@ -177,7 +166,6 @@ func Test_Settings_Check(t *testing.T) { Endpoint: netip.AddrPortFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 51820), Addresses: []netip.Prefix{{}}, }, - errWrapped: ErrAddressNotValid, errMessage: "interface address is not valid: for address 1 of 1", }, @@ -191,7 +179,6 @@ func Test_Settings_Check(t *testing.T) { netip.PrefixFrom(netip.AddrFrom4([4]byte{5, 6, 7, 8}), 24), }, }, - errWrapped: ErrAllowedIPsMissing, errMessage: "allowed IPs are missing", }, "invalid allowed IP": { @@ -205,7 +192,6 @@ func Test_Settings_Check(t *testing.T) { }, AllowedIPs: []netip.Prefix{{}}, }, - errWrapped: ErrAllowedIPNotValid, errMessage: "allowed IP is not valid: for allowed IP 1 of 1", }, "ipv6 allowed IP": { @@ -222,7 +208,6 @@ func Test_Settings_Check(t *testing.T) { }, IPv6: ptrTo(false), }, - errWrapped: ErrAllowedIPv6NotSupported, errMessage: "allowed IPv6 address not supported: for allowed IP ::/0", }, "zero firewall mark": { @@ -236,7 +221,6 @@ func Test_Settings_Check(t *testing.T) { netip.PrefixFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 24), }, }, - errWrapped: ErrFirewallMarkMissing, errMessage: "firewall mark is missing", }, "missing_MTU": { @@ -251,7 +235,6 @@ func Test_Settings_Check(t *testing.T) { }, FirewallMark: 999, }, - errWrapped: ErrMTUMissing, errMessage: "MTU is missing", }, "invalid implementation": { @@ -268,7 +251,6 @@ func Test_Settings_Check(t *testing.T) { MTU: 1420, Implementation: "x", }, - errWrapped: ErrImplementationInvalid, errMessage: "invalid implementation: x", }, "all valid": { @@ -297,9 +279,10 @@ func Test_Settings_Check(t *testing.T) { err := testCase.settings.Check() - assert.ErrorIs(t, err, testCase.errWrapped) - if testCase.errWrapped != nil { + if testCase.errMessage != "" { assert.EqualError(t, err, testCase.errMessage) + } else { + assert.NoError(t, err) } }) }