aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Guillemard <thog@protonmail.com>2018-10-10 01:15:07 +0200
committerAc_K <Acoustik666@gmail.com>2018-10-10 01:15:07 +0200
commitb2063be59747e65434b3b99a379566d640f05995 (patch)
treed089ab193fc1e25989d84e5b8649ac96d663d382
parentd5c0de8362941f957846a71d09a40a6eb77a22fa (diff)
Implement IResolver (#442)
* Implement IResolver - GetAddrInfo & GetNameInfo still need to be implemented. * Address comments * Use MakeError
-rw-r--r--Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs22
-rw-r--r--Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs380
-rw-r--r--Ryujinx.HLE/HOS/Services/Sfdnsres/NetDBError.cs13
3 files changed, 414 insertions, 1 deletions
diff --git a/Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs b/Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs
new file mode 100644
index 00000000..65d54577
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs
@@ -0,0 +1,22 @@
+namespace Ryujinx.HLE.HOS.Services.Sfdnsres
+{
+ enum GaiError
+ {
+ Success,
+ AddressFamily,
+ Again,
+ BadFlags,
+ Fail,
+ Family,
+ Memory,
+ NoData,
+ NoName,
+ Service,
+ SocketType,
+ System,
+ BadHints,
+ Protocol,
+ Overflow,
+ Max,
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs b/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs
index 26dbedf4..0ca43eda 100644
--- a/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs
+++ b/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs
@@ -1,5 +1,11 @@
using Ryujinx.HLE.HOS.Ipc;
+using System;
using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+
+using static Ryujinx.HLE.HOS.ErrorCode;
namespace Ryujinx.HLE.HOS.Services.Sfdnsres
{
@@ -13,8 +19,380 @@ namespace Ryujinx.HLE.HOS.Services.Sfdnsres
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
- //...
+ { 0, SetDnsAddressesPrivate },
+ { 1, GetDnsAddressesPrivate },
+ { 2, GetHostByName },
+ { 3, GetHostByAddress },
+ { 4, GetHostStringError },
+ { 5, GetGaiStringError },
+ { 8, RequestCancelHandle },
+ { 9, CancelSocketCall },
+ { 11, ClearDnsAddresses },
};
}
+
+ private long SerializeHostEnt(ServiceCtx Context, IPHostEntry HostEntry, List<IPAddress> Addresses = null)
+ {
+ long OriginalBufferPosition = Context.Request.ReceiveBuff[0].Position;
+ long BufferPosition = OriginalBufferPosition;
+ long BufferSize = Context.Request.ReceiveBuff[0].Size;
+
+ string HostName = HostEntry.HostName + '\0';
+
+ // h_name
+ Context.Memory.WriteBytes(BufferPosition, Encoding.ASCII.GetBytes(HostName));
+ BufferPosition += HostName.Length;
+
+ // h_aliases list size
+ Context.Memory.WriteInt32(BufferPosition, IPAddress.HostToNetworkOrder(HostEntry.Aliases.Length));
+ BufferPosition += 4;
+
+ // Actual aliases
+ foreach (string Alias in HostEntry.Aliases)
+ {
+ Context.Memory.WriteBytes(BufferPosition, Encoding.ASCII.GetBytes(Alias + '\0'));
+ BufferPosition += Alias.Length + 1;
+ }
+
+ // h_addrtype but it's a short (also only support IPv4)
+ Context.Memory.WriteInt16(BufferPosition, IPAddress.HostToNetworkOrder((short)2));
+ BufferPosition += 2;
+
+ // h_length but it's a short
+ Context.Memory.WriteInt16(BufferPosition, IPAddress.HostToNetworkOrder((short)4));
+ BufferPosition += 2;
+
+ // Ip address count, we can only support ipv4 (blame Nintendo)
+ Context.Memory.WriteInt32(BufferPosition, Addresses != null ? IPAddress.HostToNetworkOrder(Addresses.Count) : 0);
+ BufferPosition += 4;
+
+ if (Addresses != null)
+ {
+ foreach (IPAddress Ip in Addresses)
+ {
+ Context.Memory.WriteInt32(BufferPosition, IPAddress.HostToNetworkOrder(BitConverter.ToInt32(Ip.GetAddressBytes(), 0)));
+ BufferPosition += 4;
+ }
+ }
+
+ return BufferPosition - OriginalBufferPosition;
+ }
+
+ private string GetGaiStringErrorFromErrorCode(GaiError ErrorCode)
+ {
+ if (ErrorCode > GaiError.Max)
+ {
+ ErrorCode = GaiError.Max;
+ }
+
+ switch (ErrorCode)
+ {
+ case GaiError.AddressFamily:
+ return "Address family for hostname not supported";
+ case GaiError.Again:
+ return "Temporary failure in name resolution";
+ case GaiError.BadFlags:
+ return "Invalid value for ai_flags";
+ case GaiError.Fail:
+ return "Non-recoverable failure in name resolution";
+ case GaiError.Family:
+ return "ai_family not supported";
+ case GaiError.Memory:
+ return "Memory allocation failure";
+ case GaiError.NoData:
+ return "No address associated with hostname";
+ case GaiError.NoName:
+ return "hostname nor servname provided, or not known";
+ case GaiError.Service:
+ return "servname not supported for ai_socktype";
+ case GaiError.SocketType:
+ return "ai_socktype not supported";
+ case GaiError.System:
+ return "System error returned in errno";
+ case GaiError.BadHints:
+ return "Invalid value for hints";
+ case GaiError.Protocol:
+ return "Resolved protocol is unknown";
+ case GaiError.Overflow:
+ return "Argument buffer overflow";
+ case GaiError.Max:
+ return "Unknown error";
+ default:
+ return "Success";
+ }
+ }
+
+ private string GetHostStringErrorFromErrorCode(NetDBError ErrorCode)
+ {
+ if (ErrorCode <= NetDBError.Internal)
+ {
+ return "Resolver internal error";
+ }
+
+ switch (ErrorCode)
+ {
+ case NetDBError.Success:
+ return "Resolver Error 0 (no error)";
+ case NetDBError.HostNotFound:
+ return "Unknown host";
+ case NetDBError.TryAgain:
+ return "Host name lookup failure";
+ case NetDBError.NoRecovery:
+ return "Unknown server error";
+ case NetDBError.NoData:
+ return "No address associated with name";
+ default:
+ return "Unknown resolver error";
+ }
+ }
+
+ private List<IPAddress> GetIPV4Addresses(IPHostEntry HostEntry)
+ {
+ List<IPAddress> Result = new List<IPAddress>();
+ foreach (IPAddress Ip in HostEntry.AddressList)
+ {
+ if (Ip.AddressFamily == AddressFamily.InterNetwork)
+ Result.Add(Ip);
+ }
+ return Result;
+ }
+
+ // SetDnsAddressesPrivate(u32, buffer<unknown, 5, 0>)
+ public long SetDnsAddressesPrivate(ServiceCtx Context)
+ {
+ uint Unknown0 = Context.RequestData.ReadUInt32();
+ long BufferPosition = Context.Request.SendBuff[0].Position;
+ long BufferSize = Context.Request.SendBuff[0].Size;
+
+ // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake completeness.
+ Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}");
+
+ return MakeError(ErrorModule.Os, 1023);
+ }
+
+ // GetDnsAddressPrivate(u32) -> buffer<unknown, 6, 0>
+ public long GetDnsAddressesPrivate(ServiceCtx Context)
+ {
+ uint Unknown0 = Context.RequestData.ReadUInt32();
+
+ // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake completeness.
+ Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}");
+
+ return MakeError(ErrorModule.Os, 1023);
+ }
+
+ // GetHostByName(u8, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>)
+ public long GetHostByName(ServiceCtx Context)
+ {
+ byte[] RawName = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position, Context.Request.SendBuff[0].Size);
+ string Name = Encoding.ASCII.GetString(RawName).TrimEnd('\0');
+
+ // TODO: use params
+ bool EnableNsdResolve = Context.RequestData.ReadInt32() == 1;
+ int TimeOut = Context.RequestData.ReadInt32();
+ ulong PidPlaceholder = Context.RequestData.ReadUInt64();
+
+ IPHostEntry HostEntry = null;
+
+ NetDBError NetDBErrorCode = NetDBError.Success;
+ GaiError Errno = GaiError.Overflow;
+ long SerializedSize = 0;
+
+ if (Name.Length <= 255)
+ {
+ try
+ {
+ HostEntry = Dns.GetHostEntry(Name);
+ }
+ catch (SocketException Exception)
+ {
+ NetDBErrorCode = NetDBError.Internal;
+
+ if (Exception.ErrorCode == 11001)
+ {
+ NetDBErrorCode = NetDBError.HostNotFound;
+ Errno = GaiError.NoData;
+ }
+ else if (Exception.ErrorCode == 11002)
+ {
+ NetDBErrorCode = NetDBError.TryAgain;
+ }
+ else if (Exception.ErrorCode == 11003)
+ {
+ NetDBErrorCode = NetDBError.NoRecovery;
+ }
+ else if (Exception.ErrorCode == 11004)
+ {
+ NetDBErrorCode = NetDBError.NoData;
+ }
+ else if (Exception.ErrorCode == 10060)
+ {
+ Errno = GaiError.Again;
+ }
+ }
+ }
+ else
+ {
+ NetDBErrorCode = NetDBError.HostNotFound;
+ }
+
+ if (HostEntry != null)
+ {
+ Errno = GaiError.Success;
+
+ List<IPAddress> Addresses = GetIPV4Addresses(HostEntry);
+
+ if (Addresses.Count == 0)
+ {
+ Errno = GaiError.NoData;
+ NetDBErrorCode = NetDBError.NoAddress;
+ }
+ else
+ {
+ SerializedSize = SerializeHostEnt(Context, HostEntry, Addresses);
+ }
+ }
+
+ Context.ResponseData.Write((int)NetDBErrorCode);
+ Context.ResponseData.Write((int)Errno);
+ Context.ResponseData.Write(SerializedSize);
+
+ return 0;
+ }
+
+ // GetHostByAddr(u32, u32, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>)
+ public long GetHostByAddress(ServiceCtx Context)
+ {
+ byte[] RawIp = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position, Context.Request.SendBuff[0].Size);
+
+ // TODO: use params
+ uint SocketLength = Context.RequestData.ReadUInt32();
+ uint Type = Context.RequestData.ReadUInt32();
+ int TimeOut = Context.RequestData.ReadInt32();
+ ulong PidPlaceholder = Context.RequestData.ReadUInt64();
+
+ IPHostEntry HostEntry = null;
+
+ NetDBError NetDBErrorCode = NetDBError.Success;
+ GaiError Errno = GaiError.AddressFamily;
+ long SerializedSize = 0;
+
+ if (RawIp.Length == 4)
+ {
+ try
+ {
+ IPAddress Address = new IPAddress(RawIp);
+
+ HostEntry = Dns.GetHostEntry(Address);
+ }
+ catch (SocketException Exception)
+ {
+ NetDBErrorCode = NetDBError.Internal;
+ if (Exception.ErrorCode == 11001)
+ {
+ NetDBErrorCode = NetDBError.HostNotFound;
+ Errno = GaiError.NoData;
+ }
+ else if (Exception.ErrorCode == 11002)
+ {
+ NetDBErrorCode = NetDBError.TryAgain;
+ }
+ else if (Exception.ErrorCode == 11003)
+ {
+ NetDBErrorCode = NetDBError.NoRecovery;
+ }
+ else if (Exception.ErrorCode == 11004)
+ {
+ NetDBErrorCode = NetDBError.NoData;
+ }
+ else if (Exception.ErrorCode == 10060)
+ {
+ Errno = GaiError.Again;
+ }
+ }
+ }
+ else
+ {
+ NetDBErrorCode = NetDBError.NoAddress;
+ }
+
+ if (HostEntry != null)
+ {
+ Errno = GaiError.Success;
+ SerializedSize = SerializeHostEnt(Context, HostEntry, GetIPV4Addresses(HostEntry));
+ }
+
+ Context.ResponseData.Write((int)NetDBErrorCode);
+ Context.ResponseData.Write((int)Errno);
+ Context.ResponseData.Write(SerializedSize);
+
+ return 0;
+ }
+
+ // GetHostStringError(u32) -> buffer<unknown, 6, 0>
+ public long GetHostStringError(ServiceCtx Context)
+ {
+ long ResultCode = MakeError(ErrorModule.Os, 1023);
+ NetDBError ErrorCode = (NetDBError)Context.RequestData.ReadInt32();
+ string ErrorString = GetHostStringErrorFromErrorCode(ErrorCode);
+
+ if (ErrorString.Length + 1 <= Context.Request.ReceiveBuff[0].Size)
+ {
+ ResultCode = 0;
+ Context.Memory.WriteBytes(Context.Request.ReceiveBuff[0].Position, Encoding.ASCII.GetBytes(ErrorString + '\0'));
+ }
+
+ return ResultCode;
+ }
+
+ // GetGaiStringError(u32) -> buffer<unknown, 6, 0>
+ public long GetGaiStringError(ServiceCtx Context)
+ {
+ long ResultCode = MakeError(ErrorModule.Os, 1023);
+ GaiError ErrorCode = (GaiError)Context.RequestData.ReadInt32();
+ string ErrorString = GetGaiStringErrorFromErrorCode(ErrorCode);
+
+ if (ErrorString.Length + 1 <= Context.Request.ReceiveBuff[0].Size)
+ {
+ ResultCode = 0;
+ Context.Memory.WriteBytes(Context.Request.ReceiveBuff[0].Position, Encoding.ASCII.GetBytes(ErrorString + '\0'));
+ }
+
+ return ResultCode;
+ }
+
+ // RequestCancelHandle(u64, pid) -> u32
+ public long RequestCancelHandle(ServiceCtx Context)
+ {
+ ulong Unknown0 = Context.RequestData.ReadUInt64();
+
+ Context.ResponseData.Write(0);
+
+ Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}");
+
+ return 0;
+ }
+
+ // CancelSocketCall(u32, u64, pid)
+ public long CancelSocketCall(ServiceCtx Context)
+ {
+ uint Unknown0 = Context.RequestData.ReadUInt32();
+ ulong Unknown1 = Context.RequestData.ReadUInt64();
+
+ Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0} - " +
+ $"Unknown1: {Unknown1}");
+
+ return 0;
+ }
+
+ // ClearDnsAddresses(u32)
+ public long ClearDnsAddresses(ServiceCtx Context)
+ {
+ uint Unknown0 = Context.RequestData.ReadUInt32();
+
+ Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}");
+
+ return 0;
+ }
}
}
diff --git a/Ryujinx.HLE/HOS/Services/Sfdnsres/NetDBError.cs b/Ryujinx.HLE/HOS/Services/Sfdnsres/NetDBError.cs
new file mode 100644
index 00000000..6c1b7825
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Sfdnsres/NetDBError.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.HLE.HOS.Services.Sfdnsres
+{
+ enum NetDBError
+ {
+ Internal = -1,
+ Success,
+ HostNotFound,
+ TryAgain,
+ NoRecovery,
+ NoData,
+ NoAddress = NoData,
+ }
+}