/* * 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 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(); } 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 GetWirelessNetworks() { lock (@lock) { return new List(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 }; } } }