/* SPDX-License-Identifier: MIT * * Copyright (C) 2019-2026 WireGuard LLC. All Rights Reserved. */ package conf import ( "crypto/rand" "crypto/subtle" "encoding/base64" "encoding/binary" "fmt" "net/netip" "slices" "strings" "time" "golang.org/x/crypto/curve25519" "golang.zx2c4.com/wireguard/windows/l18n" "golang.zx2c4.com/wireguard/windows/phobos" ) const KeyLength = 32 type Endpoint struct { Host string Port uint16 } type ( Key [KeyLength]byte HandshakeTime time.Duration Bytes uint64 ) type Config struct { Name string Interface Interface Peers []Peer Obfuscation *Obfuscation TrailingComments []string } type ObfuscationMode int const ( ObfuscationModeWireGuard ObfuscationMode = iota ObfuscationModeSocks5 ) type Obfuscation struct { Mode ObfuscationMode SourceListenPort uint16 Target Endpoint Key string Masking phobos.Masking ObfuscateBytes uint16 MaxDummy uint16 MediaPayloadType uint8 MediaSSRC uint32 MediaClock uint16 Login string Password string RxBytes Bytes TxBytes Bytes Comments SectionComments Socks5Comments SectionComments } const Socks5TunnelMTU = 1500 var ( Socks5TunnelAddresses = []netip.Prefix{ netip.MustParsePrefix("10.42.0.2/32"), netip.MustParsePrefix("fdcc:ad94:bacf:61a3::2/128"), } Socks5TunnelDNS = []netip.Addr{netip.MustParseAddr("1.1.1.1")} Socks5TunnelRoutes = []netip.Prefix{ netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0"), } ) func (conf *Config) RoutedPrefixes() []netip.Prefix { if conf.IsSocks5() { return Socks5TunnelRoutes } prefixes := make([]netip.Prefix, 0, len(conf.Peers)) for i := range conf.Peers { prefixes = append(prefixes, conf.Peers[i].AllowedIPs...) } return prefixes } func (conf *Config) IsSocks5() bool { return conf.Obfuscation != nil && conf.Obfuscation.Mode == ObfuscationModeSocks5 } func (conf *Config) applySocks5InterfaceDefaults() { conf.Interface.Addresses = slices.Clone(Socks5TunnelAddresses) conf.Interface.DNS = slices.Clone(Socks5TunnelDNS) conf.Interface.MTU = Socks5TunnelMTU } func (o *Obfuscation) MediaParams() phobos.MediaParams { params := phobos.MediaParams{PayloadType: o.MediaPayloadType, SSRC: o.MediaSSRC} if o.MediaClock > 0 { params.TimestampStep = uint16(90000 / uint32(o.MediaClock)) } return params } type Interface struct { PrivateKey Key Addresses []netip.Prefix ListenPort uint16 MTU uint16 DNS []netip.Addr DNSSearch []string PreUp string PostUp string PreDown string PostDown string TableOff bool Comments SectionComments } type Peer struct { PublicKey Key PresharedKey Key AllowedIPs []netip.Prefix Endpoint Endpoint PersistentKeepalive uint16 RxBytes Bytes TxBytes Bytes LastHandshakeTime HandshakeTime Obfuscation *Obfuscation Comments SectionComments } type Comments struct { Before []string Suffix string } type SectionComments struct { Header Comments Lines map[string]Comments } func (conf *Config) IntersectsWith(other *Config) bool { allRoutes := make(map[netip.Prefix]bool, len(conf.Interface.Addresses)*2+len(conf.Peers)*3) for _, a := range conf.Interface.Addresses { allRoutes[netip.PrefixFrom(a.Addr(), a.Addr().BitLen())] = true allRoutes[a.Masked()] = true } for i := range conf.Peers { for _, a := range conf.Peers[i].AllowedIPs { allRoutes[a.Masked()] = true } } for _, a := range other.Interface.Addresses { if allRoutes[netip.PrefixFrom(a.Addr(), a.Addr().BitLen())] { return true } if allRoutes[a.Masked()] { return true } } for i := range other.Peers { for _, a := range other.Peers[i].AllowedIPs { if allRoutes[a.Masked()] { return true } } } return false } func (e *Endpoint) String() string { if strings.IndexByte(e.Host, ':') != -1 { return fmt.Sprintf("[%s]:%d", e.Host, e.Port) } return fmt.Sprintf("%s:%d", e.Host, e.Port) } func (e *Endpoint) IsEmpty() bool { return len(e.Host) == 0 } func (k *Key) String() string { return base64.StdEncoding.EncodeToString(k[:]) } func (k *Key) IsZero() bool { var zeros Key return subtle.ConstantTimeCompare(zeros[:], k[:]) == 1 } func (k *Key) Public() *Key { var p [KeyLength]byte curve25519.ScalarBaseMult(&p, (*[KeyLength]byte)(k)) return (*Key)(&p) } func NewPresharedKey() (*Key, error) { var k [KeyLength]byte _, err := rand.Read(k[:]) if err != nil { return nil, err } return (*Key)(&k), nil } func NewPrivateKey() (*Key, error) { k, err := NewPresharedKey() if err != nil { return nil, err } k[0] &= 248 k[31] = (k[31] & 127) | 64 return k, nil } func NewPrivateKeyFromString(b64 string) (*Key, error) { return parseKeyBase64(b64) } func (t HandshakeTime) IsEmpty() bool { return t == HandshakeTime(0) } func (t HandshakeTime) String() string { u := time.Unix(0, 0).Add(time.Duration(t)).Unix() n := time.Now().Unix() if u == n { return l18n.Sprintf("Now") } else if u > n { return l18n.Sprintf("System clock wound backward!") } left := n - u years := left / (365 * 24 * 60 * 60) left = left % (365 * 24 * 60 * 60) days := left / (24 * 60 * 60) left = left % (24 * 60 * 60) hours := left / (60 * 60) left = left % (60 * 60) minutes := left / 60 seconds := left % 60 s := make([]string, 0, 5) if years > 0 { s = append(s, l18n.Sprintf("%d year(s)", years)) } if days > 0 { s = append(s, l18n.Sprintf("%d day(s)", days)) } if hours > 0 { s = append(s, l18n.Sprintf("%d hour(s)", hours)) } if minutes > 0 { s = append(s, l18n.Sprintf("%d minute(s)", minutes)) } if seconds > 0 { s = append(s, l18n.Sprintf("%d second(s)", seconds)) } timestamp := strings.Join(s, l18n.UnitSeparator()) return l18n.Sprintf("%s ago", timestamp) } func (b Bytes) String() string { if b < 1024 { return l18n.Sprintf("%d\u00a0B", b) } else if b < 1024*1024 { return l18n.Sprintf("%.2f\u00a0KiB", float64(b)/1024) } else if b < 1024*1024*1024 { return l18n.Sprintf("%.2f\u00a0MiB", float64(b)/(1024*1024)) } else if b < 1024*1024*1024*1024 { return l18n.Sprintf("%.2f\u00a0GiB", float64(b)/(1024*1024*1024)) } return l18n.Sprintf("%.2f\u00a0TiB", float64(b)/(1024*1024*1024)/1024) } func (conf *Config) DeduplicateNetworkEntries() { m := make(map[string]bool, len(conf.Interface.Addresses)) i := 0 for _, addr := range conf.Interface.Addresses { s := addr.String() if m[s] { continue } m[s] = true conf.Interface.Addresses[i] = addr i++ } conf.Interface.Addresses = conf.Interface.Addresses[:i] m = make(map[string]bool, len(conf.Interface.DNS)) i = 0 for _, addr := range conf.Interface.DNS { s := addr.String() if m[s] { continue } m[s] = true conf.Interface.DNS[i] = addr i++ } conf.Interface.DNS = conf.Interface.DNS[:i] for peerIdx := range conf.Peers { peer := &conf.Peers[peerIdx] m = make(map[string]bool, len(peer.AllowedIPs)) i = 0 for _, addr := range peer.AllowedIPs { s := addr.String() if m[s] { continue } m[s] = true peer.AllowedIPs[i] = addr i++ } peer.AllowedIPs = peer.AllowedIPs[:i] } } func (o *Obfuscation) redact() { o.Key = "" o.Login = "" o.Password = "" o.Comments = SectionComments{} o.Socks5Comments = SectionComments{} } func (conf *Config) Redact() { conf.Interface.PrivateKey = Key{} conf.Interface.PreUp = "" conf.Interface.PostUp = "" conf.Interface.PreDown = "" conf.Interface.PostDown = "" conf.Interface.Comments = SectionComments{} for i := range conf.Peers { conf.Peers[i].PublicKey = Key{} binary.LittleEndian.PutUint64(conf.Peers[i].PublicKey[:8], uint64(i)) conf.Peers[i].PresharedKey = Key{} conf.Peers[i].Comments = SectionComments{} if o := conf.Peers[i].Obfuscation; o != nil { o.redact() } } if conf.Obfuscation != nil { conf.Obfuscation.redact() } conf.TrailingComments = nil }