/*
* Copyright (c) 2025 Proton AG
*
* This file is part of ProtonVPN.
*
* ProtonVPN is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ProtonVPN is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ProtonVPN. If not, see .
*/
using System;
using System.Runtime.InteropServices;
using ProtonVPN.Configurations.Contracts;
using ProtonVPN.Logging.Contracts;
using ProtonVPN.Logging.Contracts.Events.NetworkLogs;
using ProtonVPN.Logging.Contracts.Events.OperatingSystemLogs;
namespace ProtonVPN.Vpn.NetworkAdapters;
public class WintunAdapter : IWintunAdapter
{
private const string WINTUN_DLL = "wintun.dll";
private const string WINTUN_GUID = "344dba6b-6834-489e-a556-22e36b502107";
private readonly ILogger _logger;
private readonly IStaticConfiguration _staticConfig;
private readonly WintunLogger _wintunLogger;
private IntPtr _adapterHandle;
private IntPtr _libraryHandle;
public WintunAdapter(ILogger logger, IStaticConfiguration staticConfig)
{
_logger = logger;
_staticConfig = staticConfig;
_wintunLogger = LogWintunMessage;
}
public bool Create()
{
InitializeLibrary();
if (_libraryHandle == IntPtr.Zero)
{
_logger.Error($"Failed to initialize wintun library. Error code: {GetLastError()}");
return false;
}
WintunSetLogger(_wintunLogger);
_adapterHandle = WintunOpenAdapter(_staticConfig.WintunAdapterName);
if (_adapterHandle == IntPtr.Zero)
{
Guid guid = Guid.Parse(WINTUN_GUID);
_adapterHandle = WintunCreateAdapter("ProtonVPN TUN", "ProtonVPN TUN", ref guid);
if (_adapterHandle == IntPtr.Zero)
{
_logger.Error($"Failed to create wintun adapter. Error code: {GetLastError()}");
}
}
return _adapterHandle != IntPtr.Zero;
}
public void Close()
{
if (_adapterHandle != IntPtr.Zero)
{
WintunCloseAdapter(_adapterHandle);
_adapterHandle = IntPtr.Zero;
}
}
private void InitializeLibrary()
{
if (_libraryHandle == IntPtr.Zero)
{
_libraryHandle = LoadLibrary(_staticConfig.WintunDriverPath);
}
}
private delegate void WintunLogger(int level, ulong timestamp,
[MarshalAs(UnmanagedType.LPWStr)] string message);
private void LogWintunMessage(int level, ulong timestamp, string message)
{
string fullMessage = $"Wintun: {message}";
switch (level)
{
case 0:
_logger.Info(fullMessage);
break;
case 1:
_logger.Warn(fullMessage);
break;
case 2:
_logger.Error(fullMessage);
break;
default:
_logger.Debug(fullMessage);
break;
}
}
[DllImport(WINTUN_DLL, EntryPoint = "WintunCreateAdapter", SetLastError = true)]
private static extern IntPtr WintunCreateAdapter([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] string type, ref Guid guid);
[DllImport(WINTUN_DLL, EntryPoint = "WintunOpenAdapter", SetLastError = true)]
private static extern IntPtr WintunOpenAdapter(string name);
[DllImport(WINTUN_DLL, EntryPoint = "WintunCloseAdapter", SetLastError = true)]
private static extern void WintunCloseAdapter(IntPtr adapterHandle);
[DllImport(WINTUN_DLL, EntryPoint = "WintunSetLogger", SetLastError = true)]
private static extern void WintunSetLogger(WintunLogger logger);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr LoadLibrary(string path);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
}