Browse Source

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

Like https://github.com/XTLS/Xray-core/commit/eda8be601f56c788b1ad176c7d68d8e2ce22c7ca
𐲓𐳛𐳪𐳂𐳐 𐲀𐳢𐳦𐳫𐳢 𐲥𐳔𐳛𐳪𐳌𐳑𐳖𐳇 1 month ago
parent
commit
83c5370eec

+ 31 - 6
infra/conf/http.go

@@ -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)

+ 26 - 2
infra/conf/shadowsocks.go

@@ -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.")
 	}

+ 30 - 5
infra/conf/socks.go

@@ -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)

+ 31 - 0
infra/conf/socks_test.go

@@ -86,5 +86,36 @@ func TestSocksOutboundConfig(t *testing.T) {
 				},
 			},
 		},
+		{
+			Input: `{
+				"address": "127.0.0.1",
+				"port": 1234,
+				"user": "test user",
+				"pass": "test pass",
+				"email": "[email protected]"
+			}`,
+			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: "[email protected]",
+								Account: serial.ToTypedMessage(&socks.Account{
+									Username: "test user",
+									Password: "test pass",
+								}),
+							},
+						},
+					},
+				},
+			},
+		},
 	})
 }

+ 20 - 2
infra/conf/trojan.go

@@ -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.")
 	}

+ 33 - 0
infra/conf/vless_test.go

@@ -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,
+							},
+						},
+					},
+				},
+			},
+		},
 	})
 }
 

+ 32 - 6
infra/conf/vmess.go

@@ -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)

+ 34 - 0
infra/conf/vmess_test.go

@@ -58,6 +58,40 @@ func TestVMessOutbound(t *testing.T) {
 				},
 			},
 		},
+		{
+			Input: `{
+				"address": "127.0.0.1",
+				"port": 80,
+				"id": "e641f5ad-9397-41e3-bf1a-e8740dfed019",
+				"email": "[email protected]",
+				"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: "[email protected]",
+								Level: 255,
+								Account: serial.ToTypedMessage(&vmess.Account{
+									Id: "e641f5ad-9397-41e3-bf1a-e8740dfed019",
+									SecuritySettings: &protocol.SecurityConfig{
+										Type: protocol.SecurityType_AUTO,
+									},
+								}),
+							},
+						},
+					},
+				},
+			},
+		},
 	})
 }