Config: Outbound proxy config no need to be nested (#5124)

Like eda8be601f
main
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇 2025-09-11 21:48:20 +08:00 committed by GitHub
parent 1a48453bea
commit 83c5370eec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 237 additions and 21 deletions

View File

@ -51,12 +51,27 @@ type HTTPRemoteConfig struct {
}
type HTTPClientConfig struct {
Servers []*HTTPRemoteConfig `json:"servers"`
Headers map[string]string `json:"headers"`
Address *Address `json:"address"`
Port uint16 `json:"port"`
Level uint32 `json:"level"`
Email string `json:"email"`
Username string `json:"user"`
Password string `json:"pass"`
Servers []*HTTPRemoteConfig `json:"servers"`
Headers map[string]string `json:"headers"`
}
func (v *HTTPClientConfig) Build() (proto.Message, error) {
config := new(http.ClientConfig)
if v.Address != nil {
v.Servers = []*HTTPRemoteConfig{
{
Address: v.Address,
Port: v.Port,
Users: []json.RawMessage{{}},
},
}
}
config.Server = make([]*protocol.ServerEndpoint, len(v.Servers))
for idx, serverConfig := range v.Servers {
server := &protocol.ServerEndpoint{
@ -65,12 +80,22 @@ func (v *HTTPClientConfig) Build() (proto.Message, error) {
}
for _, rawUser := range serverConfig.Users {
user := new(protocol.User)
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("failed to parse HTTP user").Base(err).AtError()
if v.Address != nil {
user.Level = v.Level
user.Email = v.Email
} else {
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("failed to parse HTTP user").Base(err).AtError()
}
}
account := new(HTTPAccount)
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("failed to parse HTTP account").Base(err).AtError()
if v.Address != nil {
account.Username = v.Username
account.Password = v.Password
} else {
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("failed to parse HTTP account").Base(err).AtError()
}
}
user.Account = serial.ToTypedMessage(account.Build())
server.User = append(server.User, user)

View File

@ -162,20 +162,44 @@ func buildShadowsocks2022(v *ShadowsocksServerConfig) (proto.Message, error) {
type ShadowsocksServerTarget struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Level byte `json:"level"`
Email string `json:"email"`
Cipher string `json:"method"`
Password string `json:"password"`
Email string `json:"email"`
Level byte `json:"level"`
IVCheck bool `json:"ivCheck"`
UoT bool `json:"uot"`
UoTVersion int `json:"uotVersion"`
}
type ShadowsocksClientConfig struct {
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"`
}
func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
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,
},
}
}
if len(v.Servers) == 0 {
return nil, errors.New("0 Shadowsocks server configured.")
}

View File

@ -70,11 +70,26 @@ type SocksRemoteConfig struct {
}
type SocksClientConfig struct {
Servers []*SocksRemoteConfig `json:"servers"`
Address *Address `json:"address"`
Port uint16 `json:"port"`
Level uint32 `json:"level"`
Email string `json:"email"`
Username string `json:"user"`
Password string `json:"pass"`
Servers []*SocksRemoteConfig `json:"servers"`
}
func (v *SocksClientConfig) Build() (proto.Message, error) {
config := new(socks.ClientConfig)
if v.Address != nil {
v.Servers = []*SocksRemoteConfig{
{
Address: v.Address,
Port: v.Port,
Users: []json.RawMessage{{}},
},
}
}
config.Server = make([]*protocol.ServerEndpoint, len(v.Servers))
for idx, serverConfig := range v.Servers {
server := &protocol.ServerEndpoint{
@ -83,12 +98,22 @@ func (v *SocksClientConfig) Build() (proto.Message, error) {
}
for _, rawUser := range serverConfig.Users {
user := new(protocol.User)
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("failed to parse Socks user").Base(err).AtError()
if v.Address != nil {
user.Level = v.Level
user.Email = v.Email
} else {
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("failed to parse Socks user").Base(err).AtError()
}
}
account := new(SocksAccount)
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("failed to parse socks account").Base(err).AtError()
if v.Address != nil {
account.Username = v.Username
account.Password = v.Password
} else {
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("failed to parse socks account").Base(err).AtError()
}
}
user.Account = serial.ToTypedMessage(account.Build())
server.User = append(server.User, user)

View File

@ -86,5 +86,36 @@ func TestSocksOutboundConfig(t *testing.T) {
},
},
},
{
Input: `{
"address": "127.0.0.1",
"port": 1234,
"user": "test user",
"pass": "test pass",
"email": "test@email.com"
}`,
Parser: loadJSON(creator),
Output: &socks.ClientConfig{
Server: []*protocol.ServerEndpoint{
{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
},
},
Port: 1234,
User: []*protocol.User{
{
Email: "test@email.com",
Account: serial.ToTypedMessage(&socks.Account{
Username: "test user",
Password: "test pass",
}),
},
},
},
},
},
},
})
}

View File

@ -20,19 +20,37 @@ import (
type TrojanServerTarget struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Password string `json:"password"`
Email string `json:"email"`
Level byte `json:"level"`
Email string `json:"email"`
Password string `json:"password"`
Flow string `json:"flow"`
}
// TrojanClientConfig is configuration of trojan servers
type TrojanClientConfig struct {
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"`
}
// Build implements Buildable
func (c *TrojanClientConfig) Build() (proto.Message, error) {
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,
},
}
}
if len(c.Servers) == 0 {
return nil, errors.New("0 Trojan server configured.")
}

View File

@ -57,6 +57,39 @@ func TestVLessOutbound(t *testing.T) {
},
},
},
{
Input: `{
"address": "example.com",
"port": 443,
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"flow": "xtls-rprx-vision-udp443",
"encryption": "none",
"level": 0
}`,
Parser: loadJSON(creator),
Output: &outbound.Config{
Vnext: []*protocol.ServerEndpoint{
{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Domain{
Domain: "example.com",
},
},
Port: 443,
User: []*protocol.User{
{
Account: serial.ToTypedMessage(&vless.Account{
Id: "27848739-7e62-4138-9fd3-098a63964b6b",
Flow: "xtls-rprx-vision-udp443",
Encryption: "none",
}),
Level: 0,
},
},
},
},
},
},
})
}

View File

@ -117,13 +117,28 @@ type VMessOutboundTarget struct {
}
type VMessOutboundConfig struct {
Receivers []*VMessOutboundTarget `json:"vnext"`
Address *Address `json:"address"`
Port uint16 `json:"port"`
Level uint32 `json:"level"`
Email string `json:"email"`
ID string `json:"id"`
Security string `json:"security"`
Experiments string `json:"experiments"`
Receivers []*VMessOutboundTarget `json:"vnext"`
}
// Build implements Buildable
func (c *VMessOutboundConfig) Build() (proto.Message, error) {
config := new(outbound.Config)
if c.Address != nil {
c.Receivers = []*VMessOutboundTarget{
{
Address: c.Address,
Port: c.Port,
Users: []json.RawMessage{{}},
},
}
}
if len(c.Receivers) == 0 {
return nil, errors.New("0 VMess receiver configured")
}
@ -141,12 +156,23 @@ func (c *VMessOutboundConfig) Build() (proto.Message, error) {
}
for _, rawUser := range rec.Users {
user := new(protocol.User)
if err := json.Unmarshal(rawUser, user); err != nil {
return nil, errors.New("invalid VMess user").Base(err)
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("invalid VMess user").Base(err)
}
}
account := new(VMessAccount)
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("invalid VMess user").Base(err)
if c.Address != nil {
account.ID = c.ID
account.Security = c.Security
account.Experiments = c.Experiments
} else {
if err := json.Unmarshal(rawUser, account); err != nil {
return nil, errors.New("invalid VMess user").Base(err)
}
}
u, err := uuid.ParseString(account.ID)

View File

@ -58,6 +58,40 @@ func TestVMessOutbound(t *testing.T) {
},
},
},
{
Input: `{
"address": "127.0.0.1",
"port": 80,
"id": "e641f5ad-9397-41e3-bf1a-e8740dfed019",
"email": "love@example.com",
"level": 255
}`,
Parser: loadJSON(creator),
Output: &outbound.Config{
Receiver: []*protocol.ServerEndpoint{
{
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
},
},
Port: 80,
User: []*protocol.User{
{
Email: "love@example.com",
Level: 255,
Account: serial.ToTypedMessage(&vmess.Account{
Id: "e641f5ad-9397-41e3-bf1a-e8740dfed019",
SecuritySettings: &protocol.SecurityConfig{
Type: protocol.SecurityType_AUTO,
},
}),
},
},
},
},
},
},
})
}