Преглед изворни кода

fix: prevent AddUser panic on nil flow for VLESS XHTTP clients

#4056
MHSanaei пре 4 дана
родитељ
комит
53fb4fe8f9
1 измењених фајлова са 79 додато и 11 уклоњено
  1. 79 11
      xray/api.go

+ 79 - 11
xray/api.go

@@ -37,6 +37,34 @@ type XrayAPI struct {
 	isConnected          bool
 }
 
+func getRequiredUserString(user map[string]any, key string) (string, error) {
+	value, ok := user[key]
+	if !ok || value == nil {
+		return "", fmt.Errorf("missing required user field %q", key)
+	}
+
+	strValue, ok := value.(string)
+	if !ok {
+		return "", fmt.Errorf("invalid type for user field %q: %T", key, value)
+	}
+
+	return strValue, nil
+}
+
+func getOptionalUserString(user map[string]any, key string) (string, error) {
+	value, ok := user[key]
+	if !ok || value == nil {
+		return "", nil
+	}
+
+	strValue, ok := value.(string)
+	if !ok {
+		return "", fmt.Errorf("invalid type for user field %q: %T", key, value)
+	}
+
+	return strValue, nil
+}
+
 // Init connects to the Xray API server and initializes handler and stats service clients.
 func (x *XrayAPI) Init(apiPort int) error {
 	if apiPort <= 0 || apiPort > math.MaxUint16 {
@@ -104,16 +132,36 @@ func (x *XrayAPI) DelInbound(tag string) error {
 
 // AddUser adds a user to an inbound in the Xray core using the specified protocol and user data.
 func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]any) error {
+	userEmail, err := getRequiredUserString(user, "email")
+	if err != nil {
+		return err
+	}
+
 	var account *serial.TypedMessage
 	switch Protocol {
 	case "vmess":
+		userID, err := getRequiredUserString(user, "id")
+		if err != nil {
+			return err
+		}
+
 		account = serial.ToTypedMessage(&vmess.Account{
-			Id: user["id"].(string),
+			Id: userID,
 		})
 	case "vless":
+		userID, err := getRequiredUserString(user, "id")
+		if err != nil {
+			return err
+		}
+
+		userFlow, err := getOptionalUserString(user, "flow")
+		if err != nil {
+			return err
+		}
+
 		vlessAccount := &vless.Account{
-			Id:   user["id"].(string),
-			Flow: user["flow"].(string),
+			Id:   userID,
+			Flow: userFlow,
 		}
 		// Add testseed if provided
 		if testseedVal, ok := user["testseed"]; ok {
@@ -139,12 +187,27 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]an
 		}
 		account = serial.ToTypedMessage(vlessAccount)
 	case "trojan":
+		password, err := getRequiredUserString(user, "password")
+		if err != nil {
+			return err
+		}
+
 		account = serial.ToTypedMessage(&trojan.Account{
-			Password: user["password"].(string),
+			Password: password,
 		})
 	case "shadowsocks":
+		cipher, err := getOptionalUserString(user, "cipher")
+		if err != nil {
+			return err
+		}
+
+		password, err := getRequiredUserString(user, "password")
+		if err != nil {
+			return err
+		}
+
 		var ssCipherType shadowsocks.CipherType
-		switch user["cipher"].(string) {
+		switch cipher {
 		case "aes-128-gcm":
 			ssCipherType = shadowsocks.CipherType_AES_128_GCM
 		case "aes-256-gcm":
@@ -159,18 +222,23 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]an
 
 		if ssCipherType != shadowsocks.CipherType_NONE {
 			account = serial.ToTypedMessage(&shadowsocks.Account{
-				Password:   user["password"].(string),
+				Password:   password,
 				CipherType: ssCipherType,
 			})
 		} else {
 			account = serial.ToTypedMessage(&shadowsocks_2022.ServerConfig{
-				Key:   user["password"].(string),
-				Email: user["email"].(string),
+				Key:   password,
+				Email: userEmail,
 			})
 		}
 	case "hysteria":
+		auth, err := getRequiredUserString(user, "auth")
+		if err != nil {
+			return err
+		}
+
 		account = serial.ToTypedMessage(&hysteriaAccount.Account{
-			Auth: user["auth"].(string),
+			Auth: auth,
 		})
 	default:
 		return nil
@@ -178,11 +246,11 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]an
 
 	client := *x.HandlerServiceClient
 
-	_, err := client.AlterInbound(context.Background(), &command.AlterInboundRequest{
+	_, err = client.AlterInbound(context.Background(), &command.AlterInboundRequest{
 		Tag: inboundTag,
 		Operation: serial.ToTypedMessage(&command.AddUserOperation{
 			User: &protocol.User{
-				Email:   user["email"].(string),
+				Email:   userEmail,
 				Account: account,
 			},
 		}),