IResolver.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  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.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(hostName));
  21. bufferPosition += hostName.Length;
  22. // h_aliases list size
  23. context.Memory.Write((ulong)bufferPosition, IPAddress.HostToNetworkOrder(hostEntry.Aliases.Length));
  24. bufferPosition += 4;
  25. // Actual aliases
  26. foreach (string alias in hostEntry.Aliases)
  27. {
  28. context.Memory.Write((ulong)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.Write((ulong)bufferPosition, IPAddress.HostToNetworkOrder((short)2));
  33. bufferPosition += 2;
  34. // h_length but it's a short
  35. context.Memory.Write((ulong)bufferPosition, IPAddress.HostToNetworkOrder((short)4));
  36. bufferPosition += 2;
  37. // Ip address count, we can only support ipv4 (blame Nintendo)
  38. context.Memory.Write((ulong)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.Write((ulong)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.Stub?.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.Stub?.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 = new byte[context.Request.SendBuff[0].Size];
  149. context.Memory.Read((ulong)context.Request.SendBuff[0].Position, rawName);
  150. string name = Encoding.ASCII.GetString(rawName).TrimEnd('\0');
  151. // TODO: use params
  152. bool enableNsdResolve = context.RequestData.ReadInt32() == 1;
  153. int timeOut = context.RequestData.ReadInt32();
  154. ulong pidPlaceholder = context.RequestData.ReadUInt64();
  155. IPHostEntry hostEntry = null;
  156. NetDbError netDbErrorCode = NetDbError.Success;
  157. GaiError errno = GaiError.Overflow;
  158. long serializedSize = 0;
  159. if (name.Length <= 255)
  160. {
  161. try
  162. {
  163. hostEntry = Dns.GetHostEntry(name);
  164. }
  165. catch (SocketException exception)
  166. {
  167. netDbErrorCode = NetDbError.Internal;
  168. if (exception.ErrorCode == 11001)
  169. {
  170. netDbErrorCode = NetDbError.HostNotFound;
  171. errno = GaiError.NoData;
  172. }
  173. else if (exception.ErrorCode == 11002)
  174. {
  175. netDbErrorCode = NetDbError.TryAgain;
  176. }
  177. else if (exception.ErrorCode == 11003)
  178. {
  179. netDbErrorCode = NetDbError.NoRecovery;
  180. }
  181. else if (exception.ErrorCode == 11004)
  182. {
  183. netDbErrorCode = NetDbError.NoData;
  184. }
  185. else if (exception.ErrorCode == 10060)
  186. {
  187. errno = GaiError.Again;
  188. }
  189. }
  190. }
  191. else
  192. {
  193. netDbErrorCode = NetDbError.HostNotFound;
  194. }
  195. if (hostEntry != null)
  196. {
  197. errno = GaiError.Success;
  198. List<IPAddress> addresses = GetIpv4Addresses(hostEntry);
  199. if (addresses.Count == 0)
  200. {
  201. errno = GaiError.NoData;
  202. netDbErrorCode = NetDbError.NoAddress;
  203. }
  204. else
  205. {
  206. serializedSize = SerializeHostEnt(context, hostEntry, addresses);
  207. }
  208. }
  209. context.ResponseData.Write((int)netDbErrorCode);
  210. context.ResponseData.Write((int)errno);
  211. context.ResponseData.Write(serializedSize);
  212. return ResultCode.Success;
  213. }
  214. [Command(3)]
  215. // GetHostByAddr(u32, u32, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>)
  216. public ResultCode GetHostByAddress(ServiceCtx context)
  217. {
  218. byte[] rawIp = new byte[context.Request.SendBuff[0].Size];
  219. context.Memory.Read((ulong)context.Request.SendBuff[0].Position, rawIp);
  220. // TODO: use params
  221. uint socketLength = context.RequestData.ReadUInt32();
  222. uint type = context.RequestData.ReadUInt32();
  223. int timeOut = context.RequestData.ReadInt32();
  224. ulong pidPlaceholder = context.RequestData.ReadUInt64();
  225. IPHostEntry hostEntry = null;
  226. NetDbError netDbErrorCode = NetDbError.Success;
  227. GaiError errno = GaiError.AddressFamily;
  228. long serializedSize = 0;
  229. if (rawIp.Length == 4)
  230. {
  231. try
  232. {
  233. IPAddress address = new IPAddress(rawIp);
  234. hostEntry = Dns.GetHostEntry(address);
  235. }
  236. catch (SocketException exception)
  237. {
  238. netDbErrorCode = NetDbError.Internal;
  239. if (exception.ErrorCode == 11001)
  240. {
  241. netDbErrorCode = NetDbError.HostNotFound;
  242. errno = GaiError.NoData;
  243. }
  244. else if (exception.ErrorCode == 11002)
  245. {
  246. netDbErrorCode = NetDbError.TryAgain;
  247. }
  248. else if (exception.ErrorCode == 11003)
  249. {
  250. netDbErrorCode = NetDbError.NoRecovery;
  251. }
  252. else if (exception.ErrorCode == 11004)
  253. {
  254. netDbErrorCode = NetDbError.NoData;
  255. }
  256. else if (exception.ErrorCode == 10060)
  257. {
  258. errno = GaiError.Again;
  259. }
  260. }
  261. }
  262. else
  263. {
  264. netDbErrorCode = NetDbError.NoAddress;
  265. }
  266. if (hostEntry != null)
  267. {
  268. errno = GaiError.Success;
  269. serializedSize = SerializeHostEnt(context, hostEntry, GetIpv4Addresses(hostEntry));
  270. }
  271. context.ResponseData.Write((int)netDbErrorCode);
  272. context.ResponseData.Write((int)errno);
  273. context.ResponseData.Write(serializedSize);
  274. return ResultCode.Success;
  275. }
  276. [Command(4)]
  277. // GetHostStringError(u32) -> buffer<unknown, 6, 0>
  278. public ResultCode GetHostStringError(ServiceCtx context)
  279. {
  280. ResultCode resultCode = ResultCode.NotAllocated;
  281. NetDbError errorCode = (NetDbError)context.RequestData.ReadInt32();
  282. string errorString = GetHostStringErrorFromErrorCode(errorCode);
  283. if (errorString.Length + 1 <= context.Request.ReceiveBuff[0].Size)
  284. {
  285. resultCode = 0;
  286. context.Memory.Write((ulong)context.Request.ReceiveBuff[0].Position, Encoding.ASCII.GetBytes(errorString + '\0'));
  287. }
  288. return resultCode;
  289. }
  290. [Command(5)]
  291. // GetGaiStringError(u32) -> buffer<unknown, 6, 0>
  292. public ResultCode GetGaiStringError(ServiceCtx context)
  293. {
  294. ResultCode resultCode = ResultCode.NotAllocated;
  295. GaiError errorCode = (GaiError)context.RequestData.ReadInt32();
  296. string errorString = GetGaiStringErrorFromErrorCode(errorCode);
  297. if (errorString.Length + 1 <= context.Request.ReceiveBuff[0].Size)
  298. {
  299. resultCode = 0;
  300. context.Memory.Write((ulong)context.Request.ReceiveBuff[0].Position, Encoding.ASCII.GetBytes(errorString + '\0'));
  301. }
  302. return resultCode;
  303. }
  304. [Command(8)]
  305. // RequestCancelHandle(u64, pid) -> u32
  306. public ResultCode RequestCancelHandle(ServiceCtx context)
  307. {
  308. ulong unknown0 = context.RequestData.ReadUInt64();
  309. context.ResponseData.Write(0);
  310. Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { unknown0 });
  311. return ResultCode.Success;
  312. }
  313. [Command(9)]
  314. // CancelSocketCall(u32, u64, pid)
  315. public ResultCode CancelSocketCall(ServiceCtx context)
  316. {
  317. uint unknown0 = context.RequestData.ReadUInt32();
  318. ulong unknown1 = context.RequestData.ReadUInt64();
  319. Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { unknown0, unknown1 });
  320. return ResultCode.Success;
  321. }
  322. [Command(11)]
  323. // ClearDnsAddresses(u32)
  324. public ResultCode ClearDnsAddresses(ServiceCtx context)
  325. {
  326. uint unknown0 = context.RequestData.ReadUInt32();
  327. Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { unknown0 });
  328. return ResultCode.Success;
  329. }
  330. }
  331. }