package conf import ( "context" "encoding/base64" "encoding/hex" "strings" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/proxy/wireguard" "google.golang.org/protobuf/proto" ) type WireGuardPeerConfig struct { PublicKey string `json:"publicKey"` PreSharedKey string `json:"preSharedKey"` Endpoint string `json:"endpoint"` KeepAlive uint32 `json:"keepAlive"` AllowedIPs []string `json:"allowedIPs,omitempty"` } func (c *WireGuardPeerConfig) Build() (proto.Message, error) { var err error config := new(wireguard.PeerConfig) if c.PublicKey != "" { config.PublicKey, err = ParseWireGuardKey(c.PublicKey) if err != nil { return nil, err } } if c.PreSharedKey != "" { config.PreSharedKey, err = ParseWireGuardKey(c.PreSharedKey) if err != nil { return nil, err } } config.Endpoint = c.Endpoint // default 0 config.KeepAlive = c.KeepAlive if c.AllowedIPs == nil { config.AllowedIps = []string{"0.0.0.0/0", "::0/0"} } else { config.AllowedIps = c.AllowedIPs } return config, nil } type WireGuardConfig struct { IsClient bool `json:""` KernelMode *bool `json:"kernelMode"` SecretKey string `json:"secretKey"` Address []string `json:"address"` Peers []*WireGuardPeerConfig `json:"peers"` MTU int32 `json:"mtu"` NumWorkers int32 `json:"workers"` Reserved []byte `json:"reserved"` DomainStrategy string `json:"domainStrategy"` } func (c *WireGuardConfig) Build() (proto.Message, error) { config := new(wireguard.DeviceConfig) var err error config.SecretKey, err = ParseWireGuardKey(c.SecretKey) if err != nil { return nil, err } if c.Address == nil { // bogon ips config.Endpoint = []string{"10.0.0.1", "fd59:7153:2388:b5fd:0000:0000:0000:0001"} } else { config.Endpoint = c.Address } if c.Peers != nil { config.Peers = make([]*wireguard.PeerConfig, len(c.Peers)) for i, p := range c.Peers { msg, err := p.Build() if err != nil { return nil, err } config.Peers[i] = msg.(*wireguard.PeerConfig) } } if c.MTU == 0 { config.Mtu = 1420 } else { config.Mtu = c.MTU } // these a fallback code exists in wireguard-go code, // we don't need to process fallback manually config.NumWorkers = c.NumWorkers if len(c.Reserved) != 0 && len(c.Reserved) != 3 { return nil, errors.New(`"reserved" should be empty or 3 bytes`) } config.Reserved = c.Reserved switch strings.ToLower(c.DomainStrategy) { case "forceip", "": config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP case "forceipv4": config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP4 case "forceipv6": config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP6 case "forceipv4v6": config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP46 case "forceipv6v4": config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP64 default: return nil, errors.New("unsupported domain strategy: ", c.DomainStrategy) } config.IsClient = c.IsClient if c.KernelMode != nil { config.KernelMode = *c.KernelMode if config.KernelMode && !wireguard.KernelTunSupported() { errors.LogWarning(context.Background(), "kernel mode is not supported on your OS or permission is insufficient") } } else { config.KernelMode = wireguard.KernelTunSupported() if config.KernelMode { errors.LogDebug(context.Background(), "kernel mode is enabled as it's supported and permission is sufficient") } } return config, nil } func ParseWireGuardKey(str string) (string, error) { var err error if len(str)%2 == 0 { _, err = hex.DecodeString(str) if err == nil { return str, nil } } var dat []byte str = strings.TrimSuffix(str, "=") if strings.ContainsRune(str, '+') || strings.ContainsRune(str, '/') { dat, err = base64.RawStdEncoding.DecodeString(str) } else { dat, err = base64.RawURLEncoding.DecodeString(str) } if err == nil { return hex.EncodeToString(dat), nil } return "", errors.New("failed to deserialize key").Base(err) }