public override async Task <DnsDatagram> QueryAsync(DnsDatagram request, int timeout, int retries, CancellationToken cancellationToken) { _lastQueried = DateTime.UtcNow; async Task <HttpRequestMessage> GetHttpRequest() { //serialize request byte[] requestBuffer; using (MemoryStream mS = new MemoryStream(32)) { request.WriteTo(mS); requestBuffer = mS.ToArray(); } 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); } } HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Post, queryUri); httpRequest.Content = new ByteArrayContent(requestBuffer); httpRequest.Content.Headers.ContentType = new MediaTypeHeaderValue("application/dns-message"); return(httpRequest); } //DoH wire 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(); byte[] responseBuffer = await httpResponse.Content.ReadAsByteArrayAsync(cancellationToken); //parse response using (MemoryStream mS = new MemoryStream(responseBuffer, false)) { DnsDatagram response = DnsDatagram.ReadFrom(mS); response.SetMetadata(_server, _protocol, stopwatch.Elapsed.TotalMilliseconds); if (response.Identifier != request.Identifier) { throw new DnsClientResponseValidationException("Invalid response was received: query ID mismatch."); } 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."); }
public override async Task <DnsDatagram> QueryAsync(DnsDatagram request, int timeout, int retries, CancellationToken cancellationToken) { //serialize request byte[] buffer = new byte[512]; MemoryStream bufferStream = new MemoryStream(buffer); try { request.WriteTo(bufferStream); } catch (NotSupportedException) { throw new DnsClientException("DnsClient cannot send request of more than 512 bytes with UDP protocol."); } int bufferSize = (int)bufferStream.Position; Stopwatch stopwatch = new Stopwatch(); if (_proxy == null) { if (_server.IsIPEndPointStale) { await _server.RecursiveResolveIPAddressAsync(); } using (Socket socket = new Socket(_server.IPEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp)) { stopwatch.Start(); bufferSize = await socket.UdpQueryAsync(new ArraySegment <byte>(buffer, 0, bufferSize), buffer, _server.IPEndPoint, timeout, retries, false, cancellationToken); stopwatch.Stop(); } } else { stopwatch.Start(); bufferSize = await _proxy.UdpQueryAsync(new ArraySegment <byte>(buffer, 0, bufferSize), buffer, _server.EndPoint, timeout, retries, false, cancellationToken); stopwatch.Stop(); } //parse response bufferStream.Position = 0; bufferStream.SetLength(bufferSize); DnsDatagram response = DnsDatagram.ReadFrom(bufferStream); response.SetMetadata(_server, _protocol, stopwatch.Elapsed.TotalMilliseconds); if (response.Identifier != request.Identifier) { throw new DnsClientResponseValidationException("Invalid response was received: query ID mismatch."); } 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); }