Files
Quentin McGaw 4a78989d9d 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
2026-05-02 03:29:46 +00:00

235 lines
8.2 KiB
Go

package settings
import (
"fmt"
"strconv"
"strings"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gotree"
)
type AmneziaWg struct {
// Wireguard contains the configuration for Wireguard, given
// AmneziaWg is based on Wireguard
Wireguard Wireguard `json:"wireguard"`
JunkPacketCount *uint16 `json:"junk_packet_count"`
JunkPacketMin *uint16 `json:"junk_packet_min"`
JunkPacketMax *uint16 `json:"junk_packet_max"`
PaddingS1 *uint16 `json:"padding_s1"`
PaddingS2 *uint16 `json:"padding_s2"`
PaddingS3 *uint16 `json:"padding_s3"`
PaddingS4 *uint16 `json:"padding_s4"`
HeaderH1 *string `json:"header_h1"`
HeaderH2 *string `json:"header_h2"`
HeaderH3 *string `json:"header_h3"`
HeaderH4 *string `json:"header_h4"`
InitPacketI1 *string `json:"init_packet_i1"`
InitPacketI2 *string `json:"init_packet_i2"`
InitPacketI3 *string `json:"init_packet_i3"`
InitPacketI4 *string `json:"init_packet_i4"`
InitPacketI5 *string `json:"init_packet_i5"`
}
func (a *AmneziaWg) read(r *reader.Reader) (err error) {
const amneziawg = true
err = a.Wireguard.read(r, amneziawg)
if err != nil {
return err // do not wrap this error
}
uint16Fields := map[string]**uint16{
"AMNEZIAWG_JC": &a.JunkPacketCount,
"AMNEZIAWG_JMIN": &a.JunkPacketMin,
"AMNEZIAWG_JMAX": &a.JunkPacketMax,
"AMNEZIAWG_S1": &a.PaddingS1,
"AMNEZIAWG_S2": &a.PaddingS2,
"AMNEZIAWG_S3": &a.PaddingS3,
"AMNEZIAWG_S4": &a.PaddingS4,
}
for key, dst := range uint16Fields {
*dst, err = r.Uint16Ptr(key)
if err != nil {
return err
}
}
stringFields := map[string]**string{
"AMNEZIAWG_H1": &a.HeaderH1,
"AMNEZIAWG_H2": &a.HeaderH2,
"AMNEZIAWG_H3": &a.HeaderH3,
"AMNEZIAWG_H4": &a.HeaderH4,
"AMNEZIAWG_I1": &a.InitPacketI1,
"AMNEZIAWG_I2": &a.InitPacketI2,
"AMNEZIAWG_I3": &a.InitPacketI3,
"AMNEZIAWG_I4": &a.InitPacketI4,
"AMNEZIAWG_I5": &a.InitPacketI5,
}
opt := reader.ForceLowercase(false)
for key, dst := range stringFields {
*dst = r.Get(key, opt)
}
return nil
}
func (a AmneziaWg) copy() (copied AmneziaWg) {
return AmneziaWg{
Wireguard: a.Wireguard.copy(),
JunkPacketCount: gosettings.CopyPointer(a.JunkPacketCount),
JunkPacketMin: gosettings.CopyPointer(a.JunkPacketMin),
JunkPacketMax: gosettings.CopyPointer(a.JunkPacketMax),
PaddingS1: gosettings.CopyPointer(a.PaddingS1),
PaddingS2: gosettings.CopyPointer(a.PaddingS2),
PaddingS3: gosettings.CopyPointer(a.PaddingS3),
PaddingS4: gosettings.CopyPointer(a.PaddingS4),
HeaderH1: gosettings.CopyPointer(a.HeaderH1),
HeaderH2: gosettings.CopyPointer(a.HeaderH2),
HeaderH3: gosettings.CopyPointer(a.HeaderH3),
HeaderH4: gosettings.CopyPointer(a.HeaderH4),
InitPacketI1: gosettings.CopyPointer(a.InitPacketI1),
InitPacketI2: gosettings.CopyPointer(a.InitPacketI2),
InitPacketI3: gosettings.CopyPointer(a.InitPacketI3),
InitPacketI4: gosettings.CopyPointer(a.InitPacketI4),
InitPacketI5: gosettings.CopyPointer(a.InitPacketI5),
}
}
func (a *AmneziaWg) overrideWith(other AmneziaWg) {
a.Wireguard.overrideWith(other.Wireguard)
a.JunkPacketCount = gosettings.OverrideWithPointer(a.JunkPacketCount, other.JunkPacketCount)
a.JunkPacketMin = gosettings.OverrideWithPointer(a.JunkPacketMin, other.JunkPacketMin)
a.JunkPacketMax = gosettings.OverrideWithPointer(a.JunkPacketMax, other.JunkPacketMax)
a.PaddingS1 = gosettings.OverrideWithPointer(a.PaddingS1, other.PaddingS1)
a.PaddingS2 = gosettings.OverrideWithPointer(a.PaddingS2, other.PaddingS2)
a.PaddingS3 = gosettings.OverrideWithPointer(a.PaddingS3, other.PaddingS3)
a.PaddingS4 = gosettings.OverrideWithPointer(a.PaddingS4, other.PaddingS4)
a.HeaderH1 = gosettings.OverrideWithPointer(a.HeaderH1, other.HeaderH1)
a.HeaderH2 = gosettings.OverrideWithPointer(a.HeaderH2, other.HeaderH2)
a.HeaderH3 = gosettings.OverrideWithPointer(a.HeaderH3, other.HeaderH3)
a.HeaderH4 = gosettings.OverrideWithPointer(a.HeaderH4, other.HeaderH4)
a.InitPacketI1 = gosettings.OverrideWithPointer(a.InitPacketI1, other.InitPacketI1)
a.InitPacketI2 = gosettings.OverrideWithPointer(a.InitPacketI2, other.InitPacketI2)
a.InitPacketI3 = gosettings.OverrideWithPointer(a.InitPacketI3, other.InitPacketI3)
a.InitPacketI4 = gosettings.OverrideWithPointer(a.InitPacketI4, other.InitPacketI4)
a.InitPacketI5 = gosettings.OverrideWithPointer(a.InitPacketI5, other.InitPacketI5)
}
func (a *AmneziaWg) setDefaults(vpnProvider string) {
a.Wireguard.setDefaults(vpnProvider)
a.Wireguard.Implementation = "userspace" // unused except in logs
a.JunkPacketCount = gosettings.DefaultPointer(a.JunkPacketCount, 0)
a.JunkPacketMin = gosettings.DefaultPointer(a.JunkPacketMin, 0)
a.JunkPacketMax = gosettings.DefaultPointer(a.JunkPacketMax, 0)
a.PaddingS1 = gosettings.DefaultPointer(a.PaddingS1, 0)
a.PaddingS2 = gosettings.DefaultPointer(a.PaddingS2, 0)
a.PaddingS3 = gosettings.DefaultPointer(a.PaddingS3, 0)
a.PaddingS4 = gosettings.DefaultPointer(a.PaddingS4, 0)
a.HeaderH1 = gosettings.DefaultPointer(a.HeaderH1, "")
a.HeaderH2 = gosettings.DefaultPointer(a.HeaderH2, "")
a.HeaderH3 = gosettings.DefaultPointer(a.HeaderH3, "")
a.HeaderH4 = gosettings.DefaultPointer(a.HeaderH4, "")
a.InitPacketI1 = gosettings.DefaultPointer(a.InitPacketI1, "")
a.InitPacketI2 = gosettings.DefaultPointer(a.InitPacketI2, "")
a.InitPacketI3 = gosettings.DefaultPointer(a.InitPacketI3, "")
a.InitPacketI4 = gosettings.DefaultPointer(a.InitPacketI4, "")
a.InitPacketI5 = gosettings.DefaultPointer(a.InitPacketI5, "")
}
func (a AmneziaWg) toLinesNode() (node *gotree.Node) {
node = gotree.New("AmneziaWG settings:")
node.AppendNode(a.Wireguard.toLinesNode())
uintFields := []struct {
key string
val *uint16
}{
{"JC", a.JunkPacketCount},
{"JMIN", a.JunkPacketMin},
{"JMAX", a.JunkPacketMax},
{"S1", a.PaddingS1},
{"S2", a.PaddingS2},
{"S3", a.PaddingS3},
{"S4", a.PaddingS4},
}
for _, f := range uintFields {
node.Appendf("%s: %d", f.key, *f.val)
}
stringFields := []struct {
key string
val *string
}{
{"H1", a.HeaderH1},
{"H2", a.HeaderH2},
{"H3", a.HeaderH3},
{"H4", a.HeaderH4},
{"I1", a.InitPacketI1},
{"I2", a.InitPacketI2},
{"I3", a.InitPacketI3},
{"I4", a.InitPacketI4},
{"I5", a.InitPacketI5},
}
for _, f := range stringFields {
node.Appendf("%s: %s", f.key, *f.val)
}
return node
}
func (a AmneziaWg) validate(vpnProvider string, ipv6Supported bool) error {
const amneziaWG = true
err := a.Wireguard.validate(vpnProvider, ipv6Supported, amneziaWG)
if err != nil {
return fmt.Errorf("wireguard settings: %w", err)
}
if *a.JunkPacketCount == 0 {
if *a.JunkPacketMin != 0 || *a.JunkPacketMax != 0 {
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("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("junk packet minimum must be lower than or equal to maximum: "+
"jmin=%d and jmax=%d", *a.JunkPacketMin, *a.JunkPacketMax)
}
}
nameToHeaderRange := map[string]string{
"h1": *a.HeaderH1,
"h2": *a.HeaderH2,
"h3": *a.HeaderH3,
"h4": *a.HeaderH4,
}
for name, headerRange := range nameToHeaderRange {
if headerRange == "" {
continue
}
fields := strings.Split(headerRange, "-")
switch len(fields) {
case 1:
_, err := strconv.Atoi(fields[0])
if err != nil {
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("header range is malformed: "+
"%s value %s is not a valid range", name, headerRange)
}
}
default:
return fmt.Errorf("header range is malformed: "+
"%s value %s must be in the form n or n-m", name, headerRange)
}
}
return nil
}