2020-11-25 19:01:53 +08:00
package conf
import (
"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"
"github.com/xtls/xray-core/proxy/trojan"
2023-08-10 04:43:34 +00:00
"google.golang.org/protobuf/proto"
2020-11-25 19:01:53 +08:00
)
// TrojanServerTarget is configuration of a single trojan server
type TrojanServerTarget struct {
Address * Address ` json:"address" `
Port uint16 ` json:"port" `
Level byte ` json:"level" `
2025-09-11 21:48:20 +08:00
Email string ` json:"email" `
Password string ` json:"password" `
2020-11-25 19:01:53 +08:00
Flow string ` json:"flow" `
}
// TrojanClientConfig is configuration of trojan servers
type TrojanClientConfig struct {
2025-09-15 21:31:27 +08:00
Address * Address ` json:"address" `
Port uint16 ` json:"port" `
Level byte ` json:"level" `
Email string ` json:"email" `
Password string ` json:"password" `
Flow string ` json:"flow" `
Servers [ ] * TrojanServerTarget ` json:"servers" `
2020-11-25 19:01:53 +08:00
}
// Build implements Buildable
func ( c * TrojanClientConfig ) Build ( ) ( proto . Message , error ) {
2025-09-11 21:48:20 +08:00
if c . Address != nil {
c . Servers = [ ] * TrojanServerTarget {
{
Address : c . Address ,
Port : c . Port ,
Level : c . Level ,
Email : c . Email ,
Password : c . Password ,
Flow : c . Flow ,
} ,
}
}
2025-09-15 21:31:27 +08:00
if len ( c . Servers ) != 1 {
return nil , errors . New ( ` Trojan settings: "servers" should have one and only one member. Multiple endpoints in "servers" should use multiple Trojan outbounds and routing balancer instead ` )
2020-11-25 19:01:53 +08:00
}
2025-09-15 21:31:27 +08:00
config := & trojan . ClientConfig { }
2023-07-06 15:18:05 +00:00
2025-09-15 21:31:27 +08:00
for _ , rec := range c . Servers {
2020-11-25 19:01:53 +08:00
if rec . Address == nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Trojan server address is not set." )
2020-11-25 19:01:53 +08:00
}
if rec . Port == 0 {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Invalid Trojan port." )
2020-11-25 19:01:53 +08:00
}
if rec . Password == "" {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Trojan password is not specified." )
2020-11-25 19:01:53 +08:00
}
2023-07-06 15:18:05 +00:00
if rec . Flow != "" {
2024-09-15 12:55:54 +08:00
return nil , errors . PrintRemovedFeatureError ( ` Flow for Trojan ` , ` ` )
2020-12-14 17:05:15 +08:00
}
2025-09-15 21:31:27 +08:00
config . Server = & protocol . ServerEndpoint {
2020-11-25 19:01:53 +08:00
Address : rec . Address . Build ( ) ,
Port : uint32 ( rec . Port ) ,
2025-09-15 21:31:27 +08:00
User : & protocol . User {
Level : uint32 ( rec . Level ) ,
Email : rec . Email ,
Account : serial . ToTypedMessage ( & trojan . Account {
Password : rec . Password ,
} ) ,
2020-11-25 19:01:53 +08:00
} ,
}
2025-09-15 21:31:27 +08:00
break
2020-11-25 19:01:53 +08:00
}
return config , nil
}
// TrojanInboundFallback is fallback configuration
type TrojanInboundFallback struct {
2021-01-18 07:41:00 +00: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" `
}
// TrojanUserConfig is user configuration
type TrojanUserConfig struct {
Password string ` json:"password" `
Level byte ` json:"level" `
Email string ` json:"email" `
Flow string ` json:"flow" `
}
// TrojanServerConfig is Inbound configuration
type TrojanServerConfig struct {
Clients [ ] * TrojanUserConfig ` json:"clients" `
Fallbacks [ ] * TrojanInboundFallback ` json:"fallbacks" `
}
// Build implements Buildable
func ( c * TrojanServerConfig ) Build ( ) ( proto . Message , error ) {
2023-07-06 15:18:05 +00:00
config := & trojan . ServerConfig {
Users : make ( [ ] * protocol . User , len ( c . Clients ) ) ,
}
2020-11-25 19:01:53 +08:00
for idx , rawUser := range c . Clients {
2023-07-06 15:18:05 +00:00
if rawUser . Flow != "" {
2024-09-15 12:55:54 +08:00
return nil , errors . PrintRemovedFeatureError ( ` Flow for Trojan ` , ` ` )
2020-11-25 19:01:53 +08:00
}
2023-07-06 15:18:05 +00:00
config . Users [ idx ] = & protocol . User {
Level : uint32 ( rawUser . Level ) ,
Email : rawUser . Email ,
Account : serial . ToTypedMessage ( & trojan . Account {
Password : rawUser . Password ,
} ) ,
2020-12-14 17:05:15 +08:00
}
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 , & trojan . Fallback {
2021-01-18 07:41:00 +00: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 ( ` Trojan 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 ( ` Trojan 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 ( ` Trojan 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 ( ` Trojan fallbacks: invalid PROXY protocol version, "xver" only accepts 0, 1, 2 ` )
2020-11-25 19:01:53 +08:00
}
}
return config , nil
}