aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/embeddable-dll-service/csharp
diff options
context:
space:
mode:
Diffstat (limited to 'embeddable-dll-service/csharp')
-rw-r--r--embeddable-dll-service/csharp/.gitignore3
-rw-r--r--embeddable-dll-service/csharp/DemoUI/MainWindow.Designer.cs87
-rw-r--r--embeddable-dll-service/csharp/DemoUI/MainWindow.cs215
-rw-r--r--embeddable-dll-service/csharp/DemoUI/MainWindow.resx60
-rw-r--r--embeddable-dll-service/csharp/DemoUI/Program.cs44
-rw-r--r--embeddable-dll-service/csharp/DemoUI/app.manifest48
-rw-r--r--embeddable-dll-service/csharp/Program.cs92
-rw-r--r--embeddable-dll-service/csharp/README.md7
-rw-r--r--embeddable-dll-service/csharp/TunnelDll/Driver.cs234
-rw-r--r--embeddable-dll-service/csharp/TunnelDll/Keypair.cs (renamed from embeddable-dll-service/csharp/Keypair.cs)4
-rw-r--r--embeddable-dll-service/csharp/TunnelDll/Ringlogger.cs (renamed from embeddable-dll-service/csharp/Ringlogger.cs)2
-rw-r--r--embeddable-dll-service/csharp/TunnelDll/Service.cs (renamed from embeddable-dll-service/csharp/Service.cs)36
-rw-r--r--embeddable-dll-service/csharp/TunnelDll/Win32.cs (renamed from embeddable-dll-service/csharp/Win32.cs)54
-rw-r--r--embeddable-dll-service/csharp/demo-client.csproj14
-rw-r--r--embeddable-dll-service/csharp/demo-client.csproj.user8
-rw-r--r--embeddable-dll-service/csharp/demo-client.sln25
16 files changed, 816 insertions, 117 deletions
diff --git a/embeddable-dll-service/csharp/.gitignore b/embeddable-dll-service/csharp/.gitignore
new file mode 100644
index 00000000..28bcb4ab
--- /dev/null
+++ b/embeddable-dll-service/csharp/.gitignore
@@ -0,0 +1,3 @@
+/.vs
+/bin
+/obj
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..5aad1b97
--- /dev/null
+++ b/embeddable-dll-service/csharp/DemoUI/MainWindow.Designer.cs
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
+ */
+
+namespace DemoUI
+{
+ partial class MainWindow
+ {
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.IContainer components = null;
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ 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..1df17181
--- /dev/null
+++ b/embeddable-dll-service/csharp/DemoUI/MainWindow.cs
@@ -0,0 +1,215 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019-2022 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;
+using System.Security.AccessControl;
+
+namespace DemoUI
+{
+ public partial class MainWindow : Form
+ {
+ private static readonly string userDirectory = Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "Config"); //TODO: put in Program Files in real code.
+ 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()
+ {
+ makeConfigDirectory();
+ 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 makeConfigDirectory()
+ {
+ var ds = new DirectorySecurity();
+ ds.SetSecurityDescriptorSddlForm("O:BAG:BAD:PAI(A;OICI;FA;;;BA)(A;OICI;FA;;;SY)");
+ FileSystemAclExtensions.CreateDirectory(ds, userDirectory);
+ }
+
+ 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<string>(logBox.AppendText), new object[] { line + "\r\n" });
+ try
+ {
+ Thread.Sleep(300);
+ }
+ catch
+ {
+ break;
+ }
+ }
+ }
+
+ private void tailTransfer()
+ {
+ Tunnel.Driver.Adapter adapter = null;
+ while (threadsRunning)
+ {
+ if (adapter == null)
+ {
+ while (threadsRunning)
+ {
+ try
+ {
+ adapter = Tunnel.Service.GetAdapter(configFile);
+ break;
+ }
+ catch
+ {
+ try
+ {
+ Thread.Sleep(1000);
+ }
+ catch { }
+ }
+ }
+ }
+ if (adapter == null)
+ continue;
+ try
+ {
+ ulong rx = 0, tx = 0;
+ var config = adapter.GetConfiguration();
+ foreach (var peer in config.Peers)
+ {
+ rx += peer.RxBytes;
+ tx += peer.TxBytes;
+ }
+ Invoke(new Action<ulong, ulong>(updateTransferTitle), new object[] { rx, tx });
+ Thread.Sleep(1000);
+ }
+ catch { adapter = null; }
+ }
+ }
+
+ 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<string> 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 @@
+<root>
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+</root> \ 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..3649ab93
--- /dev/null
+++ b/embeddable-dll-service/csharp/DemoUI/Program.cs
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019-2022 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+ <assemblyIdentity version="1.0.0.0" name="demo-client"/>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+
+ </application>
+ </compatibility>
+
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings>
+ <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
+ </windowsSettings>
+ </application>
+
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+ </dependency>
+
+</assembly>
diff --git a/embeddable-dll-service/csharp/Program.cs b/embeddable-dll-service/csharp/Program.cs
deleted file mode 100644
index d99e66fd..00000000
--- a/embeddable-dll-service/csharp/Program.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-/* SPDX-License-Identifier: MIT
- *
- * Copyright (C) 2019 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..24b36563
--- /dev/null
+++ b/embeddable-dll-service/csharp/README.md
@@ -0,0 +1,7 @@
+# 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` and `wireguard.dll`](../README.md).
diff --git a/embeddable-dll-service/csharp/TunnelDll/Driver.cs b/embeddable-dll-service/csharp/TunnelDll/Driver.cs
new file mode 100644
index 00000000..69911ec8
--- /dev/null
+++ b/embeddable-dll-service/csharp/TunnelDll/Driver.cs
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
+ */
+
+using System;
+using System.ComponentModel;
+using System.Net;
+using System.Runtime.InteropServices;
+
+namespace Tunnel
+{
+ public class Driver
+ {
+ [DllImport("wireguard.dll", EntryPoint = "WireGuardOpenAdapter", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
+ private static extern IntPtr openAdapter([MarshalAs(UnmanagedType.LPWStr)] string name);
+ [DllImport("wireguard.dll", EntryPoint = "WireGuardCloseAdapter", CallingConvention = CallingConvention.StdCall)]
+ private static extern void freeAdapter(IntPtr adapter);
+ [DllImport("wireguard.dll", EntryPoint = "WireGuardGetConfiguration", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
+ private static extern bool getConfiguration(IntPtr adapter, byte[] iface, ref UInt32 bytes);
+
+ public class Adapter
+ {
+ private IntPtr _handle;
+ private UInt32 _lastGetGuess;
+ public Adapter(string name)
+ {
+ _lastGetGuess = 1024;
+ _handle = openAdapter(name);
+ if (_handle == IntPtr.Zero)
+ throw new Win32Exception();
+ }
+ ~Adapter()
+ {
+ freeAdapter(_handle);
+ }
+ public unsafe Interface GetConfiguration()
+ {
+ var iface = new Interface();
+ byte[] bytes;
+ for (; ; )
+ {
+ bytes = new byte[_lastGetGuess];
+ if (getConfiguration(_handle, bytes, ref _lastGetGuess))
+ break;
+ if (Marshal.GetLastWin32Error() != 234 /* ERROR_MORE_DATA */)
+ throw new Win32Exception();
+ }
+ fixed (void* start = bytes)
+ {
+ var ioctlIface = (IoctlInterface*)start;
+ if ((ioctlIface->Flags & IoctlInterfaceFlags.HasPublicKey) != 0)
+ iface.PublicKey = new Key(ioctlIface->PublicKey);
+ if ((ioctlIface->Flags & IoctlInterfaceFlags.HasPrivateKey) != 0)
+ iface.PrivateKey = new Key(ioctlIface->PrivateKey);
+ if ((ioctlIface->Flags & IoctlInterfaceFlags.HasListenPort) != 0)
+ iface.ListenPort = ioctlIface->ListenPort;
+ var peers = new Peer[ioctlIface->PeersCount];
+ var ioctlPeer = (IoctlPeer*)((byte*)ioctlIface + sizeof(IoctlInterface));
+ for (UInt32 i = 0; i < peers.Length; ++i)
+ {
+ var peer = new Peer();
+ if ((ioctlPeer->Flags & IoctlPeerFlags.HasPublicKey) != 0)
+ peer.PublicKey = new Key(ioctlPeer->PublicKey);
+ if ((ioctlPeer->Flags & IoctlPeerFlags.HasPresharedKey) != 0)
+ peer.PresharedKey = new Key(ioctlPeer->PresharedKey);
+ if ((ioctlPeer->Flags & IoctlPeerFlags.HasPersistentKeepalive) != 0)
+ peer.PersistentKeepalive = ioctlPeer->PersistentKeepalive;
+ if ((ioctlPeer->Flags & IoctlPeerFlags.HasEndpoint) != 0)
+ {
+ if (ioctlPeer->Endpoint.si_family == Win32.ADDRESS_FAMILY.AF_INET)
+ {
+ var ip = new byte[4];
+ Marshal.Copy((IntPtr)ioctlPeer->Endpoint.Ipv4.sin_addr.bytes, ip, 0, 4);
+ peer.Endpoint = new IPEndPoint(new IPAddress(ip), (ushort)IPAddress.NetworkToHostOrder((short)ioctlPeer->Endpoint.Ipv4.sin_port));
+ }
+ else if (ioctlPeer->Endpoint.si_family == Win32.ADDRESS_FAMILY.AF_INET6)
+ {
+ var ip = new byte[16];
+ Marshal.Copy((IntPtr)ioctlPeer->Endpoint.Ipv6.sin6_addr.bytes, ip, 0, 16);
+ peer.Endpoint = new IPEndPoint(new IPAddress(ip), (ushort)IPAddress.NetworkToHostOrder((short)ioctlPeer->Endpoint.Ipv6.sin6_port));
+ }
+ }
+ peer.TxBytes = ioctlPeer->TxBytes;
+ peer.RxBytes = ioctlPeer->RxBytes;
+ if (ioctlPeer->LastHandshake != 0)
+ peer.LastHandshake = DateTime.FromFileTimeUtc((long)ioctlPeer->LastHandshake);
+ var allowedIPs = new AllowedIP[ioctlPeer->AllowedIPsCount];
+ var ioctlAllowedIP = (IoctlAllowedIP*)((byte*)ioctlPeer + sizeof(IoctlPeer));
+ for (UInt32 j = 0; j < allowedIPs.Length; ++j)
+ {
+ var allowedIP = new AllowedIP();
+ if (ioctlAllowedIP->AddressFamily == Win32.ADDRESS_FAMILY.AF_INET)
+ {
+ var ip = new byte[4];
+ Marshal.Copy((IntPtr)ioctlAllowedIP->V4.bytes, ip, 0, 4);
+ allowedIP.Address = new IPAddress(ip);
+ }
+ else if (ioctlAllowedIP->AddressFamily == Win32.ADDRESS_FAMILY.AF_INET6)
+ {
+ var ip = new byte[16];
+ Marshal.Copy((IntPtr)ioctlAllowedIP->V6.bytes, ip, 0, 16);
+ allowedIP.Address = new IPAddress(ip);
+ }
+ allowedIP.Cidr = ioctlAllowedIP->Cidr;
+ allowedIPs[j] = allowedIP;
+ ioctlAllowedIP = (IoctlAllowedIP*)((byte*)ioctlAllowedIP + sizeof(IoctlAllowedIP));
+ }
+ peer.AllowedIPs = allowedIPs;
+ peers[i] = peer;
+ ioctlPeer = (IoctlPeer*)ioctlAllowedIP;
+ }
+ iface.Peers = peers;
+ }
+ return iface;
+ }
+
+ public class Key
+ {
+ private byte[] _bytes;
+ public byte[] Bytes
+ {
+ get
+ {
+ return _bytes;
+ }
+ set
+ {
+ if (value == null || value.Length != 32)
+ throw new ArgumentException("Keys must be 32 bytes");
+ _bytes = value;
+ }
+ }
+ public Key(byte[] bytes)
+ {
+ Bytes = bytes;
+ }
+ public unsafe Key(byte* bytes)
+ {
+ _bytes = new byte[32];
+ Marshal.Copy((IntPtr)bytes, _bytes, 0, 32);
+ }
+ public override String ToString()
+ {
+ return Convert.ToBase64String(_bytes);
+ }
+ }
+
+ public class Interface
+ {
+ public UInt16 ListenPort { get; set; }
+ public Key PrivateKey { get; set; }
+ public Key PublicKey { get; set; }
+ public Peer[] Peers { get; set; }
+ }
+
+ public class Peer
+ {
+ public Key PublicKey { get; set; }
+ public Key PresharedKey { get; set; }
+ public UInt16 PersistentKeepalive { get; set; }
+ public IPEndPoint Endpoint { get; set; }
+ public UInt64 TxBytes { get; set; }
+ public UInt64 RxBytes { get; set; }
+ public DateTime LastHandshake { get; set; }
+ public AllowedIP[] AllowedIPs { get; set; }
+ }
+
+ public class AllowedIP
+ {
+ public IPAddress Address { get; set; }
+ public byte Cidr { get; set; }
+ }
+
+ private enum IoctlInterfaceFlags : UInt32
+ {
+ HasPublicKey = 1 << 0,
+ HasPrivateKey = 1 << 1,
+ HasListenPort = 1 << 2,
+ ReplacePeers = 1 << 3
+ };
+
+ [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 80)]
+ private unsafe struct IoctlInterface
+ {
+ public IoctlInterfaceFlags Flags;
+ public UInt16 ListenPort;
+ public fixed byte PrivateKey[32];
+ public fixed byte PublicKey[32];
+ public UInt32 PeersCount;
+ };
+
+ private enum IoctlPeerFlags : UInt32
+ {
+ HasPublicKey = 1 << 0,
+ HasPresharedKey = 1 << 1,
+ HasPersistentKeepalive = 1 << 2,
+ HasEndpoint = 1 << 3,
+ ReplaceAllowedIPs = 1 << 5,
+ Remove = 1 << 6,
+ UpdateOnly = 1 << 7
+ };
+
+ [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 136)]
+ private unsafe struct IoctlPeer
+ {
+ public IoctlPeerFlags Flags;
+ public UInt32 Reserved;
+ public fixed byte PublicKey[32];
+ public fixed byte PresharedKey[32];
+ public UInt16 PersistentKeepalive;
+ public Win32.SOCKADDR_INET Endpoint;
+ public UInt64 TxBytes, RxBytes;
+ public UInt64 LastHandshake;
+ public UInt32 AllowedIPsCount;
+ };
+
+ [StructLayout(LayoutKind.Explicit, Pack = 8, Size = 24)]
+ private unsafe struct IoctlAllowedIP
+ {
+ [FieldOffset(0)]
+ [MarshalAs(UnmanagedType.Struct)]
+ public Win32.IN_ADDR V4;
+ [FieldOffset(0)]
+ [MarshalAs(UnmanagedType.Struct)]
+ public Win32.IN6_ADDR V6;
+ [FieldOffset(16)]
+ public Win32.ADDRESS_FAMILY AddressFamily;
+ [FieldOffset(20)]
+ public byte Cidr;
+ }
+ }
+ }
+}
diff --git a/embeddable-dll-service/csharp/Keypair.cs b/embeddable-dll-service/csharp/TunnelDll/Keypair.cs
index e5764fbd..59847b98 100644
--- a/embeddable-dll-service/csharp/Keypair.cs
+++ b/embeddable-dll-service/csharp/TunnelDll/Keypair.cs
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
using System;
@@ -13,7 +13,7 @@ namespace Tunnel
public readonly string Public;
public readonly string Private;
- private Keypair(string pub, string priv)
+ public Keypair(string pub, string priv)
{
Public = pub;
Private = priv;
diff --git a/embeddable-dll-service/csharp/Ringlogger.cs b/embeddable-dll-service/csharp/TunnelDll/Ringlogger.cs
index d0957926..9db01fc8 100644
--- a/embeddable-dll-service/csharp/Ringlogger.cs
+++ b/embeddable-dll-service/csharp/TunnelDll/Ringlogger.cs
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
using System;
diff --git a/embeddable-dll-service/csharp/Service.cs b/embeddable-dll-service/csharp/TunnelDll/Service.cs
index db600819..74e1a888 100644
--- a/embeddable-dll-service/csharp/Service.cs
+++ b/embeddable-dll-service/csharp/TunnelDll/Service.cs
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
using System;
@@ -9,36 +9,30 @@ 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.";
+ 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)
+ public static Driver.Adapter GetAdapter(string configFile)
{
- var pipepath = "ProtectedPrefix\\Administrators\\WireGuard\\" + Path.GetFileNameWithoutExtension(configFile);
- return new NamedPipeClientStream(pipepath);
+ return new Driver.Adapter(Path.GetFileNameWithoutExtension(configFile));
}
- public static void Add(string configFile)
+ 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}\"", 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 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)
@@ -49,9 +43,9 @@ namespace Tunnel
if (service != IntPtr.Zero)
{
Win32.CloseServiceHandle(service);
- Remove(configFile);
+ 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", null, null);
+ service = Win32.CreateService(scm, shortName, longName, Win32.ServiceAccessRights.AllAccess, Win32.ServiceType.Win32OwnProcess, Win32.ServiceStartType.Demand, Win32.ServiceError.Normal, pathAndArgs, null, IntPtr.Zero, "Nsi\0TcpIp\0", null, null);
if (service == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
try
@@ -66,6 +60,9 @@ namespace Tunnel
if (!Win32.StartService(service, 0, null))
throw new Win32Exception(Marshal.GetLastWin32Error());
+
+ if (ephemeral && !Win32.DeleteService(service))
+ throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
{
@@ -78,7 +75,7 @@ namespace Tunnel
}
}
- public static void Remove(string configFile)
+ public static void Remove(string configFile, bool waitForStop)
{
var tunnelName = Path.GetFileNameWithoutExtension(configFile);
var shortName = String.Format("WireGuardTunnel${0}", tunnelName);
@@ -90,19 +87,16 @@ namespace Tunnel
{
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)
+ for (int i = 0; waitForStop && i < 180 && Win32.QueryServiceStatus(service, serviceStatus) && serviceStatus.dwCurrentState != Win32.ServiceState.Stopped; ++i)
Thread.Sleep(1000);
- if (!Win32.DeleteService(service))
+ if (!Win32.DeleteService(service) && Marshal.GetLastWin32Error() != 0x00000430)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
diff --git a/embeddable-dll-service/csharp/Win32.cs b/embeddable-dll-service/csharp/TunnelDll/Win32.cs
index 76395f7e..8e7f986d 100644
--- a/embeddable-dll-service/csharp/Win32.cs
+++ b/embeddable-dll-service/csharp/TunnelDll/Win32.cs
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
using System;
@@ -135,6 +135,58 @@ namespace Tunnel
SidInfo = 5
}
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct IN_ADDR
+ {
+ public fixed byte bytes[4];
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct IN6_ADDR
+ {
+ public fixed byte bytes[16];
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SOCKADDR_IN
+ {
+ public ushort sin_family;
+ public ushort sin_port;
+ public IN_ADDR sin_addr;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SOCKADDR_IN6
+ {
+ public ushort sin6_family;
+ public ushort sin6_port;
+ public uint sin6_flowinfo;
+ public IN6_ADDR sin6_addr;
+ public uint sin6_scope_id;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct SOCKADDR_INET
+ {
+ [FieldOffset(0)]
+ [MarshalAs(UnmanagedType.Struct)]
+ public SOCKADDR_IN Ipv4;
+
+ [FieldOffset(0)]
+ [MarshalAs(UnmanagedType.Struct)]
+ public SOCKADDR_IN6 Ipv6;
+
+ [FieldOffset(0)]
+ public ADDRESS_FAMILY si_family;
+ }
+
+ public enum ADDRESS_FAMILY : UInt16
+ {
+ AF_UNSPEC = 0,
+ AF_INET = 2,
+ AF_INET6 = 23
+ }
+
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr OpenSCManager(string machineName, string databaseName, ScmAccessRights dwDesiredAccess);
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 @@
+<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
+
+ <PropertyGroup>
+ <OutputType>WinExe</OutputType>
+ <TargetFramework>net5.0-windows</TargetFramework>
+ <RootNamespace>DemoUI</RootNamespace>
+ <UseWindowsForms>true</UseWindowsForms>
+ <AssemblyName>demo-client</AssemblyName>
+ <Platforms>x64</Platforms>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <ApplicationManifest>DemoUI\app.manifest</ApplicationManifest>
+ </PropertyGroup>
+
+</Project>
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Compile Update="DemoUI\MainWindow.cs">
+ <SubType>Form</SubType>
+ </Compile>
+ </ItemGroup>
+</Project> \ 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