DnsMitmResolver.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.HLE.HOS.Services.Sockets.Nsd;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.IO.Enumeration;
  7. using System.Net;
  8. namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy
  9. {
  10. class DnsMitmResolver
  11. {
  12. private const string HostsFilePath = "/atmosphere/hosts/default.txt";
  13. private static DnsMitmResolver _instance;
  14. public static DnsMitmResolver Instance => _instance ??= new DnsMitmResolver();
  15. private readonly Dictionary<string, IPAddress> _mitmHostEntries = new();
  16. public void ReloadEntries(ServiceCtx context)
  17. {
  18. string sdPath = context.Device.Configuration.VirtualFileSystem.GetSdCardPath();
  19. string filePath = context.Device.Configuration.VirtualFileSystem.GetFullPath(sdPath, HostsFilePath);
  20. _mitmHostEntries.Clear();
  21. if (File.Exists(filePath))
  22. {
  23. using FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read);
  24. using StreamReader reader = new(fileStream);
  25. while (!reader.EndOfStream)
  26. {
  27. string line = reader.ReadLine();
  28. if (line == null)
  29. {
  30. break;
  31. }
  32. // Ignore comments and empty lines
  33. if (line.StartsWith("#") || line.Trim().Length == 0)
  34. {
  35. continue;
  36. }
  37. string[] entry = line.Split(new[] { ' ', '\t' }, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
  38. // Hosts file example entry:
  39. // 127.0.0.1 localhost loopback
  40. // 0. Check the size of the array
  41. if (entry.Length < 2)
  42. {
  43. Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"Invalid entry in hosts file: {line}");
  44. continue;
  45. }
  46. // 1. Parse the address
  47. if (!IPAddress.TryParse(entry[0], out IPAddress address))
  48. {
  49. Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"Failed to parse IP address in hosts file: {entry[0]}");
  50. continue;
  51. }
  52. // 2. Check for AMS hosts file extension: "%"
  53. for (int i = 1; i < entry.Length; i++)
  54. {
  55. entry[i] = entry[i].Replace("%", IManager.NsdSettings.Environment);
  56. }
  57. // 3. Add hostname to entry dictionary (updating duplicate entries)
  58. foreach (string hostname in entry[1..])
  59. {
  60. _mitmHostEntries[hostname] = address;
  61. }
  62. }
  63. }
  64. }
  65. public IPHostEntry ResolveAddress(string host)
  66. {
  67. foreach (var hostEntry in _mitmHostEntries)
  68. {
  69. // Check for AMS hosts file extension: "*"
  70. // NOTE: MatchesSimpleExpression also allows "?" as a wildcard
  71. if (FileSystemName.MatchesSimpleExpression(hostEntry.Key, host))
  72. {
  73. Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Redirecting '{host}' to: {hostEntry.Value}");
  74. return new IPHostEntry
  75. {
  76. AddressList = new[] { hostEntry.Value },
  77. HostName = hostEntry.Key,
  78. Aliases = Array.Empty<string>()
  79. };
  80. }
  81. }
  82. // No match has been found, resolve the host using regular dns
  83. return Dns.GetHostEntry(host);
  84. }
  85. }
  86. }