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"
2026-05-02 16:32:59 +03:00
"github.com/xtls/xray-core/common/task"
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 {
2026-05-07 19:10:48 +08:00
Users [ ] json . RawMessage ` json:"users" `
2020-11-25 19:01:53 +08:00
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 )
2026-05-07 19:10:48 +08:00
if c . Clients != nil {
c . Users = c . Clients
}
config . Users = make ( [ ] * protocol . User , len ( c . Users ) )
2025-08-16 03:07:33 +04:00
switch c . Flow {
2026-03-03 14:40:19 +03:30
case vless . XRV , "" :
2025-08-16 03:07:33 +04:00
default :
return nil , errors . New ( ` VLESS "settings.flow" doesn't support " ` + c . Flow + ` " in this version ` )
}
2026-05-02 16:32:59 +03:00
processClient := func ( idx int ) error {
2026-05-07 19:10:48 +08:00
rawUser := c . Users [ idx ]
2020-11-25 19:01:53 +08:00
user := new ( protocol . User )
if err := json . Unmarshal ( rawUser , user ) ; err != nil {
2026-05-07 19:10:48 +08:00
return errors . New ( ` VLESS users: invalid user ` ) . Base ( err )
2020-11-25 19:01:53 +08:00
}
account := new ( vless . Account )
if err := json . Unmarshal ( rawUser , account ) ; err != nil {
2026-05-07 19:10:48 +08:00
return 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 {
2026-05-02 16:32:59 +03:00
return err
2021-01-12 11:31:02 +00:00
}
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 . XRV :
2020-11-25 19:01:53 +08:00
default :
2026-05-07 19:10:48 +08:00
return errors . New ( ` VLESS users: "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 != "" {
2026-05-07 19:10:48 +08:00
return errors . New ( ` VLESS users: "encryption" should not be in inbound settings ` )
2020-11-25 19:01:53 +08:00
}
2026-03-23 09:49:32 +00:00
if account . Reverse != nil {
if account . Reverse . Tag == "" {
2026-05-07 19:10:48 +08:00
return errors . New ( ` VLESS users: "tag" can't be empty for "reverse" ` )
2026-03-23 09:49:32 +00:00
}
if account . Reverse . Sniffing != nil { // may not be reached: error json unmarshal
2026-05-07 19:10:48 +08:00
return errors . New ( ` VLESS users: inbound's "reverse" can't have "sniffing" ` )
2026-03-23 09:49:32 +00:00
}
2025-09-09 14:19:12 +00:00
}
2020-11-25 19:01:53 +08:00
user . Account = serial . ToTypedMessage ( account )
2026-05-07 19:10:48 +08:00
config . Users [ idx ] = user
2026-05-02 16:32:59 +03:00
return nil
}
2026-05-07 19:10:48 +08:00
if err := task . ParallelForN ( len ( c . Users ) , processClient ) ; err != nil {
2026-05-02 16:32:59 +03:00
return nil , err
2020-11-25 19:01:53 +08:00
}
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
}
2026-03-23 09:49:32 +00:00
type VLessReverseConfig struct {
Tag string ` json:"tag" `
Sniffing * SniffingConfig ` json:"sniffing" `
}
func ( c * VLessReverseConfig ) Build ( ) ( * vless . Reverse , error ) {
if c . Tag == "" {
return nil , errors . New ( ` VLESS reverse: "tag" can't be empty ` )
}
r := & vless . Reverse {
Tag : c . Tag ,
}
if c . Sniffing != nil {
sc , err := c . Sniffing . Build ( )
if err != nil {
return nil , errors . New ( ` VLESS reverse: invalid "sniffing" config ` ) . Base ( err )
}
r . Sniffing = sc
}
return r , nil
}
2020-11-25 19:01:53 +08:00
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" `
2026-03-23 09:49:32 +00:00
Reverse * VLessReverseConfig ` 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
2026-03-23 09:49:32 +00:00
if c . Reverse != nil {
rvs , err := c . Reverse . Build ( )
if err != nil {
return nil , err
}
account . Reverse = rvs
}
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 )
}
2026-03-23 09:49:32 +00:00
if account . Reverse != nil { // may not be reached: error json unmarshal
return nil , errors . New ( ` VLESS users: please use simplified outbound's config style to use "reverse" ` )
}
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 {
2026-01-18 04:17:25 +00:00
case "" :
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
}
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
}