IResolver.cs 15 KB

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