From e9ed04e8ba4f3bee171938a435d70b2f2302f6a8 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 4 Jan 2021 16:26:47 +0100 Subject: embeddable-dll-service: add more robust example for .NET 5 Signed-off-by: Jason A. Donenfeld --- .../csharp/DemoUI/MainWindow.Designer.cs | 87 +++++++++ embeddable-dll-service/csharp/DemoUI/MainWindow.cs | 213 +++++++++++++++++++++ .../csharp/DemoUI/MainWindow.resx | 60 ++++++ embeddable-dll-service/csharp/DemoUI/Program.cs | 44 +++++ embeddable-dll-service/csharp/DemoUI/app.manifest | 48 +++++ embeddable-dll-service/csharp/Keypair.cs | 33 ---- embeddable-dll-service/csharp/Program.cs | 92 --------- embeddable-dll-service/csharp/README.md | 15 ++ embeddable-dll-service/csharp/Ringlogger.cs | 205 -------------------- embeddable-dll-service/csharp/Service.cs | 119 ------------ embeddable-dll-service/csharp/TunnelDll/Keypair.cs | 33 ++++ .../csharp/TunnelDll/Ringlogger.cs | 205 ++++++++++++++++++++ embeddable-dll-service/csharp/TunnelDll/Service.cs | 117 +++++++++++ embeddable-dll-service/csharp/TunnelDll/Win32.cs | 175 +++++++++++++++++ embeddable-dll-service/csharp/Win32.cs | 175 ----------------- embeddable-dll-service/csharp/demo-client.csproj | 14 ++ .../csharp/demo-client.csproj.user | 8 + embeddable-dll-service/csharp/demo-client.sln | 25 +++ 18 files changed, 1044 insertions(+), 624 deletions(-) create mode 100644 embeddable-dll-service/csharp/DemoUI/MainWindow.Designer.cs create mode 100644 embeddable-dll-service/csharp/DemoUI/MainWindow.cs create mode 100644 embeddable-dll-service/csharp/DemoUI/MainWindow.resx create mode 100644 embeddable-dll-service/csharp/DemoUI/Program.cs create mode 100644 embeddable-dll-service/csharp/DemoUI/app.manifest delete mode 100644 embeddable-dll-service/csharp/Keypair.cs delete mode 100644 embeddable-dll-service/csharp/Program.cs create mode 100644 embeddable-dll-service/csharp/README.md delete mode 100644 embeddable-dll-service/csharp/Ringlogger.cs delete mode 100644 embeddable-dll-service/csharp/Service.cs create mode 100644 embeddable-dll-service/csharp/TunnelDll/Keypair.cs create mode 100644 embeddable-dll-service/csharp/TunnelDll/Ringlogger.cs create mode 100644 embeddable-dll-service/csharp/TunnelDll/Service.cs create mode 100644 embeddable-dll-service/csharp/TunnelDll/Win32.cs delete mode 100644 embeddable-dll-service/csharp/Win32.cs create mode 100644 embeddable-dll-service/csharp/demo-client.csproj create mode 100644 embeddable-dll-service/csharp/demo-client.csproj.user create mode 100644 embeddable-dll-service/csharp/demo-client.sln (limited to 'embeddable-dll-service') diff --git a/embeddable-dll-service/csharp/DemoUI/MainWindow.Designer.cs b/embeddable-dll-service/csharp/DemoUI/MainWindow.Designer.cs new file mode 100644 index 00000000..5fae35d3 --- /dev/null +++ b/embeddable-dll-service/csharp/DemoUI/MainWindow.Designer.cs @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. + */ + +namespace DemoUI +{ + partial class MainWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.connectButton = new System.Windows.Forms.Button(); + this.logBox = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // connectButton + // + this.connectButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.connectButton.Location = new System.Drawing.Point(12, 12); + this.connectButton.Name = "connectButton"; + this.connectButton.Size = new System.Drawing.Size(1137, 46); + this.connectButton.TabIndex = 5; + this.connectButton.Text = "&Connect"; + this.connectButton.UseVisualStyleBackColor = true; + this.connectButton.Click += new System.EventHandler(this.connectButton_Click); + // + // logBox + // + this.logBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.logBox.Location = new System.Drawing.Point(12, 64); + this.logBox.Multiline = true; + this.logBox.Name = "logBox"; + this.logBox.ReadOnly = true; + this.logBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.logBox.Size = new System.Drawing.Size(1137, 561); + this.logBox.TabIndex = 4; + this.logBox.TabStop = false; + // + // MainWindow + // + this.AutoScaleDimensions = new System.Drawing.SizeF(13F, 32F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1161, 637); + this.Controls.Add(this.logBox); + this.Controls.Add(this.connectButton); + this.Name = "MainWindow"; + this.Text = "WireGuard Demo Client"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainWindow_FormClosing); + this.Load += new System.EventHandler(this.MainWindow_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.Button connectButton; + private System.Windows.Forms.TextBox logBox; + } +} + diff --git a/embeddable-dll-service/csharp/DemoUI/MainWindow.cs b/embeddable-dll-service/csharp/DemoUI/MainWindow.cs new file mode 100644 index 00000000..13499fcb --- /dev/null +++ b/embeddable-dll-service/csharp/DemoUI/MainWindow.cs @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. + */ + +using System; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using System.Windows.Forms; +using System.Threading; +using System.IO.Pipes; +using System.Diagnostics; +using System.Net.Sockets; + +namespace DemoUI +{ + public partial class MainWindow : Form + { + private static readonly string userDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); + private static readonly string configFile = Path.Combine(userDirectory, "demobox.conf"); + private static readonly string logFile = Path.Combine(userDirectory, "log.bin"); + + private Tunnel.Ringlogger log; + private Thread logPrintingThread, transferUpdateThread; + private volatile bool threadsRunning; + private bool connected; + + public MainWindow() + { + InitializeComponent(); + Application.ApplicationExit += Application_ApplicationExit; + + try { File.Delete(logFile); } catch { } + log = new Tunnel.Ringlogger(logFile, "GUI"); + logPrintingThread = new Thread(new ThreadStart(tailLog)); + transferUpdateThread = new Thread(new ThreadStart(tailTransfer)); + } + + private void tailLog() + { + var cursor = Tunnel.Ringlogger.CursorAll; + while (threadsRunning) + { + var lines = log.FollowFromCursor(ref cursor); + foreach (var line in lines) + logBox.Invoke(new Action(logBox.AppendText), new object[] { line + "\r\n" }); + try + { + Thread.Sleep(300); + } + catch + { + break; + } + } + } + + private void tailTransfer() + { + NamedPipeClientStream stream = null; + try + { + while (threadsRunning) + { + while (threadsRunning) + { + try + { + stream = Tunnel.Service.GetPipe(configFile); + stream.Connect(); + break; + } + catch { } + Thread.Sleep(1000); + } + + var reader = new StreamReader(stream); + stream.Write(Encoding.UTF8.GetBytes("get=1\n\n")); + ulong rx = 0, tx = 0; + while (threadsRunning) + { + var line = reader.ReadLine(); + if (line == null) + break; + line = line.Trim(); + if (line.Length == 0) + break; + if (line.StartsWith("rx_bytes=")) + rx += ulong.Parse(line.Substring(9)); + else if (line.StartsWith("tx_bytes=")) + tx += ulong.Parse(line.Substring(9)); + } + Invoke(new Action(updateTransferTitle), new object[] { rx, tx }); + stream.Close(); + Thread.Sleep(1000); + } + } + catch { } + finally + { + if (stream != null && stream.IsConnected) + stream.Close(); + } + } + + private void Application_ApplicationExit(object sender, EventArgs e) + { + Tunnel.Service.Remove(configFile, true); + try { File.Delete(logFile); } catch { } + try { File.Delete(configFile); } catch { } + } + + private void MainWindow_Load(object sender, EventArgs e) + { + threadsRunning = true; + logPrintingThread.Start(); + transferUpdateThread.Start(); + } + + private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) + { + threadsRunning = false; + logPrintingThread.Interrupt(); + transferUpdateThread.Interrupt(); + try { logPrintingThread.Join(); } catch { } + try { transferUpdateThread.Join(); } catch { } + } + + private static string formatBytes(ulong bytes) + { + decimal d = bytes; + string selectedUnit = null; + foreach (string unit in new string[] { "B", "KiB", "MiB", "GiB", "TiB" }) + { + selectedUnit = unit; + if (d < 1024) + break; + d /= 1024; + } + return string.Format("{0:0.##} {1}", d, selectedUnit); + } + + private void updateTransferTitle(ulong rx, ulong tx) + { + var titleBase = Text; + var idx = titleBase.IndexOf(" - "); + if (idx != -1) + titleBase = titleBase.Substring(0, idx); + if (rx == 0 && tx == 0) + Text = titleBase; + else + Text = string.Format("{0} - rx: {1}, tx: {2}", titleBase, formatBytes(rx), formatBytes(tx)); + } + + private async Task generateNewConfig() + { + log.Write("Generating keys"); + var keys = Tunnel.Keypair.Generate(); + log.Write("Exchanging keys with demo server"); + var client = new TcpClient(); + await client.ConnectAsync("demo.wireguard.com", 42912); + var stream = client.GetStream(); + var reader = new StreamReader(stream, Encoding.UTF8); + var pubKeyBytes = Encoding.UTF8.GetBytes(keys.Public + "\n"); + await stream.WriteAsync(pubKeyBytes, 0, pubKeyBytes.Length); + await stream.FlushAsync(); + var ret = (await reader.ReadLineAsync()).Split(':'); + client.Close(); + var status = ret.Length >= 1 ? ret[0] : ""; + var serverPubkey = ret.Length >= 2 ? ret[1] : ""; + var serverPort = ret.Length >= 3 ? ret[2] : ""; + var internalIP = ret.Length >= 4 ? ret[3] : ""; + if (status != "OK") + throw new InvalidOperationException(string.Format("Server status is {0}", status)); + return string.Format("[Interface]\nPrivateKey = {0}\nAddress = {1}/24\nDNS = 8.8.8.8, 8.8.4.4\n\n[Peer]\nPublicKey = {2}\nEndpoint = demo.wireguard.com:{3}\nAllowedIPs = 0.0.0.0/0\n", keys.Private, internalIP, serverPubkey, serverPort); + } + + private async void connectButton_Click(object sender, EventArgs e) + { + if (connected) + { + connectButton.Enabled = false; + await Task.Run(() => + { + Tunnel.Service.Remove(configFile, true); + try { File.Delete(configFile); } catch { } + }); + updateTransferTitle(0, 0); + connectButton.Text = "&Connect"; + connectButton.Enabled = true; + connected = false; + return; + } + + connectButton.Enabled = false; + try + { + var config = await generateNewConfig(); + await File.WriteAllBytesAsync(configFile, Encoding.UTF8.GetBytes(config)); + await Task.Run(() => Tunnel.Service.Add(configFile, true)); + connected = true; + connectButton.Text = "&Disconnect"; + } + catch (Exception ex) + { + log.Write(ex.Message); + try { File.Delete(configFile); } catch { } + } + connectButton.Enabled = true; + } + } +} diff --git a/embeddable-dll-service/csharp/DemoUI/MainWindow.resx b/embeddable-dll-service/csharp/DemoUI/MainWindow.resx new file mode 100644 index 00000000..f298a7be --- /dev/null +++ b/embeddable-dll-service/csharp/DemoUI/MainWindow.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/embeddable-dll-service/csharp/DemoUI/Program.cs b/embeddable-dll-service/csharp/DemoUI/Program.cs new file mode 100644 index 00000000..8c420973 --- /dev/null +++ b/embeddable-dll-service/csharp/DemoUI/Program.cs @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. + */ + +using System; +using System.Threading; +using System.Diagnostics; +using System.Windows.Forms; + +namespace DemoUI +{ + static class Program + { + [STAThread] + static void Main(string[] args) + { + if (args.Length == 3 && args[0] == "/service") + { + var t = new Thread(() => + { + try + { + var currentProcess = Process.GetCurrentProcess(); + var uiProcess = Process.GetProcessById(int.Parse(args[2])); + if (uiProcess.MainModule.FileName != currentProcess.MainModule.FileName) + return; + uiProcess.WaitForExit(); + Tunnel.Service.Remove(args[1], false); + } + catch { } + }); + t.Start(); + Tunnel.Service.Run(args[1]); + t.Interrupt(); + return; + } + Application.SetHighDpiMode(HighDpiMode.SystemAware); + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainWindow()); + } + } +} diff --git a/embeddable-dll-service/csharp/DemoUI/app.manifest b/embeddable-dll-service/csharp/DemoUI/app.manifest new file mode 100644 index 00000000..616ab5b9 --- /dev/null +++ b/embeddable-dll-service/csharp/DemoUI/app.manifest @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + diff --git a/embeddable-dll-service/csharp/Keypair.cs b/embeddable-dll-service/csharp/Keypair.cs deleted file mode 100644 index 17a0a876..00000000 --- a/embeddable-dll-service/csharp/Keypair.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved. - */ - -using System; -using System.Runtime.InteropServices; - -namespace Tunnel -{ - public class Keypair - { - public readonly string Public; - public readonly string Private; - - private Keypair(string pub, string priv) - { - Public = pub; - Private = priv; - } - - [DllImport("tunnel.dll", EntryPoint = "WireGuardGenerateKeypair", CallingConvention = CallingConvention.Cdecl)] - private static extern bool WireGuardGenerateKeypair(byte[] publicKey, byte[] privateKey); - - public static Keypair Generate() - { - var publicKey = new byte[32]; - var privateKey = new byte[32]; - WireGuardGenerateKeypair(publicKey, privateKey); - return new Keypair(Convert.ToBase64String(publicKey), Convert.ToBase64String(privateKey)); - } - } -} diff --git a/embeddable-dll-service/csharp/Program.cs b/embeddable-dll-service/csharp/Program.cs deleted file mode 100644 index dfefc3ec..00000000 --- a/embeddable-dll-service/csharp/Program.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved. - */ - -using System; -using System.Net.Sockets; -using System.IO; -using System.Text; -using System.Diagnostics; -using System.Threading; -using System.Runtime.InteropServices; - -namespace Tunnel -{ - class Program - { - - [DllImport("kernel32.dll")] - private static extern bool SetConsoleCtrlHandler(SetConsoleCtrlEventHandler handler, bool add); - private delegate bool SetConsoleCtrlEventHandler(UInt32 signal); - - public static void Main(string[] args) - { - if (args.Length == 2 && args[0] == "/service") - { - Service.Run(args[1]); - return; - } - - var baseDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); - var configFile = Path.Combine(baseDirectory, "demobox.conf"); - var logFile = Path.Combine(baseDirectory, "log.bin"); - - try { File.Delete(logFile); } catch { } - Ringlogger log = new Ringlogger(logFile, "GUI"); - - var logPrintingThread = new Thread(() => - { - var cursor = Ringlogger.CursorAll; - while (Thread.CurrentThread.IsAlive) - { - var lines = log.FollowFromCursor(ref cursor); - foreach (var line in lines) - Console.WriteLine(line); - Thread.Sleep(300); - } - }); - logPrintingThread.Start(); - - log.Write("Generating keys"); - var keys = Keypair.Generate(); - log.Write("Exchanging keys with demo server"); - var client = new TcpClient("demo.wireguard.com", 42912); - var stream = client.GetStream(); - var reader = new StreamReader(stream, Encoding.UTF8); - var pubKeyBytes = Encoding.UTF8.GetBytes(keys.Public + "\n"); - stream.Write(pubKeyBytes, 0, pubKeyBytes.Length); - stream.Flush(); - var ret = reader.ReadLine().Split(':'); - client.Close(); - var status = ret.Length >= 1 ? ret[0] : ""; - var serverPubkey = ret.Length >= 2 ? ret[1] : ""; - var serverPort = ret.Length >= 3 ? ret[2] : ""; - var internalIP = ret.Length >= 4 ? ret[3] : ""; - - if (status != "OK") - throw new InvalidOperationException(String.Format("Server status is {0}", status)); - - SetConsoleCtrlHandler(delegate - { - Service.Remove(configFile); - Environment.Exit(0); - return true; - }, true); - - log.Write("Writing config file to disk"); - var configFileContents = String.Format("[Interface]\nPrivateKey = {0}\nAddress = {1}/24\nDNS = 8.8.8.8, 8.8.4.4\n\n[Peer]\nPublicKey = {2}\nEndpoint = demo.wireguard.com:{3}\nAllowedIPs = 0.0.0.0/0\n", keys.Private, internalIP, serverPubkey, serverPort); - File.WriteAllText(configFile, configFileContents); - - try - { - Service.Add(configFile); - logPrintingThread.Join(); - } - finally - { - Service.Remove(configFile); - } - } - } -} diff --git a/embeddable-dll-service/csharp/README.md b/embeddable-dll-service/csharp/README.md new file mode 100644 index 00000000..071d15f6 --- /dev/null +++ b/embeddable-dll-service/csharp/README.md @@ -0,0 +1,15 @@ +# Example WireGuard Demo Client for Windows + +This is a simple client for demo.wireguard.com, which brings up WireGuard tunnels using the [embeddable-dll-service](https://git.zx2c4.com/wireguard-windows/about/embeddable-dll-service/README.md). + +## Building + +The code in this repository can be built in Visual Studio 2019 by opening the .sln and pressing build. However, it requires `tunnel.dll` to be present in the run directory. That can be built by: + +```batch +> git clone https://git.zx2c4.com/wireguard-windows +> cd wireguard-windows\embeddable-dll-service +> .\build.bat +``` + +In addition, `tunnel.dll` requires `wintun.dll`, which can be downloaded from [wintun.net](https://www.wintun.net). diff --git a/embeddable-dll-service/csharp/Ringlogger.cs b/embeddable-dll-service/csharp/Ringlogger.cs deleted file mode 100644 index 801eaa59..00000000 --- a/embeddable-dll-service/csharp/Ringlogger.cs +++ /dev/null @@ -1,205 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved. - */ - -using System; -using System.IO; -using System.IO.MemoryMappedFiles; -using System.Text; -using System.Collections.Generic; -using System.Threading; -using System.Runtime.CompilerServices; - -namespace Tunnel -{ - public class Ringlogger - { - private struct UnixTimestamp - { - private Int64 _ns; - public UnixTimestamp(Int64 ns) => _ns = ns; - public bool IsEmpty => _ns == 0; - public static UnixTimestamp Empty => new UnixTimestamp(0); - public static UnixTimestamp Now - { - get - { - var now = DateTimeOffset.UtcNow; - var ns = (now.Subtract(DateTimeOffset.FromUnixTimeSeconds(0)).Ticks * 100) % 1000000000; - return new UnixTimestamp(now.ToUnixTimeSeconds() * 1000000000 + ns); - } - } - public Int64 Nanoseconds => _ns; - public override string ToString() - { - return DateTimeOffset.FromUnixTimeSeconds(_ns / 1000000000).LocalDateTime.ToString("yyyy'-'MM'-'dd HH':'mm':'ss'.'") + ((_ns % 1000000000).ToString() + "00000").Substring(0, 6); - } - } - private struct Line - { - private const int maxLineLength = 512; - private const int offsetTimeNs = 0; - private const int offsetLine = 8; - - private readonly MemoryMappedViewAccessor _view; - private readonly int _start; - public Line(MemoryMappedViewAccessor view, UInt32 index) => (_view, _start) = (view, (int)(Log.HeaderBytes + index * Bytes)); - - public static int Bytes => maxLineLength + offsetLine; - - public UnixTimestamp Timestamp - { - get => new UnixTimestamp(_view.ReadInt64(_start + offsetTimeNs)); - set => _view.Write(_start + offsetTimeNs, value.Nanoseconds); - } - - public string Text - { - get - { - var textBytes = new byte[maxLineLength]; - _view.ReadArray(_start + offsetLine, textBytes, 0, textBytes.Length); - var nullByte = Array.IndexOf(textBytes, 0); - if (nullByte <= 0) - return null; - return Encoding.UTF8.GetString(textBytes, 0, nullByte); - } - set - { - if (value == null) - { - _view.WriteArray(_start + offsetLine, new byte[maxLineLength], 0, maxLineLength); - return; - } - var textBytes = Encoding.UTF8.GetBytes(value); - var bytesToWrite = Math.Min(maxLineLength - 1, textBytes.Length); - _view.Write(_start + offsetLine + bytesToWrite, (byte)0); - _view.WriteArray(_start + offsetLine, textBytes, 0, bytesToWrite); - } - } - - public override string ToString() - { - var time = Timestamp; - if (time.IsEmpty) - return null; - var text = Text; - if (text == null) - return null; - return string.Format("{0}: {1}", time, text); - } - } - private struct Log - { - private const UInt32 maxLines = 2048; - private const UInt32 magic = 0xbadbabe; - private const int offsetMagic = 0; - private const int offsetNextIndex = 4; - private const int offsetLines = 8; - - private readonly MemoryMappedViewAccessor _view; - public Log(MemoryMappedViewAccessor view) => _view = view; - - public static int HeaderBytes => offsetLines; - public static int Bytes => (int)(HeaderBytes + Line.Bytes * maxLines); - - public UInt32 ExpectedMagic => magic; - public UInt32 Magic - { - get => _view.ReadUInt32(offsetMagic); - set => _view.Write(offsetMagic, value); - } - - public UInt32 NextIndex - { - get => _view.ReadUInt32(offsetNextIndex); - set => _view.Write(offsetNextIndex, value); - } - public unsafe UInt32 InsertNextIndex() - { - byte* pointer = null; - _view.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer); - var ret = (UInt32)Interlocked.Increment(ref Unsafe.AsRef(pointer + offsetNextIndex)); - _view.SafeMemoryMappedViewHandle.ReleasePointer(); - return ret; - } - - public UInt32 LineCount => maxLines; - public Line this[UInt32 i] => new Line(_view, i % maxLines); - - public void Clear() => _view.WriteArray(0, new byte[Bytes], 0, Bytes); - } - - private readonly Log _log; - private readonly string _tag; - - public Ringlogger(string filename, string tag) - { - var file = File.Open(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete); - file.SetLength(Log.Bytes); - var mmap = MemoryMappedFile.CreateFromFile(file, null, 0, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, false); - var view = mmap.CreateViewAccessor(0, Log.Bytes, MemoryMappedFileAccess.ReadWrite); - _log = new Log(view); - if (_log.Magic != _log.ExpectedMagic) - { - _log.Clear(); - _log.Magic = _log.ExpectedMagic; - } - _tag = tag; - } - - public void Write(string line) - { - var time = UnixTimestamp.Now; - var entry = _log[_log.InsertNextIndex() - 1]; - entry.Timestamp = UnixTimestamp.Empty; - entry.Text = null; - entry.Text = string.Format("[{0}] {1}", _tag, line.Trim()); - entry.Timestamp = time; - } - - public void WriteTo(TextWriter writer) - { - var start = _log.NextIndex; - for (UInt32 i = 0; i < _log.LineCount; ++i) - { - var entry = _log[i + start]; - if (entry.Timestamp.IsEmpty) - continue; - var text = entry.ToString(); - if (text == null) - continue; - writer.WriteLine(text); - } - } - - public static readonly UInt32 CursorAll = UInt32.MaxValue; - public List FollowFromCursor(ref UInt32 cursor) - { - var lines = new List((int)_log.LineCount); - var i = cursor; - var all = cursor == CursorAll; - if (all) - i = _log.NextIndex; - for (UInt32 l = 0; l < _log.LineCount; ++l, ++i) - { - if (!all && i % _log.LineCount == _log.NextIndex % _log.LineCount) - break; - var entry = _log[i]; - if (entry.Timestamp.IsEmpty) - { - if (all) - continue; - break; - } - cursor = (i + 1) % _log.LineCount; - var text = entry.ToString(); - if (text == null) - continue; - lines.Add(text); - } - return lines; - } - } -} diff --git a/embeddable-dll-service/csharp/Service.cs b/embeddable-dll-service/csharp/Service.cs deleted file mode 100644 index 0310d693..00000000 --- a/embeddable-dll-service/csharp/Service.cs +++ /dev/null @@ -1,119 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved. - */ - -using System; -using System.IO; -using System.IO.Pipes; -using System.Runtime.InteropServices; -using System.ComponentModel; -using System.Diagnostics; -using System.Security.Principal; -using System.Threading; - -namespace Tunnel -{ - public class Service - { - private const string LongName = "Example WireGuard Tunnel Client"; - private const string Description = "A WireGuard tunnel created by example code."; - - [DllImport("tunnel.dll", EntryPoint = "WireGuardTunnelService", CallingConvention = CallingConvention.Cdecl)] - public static extern bool Run([MarshalAs(UnmanagedType.LPWStr)] string configFile); - - public static NamedPipeClientStream GetPipe(string configFile) - { - var pipepath = "ProtectedPrefix\\Administrators\\WireGuard\\" + Path.GetFileNameWithoutExtension(configFile); - return new NamedPipeClientStream(pipepath); - } - - public static void Add(string configFile) - { - var tunnelName = Path.GetFileNameWithoutExtension(configFile); - var shortName = String.Format("WireGuardTunnel${0}", tunnelName); - var longName = String.Format("{0}: {1}", LongName, tunnelName); - var exeName = Process.GetCurrentProcess().MainModule.FileName; - var pathAndArgs = String.Format("\"{0}\" /service \"{1}\"", exeName, configFile); //TODO: This is not the proper way to escape file args. - - var accessControl = File.GetAccessControl(configFile); //TODO: TOCTOU! - accessControl.SetOwner(new NTAccount(Environment.UserDomainName, Environment.UserName)); - File.SetAccessControl(configFile, accessControl); - - var scm = Win32.OpenSCManager(null, null, Win32.ScmAccessRights.AllAccess); - if (scm == IntPtr.Zero) - throw new Win32Exception(Marshal.GetLastWin32Error()); - try - { - var service = Win32.OpenService(scm, shortName, Win32.ServiceAccessRights.AllAccess); - if (service != IntPtr.Zero) - { - Win32.CloseServiceHandle(service); - Remove(configFile); - } - service = Win32.CreateService(scm, shortName, longName, Win32.ServiceAccessRights.AllAccess, Win32.ServiceType.Win32OwnProcess, Win32.ServiceStartType.Demand, Win32.ServiceError.Normal, pathAndArgs, null, IntPtr.Zero, "Nsi\0TcpIp", null, null); - if (service == IntPtr.Zero) - throw new Win32Exception(Marshal.GetLastWin32Error()); - try - { - var sidType = Win32.ServiceSidType.Unrestricted; - if (!Win32.ChangeServiceConfig2(service, Win32.ServiceConfigType.SidInfo, ref sidType)) - throw new Win32Exception(Marshal.GetLastWin32Error()); - - var description = new Win32.ServiceDescription { lpDescription = Description }; - if (!Win32.ChangeServiceConfig2(service, Win32.ServiceConfigType.Description, ref description)) - throw new Win32Exception(Marshal.GetLastWin32Error()); - - if (!Win32.StartService(service, 0, null)) - throw new Win32Exception(Marshal.GetLastWin32Error()); - } - finally - { - Win32.CloseServiceHandle(service); - } - } - finally - { - Win32.CloseServiceHandle(scm); - } - } - - public static void Remove(string configFile) - { - var tunnelName = Path.GetFileNameWithoutExtension(configFile); - var shortName = String.Format("WireGuardTunnel${0}", tunnelName); - - var scm = Win32.OpenSCManager(null, null, Win32.ScmAccessRights.AllAccess); - if (scm == IntPtr.Zero) - throw new Win32Exception(Marshal.GetLastWin32Error()); - try - { - var service = Win32.OpenService(scm, shortName, Win32.ServiceAccessRights.AllAccess); - if (service == IntPtr.Zero) - { - Win32.CloseServiceHandle(service); - return; - } - try - { - var serviceStatus = new Win32.ServiceStatus(); - Win32.ControlService(service, Win32.ServiceControl.Stop, serviceStatus); - - for (int i = 0; i < 180 && Win32.QueryServiceStatus(service, serviceStatus) && serviceStatus.dwCurrentState != Win32.ServiceState.Stopped; ++i) - Thread.Sleep(1000); - - if (!Win32.DeleteService(service)) - throw new Win32Exception(Marshal.GetLastWin32Error()); - } - finally - { - Win32.CloseServiceHandle(service); - } - } - finally - { - Win32.CloseServiceHandle(scm); - } - } - } -} diff --git a/embeddable-dll-service/csharp/TunnelDll/Keypair.cs b/embeddable-dll-service/csharp/TunnelDll/Keypair.cs new file mode 100644 index 00000000..146d56e0 --- /dev/null +++ b/embeddable-dll-service/csharp/TunnelDll/Keypair.cs @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. + */ + +using System; +using System.Runtime.InteropServices; + +namespace Tunnel +{ + public class Keypair + { + public readonly string Public; + public readonly string Private; + + public Keypair(string pub, string priv) + { + Public = pub; + Private = priv; + } + + [DllImport("tunnel.dll", EntryPoint = "WireGuardGenerateKeypair", CallingConvention = CallingConvention.Cdecl)] + private static extern bool WireGuardGenerateKeypair(byte[] publicKey, byte[] privateKey); + + public static Keypair Generate() + { + var publicKey = new byte[32]; + var privateKey = new byte[32]; + WireGuardGenerateKeypair(publicKey, privateKey); + return new Keypair(Convert.ToBase64String(publicKey), Convert.ToBase64String(privateKey)); + } + } +} diff --git a/embeddable-dll-service/csharp/TunnelDll/Ringlogger.cs b/embeddable-dll-service/csharp/TunnelDll/Ringlogger.cs new file mode 100644 index 00000000..469927c1 --- /dev/null +++ b/embeddable-dll-service/csharp/TunnelDll/Ringlogger.cs @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. + */ + +using System; +using System.IO; +using System.IO.MemoryMappedFiles; +using System.Text; +using System.Collections.Generic; +using System.Threading; +using System.Runtime.CompilerServices; + +namespace Tunnel +{ + public class Ringlogger + { + private struct UnixTimestamp + { + private Int64 _ns; + public UnixTimestamp(Int64 ns) => _ns = ns; + public bool IsEmpty => _ns == 0; + public static UnixTimestamp Empty => new UnixTimestamp(0); + public static UnixTimestamp Now + { + get + { + var now = DateTimeOffset.UtcNow; + var ns = (now.Subtract(DateTimeOffset.FromUnixTimeSeconds(0)).Ticks * 100) % 1000000000; + return new UnixTimestamp(now.ToUnixTimeSeconds() * 1000000000 + ns); + } + } + public Int64 Nanoseconds => _ns; + public override string ToString() + { + return DateTimeOffset.FromUnixTimeSeconds(_ns / 1000000000).LocalDateTime.ToString("yyyy'-'MM'-'dd HH':'mm':'ss'.'") + ((_ns % 1000000000).ToString() + "00000").Substring(0, 6); + } + } + private struct Line + { + private const int maxLineLength = 512; + private const int offsetTimeNs = 0; + private const int offsetLine = 8; + + private readonly MemoryMappedViewAccessor _view; + private readonly int _start; + public Line(MemoryMappedViewAccessor view, UInt32 index) => (_view, _start) = (view, (int)(Log.HeaderBytes + index * Bytes)); + + public static int Bytes => maxLineLength + offsetLine; + + public UnixTimestamp Timestamp + { + get => new UnixTimestamp(_view.ReadInt64(_start + offsetTimeNs)); + set => _view.Write(_start + offsetTimeNs, value.Nanoseconds); + } + + public string Text + { + get + { + var textBytes = new byte[maxLineLength]; + _view.ReadArray(_start + offsetLine, textBytes, 0, textBytes.Length); + var nullByte = Array.IndexOf(textBytes, 0); + if (nullByte <= 0) + return null; + return Encoding.UTF8.GetString(textBytes, 0, nullByte); + } + set + { + if (value == null) + { + _view.WriteArray(_start + offsetLine, new byte[maxLineLength], 0, maxLineLength); + return; + } + var textBytes = Encoding.UTF8.GetBytes(value); + var bytesToWrite = Math.Min(maxLineLength - 1, textBytes.Length); + _view.Write(_start + offsetLine + bytesToWrite, (byte)0); + _view.WriteArray(_start + offsetLine, textBytes, 0, bytesToWrite); + } + } + + public override string ToString() + { + var time = Timestamp; + if (time.IsEmpty) + return null; + var text = Text; + if (text == null) + return null; + return string.Format("{0}: {1}", time, text); + } + } + private struct Log + { + private const UInt32 maxLines = 2048; + private const UInt32 magic = 0xbadbabe; + private const int offsetMagic = 0; + private const int offsetNextIndex = 4; + private const int offsetLines = 8; + + private readonly MemoryMappedViewAccessor _view; + public Log(MemoryMappedViewAccessor view) => _view = view; + + public static int HeaderBytes => offsetLines; + public static int Bytes => (int)(HeaderBytes + Line.Bytes * maxLines); + + public UInt32 ExpectedMagic => magic; + public UInt32 Magic + { + get => _view.ReadUInt32(offsetMagic); + set => _view.Write(offsetMagic, value); + } + + public UInt32 NextIndex + { + get => _view.ReadUInt32(offsetNextIndex); + set => _view.Write(offsetNextIndex, value); + } + public unsafe UInt32 InsertNextIndex() + { + byte* pointer = null; + _view.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer); + var ret = (UInt32)Interlocked.Increment(ref Unsafe.AsRef(pointer + offsetNextIndex)); + _view.SafeMemoryMappedViewHandle.ReleasePointer(); + return ret; + } + + public UInt32 LineCount => maxLines; + public Line this[UInt32 i] => new Line(_view, i % maxLines); + + public void Clear() => _view.WriteArray(0, new byte[Bytes], 0, Bytes); + } + + private readonly Log _log; + private readonly string _tag; + + public Ringlogger(string filename, string tag) + { + var file = File.Open(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete); + file.SetLength(Log.Bytes); + var mmap = MemoryMappedFile.CreateFromFile(file, null, 0, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, false); + var view = mmap.CreateViewAccessor(0, Log.Bytes, MemoryMappedFileAccess.ReadWrite); + _log = new Log(view); + if (_log.Magic != _log.ExpectedMagic) + { + _log.Clear(); + _log.Magic = _log.ExpectedMagic; + } + _tag = tag; + } + + public void Write(string line) + { + var time = UnixTimestamp.Now; + var entry = _log[_log.InsertNextIndex() - 1]; + entry.Timestamp = UnixTimestamp.Empty; + entry.Text = null; + entry.Text = string.Format("[{0}] {1}", _tag, line.Trim()); + entry.Timestamp = time; + } + + public void WriteTo(TextWriter writer) + { + var start = _log.NextIndex; + for (UInt32 i = 0; i < _log.LineCount; ++i) + { + var entry = _log[i + start]; + if (entry.Timestamp.IsEmpty) + continue; + var text = entry.ToString(); + if (text == null) + continue; + writer.WriteLine(text); + } + } + + public static readonly UInt32 CursorAll = UInt32.MaxValue; + public List FollowFromCursor(ref UInt32 cursor) + { + var lines = new List((int)_log.LineCount); + var i = cursor; + var all = cursor == CursorAll; + if (all) + i = _log.NextIndex; + for (UInt32 l = 0; l < _log.LineCount; ++l, ++i) + { + if (!all && i % _log.LineCount == _log.NextIndex % _log.LineCount) + break; + var entry = _log[i]; + if (entry.Timestamp.IsEmpty) + { + if (all) + continue; + break; + } + cursor = (i + 1) % _log.LineCount; + var text = entry.ToString(); + if (text == null) + continue; + lines.Add(text); + } + return lines; + } + } +} diff --git a/embeddable-dll-service/csharp/TunnelDll/Service.cs b/embeddable-dll-service/csharp/TunnelDll/Service.cs new file mode 100644 index 00000000..7f2c622e --- /dev/null +++ b/embeddable-dll-service/csharp/TunnelDll/Service.cs @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. + */ + +using System; +using System.IO; +using System.IO.Pipes; +using System.Runtime.InteropServices; +using System.ComponentModel; +using System.Diagnostics; +using System.Threading; + +namespace Tunnel +{ + public class Service + { + private const string LongName = "WireGuard Demo Box"; + private const string Description = "Demonstration tunnel for testing WireGuard"; + + [DllImport("tunnel.dll", EntryPoint = "WireGuardTunnelService", CallingConvention = CallingConvention.Cdecl)] + public static extern bool Run([MarshalAs(UnmanagedType.LPWStr)] string configFile); + + public static NamedPipeClientStream GetPipe(string configFile) + { + var pipepath = "ProtectedPrefix\\Administrators\\WireGuard\\" + Path.GetFileNameWithoutExtension(configFile); + return new NamedPipeClientStream(pipepath); + } + + public static void Add(string configFile, bool ephemeral) + { + var tunnelName = Path.GetFileNameWithoutExtension(configFile); + var shortName = String.Format("WireGuardTunnel${0}", tunnelName); + var longName = String.Format("{0}: {1}", LongName, tunnelName); + var exeName = Process.GetCurrentProcess().MainModule.FileName; + var pathAndArgs = String.Format("\"{0}\" /service \"{1}\" {2}", exeName, configFile, Process.GetCurrentProcess().Id); //TODO: This is not the proper way to escape file args. + + var scm = Win32.OpenSCManager(null, null, Win32.ScmAccessRights.AllAccess); + if (scm == IntPtr.Zero) + throw new Win32Exception(Marshal.GetLastWin32Error()); + try + { + var service = Win32.OpenService(scm, shortName, Win32.ServiceAccessRights.AllAccess); + if (service != IntPtr.Zero) + { + Win32.CloseServiceHandle(service); + Remove(configFile, true); + } + service = Win32.CreateService(scm, shortName, longName, Win32.ServiceAccessRights.AllAccess, Win32.ServiceType.Win32OwnProcess, Win32.ServiceStartType.Demand, Win32.ServiceError.Normal, pathAndArgs, null, IntPtr.Zero, "Nsi\0TcpIp", null, null); + if (service == IntPtr.Zero) + throw new Win32Exception(Marshal.GetLastWin32Error()); + try + { + var sidType = Win32.ServiceSidType.Unrestricted; + if (!Win32.ChangeServiceConfig2(service, Win32.ServiceConfigType.SidInfo, ref sidType)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + var description = new Win32.ServiceDescription { lpDescription = Description }; + if (!Win32.ChangeServiceConfig2(service, Win32.ServiceConfigType.Description, ref description)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + if (!Win32.StartService(service, 0, null)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + if (ephemeral && !Win32.DeleteService(service)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + finally + { + Win32.CloseServiceHandle(service); + } + } + finally + { + Win32.CloseServiceHandle(scm); + } + } + + public static void Remove(string configFile, bool waitForStop) + { + var tunnelName = Path.GetFileNameWithoutExtension(configFile); + var shortName = String.Format("WireGuardTunnel${0}", tunnelName); + + var scm = Win32.OpenSCManager(null, null, Win32.ScmAccessRights.AllAccess); + if (scm == IntPtr.Zero) + throw new Win32Exception(Marshal.GetLastWin32Error()); + try + { + var service = Win32.OpenService(scm, shortName, Win32.ServiceAccessRights.AllAccess); + if (service == IntPtr.Zero) + { + Win32.CloseServiceHandle(service); + return; + } + try + { + var serviceStatus = new Win32.ServiceStatus(); + Win32.ControlService(service, Win32.ServiceControl.Stop, serviceStatus); + + for (int i = 0; waitForStop && i < 180 && Win32.QueryServiceStatus(service, serviceStatus) && serviceStatus.dwCurrentState != Win32.ServiceState.Stopped; ++i) + Thread.Sleep(1000); + + if (!Win32.DeleteService(service) && Marshal.GetLastWin32Error() != 0x00000430) + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + finally + { + Win32.CloseServiceHandle(service); + } + } + finally + { + Win32.CloseServiceHandle(scm); + } + } + } +} diff --git a/embeddable-dll-service/csharp/TunnelDll/Win32.cs b/embeddable-dll-service/csharp/TunnelDll/Win32.cs new file mode 100644 index 00000000..d8447f7f --- /dev/null +++ b/embeddable-dll-service/csharp/TunnelDll/Win32.cs @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. + */ + +using System; +using System.Runtime.InteropServices; + +namespace Tunnel +{ + static class Win32 + { + [Flags] + public enum ScmAccessRights + { + Connect = 0x0001, + CreateService = 0x0002, + EnumerateService = 0x0004, + Lock = 0x0008, + QueryLockStatus = 0x0010, + ModifyBootConfig = 0x0020, + StandardRightsRequired = 0xF0000, + AllAccess = (StandardRightsRequired | Connect | CreateService | EnumerateService | Lock | QueryLockStatus | ModifyBootConfig) + } + + [Flags] + public enum ServiceAccessRights + { + QueryConfig = 0x1, + ChangeConfig = 0x2, + QueryStatus = 0x4, + EnumerateDependants = 0x8, + Start = 0x10, + Stop = 0x20, + PauseContinue = 0x40, + Interrogate = 0x80, + UserDefinedControl = 0x100, + Delete = 0x00010000, + StandardRightsRequired = 0xF0000, + AllAccess = (StandardRightsRequired | QueryConfig | ChangeConfig | QueryStatus | EnumerateDependants | Start | Stop | PauseContinue | Interrogate | UserDefinedControl) + } + + [Flags] + public enum ServiceStartType + { + Boot = 0x00000000, + System = 0x00000001, + Auto = 0x00000002, + Demand = 0x00000003, + Disabled = 0x00000004 + } + + [Flags] + public enum ServiceControl + { + Stop = 0x00000001, + Pause = 0x00000002, + Continue = 0x00000003, + Interrogate = 0x00000004, + Shutdown = 0x00000005, + ParamChange = 0x00000006, + NetBindAdd = 0x00000007, + NetBindRemove = 0x00000008, + NetBindEnable = 0x00000009, + NetBindDisable = 0x0000000A + } + + [Flags] + public enum ServiceError + { + Ignore = 0x00000000, + Normal = 0x00000001, + Severe = 0x00000002, + Critical = 0x00000003 + } + + [Flags] + public enum ServiceSidType + { + None = 0x00000000, + Unrestricted = 0x00000001, + Restricted = 0x00000003 + } + + [Flags] + public enum ServiceType + { + KernelDriver = 0x00000001, + FileSystemDriver = 0x00000002, + Win32OwnProcess = 0x00000010, + Win32ShareProcess = 0x00000020, + InteractiveProcess = 0x00000100 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Size = 8192), ComVisible(false)] + public struct ServiceSidInfo + { + public ServiceSidType serviceSidType; + }; + + public enum ServiceState + { + Unknown = -1, + NotFound = 0, + Stopped = 1, + StartPending = 2, + StopPending = 3, + Running = 4, + ContinuePending = 5, + PausePending = 6, + Paused = 7 + } + + [StructLayout(LayoutKind.Sequential)] + public class ServiceStatus + { + public int dwServiceType = 0; + public ServiceState dwCurrentState = 0; + public int dwControlsAccepted = 0; + public int dwWin32ExitCode = 0; + public int dwServiceSpecificExitCode = 0; + public int dwCheckPoint = 0; + public int dwWaitHint = 0; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Size = 8192), ComVisible(false)] + public struct ServiceDescription + { + public String lpDescription; + }; + + public enum ServiceConfigType + { + Description = 1, + SidInfo = 5 + } + + [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] + public static extern IntPtr OpenSCManager(string machineName, string databaseName, ScmAccessRights dwDesiredAccess); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, ServiceAccessRights dwDesiredAccess); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseServiceHandle(IntPtr hSCObject); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool StartService(IntPtr hService, int dwNumServiceArgs, string[] lpServiceArgVectors); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr CreateService(IntPtr hSCManager, string lpServiceName, string lpDisplayName, ServiceAccessRights dwDesiredAccess, ServiceType dwServiceType, ServiceStartType dwStartType, ServiceError dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string lpDependencies, string lp, string lpPassword); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DeleteService(IntPtr hService); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ControlService(IntPtr hService, ServiceControl dwControl, ServiceStatus lpServiceStatus); + + [DllImport("advapi32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryServiceStatus(IntPtr hService, ServiceStatus lpServiceStatus); + + [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ChangeServiceConfig2(IntPtr hService, ServiceConfigType dwInfoLevel, ref ServiceSidType lpInfo); + + [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ChangeServiceConfig2(IntPtr hService, ServiceConfigType dwInfoLevel, ref ServiceDescription lpInfo); + } +} diff --git a/embeddable-dll-service/csharp/Win32.cs b/embeddable-dll-service/csharp/Win32.cs deleted file mode 100644 index 9d6808f3..00000000 --- a/embeddable-dll-service/csharp/Win32.cs +++ /dev/null @@ -1,175 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019-2020 WireGuard LLC. All Rights Reserved. - */ - -using System; -using System.Runtime.InteropServices; - -namespace Tunnel -{ - static class Win32 - { - [Flags] - public enum ScmAccessRights - { - Connect = 0x0001, - CreateService = 0x0002, - EnumerateService = 0x0004, - Lock = 0x0008, - QueryLockStatus = 0x0010, - ModifyBootConfig = 0x0020, - StandardRightsRequired = 0xF0000, - AllAccess = (StandardRightsRequired | Connect | CreateService | EnumerateService | Lock | QueryLockStatus | ModifyBootConfig) - } - - [Flags] - public enum ServiceAccessRights - { - QueryConfig = 0x1, - ChangeConfig = 0x2, - QueryStatus = 0x4, - EnumerateDependants = 0x8, - Start = 0x10, - Stop = 0x20, - PauseContinue = 0x40, - Interrogate = 0x80, - UserDefinedControl = 0x100, - Delete = 0x00010000, - StandardRightsRequired = 0xF0000, - AllAccess = (StandardRightsRequired | QueryConfig | ChangeConfig | QueryStatus | EnumerateDependants | Start | Stop | PauseContinue | Interrogate | UserDefinedControl) - } - - [Flags] - public enum ServiceStartType - { - Boot = 0x00000000, - System = 0x00000001, - Auto = 0x00000002, - Demand = 0x00000003, - Disabled = 0x00000004 - } - - [Flags] - public enum ServiceControl - { - Stop = 0x00000001, - Pause = 0x00000002, - Continue = 0x00000003, - Interrogate = 0x00000004, - Shutdown = 0x00000005, - ParamChange = 0x00000006, - NetBindAdd = 0x00000007, - NetBindRemove = 0x00000008, - NetBindEnable = 0x00000009, - NetBindDisable = 0x0000000A - } - - [Flags] - public enum ServiceError - { - Ignore = 0x00000000, - Normal = 0x00000001, - Severe = 0x00000002, - Critical = 0x00000003 - } - - [Flags] - public enum ServiceSidType - { - None = 0x00000000, - Unrestricted = 0x00000001, - Restricted = 0x00000003 - } - - [Flags] - public enum ServiceType - { - KernelDriver = 0x00000001, - FileSystemDriver = 0x00000002, - Win32OwnProcess = 0x00000010, - Win32ShareProcess = 0x00000020, - InteractiveProcess = 0x00000100 - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Size = 8192), ComVisible(false)] - public struct ServiceSidInfo - { - public ServiceSidType serviceSidType; - }; - - public enum ServiceState - { - Unknown = -1, - NotFound = 0, - Stopped = 1, - StartPending = 2, - StopPending = 3, - Running = 4, - ContinuePending = 5, - PausePending = 6, - Paused = 7 - } - - [StructLayout(LayoutKind.Sequential)] - public class ServiceStatus - { - public int dwServiceType = 0; - public ServiceState dwCurrentState = 0; - public int dwControlsAccepted = 0; - public int dwWin32ExitCode = 0; - public int dwServiceSpecificExitCode = 0; - public int dwCheckPoint = 0; - public int dwWaitHint = 0; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Size = 8192), ComVisible(false)] - public struct ServiceDescription - { - public String lpDescription; - }; - - public enum ServiceConfigType - { - Description = 1, - SidInfo = 5 - } - - [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] - public static extern IntPtr OpenSCManager(string machineName, string databaseName, ScmAccessRights dwDesiredAccess); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] - public static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, ServiceAccessRights dwDesiredAccess); - - [DllImport("advapi32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool CloseServiceHandle(IntPtr hSCObject); - - [DllImport("advapi32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool StartService(IntPtr hService, int dwNumServiceArgs, string[] lpServiceArgVectors); - - [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] - public static extern IntPtr CreateService(IntPtr hSCManager, string lpServiceName, string lpDisplayName, ServiceAccessRights dwDesiredAccess, ServiceType dwServiceType, ServiceStartType dwStartType, ServiceError dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string lpDependencies, string lp, string lpPassword); - - [DllImport("advapi32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool DeleteService(IntPtr hService); - - [DllImport("advapi32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool ControlService(IntPtr hService, ServiceControl dwControl, ServiceStatus lpServiceStatus); - - [DllImport("advapi32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool QueryServiceStatus(IntPtr hService, ServiceStatus lpServiceStatus); - - [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool ChangeServiceConfig2(IntPtr hService, ServiceConfigType dwInfoLevel, ref ServiceSidType lpInfo); - - [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool ChangeServiceConfig2(IntPtr hService, ServiceConfigType dwInfoLevel, ref ServiceDescription lpInfo); - } -} diff --git a/embeddable-dll-service/csharp/demo-client.csproj b/embeddable-dll-service/csharp/demo-client.csproj new file mode 100644 index 00000000..00339ee2 --- /dev/null +++ b/embeddable-dll-service/csharp/demo-client.csproj @@ -0,0 +1,14 @@ + + + + WinExe + net5.0-windows + DemoUI + true + demo-client + x64 + true + DemoUI\app.manifest + + + diff --git a/embeddable-dll-service/csharp/demo-client.csproj.user b/embeddable-dll-service/csharp/demo-client.csproj.user new file mode 100644 index 00000000..27d1a581 --- /dev/null +++ b/embeddable-dll-service/csharp/demo-client.csproj.user @@ -0,0 +1,8 @@ + + + + + Form + + + \ No newline at end of file diff --git a/embeddable-dll-service/csharp/demo-client.sln b/embeddable-dll-service/csharp/demo-client.sln new file mode 100644 index 00000000..a7d8b0ee --- /dev/null +++ b/embeddable-dll-service/csharp/demo-client.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30804.86 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "demo-client", "demo-client.csproj", "{AADC81E1-0294-483B-ABAE-63DBE82436E9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AADC81E1-0294-483B-ABAE-63DBE82436E9}.Debug|x64.ActiveCfg = Debug|x64 + {AADC81E1-0294-483B-ABAE-63DBE82436E9}.Debug|x64.Build.0 = Debug|x64 + {AADC81E1-0294-483B-ABAE-63DBE82436E9}.Release|x64.ActiveCfg = Release|x64 + {AADC81E1-0294-483B-ABAE-63DBE82436E9}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E410FD53-4E4A-4299-B6BD-CE91685DF7BE} + EndGlobalSection +EndGlobal -- cgit v1.2.3-59-g8ed1b