コード例 #1
0
        public override async Task <DnsDatagram> QueryAsync(DnsDatagram request, int timeout, int retries, CancellationToken cancellationToken)
        {
            _lastQueried = DateTime.UtcNow;

            async Task <HttpRequestMessage> GetHttpRequest()
            {
                Uri queryUri;

                if (_proxy == null)
                {
                    if (_server.IsIPEndPointStale)
                    {
                        await _server.RecursiveResolveIPAddressAsync();
                    }

                    queryUri = new Uri(_server.DnsOverHttpEndPoint.Scheme + "://" + _server.IPEndPoint.ToString() + _server.DnsOverHttpEndPoint.PathAndQuery);
                }
                else
                {
                    if (_server.IPEndPoint == null)
                    {
                        queryUri = _server.DnsOverHttpEndPoint;
                    }
                    else
                    {
                        queryUri = new Uri(_server.DnsOverHttpEndPoint.Scheme + "://" + _server.IPEndPoint.ToString() + _server.DnsOverHttpEndPoint.PathAndQuery);
                    }
                }

                return(new HttpRequestMessage(HttpMethod.Get, queryUri.AbsoluteUri + "?name=" + request.Question[0].Name + "&type=" + Convert.ToString((int)request.Question[0].Type)));
            }

            //DoH JSON format request
            Stopwatch stopwatch = new Stopwatch();
            int       retry     = 0;

            while (retry < retries) //retry loop
            {
                retry++;

                if (cancellationToken.IsCancellationRequested)
                {
                    return(await Task.FromCanceled <DnsDatagram>(cancellationToken)); //task cancelled
                }
                stopwatch.Start();

                Task <HttpResponseMessage> task = _httpClient.SendAsync(await GetHttpRequest(), cancellationToken);

                using (CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource())
                {
                    using (CancellationTokenRegistration ctr = cancellationToken.Register(delegate() { timeoutCancellationTokenSource.Cancel(); }))
                    {
                        if (await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token)) != task)
                        {
                            continue; //request timed out; retry
                        }
                    }

                    timeoutCancellationTokenSource.Cancel(); //to stop delay task
                }

                HttpResponseMessage httpResponse = await task;

                stopwatch.Stop();
                httpResponse.EnsureSuccessStatusCode();

                string responseJson = await httpResponse.Content.ReadAsStringAsync(cancellationToken);

                //parse response
                DnsDatagram response = DnsDatagram.ReadFromJson(JsonConvert.DeserializeObject(responseJson), responseJson.Length);

                response.SetIdentifier(request.Identifier);
                response.SetMetadata(_server, _protocol, stopwatch.Elapsed.TotalMilliseconds);

                if (response.Question.Count != request.Question.Count)
                {
                    throw new DnsClientResponseValidationException("Invalid response was received: question count mismatch.");
                }

                for (int i = 0; i < response.Question.Count; i++)
                {
                    if (request.Question[i].ZoneCut == null)
                    {
                        if (!response.Question[i].Name.Equals(request.Question[i].Name, StringComparison.Ordinal))
                        {
                            throw new DnsClientResponseValidationException("Invalid response was received: QNAME mismatch.");
                        }

                        if (response.Question[i].Type != request.Question[i].Type)
                        {
                            throw new DnsClientResponseValidationException("Invalid response was received: QTYPE mismatch.");
                        }
                    }
                    else
                    {
                        if (!response.Question[i].Name.Equals(request.Question[i].MinimizedName, StringComparison.Ordinal))
                        {
                            throw new DnsClientResponseValidationException("Invalid response was received: QNAME mismatch.");
                        }

                        if (response.Question[i].Type != request.Question[i].MinimizedType)
                        {
                            throw new DnsClientResponseValidationException("Invalid response was received: QTYPE mismatch.");
                        }
                    }

                    if (response.Question[i].Class != request.Question[i].Class)
                    {
                        throw new DnsClientResponseValidationException("Invalid response was received: QCLASS mismatch.");
                    }
                }

                return(response);
            }

            throw new DnsClientException("DnsClient failed to resolve the request: request timed out.");
        }