DomainResolver.cs 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. using DNS.Client;
  2. using Microsoft.Extensions.Caching.Memory;
  3. using Microsoft.Extensions.Logging;
  4. using System;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. namespace FastGithub.ReverseProxy
  10. {
  11. /// <summary>
  12. /// 域名解析器
  13. /// </summary>
  14. sealed class DomainResolver
  15. {
  16. private readonly IMemoryCache memoryCache;
  17. private readonly FastGithubConfig fastGithubConfig;
  18. private readonly ILogger<DomainResolver> logger;
  19. private readonly TimeSpan cacheTimeSpan = TimeSpan.FromSeconds(10d);
  20. /// <summary>
  21. /// 域名解析器
  22. /// </summary>
  23. /// <param name="memoryCache"></param>
  24. /// <param name="fastGithubConfig"></param>
  25. /// <param name="logger"></param>
  26. public DomainResolver(
  27. IMemoryCache memoryCache,
  28. FastGithubConfig fastGithubConfig,
  29. ILogger<DomainResolver> logger)
  30. {
  31. this.memoryCache = memoryCache;
  32. this.fastGithubConfig = fastGithubConfig;
  33. this.logger = logger;
  34. }
  35. /// <summary>
  36. /// 解析指定的域名
  37. /// </summary>
  38. /// <param name="domain"></param>
  39. /// <returns></returns>
  40. /// <exception cref="FastGithubException"></exception>
  41. public Task<IPAddress> ResolveAsync(string domain, CancellationToken cancellationToken)
  42. {
  43. // 缓存以避免做不必要的并发查询
  44. var key = $"{nameof(DomainResolver)}:{domain}";
  45. return this.memoryCache.GetOrCreateAsync(key, e =>
  46. {
  47. e.SetAbsoluteExpiration(this.cacheTimeSpan);
  48. return this.LookupAsync(domain, cancellationToken);
  49. });
  50. }
  51. /// <summary>
  52. /// 查找ip
  53. /// </summary>
  54. /// <param name="domain"></param>
  55. /// <param name="cancellationToken"></param>
  56. /// <returns></returns>
  57. /// <exception cref="FastGithubException">
  58. private async Task<IPAddress> LookupAsync(string domain, CancellationToken cancellationToken)
  59. {
  60. try
  61. {
  62. var dnsClient = new DnsClient(this.fastGithubConfig.PureDns);
  63. var addresses = await dnsClient.Lookup(domain, DNS.Protocol.RecordType.A, cancellationToken);
  64. var address = addresses?.FirstOrDefault();
  65. if (address == null)
  66. {
  67. throw new Exception($"解析不到{domain}的ip");
  68. }
  69. // 受干扰的dns,常常返回127.0.0.1来阻断请求
  70. // 虽然DnscryptProxy的抗干扰能力,但它仍然可能降级到不安全的普通dns上游
  71. if (address.Equals(IPAddress.Loopback))
  72. {
  73. throw new Exception($"dns被污染,解析{domain}为{address}");
  74. }
  75. this.logger.LogInformation($"[{domain}->{address}]");
  76. return address;
  77. }
  78. catch (Exception ex)
  79. {
  80. var dns = this.fastGithubConfig.PureDns;
  81. throw new FastGithubException($"dns({dns})服务器异常:{ex.Message}", ex);
  82. }
  83. }
  84. }
  85. }