2020-11-25 19:01:53 +08:00
package conf
import (
2025-08-28 04:55:36 +00:00
"encoding/base64"
2020-11-25 19:01:53 +08:00
"encoding/json"
2023-12-27 12:19:52 +08:00
"path/filepath"
2020-11-25 19:01:53 +08:00
"runtime"
"strconv"
2023-12-27 12:19:52 +08:00
"strings"
2020-11-25 19:01:53 +08:00
"syscall"
2024-06-29 14:32:57 -04:00
"github.com/xtls/xray-core/common/errors"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/serial"
2021-01-12 11:31:02 +00:00
"github.com/xtls/xray-core/common/uuid"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/proxy/vless"
"github.com/xtls/xray-core/proxy/vless/inbound"
"github.com/xtls/xray-core/proxy/vless/outbound"
2023-08-10 04:43:34 +00:00
"google.golang.org/protobuf/proto"
2020-11-25 19:01:53 +08:00
)
type VLessInboundFallback struct {
2021-01-13 23:13:51 +08:00
Name string ` json:"name" `
2020-11-25 19:01:53 +08:00
Alpn string ` json:"alpn" `
Path string ` json:"path" `
Type string ` json:"type" `
Dest json . RawMessage ` json:"dest" `
Xver uint64 ` json:"xver" `
}
type VLessInboundConfig struct {
Clients [ ] json . RawMessage ` json:"clients" `
Decryption string ` json:"decryption" `
Fallbacks [ ] * VLessInboundFallback ` json:"fallbacks" `
2025-08-16 03:07:33 +04:00
Flow string ` json:"flow" `
2025-12-01 13:10:54 +00:00
Testseed [ ] uint32 ` json:"testseed" `
2020-11-25 19:01:53 +08:00
}
// Build implements Buildable
func ( c * VLessInboundConfig ) Build ( ) ( proto . Message , error ) {
config := new ( inbound . Config )
config . Clients = make ( [ ] * protocol . User , len ( c . Clients ) )
2025-08-16 03:07:33 +04:00
switch c . Flow {
case vless . None :
c . Flow = ""
case "" , vless . XRV :
default :
return nil , errors . New ( ` VLESS "settings.flow" doesn't support " ` + c . Flow + ` " in this version ` )
}
2020-11-25 19:01:53 +08:00
for idx , rawUser := range c . Clients {
user := new ( protocol . User )
if err := json . Unmarshal ( rawUser , user ) ; err != nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( ` VLESS clients: invalid user ` ) . Base ( err )
2020-11-25 19:01:53 +08:00
}
account := new ( vless . Account )
if err := json . Unmarshal ( rawUser , account ) ; err != nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( ` VLESS clients: invalid user ` ) . Base ( err )
2020-11-25 19:01:53 +08:00
}
2021-01-12 11:31:02 +00:00
u , err := uuid . ParseString ( account . Id )
if err != nil {
return nil , err
}
account . Id = u . String ( )
2023-03-04 15:39:27 +00:00
switch account . Flow {
2025-08-16 03:07:33 +04:00
case "" :
account . Flow = c . Flow
case vless . None :
account . Flow = ""
case vless . XRV :
2020-11-25 19:01:53 +08:00
default :
2024-06-29 14:32:57 -04:00
return nil , errors . New ( ` VLESS clients: "flow" doesn't support " ` + account . Flow + ` " in this version ` )
2020-11-25 19:01:53 +08:00
}
2025-12-01 13:10:54 +00:00
if len ( account . Testseed ) < 4 {
account . Testseed = c . Testseed
}
2020-11-25 19:01:53 +08:00
if account . Encryption != "" {
2025-08-31 04:09:28 +00:00
return nil , errors . New ( ` VLESS clients: "encryption" should not be in inbound settings ` )
2020-11-25 19:01:53 +08:00
}
2025-09-09 14:19:12 +00:00
if account . Reverse != nil && account . Reverse . Tag == "" {
return nil , errors . New ( ` VLESS clients: "tag" can't be empty for "reverse" ` )
}
2020-11-25 19:01:53 +08:00
user . Account = serial . ToTypedMessage ( account )
config . Clients [ idx ] = user
}
config . Decryption = c . Decryption
2025-08-28 04:55:36 +00:00
if ! func ( ) bool {
s := strings . Split ( config . Decryption , "." )
if len ( s ) < 4 || s [ 0 ] != "mlkem768x25519plus" {
return false
}
switch s [ 1 ] {
case "native" :
case "xorpub" :
config . XorMode = 1
case "random" :
config . XorMode = 2
default :
return false
}
2025-09-02 23:37:14 +00:00
t := strings . SplitN ( strings . TrimSuffix ( s [ 2 ] , "s" ) , "-" , 2 )
i , err := strconv . Atoi ( t [ 0 ] )
if err != nil {
return false
}
2025-09-04 14:03:55 +00:00
config . SecondsFrom = int64 ( i )
if len ( t ) == 2 {
2025-09-02 23:37:14 +00:00
i , err := strconv . Atoi ( t [ 1 ] )
2025-08-28 04:55:36 +00:00
if err != nil {
return false
}
2025-09-04 14:03:55 +00:00
config . SecondsTo = int64 ( i )
2025-08-28 04:55:36 +00:00
}
2025-08-31 04:09:28 +00:00
padding := 0
for _ , r := range s [ 3 : ] {
if len ( r ) < 20 {
padding += len ( r ) + 1
continue
}
if b , _ := base64 . RawURLEncoding . DecodeString ( r ) ; len ( b ) != 32 && len ( b ) != 64 {
2025-08-28 04:55:36 +00:00
return false
}
}
config . Decryption = config . Decryption [ 27 + len ( s [ 2 ] ) : ]
2025-08-31 04:09:28 +00:00
if padding > 0 {
config . Padding = config . Decryption [ : padding - 1 ]
config . Decryption = config . Decryption [ padding : ]
}
2025-08-28 04:55:36 +00:00
return true
} ( ) && config . Decryption != "none" {
if config . Decryption == "" {
return nil , errors . New ( ` VLESS settings: please add/set "decryption":"none" to every settings ` )
}
return nil , errors . New ( ` VLESS settings: unsupported "decryption": ` + config . Decryption )
}
2020-11-25 19:01:53 +08:00
2025-08-31 04:09:28 +00:00
if config . Decryption != "none" && c . Fallbacks != nil {
return nil , errors . New ( ` VLESS settings: "fallbacks" can not be used together with "decryption" ` )
}
2020-11-25 19:01:53 +08:00
for _ , fb := range c . Fallbacks {
var i uint16
var s string
if err := json . Unmarshal ( fb . Dest , & i ) ; err == nil {
s = strconv . Itoa ( int ( i ) )
} else {
_ = json . Unmarshal ( fb . Dest , & s )
}
config . Fallbacks = append ( config . Fallbacks , & inbound . Fallback {
2021-01-13 23:13:51 +08:00
Name : fb . Name ,
2020-11-25 19:01:53 +08:00
Alpn : fb . Alpn ,
Path : fb . Path ,
Type : fb . Type ,
Dest : s ,
Xver : fb . Xver ,
} )
}
for _ , fb := range config . Fallbacks {
/ *
if fb . Alpn == "h2" && fb . Path != "" {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( ` VLESS fallbacks: "alpn":"h2" doesn't support "path" ` )
2020-11-25 19:01:53 +08:00
}
* /
if fb . Path != "" && fb . Path [ 0 ] != '/' {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( ` VLESS fallbacks: "path" must be empty or start with "/" ` )
2020-11-25 19:01:53 +08:00
}
if fb . Type == "" && fb . Dest != "" {
if fb . Dest == "serve-ws-none" {
fb . Type = "serve"
2023-12-27 12:19:52 +08:00
} else if filepath . IsAbs ( fb . Dest ) || fb . Dest [ 0 ] == '@' {
fb . Type = "unix"
if strings . HasPrefix ( fb . Dest , "@@" ) && ( runtime . GOOS == "linux" || runtime . GOOS == "android" ) {
fullAddr := make ( [ ] byte , len ( syscall . RawSockaddrUnix { } . Path ) ) // may need padding to work with haproxy
copy ( fullAddr , fb . Dest [ 1 : ] )
fb . Dest = string ( fullAddr )
}
2020-11-25 19:01:53 +08:00
} else {
2023-12-27 12:19:52 +08:00
if _ , err := strconv . Atoi ( fb . Dest ) ; err == nil {
2025-07-19 08:47:43 +08:00
fb . Dest = "localhost:" + fb . Dest
2023-12-27 12:19:52 +08:00
}
if _ , _ , err := net . SplitHostPort ( fb . Dest ) ; err == nil {
fb . Type = "tcp"
2020-11-25 19:01:53 +08:00
}
}
}
if fb . Type == "" {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( ` VLESS fallbacks: please fill in a valid value for every "dest" ` )
2020-11-25 19:01:53 +08:00
}
if fb . Xver > 2 {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( ` VLESS fallbacks: invalid PROXY protocol version, "xver" only accepts 0, 1, 2 ` )
2020-11-25 19:01:53 +08:00
}
}
return config , nil
}
type VLessOutboundVnext struct {
Address * Address ` json:"address" `
Port uint16 ` json:"port" `
Users [ ] json . RawMessage ` json:"users" `
}
type VLessOutboundConfig struct {
2025-09-09 14:19:12 +00:00
Address * Address ` json:"address" `
Port uint16 ` json:"port" `
Level uint32 ` json:"level" `
Email string ` json:"email" `
Id string ` json:"id" `
Flow string ` json:"flow" `
Seed string ` json:"seed" `
Encryption string ` json:"encryption" `
Reverse * vless . Reverse ` json:"reverse" `
2025-12-01 13:10:54 +00:00
Testpre uint32 ` json:"testpre" `
Testseed [ ] uint32 ` json:"testseed" `
2025-09-09 14:19:12 +00:00
Vnext [ ] * VLessOutboundVnext ` json:"vnext" `
2020-11-25 19:01:53 +08:00
}
// Build implements Buildable
func ( c * VLessOutboundConfig ) Build ( ) ( proto . Message , error ) {
config := new ( outbound . Config )
2025-09-09 14:19:12 +00:00
if c . Address != nil {
c . Vnext = [ ] * VLessOutboundVnext {
{
Address : c . Address ,
Port : c . Port ,
Users : [ ] json . RawMessage { { } } ,
} ,
}
}
2025-08-28 04:55:36 +00:00
if len ( c . Vnext ) != 1 {
2025-09-15 21:31:27 +08:00
return nil , errors . New ( ` VLESS settings: "vnext" should have one and only one member. Multiple endpoints in "vnext" should use multiple VLESS outbounds and routing balancer instead ` )
2020-11-25 19:01:53 +08:00
}
2025-09-15 21:31:27 +08:00
for _ , rec := range c . Vnext {
2020-11-25 19:01:53 +08:00
if rec . Address == nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( ` VLESS vnext: "address" is not set ` )
2020-11-25 19:01:53 +08:00
}
2025-08-28 04:55:36 +00:00
if len ( rec . Users ) != 1 {
2025-09-15 21:31:27 +08:00
return nil , errors . New ( ` VLESS vnext: "users" should have one and only one member. Multiple members in "users" should use multiple VLESS outbounds and routing balancer instead ` )
2020-11-25 19:01:53 +08:00
}
spec := & protocol . ServerEndpoint {
Address : rec . Address . Build ( ) ,
Port : uint32 ( rec . Port ) ,
}
2025-09-15 21:31:27 +08:00
for _ , rawUser := range rec . Users {
2020-11-25 19:01:53 +08:00
user := new ( protocol . User )
2025-09-09 14:19:12 +00:00
if c . Address != nil {
user . Level = c . Level
user . Email = c . Email
} else {
if err := json . Unmarshal ( rawUser , user ) ; err != nil {
return nil , errors . New ( ` VLESS users: invalid user ` ) . Base ( err )
}
2020-11-25 19:01:53 +08:00
}
account := new ( vless . Account )
2025-09-09 14:19:12 +00:00
if c . Address != nil {
account . Id = c . Id
account . Flow = c . Flow
//account.Seed = c.Seed
account . Encryption = c . Encryption
account . Reverse = c . Reverse
2025-12-01 13:10:54 +00:00
account . Testpre = c . Testpre
account . Testseed = c . Testseed
2025-09-09 14:19:12 +00:00
} else {
if err := json . Unmarshal ( rawUser , account ) ; err != nil {
return nil , errors . New ( ` VLESS users: invalid user ` ) . Base ( err )
}
2020-11-25 19:01:53 +08:00
}
2021-01-12 11:31:02 +00:00
u , err := uuid . ParseString ( account . Id )
if err != nil {
return nil , err
}
account . Id = u . String ( )
2020-11-25 19:01:53 +08:00
switch account . Flow {
2023-03-04 05:39:26 -05:00
case "" , vless . XRV , vless . XRV + "-udp443" :
2020-11-25 19:01:53 +08:00
default :
2024-06-29 14:32:57 -04:00
return nil , errors . New ( ` VLESS users: "flow" doesn't support " ` + account . Flow + ` " in this version ` )
2020-11-25 19:01:53 +08:00
}
2025-08-28 04:55:36 +00:00
if ! func ( ) bool {
s := strings . Split ( account . Encryption , "." )
if len ( s ) < 4 || s [ 0 ] != "mlkem768x25519plus" {
return false
}
switch s [ 1 ] {
case "native" :
case "xorpub" :
account . XorMode = 1
case "random" :
account . XorMode = 2
default :
return false
}
switch s [ 2 ] {
case "1rtt" :
case "0rtt" :
account . Seconds = 1
default :
return false
}
2025-08-31 04:09:28 +00:00
padding := 0
for _ , r := range s [ 3 : ] {
if len ( r ) < 20 {
padding += len ( r ) + 1
continue
}
if b , _ := base64 . RawURLEncoding . DecodeString ( r ) ; len ( b ) != 32 && len ( b ) != 1184 {
2025-08-28 04:55:36 +00:00
return false
}
}
account . Encryption = account . Encryption [ 27 + len ( s [ 2 ] ) : ]
2025-08-31 04:09:28 +00:00
if padding > 0 {
account . Padding = account . Encryption [ : padding - 1 ]
account . Encryption = account . Encryption [ padding : ]
}
2025-08-28 04:55:36 +00:00
return true
} ( ) && account . Encryption != "none" {
if account . Encryption == "" {
return nil , errors . New ( ` VLESS users: please add/set "encryption":"none" for every user ` )
}
return nil , errors . New ( ` VLESS users: unsupported "encryption": ` + account . Encryption )
2020-11-25 19:01:53 +08:00
}
2025-09-09 14:19:12 +00:00
if account . Reverse != nil && account . Reverse . Tag == "" {
return nil , errors . New ( ` VLESS clients: "tag" can't be empty for "reverse" ` )
}
2020-11-25 19:01:53 +08:00
user . Account = serial . ToTypedMessage ( account )
2025-09-15 21:31:27 +08:00
spec . User = user
break
2020-11-25 19:01:53 +08:00
}
2025-09-15 21:31:27 +08:00
config . Vnext = spec
break
2020-11-25 19:01:53 +08:00
}
return config , nil
}