aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE
diff options
context:
space:
mode:
authorAc_K <Acoustik666@gmail.com>2021-03-24 18:43:23 +0100
committerGitHub <noreply@github.com>2021-03-24 18:43:23 +0100
commite5741fae2d4c7baa63d71f7fb56ea7d0eac8fe36 (patch)
treede0e9fefb17912c05551a8892141b54288c34f01 /Ryujinx.HLE
parent69f8722e795b0d76b84efa4de52e199e9441fca7 (diff)
sfdnsres: Cleanup service and implements some calls (#2130)
* sfdnsres: Cleanup service and implements some calls This PR is a big cleanup to our current implementation of `sfdnsres` service. Additionnaly to that, some calls and fix have been done by @Thog (PRd with approval, thanks to her). - Implementation of `GetAddrInfoRequest` (Fixes #637). - Partial implementation of `GetHostByNameRequestWithOptions`, `GetHostByAddrRequestWithOptions` and `GetAddrInfoRequestWithOptions` (Fixes #642, Fixes #1233). A DNS Blacklist have been done by @riperiperi (which is currently used in LDN build, then that reduces code differences between the LDN build and master. Now a lot of games are playable or are blocked to the menu because it needs online service: Co-Authored-By: Mary <1760003+Thog@users.noreply.github.com> Co-Authored-By: riperiperi <6294155+riperiperi@users.noreply.github.com> * Addressed gdkchan's comments * IPAddress[] to IEnumerable * Nits Co-authored-by: Mary <1760003+Thog@users.noreply.github.com> Co-authored-by: riperiperi <6294155+riperiperi@users.noreply.github.com>
Diffstat (limited to 'Ryujinx.HLE')
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs606
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs28
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs29
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs37
-rw-r--r--Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs7
5 files changed, 492 insertions, 215 deletions
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs
index 2258ada3..d15a13a7 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs
@@ -1,8 +1,15 @@
using Ryujinx.Common.Logging;
+using Ryujinx.Cpu;
+using Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy;
+using Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types;
+using Ryujinx.Memory;
using System;
+using System.Buffers.Binary;
using System.Collections.Generic;
+using System.Linq;
using System.Net;
using System.Net.Sockets;
+using System.Runtime.CompilerServices;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
@@ -12,209 +19,250 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
{
public IResolver(ServiceCtx context) { }
- private long SerializeHostEnt(ServiceCtx context, IPHostEntry hostEntry, List<IPAddress> addresses = null)
+ [Command(0)]
+ // SetDnsAddressesPrivateRequest(u32, buffer<unknown, 5, 0>)
+ public ResultCode SetDnsAddressesPrivateRequest(ServiceCtx context)
{
- long originalBufferPosition = context.Request.ReceiveBuff[0].Position;
- long bufferPosition = originalBufferPosition;
- long bufferSize = context.Request.ReceiveBuff[0].Size;
+ uint cancelHandleRequest = context.RequestData.ReadUInt32();
+ long bufferPosition = context.Request.SendBuff[0].Position;
+ long bufferSize = context.Request.SendBuff[0].Size;
- string hostName = hostEntry.HostName + '\0';
+ // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake of completeness.
+ Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { cancelHandleRequest });
- // h_name
- context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(hostName));
- bufferPosition += hostName.Length;
+ return ResultCode.NotAllocated;
+ }
- // h_aliases list size
- context.Memory.Write((ulong)bufferPosition, IPAddress.HostToNetworkOrder(hostEntry.Aliases.Length));
- bufferPosition += 4;
+ [Command(1)]
+ // GetDnsAddressPrivateRequest(u32) -> buffer<unknown, 6, 0>
+ public ResultCode GetDnsAddressPrivateRequest(ServiceCtx context)
+ {
+ uint cancelHandleRequest = context.RequestData.ReadUInt32();
+ long bufferPosition = context.Request.ReceiveBuff[0].Position;
+ long bufferSize = context.Request.ReceiveBuff[0].Size;
- // Actual aliases
- foreach (string alias in hostEntry.Aliases)
- {
- context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(alias + '\0'));
- bufferPosition += alias.Length + 1;
- }
+ // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake of completeness.
+ Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { cancelHandleRequest });
- // h_addrtype but it's a short (also only support IPv4)
- context.Memory.Write((ulong)bufferPosition, IPAddress.HostToNetworkOrder((short)2));
- bufferPosition += 2;
+ return ResultCode.NotAllocated;
+ }
- // h_length but it's a short
- context.Memory.Write((ulong)bufferPosition, IPAddress.HostToNetworkOrder((short)4));
- bufferPosition += 2;
+ [Command(2)]
+ // GetHostByNameRequest(u8, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>)
+ public ResultCode GetHostByNameRequest(ServiceCtx context)
+ {
+ long inputBufferPosition = context.Request.SendBuff[0].Position;
+ long inputBufferSize = context.Request.SendBuff[0].Size;
- // Ip address count, we can only support ipv4 (blame Nintendo)
- context.Memory.Write((ulong)bufferPosition, addresses != null ? IPAddress.HostToNetworkOrder(addresses.Count) : 0);
- bufferPosition += 4;
+ long outputBufferPosition = context.Request.ReceiveBuff[0].Position;
+ long outputBufferSize = context.Request.ReceiveBuff[0].Size;
- if (addresses != null)
+ return GetHostByNameRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, 0, 0);
+ }
+
+ [Command(3)]
+ // GetHostByAddrRequest(u32, u32, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>)
+ public ResultCode GetHostByAddrRequest(ServiceCtx context)
+ {
+ long inputBufferPosition = context.Request.SendBuff[0].Position;
+ long inputBufferSize = context.Request.SendBuff[0].Size;
+
+ long outputBufferPosition = context.Request.ReceiveBuff[0].Position;
+ long outputBufferSize = context.Request.ReceiveBuff[0].Size;
+
+ return GetHostByAddrRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, 0, 0);
+ }
+
+ [Command(4)]
+ // GetHostStringErrorRequest(u32) -> buffer<unknown, 6, 0>
+ public ResultCode GetHostStringErrorRequest(ServiceCtx context)
+ {
+ ResultCode resultCode = ResultCode.NotAllocated;
+ NetDbError errorCode = (NetDbError)context.RequestData.ReadInt32();
+
+ string errorString = errorCode switch
{
- foreach (IPAddress ip in addresses)
- {
- context.Memory.Write((ulong)bufferPosition, IPAddress.HostToNetworkOrder(BitConverter.ToInt32(ip.GetAddressBytes(), 0)));
- bufferPosition += 4;
- }
+ NetDbError.Success => "Resolver Error 0 (no error)",
+ NetDbError.HostNotFound => "Unknown host",
+ NetDbError.TryAgain => "Host name lookup failure",
+ NetDbError.NoRecovery => "Unknown server error",
+ NetDbError.NoData => "No address associated with name",
+ _ => (errorCode <= NetDbError.Internal) ? "Resolver internal error" : "Unknown resolver error"
+ };
+
+ long bufferPosition = context.Request.ReceiveBuff[0].Position;
+ long bufferSize = context.Request.ReceiveBuff[0].Size;
+
+ if (errorString.Length + 1 <= bufferSize)
+ {
+ context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(errorString + '\0'));
+
+ resultCode = ResultCode.Success;
}
- return bufferPosition - originalBufferPosition;
+ return resultCode;
}
- private string GetGaiStringErrorFromErrorCode(GaiError errorCode)
+ [Command(5)]
+ // GetGaiStringErrorRequest(u32) -> buffer<byte, 6, 0>
+ public ResultCode GetGaiStringErrorRequest(ServiceCtx context)
{
+ ResultCode resultCode = ResultCode.NotAllocated;
+ GaiError errorCode = (GaiError)context.RequestData.ReadInt32();
+
if (errorCode > GaiError.Max)
{
errorCode = GaiError.Max;
}
- switch (errorCode)
+ string errorString = errorCode switch
+ {
+ GaiError.AddressFamily => "Address family for hostname not supported",
+ GaiError.Again => "Temporary failure in name resolution",
+ GaiError.BadFlags => "Invalid value for ai_flags",
+ GaiError.Fail => "Non-recoverable failure in name resolution",
+ GaiError.Family => "ai_family not supported",
+ GaiError.Memory => "Memory allocation failure",
+ GaiError.NoData => "No address associated with hostname",
+ GaiError.NoName => "hostname nor servname provided, or not known",
+ GaiError.Service => "servname not supported for ai_socktype",
+ GaiError.SocketType => "ai_socktype not supported",
+ GaiError.System => "System error returned in errno",
+ GaiError.BadHints => "Invalid value for hints",
+ GaiError.Protocol => "Resolved protocol is unknown",
+ GaiError.Overflow => "Argument buffer overflow",
+ GaiError.Max => "Unknown error",
+ _ => "Success"
+ };
+
+ long bufferPosition = context.Request.ReceiveBuff[0].Position;
+ long bufferSize = context.Request.ReceiveBuff[0].Size;
+
+ if (errorString.Length + 1 <= bufferSize)
{
- 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";
+ context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(errorString + '\0'));
+
+ resultCode = ResultCode.Success;
}
+
+ return resultCode;
}
- private string GetHostStringErrorFromErrorCode(NetDbError errorCode)
+ [Command(6)]
+ // GetAddrInfoRequest(bool enable_nsd_resolve, u32, u64 pid_placeholder, pid, buffer<i8, 5, 0> host, buffer<i8, 5, 0> service, buffer<packed_addrinfo, 5, 0> hints) -> (i32 ret, u32 bsd_errno, u32 packed_addrinfo_size, buffer<packed_addrinfo, 6, 0> response)
+ public ResultCode GetAddrInfoRequest(ServiceCtx context)
{
- if (errorCode <= NetDbError.Internal)
- {
- return "Resolver internal error";
- }
+ long responseBufferPosition = context.Request.ReceiveBuff[0].Position;
+ long responseBufferSize = context.Request.ReceiveBuff[0].Size;
- 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";
- }
+ return GetAddrInfoRequestImpl(context, responseBufferPosition, responseBufferSize, 0, 0);
}
- private List<IPAddress> GetIpv4Addresses(IPHostEntry hostEntry)
+ [Command(8)]
+ // GetCancelHandleRequest(u64, pid) -> u32
+ public ResultCode GetCancelHandleRequest(ServiceCtx context)
{
- List<IPAddress> result = new List<IPAddress>();
- foreach (IPAddress ip in hostEntry.AddressList)
- {
- if (ip.AddressFamily == AddressFamily.InterNetwork)
- result.Add(ip);
- }
- return result;
+ ulong pidPlaceHolder = context.RequestData.ReadUInt64();
+ uint cancelHandleRequest = 0;
+
+ context.ResponseData.Write(cancelHandleRequest);
+
+ Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { cancelHandleRequest });
+
+ return ResultCode.Success;
}
- [Command(0)]
- // SetDnsAddressesPrivate(u32, buffer<unknown, 5, 0>)
- public ResultCode SetDnsAddressesPrivate(ServiceCtx context)
+ [Command(9)]
+ // CancelRequest(u32, u64, pid)
+ public ResultCode CancelRequest(ServiceCtx context)
{
- uint unknown0 = context.RequestData.ReadUInt32();
- long bufferPosition = context.Request.SendBuff[0].Position;
- long bufferSize = context.Request.SendBuff[0].Size;
+ uint cancelHandleRequest = context.RequestData.ReadUInt32();
+ ulong pidPlaceHolder = context.RequestData.ReadUInt64();
- // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake completeness.
- Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { unknown0 });
+ Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { cancelHandleRequest });
- return ResultCode.NotAllocated;
+ return ResultCode.Success;
}
- [Command(1)]
- // GetDnsAddressPrivate(u32) -> buffer<unknown, 6, 0>
- public ResultCode GetDnsAddressesPrivate(ServiceCtx context)
+ [Command(10)] // 5.0.0+
+ // GetHostByNameRequestWithOptions(u8, u32, u64, pid, buffer<unknown, 21, 0>, buffer<unknown, 21, 0>) -> (u32, u32, u32, buffer<unknown, 22, 0>)
+ public ResultCode GetHostByNameRequestWithOptions(ServiceCtx context)
{
- uint unknown0 = context.RequestData.ReadUInt32();
+ (long inputBufferPosition, long inputBufferSize) = context.Request.GetBufferType0x21();
+ (long outputBufferPosition, long outputBufferSize) = context.Request.GetBufferType0x22();
+ (long optionsBufferPosition, long optionsBufferSize) = context.Request.GetBufferType0x21();
- // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake completeness.
- Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { unknown0 });
+ return GetHostByNameRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, optionsBufferPosition, optionsBufferSize);
+ }
- return ResultCode.NotAllocated;
+ [Command(11)] // 5.0.0+
+ // GetHostByAddrRequestWithOptions(u32, u32, u32, u64, pid, buffer<unknown, 21, 0>, buffer<unknown, 21, 0>) -> (u32, u32, u32, buffer<unknown, 22, 0>)
+ public ResultCode GetHostByAddrRequestWithOptions(ServiceCtx context)
+ {
+ (long inputBufferPosition, long inputBufferSize) = context.Request.GetBufferType0x21();
+ (long outputBufferPosition, long outputBufferSize) = context.Request.GetBufferType0x22();
+ (long optionsBufferPosition, long optionsBufferSize) = context.Request.GetBufferType0x21();
+
+ return GetHostByAddrRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, optionsBufferPosition, optionsBufferSize);
}
- [Command(2)]
- // GetHostByName(u8, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>)
- public ResultCode GetHostByName(ServiceCtx context)
+ [Command(12)] // 5.0.0+
+ // GetAddrInfoRequestWithOptions(bool enable_nsd_resolve, u32, u64 pid_placeholder, pid, buffer<i8, 5, 0> host, buffer<i8, 5, 0> service, buffer<packed_addrinfo, 5, 0> hints, buffer<unknown, 21, 0>) -> (i32 ret, u32 bsd_errno, u32 unknown, u32 packed_addrinfo_size, buffer<packed_addrinfo, 22, 0> response)
+ public ResultCode GetAddrInfoRequestWithOptions(ServiceCtx context)
+ {
+ (long responseBufferPosition, long responseBufferSize) = context.Request.GetBufferType0x22();
+ (long optionsBufferPosition, long optionsBufferSize) = context.Request.GetBufferType0x21();
+
+ return GetAddrInfoRequestImpl(context, responseBufferPosition, responseBufferSize, optionsBufferPosition, optionsBufferSize);
+ }
+
+ private ResultCode GetHostByNameRequestImpl(ServiceCtx context, long inputBufferPosition, long inputBufferSize, long outputBufferPosition, long outputBufferSize, long optionsBufferPosition, long optionsBufferSize)
{
- byte[] rawName = new byte[context.Request.SendBuff[0].Size];
+ byte[] rawName = new byte[inputBufferSize];
- context.Memory.Read((ulong)context.Request.SendBuff[0].Position, rawName);
+ context.Memory.Read((ulong)inputBufferPosition, rawName);
string name = Encoding.ASCII.GetString(rawName).TrimEnd('\0');
- // TODO: use params
- bool enableNsdResolve = context.RequestData.ReadInt32() == 1;
+ // TODO: Use params.
+ bool enableNsdResolve = (context.RequestData.ReadInt32() & 1) != 0;
int timeOut = context.RequestData.ReadInt32();
ulong pidPlaceholder = context.RequestData.ReadUInt64();
+ if (optionsBufferSize > 0)
+ {
+ // TODO: Parse and use options.
+ }
+
IPHostEntry hostEntry = null;
NetDbError netDbErrorCode = NetDbError.Success;
GaiError errno = GaiError.Overflow;
long serializedSize = 0;
- if (name.Length <= 255)
+ if (name.Length <= byte.MaxValue)
{
- try
+ string targetHost = name;
+
+ if (DnsBlacklist.IsHostBlocked(name))
{
- hostEntry = Dns.GetHostEntry(name);
+ Logger.Info?.Print(LogClass.ServiceSfdnsres, $"DNS Blocked: {name}");
+
+ netDbErrorCode = NetDbError.HostNotFound;
+ errno = GaiError.NoData;
}
- catch (SocketException exception)
+ else
{
- netDbErrorCode = NetDbError.Internal;
+ Logger.Info?.Print(LogClass.ServiceSfdnsres, $"Trying to resolve: {name}");
- 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)
+ try
{
- netDbErrorCode = NetDbError.NoData;
+ hostEntry = Dns.GetHostEntry(targetHost);
}
- else if (exception.ErrorCode == 10060)
+ catch (SocketException exception)
{
- errno = GaiError.Again;
+ netDbErrorCode = ConvertSocketErrorCodeToNetDbError(exception.ErrorCode);
+ errno = ConvertSocketErrorCodeToGaiError(exception.ErrorCode, errno);
}
}
}
@@ -225,18 +273,17 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
if (hostEntry != null)
{
- errno = GaiError.Success;
-
- List<IPAddress> addresses = GetIpv4Addresses(hostEntry);
+ IEnumerable<IPAddress> addresses = GetIpv4Addresses(hostEntry);
- if (addresses.Count == 0)
+ if (!addresses.Any())
{
errno = GaiError.NoData;
netDbErrorCode = NetDbError.NoAddress;
}
else
{
- serializedSize = SerializeHostEnt(context, hostEntry, addresses);
+ errno = GaiError.Success;
+ serializedSize = SerializeHostEntries(context, outputBufferPosition, outputBufferSize, hostEntry, addresses);
}
}
@@ -247,20 +294,23 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
return ResultCode.Success;
}
- [Command(3)]
- // GetHostByAddr(u32, u32, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>)
- public ResultCode GetHostByAddress(ServiceCtx context)
+ private ResultCode GetHostByAddrRequestImpl(ServiceCtx context, long inputBufferPosition, long inputBufferSize, long outputBufferPosition, long outputBufferSize, long optionsBufferPosition, long optionsBufferSize)
{
- byte[] rawIp = new byte[context.Request.SendBuff[0].Size];
+ byte[] rawIp = new byte[inputBufferSize];
- context.Memory.Read((ulong)context.Request.SendBuff[0].Position, rawIp);
+ context.Memory.Read((ulong)inputBufferPosition, rawIp);
- // TODO: use params
+ // TODO: Use params.
uint socketLength = context.RequestData.ReadUInt32();
uint type = context.RequestData.ReadUInt32();
int timeOut = context.RequestData.ReadInt32();
ulong pidPlaceholder = context.RequestData.ReadUInt64();
+ if (optionsBufferSize > 0)
+ {
+ // TODO: Parse and use options.
+ }
+
IPHostEntry hostEntry = null;
NetDbError netDbErrorCode = NetDbError.Success;
@@ -277,28 +327,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
}
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;
- }
+ netDbErrorCode = ConvertSocketErrorCodeToNetDbError(exception.ErrorCode);
+ errno = ConvertSocketErrorCodeToGaiError(exception.ErrorCode, errno);
}
}
else
@@ -308,8 +338,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
if (hostEntry != null)
{
- errno = GaiError.Success;
- serializedSize = SerializeHostEnt(context, hostEntry, GetIpv4Addresses(hostEntry));
+ errno = GaiError.Success;
+ serializedSize = SerializeHostEntries(context, outputBufferPosition, outputBufferSize, hostEntry, GetIpv4Addresses(hostEntry));
}
context.ResponseData.Write((int)netDbErrorCode);
@@ -319,74 +349,220 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
return ResultCode.Success;
}
- [Command(4)]
- // GetHostStringError(u32) -> buffer<unknown, 6, 0>
- public ResultCode GetHostStringError(ServiceCtx context)
+ private long SerializeHostEntries(ServiceCtx context, long outputBufferPosition, long outputBufferSize, IPHostEntry hostEntry, IEnumerable<IPAddress> addresses = null)
{
- ResultCode resultCode = ResultCode.NotAllocated;
- NetDbError errorCode = (NetDbError)context.RequestData.ReadInt32();
- string errorString = GetHostStringErrorFromErrorCode(errorCode);
+ long originalBufferPosition = outputBufferPosition;
+ long bufferPosition = originalBufferPosition;
- if (errorString.Length + 1 <= context.Request.ReceiveBuff[0].Size)
+ string hostName = hostEntry.HostName + '\0';
+
+ // h_name
+ context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(hostName));
+ bufferPosition += hostName.Length;
+
+ // h_aliases list size
+ context.Memory.Write((ulong)bufferPosition, BinaryPrimitives.ReverseEndianness(hostEntry.Aliases.Length));
+ bufferPosition += 4;
+
+ // Actual aliases
+ foreach (string alias in hostEntry.Aliases)
{
- resultCode = 0;
- context.Memory.Write((ulong)context.Request.ReceiveBuff[0].Position, Encoding.ASCII.GetBytes(errorString + '\0'));
+ context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(alias + '\0'));
+ bufferPosition += alias.Length + 1;
}
- return resultCode;
+ // h_addrtype but it's a short (also only support IPv4)
+ context.Memory.Write((ulong)bufferPosition, BinaryPrimitives.ReverseEndianness((short)AddressFamily.InterNetwork));
+ bufferPosition += 2;
+
+ // h_length but it's a short
+ context.Memory.Write((ulong)bufferPosition, BinaryPrimitives.ReverseEndianness((short)4));
+ bufferPosition += 2;
+
+ // Ip address count, we can only support ipv4 (blame Nintendo)
+ context.Memory.Write((ulong)bufferPosition, addresses != null ? BinaryPrimitives.ReverseEndianness(addresses.Count()) : 0);
+ bufferPosition += 4;
+
+ if (addresses != null)
+ {
+ foreach (IPAddress ip in addresses)
+ {
+ context.Memory.Write((ulong)bufferPosition, BinaryPrimitives.ReverseEndianness(BitConverter.ToInt32(ip.GetAddressBytes(), 0)));
+ bufferPosition += 4;
+ }
+ }
+
+ return bufferPosition - originalBufferPosition;
}
- [Command(5)]
- // GetGaiStringError(u32) -> buffer<unknown, 6, 0>
- public ResultCode GetGaiStringError(ServiceCtx context)
+ private ResultCode GetAddrInfoRequestImpl(ServiceCtx context, long responseBufferPosition, long responseBufferSize, long optionsBufferPosition, long optionsBufferSize)
{
- ResultCode resultCode = ResultCode.NotAllocated;
- GaiError errorCode = (GaiError)context.RequestData.ReadInt32();
- string errorString = GetGaiStringErrorFromErrorCode(errorCode);
+ bool enableNsdResolve = (context.RequestData.ReadInt32() & 1) != 0;
+ uint cancelHandle = context.RequestData.ReadUInt32();
+
+ string host = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[0].Position, context.Request.SendBuff[0].Size);
+ string service = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[1].Position, context.Request.SendBuff[1].Size);
+
+ // NOTE: We ignore hints for now.
+ DeserializeAddrInfos(context.Memory, (ulong)context.Request.SendBuff[2].Position, (ulong)context.Request.SendBuff[2].Size);
- if (errorString.Length + 1 <= context.Request.ReceiveBuff[0].Size)
+ if (optionsBufferSize > 0)
{
- resultCode = 0;
- context.Memory.Write((ulong)context.Request.ReceiveBuff[0].Position, Encoding.ASCII.GetBytes(errorString + '\0'));
+ // TODO: Find unknown, Parse and use options.
+ uint unknown = context.RequestData.ReadUInt32();
}
- return resultCode;
+ ulong pidPlaceHolder = context.RequestData.ReadUInt64();
+
+ Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { enableNsdResolve, cancelHandle, pidPlaceHolder, host, service });
+
+ IPHostEntry hostEntry = null;
+
+ NetDbError netDbErrorCode = NetDbError.Success;
+ GaiError errno = GaiError.AddressFamily;
+ ulong serializedSize = 0;
+
+ if (host.Length <= byte.MaxValue)
+ {
+ string targetHost = host;
+
+ if (DnsBlacklist.IsHostBlocked(host))
+ {
+ Logger.Info?.Print(LogClass.ServiceSfdnsres, $"DNS Blocked: {host}");
+
+ netDbErrorCode = NetDbError.HostNotFound;
+ errno = GaiError.NoData;
+ }
+ else
+ {
+ Logger.Info?.Print(LogClass.ServiceSfdnsres, $"Trying to resolve: {host}");
+
+ try
+ {
+ hostEntry = Dns.GetHostEntry(targetHost);
+ }
+ catch (SocketException exception)
+ {
+ netDbErrorCode = ConvertSocketErrorCodeToNetDbError(exception.ErrorCode);
+ errno = ConvertSocketErrorCodeToGaiError(exception.ErrorCode, errno);
+ }
+ }
+ }
+ else
+ {
+ netDbErrorCode = NetDbError.NoAddress;
+ }
+
+ if (hostEntry != null)
+ {
+ int.TryParse(service, out int port);
+
+ errno = GaiError.Success;
+ serializedSize = SerializeAddrInfos(context, responseBufferPosition, responseBufferSize, hostEntry, port);
+ }
+
+ context.ResponseData.Write((int)netDbErrorCode);
+ context.ResponseData.Write((int)errno);
+ context.ResponseData.Write(serializedSize);
+
+ return ResultCode.Success;
}
- [Command(8)]
- // RequestCancelHandle(u64, pid) -> u32
- public ResultCode RequestCancelHandle(ServiceCtx context)
+ private void DeserializeAddrInfos(IVirtualMemoryManager memory, ulong address, ulong size)
{
- ulong unknown0 = context.RequestData.ReadUInt64();
+ ulong endAddress = address + size;
+
+ while (address < endAddress)
+ {
+ AddrInfoSerializedHeader header = memory.Read<AddrInfoSerializedHeader>(address);
- context.ResponseData.Write(0);
+ if (header.Magic != SfdnsresContants.AddrInfoMagic)
+ {
+ break;
+ }
- Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { unknown0 });
+ address += (ulong)Unsafe.SizeOf<AddrInfoSerializedHeader>() + header.AddressLength;
- return ResultCode.Success;
+ // ai_canonname
+ string canonname = string.Empty;
+
+ while (true)
+ {
+ byte chr = memory.Read<byte>(address++);
+
+ if (chr == 0)
+ {
+ break;
+ }
+
+ canonname += (char)chr;
+ }
+ }
}
- [Command(9)]
- // CancelSocketCall(u32, u64, pid)
- public ResultCode CancelSocketCall(ServiceCtx context)
+ private ulong SerializeAddrInfos(ServiceCtx context, long responseBufferPosition, long responseBufferSize, IPHostEntry hostEntry, int port)
{
- uint unknown0 = context.RequestData.ReadUInt32();
- ulong unknown1 = context.RequestData.ReadUInt64();
+ ulong originalBufferPosition = (ulong)responseBufferPosition;
+ ulong bufferPosition = originalBufferPosition;
- Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { unknown0, unknown1 });
+ string hostName = hostEntry.HostName + '\0';
- return ResultCode.Success;
+ for (int i = 0; i < hostEntry.AddressList.Length; i++)
+ {
+ IPAddress ip = hostEntry.AddressList[i];
+
+ if (ip.AddressFamily != AddressFamily.InterNetwork)
+ {
+ continue;
+ }
+
+ AddrInfoSerializedHeader header = new AddrInfoSerializedHeader(ip, 0);
+
+ // NOTE: 0 = Any
+ context.Memory.Write(bufferPosition, header);
+ bufferPosition += (ulong)Unsafe.SizeOf<AddrInfoSerializedHeader>();
+
+ // addrinfo_in
+ context.Memory.Write(bufferPosition, new AddrInfo4(ip, (short)port));
+ bufferPosition += header.AddressLength;
+
+ // ai_canonname
+ context.Memory.Write(bufferPosition, Encoding.ASCII.GetBytes(hostName));
+ bufferPosition += (ulong)hostName.Length;
+ }
+
+ // Termination zero value.
+ context.Memory.Write(bufferPosition, 0);
+ bufferPosition += 4;
+
+ return bufferPosition - originalBufferPosition;
}
- [Command(11)]
- // ClearDnsAddresses(u32)
- public ResultCode ClearDnsAddresses(ServiceCtx context)
+ private IEnumerable<IPAddress> GetIpv4Addresses(IPHostEntry hostEntry)
{
- uint unknown0 = context.RequestData.ReadUInt32();
+ return hostEntry.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetwork);
+ }
- Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { unknown0 });
+ private NetDbError ConvertSocketErrorCodeToNetDbError(int errorCode)
+ {
+ return errorCode switch
+ {
+ 11001 => NetDbError.HostNotFound,
+ 11002 => NetDbError.TryAgain,
+ 11003 => NetDbError.NoRecovery,
+ 11004 => NetDbError.NoData,
+ _ => NetDbError.Internal
+ };
+ }
- return ResultCode.Success;
+ private GaiError ConvertSocketErrorCodeToGaiError(int errorCode, GaiError errno)
+ {
+ return errorCode switch
+ {
+ 11001 => GaiError.NoData,
+ 10060 => GaiError.Again,
+ _ => errno
+ };
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs
new file mode 100644
index 00000000..db499e24
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Proxy/DnsBlacklist.cs
@@ -0,0 +1,28 @@
+using System.Text.RegularExpressions;
+
+namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy
+{
+ static class DnsBlacklist
+ {
+ private static readonly Regex[] BlockedHosts = new Regex[]
+ {
+ new Regex(@"^g(.*)\-lp1\.s\.n\.srv\.nintendo\.net$"),
+ new Regex(@"^(.*)\-sb\-api\.accounts\.nintendo\.com$"),
+ new Regex(@"^(.*)\-sb\.accounts\.nintendo\.com$"),
+ new Regex(@"^accounts\.nintendo\.com$")
+ };
+
+ public static bool IsHostBlocked(string host)
+ {
+ foreach (Regex regex in BlockedHosts)
+ {
+ if (regex.IsMatch(host))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs
new file mode 100644
index 00000000..515d8649
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs
@@ -0,0 +1,29 @@
+using Ryujinx.Common.Memory;
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)]
+ struct AddrInfo4
+ {
+ public byte Length;
+ public byte Family;
+ public short Port;
+ public Array4<byte> Address;
+
+ public AddrInfo4(IPAddress address, short port)
+ {
+ Length = 0;
+ Family = (byte)AddressFamily.InterNetwork;
+ Port = port;
+ Address = default;
+
+ address.GetAddressBytes().AsSpan().CopyTo(Address.ToSpan());
+
+ Address.ToSpan().Reverse();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs
new file mode 100644
index 00000000..b6251a45
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerializedHeader.cs
@@ -0,0 +1,37 @@
+using System.Buffers.Binary;
+using System.Net;
+using System.Net.Sockets;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 6 * sizeof(int))]
+ struct AddrInfoSerializedHeader
+ {
+ public uint Magic;
+ public int Flags;
+ public int Family;
+ public int SocketType;
+ public int Protocol;
+ public uint AddressLength;
+
+ public AddrInfoSerializedHeader(IPAddress address, SocketType socketType)
+ {
+ Magic = (uint)BinaryPrimitives.ReverseEndianness(unchecked((int)SfdnsresContants.AddrInfoMagic));
+ Flags = 0; // Big Endian
+ Family = BinaryPrimitives.ReverseEndianness((int)address.AddressFamily);
+ SocketType = BinaryPrimitives.ReverseEndianness((int)socketType);
+ Protocol = 0; // Big Endian
+
+ if (address.AddressFamily == AddressFamily.InterNetwork)
+ {
+ AddressLength = (uint)Unsafe.SizeOf<AddrInfo4>();
+ }
+ else
+ {
+ AddressLength = 4;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs
new file mode 100644
index 00000000..d194a3c6
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/SfdnsresContants.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types
+{
+ static class SfdnsresContants
+ {
+ public const uint AddrInfoMagic = 0xBEEFCAFE;
+ }
+} \ No newline at end of file