2022-04-19 18:21:29 +02:00
|
|
|
|
/*
|
2024-03-05 18:37:42 +01:00
|
|
|
|
* Copyright (c) 2024 ETH Zürich, IT Services
|
2022-04-19 18:21:29 +02:00
|
|
|
|
*
|
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Net.NetworkInformation;
|
|
|
|
|
using SafeExamBrowser.Logging.Contracts;
|
|
|
|
|
using SafeExamBrowser.SystemComponents.Contracts.Network;
|
|
|
|
|
using SafeExamBrowser.SystemComponents.Contracts.Network.Events;
|
|
|
|
|
using SafeExamBrowser.WindowsApi.Contracts;
|
2024-05-21 19:11:42 +02:00
|
|
|
|
using Windows.Devices.Enumeration;
|
|
|
|
|
using Windows.Devices.WiFi;
|
|
|
|
|
using Windows.Foundation;
|
|
|
|
|
using Windows.Networking.Connectivity;
|
|
|
|
|
using Windows.Security.Credentials;
|
2024-05-02 10:30:26 +02:00
|
|
|
|
using Timer = System.Timers.Timer;
|
2022-04-19 18:21:29 +02:00
|
|
|
|
|
|
|
|
|
namespace SafeExamBrowser.SystemComponents.Network
|
|
|
|
|
{
|
|
|
|
|
public class NetworkAdapter : INetworkAdapter
|
|
|
|
|
{
|
|
|
|
|
private readonly object @lock = new object();
|
2024-05-02 10:30:26 +02:00
|
|
|
|
|
2022-04-19 18:21:29 +02:00
|
|
|
|
private readonly ILogger logger;
|
|
|
|
|
private readonly INativeMethods nativeMethods;
|
|
|
|
|
private readonly List<WirelessNetwork> wirelessNetworks;
|
|
|
|
|
|
|
|
|
|
private Timer timer;
|
2024-05-21 19:11:42 +02:00
|
|
|
|
private WiFiAdapter adapter;
|
|
|
|
|
|
|
|
|
|
private bool HasWirelessAdapter => adapter != default;
|
2022-04-19 18:21:29 +02:00
|
|
|
|
|
|
|
|
|
public ConnectionStatus Status { get; private set; }
|
|
|
|
|
public ConnectionType Type { get; private set; }
|
|
|
|
|
|
|
|
|
|
public event ChangedEventHandler Changed;
|
2024-05-02 10:30:26 +02:00
|
|
|
|
public event CredentialsRequiredEventHandler CredentialsRequired;
|
2022-04-19 18:21:29 +02:00
|
|
|
|
|
|
|
|
|
public NetworkAdapter(ILogger logger, INativeMethods nativeMethods)
|
|
|
|
|
{
|
|
|
|
|
this.logger = logger;
|
|
|
|
|
this.nativeMethods = nativeMethods;
|
|
|
|
|
this.wirelessNetworks = new List<WirelessNetwork>();
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-02 10:30:26 +02:00
|
|
|
|
public void ConnectToWirelessNetwork(string name)
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
var network = default(WiFiAvailableNetwork);
|
|
|
|
|
|
2022-04-19 18:21:29 +02:00
|
|
|
|
lock (@lock)
|
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
network = wirelessNetworks.FirstOrDefault(n => n.Name == name)?.Network;
|
|
|
|
|
}
|
2022-04-19 18:21:29 +02:00
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
if (network != default)
|
|
|
|
|
{
|
|
|
|
|
var isOpen = network.SecuritySettings.NetworkAuthenticationType == NetworkAuthenticationType.Open80211 && network.SecuritySettings.NetworkEncryptionType == NetworkEncryptionType.None;
|
2022-04-19 18:21:29 +02:00
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
if (isOpen)
|
|
|
|
|
{
|
|
|
|
|
logger.Info($"Attempting to connect to open wireless network '{name}'...");
|
2022-04-19 18:21:29 +02:00
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
adapter.ConnectAsync(network, WiFiReconnectionKind.Automatic).Completed = (o, s) => Adapter_ConnectCompleted(name, o, s);
|
|
|
|
|
Status = ConnectionStatus.Connecting;
|
2022-04-19 18:21:29 +02:00
|
|
|
|
}
|
2024-05-21 19:11:42 +02:00
|
|
|
|
else if (TryGetCredentials(name, out var credentials))
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
logger.Info($"Attempting to connect to wireless network '{name}' with credentials...");
|
|
|
|
|
|
|
|
|
|
adapter.ConnectAsync(network, WiFiReconnectionKind.Automatic, credentials).Completed = (o, s) => Adapter_ConnectCompleted(name, o, s);
|
|
|
|
|
Status = ConnectionStatus.Connecting;
|
2022-04-19 18:21:29 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-21 19:11:42 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.Warn($"Could not find wireless network '{name}'!");
|
|
|
|
|
}
|
2022-04-19 18:21:29 +02:00
|
|
|
|
|
|
|
|
|
Changed?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IEnumerable<IWirelessNetwork> GetWirelessNetworks()
|
|
|
|
|
{
|
|
|
|
|
lock (@lock)
|
|
|
|
|
{
|
|
|
|
|
return new List<WirelessNetwork>(wirelessNetworks);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Initialize()
|
|
|
|
|
{
|
|
|
|
|
const int FIVE_SECONDS = 5000;
|
|
|
|
|
|
|
|
|
|
timer = new Timer(FIVE_SECONDS);
|
|
|
|
|
timer.Elapsed += (o, args) => Update();
|
|
|
|
|
timer.AutoReset = true;
|
2024-05-21 19:11:42 +02:00
|
|
|
|
|
|
|
|
|
InitializeAdapter();
|
|
|
|
|
|
|
|
|
|
NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
|
|
|
|
|
NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
|
|
|
|
|
NetworkInformation.NetworkStatusChanged += NetworkInformation_NetworkStatusChanged;
|
2022-04-19 18:21:29 +02:00
|
|
|
|
|
|
|
|
|
Update();
|
|
|
|
|
|
|
|
|
|
logger.Info("Started monitoring the network adapter.");
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
public void StartWirelessNetworkScanning()
|
|
|
|
|
{
|
|
|
|
|
timer?.Start();
|
|
|
|
|
|
|
|
|
|
if (HasWirelessAdapter)
|
|
|
|
|
{
|
|
|
|
|
_ = adapter.ScanAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StopWirelessNetworkScanning()
|
|
|
|
|
{
|
|
|
|
|
timer?.Stop();
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-19 18:21:29 +02:00
|
|
|
|
public void Terminate()
|
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
NetworkChange.NetworkAddressChanged -= NetworkChange_NetworkAddressChanged;
|
|
|
|
|
NetworkChange.NetworkAvailabilityChanged -= NetworkChange_NetworkAvailabilityChanged;
|
|
|
|
|
NetworkInformation.NetworkStatusChanged -= NetworkInformation_NetworkStatusChanged;
|
|
|
|
|
|
|
|
|
|
if (HasWirelessAdapter)
|
|
|
|
|
{
|
|
|
|
|
adapter.AvailableNetworksChanged -= Adapter_AvailableNetworksChanged;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (timer != default)
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
|
|
|
|
timer.Stop();
|
|
|
|
|
}
|
2024-05-21 19:11:42 +02:00
|
|
|
|
|
|
|
|
|
logger.Info("Stopped monitoring the network adapter.");
|
2022-04-19 18:21:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
private void InitializeAdapter()
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Requesting access is required as of fall 2024 and must be granted manually by the user, otherwise all wireless functionality will
|
|
|
|
|
// be denied by the system (see also https://learn.microsoft.com/en-us/windows/win32/nativewifi/wi-fi-access-location-changes).
|
|
|
|
|
var task = WiFiAdapter.RequestAccessAsync().AsTask();
|
|
|
|
|
var status = task.GetAwaiter().GetResult();
|
|
|
|
|
|
|
|
|
|
if (status == WiFiAccessStatus.Allowed)
|
|
|
|
|
{
|
|
|
|
|
var findAll = DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector()).AsTask();
|
|
|
|
|
var devices = findAll.GetAwaiter().GetResult();
|
|
|
|
|
|
|
|
|
|
if (devices.Any())
|
|
|
|
|
{
|
|
|
|
|
var id = devices.First().Id;
|
|
|
|
|
var getById = WiFiAdapter.FromIdAsync(id).AsTask();
|
|
|
|
|
|
|
|
|
|
logger.Debug($"Found {devices.Count()} wireless network adapter(s).");
|
|
|
|
|
|
|
|
|
|
adapter = getById.GetAwaiter().GetResult();
|
|
|
|
|
adapter.AvailableNetworksChanged += Adapter_AvailableNetworksChanged;
|
|
|
|
|
|
|
|
|
|
logger.Debug($"Successfully initialized wireless network adapter '{id}'.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.Info("Could not find a wireless network adapter.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.Error($"Access to the wireless network adapter has been denied ({status})!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
logger.Error("Failed to initialize wireless network adapter!", e);
|
2022-04-19 18:21:29 +02:00
|
|
|
|
}
|
2024-05-21 19:11:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Adapter_AvailableNetworksChanged(WiFiAdapter sender, object args)
|
|
|
|
|
{
|
|
|
|
|
Update(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Adapter_ConnectCompleted(string name, IAsyncOperation<WiFiConnectionResult> operation, AsyncStatus status)
|
|
|
|
|
{
|
|
|
|
|
var connectionStatus = default(WiFiConnectionStatus?);
|
2022-04-19 18:21:29 +02:00
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
if (status == AsyncStatus.Completed)
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
connectionStatus = operation.GetResults()?.ConnectionStatus;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.Error($"Failed to complete connection operation! Status: {status}.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (connectionStatus == WiFiConnectionStatus.Success)
|
|
|
|
|
{
|
|
|
|
|
Status = ConnectionStatus.Connected;
|
2022-04-19 18:21:29 +02:00
|
|
|
|
logger.Info($"Successfully connected to wireless network '{name}'.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
Status = ConnectionStatus.Disconnected;
|
|
|
|
|
logger.Error($"Failed to connect to wireless network '{name}'! Reason: {connectionStatus}.");
|
2022-04-19 18:21:29 +02:00
|
|
|
|
}
|
2024-05-21 19:11:42 +02:00
|
|
|
|
|
|
|
|
|
Update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
logger.Debug("Network address changed.");
|
|
|
|
|
Update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
logger.Debug($"Network availability changed ({(e.IsAvailable ? "available" : "unavailable")}.");
|
|
|
|
|
Update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void NetworkInformation_NetworkStatusChanged(object sender)
|
|
|
|
|
{
|
|
|
|
|
logger.Debug("Network status changed.");
|
|
|
|
|
Update();
|
2022-04-19 18:21:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
private bool TryGetCredentials(string network, out PasswordCredential credentials)
|
2024-05-02 10:30:26 +02:00
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
var args = new CredentialsRequiredEventArgs { NetworkName = network };
|
|
|
|
|
|
|
|
|
|
credentials = new PasswordCredential();
|
2024-05-02 10:30:26 +02:00
|
|
|
|
|
|
|
|
|
CredentialsRequired?.Invoke(args);
|
|
|
|
|
|
|
|
|
|
if (args.Success)
|
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
if (!string.IsNullOrEmpty(args.Password))
|
|
|
|
|
{
|
|
|
|
|
credentials.Password = args.Password;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(args.Username))
|
|
|
|
|
{
|
|
|
|
|
credentials.UserName = args.Username;
|
|
|
|
|
}
|
2024-05-02 10:30:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return args.Success;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
private bool TryGetCurrentWirelessNetwork(out string name)
|
|
|
|
|
{
|
|
|
|
|
name = default;
|
|
|
|
|
|
|
|
|
|
if (HasWirelessAdapter)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var getProfile = adapter.NetworkAdapter.GetConnectedProfileAsync().AsTask();
|
|
|
|
|
var profile = getProfile.GetAwaiter().GetResult();
|
|
|
|
|
|
|
|
|
|
if (profile?.IsWlanConnectionProfile == true)
|
|
|
|
|
{
|
|
|
|
|
name = profile.WlanConnectionProfileDetails.GetConnectedSsid();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return name != default;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Update(bool rescan = true)
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
lock (@lock)
|
|
|
|
|
{
|
|
|
|
|
var hasInternet = nativeMethods.HasInternetConnection();
|
|
|
|
|
var isConnecting = Status == ConnectionStatus.Connecting;
|
|
|
|
|
var previousStatus = Status;
|
|
|
|
|
|
|
|
|
|
wirelessNetworks.Clear();
|
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
if (HasWirelessAdapter)
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
TryGetCurrentWirelessNetwork(out var currentNetwork);
|
|
|
|
|
|
|
|
|
|
foreach (var network in adapter.NetworkReport.AvailableNetworks.FilterAndOrder())
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
var wirelessNetwork = network.ToWirelessNetwork();
|
2024-05-02 10:30:26 +02:00
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
if (network.Ssid == currentNetwork)
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
wirelessNetwork.Status = ConnectionStatus.Connected;
|
2022-04-19 18:21:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
wirelessNetworks.Add(wirelessNetwork);
|
|
|
|
|
}
|
2022-04-19 18:21:29 +02:00
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
if (rescan)
|
|
|
|
|
{
|
|
|
|
|
_ = adapter.ScanAsync();
|
|
|
|
|
}
|
2022-04-19 18:21:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
Type = HasWirelessAdapter ? ConnectionType.Wireless : (hasInternet ? ConnectionType.Wired : ConnectionType.Undefined);
|
|
|
|
|
Status = hasInternet ? ConnectionStatus.Connected : (HasWirelessAdapter && isConnecting ? ConnectionStatus.Connecting : ConnectionStatus.Disconnected);
|
|
|
|
|
|
|
|
|
|
LogNetworkChanges(previousStatus, wirelessNetworks.FirstOrDefault(n => n.Status == ConnectionStatus.Connected));
|
2022-04-19 18:21:29 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
logger.Error("Failed to update network adapter!", e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Changed?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-22 15:25:52 +02:00
|
|
|
|
private void LogNetworkChanges(ConnectionStatus previousStatus, WirelessNetwork currentNetwork = default)
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
if (previousStatus != ConnectionStatus.Connected && Status == ConnectionStatus.Connected)
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
logger.Info($"Connection established ({Type}{(currentNetwork != default ? $", {currentNetwork.Name}" : "")}).");
|
2022-04-19 18:21:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-21 19:11:42 +02:00
|
|
|
|
if (previousStatus != ConnectionStatus.Disconnected && Status == ConnectionStatus.Disconnected)
|
2022-04-19 18:21:29 +02:00
|
|
|
|
{
|
2024-05-21 19:11:42 +02:00
|
|
|
|
logger.Info("Connection lost.");
|
|
|
|
|
}
|
2022-04-19 18:21:29 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|