chore: do not use sentinel errors when unneeded

- main reason being it's a burden to always define sentinel errors at global scope, wrap them with `%w` instead of using a string directly
- only use sentinel errors when it has to be checked using `errors.Is`
- replace all usage of these sentinel errors in `fmt.Errorf` with direct strings that were in the sentinel error
- exclude the sentinel error definition requirement from .golangci.yml
- update unit tests to use ContainersError instead of ErrorIs so it stays as a "not a change detector test" without requiring a sentinel error
This commit is contained in:
Quentin McGaw
2026-05-02 00:50:16 +00:00
parent 9b6f048fe8
commit 4a78989d9d
172 changed files with 666 additions and 1433 deletions
+2 -4
View File
@@ -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
}
+5 -8
View File
@@ -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)
+1 -6
View File
@@ -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")
}
+6 -14
View File
@@ -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
}
+18 -34
View File
@@ -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