namespace Unosquare.RaspberryIO.Computer { using Swan; using Swan.Abstractions; using Swan.Components; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; /// /// Represents the network information /// public class NetworkSettings : SingletonBase { private const string EssidTag = "ESSID:"; /// /// Gets the local machine Host Name. /// public string HostName => Network.HostName; /// /// Retrieves the wireless networks. /// /// The adapter. /// A list of WiFi networks public List RetrieveWirelessNetworks(string adapter) => RetrieveWirelessNetworks(new[] { adapter }); /// /// Retrieves the wireless networks. /// /// The adapters. /// A list of WiFi networks public List RetrieveWirelessNetworks(string[] adapters = null) { var result = new List(); foreach (var networkAdapter in adapters ?? RetrieveAdapters().Where(x => x.IsWireless).Select(x => x.Name)) { var wirelessOutput = ProcessRunner.GetProcessOutputAsync("iwlist", $"{networkAdapter} scanning").Result; var outputLines = wirelessOutput.Split('\n') .Select(x => x.Trim()) .Where(x => string.IsNullOrWhiteSpace(x) == false) .ToArray(); for (var i = 0; i < outputLines.Length; i++) { var line = outputLines[i]; if (line.StartsWith(EssidTag) == false) continue; var network = new WirelessNetworkInfo() { Name = line.Replace(EssidTag, string.Empty).Replace("\"", string.Empty) }; while (true) { if (i + 1 >= outputLines.Length) break; // should look for two lines before the ESSID acording to the scan line = outputLines[i - 2]; if (line.StartsWith("Quality=")) { network.Quality = line.Replace("Quality=", string.Empty); break; } } while (true) { if (i + 1 >= outputLines.Length) break; // should look for a line before the ESSID acording to the scan line = outputLines[i - 1]; if (line.StartsWith("Encryption key:")) { network.IsEncrypted = line.Replace("Encryption key:", string.Empty).Trim() == "on"; break; } } if (result.Any(x => x.Name == network.Name) == false) result.Add(network); } } return result.OrderBy(x => x.Name).ToList(); } /// /// Setups the wireless network. /// /// Name of the adapter. /// The network ssid. /// The password. /// The 2-letter country code in uppercase. Default is US. /// True if successful. Otherwise, false. public bool SetupWirelessNetwork(string adapterName, string networkSsid, string password = null, string countryCode = "US") { // TODO: Get the country where the device is located to set 'country' param in payload var var payload = $"country={countryCode}\nctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\nupdate_config=1\n"; payload += string.IsNullOrEmpty(password) ? $"network={{\n\tssid=\"{networkSsid}\"\n\t}}\n" : $"network={{\n\tssid=\"{networkSsid}\"\n\tpsk=\"{password}\"\n\t}}\n"; try { File.WriteAllText("/etc/wpa_supplicant/wpa_supplicant.conf", payload); ProcessRunner.GetProcessOutputAsync("pkill", "-f wpa_supplicant").Wait(); ProcessRunner.GetProcessOutputAsync("ifdown", adapterName).Wait(); ProcessRunner.GetProcessOutputAsync("ifup", adapterName).Wait(); } catch (Exception ex) { ex.Log(nameof(NetworkSettings)); return false; } return true; } /// /// Retrieves the network adapters. /// /// A list of network adapters. public List RetrieveAdapters() { const string hWaddr = "HWaddr "; const string ether = "ether "; var result = new List(); var interfacesOutput = ProcessRunner.GetProcessOutputAsync("ifconfig").Result; var wlanOutput = ProcessRunner.GetProcessOutputAsync("iwconfig") .Result.Split('\n') .Where(x => x.Contains("no wireless extensions.") == false) .ToArray(); var outputLines = interfacesOutput.Split('\n').Where(x => string.IsNullOrWhiteSpace(x) == false).ToArray(); for (var i = 0; i < outputLines.Length; i++) { // grab the current line var line = outputLines[i]; // skip if the line is indented if (char.IsLetterOrDigit(line[0]) == false) continue; // Read the line as an adatper var adapter = new NetworkAdapterInfo { Name = line.Substring(0, line.IndexOf(' ')).TrimEnd(':') }; // Parse the MAC address in old version of ifconfig; it comes in the first line if (line.IndexOf(hWaddr) >= 0) { var startIndexHwd = line.IndexOf(hWaddr) + hWaddr.Length; adapter.MacAddress = line.Substring(startIndexHwd, 17).Trim(); } // Parse the info in lines other than the first for (var j = i + 1; j < outputLines.Length; j++) { // Get the contents of the indented line var indentedLine = outputLines[j]; // We have hit the next adapter info if (char.IsLetterOrDigit(indentedLine[0])) { i = j - 1; break; } // Parse the MAC address in new versions of ifconfig; it no longer comes in the first line if (indentedLine.IndexOf(ether) >= 0 && string.IsNullOrWhiteSpace(adapter.MacAddress)) { var startIndexHwd = indentedLine.IndexOf(ether) + ether.Length; adapter.MacAddress = indentedLine.Substring(startIndexHwd, 17).Trim(); } // Parse the IPv4 Address { var addressText = ParseOutputTagFromLine(indentedLine, "inet addr:") ?? ParseOutputTagFromLine(indentedLine, "inet "); if (addressText != null) { if (IPAddress.TryParse(addressText, out var outValue)) adapter.IPv4 = outValue; } } // Parse the IPv6 Address { var addressText = ParseOutputTagFromLine(indentedLine, "inet6 addr:") ?? ParseOutputTagFromLine(indentedLine, "inet6 "); if (addressText != null) { if (IPAddress.TryParse(addressText, out var outValue)) adapter.IPv6 = outValue; } } // we have hit the end of the output in an indented line if (j >= outputLines.Length - 1) i = outputLines.Length; } // Retrieve the wireless LAN info var wlanInfo = wlanOutput.FirstOrDefault(x => x.StartsWith(adapter.Name)); if (wlanInfo != null) { adapter.IsWireless = true; var essidParts = wlanInfo.Split(new[] { EssidTag }, StringSplitOptions.RemoveEmptyEntries); if (essidParts.Length >= 2) { adapter.AccessPointName = essidParts[1].Replace("\"", string.Empty).Trim(); } } // Add the current adapter to the result result.Add(adapter); } return result.OrderBy(x => x.Name).ToList(); } /// /// Retrieves current wireless connected network name. /// /// The connected network name. public string GetWirelessNetworkName() => ProcessRunner.GetProcessOutputAsync("iwgetid", "-r").Result; /// /// Parses the output tag from the given line. /// /// The indented line. /// Name of the tag. /// The value after the tag identifier private static string ParseOutputTagFromLine(string indentedLine, string tagName) { if (indentedLine.IndexOf(tagName) < 0) return null; var startIndex = indentedLine.IndexOf(tagName) + tagName.Length; var builder = new StringBuilder(1024); for (var c = startIndex; c < indentedLine.Length; c++) { var currentChar = indentedLine[c]; if (!char.IsPunctuation(currentChar) && !char.IsLetterOrDigit(currentChar)) break; builder.Append(currentChar); } return builder.ToString(); } } }