2020-11-25 19:01:53 +08:00
package conf
import (
"strings"
2022-05-25 17:25:26 +08:00
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
2022-05-23 20:45:30 +08:00
C "github.com/sagernet/sing/common"
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/protocol"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/proxy/shadowsocks"
2022-05-23 20:45:30 +08:00
"github.com/xtls/xray-core/proxy/shadowsocks_2022"
2023-08-10 04:43:34 +00:00
"google.golang.org/protobuf/proto"
2020-11-25 19:01:53 +08:00
)
func cipherFromString ( c string ) shadowsocks . CipherType {
switch strings . ToLower ( c ) {
case "aes-128-gcm" , "aead_aes_128_gcm" :
return shadowsocks . CipherType_AES_128_GCM
case "aes-256-gcm" , "aead_aes_256_gcm" :
return shadowsocks . CipherType_AES_256_GCM
case "chacha20-poly1305" , "aead_chacha20_poly1305" , "chacha20-ietf-poly1305" :
return shadowsocks . CipherType_CHACHA20_POLY1305
2021-05-25 23:45:48 +08:00
case "xchacha20-poly1305" , "aead_xchacha20_poly1305" , "xchacha20-ietf-poly1305" :
return shadowsocks . CipherType_XCHACHA20_POLY1305
2020-11-25 19:01:53 +08:00
case "none" , "plain" :
return shadowsocks . CipherType_NONE
default :
return shadowsocks . CipherType_UNKNOWN
}
}
2021-01-18 22:52:35 +00:00
type ShadowsocksUserConfig struct {
2022-08-07 19:18:23 -04:00
Cipher string ` json:"method" `
Password string ` json:"password" `
Level byte ` json:"level" `
Email string ` json:"email" `
Address * Address ` json:"address" `
Port uint16 ` json:"port" `
2021-01-18 22:52:35 +00:00
}
2020-11-25 19:01:53 +08:00
type ShadowsocksServerConfig struct {
2021-01-18 22:52:35 +00:00
Cipher string ` json:"method" `
Password string ` json:"password" `
Level byte ` json:"level" `
Email string ` json:"email" `
Users [ ] * ShadowsocksUserConfig ` json:"clients" `
NetworkList * NetworkList ` json:"network" `
2021-10-22 00:03:09 -04:00
IVCheck bool ` json:"ivCheck" `
2020-11-25 19:01:53 +08:00
}
func ( v * ShadowsocksServerConfig ) Build ( ) ( proto . Message , error ) {
2022-05-23 20:45:30 +08:00
if C . Contains ( shadowaead_2022 . List , v . Cipher ) {
2022-08-07 19:18:23 -04:00
return buildShadowsocks2022 ( v )
2022-05-23 20:45:30 +08:00
}
2020-11-25 19:01:53 +08:00
config := new ( shadowsocks . ServerConfig )
config . Network = v . NetworkList . Build ( )
2021-01-18 22:52:35 +00:00
if v . Users != nil {
for _ , user := range v . Users {
account := & shadowsocks . Account {
Password : user . Password ,
CipherType : cipherFromString ( user . Cipher ) ,
2021-10-22 00:03:09 -04:00
IvCheck : v . IVCheck ,
2021-01-18 22:52:35 +00:00
}
if account . Password == "" {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Shadowsocks password is not specified." )
2021-01-18 22:52:35 +00:00
}
2021-09-20 22:41:09 +08:00
if account . CipherType < shadowsocks . CipherType_AES_128_GCM ||
2021-12-17 00:25:16 +08:00
account . CipherType > shadowsocks . CipherType_XCHACHA20_POLY1305 {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "unsupported cipher method: " , user . Cipher )
2021-01-18 22:52:35 +00:00
}
config . Users = append ( config . Users , & protocol . User {
Email : user . Email ,
Level : uint32 ( user . Level ) ,
Account : serial . ToTypedMessage ( account ) ,
} )
}
} else {
account := & shadowsocks . Account {
Password : v . Password ,
CipherType : cipherFromString ( v . Cipher ) ,
2021-10-22 00:03:09 -04:00
IvCheck : v . IVCheck ,
2021-01-18 22:52:35 +00:00
}
if account . Password == "" {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Shadowsocks password is not specified." )
2021-01-18 22:52:35 +00:00
}
if account . CipherType == shadowsocks . CipherType_UNKNOWN {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "unknown cipher method: " , v . Cipher )
2021-01-18 22:52:35 +00:00
}
config . Users = append ( config . Users , & protocol . User {
Email : v . Email ,
Level : uint32 ( v . Level ) ,
Account : serial . ToTypedMessage ( account ) ,
} )
2020-11-25 19:01:53 +08:00
}
return config , nil
}
2022-08-07 19:18:23 -04:00
func buildShadowsocks2022 ( v * ShadowsocksServerConfig ) ( proto . Message , error ) {
if len ( v . Users ) == 0 {
config := new ( shadowsocks_2022 . ServerConfig )
config . Method = v . Cipher
config . Key = v . Password
config . Network = v . NetworkList . Build ( )
config . Email = v . Email
return config , nil
}
2022-12-25 19:37:35 -05:00
2022-08-07 19:18:23 -04:00
if v . Cipher == "" {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "shadowsocks 2022 (multi-user): missing server method" )
2022-08-07 19:18:23 -04:00
}
if ! strings . Contains ( v . Cipher , "aes" ) {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "shadowsocks 2022 (multi-user): only blake3-aes-*-gcm methods are supported" )
2022-08-07 19:18:23 -04:00
}
if v . Users [ 0 ] . Address == nil {
config := new ( shadowsocks_2022 . MultiUserServerConfig )
config . Method = v . Cipher
config . Key = v . Password
config . Network = v . NetworkList . Build ( )
2022-12-25 19:37:35 -05:00
2022-08-07 19:18:23 -04:00
for _ , user := range v . Users {
if user . Cipher != "" {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "shadowsocks 2022 (multi-user): users must have empty method" )
2022-08-07 19:18:23 -04:00
}
2024-11-03 00:25:23 -04:00
account := & shadowsocks_2022 . Account {
Key : user . Password ,
}
config . Users = append ( config . Users , & protocol . User {
Email : user . Email ,
Level : uint32 ( user . Level ) ,
Account : serial . ToTypedMessage ( account ) ,
2022-08-07 19:18:23 -04:00
} )
}
return config , nil
}
config := new ( shadowsocks_2022 . RelayServerConfig )
config . Method = v . Cipher
config . Key = v . Password
config . Network = v . NetworkList . Build ( )
for _ , user := range v . Users {
if user . Cipher != "" {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "shadowsocks 2022 (relay): users must have empty method" )
2022-08-07 19:18:23 -04:00
}
if user . Address == nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "shadowsocks 2022 (relay): all users must have relay address" )
2022-08-07 19:18:23 -04:00
}
config . Destinations = append ( config . Destinations , & shadowsocks_2022 . RelayDestination {
2022-12-25 19:37:35 -05:00
Key : user . Password ,
Email : user . Email ,
2022-08-07 19:18:23 -04:00
Address : user . Address . Build ( ) ,
2022-12-25 19:37:35 -05:00
Port : uint32 ( user . Port ) ,
2022-08-07 19:18:23 -04:00
} )
}
return config , nil
}
2020-11-25 19:01:53 +08:00
type ShadowsocksServerTarget struct {
2023-03-15 14:42:32 +08:00
Address * Address ` json:"address" `
Port uint16 ` json:"port" `
2025-09-11 21:48:20 +08:00
Level byte ` json:"level" `
Email string ` json:"email" `
2023-03-15 14:42:32 +08:00
Cipher string ` json:"method" `
Password string ` json:"password" `
IVCheck bool ` json:"ivCheck" `
UoT bool ` json:"uot" `
UoTVersion int ` json:"uotVersion" `
2020-11-25 19:01:53 +08:00
}
type ShadowsocksClientConfig 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" `
Cipher string ` json:"method" `
Password string ` json:"password" `
IVCheck bool ` json:"ivCheck" `
UoT bool ` json:"uot" `
UoTVersion int ` json:"uotVersion" `
Servers [ ] * ShadowsocksServerTarget ` json:"servers" `
2020-11-25 19:01:53 +08:00
}
func ( v * ShadowsocksClientConfig ) Build ( ) ( proto . Message , error ) {
2025-09-11 21:48:20 +08:00
if v . Address != nil {
v . Servers = [ ] * ShadowsocksServerTarget {
{
Address : v . Address ,
Port : v . Port ,
Level : v . Level ,
Email : v . Email ,
Cipher : v . Cipher ,
Password : v . Password ,
IVCheck : v . IVCheck ,
UoT : v . UoT ,
UoTVersion : v . UoTVersion ,
} ,
}
}
2025-09-15 21:31:27 +08:00
if len ( v . Servers ) != 1 {
return nil , errors . New ( ` Shadowsocks settings: "servers" should have one and only one member. Multiple endpoints in "servers" should use multiple Shadowsocks outbounds and routing balancer instead ` )
2020-11-25 19:01:53 +08:00
}
2022-05-23 20:45:30 +08:00
if len ( v . Servers ) == 1 {
server := v . Servers [ 0 ]
if C . Contains ( shadowaead_2022 . List , server . Cipher ) {
if server . Address == nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Shadowsocks server address is not set." )
2022-05-23 20:45:30 +08:00
}
if server . Port == 0 {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Invalid Shadowsocks port." )
2022-05-23 20:45:30 +08:00
}
if server . Password == "" {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Shadowsocks password is not specified." )
2022-05-23 20:45:30 +08:00
}
config := new ( shadowsocks_2022 . ClientConfig )
config . Address = server . Address . Build ( )
config . Port = uint32 ( server . Port )
config . Method = server . Cipher
config . Key = server . Password
2022-06-01 11:12:43 +08:00
config . UdpOverTcp = server . UoT
2023-03-15 14:42:32 +08:00
config . UdpOverTcpVersion = uint32 ( server . UoTVersion )
2022-05-23 20:45:30 +08:00
return config , nil
}
}
config := new ( shadowsocks . ClientConfig )
2025-09-15 21:31:27 +08:00
for _ , server := range v . Servers {
2022-05-23 20:45:30 +08:00
if C . Contains ( shadowaead_2022 . List , server . Cipher ) {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Shadowsocks 2022 accept no multi servers" )
2022-05-23 20:45:30 +08:00
}
2020-11-25 19:01:53 +08:00
if server . Address == nil {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Shadowsocks server address is not set." )
2020-11-25 19:01:53 +08:00
}
if server . Port == 0 {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Invalid Shadowsocks port." )
2020-11-25 19:01:53 +08:00
}
if server . Password == "" {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "Shadowsocks password is not specified." )
2020-11-25 19:01:53 +08:00
}
account := & shadowsocks . Account {
Password : server . Password ,
}
account . CipherType = cipherFromString ( server . Cipher )
if account . CipherType == shadowsocks . CipherType_UNKNOWN {
2024-06-29 14:32:57 -04:00
return nil , errors . New ( "unknown cipher method: " , server . Cipher )
2020-11-25 19:01:53 +08:00
}
2021-10-22 00:03:09 -04:00
account . IvCheck = server . IVCheck
2020-11-25 19:01:53 +08:00
ss := & protocol . ServerEndpoint {
Address : server . Address . Build ( ) ,
Port : uint32 ( server . Port ) ,
2025-09-15 21:31:27 +08:00
User : & protocol . User {
Level : uint32 ( server . Level ) ,
Email : server . Email ,
Account : serial . ToTypedMessage ( account ) ,
2020-11-25 19:01:53 +08:00
} ,
}
2025-09-15 21:31:27 +08:00
config . Server = ss
break
2020-11-25 19:01:53 +08:00
}
return config , nil
}