// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using Microsoft.PowerToys.Run.Plugin.System.Properties;
namespace Microsoft.PowerToys.Run.Plugin.System.Components
{
///
/// This class represents the information for a network connection/interface
///
internal sealed class NetworkConnectionProperties
{
///
/// Gets the name of the adapter
///
internal string Adapter { get; private set; }
///
/// Gets the physical address (MAC) of the adapter
///
internal string PhysicalAddress { get; private set; }
///
/// Gets a value indicating the interface type
///
internal NetworkInterfaceType Type { get; private set; }
///
/// Gets the speed of the adapter as unformatted value (Static information form the adapter device)
///
internal long Speed { get; private set; }
///
/// Gets a value indicating the operational state of the adapter
///
internal OperationalStatus State { get; private set; }
///
/// Gets the name of the network connection
///
internal string ConnectionName { get; private set; }
///
/// Gets a string with the suffix of the connection
///
internal string Suffix { get; private set; }
///
/// Gets the IPv4 address
///
internal string IPv4 { get; private set; }
///
/// Gets the IPv4 subnet mask
///
internal string IPv4Mask { get; private set; }
///
/// Gets the primarily used IPv6 address
///
internal string IPv6Primary { get; private set; }
///
/// Gets the global IPv6 address
///
internal string IPv6Global { get; private set; }
///
/// Gets the temporary IPv6 address
///
internal string IPv6Temporary { get; private set; }
///
/// Gets the link local IPv6 address
///
internal string IPv6LinkLocal { get; private set; }
///
/// Gets the site local IPv6 address
///
internal string IPv6SiteLocal { get; private set; }
///
/// Gets the unique local IPv6 address
///
internal string IPv6UniqueLocal { get; private set; }
///
/// Gets the list of gateway IPs as string
///
internal List Gateways { get; private set; } = new List();
///
/// Gets the list of DHCP server IPs as string
///
internal IPAddressCollection DhcpServers { get; private set; }
///
/// Gets the list of DNS server IPs as string
///
internal IPAddressCollection DnsServers { get; private set; }
///
/// Gets the list of WINS server IPs as string
///
internal IPAddressCollection WinsServers { get; private set; }
private static readonly CompositeFormat MicrosoftPluginSysGbps = CompositeFormat.Parse(Properties.Resources.Microsoft_plugin_sys_Gbps);
private static readonly CompositeFormat MicrosoftPluginSysMbps = CompositeFormat.Parse(Properties.Resources.Microsoft_plugin_sys_Mbps);
///
/// Initializes a new instance of the class.
/// This private constructor is used when we crete the list of adapter (properties) by calling .
///
/// Network interface of the connection
private NetworkConnectionProperties(NetworkInterface networkInterface)
{
// Setting adapter properties
Adapter = networkInterface.Description;
PhysicalAddress = networkInterface.GetPhysicalAddress().ToString();
Type = networkInterface.NetworkInterfaceType;
Speed = networkInterface.Speed;
State = networkInterface.OperationalStatus;
// Connection properties
ConnectionName = networkInterface.Name;
if (State == OperationalStatus.Up)
{
Suffix = networkInterface.GetIPProperties().DnsSuffix;
SetIpProperties(networkInterface.GetIPProperties());
}
}
///
/// Creates a list with all network adapters and their properties
///
/// List containing all network adapters
internal static List GetList()
{
var interfaces = NetworkInterface.GetAllNetworkInterfaces()
.Where(x => x.NetworkInterfaceType != NetworkInterfaceType.Loopback && x.GetPhysicalAddress() != null)
.Select(i => new NetworkConnectionProperties(i))
.OrderByDescending(i => i.IPv4) // list IPv4 first
.ThenBy(i => i.IPv6Primary) // then IPv6
.ToList();
return interfaces;
}
///
/// Gets a formatted string with the adapter details
///
/// String with the details
internal string GetAdapterDetails()
{
return $"{Resources.Microsoft_plugin_sys_AdapterName}: {Adapter}" +
$"\n{Resources.Microsoft_plugin_sys_PhysicalAddress}: {PhysicalAddress}" +
$"\n{Resources.Microsoft_plugin_sys_Speed}: {GetFormattedSpeedValue(Speed)}" +
$"\n{Resources.Microsoft_plugin_sys_Type}: {GetAdapterTypeAsString(Type)}" +
$"\n{Resources.Microsoft_plugin_sys_State}: " + (State == OperationalStatus.Up ? Resources.Microsoft_plugin_sys_Connected : Resources.Microsoft_plugin_sys_Disconnected) +
$"\n{Resources.Microsoft_plugin_sys_ConnectionName}: {ConnectionName}";
}
///
/// Returns a formatted string with the connection details
///
/// String with the details
internal string GetConnectionDetails()
{
return $"{Resources.Microsoft_plugin_sys_ConnectionName}: {ConnectionName}" +
$"\n{Resources.Microsoft_plugin_sys_State}: " + (State == OperationalStatus.Up ? Resources.Microsoft_plugin_sys_Connected : Resources.Microsoft_plugin_sys_Disconnected) +
$"\n{Resources.Microsoft_plugin_sys_Type}: {GetAdapterTypeAsString(Type)}" +
$"\n{Resources.Microsoft_plugin_sys_Suffix}: {Suffix}" +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip4Address}: ", IPv4) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip4SubnetMask}: ", IPv4Mask) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Address}:\n\t", IPv6Global) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Temp}:\n\t", IPv6Temporary) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Link}:\n\t", IPv6LinkLocal) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Site}:\n\t", IPv6SiteLocal) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Unique}:\n\t", IPv6UniqueLocal) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Gateways}:\n\t", Gateways) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Dhcp}:\n\t", DhcpServers) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Dns}:\n\t", DnsServers) +
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Wins}:\n\t", WinsServers) +
$"\n\n{Resources.Microsoft_plugin_sys_AdapterName}: {Adapter}" +
$"\n{Resources.Microsoft_plugin_sys_PhysicalAddress}: {PhysicalAddress}" +
$"\n{Resources.Microsoft_plugin_sys_Speed}: {GetFormattedSpeedValue(Speed)}";
}
///
/// Set the ip address properties of the instance.
///
/// Element of the type .
private void SetIpProperties(IPInterfaceProperties properties)
{
DateTime t = DateTime.Now;
UnicastIPAddressInformationCollection ipList = properties.UnicastAddresses;
GatewayIPAddressInformationCollection gwList = properties.GatewayAddresses;
DhcpServers = properties.DhcpServerAddresses;
DnsServers = properties.DnsAddresses;
WinsServers = properties.WinsServersAddresses;
for (int i = 0; i < ipList.Count; i++)
{
IPAddress ip = ipList[i].Address;
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
IPv4 = ip.ToString();
IPv4Mask = ipList[i].IPv4Mask.ToString();
}
else if (ip.AddressFamily == AddressFamily.InterNetworkV6)
{
if (string.IsNullOrEmpty(IPv6Primary))
{
IPv6Primary = ip.ToString();
}
if (ip.IsIPv6LinkLocal)
{
IPv6LinkLocal = ip.ToString();
}
else if (ip.IsIPv6SiteLocal)
{
IPv6SiteLocal = ip.ToString();
}
else if (ip.IsIPv6UniqueLocal)
{
IPv6UniqueLocal = ip.ToString();
}
else if (ipList[i].SuffixOrigin == SuffixOrigin.Random)
{
IPv6Temporary = ip.ToString();
}
else
{
IPv6Global = ip.ToString();
}
}
}
for (int i = 0; i < gwList.Count; i++)
{
Gateways.Add(gwList[i].Address);
}
Debug.Print($"time for getting ips: {DateTime.Now - t}");
}
///
/// Gets the interface type as string
///
/// The type to convert
/// A string indicating the interface type
private string GetAdapterTypeAsString(NetworkInterfaceType type)
{
switch (type)
{
case NetworkInterfaceType.Wman:
case NetworkInterfaceType.Wwanpp:
case NetworkInterfaceType.Wwanpp2:
return Resources.Microsoft_plugin_sys_MobileBroadband;
case NetworkInterfaceType.Wireless80211:
return Resources.Microsoft_plugin_sys_WirelessLan;
case NetworkInterfaceType.Loopback:
return Resources.Microsoft_plugin_sys_Loopback;
case NetworkInterfaceType.Tunnel:
return Resources.Microsoft_plugin_sys_TunnelConnection;
case NetworkInterfaceType.Unknown:
return Resources.Microsoft_plugin_sys_Unknown;
default:
return Resources.Microsoft_plugin_sys_Cable;
}
}
///
/// Gets the speed as formatted text value
///
/// The adapter speed as .
/// A formatted string like `100 MB/s`
private static string GetFormattedSpeedValue(long speed)
{
return (speed >= 1000000000) ? string.Format(CultureInfo.InvariantCulture, MicrosoftPluginSysGbps, speed / 1000000000) : string.Format(CultureInfo.InvariantCulture, MicrosoftPluginSysMbps, speed / 1000000);
}
///
/// Returns IP info or an empty string
///
/// Descriptive header for the information.
/// IP value as or .
/// Formatted string or an empty string.
/// If the parameter is not of the type or .
private static string CreateIpInfoForDetailsText(string title, dynamic property)
{
switch (property)
{
case string:
return $"\n{title}{property}";
case List listString:
return listString.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
case List listIP:
return listIP.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
case IPAddressCollection collectionIP:
return collectionIP.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
case null:
return string.Empty;
default:
throw new ArgumentException($"'{property}' is not of type 'string', 'List', 'List' or 'IPAddressCollection'.", nameof(property));
}
}
}
}