IResolver.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. using Ryujinx.HLE.HOS.Ipc;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Net;
  5. using System.Net.Sockets;
  6. using System.Text;
  7. using static Ryujinx.HLE.HOS.ErrorCode;
  8. namespace Ryujinx.HLE.HOS.Services.Sfdnsres
  9. {
  10. class IResolver : IpcService
  11. {
  12. private Dictionary<int, ServiceProcessRequest> m_Commands;
  13. public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
  14. public IResolver()
  15. {
  16. m_Commands = new Dictionary<int, ServiceProcessRequest>()
  17. {
  18. { 0, SetDnsAddressesPrivate },
  19. { 1, GetDnsAddressesPrivate },
  20. { 2, GetHostByName },
  21. { 3, GetHostByAddress },
  22. { 4, GetHostStringError },
  23. { 5, GetGaiStringError },
  24. { 8, RequestCancelHandle },
  25. { 9, CancelSocketCall },
  26. { 11, ClearDnsAddresses },
  27. };
  28. }
  29. private long SerializeHostEnt(ServiceCtx Context, IPHostEntry HostEntry, List<IPAddress> Addresses = null)
  30. {
  31. long OriginalBufferPosition = Context.Request.ReceiveBuff[0].Position;
  32. long BufferPosition = OriginalBufferPosition;
  33. long BufferSize = Context.Request.ReceiveBuff[0].Size;
  34. string HostName = HostEntry.HostName + '\0';
  35. // h_name
  36. Context.Memory.WriteBytes(BufferPosition, Encoding.ASCII.GetBytes(HostName));
  37. BufferPosition += HostName.Length;
  38. // h_aliases list size
  39. Context.Memory.WriteInt32(BufferPosition, IPAddress.HostToNetworkOrder(HostEntry.Aliases.Length));
  40. BufferPosition += 4;
  41. // Actual aliases
  42. foreach (string Alias in HostEntry.Aliases)
  43. {
  44. Context.Memory.WriteBytes(BufferPosition, Encoding.ASCII.GetBytes(Alias + '\0'));
  45. BufferPosition += Alias.Length + 1;
  46. }
  47. // h_addrtype but it's a short (also only support IPv4)
  48. Context.Memory.WriteInt16(BufferPosition, IPAddress.HostToNetworkOrder((short)2));
  49. BufferPosition += 2;
  50. // h_length but it's a short
  51. Context.Memory.WriteInt16(BufferPosition, IPAddress.HostToNetworkOrder((short)4));
  52. BufferPosition += 2;
  53. // Ip address count, we can only support ipv4 (blame Nintendo)
  54. Context.Memory.WriteInt32(BufferPosition, Addresses != null ? IPAddress.HostToNetworkOrder(Addresses.Count) : 0);
  55. BufferPosition += 4;
  56. if (Addresses != null)
  57. {
  58. foreach (IPAddress Ip in Addresses)
  59. {
  60. Context.Memory.WriteInt32(BufferPosition, IPAddress.HostToNetworkOrder(BitConverter.ToInt32(Ip.GetAddressBytes(), 0)));
  61. BufferPosition += 4;
  62. }
  63. }
  64. return BufferPosition - OriginalBufferPosition;
  65. }
  66. private string GetGaiStringErrorFromErrorCode(GaiError ErrorCode)
  67. {
  68. if (ErrorCode > GaiError.Max)
  69. {
  70. ErrorCode = GaiError.Max;
  71. }
  72. switch (ErrorCode)
  73. {
  74. case GaiError.AddressFamily:
  75. return "Address family for hostname not supported";
  76. case GaiError.Again:
  77. return "Temporary failure in name resolution";
  78. case GaiError.BadFlags:
  79. return "Invalid value for ai_flags";
  80. case GaiError.Fail:
  81. return "Non-recoverable failure in name resolution";
  82. case GaiError.Family:
  83. return "ai_family not supported";
  84. case GaiError.Memory:
  85. return "Memory allocation failure";
  86. case GaiError.NoData:
  87. return "No address associated with hostname";
  88. case GaiError.NoName:
  89. return "hostname nor servname provided, or not known";
  90. case GaiError.Service:
  91. return "servname not supported for ai_socktype";
  92. case GaiError.SocketType:
  93. return "ai_socktype not supported";
  94. case GaiError.System:
  95. return "System error returned in errno";
  96. case GaiError.BadHints:
  97. return "Invalid value for hints";
  98. case GaiError.Protocol:
  99. return "Resolved protocol is unknown";
  100. case GaiError.Overflow:
  101. return "Argument buffer overflow";
  102. case GaiError.Max:
  103. return "Unknown error";
  104. default:
  105. return "Success";
  106. }
  107. }
  108. private string GetHostStringErrorFromErrorCode(NetDBError ErrorCode)
  109. {
  110. if (ErrorCode <= NetDBError.Internal)
  111. {
  112. return "Resolver internal error";
  113. }
  114. switch (ErrorCode)
  115. {
  116. case NetDBError.Success:
  117. return "Resolver Error 0 (no error)";
  118. case NetDBError.HostNotFound:
  119. return "Unknown host";
  120. case NetDBError.TryAgain:
  121. return "Host name lookup failure";
  122. case NetDBError.NoRecovery:
  123. return "Unknown server error";
  124. case NetDBError.NoData:
  125. return "No address associated with name";
  126. default:
  127. return "Unknown resolver error";
  128. }
  129. }
  130. private List<IPAddress> GetIPV4Addresses(IPHostEntry HostEntry)
  131. {
  132. List<IPAddress> Result = new List<IPAddress>();
  133. foreach (IPAddress Ip in HostEntry.AddressList)
  134. {
  135. if (Ip.AddressFamily == AddressFamily.InterNetwork)
  136. Result.Add(Ip);
  137. }
  138. return Result;
  139. }
  140. // SetDnsAddressesPrivate(u32, buffer<unknown, 5, 0>)
  141. public long SetDnsAddressesPrivate(ServiceCtx Context)
  142. {
  143. uint Unknown0 = Context.RequestData.ReadUInt32();
  144. long BufferPosition = Context.Request.SendBuff[0].Position;
  145. long BufferSize = Context.Request.SendBuff[0].Size;
  146. // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake completeness.
  147. Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}");
  148. return MakeError(ErrorModule.Os, 1023);
  149. }
  150. // GetDnsAddressPrivate(u32) -> buffer<unknown, 6, 0>
  151. public long GetDnsAddressesPrivate(ServiceCtx Context)
  152. {
  153. uint Unknown0 = Context.RequestData.ReadUInt32();
  154. // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake completeness.
  155. Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}");
  156. return MakeError(ErrorModule.Os, 1023);
  157. }
  158. // GetHostByName(u8, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>)
  159. public long GetHostByName(ServiceCtx Context)
  160. {
  161. byte[] RawName = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position, Context.Request.SendBuff[0].Size);
  162. string Name = Encoding.ASCII.GetString(RawName).TrimEnd('\0');
  163. // TODO: use params
  164. bool EnableNsdResolve = Context.RequestData.ReadInt32() == 1;
  165. int TimeOut = Context.RequestData.ReadInt32();
  166. ulong PidPlaceholder = Context.RequestData.ReadUInt64();
  167. IPHostEntry HostEntry = null;
  168. NetDBError NetDBErrorCode = NetDBError.Success;
  169. GaiError Errno = GaiError.Overflow;
  170. long SerializedSize = 0;
  171. if (Name.Length <= 255)
  172. {
  173. try
  174. {
  175. HostEntry = Dns.GetHostEntry(Name);
  176. }
  177. catch (SocketException Exception)
  178. {
  179. NetDBErrorCode = NetDBError.Internal;
  180. if (Exception.ErrorCode == 11001)
  181. {
  182. NetDBErrorCode = NetDBError.HostNotFound;
  183. Errno = GaiError.NoData;
  184. }
  185. else if (Exception.ErrorCode == 11002)
  186. {
  187. NetDBErrorCode = NetDBError.TryAgain;
  188. }
  189. else if (Exception.ErrorCode == 11003)
  190. {
  191. NetDBErrorCode = NetDBError.NoRecovery;
  192. }
  193. else if (Exception.ErrorCode == 11004)
  194. {
  195. NetDBErrorCode = NetDBError.NoData;
  196. }
  197. else if (Exception.ErrorCode == 10060)
  198. {
  199. Errno = GaiError.Again;
  200. }
  201. }
  202. }
  203. else
  204. {
  205. NetDBErrorCode = NetDBError.HostNotFound;
  206. }
  207. if (HostEntry != null)
  208. {
  209. Errno = GaiError.Success;
  210. List<IPAddress> Addresses = GetIPV4Addresses(HostEntry);
  211. if (Addresses.Count == 0)
  212. {
  213. Errno = GaiError.NoData;
  214. NetDBErrorCode = NetDBError.NoAddress;
  215. }
  216. else
  217. {
  218. SerializedSize = SerializeHostEnt(Context, HostEntry, Addresses);
  219. }
  220. }
  221. Context.ResponseData.Write((int)NetDBErrorCode);
  222. Context.ResponseData.Write((int)Errno);
  223. Context.ResponseData.Write(SerializedSize);
  224. return 0;
  225. }
  226. // GetHostByAddr(u32, u32, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>)
  227. public long GetHostByAddress(ServiceCtx Context)
  228. {
  229. byte[] RawIp = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position, Context.Request.SendBuff[0].Size);
  230. // TODO: use params
  231. uint SocketLength = Context.RequestData.ReadUInt32();
  232. uint Type = Context.RequestData.ReadUInt32();
  233. int TimeOut = Context.RequestData.ReadInt32();
  234. ulong PidPlaceholder = Context.RequestData.ReadUInt64();
  235. IPHostEntry HostEntry = null;
  236. NetDBError NetDBErrorCode = NetDBError.Success;
  237. GaiError Errno = GaiError.AddressFamily;
  238. long SerializedSize = 0;
  239. if (RawIp.Length == 4)
  240. {
  241. try
  242. {
  243. IPAddress Address = new IPAddress(RawIp);
  244. HostEntry = Dns.GetHostEntry(Address);
  245. }
  246. catch (SocketException Exception)
  247. {
  248. NetDBErrorCode = NetDBError.Internal;
  249. if (Exception.ErrorCode == 11001)
  250. {
  251. NetDBErrorCode = NetDBError.HostNotFound;
  252. Errno = GaiError.NoData;
  253. }
  254. else if (Exception.ErrorCode == 11002)
  255. {
  256. NetDBErrorCode = NetDBError.TryAgain;
  257. }
  258. else if (Exception.ErrorCode == 11003)
  259. {
  260. NetDBErrorCode = NetDBError.NoRecovery;
  261. }
  262. else if (Exception.ErrorCode == 11004)
  263. {
  264. NetDBErrorCode = NetDBError.NoData;
  265. }
  266. else if (Exception.ErrorCode == 10060)
  267. {
  268. Errno = GaiError.Again;
  269. }
  270. }
  271. }
  272. else
  273. {
  274. NetDBErrorCode = NetDBError.NoAddress;
  275. }
  276. if (HostEntry != null)
  277. {
  278. Errno = GaiError.Success;
  279. SerializedSize = SerializeHostEnt(Context, HostEntry, GetIPV4Addresses(HostEntry));
  280. }
  281. Context.ResponseData.Write((int)NetDBErrorCode);
  282. Context.ResponseData.Write((int)Errno);
  283. Context.ResponseData.Write(SerializedSize);
  284. return 0;
  285. }
  286. // GetHostStringError(u32) -> buffer<unknown, 6, 0>
  287. public long GetHostStringError(ServiceCtx Context)
  288. {
  289. long ResultCode = MakeError(ErrorModule.Os, 1023);
  290. NetDBError ErrorCode = (NetDBError)Context.RequestData.ReadInt32();
  291. string ErrorString = GetHostStringErrorFromErrorCode(ErrorCode);
  292. if (ErrorString.Length + 1 <= Context.Request.ReceiveBuff[0].Size)
  293. {
  294. ResultCode = 0;
  295. Context.Memory.WriteBytes(Context.Request.ReceiveBuff[0].Position, Encoding.ASCII.GetBytes(ErrorString + '\0'));
  296. }
  297. return ResultCode;
  298. }
  299. // GetGaiStringError(u32) -> buffer<unknown, 6, 0>
  300. public long GetGaiStringError(ServiceCtx Context)
  301. {
  302. long ResultCode = MakeError(ErrorModule.Os, 1023);
  303. GaiError ErrorCode = (GaiError)Context.RequestData.ReadInt32();
  304. string ErrorString = GetGaiStringErrorFromErrorCode(ErrorCode);
  305. if (ErrorString.Length + 1 <= Context.Request.ReceiveBuff[0].Size)
  306. {
  307. ResultCode = 0;
  308. Context.Memory.WriteBytes(Context.Request.ReceiveBuff[0].Position, Encoding.ASCII.GetBytes(ErrorString + '\0'));
  309. }
  310. return ResultCode;
  311. }
  312. // RequestCancelHandle(u64, pid) -> u32
  313. public long RequestCancelHandle(ServiceCtx Context)
  314. {
  315. ulong Unknown0 = Context.RequestData.ReadUInt64();
  316. Context.ResponseData.Write(0);
  317. Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}");
  318. return 0;
  319. }
  320. // CancelSocketCall(u32, u64, pid)
  321. public long CancelSocketCall(ServiceCtx Context)
  322. {
  323. uint Unknown0 = Context.RequestData.ReadUInt32();
  324. ulong Unknown1 = Context.RequestData.ReadUInt64();
  325. Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0} - " +
  326. $"Unknown1: {Unknown1}");
  327. return 0;
  328. }
  329. // ClearDnsAddresses(u32)
  330. public long ClearDnsAddresses(ServiceCtx Context)
  331. {
  332. uint Unknown0 = Context.RequestData.ReadUInt32();
  333. Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}");
  334. return 0;
  335. }
  336. }
  337. }