IResolver.cs 14 KB

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