package conf
import (
"context"
"encoding/base64"
"encoding/hex"
"fmt"
"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:"" `
KernelTun * bool ` json:"kernelTun" `
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
kernelTunSupported , err := wireguard . KernelTunSupported ( )
if err != nil {
errors . LogWarning ( context . Background ( ) , fmt . Sprintf ( "Failed to check kernel TUN support: %v. This may indicate that your OS doesn't support kernel TUN or you lack the necessary permissions. Please ensure you have the required privileges." , err ) )
config . KernelMode = false
return config , nil
}
if c . KernelMode == nil {
c . KernelMode = c . KernelTun
}
if c . KernelMode != nil {
config . KernelMode = * c . KernelMode
if config . KernelMode && ! kernelTunSupported {
errors . LogWarning ( context . Background ( ) , "kernel TUN is not supported on your OS or permission is insufficient" )
}
} else {
config . KernelMode = kernelTunSupported
if config . KernelMode {
errors . LogDebug ( context . Background ( ) , "kernel TUN 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 )
}