FastGithub/FastGithub.Dns/DnsOverUdpServer.cs
老九 bec32d2e35 dnscrypt-proxy使用随机端口;
支持多个回退DNS上游;
2021-08-27 00:51:24 +08:00

116 lines
4.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using DNS.Protocol;
using FastGithub.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace FastGithub.Dns
{
/// <summary>
/// dns服务器
/// </summary>
sealed class DnsOverUdpServer : IDisposable
{
private readonly RequestResolver requestResolver;
private readonly ILogger<DnsOverUdpServer> logger;
private readonly Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
private readonly byte[] buffer = new byte[ushort.MaxValue];
/// <summary>
/// dns服务器
/// </summary>
/// <param name="requestResolver"></param>
/// <param name="logger"></param>
public DnsOverUdpServer(
RequestResolver requestResolver,
ILogger<DnsOverUdpServer> logger)
{
this.requestResolver = requestResolver;
this.logger = logger;
}
/// <summary>
/// 绑定地址和端口
/// </summary>
/// <param name="address"></param>
/// <param name="port"></param>
/// <returns></returns>
public void Bind(IPAddress address, int port)
{
if (OperatingSystem.IsWindows())
{
UdpTable.KillPortOwner(port);
}
if (LocalMachine.CanListenUdp(port) == false)
{
throw new FastGithubException($"udp端口{port}已经被其它进程占用");
}
if (OperatingSystem.IsWindows())
{
const int SIO_UDP_CONNRESET = unchecked((int)0x9800000C);
this.socket.IOControl(SIO_UDP_CONNRESET, new byte[4], new byte[4]);
}
this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
this.socket.Bind(new IPEndPoint(address, port));
}
/// <summary>
/// 监听和处理dns请求
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task HandleAsync(CancellationToken cancellationToken)
{
var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
while (cancellationToken.IsCancellationRequested == false)
{
try
{
var result = await this.socket.ReceiveFromAsync(this.buffer, SocketFlags.None, remoteEndPoint);
var datas = new byte[result.ReceivedBytes];
this.buffer.AsSpan(0, datas.Length).CopyTo(datas);
this.HandleRequestAsync(datas, result.RemoteEndPoint, cancellationToken);
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
{
break;
}
}
}
/// <summary>
/// 处理dns请求
/// </summary>
/// <param name="datas"></param>
/// <param name="remoteEndPoint"></param>
/// <param name="cancellationToken"></param>
private async void HandleRequestAsync(byte[] datas, EndPoint remoteEndPoint, CancellationToken cancellationToken)
{
try
{
var request = Request.FromArray(datas);
var remoteEndPointRequest = new RemoteEndPointRequest(request, remoteEndPoint);
var response = await this.requestResolver.Resolve(remoteEndPointRequest, cancellationToken);
await this.socket.SendToAsync(response.ToArray(), SocketFlags.None, remoteEndPoint);
}
catch (Exception ex)
{
this.logger.LogWarning($"处理DNS异常{ex.Message}");
}
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
this.socket.Dispose();
}
}
}