From 15d3418c79f3da21da7324c6735a0de750aca428 Mon Sep 17 00:00:00 2001
From: DHR60 <dehongren60@gmail.com>
Date: Wed, 9 Apr 2025 15:09:36 +0800
Subject: [PATCH] add xray wireguard support (#7089)

* add xray wireguard support

* add wireguard core type settings

* Update OptionSettingWindow.axaml
---
 v2rayN/ServiceLib/Common/JsonUtils.cs         |  4 ++-
 v2rayN/ServiceLib/Handler/CoreHandler.cs      |  2 +-
 v2rayN/ServiceLib/Models/V2rayConfig.cs       | 20 +++++++++++++
 .../CoreConfig/CoreConfigV2rayService.cs      | 28 +++++++++++++++----
 .../ViewModels/OptionSettingViewModel.cs      |  9 ++++++
 .../Views/OptionSettingWindow.axaml           | 15 +++++++++-
 .../Views/OptionSettingWindow.axaml.cs        |  2 ++
 v2rayN/v2rayN/Views/OptionSettingWindow.xaml  | 16 +++++++++++
 .../v2rayN/Views/OptionSettingWindow.xaml.cs  |  2 ++
 9 files changed, 90 insertions(+), 8 deletions(-)

diff --git a/v2rayN/ServiceLib/Common/JsonUtils.cs b/v2rayN/ServiceLib/Common/JsonUtils.cs
index 9ffa00ca..ea651022 100644
--- a/v2rayN/ServiceLib/Common/JsonUtils.cs
+++ b/v2rayN/ServiceLib/Common/JsonUtils.cs
@@ -1,3 +1,4 @@
+using System.Text.Encodings.Web;
 using System.Text.Json;
 using System.Text.Json.Nodes;
 using System.Text.Json.Serialization;
@@ -86,7 +87,8 @@ public class JsonUtils
             var options = new JsonSerializerOptions
             {
                 WriteIndented = indented,
-                DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull
+                DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull,
+                Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping // 避免转义加号
             };
             result = JsonSerializer.Serialize(obj, options);
         }
diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs
index c3535a4a..41ea866d 100644
--- a/v2rayN/ServiceLib/Handler/CoreHandler.cs
+++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs
@@ -101,7 +101,7 @@ public class CoreHandler
 
     public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
     {
-        var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
+        var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC) ? ECoreType.sing_box : ECoreType.Xray;
         var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
         var configPath = Utils.GetBinConfigPath(fileName);
         var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs
index ca6636b7..d080d14c 100644
--- a/v2rayN/ServiceLib/Models/V2rayConfig.cs
+++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs
@@ -127,6 +127,26 @@ public class Outboundsettings4Ray
     public int? userLevel { get; set; }
 
     public FragmentItem4Ray? fragment { get; set; }
+
+    public string? secretKey { get; set; }
+
+    public List<string>? address { get; set; }
+
+    public List<WireguardPeer4Ray>? peers { get; set; }
+
+    public bool? noKernelTun { get; set; }
+
+    public int? mtu { get; set; }
+
+    public List<int>? reserved { get; set; }
+
+    public int? workers { get; set; }
+}
+
+public class WireguardPeer4Ray
+{
+    public string endpoint { get; set; }
+    public string publicKey { get; set; }
 }
 
 public class VnextItem4Ray
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs
index c09d325d..4106dc43 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs
@@ -120,7 +120,7 @@ public class CoreConfigV2rayService
                 {
                     continue;
                 }
-                if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard)
+                if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC)
                 {
                     continue;
                 }
@@ -805,6 +805,26 @@ public class CoreConfigV2rayService
                         outbound.settings.vnext = null;
                         break;
                     }
+                case EConfigType.WireGuard:
+                    {
+                        var peer = new WireguardPeer4Ray
+                        {
+                            publicKey = node.PublicKey,
+                            endpoint = node.Address + ":" + node.Port.ToString()
+                        };
+                        var setting = new Outboundsettings4Ray
+                        {
+                            address = Utils.String2List(node.RequestHost),
+                            secretKey = node.Id,
+                            reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(),
+                            mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(),
+                            peers = new List<WireguardPeer4Ray> { peer }
+                        };
+                        outbound.settings = setting;
+                        outbound.settings.vnext = null;
+                        outbound.settings.servers = null;
+                        break;
+                    }
             }
 
             outbound.protocol = Global.ProtocolTypes[node.ConfigType];
@@ -1270,8 +1290,7 @@ public class CoreConfigV2rayService
             if (prevNode is not null
                 && prevNode.ConfigType != EConfigType.Custom
                 && prevNode.ConfigType != EConfigType.Hysteria2
-                && prevNode.ConfigType != EConfigType.TUIC
-                && prevNode.ConfigType != EConfigType.WireGuard)
+                && prevNode.ConfigType != EConfigType.TUIC)
             {
                 var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
                 await GenOutbound(prevNode, prevOutbound);
@@ -1289,8 +1308,7 @@ public class CoreConfigV2rayService
             if (nextNode is not null
                 && nextNode.ConfigType != EConfigType.Custom
                 && nextNode.ConfigType != EConfigType.Hysteria2
-                && nextNode.ConfigType != EConfigType.TUIC
-                && nextNode.ConfigType != EConfigType.WireGuard)
+                && nextNode.ConfigType != EConfigType.TUIC)
             {
                 var nextOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
                 await GenOutbound(nextNode, nextOutbound);
diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs
index 040d12ed..b99339d6 100644
--- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs
@@ -100,6 +100,7 @@ public class OptionSettingViewModel : MyReactiveObject
     [Reactive] public string CoreType4 { get; set; }
     [Reactive] public string CoreType5 { get; set; }
     [Reactive] public string CoreType6 { get; set; }
+    [Reactive] public string CoreType9 { get; set; }
 
     #endregion CoreType
 
@@ -259,6 +260,10 @@ public class OptionSettingViewModel : MyReactiveObject
                 case 6:
                     CoreType6 = type;
                     break;
+
+                case 9:
+                    CoreType9 = type;
+                    break;
             }
         });
         await Task.CompletedTask;
@@ -407,6 +412,10 @@ public class OptionSettingViewModel : MyReactiveObject
                     type = CoreType6;
                     break;
 
+                case 9:
+                    type = CoreType9;
+                    break;
+
                 default:
                     continue;
             }
diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
index 47df97d2..8c9351d7 100644
--- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml
@@ -813,7 +813,7 @@
                 <Grid
                     Margin="{StaticResource Margin4}"
                     ColumnDefinitions="Auto,Auto"
-                    RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto">
+                    RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
                     <TextBlock
                         Grid.Row="1"
                         Grid.Column="0"
@@ -891,6 +891,19 @@
                         Grid.Column="1"
                         Width="200"
                         Margin="{StaticResource Margin4}" />
+
+                    <TextBlock
+                        Grid.Row="7"
+                        Grid.Column="0"
+                        Margin="{StaticResource Margin4}"
+                        VerticalAlignment="Center"
+                        Text="Wireguard" />
+                    <ComboBox
+                        x:Name="cmbCoreType9"
+                        Grid.Row="7"
+                        Grid.Column="1"
+                        Width="200"
+                        Margin="{StaticResource Margin4}" />
                 </Grid>
             </TabItem>
         </TabControl>
diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs
index ea8ba8a7..58aceaf1 100644
--- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs
@@ -64,6 +64,7 @@ public partial class OptionSettingWindow : ReactiveWindow<OptionSettingViewModel
             cmbCoreType4.Items.Add(it);
             cmbCoreType5.Items.Add(it);
             cmbCoreType6.Items.Add(it);
+            cmbCoreType9.Items.Add(it);
         });
 
         for (var i = 2; i <= 8; i++)
@@ -160,6 +161,7 @@ public partial class OptionSettingWindow : ReactiveWindow<OptionSettingViewModel
             this.Bind(ViewModel, vm => vm.CoreType4, v => v.cmbCoreType4.SelectedValue).DisposeWith(disposables);
             this.Bind(ViewModel, vm => vm.CoreType5, v => v.cmbCoreType5.SelectedValue).DisposeWith(disposables);
             this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.SelectedValue).DisposeWith(disposables);
+            this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.SelectedValue).DisposeWith(disposables);
 
             this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
         });
diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml
index a5d7aeee..374dce35 100644
--- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml
+++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml
@@ -1098,6 +1098,7 @@
                         <RowDefinition Height="Auto" />
                         <RowDefinition Height="Auto" />
                         <RowDefinition Height="Auto" />
+                        <RowDefinition Height="Auto" />
                     </Grid.RowDefinitions>
                     <Grid.ColumnDefinitions>
                         <ColumnDefinition Width="Auto" />
@@ -1192,6 +1193,21 @@
                         Width="200"
                         Margin="{StaticResource Margin8}"
                         Style="{StaticResource DefComboBox}" />
+
+                    <TextBlock
+                        Grid.Row="7"
+                        Grid.Column="0"
+                        Margin="{StaticResource Margin8}"
+                        VerticalAlignment="Center"
+                        Style="{StaticResource ToolbarTextBlock}"
+                        Text="Wireguard" />
+                    <ComboBox
+                        x:Name="cmbCoreType9"
+                        Grid.Row="7"
+                        Grid.Column="1"
+                        Width="200"
+                        Margin="{StaticResource Margin8}"
+                        Style="{StaticResource DefComboBox}" />
                 </Grid>
             </TabItem>
         </TabControl>
diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs
index 3bb433bc..b0b5440c 100644
--- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs
@@ -66,6 +66,7 @@ public partial class OptionSettingWindow
             cmbCoreType4.Items.Add(it);
             cmbCoreType5.Items.Add(it);
             cmbCoreType6.Items.Add(it);
+            cmbCoreType9.Items.Add(it);
         });
 
         for (var i = 2; i <= 8; i++)
@@ -178,6 +179,7 @@ public partial class OptionSettingWindow
             this.Bind(ViewModel, vm => vm.CoreType4, v => v.cmbCoreType4.Text).DisposeWith(disposables);
             this.Bind(ViewModel, vm => vm.CoreType5, v => v.cmbCoreType5.Text).DisposeWith(disposables);
             this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.Text).DisposeWith(disposables);
+            this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.Text).DisposeWith(disposables);
 
             this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
         });