nifm/ssl: Implement GetCurrentNetworkProfile and stub Ssl Service (#2186)

* nifm/ssl: Implement GetCurrentNetworkProfile and stub Ssl Service

* remove InterfaceVersion
This commit is contained in:
Ac_K 2021-04-13 03:04:18 +02:00 committed by GitHub
parent 73881fad19
commit b662a26c7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 375 additions and 56 deletions

View file

@ -2,8 +2,11 @@ using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService; using Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService;
using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types; using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types;
using Ryujinx.HLE.Utilities;
using System; using System;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Runtime.CompilerServices;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
{ {
@ -51,6 +54,38 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
return ResultCode.Success; return ResultCode.Success;
} }
[Command(5)]
// GetCurrentNetworkProfile() -> buffer<nn::nifm::detail::sf::NetworkProfileData, 0x1a, 0x17c>
public ResultCode GetCurrentNetworkProfile(ServiceCtx context)
{
long networkProfileDataPosition = context.Request.RecvListBuff[0].Position;
(IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface();
if (interfaceProperties == null || unicastAddress == null)
{
return ResultCode.NoInternetConnection;
}
Logger.Info?.Print(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\".");
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Unsafe.SizeOf<NetworkProfileData>());
NetworkProfileData networkProfile = new NetworkProfileData
{
Uuid = new UInt128(Guid.NewGuid().ToByteArray())
};
networkProfile.IpSettingData.IpAddressSetting = new IpAddressSetting(interfaceProperties, unicastAddress);
networkProfile.IpSettingData.DnsSetting = new DnsSetting(interfaceProperties);
Encoding.ASCII.GetBytes("RyujinxNetwork").CopyTo(networkProfile.Name.ToSpan());
context.Memory.Write((ulong)networkProfileDataPosition, networkProfile);
return ResultCode.Success;
}
[Command(12)] [Command(12)]
// GetCurrentIpAddress() -> nn::nifm::IpV4Address // GetCurrentIpAddress() -> nn::nifm::IpV4Address
public ResultCode GetCurrentIpAddress(ServiceCtx context) public ResultCode GetCurrentIpAddress(ServiceCtx context)
@ -75,7 +110,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
{ {
(IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(); (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface();
if (interfaceProperties == null) if (interfaceProperties == null || unicastAddress == null)
{ {
return ResultCode.NoInternetConnection; return ResultCode.NoInternetConnection;
} }
@ -138,11 +173,11 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
foreach (NetworkInterface adapter in interfaces) foreach (NetworkInterface adapter in interfaces)
{ {
// Ignore loopback and non IPv4 capable interface. // Ignore loopback and non IPv4 capable interface.
if (adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4)) if (targetProperties == null && adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4))
{ {
IPInterfaceProperties properties = adapter.GetIPProperties(); IPInterfaceProperties properties = adapter.GetIPProperties();
if (properties.GatewayAddresses.Count > 0 && properties.DnsAddresses.Count > 1) if (properties.GatewayAddresses.Count > 0 && properties.DnsAddresses.Count > 0)
{ {
foreach (UnicastIPAddressInformation info in properties.UnicastAddresses) foreach (UnicastIPAddressInformation info in properties.UnicastAddresses)
{ {
@ -156,12 +191,6 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
} }
} }
} }
// Found the target interface, stop here.
if (targetProperties != null)
{
break;
}
} }
} }

View file

@ -14,8 +14,17 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
public DnsSetting(IPInterfaceProperties interfaceProperties) public DnsSetting(IPInterfaceProperties interfaceProperties)
{ {
IsDynamicDnsEnabled = interfaceProperties.IsDynamicDnsEnabled; IsDynamicDnsEnabled = interfaceProperties.IsDynamicDnsEnabled;
PrimaryDns = new IpV4Address(interfaceProperties.DnsAddresses[0]);
SecondaryDns = new IpV4Address(interfaceProperties.DnsAddresses[1]); if (interfaceProperties.DnsAddresses.Count == 0)
{
PrimaryDns = new IpV4Address();
SecondaryDns = new IpV4Address();
}
else
{
PrimaryDns = new IpV4Address(interfaceProperties.DnsAddresses[0]);
SecondaryDns = new IpV4Address(interfaceProperties.DnsAddresses[interfaceProperties.DnsAddresses.Count > 1 ? 1 : 0]);
}
} }
} }
} }

View file

@ -0,0 +1,13 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
{
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xc2)]
struct IpSettingData
{
public IpAddressSetting IpAddressSetting;
public DnsSetting DnsSetting;
public ProxySetting ProxySetting;
public short Mtu;
}
}

View file

@ -0,0 +1,17 @@
using Ryujinx.Common.Memory;
using Ryujinx.HLE.Utilities;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
{
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x17C)]
struct NetworkProfileData
{
public IpSettingData IpSettingData;
public UInt128 Uuid;
public Array64<byte> Name;
public Array4<byte> Unknown;
public WirelessSettingData WirelessSettingData;
public byte Padding;
}
}

View file

@ -0,0 +1,27 @@
using LibHac.Common;
using Ryujinx.Common.Memory;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
{
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xaa)]
public struct ProxySetting
{
[MarshalAs(UnmanagedType.I1)]
public bool Enabled;
private byte _padding;
public short Port;
private NameStruct _name;
[MarshalAs(UnmanagedType.I1)]
public bool AutoAuthEnabled;
public Array32<byte> User;
public Array32<byte> Pass;
private byte _padding2;
[StructLayout(LayoutKind.Sequential, Size = 0x64)]
private struct NameStruct { }
public Span<byte> Name => SpanHelpers.AsSpan<NameStruct, byte>(ref _name);
}
}

View file

@ -0,0 +1,15 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
{
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x65)]
struct WirelessSettingData
{
public byte SsidLength;
public Array32<byte> Ssid;
public Array3<byte> Unknown;
public Array64<byte> Passphrase1;
public byte Passphrase2;
}
}

View file

@ -132,7 +132,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
// Resolve(buffer<unknown<0x100>, 0x15>) -> buffer<unknown<0x100>, 0x16> // Resolve(buffer<unknown<0x100>, 0x15>) -> buffer<unknown<0x100>, 0x16>
public ResultCode Resolve(ServiceCtx context) public ResultCode Resolve(ServiceCtx context)
{ {
(long outputPosition, long outputSize) = context.Request.GetBufferType0x22(); long outputPosition = context.Request.ReceiveBuff[0].Position;
long outputSize = context.Request.ReceiveBuff[0].Size;
ResultCode result = _fqdnResolver.ResolveEx(context, out ResultCode errorCode, out string resolvedAddress); ResultCode result = _fqdnResolver.ResolveEx(context, out ResultCode errorCode, out string resolvedAddress);
@ -147,7 +148,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
// ResolveEx(buffer<unknown<0x100>, 0x15>) -> (u32, buffer<unknown<0x100>, 0x16>) // ResolveEx(buffer<unknown<0x100>, 0x15>) -> (u32, buffer<unknown<0x100>, 0x16>)
public ResultCode ResolveEx(ServiceCtx context) public ResultCode ResolveEx(ServiceCtx context)
{ {
(long outputPosition, long outputSize) = context.Request.GetBufferType0x22(); long outputPosition = context.Request.ReceiveBuff[0].Position;
long outputSize = context.Request.ReceiveBuff[0].Size;
ResultCode result = _fqdnResolver.ResolveEx(context, out ResultCode errorCode, out string resolvedAddress); ResultCode result = _fqdnResolver.ResolveEx(context, out ResultCode errorCode, out string resolvedAddress);

View file

@ -73,30 +73,19 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager
return ResultCode.SettingsNotLoaded; return ResultCode.SettingsNotLoaded;
} }
switch (address) resolvedAddress = address switch
{ {
case "e97b8a9d672e4ce4845ec6947cd66ef6-sb-api.accounts.nintendo.com": // dp1 environment "e97b8a9d672e4ce4845ec6947cd66ef6-sb-api.accounts.nintendo.com" => "e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com", // dp1 environment
resolvedAddress = "e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com"; "api.accounts.nintendo.com" => "e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com", // dp1 environment
break; "e97b8a9d672e4ce4845ec6947cd66ef6-sb.accounts.nintendo.com" => "e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com", // lp1 environment
case "api.accounts.nintendo.com": // dp1 environment "accounts.nintendo.com" => "e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com", // lp1 environment
resolvedAddress = "e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com";
break;
case "e97b8a9d672e4ce4845ec6947cd66ef6-sb.accounts.nintendo.com": // lp1 environment
resolvedAddress = "e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com";
break;
case "accounts.nintendo.com": // lp1 environment
resolvedAddress = "e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com";
break;
/* /*
// TODO: Determine fields of the struct. // TODO: Determine fields of the struct.
case "": // + 0xEB8 || + 0x2BE8 this + 0xEB8 => this + 0xEB8 + 0x300
resolvedAddress = ""; // + 0xEB8 + 0x300 || + 0x2BE8 + 0x300 this + 0x2BE8 => this + 0x2BE8 + 0x300
break;
*/ */
default: _ => address,
resolvedAddress = address; };
break;
}
} }
else else
{ {
@ -108,7 +97,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager
public ResultCode ResolveEx(ServiceCtx context, out ResultCode resultCode, out string resolvedAddress) public ResultCode ResolveEx(ServiceCtx context, out ResultCode resultCode, out string resolvedAddress)
{ {
(long inputPosition, long inputSize) = context.Request.GetBufferType0x21(); long inputPosition = context.Request.SendBuff[0].Position;
long inputSize = context.Request.SendBuff[0].Size;
byte[] addressBuffer = new byte[inputSize]; byte[] addressBuffer = new byte[inputSize];

View file

@ -1,24 +1,27 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Ssl.SslService; using Ryujinx.HLE.HOS.Services.Ssl.SslService;
using Ryujinx.HLE.HOS.Services.Ssl.Types;
namespace Ryujinx.HLE.HOS.Services.Ssl namespace Ryujinx.HLE.HOS.Services.Ssl
{ {
[Service("ssl")] [Service("ssl")]
class ISslService : IpcService class ISslService : IpcService
{ {
// NOTE: The SSL service is used by games to connect it to various official online services, which we do not intend to support.
// In this case it is acceptable to stub all calls of the service.
public ISslService(ServiceCtx context) { } public ISslService(ServiceCtx context) { }
[Command(0)] [Command(0)]
// CreateContext(nn::ssl::sf::SslVersion, u64, pid) -> object<nn::ssl::sf::ISslContext> // CreateContext(nn::ssl::sf::SslVersion, u64, pid) -> object<nn::ssl::sf::ISslContext>
public ResultCode CreateContext(ServiceCtx context) public ResultCode CreateContext(ServiceCtx context)
{ {
int sslVersion = context.RequestData.ReadInt32(); SslVersion sslVersion = (SslVersion)context.RequestData.ReadUInt32();
long unknown = context.RequestData.ReadInt64(); ulong pidPlaceholder = context.RequestData.ReadUInt64();
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { sslVersion, unknown });
MakeObject(context, new ISslContext(context)); MakeObject(context, new ISslContext(context));
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { sslVersion });
return ResultCode.Success; return ResultCode.Success;
} }
@ -26,9 +29,10 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
// SetInterfaceVersion(u32) // SetInterfaceVersion(u32)
public ResultCode SetInterfaceVersion(ServiceCtx context) public ResultCode SetInterfaceVersion(ServiceCtx context)
{ {
int version = context.RequestData.ReadInt32(); // 1 = 3.0.0+, 2 = 5.0.0+, 3 = 6.0.0+
uint interfaceVersion = context.RequestData.ReadUInt32();
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { version }); Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { interfaceVersion });
return ResultCode.Success; return ResultCode.Success;
} }

View file

@ -0,0 +1,113 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Ssl.Types;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
{
class ISslConnection : IpcService
{
public ISslConnection() { }
[Command(0)]
// SetSocketDescriptor(u32) -> u32
public ResultCode SetSocketDescriptor(ServiceCtx context)
{
uint socketFd = context.RequestData.ReadUInt32();
uint duplicateSocketFd = 0;
context.ResponseData.Write(duplicateSocketFd);
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { socketFd });
return ResultCode.Success;
}
[Command(1)]
// SetHostName(buffer<bytes, 5>)
public ResultCode SetHostName(ServiceCtx context)
{
long hostNameDataPosition = context.Request.SendBuff[0].Position;
long hostNameDataSize = context.Request.SendBuff[0].Size;
byte[] hostNameData = new byte[hostNameDataSize];
context.Memory.Read((ulong)hostNameDataPosition, hostNameData);
string hostName = Encoding.ASCII.GetString(hostNameData).Trim('\0');
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { hostName });
return ResultCode.Success;
}
[Command(2)]
// SetVerifyOption(nn::ssl::sf::VerifyOption)
public ResultCode SetVerifyOption(ServiceCtx context)
{
VerifyOption verifyOption = (VerifyOption)context.RequestData.ReadUInt32();
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { verifyOption });
return ResultCode.Success;
}
[Command(3)]
// SetIoMode(nn::ssl::sf::IoMode)
public ResultCode SetIoMode(ServiceCtx context)
{
IoMode ioMode = (IoMode)context.RequestData.ReadUInt32();
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { ioMode });
return ResultCode.Success;
}
[Command(8)]
// DoHandshake()
public ResultCode DoHandshake(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceSsl);
return ResultCode.Success;
}
[Command(11)]
// Write(buffer<bytes, 5>) -> u32
public ResultCode Write(ServiceCtx context)
{
long inputDataPosition = context.Request.SendBuff[0].Position;
long inputDataSize = context.Request.SendBuff[0].Size;
uint transferredSize = 0;
context.ResponseData.Write(transferredSize);
Logger.Stub?.PrintStub(LogClass.ServiceSsl);
return ResultCode.Success;
}
[Command(17)]
// SetSessionCacheMode(nn::ssl::sf::SessionCacheMode)
public ResultCode SetSessionCacheMode(ServiceCtx context)
{
SessionCacheMode sessionCacheMode = (SessionCacheMode)context.RequestData.ReadUInt32();
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { sessionCacheMode });
return ResultCode.Success;
}
[Command(22)]
// SetOption(b8, nn::ssl::sf::OptionType)
public ResultCode SetOption(ServiceCtx context)
{
bool optionEnabled = context.RequestData.ReadBoolean();
OptionType optionType = (OptionType)context.RequestData.ReadUInt32();
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { optionType, optionEnabled });
return ResultCode.Success;
}
}
}

View file

@ -1,27 +1,62 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System; using Ryujinx.HLE.HOS.Services.Ssl.Types;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Ssl.SslService namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
{ {
class ISslContext : IpcService class ISslContext : IpcService
{ {
private ulong _serverCertificateId;
private ulong _clientCertificateId;
public ISslContext(ServiceCtx context) { } public ISslContext(ServiceCtx context) { }
[Command(4)] [Command(2)]
// ImportServerPki(nn::ssl::sf::CertificateFormat certificateFormat, buffer<bytes, 5> certificate) -> u64 certificateId // CreateConnection() -> object<nn::ssl::sf::ISslConnection>
public ResultCode ImportServerPki(ServiceCtx context) public ResultCode CreateConnection(ServiceCtx context)
{ {
int certificateFormat = context.RequestData.ReadInt32(); MakeObject(context, new ISslConnection());
long certificateDataPosition = context.Request.SendBuff[0].Position;
long certificateDataSize = context.Request.SendBuff[0].Size;
ulong certificateId = 1;
context.ResponseData.Write(certificateId);
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { certificateFormat, certificateDataPosition, certificateDataSize });
return ResultCode.Success; return ResultCode.Success;
} }
[Command(4)]
// ImportServerPki(nn::ssl::sf::CertificateFormat certificateFormat, buffer<bytes, 5> certificate) -> u64 certificateId
public ResultCode ImportServerPki(ServiceCtx context)
{
CertificateFormat certificateFormat = (CertificateFormat)context.RequestData.ReadUInt32();
long certificateDataPosition = context.Request.SendBuff[0].Position;
long certificateDataSize = context.Request.SendBuff[0].Size;
context.ResponseData.Write(_serverCertificateId++);
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { certificateFormat });
return ResultCode.Success;
}
[Command(5)]
// ImportClientPki(buffer<bytes, 5> certificate, buffer<bytes, 5> ascii_password) -> u64 certificateId
public ResultCode ImportClientPki(ServiceCtx context)
{
long certificateDataPosition = context.Request.SendBuff[0].Position;
long certificateDataSize = context.Request.SendBuff[0].Size;
long asciiPasswordDataPosition = context.Request.SendBuff[1].Position;
long asciiPasswordDataSize = context.Request.SendBuff[1].Size;
byte[] asciiPasswordData = new byte[asciiPasswordDataSize];
context.Memory.Read((ulong)asciiPasswordDataPosition, asciiPasswordData);
string asciiPassword = Encoding.ASCII.GetString(asciiPasswordData).Trim('\0');
context.ResponseData.Write(_clientCertificateId++);
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { asciiPassword });
return ResultCode.Success;
}
} }
} }

View file

@ -0,0 +1,8 @@
namespace Ryujinx.HLE.HOS.Services.Ssl.Types
{
enum CertificateFormat : uint
{
Pem = 1,
Der = 2
}
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.HLE.HOS.Services.Ssl.Types
{
enum IoMode : uint
{
Blocking = 1,
NonBlocking = 2
}
}

View file

@ -0,0 +1,10 @@
namespace Ryujinx.HLE.HOS.Services.Ssl.Types
{
enum OptionType : uint
{
DoNotCloseSocket,
GetServerCertChain, // 3.0.0+
SkipDefaultVerify, // 5.0.0+
EnableAlpn // 9.0.0+
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.HLE.HOS.Services.Ssl.Types
{
enum SessionCacheMode : uint
{
None,
SessionId,
SessionTicket
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace Ryujinx.HLE.HOS.Services.Ssl.Types
{
[Flags]
enum SslVersion : uint
{
Auto = 1 << 0,
TlsV10 = 1 << 3,
TlsV11 = 1 << 4,
TlsV12 = 1 << 5,
TlsV13 = 1 << 6, // 11.0.0+
Auto2 = 1 << 24 // 11.0.0+
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace Ryujinx.HLE.HOS.Services.Ssl.Types
{
[Flags]
enum VerifyOption : uint
{
PeerCa = 1 << 0,
HostName = 1 << 1,
DateCheck = 1 << 2,
EvCertPartial = 1 << 3,
EvPolicyOid = 1 << 4, // 6.0.0+
EvCertFingerprint = 1 << 5 // 6.0.0+
}
}