IResolver.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  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> _commands;
  14. public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
  15. public IResolver()
  16. {
  17. _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, new { 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, new { 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, new { 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, new { unknown0, unknown1 });
  327. return 0;
  328. }
  329. // ClearDnsAddresses(u32)
  330. public long ClearDnsAddresses(ServiceCtx context)
  331. {
  332. uint unknown0 = context.RequestData.ReadUInt32();
  333. Logger.PrintStub(LogClass.ServiceSfdnsres, new { unknown0 });
  334. return 0;
  335. }
  336. }
  337. }