commit 69ce69b7716fd78226437323dae965d48675cd1e Author: DevRas Date: Sun Sep 4 08:35:19 2022 +0900 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d4a6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin +obj \ No newline at end of file diff --git a/DeviceInfo.cs b/DeviceInfo.cs new file mode 100644 index 0000000..c463839 --- /dev/null +++ b/DeviceInfo.cs @@ -0,0 +1,36 @@ +using System.Net; +using System.Text; + +namespace NWService +{ + public class DeviceInfo + { + private IPAddress ipAddr; + private byte[] macAddr; + + public DeviceInfo(IPAddress iPAddress, byte[] macAddressBytes) + { + this.ipAddr = iPAddress; + this.macAddr = macAddressBytes; + } + + public IPAddress GetIPAddress() + { + return ipAddr; + } + + public string GetMacAddress(char separate = ':') + { + StringBuilder sb = new StringBuilder(18); + foreach (byte b in this.macAddr) { + if (sb.Length > 0) + { + sb.Append(separate); + } + sb.Append(string.Format("{0:X2}", b)); + } + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/DicSerialize.cs b/DicSerialize.cs new file mode 100644 index 0000000..d0d365f --- /dev/null +++ b/DicSerialize.cs @@ -0,0 +1,104 @@ +using System.Xml.Serialization; +using System.Text; +using System.Text.Encodings; + +namespace NWService +{ + public class DicSerialize + { + /// + /// シリアル化できる、KeyValuePairに代わる構造体 + /// + /// Keyの型 + /// Valueの型 + [Serializable] + public struct KeyAndValue + { + public TKey Key; + public TValue Value; + + public KeyAndValue(KeyValuePair pair) + { + Key = pair.Key; + Value = pair.Value; + } + } + + /// + /// DictionaryをKeyAndValueのListに変換する + /// + /// Dictionaryのキーの型 + /// Dictionaryの値の型 + /// 変換するDictionary + /// 変換されたKeyAndValueのList + public static List> ConvertDictionaryToList(Dictionary dic) + { + List> lst = new List>(); + foreach (KeyValuePair pair in dic) + { + lst.Add(new KeyAndValue(pair)); + } + + return lst; + } + + /// + /// KeyAndValueのListをDictionaryに変換する + /// + /// KeyAndValueのKeyの型 + /// KeyAndValueのValueの型 + /// 変換するKeyAndValueのList + /// 変換されたDictionary + public static Dictionary ConvertListToDictionary(List> lst) + { + Dictionary dic = new Dictionary(); + foreach (KeyAndValue pair in lst) + { + dic.Add(pair.Key, pair.Value); + } + return dic; + } + + /// + /// DictionaryをXMLファイルに保存する + /// + /// Dictionaryのキーの型 + /// Dictionaryの値の型 + /// 保存先のXMLファイル名 + /// 保存するDictionary + public static void XmlSerialize(string fileName, Dictionary dic) + { + //シリアル化できる型に変換 + List> obj = ConvertDictionaryToList(dic); + + //XMLファイルに保存 + XmlSerializer serializer = new XmlSerializer(typeof(List>)); + StreamWriter sw = + new StreamWriter(fileName, false, new UTF8Encoding(false)); + serializer.Serialize(sw, obj); + sw.Close(); + } + + /// + /// シリアル化されたXMLファイルをDictionaryに復元する + /// + /// Dictionaryのキーの型 + /// Dictionaryの値の型 + /// 復元するXMLファイル名 + /// 復元されたDictionary + public static Dictionary XmlDeserialize( + string fileName) + { + //XMLファイルから復元 + XmlSerializer serializer = new XmlSerializer(typeof(List>)); + StreamReader sr = new StreamReader(fileName, new UTF8Encoding(false)); + List> obj = + (List>)serializer.Deserialize(sr); + sr.Close(); + + //Dictionaryに戻す + Dictionary dic = ConvertListToDictionary(obj); + return dic; + } + } +} \ No newline at end of file diff --git a/LanguageXml.cs b/LanguageXml.cs new file mode 100644 index 0000000..0e831a3 --- /dev/null +++ b/LanguageXml.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace NWService +{ + public class LanguageXml + { + public LanguageXml() + { + this.messageMaps = new Dictionary(); + } + + public Dictionary messageMaps = new Dictionary(); + public string GetMessage(string key, string defaultMessage) + { + if (messageMaps.ContainsKey(key)) + { + return messageMaps[key]; + } + else + { + return defaultMessage; + } + } + } +} \ No newline at end of file diff --git a/LanguageXmlLoader.cs b/LanguageXmlLoader.cs new file mode 100644 index 0000000..77a2187 --- /dev/null +++ b/LanguageXmlLoader.cs @@ -0,0 +1,47 @@ +using System.Xml; +using System.Xml.Serialization; +using System.IO; + +namespace NWService +{ + public class LanguageXmlLoader + { + public static string GetPath() + { + return Path.Combine(Directory.GetCurrentDirectory(), "language.xml"); + } + + public static LanguageXml Load() + { + if (!File.Exists(GetPath())) + { + return default; + } + + try + { + Dictionary dic = DicSerialize.XmlDeserialize(GetPath()); + return new LanguageXml() + { + messageMaps = dic + }; + } + catch(Exception e) + { + Console.WriteLine(e); + return default; + } + } + public static void Save(LanguageXml xml) + { + try + { + DicSerialize.XmlSerialize(GetPath(), xml.messageMaps); + } + catch(Exception e) + { + Console.WriteLine(e); + } + } + } +} diff --git a/NetworkService.cs b/NetworkService.cs new file mode 100644 index 0000000..a62431d --- /dev/null +++ b/NetworkService.cs @@ -0,0 +1,131 @@ +using System.Net; +using System.Net.NetworkInformation; +using System.Runtime.InteropServices; + +namespace NWService +{ + public class NetworkService + { + [DllImport("iphlpapi.dll", ExactSpelling = true)] + private static extern int SendARP(int dstIp, int srcIp, byte[] mac, ref int macLen); + + public static PingReply Ping(IPAddress iPAddress) + { + using (Ping ping = new Ping()) + { + PingReply reply = ping.Send(iPAddress); + return reply; + } + } + + public static List SearchByPing(IPAddress targetIPv4) + { + int workThreadsMin; + int ioThreadsMin; + + ThreadPool.GetMinThreads(out workThreadsMin, out ioThreadsMin); + ThreadPool.SetMinThreads(260, ioThreadsMin); + + List avaIPs = new List(); + List allTasks = new List(); + + for (int i = 1 ; i <= 254 ; i++) + { + int hostPart = i; + allTasks.Add(Task.Run(() => { + List separateIP = targetIPv4.ToString().Split('.').ToList(); + separateIP.RemoveAt(3); + separateIP.Add(hostPart.ToString()); + string networkPart = string.Join(".", separateIP); + PingReply reply = Ping(IPAddress.Parse(networkPart)); + if (reply != null && reply.Status == IPStatus.Success) + { + avaIPs.Add(reply); + } + })); + } + + Task t = Task.WhenAll(allTasks); + try + { + t.Wait(); + } + catch(Exception error) + { + Console.WriteLine(error); + } + + return avaIPs; + } + + public static List SearchARP(IPAddress targetIPv4) + { + int workThreadsMin; + int ioThreadsMin; + + ThreadPool.GetMinThreads(out workThreadsMin, out ioThreadsMin); + ThreadPool.SetMinThreads(260, ioThreadsMin); + + List avaIPs = new List(); + + List allTasks = new List(); + for (int i = 1 ; i <= 254; i++) + { + int hostPart = i; + allTasks.Add(Task.Run(() => { + List separateIP = targetIPv4.ToString().Split('.').ToList(); + separateIP.RemoveAt(3); + separateIP.Add(hostPart.ToString()); + string networkPart = string.Join(".", separateIP); + + int IPBytes = BitConverter.ToInt32(IPAddress.Parse(networkPart).GetAddressBytes(), 0); + byte[] macAddressPointer = new byte[6]; + int physicalAddressLength = macAddressPointer.Length; + + // ARP + int ret = SendARP(IPBytes, 0, macAddressPointer, ref physicalAddressLength); + if (ret == 0) + { + DeviceInfo info = new DeviceInfo(IPAddress.Parse(networkPart), macAddressPointer); + avaIPs.Add(info); + } + })); + } + + Task t = Task.WhenAll(allTasks); + try + { + t.Wait(); + } + catch(Exception error) + { + Console.WriteLine(error); + } + + return avaIPs; + } + + public static IEnumerable GetLocalIPv4() + { + List list = new List(); + NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); + foreach (NetworkInterface nic in nics) + { + if (nic.OperationalStatus == OperationalStatus.Up + && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback + && nic.NetworkInterfaceType != NetworkInterfaceType.Tunnel) + { + UnicastIPAddressInformationCollection unicast = nic.GetIPProperties().UnicastAddresses; + foreach (UnicastIPAddressInformation ip in unicast) + { + if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { + list.Add(ip.Address); + } + } + } + } + return list; + } + } +} \ No newline at end of file diff --git a/NetworkService.csproj b/NetworkService.csproj new file mode 100644 index 0000000..5eb1e84 --- /dev/null +++ b/NetworkService.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + disable + + + diff --git a/OptionConsole.cs b/OptionConsole.cs new file mode 100644 index 0000000..39a57af --- /dev/null +++ b/OptionConsole.cs @@ -0,0 +1,74 @@ +using System.Net; + +namespace NWService +{ + public class OptionConsole + { + public static ServiceMode GetServiceMode(LanguageXml language) + { + var arry = Enum.GetValues(typeof(ServiceMode)); + for (int i = 0 ; i < arry.Length ; i++) + { + Console.WriteLine("[{0}]: {1}", i, language.GetMessage(arry.GetValue(i).ToString(), arry.GetValue(i).ToString())); + } + + Console.Write("{0} ", language.GetMessage("select_service_mode", "Please type service mode:")); + string tInput = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(tInput) || !int.TryParse(tInput, out int mode)) + { + throw new ArgumentException(string.Format("{0}", language.GetMessage("exception_invalid_input_mode", "invalid input mode."))); + } + + if (mode < 0 || mode > arry.Length) + { + throw new ArgumentException(string.Format("{0}", language.GetMessage("exception_invalid_input_mode", "invalid input mode."))); + } + + return (ServiceMode) mode; + } + + public static IPAddress GetIPAddress(List localIPv4, LanguageXml language) + { + for (int i = 0 ; i < localIPv4.Count(); i++) + { + Console.WriteLine("#{0}: {1}", i + 1, localIPv4[i]); + } + + Console.WriteLine("[0]: {0}", language.GetMessage("select_target_ipaddr", "Type target IPAddress.")); + Console.WriteLine("[1-]: {0}", language.GetMessage("select_local_ipaddr", "Select local IPAddress.")); + Console.Write("{0} ", language.GetMessage("type_mode", "Please type select mode:")); + + string tMode = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(tMode)) + { + throw new ArgumentException(string.Format("{0}", language.GetMessage("exception_invalid_input_mode", "invalid input mode."))); + } + + if (!int.TryParse(tMode, out int mode)) + { + throw new ArgumentException(string.Format("{0}", language.GetMessage("exception_invalid_input_mode", "invalid input mode."))); + } + + if (mode == 0) + { + Console.Write("{0} ", language.GetMessage("type_target_ipaddr", "Please type target IPAddress:")); + string tmpAddr = Console.ReadLine(); + if (!IPAddress.TryParse(tmpAddr, out IPAddress addr)) + { + throw new ArgumentException(string.Format("{0}", language.GetMessage("exception_invalid_input_ipaddr", "invalid input IPAddress."))); + } + + return addr; + } + + mode = mode - 1; + + if (mode < 0 || mode > localIPv4.Count()) + { + throw new ArgumentException(string.Format("{0}", language.GetMessage("exception_invalid_input_mode", "invalid input mode."))); + } + + return localIPv4[mode]; + } + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..2cfad68 --- /dev/null +++ b/Program.cs @@ -0,0 +1,135 @@ +using System.Net; +using System.Net.NetworkInformation; + +namespace NWService +{ + public class Program + { + public static void Main() + { + try + { + LanguageXml language = LanguageXmlLoader.Load(); + + ServiceMode mode = OptionConsole.GetServiceMode(language); + + List localIPv4 = NetworkService.GetLocalIPv4().ToList(); + IPAddress targetIPv4 = OptionConsole.GetIPAddress(localIPv4, language); + + switch(mode) + { + case ServiceMode.SEARCH_ARP: + Search_ARP(targetIPv4, language); + break; + case ServiceMode.SEARCH_PING: + Search_Ping(targetIPv4, language); + break; + default: + Console.WriteLine("unknown mode..."); + break; + } + } + catch(Exception error) + { + Console.WriteLine(error.Message); + } + } + + public static void Search_ARP(IPAddress targetIPv4, LanguageXml language) + { + try + { + List findList = new List(); + Task task = Task.Run(() => { + findList.AddRange(NetworkService.SearchARP(targetIPv4)); + + Thread.Sleep(10); + }); + + string[] frames = {"\","|","/"}; + int frameCount = 0; + (int cpL, int cpT) = Console.GetCursorPosition(); + while (!task.IsCompleted) + { + Console.SetCursorPosition(cpL, cpT); + frameCount++; + int targetFrameNumber = frameCount%frames.Length; + Console.WriteLine("{0} {1}", frames[targetFrameNumber], language.GetMessage("searching", "searching...")); + Thread.Sleep(100); + } + + Console.SetCursorPosition(cpL, cpT); + Console.WriteLine(" "); + + if (findList.Count() == 0) + { + Console.WriteLine("{0}", language.GetMessage("empty_find_list", "Not found...")); + } + else + { + Console.WriteLine(string.Format(language.GetMessage("found_devices","Found {0} Devices."), findList.Count())); + + foreach (DeviceInfo deviceInfo in findList) + { + Console.WriteLine("――――――――――――――――――――――――――――"); + Console.WriteLine("IP: {0}", deviceInfo.GetIPAddress().ToString()); + Console.WriteLine("Mac: {0}", deviceInfo.GetMacAddress()); + } + } + } + catch(Exception e) + { + Console.WriteLine(e); + } + } + + public static void Search_Ping(IPAddress targetIPv4, LanguageXml language) + { + try + { + List findList = new List(); + Task task = Task.Run(() => { + findList.AddRange(NetworkService.SearchByPing(targetIPv4)); + Thread.Sleep(10); + }); + + string[] frames = {"\","|","/"}; + int frameCount = 0; + (int cpL, int cpT) = Console.GetCursorPosition(); + while (!task.IsCompleted) + { + Console.SetCursorPosition(cpL, cpT); + frameCount++; + int targetFrameNumber = frameCount%frames.Length; + Console.WriteLine("{0} {1}", frames[targetFrameNumber], language.GetMessage("searching", "searching...")); + Thread.Sleep(100); + } + + Console.SetCursorPosition(cpL, cpT); + Console.WriteLine(" "); + + if (findList.Count() == 0) + { + Console.WriteLine("{0}", language.GetMessage("empty_find_list", "Not found...")); + } + else + { + Console.WriteLine(string.Format(language.GetMessage("found_devices","Found {0} Devices."), findList.Count())); + + foreach (var reply in findList) + { + Console.WriteLine("――――――――――――――――――――――――――――"); + Console.WriteLine("IP: {0}", reply.Address.ToString()); + Console.WriteLine("Time: {0}ms", reply.RoundtripTime); + Console.WriteLine("Status: {0}", reply.Status); + Console.WriteLine("Ttl: {0}", reply.Options.Ttl); + } + } + } + catch(Exception e) + { + Console.WriteLine(e); + } + } + } +} \ No newline at end of file diff --git a/ServiceMode.cs b/ServiceMode.cs new file mode 100644 index 0000000..ad1769c --- /dev/null +++ b/ServiceMode.cs @@ -0,0 +1,8 @@ +namespace NWService +{ + public enum ServiceMode + { + SEARCH_ARP, + SEARCH_PING, + } +} \ No newline at end of file diff --git a/language.xml b/language.xml new file mode 100644 index 0000000..68b175a --- /dev/null +++ b/language.xml @@ -0,0 +1,47 @@ + + + + SEARCH_ARP + 検索 [ARP] + + + SEARCH_PING + 検索 [PING] + + + searching + 検索中 + + + empty_find_list + IPアドレスが見つかりませんでした。 + + + select_target_ipaddr + 対象のIPアドレスを入力する + + + select_local_ipaddr + ローカルIPアドレスから選択する + + + type_target_ipaddr + 対象のIPアドレスを入力してください: + + + type_mode + モードを入力してください: + + + exception_invalid_input_mode + 入力されたモードが正しくありません。 + + + exception_invalid_input_ipaddr + 入力されたIPアドレスが正しくありません。 + + + version + 1.0.0 + + \ No newline at end of file