mirror of
https://github.com/2dust/v2rayN.git
synced 2026-03-12 19:33:01 +00:00
parent
a3ff31088e
commit
6b4ae5a386
6 changed files with 144 additions and 93 deletions
|
|
@ -5,6 +5,39 @@ public record CoreConfigContextBuilderResult(CoreConfigContext Context, NodeVali
|
||||||
public bool Success => ValidatorResult.Success;
|
public bool Success => ValidatorResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Holds the results of a full context build, including the main context and an optional
|
||||||
|
/// pre-socks context (e.g. for TUN protection or pre-socks chaining).
|
||||||
|
/// </summary>
|
||||||
|
public record CoreConfigContextBuilderAllResult(
|
||||||
|
CoreConfigContextBuilderResult MainResult,
|
||||||
|
CoreConfigContextBuilderResult? PreSocksResult)
|
||||||
|
{
|
||||||
|
/// <summary>True only when both the main result and (if present) the pre-socks result succeeded.</summary>
|
||||||
|
public bool Success => MainResult.Success && (PreSocksResult?.Success ?? true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Merges all errors and warnings from the main result and the optional pre-socks result
|
||||||
|
/// into a single <see cref="NodeValidatorResult"/> for unified notification.
|
||||||
|
/// </summary>
|
||||||
|
public NodeValidatorResult CombinedValidatorResult => new(
|
||||||
|
[.. MainResult.ValidatorResult.Errors, .. PreSocksResult?.ValidatorResult.Errors ?? []],
|
||||||
|
[.. MainResult.ValidatorResult.Warnings, .. PreSocksResult?.ValidatorResult.Warnings ?? []]);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The main context with TunProtectSsPort/ProxyRelaySsPort and ProtectDomainList merged in
|
||||||
|
/// from the pre-socks result (if any). Pass this to the core runner.
|
||||||
|
/// </summary>
|
||||||
|
public CoreConfigContext ResolvedMainContext => PreSocksResult is not null
|
||||||
|
? MainResult.Context with
|
||||||
|
{
|
||||||
|
TunProtectSsPort = PreSocksResult.Context.TunProtectSsPort,
|
||||||
|
ProxyRelaySsPort = PreSocksResult.Context.ProxyRelaySsPort,
|
||||||
|
ProtectDomainList = [.. MainResult.Context.ProtectDomainList ?? [], .. PreSocksResult.Context.ProtectDomainList ?? []],
|
||||||
|
}
|
||||||
|
: MainResult.Context;
|
||||||
|
}
|
||||||
|
|
||||||
public class CoreConfigContextBuilder
|
public class CoreConfigContextBuilder
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -75,6 +108,79 @@ public class CoreConfigContextBuilder
|
||||||
return new CoreConfigContextBuilderResult(context, validatorResult);
|
return new CoreConfigContextBuilderResult(context, validatorResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the main <see cref="CoreConfigContext"/> for <paramref name="node"/> and, when
|
||||||
|
/// the main build succeeds, also builds the optional pre-socks context required for TUN
|
||||||
|
/// protection or pre-socks proxy chaining.
|
||||||
|
/// </summary>
|
||||||
|
public static async Task<CoreConfigContextBuilderAllResult> BuildAll(Config config, ProfileItem node)
|
||||||
|
{
|
||||||
|
var mainResult = await Build(config, node);
|
||||||
|
if (!mainResult.Success)
|
||||||
|
{
|
||||||
|
return new CoreConfigContextBuilderAllResult(mainResult, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
var preResult = await BuildPreSocksIfNeeded(mainResult.Context);
|
||||||
|
return new CoreConfigContextBuilderAllResult(mainResult, preResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether a pre-socks context is required for <paramref name="nodeContext"/>
|
||||||
|
/// and, if so, builds and returns it. Returns <c>null</c> when no pre-socks core is needed.
|
||||||
|
/// </summary>
|
||||||
|
private static async Task<CoreConfigContextBuilderResult?> BuildPreSocksIfNeeded(CoreConfigContext nodeContext)
|
||||||
|
{
|
||||||
|
var config = nodeContext.AppConfig;
|
||||||
|
var node = nodeContext.Node;
|
||||||
|
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||||
|
|
||||||
|
var preSocksItem = ConfigHandler.GetPreSocksItem(config, node, coreType);
|
||||||
|
if (preSocksItem != null)
|
||||||
|
{
|
||||||
|
var preSocksResult = await Build(nodeContext.AppConfig, preSocksItem);
|
||||||
|
return preSocksResult with
|
||||||
|
{
|
||||||
|
Context = preSocksResult.Context with
|
||||||
|
{
|
||||||
|
ProtectDomainList = [.. nodeContext.ProtectDomainList ?? [], .. preSocksResult.Context.ProtectDomainList ?? []],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nodeContext.IsTunEnabled
|
||||||
|
|| coreType != ECoreType.Xray
|
||||||
|
|| node.ConfigType == EConfigType.Custom)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tunProtectSsPort = Utils.GetFreePort();
|
||||||
|
var proxyRelaySsPort = Utils.GetFreePort();
|
||||||
|
var preItem = new ProfileItem()
|
||||||
|
{
|
||||||
|
CoreType = ECoreType.sing_box,
|
||||||
|
ConfigType = EConfigType.Shadowsocks,
|
||||||
|
Address = Global.Loopback,
|
||||||
|
Port = proxyRelaySsPort,
|
||||||
|
Password = Global.None,
|
||||||
|
};
|
||||||
|
preItem.SetProtocolExtra(preItem.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
SsMethod = Global.None,
|
||||||
|
});
|
||||||
|
var preResult2 = await Build(nodeContext.AppConfig, preItem);
|
||||||
|
return preResult2 with
|
||||||
|
{
|
||||||
|
Context = preResult2.Context with
|
||||||
|
{
|
||||||
|
ProtectDomainList = [.. nodeContext.ProtectDomainList ?? [], .. preResult2.Context.ProtectDomainList ?? []],
|
||||||
|
TunProtectSsPort = tunProtectSsPort,
|
||||||
|
ProxyRelaySsPort = proxyRelaySsPort,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves a node into the context, optionally wrapping it in a subscription-level proxy chain.
|
/// Resolves a node into the context, optionally wrapping it in a subscription-level proxy chain.
|
||||||
/// Returns the effective (possibly replaced) node and the validation result.
|
/// Returns the effective (possibly replaced) node and the validation result.
|
||||||
|
|
|
||||||
|
|
@ -1321,47 +1321,6 @@ public static class ConfigHandler
|
||||||
return itemSocks;
|
return itemSocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CoreConfigContext? GetPreSocksCoreConfigContext(CoreConfigContext nodeContext)
|
|
||||||
{
|
|
||||||
var config = nodeContext.AppConfig;
|
|
||||||
var node = nodeContext.Node;
|
|
||||||
var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
|
||||||
|
|
||||||
var preSocksItem = GetPreSocksItem(config, node, coreType);
|
|
||||||
if (preSocksItem != null)
|
|
||||||
{
|
|
||||||
return nodeContext with { Node = preSocksItem, };
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!nodeContext.IsTunEnabled)
|
|
||||||
|| coreType != ECoreType.Xray
|
|
||||||
|| node.ConfigType == EConfigType.Custom)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var tunProtectSsPort = Utils.GetFreePort();
|
|
||||||
var proxyRelaySsPort = Utils.GetFreePort();
|
|
||||||
var preItem = new ProfileItem()
|
|
||||||
{
|
|
||||||
CoreType = ECoreType.sing_box,
|
|
||||||
ConfigType = EConfigType.Shadowsocks,
|
|
||||||
Address = Global.Loopback,
|
|
||||||
Port = proxyRelaySsPort,
|
|
||||||
Password = Global.None,
|
|
||||||
};
|
|
||||||
preItem.SetProtocolExtra(preItem.GetProtocolExtra() with
|
|
||||||
{
|
|
||||||
SsMethod = Global.None,
|
|
||||||
});
|
|
||||||
var preContext = nodeContext with
|
|
||||||
{
|
|
||||||
Node = preItem,
|
|
||||||
TunProtectSsPort = tunProtectSsPort,
|
|
||||||
ProxyRelaySsPort = proxyRelaySsPort,
|
|
||||||
};
|
|
||||||
return preContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove servers with invalid test results (timeout)
|
/// Remove servers with invalid test results (timeout)
|
||||||
/// Useful for cleaning up subscription lists
|
/// Useful for cleaning up subscription lists
|
||||||
|
|
|
||||||
|
|
@ -57,27 +57,19 @@ public class CoreManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadCore(CoreConfigContext? context)
|
/// <param name="mainContext">Resolved main context (with pre-socks ports already merged if applicable).</param>
|
||||||
|
/// <param name="preContext">Optional pre-socks context passed to <see cref="CoreStartPreService"/>.</param>
|
||||||
|
public async Task LoadCore(CoreConfigContext? mainContext, CoreConfigContext? preContext)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (mainContext == null)
|
||||||
{
|
{
|
||||||
await UpdateFunc(false, ResUI.CheckServerSettings);
|
await UpdateFunc(false, ResUI.CheckServerSettings);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var contextMod = context;
|
var node = mainContext.Node;
|
||||||
var node = contextMod.Node;
|
|
||||||
var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
|
var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
|
||||||
var preContext = ConfigHandler.GetPreSocksCoreConfigContext(contextMod);
|
var result = await CoreConfigHandler.GenerateClientConfig(mainContext, fileName);
|
||||||
if (preContext is not null)
|
|
||||||
{
|
|
||||||
contextMod = contextMod with
|
|
||||||
{
|
|
||||||
TunProtectSsPort = preContext.TunProtectSsPort,
|
|
||||||
ProxyRelaySsPort = preContext.ProxyRelaySsPort,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(contextMod, fileName);
|
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
await UpdateFunc(true, result.Msg);
|
await UpdateFunc(true, result.Msg);
|
||||||
|
|
@ -96,7 +88,7 @@ public class CoreManager
|
||||||
await WindowsUtils.RemoveTunDevice();
|
await WindowsUtils.RemoveTunDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
await CoreStart(contextMod);
|
await CoreStart(mainContext);
|
||||||
await CoreStartPreService(preContext);
|
await CoreStartPreService(preContext);
|
||||||
if (_processService != null)
|
if (_processService != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -38,4 +38,25 @@ public class NoticeManager
|
||||||
Enqueue(msg);
|
Enqueue(msg);
|
||||||
SendMessage(msg);
|
SendMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends each error and warning in <paramref name="validatorResult"/> to the message panel
|
||||||
|
/// and enqueues a summary snack notification (capped at 10 messages).
|
||||||
|
/// Returns <c>true</c> when there were any messages so the caller can decide on early-return
|
||||||
|
/// based on <see cref="NodeValidatorResult.Success"/>.
|
||||||
|
/// </summary>
|
||||||
|
public bool NotifyValidatorResult(NodeValidatorResult validatorResult)
|
||||||
|
{
|
||||||
|
var msgs = new List<string>([.. validatorResult.Errors, .. validatorResult.Warnings]);
|
||||||
|
if (msgs.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foreach (var msg in msgs)
|
||||||
|
{
|
||||||
|
SendMessage(msg);
|
||||||
|
}
|
||||||
|
Enqueue(Utils.List2String(msgs.Take(10).ToList(), true));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -546,24 +546,15 @@ public class MainWindowViewModel : MyReactiveObject
|
||||||
NoticeManager.Instance.Enqueue(ResUI.CheckServerSettings);
|
NoticeManager.Instance.Enqueue(ResUI.CheckServerSettings);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var (context, validatorResult) = await CoreConfigContextBuilder.Build(_config, profileItem);
|
var allResult = await CoreConfigContextBuilder.BuildAll(_config, profileItem);
|
||||||
var msgs = new List<string>([..validatorResult.Errors, ..validatorResult.Warnings]);
|
if (NoticeManager.Instance.NotifyValidatorResult(allResult.CombinedValidatorResult) && !allResult.Success)
|
||||||
if (msgs.Count > 0)
|
|
||||||
{
|
{
|
||||||
foreach (var msg in msgs)
|
return;
|
||||||
{
|
|
||||||
NoticeManager.Instance.SendMessage(msg);
|
|
||||||
}
|
|
||||||
NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true));
|
|
||||||
if (!validatorResult.Success)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Run(async () =>
|
await Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await LoadCore(context);
|
await LoadCore(allResult.ResolvedMainContext, allResult.PreSocksResult?.Context);
|
||||||
await SysProxyHandler.UpdateSysProxy(_config, false);
|
await SysProxyHandler.UpdateSysProxy(_config, false);
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
});
|
});
|
||||||
|
|
@ -604,9 +595,9 @@ public class MainWindowViewModel : MyReactiveObject
|
||||||
RxApp.MainThreadScheduler.Schedule(() => BlReloadEnabled = enabled);
|
RxApp.MainThreadScheduler.Schedule(() => BlReloadEnabled = enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadCore(CoreConfigContext? context)
|
private async Task LoadCore(CoreConfigContext? mainContext, CoreConfigContext? preContext)
|
||||||
{
|
{
|
||||||
await CoreManager.Instance.LoadCore(context);
|
await CoreManager.Instance.LoadCore(mainContext, preContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion core job
|
#endregion core job
|
||||||
|
|
|
||||||
|
|
@ -764,18 +764,9 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
}
|
}
|
||||||
|
|
||||||
var (context, validatorResult) = await CoreConfigContextBuilder.Build(_config, item);
|
var (context, validatorResult) = await CoreConfigContextBuilder.Build(_config, item);
|
||||||
var msgs = new List<string>([..validatorResult.Errors, ..validatorResult.Warnings]);
|
if (NoticeManager.Instance.NotifyValidatorResult(validatorResult) && !validatorResult.Success)
|
||||||
if (msgs.Count > 0)
|
|
||||||
{
|
{
|
||||||
foreach (var msg in msgs)
|
return;
|
||||||
{
|
|
||||||
NoticeManager.Instance.SendMessage(msg);
|
|
||||||
}
|
|
||||||
NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true));
|
|
||||||
if (!validatorResult.Success)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blClipboard)
|
if (blClipboard)
|
||||||
|
|
@ -804,18 +795,9 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var (context, validatorResult) = await CoreConfigContextBuilder.Build(_config, item);
|
var (context, validatorResult) = await CoreConfigContextBuilder.Build(_config, item);
|
||||||
var msgs = new List<string>([..validatorResult.Errors, ..validatorResult.Warnings]);
|
if (NoticeManager.Instance.NotifyValidatorResult(validatorResult) && !validatorResult.Success)
|
||||||
if (msgs.Count > 0)
|
|
||||||
{
|
{
|
||||||
foreach (var msg in msgs)
|
return;
|
||||||
{
|
|
||||||
NoticeManager.Instance.SendMessage(msg);
|
|
||||||
}
|
|
||||||
NoticeManager.Instance.Enqueue(Utils.List2String(msgs.Take(10).ToList(), true));
|
|
||||||
if (!validatorResult.Success)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(context, fileName);
|
var result = await CoreConfigHandler.GenerateClientConfig(context, fileName);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue