UWP 向指定的Dns服务器请求域名解析 --简单的解决方案

  笔者近期做的UWP项目需要实现的一个小的功能模块--对指定Dns服务器发起域名解析的请求。刚开始的时候遇到了很多坑,翻阅了dalao们的文章,回答,查阅了官方文档,最终找到了合适的Nuget包得以解决,特此摘记下这个过程:

  先是UWP System.Net命名空间下暂时不能做到对指定的Dns服务器发起这样的请求(System.Net.Dns 仅支持用户使用本地选择的Dns服务器进行域名解析)。上一段在StackOverFlow上dalao关于Dns类的描述

No this cannot be done with the .Net Framework. The Dns.Resolve method relies on the internal Win32 APIs which in turn go through the DNS servers associated with the network connection.

In order to get this to work, you'd have to change the DNS servers associated with the network adapter's address.

  OK,然后笔者尝试了在UWP中调用cmd进行nslookup查询(nslookup 是用于查询DNS的记录的命令)。但是!但是!但是,UWP说明是无法直接调用一系列脚本文件(包括.exe),上一段StackOverFlow里dalao的回复

You cannot not launch external executable from your UWP application. This is prevented by the security model. You are restricted to the methods provided by the Launcher API.

You can open a file with its default application using LaunchFile or LaunchUri. The system will start the application registered by the user to open the file.

然后笔者就去查阅了Launcher类的使用,发现了在Launcher.LaunchFile()方法的描述中,官方有一段特意的强调

The calling app must be visible to the user when the API is invoked.

This API must be called from an ASTA thread (also known as a UI thread).

This API also imposes several restrictions on what types of files it can launch. Many file types that contain executable code, for example .exe, .msi, and .js files, are blocked from launching. This restriction protects users from potentially malicious files that could modify the system.

  OK,然后笔者查找了很多REST API也没有找到特别合适的,Nuget包里有很多也用不了,最后在一位dalao的回复中找到了这个:Heijden.Dns.Portable --Nuget 包的github传送门

一个dalao做的Dns Nuget包,支持指定Dns服务器!

OK,话不多说,上段代码:

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ServerMonitor.Controls
{
    public class DnsRequest : BasicRequest
    {
        // 继承的属性:CreateTime TimeCost OverTime Status Others ErrorException
        /// <summary>
        /// Dns解析记录类型,默认是A记录
        /// </summary>
        QType recordType = QType.A;
        /// <summary>
        /// 测试服务器状态使用的域名
        /// </summary>
        string domainName;
        /// <summary>
        /// 测试期待值
        /// </summary>
        HashSet<string> actualResult = null;
        /// <summary>
        /// Dns服务器IP地址
        /// </summary>
        IPAddress dnsServer;

        public QType RecordType { get => recordType; set => recordType = value; }
        public string DomainName { get => domainName; set => domainName = value; }
        public IPAddress DnsServer { get => dnsServer; set => dnsServer = value; }
        public HashSet<string> ActualResult { get => actualResult; set => actualResult = value; }

        /// <summary>
        /// 生成一个Dns请求对象
        /// </summary>
        /// <param name="DnsServer">用于解析域名的Dns服务器</param>
        /// <param name="DomainName">待解析的域名</param>
        public DnsRequest(IPAddress DnsServer, string DomainName)
        {
            this.DnsServer = DnsServer;
            this.DomainName = DomainName;
        }

        /// <summary>
        /// Dns请求
        /// </summary>
        /// <returns></returns>
        public override async Task<bool> MakeRequest()
        {
            // 赋值生成请求的时间
            CreateTime = DateTime.Now;
            // 创建解析使用的Dns服务器
            var resolver = new Resolver(DnsServer, 53);
            Response response = null;

            // 超时控制
            CancellationTokenSource cts = new CancellationTokenSource();
            cts.CancelAfter(OverTime);
            // 这里二次封装是为了引入超时控制
            try
            {
                // 记录请求耗时
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                // 二次封装任务
                Task t = Task.Run(async () =>
                {
                    response = await resolver.Query(DomainName, RecordType).ConfigureAwait(false);
                }, cts.Token);
                await t;
                stopwatch.Stop();

                if (response.Answers.Count != 0&&t.IsCompleted) // 请求成功,获取到了解析结果
                {
                    // Dns服务器状态良好
                    Status = "1000";
                    // 请求耗时应该在2^15-1(ms)内完成
                    TimeCost = (short)stopwatch.ElapsedMilliseconds;
                    // 记录解析记录
                    actualResult = new HashSet<string>();
                    foreach (var item in response.Answers)
                    {
                        actualResult.Add(item.RECORD.ToString());
                    }
                    Debug.WriteLine(TimeCost+"eeeeeeeeeeeee");                   
                    return true;
                }
                else // 请求失败,无解析结果
                {
                    // Dns服务器状态未知,但是该域名无法解析
                    Status = "1002";
                    // 请求耗时应该在2^15-1(ms)内完成
                    TimeCost = (short)stopwatch.ElapsedMilliseconds;
                    ActualResult.Add("No Data!");
                    return false;
                }
            }
            // 捕获到请求超时的情况
            catch (TaskCanceledException e)
            {
                // Dns服务器超时
                Status = "1003";
                // 收集捕获到的异常
                ErrorException = e;
                // 请求耗时设置为超时上限
                TimeCost = OverTime;
                return false;
            }
            // 这个是TaskCanceledException的基类
            catch (OperationCanceledException e)
            {
                // Dns服务器超时
                Status = "1003";
                // 收集捕获到的异常
                ErrorException = e;
                // 请求耗时设置为超时上限
                TimeCost = OverTime;
                return false;
            }
            // 用于后期做异常捕获延伸
            catch (Exception e)
            {
                // Dns服务器请求出现未捕获到的异常
                Status = "1002";
                // 收集捕获到的异常
                ErrorException = e.InnerException;
                // 请求耗时设置为超时上限
                TimeCost = OverTime;
                return false;
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Domainname"></param>
        /// <returns></returns>
        private bool IsDomainnameCorrect(string Domainname)
        {
            if (string.IsNullOrEmpty(Domainname))
            {
                return false;
            }
            else
            {
                // 判断域名是否合法 ,待补充...
                return Uri.IsWellFormedUriString(Domainname, UriKind.Absolute);
            }

        }

        /// <summary>
        /// 检查expectResult是否命中解析结果resultSet
        /// </summary>
        /// <param name="expectResult"></param>
        /// <param name="resultSet"></param>
        /// <returns></returns>
        public bool IsMatchResult(string expectResult,HashSet<string> resultSet)
        {
            return resultSet.Contains(expectResult);
        }
        /**
         * int i = 0;
         * QType q = (QType)Enum.Parse(typeof(QType), i.ToString());
         * 用来获取枚举值得下标
         */
    }
}

PS:recordType 是有很多种的,取决于Dns服务器登记上关于此域名的记录是哪种类型的(通常多数采用的:A、CNAME、SOA、MX等)

  OK,先记录到这!

猜你喜欢

转载自my.oschina.net/u/3744313/blog/1798676
UWP