seb-win-refactoring/SafeExamBrowser.SystemComponents/Network/NetworkAdapter.cs

215 lines
5.6 KiB
C#

/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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 System.Timers;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.Network.Events;
using SafeExamBrowser.WindowsApi.Contracts;
using SimpleWifi;
using SimpleWifi.Win32;
using SimpleWifi.Win32.Interop;
namespace SafeExamBrowser.SystemComponents.Network
{
public class NetworkAdapter : INetworkAdapter
{
private readonly object @lock = new object();
private readonly ILogger logger;
private readonly INativeMethods nativeMethods;
private readonly List<WirelessNetwork> wirelessNetworks;
private Timer timer;
private Wifi wifi;
public ConnectionStatus Status { get; private set; }
public ConnectionType Type { get; private set; }
public event ChangedEventHandler Changed;
public NetworkAdapter(ILogger logger, INativeMethods nativeMethods)
{
this.logger = logger;
this.nativeMethods = nativeMethods;
this.wirelessNetworks = new List<WirelessNetwork>();
}
public void ConnectToWirelessNetwork(Guid id)
{
lock (@lock)
{
var network = wirelessNetworks.FirstOrDefault(n => n.Id == id);
if (network != default)
{
try
{
var request = new AuthRequest(network.AccessPoint);
logger.Info($"Attempting to connect to '{network.Name}'...");
network.AccessPoint.ConnectAsync(request, false, (success) => AccessPoint_OnConnectCompleted(network.Name, success));
Status = ConnectionStatus.Connecting;
}
catch (Exception e)
{
logger.Error($"Failed to connect to wireless network '{network.Name}!'", e);
}
}
else
{
logger.Warn($"Could not find network with id '{id}'!");
}
}
Changed?.Invoke();
}
public IEnumerable<IWirelessNetwork> GetWirelessNetworks()
{
lock (@lock)
{
return new List<WirelessNetwork>(wirelessNetworks);
}
}
public void Initialize()
{
const int FIVE_SECONDS = 5000;
NetworkChange.NetworkAddressChanged += (o, args) => Update();
NetworkChange.NetworkAvailabilityChanged += (o, args) => Update();
wifi = new Wifi();
wifi.ConnectionStatusChanged += (o, args) => Update();
timer = new Timer(FIVE_SECONDS);
timer.Elapsed += (o, args) => Update();
timer.AutoReset = true;
timer.Start();
Update();
logger.Info("Started monitoring the network adapter.");
}
public void Terminate()
{
if (timer != null)
{
timer.Stop();
logger.Info("Stopped monitoring the network adapter.");
}
}
private void AccessPoint_OnConnectCompleted(string name, bool success)
{
lock (@lock)
{
// This handler seems to be called before the connection has been fully established, thus we don't yet set the status to connected...
Status = success ? ConnectionStatus.Connecting : ConnectionStatus.Disconnected;
}
if (success)
{
logger.Info($"Successfully connected to wireless network '{name}'.");
}
else
{
logger.Error($"Failed to connect to wireless network '{name}!'");
}
}
private void Update()
{
try
{
lock (@lock)
{
var hasInternet = nativeMethods.HasInternetConnection();
var hasWireless = !wifi.NoWifiAvailable && !IsTurnedOff();
var isConnecting = Status == ConnectionStatus.Connecting;
var previousStatus = Status;
wirelessNetworks.Clear();
if (hasWireless)
{
foreach (var accessPoint in wifi.GetAccessPoints())
{
// The user may only connect to an already configured or connected wireless network!
if (accessPoint.HasProfile || accessPoint.IsConnected)
{
wirelessNetworks.Add(ToWirelessNetwork(accessPoint));
}
}
}
Type = hasWireless ? ConnectionType.Wireless : (hasInternet ? ConnectionType.Wired : ConnectionType.Undefined);
Status = hasInternet ? ConnectionStatus.Connected : (hasWireless && isConnecting ? ConnectionStatus.Connecting : ConnectionStatus.Disconnected);
if (previousStatus != ConnectionStatus.Connected && Status == ConnectionStatus.Connected)
{
logger.Info("Connection established.");
}
if (previousStatus != ConnectionStatus.Disconnected && Status == ConnectionStatus.Disconnected)
{
logger.Info("Connection lost.");
}
}
}
catch (Exception e)
{
logger.Error("Failed to update network adapter!", e);
}
Changed?.Invoke();
}
private bool IsTurnedOff()
{
try
{
var client = new WlanClient();
foreach (var @interface in client.Interfaces)
{
foreach (var state in @interface.RadioState.PhyRadioState)
{
if (state.dot11SoftwareRadioState == Dot11RadioState.On && state.dot11HardwareRadioState == Dot11RadioState.On)
{
return false;
}
}
}
}
catch (Exception e)
{
logger.Error("Failed to determine the radio state of the wireless adapter(s)! Assuming it is (all are) turned off...", e);
}
return true;
}
private WirelessNetwork ToWirelessNetwork(AccessPoint accessPoint)
{
return new WirelessNetwork
{
AccessPoint = accessPoint,
Name = accessPoint.Name,
SignalStrength = Convert.ToInt32(accessPoint.SignalStrength),
Status = accessPoint.IsConnected ? ConnectionStatus.Connected : ConnectionStatus.Disconnected
};
}
}
}