/*
* Copyright (c) 2026 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.Security;
using Microsoft.Win32;
using ProtonVPN.IssueReporting.Contracts;
using ProtonVPN.Logging.Contracts;
using ProtonVPN.Logging.Contracts.Events.OperatingSystemLogs;
namespace ProtonVPN.Vpn.Wintun;
/**
* Sometimes ComponentId is missing in the registry and OpenVPN won't find TUN adapter.
* This class checks if the key is missing and creates one.
*/
public class WintunRegistryFixer : IWintunRegistryFixer
{
// https://docs.microsoft.com/en-us/windows-hardware/drivers/install/system-defined-device-setup-classes-available-to-vendors
private readonly string _regPath = "SYSTEM\\ControlSet001\\Control\\Class\\{4d36e972-e325-11ce-bfc1-08002be10318}";
private readonly ILogger _logger;
private readonly IIssueReporter _issueReporter;
public WintunRegistryFixer(ILogger logger, IIssueReporter issueReporter)
{
_logger = logger;
_issueReporter = issueReporter;
}
public void EnsureTunAdapterRegistryIsCorrect()
{
try
{
using RegistryKey? adaptersKey = Registry.LocalMachine.OpenSubKey(_regPath);
if (adaptersKey == null)
{
return;
}
string[] folders = adaptersKey.GetSubKeyNames();
foreach (string folder in folders)
{
if (folder.Length != 4)
{
continue;
}
using (RegistryKey? adapterKey = Registry.LocalMachine.OpenSubKey($"{_regPath}\\{folder}"))
{
if (adapterKey == null)
{
continue;
}
string? matchingDeviceId = adapterKey.GetValue("MatchingDeviceId") as string;
if (matchingDeviceId != "wintun")
{
continue;
}
string? componentId = adapterKey.GetValue("ComponentId") as string;
if (string.IsNullOrEmpty(componentId))
{
Registry.SetValue($"HKEY_LOCAL_MACHINE\\{_regPath}\\{folder}", "ComponentId", matchingDeviceId);
_issueReporter.CaptureMessage("Fixed missing ComponentId on wintun adapter.");
}
else if (matchingDeviceId != componentId)
{
_logger.Info($"WintunRegistryFixer: ComponentId '{componentId}' " +
$"has a value but is different from the MatchingDeviceId '{matchingDeviceId}'.");
}
}
}
}
catch (SecurityException)
{
}
}
}