diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs index aa786c8e..73a854c9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/DynamicProxy/DynamicHttpProxy.cs @@ -1,4 +1,4 @@ -// Copyright (c) DGP Studio. All rights reserved. +// Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. using CommunityToolkit.Mvvm.ComponentModel; @@ -14,50 +14,34 @@ internal sealed partial class DynamicHttpProxy : ObservableObject, IWebProxy, ID { private const string ProxySettingPath = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections"; - private static readonly MethodInfo ConstructSystemProxyMethod; + private static readonly Lazy LazyConstructSystemProxyMethod = new(GetConstructSystemProxyMethod); + private readonly IServiceProvider serviceProvider; private readonly RegistryWatcher watcher; private IWebProxy innerProxy = default!; - [SuppressMessage("", "CA1810")] - static DynamicHttpProxy() + public DynamicHttpProxy(IServiceProvider serviceProvider) { - Type? systemProxyInfoType = typeof(System.Net.Http.SocketsHttpHandler).Assembly.GetType("System.Net.Http.SystemProxyInfo"); - ArgumentNullException.ThrowIfNull(systemProxyInfoType); + this.serviceProvider = serviceProvider; + UpdateInnerProxy(); - MethodInfo? constructSystemProxyMethod = systemProxyInfoType.GetMethod("ConstructSystemProxy", BindingFlags.Static | BindingFlags.Public); - ArgumentNullException.ThrowIfNull(constructSystemProxyMethod); - ConstructSystemProxyMethod = constructSystemProxyMethod; - } - - public DynamicHttpProxy() - { - UpdateProxy(); - - watcher = new(ProxySettingPath, OnProxyChanged); + watcher = new(ProxySettingPath, OnSystemProxySettingsChanged); watcher.Start(); } - /// - public ICredentials? Credentials - { - get => InnerProxy.Credentials; - set => InnerProxy.Credentials = value; - } - - public string CurrentProxy + public string CurrentProxyUri { get { - Uri? proxyUri = GetProxy(HutaoEndpoints.Website(string.Empty).ToUri()); + Uri? proxyUri = GetProxy("https://hut.ao".ToUri()); return proxyUri is null ? SH.ViewPageFeedbackCurrentProxyNoProxyDescription : proxyUri.AbsoluteUri; } } - private IWebProxy InnerProxy + public IWebProxy InnerProxy { get => innerProxy; @@ -74,6 +58,12 @@ internal sealed partial class DynamicHttpProxy : ObservableObject, IWebProxy, ID } } + public ICredentials? Credentials + { + get => InnerProxy.Credentials; + set => InnerProxy.Credentials = value; + } + public Uri? GetProxy(Uri destination) { return InnerProxy.GetProxy(destination); @@ -90,17 +80,30 @@ internal sealed partial class DynamicHttpProxy : ObservableObject, IWebProxy, ID watcher.Dispose(); } - public void OnProxyChanged() + public void OnSystemProxySettingsChanged() { - UpdateProxy(); + UpdateInnerProxy(); - Ioc.Default.GetRequiredService().InvokeOnMainThread(() => OnPropertyChanged(nameof(CurrentProxy))); + // TaskContext can't be injected directly since there are some recursive dependencies. + ITaskContext taskContext = serviceProvider.GetRequiredService(); + taskContext.BeginInvokeOnMainThread(() => OnPropertyChanged(nameof(CurrentProxyUri))); + } + + private static MethodInfo GetConstructSystemProxyMethod() + { + Type? systemProxyInfoType = typeof(System.Net.Http.SocketsHttpHandler).Assembly.GetType("System.Net.Http.SystemProxyInfo"); + ArgumentNullException.ThrowIfNull(systemProxyInfoType); + + MethodInfo? constructSystemProxyMethod = systemProxyInfoType.GetMethod("ConstructSystemProxy", BindingFlags.Static | BindingFlags.Public); + ArgumentNullException.ThrowIfNull(constructSystemProxyMethod); + + return constructSystemProxyMethod; } [MemberNotNull(nameof(innerProxy))] - private void UpdateProxy() + private void UpdateInnerProxy() { - IWebProxy? proxy = ConstructSystemProxyMethod.Invoke(default, default) as IWebProxy; + IWebProxy? proxy = LazyConstructSystemProxyMethod.Value.Invoke(default, default) as IWebProxy; ArgumentNullException.ThrowIfNull(proxy); InnerProxy = proxy; diff --git a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs index e80c9d7f..607349aa 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/IO/Http/Loopback/LoopbackManager.cs @@ -5,72 +5,81 @@ using CommunityToolkit.Mvvm.ComponentModel; using Snap.Hutao.Win32.Foundation; using Snap.Hutao.Win32.NetworkManagement.WindowsFirewall; using Snap.Hutao.Win32.Security; +using System.Runtime.InteropServices; using static Snap.Hutao.Win32.AdvApi32; using static Snap.Hutao.Win32.ApiMsWinNetIsolation; +using static Snap.Hutao.Win32.Macros; namespace Snap.Hutao.Core.IO.Http.Loopback; [Injection(InjectAs.Singleton)] -internal sealed unsafe class LoopbackManager : ObservableObject, IDisposable +internal sealed unsafe class LoopbackManager : ObservableObject { - private readonly INET_FIREWALL_APP_CONTAINER* pContainers; - private readonly INET_FIREWALL_APP_CONTAINER* pHutaoContainer; - private readonly RuntimeOptions runtimeOptions; private readonly ITaskContext taskContext; + private readonly SID hutaoContainerSID; + private bool isLoopbackEnabled; + public LoopbackManager(IServiceProvider serviceProvider) { runtimeOptions = serviceProvider.GetRequiredService(); taskContext = serviceProvider.GetRequiredService(); - NetworkIsolationEnumAppContainers(NETISO_FLAG.NETISO_FLAG_MAX, out uint acCount, out pContainers); - for (uint i = 0; i < acCount; i++) + INET_FIREWALL_APP_CONTAINER* pContainers = default; + try { - INET_FIREWALL_APP_CONTAINER* pContainer = pContainers + i; - if (new string(pContainer->appContainerName) == runtimeOptions.FamilyName) + WIN32_ERROR error = NetworkIsolationEnumAppContainers(NETISO_FLAG.NETISO_FLAG_MAX, out uint acCount, out pContainers); + Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(error)); + for (uint i = 0; i < acCount; i++) { - pHutaoContainer = pContainer; - break; + INET_FIREWALL_APP_CONTAINER* pContainer = pContainers + i; + ReadOnlySpan appContainerName = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(pContainer->appContainerName); + if (appContainerName.Equals(runtimeOptions.FamilyName)) + { + hutaoContainerSID = *pContainer->appContainerSid; + break; + } } } - - NetworkIsolationGetAppContainerConfig(out uint accCount, out SID_AND_ATTRIBUTES* pSids); - for (uint i = 0; i < accCount; i++) + finally { - if (EqualSid(new PSID { Value = pHutaoContainer->appContainerSid }, (pSids + i)->Sid)) + uint retVal = NetworkIsolationFreeAppContainers(pContainers); + Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(*(WIN32_ERROR*)&retVal)); + } + + WIN32_ERROR error2 = NetworkIsolationGetAppContainerConfig(out uint accCount, out SID_AND_ATTRIBUTES* pSids); + Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(error2)); + fixed (SID* phutaoContainerSID = &hutaoContainerSID) + { + for (uint i = 0; i < accCount; i++) { - IsLoopbackEnabled = true; - break; + if (EqualSid(phutaoContainerSID, (pSids + i)->Sid)) + { + IsLoopbackEnabled = true; + break; + } } } } - public bool IsLoopbackEnabled { get; private set; } + public bool IsLoopbackEnabled { get => isLoopbackEnabled; private set => SetProperty(ref isLoopbackEnabled, value); } public void EnableLoopback() { - List sids = []; - NetworkIsolationGetAppContainerConfig(out uint accCount, out SID_AND_ATTRIBUTES* pSids); + List sids = new((int)(accCount + 1)); for (uint i = 0; i < accCount; i++) { sids.Add(*(pSids + i)); } - sids.Add(new SID_AND_ATTRIBUTES + fixed (SID* phutaoContainerSID = &hutaoContainerSID) { - Sid = new PSID { Value = pHutaoContainer->appContainerSid }, - Attributes = 0, - }); - - IsLoopbackEnabled = NetworkIsolationSetAppContainerConfig(sids.ToArray()) is 0U; - - taskContext.InvokeOnMainThread(() => OnPropertyChanged(nameof(IsLoopbackEnabled))); - } - - public void Dispose() - { - _ = NetworkIsolationFreeAppContainers(pContainers); + SID_AND_ATTRIBUTES sidAndAttributes = default; + sidAndAttributes.Sid = phutaoContainerSID; + sids.Add(sidAndAttributes); + IsLoopbackEnabled = NetworkIsolationSetAppContainerConfig(CollectionsMarshal.AsSpan(sids)) is WIN32_ERROR.ERROR_SUCCESS; + } } } diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Feedback/FeedbackViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Feedback/FeedbackViewModel.cs index 7a7bd53a..e80b8edb 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Feedback/FeedbackViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Feedback/FeedbackViewModel.cs @@ -130,6 +130,7 @@ internal sealed partial class FeedbackViewModel : Abstraction.ViewModel if (result is ContentDialogResult.Primary) { + await taskContext.SwitchToMainThreadAsync(); LoopbackManager.EnableLoopback(); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/AdvApi32.cs b/src/Snap.Hutao/Snap.Hutao/Win32/AdvApi32.cs index 5958a9dc..d95aa0e4 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/AdvApi32.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/AdvApi32.cs @@ -13,8 +13,8 @@ namespace Snap.Hutao.Win32; [SuppressMessage("", "SYSLIB1054")] internal static class AdvApi32 { - [DllImport("ADVAPI32.dll", ExactSpelling = true)] - [SupportedOSPlatform("windows5.0")] + [DllImport("ADVAPI32.dll", ExactSpelling = true, SetLastError = true)] + [SupportedOSPlatform("windows5.1.2600")] public static extern BOOL EqualSid(PSID pSid1, PSID pSid2); [DllImport("ADVAPI32.dll", ExactSpelling = true)] diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/ApiMsWinNetIsolation.cs b/src/Snap.Hutao/Snap.Hutao/Win32/ApiMsWinNetIsolation.cs index b28db068..29fffef3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/ApiMsWinNetIsolation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/ApiMsWinNetIsolation.cs @@ -1,6 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Win32.Foundation; using Snap.Hutao.Win32.NetworkManagement.WindowsFirewall; using Snap.Hutao.Win32.Security; using System.Diagnostics; @@ -19,13 +20,14 @@ internal static class ApiMsWinNetIsolation public static unsafe extern uint NetworkIsolationEnumAppContainers(uint Flags, uint* pdwNumPublicAppCs, INET_FIREWALL_APP_CONTAINER** ppPublicAppCs); [DebuggerStepThrough] - public static unsafe uint NetworkIsolationEnumAppContainers(NETISO_FLAG Flags, out uint dwNumPublicAppCs, out INET_FIREWALL_APP_CONTAINER* pPublicAppCs) + public static unsafe WIN32_ERROR NetworkIsolationEnumAppContainers(NETISO_FLAG Flags, out uint dwNumPublicAppCs, out INET_FIREWALL_APP_CONTAINER* pPublicAppCs) { fixed (uint* pdwNumPublicAppCs = &dwNumPublicAppCs) { fixed (INET_FIREWALL_APP_CONTAINER** ppPublicAppCs = &pPublicAppCs) { - return NetworkIsolationEnumAppContainers((uint)Flags, pdwNumPublicAppCs, ppPublicAppCs); + uint retVal = NetworkIsolationEnumAppContainers((uint)Flags, pdwNumPublicAppCs, ppPublicAppCs); + return *(WIN32_ERROR*)&retVal; } } } @@ -35,11 +37,12 @@ internal static class ApiMsWinNetIsolation public static unsafe extern uint NetworkIsolationFreeAppContainers(INET_FIREWALL_APP_CONTAINER* pPublicAppCs); [DebuggerStepThrough] - public static unsafe uint NetworkIsolationFreeAppContainers(ref readonly INET_FIREWALL_APP_CONTAINER publicAppCs) + public static unsafe WIN32_ERROR NetworkIsolationFreeAppContainers(ref readonly INET_FIREWALL_APP_CONTAINER publicAppCs) { fixed (INET_FIREWALL_APP_CONTAINER* pPublicAppCs = &publicAppCs) { - return NetworkIsolationFreeAppContainers(pPublicAppCs); + uint retVal = NetworkIsolationFreeAppContainers(pPublicAppCs); + return *(WIN32_ERROR*)&retVal; } } @@ -48,13 +51,14 @@ internal static class ApiMsWinNetIsolation public static unsafe extern uint NetworkIsolationGetAppContainerConfig(uint* pdwNumPublicAppCs, SID_AND_ATTRIBUTES** appContainerSids); [DebuggerStepThrough] - public static unsafe uint NetworkIsolationGetAppContainerConfig(out uint dwNumPublicAppCs, out SID_AND_ATTRIBUTES* appContainerSids) + public static unsafe WIN32_ERROR NetworkIsolationGetAppContainerConfig(out uint dwNumPublicAppCs, out SID_AND_ATTRIBUTES* appContainerSids) { fixed (uint* pdwNumPublicAppCs = &dwNumPublicAppCs) { fixed (SID_AND_ATTRIBUTES** pAppContainerSids = &appContainerSids) { - return NetworkIsolationGetAppContainerConfig(pdwNumPublicAppCs, pAppContainerSids); + uint retVal = NetworkIsolationGetAppContainerConfig(pdwNumPublicAppCs, pAppContainerSids); + return *(WIN32_ERROR*)&retVal; } } } @@ -64,11 +68,12 @@ internal static class ApiMsWinNetIsolation public static unsafe extern uint NetworkIsolationSetAppContainerConfig(uint dwNumPublicAppCs, SID_AND_ATTRIBUTES* appContainerSids); [DebuggerStepThrough] - public static unsafe uint NetworkIsolationSetAppContainerConfig(ReadOnlySpan appContainerSids) + public static unsafe WIN32_ERROR NetworkIsolationSetAppContainerConfig(ReadOnlySpan appContainerSids) { fixed (SID_AND_ATTRIBUTES* pAppContainerSids = appContainerSids) { - return NetworkIsolationSetAppContainerConfig((uint)appContainerSids.Length, pAppContainerSids); + uint retVal = NetworkIsolationSetAppContainerConfig((uint)appContainerSids.Length, pAppContainerSids); + return *(WIN32_ERROR*)&retVal; } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/FlexibleArray.cs b/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/FlexibleArray.cs index e4a4ba5e..97e163ec 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/FlexibleArray.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/FlexibleArray.cs @@ -3,8 +3,8 @@ namespace Snap.Hutao.Win32.Foundation; -internal unsafe struct FlexibleArray - where T : unmanaged +internal unsafe struct FlexibleArray + where TElement : unmanaged { - public T* Reference; + public TElement* Reference; } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/PSID.cs b/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/PSID.cs index 8978cf35..f039093d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/PSID.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/Foundation/PSID.cs @@ -1,9 +1,13 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Win32.Security; + namespace Snap.Hutao.Win32.Foundation; internal struct PSID { public unsafe void* Value; + + public static unsafe implicit operator PSID(SID* value) => *(PSID*)&value; } \ No newline at end of file