2
0

DnsOverUdpServer.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. using DNS.Protocol;
  2. using FastGithub.Configuration;
  3. using Microsoft.Extensions.Logging;
  4. using Microsoft.Extensions.Options;
  5. using System;
  6. using System.Net;
  7. using System.Net.Sockets;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. namespace FastGithub.Dns
  11. {
  12. /// <summary>
  13. /// dns服务器
  14. /// </summary>
  15. sealed class DnsOverUdpServer : IDisposable
  16. {
  17. private readonly RequestResolver requestResolver;
  18. private readonly ILogger<DnsOverUdpServer> logger;
  19. private readonly Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  20. private readonly byte[] buffer = new byte[ushort.MaxValue];
  21. private bool listened = false;
  22. /// <summary>
  23. /// dns服务器
  24. /// </summary>
  25. /// <param name="requestResolver"></param>
  26. /// <param name="logger"></param>
  27. public DnsOverUdpServer(
  28. RequestResolver requestResolver,
  29. ILogger<DnsOverUdpServer> logger,
  30. IOptionsMonitor<FastGithubOptions> options)
  31. {
  32. this.requestResolver = requestResolver;
  33. this.logger = logger;
  34. options.OnChange(opt => SystemDnsUtil.FlushResolverCache());
  35. }
  36. /// <summary>
  37. /// 监听IP地址和端口
  38. /// </summary>
  39. /// <param name="address"></param>
  40. /// <param name="port"></param>
  41. /// <exception cref="SocketException"></exception>
  42. /// <exception cref="FastGithubException"></exception>
  43. public void Listen(IPAddress address, int port)
  44. {
  45. if (OperatingSystem.IsWindows())
  46. {
  47. UdpTable.KillPortOwner(port);
  48. }
  49. if (LocalMachine.CanListenUdp(port) == false)
  50. {
  51. throw new FastGithubException($"udp端口{port}已经被其它进程占用");
  52. }
  53. if (OperatingSystem.IsWindows())
  54. {
  55. const int SIO_UDP_CONNRESET = unchecked((int)0x9800000C);
  56. this.socket.IOControl(SIO_UDP_CONNRESET, new byte[4], new byte[4]);
  57. }
  58. this.socket.Bind(new IPEndPoint(address, port));
  59. this.listened = true;
  60. try
  61. {
  62. SystemDnsUtil.SetAsPrimitiveDns();
  63. SystemDnsUtil.FlushResolverCache();
  64. }
  65. catch (Exception ex)
  66. {
  67. this.logger.LogWarning(ex.Message);
  68. }
  69. }
  70. /// <summary>
  71. /// 监听和处理dns请求
  72. /// </summary>
  73. /// <param name="cancellationToken"></param>
  74. /// <returns></returns>
  75. public async Task HandleAsync(CancellationToken cancellationToken)
  76. {
  77. if (this.listened == false)
  78. {
  79. return;
  80. }
  81. var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
  82. while (cancellationToken.IsCancellationRequested == false)
  83. {
  84. try
  85. {
  86. var result = await this.socket.ReceiveFromAsync(this.buffer, SocketFlags.None, remoteEndPoint);
  87. var datas = new byte[result.ReceivedBytes];
  88. this.buffer.AsSpan(0, datas.Length).CopyTo(datas);
  89. this.HandleRequestAsync(datas, result.RemoteEndPoint, cancellationToken);
  90. }
  91. catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
  92. {
  93. break;
  94. }
  95. }
  96. }
  97. /// <summary>
  98. /// 处理dns请求
  99. /// </summary>
  100. /// <param name="datas"></param>
  101. /// <param name="remoteEndPoint"></param>
  102. /// <param name="cancellationToken"></param>
  103. private async void HandleRequestAsync(byte[] datas, EndPoint remoteEndPoint, CancellationToken cancellationToken)
  104. {
  105. try
  106. {
  107. var request = Request.FromArray(datas);
  108. var remoteEndPointRequest = new RemoteEndPointRequest(request, remoteEndPoint);
  109. var response = await this.requestResolver.Resolve(remoteEndPointRequest, cancellationToken);
  110. await this.socket.SendToAsync(response.ToArray(), SocketFlags.None, remoteEndPoint);
  111. }
  112. catch (Exception ex)
  113. {
  114. this.logger.LogWarning($"处理DNS异常:{ex.Message}");
  115. }
  116. }
  117. /// <summary>
  118. /// 释放资源
  119. /// </summary>
  120. public void Dispose()
  121. {
  122. this.socket.Dispose();
  123. if (this.listened == false)
  124. {
  125. return;
  126. }
  127. try
  128. {
  129. SystemDnsUtil.RemoveFromPrimitiveDns();
  130. }
  131. catch (Exception ex)
  132. {
  133. this.logger.LogWarning(ex.Message);
  134. }
  135. finally
  136. {
  137. SystemDnsUtil.FlushResolverCache();
  138. }
  139. }
  140. }
  141. }