From e9a37ca6a85346c05149deac916dc90de43ad240 Mon Sep 17 00:00:00 2001 From: Thog Date: Mon, 17 Feb 2020 16:28:41 +0100 Subject: [PATCH] Implement GetCurrentIpConfigInfo (#943) * Implement GetCurrentIpConfigInfo This is needed by Rocket League. Also fix GetCurrentIpConfigInfo to not return ipv6 addresses * Address Ac_K comment --- .../Nifm/StaticService/IGeneralService.cs | 74 +++++++++++++++++-- .../Nifm/StaticService/Types/DnsSetting.cs | 21 ++++++ .../StaticService/Types/IpAddressSetting.cs | 23 ++++++ .../Nifm/StaticService/Types/IpV4Address.cs | 24 ++++++ 4 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpV4Address.cs diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index c1642c3f..eaa9be49 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -3,10 +3,12 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService; using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types; using System; +using System.Diagnostics; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Runtime.CompilerServices; namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService { @@ -57,18 +59,35 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService // GetCurrentIpAddress() -> nn::nifm::IpV4Address public ResultCode GetCurrentIpAddress(ServiceCtx context) { - if (!NetworkInterface.GetIsNetworkAvailable()) + (_, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(); + + if (unicastAddress == null) { return ResultCode.NoInternetConnection; } - IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName()); + context.ResponseData.WriteStruct(new IpV4Address(unicastAddress.Address)); - IPAddress address = host.AddressList.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork); + Logger.PrintInfo(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\"."); - context.ResponseData.Write(BitConverter.ToUInt32(address.GetAddressBytes())); + return ResultCode.Success; + } - Logger.PrintInfo(LogClass.ServiceNifm, $"Console's local IP is \"{address}\"."); + [Command(15)] + // GetCurrentIpConfigInfo() -> (nn::nifm::IpAddressSetting, nn::nifm::DnsSetting) + public ResultCode GetCurrentIpConfigInfo(ServiceCtx context) + { + (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(); + + if (interfaceProperties == null) + { + return ResultCode.NoInternetConnection; + } + + Logger.PrintInfo(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\"."); + + context.ResponseData.WriteStruct(new IpAddressSetting(interfaceProperties, unicastAddress)); + context.ResponseData.WriteStruct(new DnsSetting(interfaceProperties)); return ResultCode.Success; } @@ -108,6 +127,51 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } + private (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface() + { + if (!NetworkInterface.GetIsNetworkAvailable()) + { + return (null, null); + } + + IPInterfaceProperties targetProperties = null; + UnicastIPAddressInformation targetAddressInfo = null; + + NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces(); + + foreach (NetworkInterface adapter in interfaces) + { + // Ignore loopback and non IPv4 capable interface. + if (adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4)) + { + IPInterfaceProperties properties = adapter.GetIPProperties(); + + if (properties.GatewayAddresses.Count > 0 && properties.DnsAddresses.Count > 1) + { + foreach (UnicastIPAddressInformation info in properties.UnicastAddresses) + { + // Only accept an IPv4 address + if (info.Address.GetAddressBytes().Length == 4) + { + targetProperties = properties; + targetAddressInfo = info; + + break; + } + } + } + + // Found the target interface, stop here. + if (targetProperties != null) + { + break; + } + } + } + + return (targetProperties, targetAddressInfo); + } + public void Dispose() { GeneralServiceManager.Remove(_generalServiceDetail.ClientId); diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs new file mode 100644 index 00000000..96f99dfa --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs @@ -0,0 +1,21 @@ +using System.Net.NetworkInformation; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types +{ + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 9)] + struct DnsSetting + { + [MarshalAs(UnmanagedType.U1)] + public bool IsDynamicDnsEnabled; + public IpV4Address PrimaryDns; + public IpV4Address SecondaryDns; + + public DnsSetting(IPInterfaceProperties interfaceProperties) + { + IsDynamicDnsEnabled = interfaceProperties.IsDynamicDnsEnabled; + PrimaryDns = new IpV4Address(interfaceProperties.DnsAddresses[0]); + SecondaryDns = new IpV4Address(interfaceProperties.DnsAddresses[1]); + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs new file mode 100644 index 00000000..50e6b01c --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpAddressSetting.cs @@ -0,0 +1,23 @@ +using System.Net.NetworkInformation; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types +{ + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xd)] + struct IpAddressSetting + { + [MarshalAs(UnmanagedType.U1)] + public bool IsDhcpEnabled; + public IpV4Address Address; + public IpV4Address IPv4Mask; + public IpV4Address GatewayAddress; + + public IpAddressSetting(IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastIPAddressInformation) + { + IsDhcpEnabled = interfaceProperties.DhcpServerAddresses.Count != 0; + Address = new IpV4Address(unicastIPAddressInformation.Address); + IPv4Mask = new IpV4Address(unicastIPAddressInformation.IPv4Mask); + GatewayAddress = new IpV4Address(interfaceProperties.GatewayAddresses[0].Address); + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpV4Address.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpV4Address.cs new file mode 100644 index 00000000..e5c2f39a --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpV4Address.cs @@ -0,0 +1,24 @@ +using System; +using System.Net; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types +{ + [StructLayout(LayoutKind.Sequential)] + struct IpV4Address + { + public uint Address; + + public IpV4Address(IPAddress address) + { + if (address == null) + { + Address = 0; + } + else + { + Address = BitConverter.ToUInt32(address.GetAddressBytes()); + } + } + } +}