winApi安装dnscrypt-proxy服务

This commit is contained in:
老九 2021-11-06 19:58:03 +08:00
parent f997976fd2
commit c11f7af44d
7 changed files with 232 additions and 220 deletions

View File

@ -10,6 +10,7 @@ using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using static PInvoke.AdvApi32;
namespace FastGithub.DomainResolve
{
@ -18,10 +19,11 @@ namespace FastGithub.DomainResolve
/// </summary>
sealed class DnscryptProxy
{
private const string PATH = "dnscrypt-proxy";
private const string NAME = "dnscrypt-proxy";
private readonly ILogger<DnscryptProxy> logger;
private readonly string processName;
private readonly string serviceName;
private readonly string exeFilePath;
private readonly string tomlFilePath;
/// <summary>
/// 相关进程
@ -39,7 +41,14 @@ namespace FastGithub.DomainResolve
/// <param name="logger"></param>
public DnscryptProxy(ILogger<DnscryptProxy> logger)
{
const string PATH = "dnscrypt-proxy";
const string NAME = "dnscrypt-proxy";
this.logger = logger;
this.processName = NAME;
this.serviceName = $"{nameof(FastGithub)}.{NAME}";
this.exeFilePath = Path.Combine(PATH, OperatingSystem.IsWindows() ? $"{NAME}.exe" : NAME);
this.tomlFilePath = Path.Combine(PATH, $"{NAME}.toml");
}
/// <summary>
@ -55,7 +64,7 @@ namespace FastGithub.DomainResolve
}
catch (Exception ex)
{
this.logger.LogWarning($"{NAME}启动失败:{ex.Message}");
this.logger.LogWarning($"{this.processName}启动失败:{ex.Message}");
}
}
@ -66,30 +75,22 @@ namespace FastGithub.DomainResolve
/// <returns></returns>
private async Task StartCoreAsync(CancellationToken cancellationToken)
{
var tomlPath = Path.Combine(PATH, $"{NAME}.toml");
var port = GetAvailablePort(IPAddress.Loopback.AddressFamily);
var localEndPoint = new IPEndPoint(IPAddress.Loopback, port);
await TomlUtil.SetListensAsync(tomlPath, localEndPoint, cancellationToken);
await TomlUtil.SetlogLevelAsync(tomlPath, 6, cancellationToken);
await TomlUtil.SetEdnsClientSubnetAsync(tomlPath, cancellationToken);
foreach (var process in Process.GetProcessesByName(NAME))
{
process.Kill();
process.WaitForExit();
}
await TomlUtil.SetListensAsync(this.tomlFilePath, localEndPoint, cancellationToken);
await TomlUtil.SetlogLevelAsync(this.tomlFilePath, 6, cancellationToken);
await TomlUtil.SetEdnsClientSubnetAsync(this.tomlFilePath, cancellationToken);
if (OperatingSystem.IsWindows() && Process.GetCurrentProcess().SessionId == 0)
{
StartDnscryptProxy("-service uninstall")?.WaitForExit();
StartDnscryptProxy("-service install")?.WaitForExit();
StartDnscryptProxy("-service start")?.WaitForExit();
this.process = Process.GetProcessesByName(NAME).FirstOrDefault(item => item.SessionId == 0);
ServiceInstallUtil.StopAndDeleteService(this.serviceName);
ServiceInstallUtil.InstallAndStartService(this.serviceName, this.exeFilePath, ServiceStartType.SERVICE_DEMAND_START);
this.process = Process.GetProcessesByName(this.processName).FirstOrDefault(item => item.SessionId == 0);
}
else
{
this.process = StartDnscryptProxy(string.Empty);
this.process = StartDnscryptProxy();
}
if (this.process != null)
@ -109,9 +110,9 @@ namespace FastGithub.DomainResolve
{
if (OperatingSystem.IsWindows() && Process.GetCurrentProcess().SessionId == 0)
{
StartDnscryptProxy("-service stop")?.WaitForExit();
StartDnscryptProxy("-service uninstall")?.WaitForExit();
ServiceInstallUtil.StopAndDeleteService(this.serviceName);
}
if (this.process != null && this.process.HasExited == false)
{
this.process.Kill();
@ -119,7 +120,7 @@ namespace FastGithub.DomainResolve
}
catch (Exception ex)
{
this.logger.LogWarning($"{NAME}停止失败:{ex.Message }");
this.logger.LogWarning($"{this.processName}停止失败:{ex.Message }");
}
finally
{
@ -161,15 +162,13 @@ namespace FastGithub.DomainResolve
/// <summary>
/// 启动DnscryptProxy进程
/// </summary>
/// <param name="arguments"></param>
private static Process? StartDnscryptProxy(string arguments)
/// <returns></returns>
private Process? StartDnscryptProxy()
{
var fileName = OperatingSystem.IsWindows() ? $"{NAME}.exe" : NAME;
return Process.Start(new ProcessStartInfo
{
FileName = Path.Combine(PATH, fileName),
Arguments = arguments,
WorkingDirectory = PATH,
FileName = this.exeFilePath,
WorkingDirectory = Path.GetDirectoryName(this.exeFilePath),
UseShellExecute = false,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
@ -182,7 +181,7 @@ namespace FastGithub.DomainResolve
/// <returns></returns>
public override string ToString()
{
return NAME;
return this.processName;
}
}
}

View File

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="PInvoke.AdvApi32" Version="0.7.104" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
<PackageReference Include="DNS" Version="6.1.0" />

View File

@ -0,0 +1,88 @@
using System.IO;
using System.Runtime.Versioning;
using static PInvoke.AdvApi32;
namespace FastGithub.DomainResolve
{
public static class ServiceInstallUtil
{
/// <summary>
/// 安装并启动服务
/// </summary>
/// <param name="serviceName"></param>
/// <param name="binaryPath"></param>
/// <param name="startType"></param>
/// <returns></returns>
[SupportedOSPlatform("windows")]
public static bool InstallAndStartService(string serviceName, string binaryPath, ServiceStartType startType = ServiceStartType.SERVICE_AUTO_START)
{
using var hSCManager = OpenSCManager(null, null, ServiceManagerAccess.SC_MANAGER_ALL_ACCESS);
if (hSCManager.IsInvalid == true)
{
return false;
}
var hService = OpenService(hSCManager, serviceName, ServiceAccess.SERVICE_ALL_ACCESS);
if (hService.IsInvalid == true)
{
hService = CreateService(
hSCManager,
serviceName,
serviceName,
ServiceAccess.SERVICE_ALL_ACCESS,
ServiceType.SERVICE_WIN32_OWN_PROCESS,
startType,
ServiceErrorControl.SERVICE_ERROR_NORMAL,
Path.GetFullPath(binaryPath),
lpLoadOrderGroup: null,
lpdwTagId: 0,
lpDependencies: null,
lpServiceStartName: null,
lpPassword: null);
}
if (hService.IsInvalid == true)
{
return false;
}
using (hService)
{
return StartService(hService, 0, null);
}
}
/// <summary>
/// 停止并删除服务
/// </summary>
/// <param name="serviceName"></param>
/// <returns></returns>
[SupportedOSPlatform("windows")]
public static bool StopAndDeleteService(string serviceName)
{
using var hSCManager = OpenSCManager(null, null, ServiceManagerAccess.SC_MANAGER_ALL_ACCESS);
if (hSCManager.IsInvalid == true)
{
return false;
}
using var hService = OpenService(hSCManager, serviceName, ServiceAccess.SERVICE_ALL_ACCESS);
if (hService.IsInvalid == true)
{
return true;
}
var status = new SERVICE_STATUS();
if (QueryServiceStatus(hService, ref status) == true)
{
if (status.dwCurrentState != ServiceState.SERVICE_STOP_PENDING &&
status.dwCurrentState != ServiceState.SERVICE_STOPPED)
{
ControlService(hService, ServiceControl.SERVICE_CONTROL_STOP, ref status);
}
}
return DeleteService(hService);
}
}
}

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31808.319
# Visual Studio Version 16
VisualStudioVersion = 16.0.31320.298
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub", "FastGithub\FastGithub.csproj", "{C1099390-6103-4917-A740-A3002B542FE0}"
EndProject
@ -15,7 +15,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.HttpServer", "Fa
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.PacketIntercept", "FastGithub.PacketIntercept\FastGithub.PacketIntercept.csproj", "{701FF90C-E651-4E0B-AE7F-84D1F17DD178}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastGithub.UI", "FastGithub.UI\FastGithub.UI.csproj", "{5082061F-38D5-4F50-945E-791C85B9BDB5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.UI", "FastGithub.UI\FastGithub.UI.csproj", "{5082061F-38D5-4F50-945E-791C85B9BDB5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastGithub.FlowAnalyze", "FastGithub.FlowAnalyze\FastGithub.FlowAnalyze.csproj", "{93478EAF-739C-47DA-B8FE-AEBA78A75E11}"
EndProject

View File

@ -9,7 +9,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="PInvoke.AdvApi32" Version="0.7.104" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.Network" Version="2.0.2.68" />
<ProjectReference Include="..\FastGithub.DomainResolve\FastGithub.DomainResolve.csproj" />

View File

@ -1,185 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using PInvoke;
using System;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using System.Threading;
using static PInvoke.AdvApi32;
namespace FastGithub
{
/// <summary>
/// IHostBuilder扩展
/// </summary>
static class WindowServiceExtensions
{
/// <summary>
/// 控制命令
/// </summary>
private enum Command
{
Start,
Stop,
}
/// <summary>
/// 使用windows服务
/// </summary>
/// <param name="hostBuilder"></param>
/// <returns></returns>
public static IHostBuilder UseWindowsService(this IHostBuilder hostBuilder)
{
var contentRoot = Path.GetDirectoryName(Environment.GetCommandLineArgs().First());
if (contentRoot != null)
{
Environment.CurrentDirectory = contentRoot;
hostBuilder.UseContentRoot(contentRoot);
}
return WindowsServiceLifetimeHostBuilderExtensions.UseWindowsService(hostBuilder);
}
/// <summary>
/// 运行主机
/// </summary>
/// <param name="host"></param>
/// <param name="singleton"></param>
public static void Run(this IHost host, bool singleton = true)
{
if (OperatingSystem.IsWindows() && TryGetCommand(out var cmd))
{
try
{
UseCommand(cmd);
}
catch (Exception ex)
{
var loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();
loggerFactory.CreateLogger(nameof(FastGithub)).LogError(ex.Message);
}
}
else
{
using var mutex = new Mutex(true, "Global\\FastGithub", out var firstInstance);
if (singleton == false || firstInstance)
{
HostingAbstractionsHostExtensions.Run(host);
}
}
}
/// <summary>
/// 获取控制指令
/// </summary>
/// <param name="cmd"></param>
/// <returns></returns>
private static bool TryGetCommand(out Command cmd)
{
var args = Environment.GetCommandLineArgs();
return Enum.TryParse(args.Skip(1).FirstOrDefault(), true, out cmd);
}
/// <summary>
/// 应用控制指令
/// </summary>
/// <param name="cmd"></param>
[SupportedOSPlatform("windows")]
private static void UseCommand(Command cmd)
{
var binaryPath = Environment.GetCommandLineArgs().First();
var serviceName = Path.GetFileNameWithoutExtension(binaryPath);
if (cmd == Command.Start)
{
InstallAndStartService(serviceName, binaryPath);
}
else if (cmd == Command.Stop)
{
StopAndDeleteService(serviceName);
}
}
/// <summary>
/// 安装并启动服务
/// </summary>
/// <param name="serviceName"></param>
/// <param name="binaryPath"></param>
/// <exception cref = "Win32Exception" ></ exception >
[SupportedOSPlatform("windows")]
private static void InstallAndStartService(string serviceName, string binaryPath)
{
using var hSCManager = OpenSCManager(null, null, ServiceManagerAccess.SC_MANAGER_ALL_ACCESS);
if (hSCManager.IsInvalid == true)
{
throw new Win32Exception();
}
var hService = OpenService(hSCManager, serviceName, ServiceAccess.SERVICE_ALL_ACCESS);
if (hService.IsInvalid == true)
{
hService = CreateService(
hSCManager,
serviceName,
serviceName,
ServiceAccess.SERVICE_ALL_ACCESS,
ServiceType.SERVICE_WIN32_OWN_PROCESS,
ServiceStartType.SERVICE_AUTO_START,
ServiceErrorControl.SERVICE_ERROR_NORMAL,
binaryPath,
lpLoadOrderGroup: null,
lpdwTagId: 0,
lpDependencies: null,
lpServiceStartName: null,
lpPassword: null);
}
if (hService.IsInvalid == true)
{
throw new Win32Exception();
}
using (hService)
{
StartService(hService, 0, null);
}
}
/// <summary>
/// 停止并删除服务
/// </summary>
/// <param name="serviceName"></param>
/// <exception cref = "Win32Exception" ></ exception >
[SupportedOSPlatform("windows")]
private static void StopAndDeleteService(string serviceName)
{
using var hSCManager = OpenSCManager(null, null, ServiceManagerAccess.SC_MANAGER_ALL_ACCESS);
if (hSCManager.IsInvalid == true)
{
throw new Win32Exception();
}
using var hService = OpenService(hSCManager, serviceName, ServiceAccess.SERVICE_ALL_ACCESS);
if (hService.IsInvalid == true)
{
return;
}
var status = new SERVICE_STATUS();
if (QueryServiceStatus(hService, ref status) == true)
{
if (status.dwCurrentState != ServiceState.SERVICE_STOP_PENDING &&
status.dwCurrentState != ServiceState.SERVICE_STOPPED)
{
ControlService(hService, ServiceControl.SERVICE_CONTROL_STOP, ref status);
}
}
if (DeleteService(hService) == false)
{
throw new Win32Exception();
}
}
}
}

View File

@ -0,0 +1,110 @@
using FastGithub.DomainResolve;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using PInvoke;
using System;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using System.Threading;
namespace FastGithub
{
/// <summary>
/// IHostBuilder扩展
/// </summary>
static class WindowsServiceExtensions
{
/// <summary>
/// 控制命令
/// </summary>
private enum Command
{
Start,
Stop,
}
/// <summary>
/// 使用windows服务
/// </summary>
/// <param name="hostBuilder"></param>
/// <returns></returns>
public static IHostBuilder UseWindowsService(this IHostBuilder hostBuilder)
{
var contentRoot = Path.GetDirectoryName(Environment.GetCommandLineArgs().First());
if (contentRoot != null)
{
Environment.CurrentDirectory = contentRoot;
hostBuilder.UseContentRoot(contentRoot);
}
return WindowsServiceLifetimeHostBuilderExtensions.UseWindowsService(hostBuilder);
}
/// <summary>
/// 运行主机
/// </summary>
/// <param name="host"></param>
/// <param name="singleton"></param>
public static void Run(this IHost host, bool singleton = true)
{
if (OperatingSystem.IsWindows() && TryGetCommand(out var cmd))
{
try
{
UseCommand(cmd);
}
catch (Exception ex)
{
var loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();
loggerFactory.CreateLogger(nameof(FastGithub)).LogError(ex.Message);
}
}
else
{
using var mutex = new Mutex(true, "Global\\FastGithub", out var firstInstance);
if (singleton == false || firstInstance)
{
HostingAbstractionsHostExtensions.Run(host);
}
}
}
/// <summary>
/// 获取控制指令
/// </summary>
/// <param name="cmd"></param>
/// <returns></returns>
private static bool TryGetCommand(out Command cmd)
{
var args = Environment.GetCommandLineArgs();
return Enum.TryParse(args.Skip(1).FirstOrDefault(), true, out cmd);
}
/// <summary>
/// 应用控制指令
/// </summary>
/// <param name="cmd"></param>
[SupportedOSPlatform("windows")]
private static void UseCommand(Command cmd)
{
var binaryPath = Environment.GetCommandLineArgs().First();
var serviceName = Path.GetFileNameWithoutExtension(binaryPath);
var state = true;
if (cmd == Command.Start)
{
state = ServiceInstallUtil.InstallAndStartService(serviceName, binaryPath);
}
else if (cmd == Command.Stop)
{
state = ServiceInstallUtil.StopAndDeleteService(serviceName);
}
if (state == false)
{
throw new Win32Exception();
}
}
}
}