2020-11-25 14:01:53 +03:00
package conf
import (
2021-10-22 07:04:06 +03:00
"encoding/base64"
2023-02-15 19:07:12 +03:00
"encoding/hex"
2020-11-25 14:01:53 +03:00
"encoding/json"
2021-03-06 17:45:12 +03:00
"math"
2021-03-14 10:10:10 +03:00
"net/url"
2023-02-15 19:07:12 +03:00
"runtime"
2021-03-14 10:10:10 +03:00
"strconv"
2020-11-25 14:01:53 +03:00
"strings"
2023-02-15 19:07:12 +03:00
"syscall"
2020-11-25 14:01:53 +03:00
"github.com/golang/protobuf/proto"
2023-02-15 19:07:12 +03:00
"github.com/xtls/xray-core/common/net"
2020-12-04 04:36:16 +03:00
"github.com/xtls/xray-core/common/platform/filesystem"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/domainsocket"
2021-10-12 18:58:12 +03:00
httpheader "github.com/xtls/xray-core/transport/internet/headers/http"
2020-12-04 04:36:16 +03:00
"github.com/xtls/xray-core/transport/internet/http"
"github.com/xtls/xray-core/transport/internet/kcp"
"github.com/xtls/xray-core/transport/internet/quic"
2023-02-15 19:07:12 +03:00
"github.com/xtls/xray-core/transport/internet/reality"
2020-12-04 04:36:16 +03:00
"github.com/xtls/xray-core/transport/internet/tcp"
"github.com/xtls/xray-core/transport/internet/tls"
"github.com/xtls/xray-core/transport/internet/websocket"
"github.com/xtls/xray-core/transport/internet/xtls"
2020-11-25 14:01:53 +03:00
)
var (
kcpHeaderLoader = NewJSONConfigLoader ( ConfigCreatorCache {
"none" : func ( ) interface { } { return new ( NoOpAuthenticator ) } ,
"srtp" : func ( ) interface { } { return new ( SRTPAuthenticator ) } ,
"utp" : func ( ) interface { } { return new ( UTPAuthenticator ) } ,
"wechat-video" : func ( ) interface { } { return new ( WechatVideoAuthenticator ) } ,
"dtls" : func ( ) interface { } { return new ( DTLSAuthenticator ) } ,
"wireguard" : func ( ) interface { } { return new ( WireguardAuthenticator ) } ,
} , "type" , "" )
tcpHeaderLoader = NewJSONConfigLoader ( ConfigCreatorCache {
"none" : func ( ) interface { } { return new ( NoOpConnectionAuthenticator ) } ,
"http" : func ( ) interface { } { return new ( Authenticator ) } ,
} , "type" , "" )
)
type KCPConfig struct {
Mtu * uint32 ` json:"mtu" `
Tti * uint32 ` json:"tti" `
UpCap * uint32 ` json:"uplinkCapacity" `
DownCap * uint32 ` json:"downlinkCapacity" `
Congestion * bool ` json:"congestion" `
ReadBufferSize * uint32 ` json:"readBufferSize" `
WriteBufferSize * uint32 ` json:"writeBufferSize" `
HeaderConfig json . RawMessage ` json:"header" `
Seed * string ` json:"seed" `
}
// Build implements Buildable.
func ( c * KCPConfig ) Build ( ) ( proto . Message , error ) {
config := new ( kcp . Config )
if c . Mtu != nil {
mtu := * c . Mtu
if mtu < 576 || mtu > 1460 {
return nil , newError ( "invalid mKCP MTU size: " , mtu ) . AtError ( )
}
config . Mtu = & kcp . MTU { Value : mtu }
}
if c . Tti != nil {
tti := * c . Tti
if tti < 10 || tti > 100 {
return nil , newError ( "invalid mKCP TTI: " , tti ) . AtError ( )
}
config . Tti = & kcp . TTI { Value : tti }
}
if c . UpCap != nil {
config . UplinkCapacity = & kcp . UplinkCapacity { Value : * c . UpCap }
}
if c . DownCap != nil {
config . DownlinkCapacity = & kcp . DownlinkCapacity { Value : * c . DownCap }
}
if c . Congestion != nil {
config . Congestion = * c . Congestion
}
if c . ReadBufferSize != nil {
size := * c . ReadBufferSize
if size > 0 {
config . ReadBuffer = & kcp . ReadBuffer { Size : size * 1024 * 1024 }
} else {
config . ReadBuffer = & kcp . ReadBuffer { Size : 512 * 1024 }
}
}
if c . WriteBufferSize != nil {
size := * c . WriteBufferSize
if size > 0 {
config . WriteBuffer = & kcp . WriteBuffer { Size : size * 1024 * 1024 }
} else {
config . WriteBuffer = & kcp . WriteBuffer { Size : 512 * 1024 }
}
}
if len ( c . HeaderConfig ) > 0 {
headerConfig , _ , err := kcpHeaderLoader . Load ( c . HeaderConfig )
if err != nil {
return nil , newError ( "invalid mKCP header config." ) . Base ( err ) . AtError ( )
}
ts , err := headerConfig . ( Buildable ) . Build ( )
if err != nil {
return nil , newError ( "invalid mKCP header config" ) . Base ( err ) . AtError ( )
}
config . HeaderConfig = serial . ToTypedMessage ( ts )
}
if c . Seed != nil {
config . Seed = & kcp . EncryptionSeed { Seed : * c . Seed }
}
return config , nil
}
type TCPConfig struct {
HeaderConfig json . RawMessage ` json:"header" `
AcceptProxyProtocol bool ` json:"acceptProxyProtocol" `
}
// Build implements Buildable.
func ( c * TCPConfig ) Build ( ) ( proto . Message , error ) {
config := new ( tcp . Config )
if len ( c . HeaderConfig ) > 0 {
headerConfig , _ , err := tcpHeaderLoader . Load ( c . HeaderConfig )
if err != nil {
return nil , newError ( "invalid TCP header config" ) . Base ( err ) . AtError ( )
}
ts , err := headerConfig . ( Buildable ) . Build ( )
if err != nil {
return nil , newError ( "invalid TCP header config" ) . Base ( err ) . AtError ( )
}
config . HeaderSettings = serial . ToTypedMessage ( ts )
}
if c . AcceptProxyProtocol {
config . AcceptProxyProtocol = c . AcceptProxyProtocol
}
return config , nil
}
type WebSocketConfig struct {
Path string ` json:"path" `
Headers map [ string ] string ` json:"headers" `
AcceptProxyProtocol bool ` json:"acceptProxyProtocol" `
}
// Build implements Buildable.
func ( c * WebSocketConfig ) Build ( ) ( proto . Message , error ) {
path := c . Path
header := make ( [ ] * websocket . Header , 0 , 32 )
for key , value := range c . Headers {
header = append ( header , & websocket . Header {
Key : key ,
Value : value ,
} )
}
2021-03-14 10:10:10 +03:00
var ed uint32
if u , err := url . Parse ( path ) ; err == nil {
if q := u . Query ( ) ; q . Get ( "ed" ) != "" {
Ed , _ := strconv . Atoi ( q . Get ( "ed" ) )
ed = uint32 ( Ed )
q . Del ( "ed" )
u . RawQuery = q . Encode ( )
path = u . String ( )
}
}
2020-11-25 14:01:53 +03:00
config := & websocket . Config {
Path : path ,
Header : header ,
2021-03-14 10:10:10 +03:00
Ed : ed ,
2020-11-25 14:01:53 +03:00
}
if c . AcceptProxyProtocol {
config . AcceptProxyProtocol = c . AcceptProxyProtocol
}
return config , nil
}
type HTTPConfig struct {
2021-10-12 18:58:12 +03:00
Host * StringList ` json:"host" `
Path string ` json:"path" `
ReadIdleTimeout int32 ` json:"read_idle_timeout" `
HealthCheckTimeout int32 ` json:"health_check_timeout" `
Method string ` json:"method" `
Headers map [ string ] * StringList ` json:"headers" `
2020-11-25 14:01:53 +03:00
}
// Build implements Buildable.
func ( c * HTTPConfig ) Build ( ) ( proto . Message , error ) {
2021-07-03 11:01:59 +03:00
if c . ReadIdleTimeout <= 0 {
c . ReadIdleTimeout = 0
}
if c . HealthCheckTimeout <= 0 {
c . HealthCheckTimeout = 0
}
2020-11-25 14:01:53 +03:00
config := & http . Config {
2021-07-03 11:01:59 +03:00
Path : c . Path ,
IdleTimeout : c . ReadIdleTimeout ,
HealthCheckTimeout : c . HealthCheckTimeout ,
2020-11-25 14:01:53 +03:00
}
if c . Host != nil {
config . Host = [ ] string ( * c . Host )
}
2021-10-12 18:58:12 +03:00
if c . Method != "" {
config . Method = c . Method
}
if len ( c . Headers ) > 0 {
config . Header = make ( [ ] * httpheader . Header , 0 , len ( c . Headers ) )
headerNames := sortMapKeys ( c . Headers )
for _ , key := range headerNames {
value := c . Headers [ key ]
if value == nil {
return nil , newError ( "empty HTTP header value: " + key ) . AtError ( )
}
config . Header = append ( config . Header , & httpheader . Header {
Name : key ,
Value : append ( [ ] string ( nil ) , ( * value ) ... ) ,
} )
}
}
2020-11-25 14:01:53 +03:00
return config , nil
}
type QUICConfig struct {
Header json . RawMessage ` json:"header" `
Security string ` json:"security" `
Key string ` json:"key" `
}
// Build implements Buildable.
func ( c * QUICConfig ) Build ( ) ( proto . Message , error ) {
config := & quic . Config {
Key : c . Key ,
}
if len ( c . Header ) > 0 {
headerConfig , _ , err := kcpHeaderLoader . Load ( c . Header )
if err != nil {
return nil , newError ( "invalid QUIC header config." ) . Base ( err ) . AtError ( )
}
ts , err := headerConfig . ( Buildable ) . Build ( )
if err != nil {
return nil , newError ( "invalid QUIC header config" ) . Base ( err ) . AtError ( )
}
config . Header = serial . ToTypedMessage ( ts )
}
var st protocol . SecurityType
switch strings . ToLower ( c . Security ) {
case "aes-128-gcm" :
st = protocol . SecurityType_AES128_GCM
case "chacha20-poly1305" :
st = protocol . SecurityType_CHACHA20_POLY1305
default :
st = protocol . SecurityType_NONE
}
config . Security = & protocol . SecurityConfig {
Type : st ,
}
return config , nil
}
type DomainSocketConfig struct {
Path string ` json:"path" `
Abstract bool ` json:"abstract" `
Padding bool ` json:"padding" `
}
// Build implements Buildable.
func ( c * DomainSocketConfig ) Build ( ) ( proto . Message , error ) {
return & domainsocket . Config {
Path : c . Path ,
Abstract : c . Abstract ,
Padding : c . Padding ,
} , nil
}
func readFileOrString ( f string , s [ ] string ) ( [ ] byte , error ) {
if len ( f ) > 0 {
return filesystem . ReadFile ( f )
}
if len ( s ) > 0 {
return [ ] byte ( strings . Join ( s , "\n" ) ) , nil
}
return nil , newError ( "both file and bytes are empty." )
}
type TLSCertConfig struct {
2021-02-20 05:15:57 +03:00
CertFile string ` json:"certificateFile" `
CertStr [ ] string ` json:"certificate" `
KeyFile string ` json:"keyFile" `
KeyStr [ ] string ` json:"key" `
Usage string ` json:"usage" `
OcspStapling uint64 ` json:"ocspStapling" `
OneTimeLoading bool ` json:"oneTimeLoading" `
2020-11-25 14:01:53 +03:00
}
// Build implements Buildable.
func ( c * TLSCertConfig ) Build ( ) ( * tls . Certificate , error ) {
certificate := new ( tls . Certificate )
cert , err := readFileOrString ( c . CertFile , c . CertStr )
if err != nil {
return nil , newError ( "failed to parse certificate" ) . Base ( err )
}
certificate . Certificate = cert
2021-02-12 18:33:19 +03:00
certificate . CertificatePath = c . CertFile
2020-11-25 14:01:53 +03:00
if len ( c . KeyFile ) > 0 || len ( c . KeyStr ) > 0 {
key , err := readFileOrString ( c . KeyFile , c . KeyStr )
if err != nil {
return nil , newError ( "failed to parse key" ) . Base ( err )
}
certificate . Key = key
2021-02-12 18:33:19 +03:00
certificate . KeyPath = c . KeyFile
2020-11-25 14:01:53 +03:00
}
switch strings . ToLower ( c . Usage ) {
case "encipherment" :
certificate . Usage = tls . Certificate_ENCIPHERMENT
case "verify" :
certificate . Usage = tls . Certificate_AUTHORITY_VERIFY
case "issue" :
certificate . Usage = tls . Certificate_AUTHORITY_ISSUE
default :
certificate . Usage = tls . Certificate_ENCIPHERMENT
}
2021-02-20 05:15:57 +03:00
if certificate . KeyPath == "" && certificate . CertificatePath == "" {
certificate . OneTimeLoading = true
} else {
certificate . OneTimeLoading = c . OneTimeLoading
}
2020-12-25 11:01:20 +03:00
certificate . OcspStapling = c . OcspStapling
2020-12-25 18:10:12 +03:00
2020-11-25 14:01:53 +03:00
return certificate , nil
}
type TLSConfig struct {
2021-10-22 07:04:06 +03:00
Insecure bool ` json:"allowInsecure" `
Certs [ ] * TLSCertConfig ` json:"certificates" `
ServerName string ` json:"serverName" `
ALPN * StringList ` json:"alpn" `
EnableSessionResumption bool ` json:"enableSessionResumption" `
DisableSystemRoot bool ` json:"disableSystemRoot" `
MinVersion string ` json:"minVersion" `
MaxVersion string ` json:"maxVersion" `
CipherSuites string ` json:"cipherSuites" `
PreferServerCipherSuites bool ` json:"preferServerCipherSuites" `
Fingerprint string ` json:"fingerprint" `
RejectUnknownSNI bool ` json:"rejectUnknownSni" `
PinnedPeerCertificateChainSha256 * [ ] string ` json:"pinnedPeerCertificateChainSha256" `
2020-11-25 14:01:53 +03:00
}
// Build implements Buildable.
func ( c * TLSConfig ) Build ( ) ( proto . Message , error ) {
config := new ( tls . Config )
config . Certificate = make ( [ ] * tls . Certificate , len ( c . Certs ) )
for idx , certConf := range c . Certs {
cert , err := certConf . Build ( )
if err != nil {
return nil , err
}
config . Certificate [ idx ] = cert
}
serverName := c . ServerName
config . AllowInsecure = c . Insecure
if len ( c . ServerName ) > 0 {
config . ServerName = serverName
}
if c . ALPN != nil && len ( * c . ALPN ) > 0 {
config . NextProtocol = [ ] string ( * c . ALPN )
}
2021-01-01 14:33:09 +03:00
config . EnableSessionResumption = c . EnableSessionResumption
2020-11-25 14:01:53 +03:00
config . DisableSystemRoot = c . DisableSystemRoot
2020-12-16 18:59:04 +03:00
config . MinVersion = c . MinVersion
config . MaxVersion = c . MaxVersion
config . CipherSuites = c . CipherSuites
config . PreferServerCipherSuites = c . PreferServerCipherSuites
2021-03-29 13:08:29 +03:00
config . Fingerprint = strings . ToLower ( c . Fingerprint )
2023-02-01 15:58:17 +03:00
if config . Fingerprint != "" && tls . GetFingerprint ( config . Fingerprint ) == nil {
return nil , newError ( ` unknown fingerprint: ` , config . Fingerprint )
}
2021-05-09 18:47:21 +03:00
config . RejectUnknownSni = c . RejectUnknownSNI
2021-10-22 07:04:06 +03:00
if c . PinnedPeerCertificateChainSha256 != nil {
config . PinnedPeerCertificateChainSha256 = [ ] [ ] byte { }
for _ , v := range * c . PinnedPeerCertificateChainSha256 {
hashValue , err := base64 . StdEncoding . DecodeString ( v )
if err != nil {
return nil , err
}
config . PinnedPeerCertificateChainSha256 = append ( config . PinnedPeerCertificateChainSha256 , hashValue )
}
}
2020-11-25 14:01:53 +03:00
return config , nil
}
type XTLSCertConfig struct {
2021-02-20 05:15:57 +03:00
CertFile string ` json:"certificateFile" `
CertStr [ ] string ` json:"certificate" `
KeyFile string ` json:"keyFile" `
KeyStr [ ] string ` json:"key" `
Usage string ` json:"usage" `
OcspStapling uint64 ` json:"ocspStapling" `
OneTimeLoading bool ` json:"oneTimeLoading" `
2020-11-25 14:01:53 +03:00
}
// Build implements Buildable.
func ( c * XTLSCertConfig ) Build ( ) ( * xtls . Certificate , error ) {
certificate := new ( xtls . Certificate )
cert , err := readFileOrString ( c . CertFile , c . CertStr )
if err != nil {
return nil , newError ( "failed to parse certificate" ) . Base ( err )
}
certificate . Certificate = cert
2021-02-12 18:33:19 +03:00
certificate . CertificatePath = c . CertFile
2020-11-25 14:01:53 +03:00
if len ( c . KeyFile ) > 0 || len ( c . KeyStr ) > 0 {
key , err := readFileOrString ( c . KeyFile , c . KeyStr )
if err != nil {
return nil , newError ( "failed to parse key" ) . Base ( err )
}
certificate . Key = key
2021-02-12 18:33:19 +03:00
certificate . KeyPath = c . KeyFile
2020-11-25 14:01:53 +03:00
}
switch strings . ToLower ( c . Usage ) {
case "encipherment" :
certificate . Usage = xtls . Certificate_ENCIPHERMENT
case "verify" :
certificate . Usage = xtls . Certificate_AUTHORITY_VERIFY
case "issue" :
certificate . Usage = xtls . Certificate_AUTHORITY_ISSUE
default :
certificate . Usage = xtls . Certificate_ENCIPHERMENT
}
2021-02-20 05:15:57 +03:00
if certificate . KeyPath == "" && certificate . CertificatePath == "" {
certificate . OneTimeLoading = true
} else {
certificate . OneTimeLoading = c . OneTimeLoading
}
2020-12-25 11:01:20 +03:00
certificate . OcspStapling = c . OcspStapling
2020-12-25 18:10:12 +03:00
2020-11-25 14:01:53 +03:00
return certificate , nil
}
type XTLSConfig struct {
2021-10-22 07:04:06 +03:00
Insecure bool ` json:"allowInsecure" `
Certs [ ] * XTLSCertConfig ` json:"certificates" `
ServerName string ` json:"serverName" `
ALPN * StringList ` json:"alpn" `
EnableSessionResumption bool ` json:"enableSessionResumption" `
DisableSystemRoot bool ` json:"disableSystemRoot" `
MinVersion string ` json:"minVersion" `
MaxVersion string ` json:"maxVersion" `
CipherSuites string ` json:"cipherSuites" `
PreferServerCipherSuites bool ` json:"preferServerCipherSuites" `
2023-02-03 18:29:46 +03:00
Fingerprint string ` json:"fingerprint" `
2021-10-22 07:04:06 +03:00
RejectUnknownSNI bool ` json:"rejectUnknownSni" `
PinnedPeerCertificateChainSha256 * [ ] string ` json:"pinnedPeerCertificateChainSha256" `
2020-11-25 14:01:53 +03:00
}
// Build implements Buildable.
func ( c * XTLSConfig ) Build ( ) ( proto . Message , error ) {
config := new ( xtls . Config )
config . Certificate = make ( [ ] * xtls . Certificate , len ( c . Certs ) )
for idx , certConf := range c . Certs {
cert , err := certConf . Build ( )
if err != nil {
return nil , err
}
config . Certificate [ idx ] = cert
}
serverName := c . ServerName
config . AllowInsecure = c . Insecure
if len ( c . ServerName ) > 0 {
config . ServerName = serverName
}
if c . ALPN != nil && len ( * c . ALPN ) > 0 {
config . NextProtocol = [ ] string ( * c . ALPN )
}
2021-01-01 14:33:09 +03:00
config . EnableSessionResumption = c . EnableSessionResumption
2020-12-16 18:59:04 +03:00
config . DisableSystemRoot = c . DisableSystemRoot
2020-12-16 08:20:24 +03:00
config . MinVersion = c . MinVersion
2020-12-16 18:59:04 +03:00
config . MaxVersion = c . MaxVersion
2020-12-16 15:53:55 +03:00
config . CipherSuites = c . CipherSuites
config . PreferServerCipherSuites = c . PreferServerCipherSuites
2023-02-03 18:29:46 +03:00
if c . Fingerprint != "" {
return nil , newError ( ` Old version of XTLS does not support fingerprint. Please use flow "xtls-rprx-vision" with "tls & tlsSettings" instead. ` )
}
2021-05-09 18:47:21 +03:00
config . RejectUnknownSni = c . RejectUnknownSNI
2021-10-22 07:04:06 +03:00
if c . PinnedPeerCertificateChainSha256 != nil {
config . PinnedPeerCertificateChainSha256 = [ ] [ ] byte { }
for _ , v := range * c . PinnedPeerCertificateChainSha256 {
hashValue , err := base64 . StdEncoding . DecodeString ( v )
if err != nil {
return nil , err
}
config . PinnedPeerCertificateChainSha256 = append ( config . PinnedPeerCertificateChainSha256 , hashValue )
}
}
2023-02-03 18:29:46 +03:00
newError ( ` You are using an old version of XTLS, which is deprecated now and will be removed soon. Please use flow "xtls-rprx-vision" with "tls & tlsSettings" instead. ` ) . AtWarning ( ) . WriteToLog ( )
2020-11-25 14:01:53 +03:00
return config , nil
}
2023-02-15 19:07:12 +03:00
type REALITYConfig struct {
Show bool ` json:"show" `
Dest json . RawMessage ` json:"dest" `
Type string ` json:"type" `
Xver uint64 ` json:"xver" `
ServerNames [ ] string ` json:"serverNames" `
PrivateKey string ` json:"privateKey" `
MinClientVer string ` json:"minClientVer" `
MaxClientVer string ` json:"maxClientVer" `
MaxTimeDiff uint64 ` json:"maxTimeDiff" `
ShortIds [ ] string ` json:"shortIds" `
Fingerprint string ` json:"fingerprint" `
ServerName string ` json:"serverName" `
PublicKey string ` json:"publicKey" `
ShortId string ` json:"shortId" `
SpiderX string ` json:"spiderX" `
}
func ( c * REALITYConfig ) Build ( ) ( proto . Message , error ) {
config := new ( reality . Config )
config . Show = c . Show
var err error
if c . Dest != nil {
var i uint16
var s string
if err = json . Unmarshal ( c . Dest , & i ) ; err == nil {
s = strconv . Itoa ( int ( i ) )
} else {
_ = json . Unmarshal ( c . Dest , & s )
}
if c . Type == "" && s != "" {
switch s [ 0 ] {
case '@' , '/' :
c . Type = "unix"
if s [ 0 ] == '@' && len ( s ) > 1 && s [ 1 ] == '@' && ( runtime . GOOS == "linux" || runtime . GOOS == "android" ) {
fullAddr := make ( [ ] byte , len ( syscall . RawSockaddrUnix { } . Path ) ) // may need padding to work with haproxy
copy ( fullAddr , s [ 1 : ] )
s = string ( fullAddr )
}
default :
if _ , err = strconv . Atoi ( s ) ; err == nil {
s = "127.0.0.1:" + s
}
if _ , _ , err = net . SplitHostPort ( s ) ; err == nil {
c . Type = "tcp"
}
}
}
if c . Type == "" {
return nil , newError ( ` please fill in a valid value for "dest" ` )
}
if c . Xver > 2 {
return nil , newError ( ` invalid PROXY protocol version, "xver" only accepts 0, 1, 2 ` )
}
if len ( c . ServerNames ) == 0 {
return nil , newError ( ` empty "serverNames" ` )
}
if c . PrivateKey == "" {
return nil , newError ( ` empty "privateKey" ` )
}
if config . PrivateKey , err = base64 . RawURLEncoding . DecodeString ( c . PrivateKey ) ; err != nil || len ( config . PrivateKey ) != 32 {
return nil , newError ( ` invalid "privateKey": ` , c . PrivateKey )
}
if c . MinClientVer != "" {
config . MinClientVer = make ( [ ] byte , 3 )
var u uint64
for i , s := range strings . Split ( c . MinClientVer , "." ) {
if i == 3 {
return nil , newError ( ` invalid "minClientVer": ` , c . MinClientVer )
}
if u , err = strconv . ParseUint ( s , 10 , 8 ) ; err != nil {
return nil , newError ( ` "minClientVer[ ` , i , ` ]" should be lesser than 256 ` )
} else {
config . MinClientVer [ i ] = byte ( u )
}
}
}
if c . MaxClientVer != "" {
config . MaxClientVer = make ( [ ] byte , 3 )
var u uint64
for i , s := range strings . Split ( c . MaxClientVer , "." ) {
if i == 3 {
return nil , newError ( ` invalid "maxClientVer": ` , c . MaxClientVer )
}
if u , err = strconv . ParseUint ( s , 10 , 8 ) ; err != nil {
return nil , newError ( ` "maxClientVer[ ` , i , ` ]" should be lesser than 256 ` )
} else {
config . MaxClientVer [ i ] = byte ( u )
}
}
}
if len ( c . ShortIds ) == 0 {
return nil , newError ( ` empty "shortIds" ` )
}
config . ShortIds = make ( [ ] [ ] byte , len ( c . ShortIds ) )
for i , s := range c . ShortIds {
config . ShortIds [ i ] = make ( [ ] byte , 8 )
if _ , err = hex . Decode ( config . ShortIds [ i ] , [ ] byte ( s ) ) ; err != nil {
return nil , newError ( ` invalid "shortIds[ ` , i , ` ]": ` , s )
}
}
config . Dest = s
config . Type = c . Type
config . Xver = c . Xver
config . ServerNames = c . ServerNames
config . MaxTimeDiff = c . MaxTimeDiff
} else {
if c . Fingerprint == "" {
return nil , newError ( ` empty "fingerprint" ` )
}
if config . Fingerprint = strings . ToLower ( c . Fingerprint ) ; tls . GetFingerprint ( config . Fingerprint ) == nil {
return nil , newError ( ` unknown "fingerprint": ` , config . Fingerprint )
}
if config . Fingerprint == "hellogolang" {
return nil , newError ( ` invalid "fingerprint": ` , config . Fingerprint )
}
if c . PublicKey == "" {
return nil , newError ( ` empty "publicKey" ` )
}
if config . PublicKey , err = base64 . RawURLEncoding . DecodeString ( c . PublicKey ) ; err != nil || len ( config . PublicKey ) != 32 {
return nil , newError ( ` invalid "publicKey": ` , c . PublicKey )
}
config . ShortId = make ( [ ] byte , 8 )
if _ , err = hex . Decode ( config . ShortId , [ ] byte ( c . ShortId ) ) ; err != nil {
return nil , newError ( ` invalid "shortId": ` , c . ShortId )
}
if c . SpiderX == "" {
return nil , newError ( ` empty "spiderX" ` )
}
if c . SpiderX [ 0 ] != '/' {
return nil , newError ( ` invalid "spiderX": ` , c . SpiderX )
}
config . SpiderY = make ( [ ] int64 , 10 )
u , _ := url . Parse ( c . SpiderX )
q := u . Query ( )
parse := func ( param string , index int ) {
if q . Get ( param ) != "" {
s := strings . Split ( q . Get ( param ) , "-" )
if len ( s ) == 1 {
config . SpiderY [ index ] , _ = strconv . ParseInt ( s [ 0 ] , 10 , 64 )
config . SpiderY [ index + 1 ] , _ = strconv . ParseInt ( s [ 0 ] , 10 , 64 )
} else {
config . SpiderY [ index ] , _ = strconv . ParseInt ( s [ 0 ] , 10 , 64 )
config . SpiderY [ index + 1 ] , _ = strconv . ParseInt ( s [ 1 ] , 10 , 64 )
}
}
q . Del ( param )
}
parse ( "p" , 0 ) // padding
parse ( "c" , 2 ) // concurrency
parse ( "t" , 4 ) // times
parse ( "i" , 6 ) // interval
parse ( "r" , 8 ) // return
u . RawQuery = q . Encode ( )
config . SpiderX = u . String ( )
config . ServerName = c . ServerName
}
return config , nil
}
2020-11-25 14:01:53 +03:00
type TransportProtocol string
// Build implements Buildable.
func ( p TransportProtocol ) Build ( ) ( string , error ) {
switch strings . ToLower ( string ( p ) ) {
case "tcp" :
return "tcp" , nil
case "kcp" , "mkcp" :
return "mkcp" , nil
case "ws" , "websocket" :
return "websocket" , nil
case "h2" , "http" :
return "http" , nil
case "ds" , "domainsocket" :
return "domainsocket" , nil
case "quic" :
return "quic" , nil
2021-03-14 18:02:07 +03:00
case "grpc" , "gun" :
return "grpc" , nil
2020-11-25 14:01:53 +03:00
default :
return "" , newError ( "Config: unknown transport protocol: " , p )
}
}
type SocketConfig struct {
2021-10-22 07:01:31 +03:00
Mark int32 ` json:"mark" `
TFO interface { } ` json:"tcpFastOpen" `
TProxy string ` json:"tproxy" `
AcceptProxyProtocol bool ` json:"acceptProxyProtocol" `
DomainStrategy string ` json:"domainStrategy" `
DialerProxy string ` json:"dialerProxy" `
TCPKeepAliveInterval int32 ` json:"tcpKeepAliveInterval" `
2022-07-31 16:55:40 +03:00
TCPKeepAliveIdle int32 ` json:"tcpKeepAliveIdle" `
2022-12-26 03:37:35 +03:00
TCPCongestion string ` json:"tcpCongestion" `
2023-01-09 17:26:23 +03:00
Interface string ` json:"interface" `
2020-11-25 14:01:53 +03:00
}
// Build implements Buildable.
func ( c * SocketConfig ) Build ( ) ( * internet . SocketConfig , error ) {
2021-03-30 19:42:02 +03:00
tfo := int32 ( 0 ) // don't invoke setsockopt() for TFO
2020-11-25 14:01:53 +03:00
if c . TFO != nil {
2021-03-06 17:45:12 +03:00
switch v := c . TFO . ( type ) {
case bool :
if v {
tfo = 256
} else {
2021-03-30 19:42:02 +03:00
tfo = - 1 // TFO need to be disabled
2021-03-06 17:45:12 +03:00
}
case float64 :
tfo = int32 ( math . Min ( v , math . MaxInt32 ) )
default :
2021-03-30 19:42:02 +03:00
return nil , newError ( "tcpFastOpen: only boolean and integer value is acceptable" )
2020-11-25 14:01:53 +03:00
}
}
var tproxy internet . SocketConfig_TProxyMode
switch strings . ToLower ( c . TProxy ) {
case "tproxy" :
tproxy = internet . SocketConfig_TProxy
case "redirect" :
tproxy = internet . SocketConfig_Redirect
default :
tproxy = internet . SocketConfig_Off
}
2021-10-19 19:57:14 +03:00
dStrategy := internet . DomainStrategy_AS_IS
2021-03-06 19:29:17 +03:00
switch strings . ToLower ( c . DomainStrategy ) {
case "useip" , "use_ip" :
dStrategy = internet . DomainStrategy_USE_IP
case "useip4" , "useipv4" , "use_ipv4" , "use_ip_v4" , "use_ip4" :
dStrategy = internet . DomainStrategy_USE_IP4
case "useip6" , "useipv6" , "use_ipv6" , "use_ip_v6" , "use_ip6" :
dStrategy = internet . DomainStrategy_USE_IP6
}
2020-11-25 14:01:53 +03:00
return & internet . SocketConfig {
2021-10-12 18:39:08 +03:00
Mark : c . Mark ,
Tfo : tfo ,
Tproxy : tproxy ,
DomainStrategy : dStrategy ,
AcceptProxyProtocol : c . AcceptProxyProtocol ,
DialerProxy : c . DialerProxy ,
TcpKeepAliveInterval : c . TCPKeepAliveInterval ,
2022-07-31 16:55:40 +03:00
TcpKeepAliveIdle : c . TCPKeepAliveIdle ,
2022-12-26 03:37:35 +03:00
TcpCongestion : c . TCPCongestion ,
2023-01-09 17:26:23 +03:00
Interface : c . Interface ,
2020-11-25 14:01:53 +03:00
} , nil
}
type StreamConfig struct {
2023-02-15 19:07:12 +03:00
Network * TransportProtocol ` json:"network" `
Security string ` json:"security" `
TLSSettings * TLSConfig ` json:"tlsSettings" `
XTLSSettings * XTLSConfig ` json:"xtlsSettings" `
REALITYSettings * REALITYConfig ` json:"realitySettings" `
TCPSettings * TCPConfig ` json:"tcpSettings" `
KCPSettings * KCPConfig ` json:"kcpSettings" `
WSSettings * WebSocketConfig ` json:"wsSettings" `
HTTPSettings * HTTPConfig ` json:"httpSettings" `
DSSettings * DomainSocketConfig ` json:"dsSettings" `
QUICSettings * QUICConfig ` json:"quicSettings" `
SocketSettings * SocketConfig ` json:"sockopt" `
GRPCConfig * GRPCConfig ` json:"grpcSettings" `
GUNConfig * GRPCConfig ` json:"gunSettings" `
2020-11-25 14:01:53 +03:00
}
// Build implements Buildable.
func ( c * StreamConfig ) Build ( ) ( * internet . StreamConfig , error ) {
config := & internet . StreamConfig {
ProtocolName : "tcp" ,
}
if c . Network != nil {
protocol , err := c . Network . Build ( )
if err != nil {
return nil , err
}
config . ProtocolName = protocol
}
if strings . EqualFold ( c . Security , "tls" ) {
tlsSettings := c . TLSSettings
if tlsSettings == nil {
if c . XTLSSettings != nil {
return nil , newError ( ` TLS: Please use "tlsSettings" instead of "xtlsSettings". ` )
}
tlsSettings = & TLSConfig { }
}
ts , err := tlsSettings . Build ( )
if err != nil {
return nil , newError ( "Failed to build TLS config." ) . Base ( err )
}
tm := serial . ToTypedMessage ( ts )
config . SecuritySettings = append ( config . SecuritySettings , tm )
config . SecurityType = tm . Type
}
if strings . EqualFold ( c . Security , "xtls" ) {
if config . ProtocolName != "tcp" && config . ProtocolName != "mkcp" && config . ProtocolName != "domainsocket" {
return nil , newError ( "XTLS only supports TCP, mKCP and DomainSocket for now." )
}
xtlsSettings := c . XTLSSettings
if xtlsSettings == nil {
if c . TLSSettings != nil {
return nil , newError ( ` XTLS: Please use "xtlsSettings" instead of "tlsSettings". ` )
}
xtlsSettings = & XTLSConfig { }
}
ts , err := xtlsSettings . Build ( )
if err != nil {
return nil , newError ( "Failed to build XTLS config." ) . Base ( err )
2023-02-15 19:07:12 +03:00
}
tm := serial . ToTypedMessage ( ts )
config . SecuritySettings = append ( config . SecuritySettings , tm )
config . SecurityType = tm . Type
}
if strings . EqualFold ( c . Security , "reality" ) {
if config . ProtocolName != "tcp" && config . ProtocolName != "http" && config . ProtocolName != "domainsocket" {
return nil , newError ( "REALITY only supports TCP, H2 and DomainSocket for now." )
}
if c . REALITYSettings == nil {
return nil , newError ( ` REALITY: Empty "realitySettings". ` )
}
ts , err := c . REALITYSettings . Build ( )
if err != nil {
return nil , newError ( "Failed to build REALITY config." ) . Base ( err )
2020-11-25 14:01:53 +03:00
}
tm := serial . ToTypedMessage ( ts )
config . SecuritySettings = append ( config . SecuritySettings , tm )
config . SecurityType = tm . Type
}
if c . TCPSettings != nil {
ts , err := c . TCPSettings . Build ( )
if err != nil {
return nil , newError ( "Failed to build TCP config." ) . Base ( err )
}
config . TransportSettings = append ( config . TransportSettings , & internet . TransportConfig {
ProtocolName : "tcp" ,
Settings : serial . ToTypedMessage ( ts ) ,
} )
}
if c . KCPSettings != nil {
ts , err := c . KCPSettings . Build ( )
if err != nil {
return nil , newError ( "Failed to build mKCP config." ) . Base ( err )
}
config . TransportSettings = append ( config . TransportSettings , & internet . TransportConfig {
ProtocolName : "mkcp" ,
Settings : serial . ToTypedMessage ( ts ) ,
} )
}
if c . WSSettings != nil {
ts , err := c . WSSettings . Build ( )
if err != nil {
return nil , newError ( "Failed to build WebSocket config." ) . Base ( err )
}
config . TransportSettings = append ( config . TransportSettings , & internet . TransportConfig {
ProtocolName : "websocket" ,
Settings : serial . ToTypedMessage ( ts ) ,
} )
}
if c . HTTPSettings != nil {
ts , err := c . HTTPSettings . Build ( )
if err != nil {
return nil , newError ( "Failed to build HTTP config." ) . Base ( err )
}
config . TransportSettings = append ( config . TransportSettings , & internet . TransportConfig {
ProtocolName : "http" ,
Settings : serial . ToTypedMessage ( ts ) ,
} )
}
if c . DSSettings != nil {
ds , err := c . DSSettings . Build ( )
if err != nil {
return nil , newError ( "Failed to build DomainSocket config." ) . Base ( err )
}
config . TransportSettings = append ( config . TransportSettings , & internet . TransportConfig {
ProtocolName : "domainsocket" ,
Settings : serial . ToTypedMessage ( ds ) ,
} )
}
if c . QUICSettings != nil {
qs , err := c . QUICSettings . Build ( )
if err != nil {
return nil , newError ( "Failed to build QUIC config" ) . Base ( err )
}
config . TransportSettings = append ( config . TransportSettings , & internet . TransportConfig {
ProtocolName : "quic" ,
Settings : serial . ToTypedMessage ( qs ) ,
} )
}
2021-03-14 18:02:07 +03:00
if c . GRPCConfig == nil {
c . GRPCConfig = c . GUNConfig
}
if c . GRPCConfig != nil {
gs , err := c . GRPCConfig . Build ( )
if err != nil {
return nil , newError ( "Failed to build gRPC config." ) . Base ( err )
}
config . TransportSettings = append ( config . TransportSettings , & internet . TransportConfig {
ProtocolName : "grpc" ,
Settings : serial . ToTypedMessage ( gs ) ,
} )
}
2020-11-25 14:01:53 +03:00
if c . SocketSettings != nil {
ss , err := c . SocketSettings . Build ( )
if err != nil {
return nil , newError ( "Failed to build sockopt" ) . Base ( err )
}
config . SocketSettings = ss
}
return config , nil
}
type ProxyConfig struct {
Tag string ` json:"tag" `
2021-03-06 19:29:17 +03:00
// TransportLayerProxy: For compatibility.
TransportLayerProxy bool ` json:"transportLayer" `
2020-11-25 14:01:53 +03:00
}
// Build implements Buildable.
func ( v * ProxyConfig ) Build ( ) ( * internet . ProxyConfig , error ) {
if v . Tag == "" {
return nil , newError ( "Proxy tag is not set." )
}
return & internet . ProxyConfig {
2021-03-06 19:29:17 +03:00
Tag : v . Tag ,
TransportLayerProxy : v . TransportLayerProxy ,
2020-11-25 14:01:53 +03:00
} , nil
}