/// <summary> /// Runs the tool /// </summary> /// <param name="request">Incoming request</param> /// <param name="responseStream">Stream to write responses to</param> /// <param name="cancellationToken">Cancellation token for if user cancels the request</param> public async Task RunAsync( DnsTraversalRequest request, IServerStreamWriter <DnsTraversalResponse> responseStream, CancellationToken cancellationToken ) { var responseQueue = new GrpcStreamResponseQueue <DnsTraversalResponse>(responseStream); request.Host = ConvertToArpaNameIfRequired(request); var servers = request.Servers.Count == 0 ? _rootServers : request.Servers; try { await Task.WhenAll(servers.Select( async serverName => await DoLookup( request, responseQueue, cancellationToken, serverName, null, 1 ) )); } finally { await responseQueue.CompleteAsync(); } }
/// <summary> /// Wraps <see cref="DoLookupImpl"/> and ensures each lookup is only performed once, even /// if multiple servers return the same referral. /// </summary> private async Task DoLookup( DnsTraversalRequest request, GrpcStreamResponseQueue <DnsTraversalResponse> responseQueue, CancellationToken cancellationToken, string serverName, IDnsQueryResponse?responseForGlue, uint level ) { await _lookupsByServer.GetOrAdd( $"{serverName}-{level}", new Lazy <Task>(async() => await DoLookupImpl( request, responseQueue, cancellationToken, serverName, responseForGlue, level )) ).Value; }
private async Task DoLookupImpl( DnsTraversalRequest request, GrpcStreamResponseQueue <DnsTraversalResponse> responseQueue, CancellationToken cancellationToken, string serverName, IDnsQueryResponse?responseForGlue, uint level ) { var serverIps = await GetDnsServerIPs(serverName, responseForGlue); var client = new LookupClient(new LookupClientOptions(serverIps) { UseCache = false, ThrowDnsErrors = true, Retries = 0, Timeout = _timeout, }); IDnsQueryResponse response; var stopwatch = new Stopwatch(); stopwatch.Start(); try { response = await client.QueryAsync( request.Host, request.Type.ToQueryType(), cancellationToken : cancellationToken ); stopwatch.Stop(); } catch (Exception ex) { stopwatch.Stop(); await responseQueue.WriteAsync(new DnsTraversalResponse { Duration = (uint)stopwatch.ElapsedMilliseconds, Error = new Error { Title = $"Failed: {ex.Message}", Message = $"There is a problem with the DNS server at {serverName}", }, From = serverName, Level = level, }, cancellationToken); return; } // DNS server was authoritive and no results exist if (response.Header.HasAuthorityAnswer && response.Answers.Count == 0) { await responseQueue.WriteAsync(new DnsTraversalResponse { Duration = (uint)stopwatch.ElapsedMilliseconds, Error = new Error { Title = "Failed: No results", Message = "This DNS record does not exist", }, From = serverName, Level = level, }, cancellationToken); return; } await responseQueue.WriteAsync(new DnsTraversalResponse { Duration = (uint)stopwatch.ElapsedMilliseconds, From = serverName, Level = level, Reply = response.ToReply() }, cancellationToken); // Was this server non-authoritive? if (response.Answers.Count == 0) { await Task.WhenAll( response.Authorities.NsRecords().Select(async record => await DoLookup( request, responseQueue, cancellationToken, record.NSDName.Value.TrimEnd('.'), response, level + 1 )) ); } }