private async Task <IDnsQueryResponse> ResolveQueryAsync(DnsMessageHandler handler, DnsRequestMessage request, CancellationToken cancellationToken, Audit continueAudit = null) { if (request == null) { throw new ArgumentNullException(nameof(request)); } var audit = continueAudit ?? new Audit(); var servers = GetNextServers(); foreach (var serverInfo in servers) { var tries = 0; do { tries++; try { cancellationToken.ThrowIfCancellationRequested(); if (EnableAuditTrail) { audit.StartTimer(); } DnsResponseMessage response; Action onCancel = () => { }; Task <DnsResponseMessage> resultTask = handler.QueryAsync(serverInfo.Endpoint, request, cancellationToken, (cancel) => { onCancel = cancel; }); if (Timeout != s_infiniteTimeout || (cancellationToken != CancellationToken.None && cancellationToken.CanBeCanceled)) { var cts = new CancellationTokenSource(Timeout); CancellationTokenSource linkedCts = null; if (cancellationToken != CancellationToken.None) { linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancellationToken); } using (cts) using (linkedCts) { response = await resultTask.WithCancellation((linkedCts ?? cts).Token, onCancel).ConfigureAwait(false); } } else { response = await resultTask.ConfigureAwait(false); } if (response.Header.ResultTruncated && UseTcpFallback && !handler.GetType().Equals(typeof(DnsTcpMessageHandler))) { if (EnableAuditTrail) { audit.AuditTruncatedRetryTcp(); } return(await ResolveQueryAsync(_tcpFallbackHandler, request, cancellationToken, audit).ConfigureAwait(false)); } if (EnableAuditTrail) { audit.AuditResolveServers(_endpoints.Count); audit.AuditResponseHeader(response.Header); } if (response.Header.ResponseCode != DnsResponseCode.NoError) { if (EnableAuditTrail) { audit.AuditResponseError(response.Header.ResponseCode); } if (ThrowDnsErrors) { throw new DnsResponseException(response.Header.ResponseCode); } } HandleOptRecords(audit, serverInfo, response); DnsQueryResponse queryResponse = response.AsQueryResponse(serverInfo.Clone()); if (EnableAuditTrail) { audit.AuditResponse(queryResponse); audit.AuditEnd(queryResponse); queryResponse.AuditTrail = audit.Build(); } Interlocked.Increment(ref StaticLog.ResolveQueryCount); Interlocked.Add(ref StaticLog.ResolveQueryTries, tries); return(queryResponse); } catch (DnsResponseException ex) { audit.AuditException(ex); ex.AuditTrail = audit.Build(); throw; } catch (SocketException ex) when(ex.SocketErrorCode == SocketError.AddressFamilyNotSupported) { // this socket error might indicate the server endpoint is actually bad and should be ignored in future queries. DisableServer(serverInfo); break; } catch (Exception ex) when(ex is TimeoutException || handler.IsTransientException(ex)) { // our timeout got eventually triggered by the a task cancelation token, throw OCE instead... if (cancellationToken.IsCancellationRequested) { throw new OperationCanceledException(cancellationToken); } DisableServer(serverInfo); } catch (Exception ex) { DisableServer(serverInfo); var handleEx = ex; var agg = ex as AggregateException; if (agg != null) { if (agg.InnerExceptions.Any(e => e is TimeoutException || handler.IsTransientException(e))) { continue; } handleEx = agg.InnerException; } if (handleEx is OperationCanceledException || handleEx is TaskCanceledException) { if (cancellationToken.IsCancellationRequested) { throw new OperationCanceledException(cancellationToken); } continue; } audit.AuditException(ex); throw new DnsResponseException("Unhandled exception", ex) { AuditTrail = audit.Build() }; } } while (tries <= Retries && !cancellationToken.IsCancellationRequested && serverInfo.Enabled); } throw new DnsResponseException( DnsResponseCode.ConnectionTimeout, $"No connection could be established to any of the following name servers: {string.Join(", ", NameServers)}.") { AuditTrail = audit.Build() }; }
private IDnsQueryResponse ResolveQuery(DnsMessageHandler handler, DnsRequestMessage request, Audit continueAudit = null) { if (request == null) { throw new ArgumentNullException(nameof(request)); } var audit = continueAudit ?? new Audit(); var servers = GetNextServers(); foreach (var serverInfo in servers) { var tries = 0; do { tries++; try { if (EnableAuditTrail) { audit.StartTimer(); } DnsResponseMessage response = handler.Query(serverInfo.Endpoint, request, Timeout); if (response.Header.ResultTruncated && UseTcpFallback && !handler.GetType().Equals(typeof(DnsTcpMessageHandler))) { if (EnableAuditTrail) { audit.AuditTruncatedRetryTcp(); } return(ResolveQuery(_tcpFallbackHandler, request, audit)); } if (EnableAuditTrail) { audit.AuditResolveServers(_endpoints.Count); audit.AuditResponseHeader(response.Header); } if (response.Header.ResponseCode != DnsResponseCode.NoError) { if (EnableAuditTrail) { audit.AuditResponseError(response.Header.ResponseCode); } if (ThrowDnsErrors) { throw new DnsResponseException(response.Header.ResponseCode); } } HandleOptRecords(audit, serverInfo, response); DnsQueryResponse queryResponse = response.AsQueryResponse(serverInfo.Clone()); if (EnableAuditTrail) { audit.AuditResponse(queryResponse); audit.AuditEnd(queryResponse); queryResponse.AuditTrail = audit.Build(); } Interlocked.Increment(ref StaticLog.ResolveQueryCount); Interlocked.Add(ref StaticLog.ResolveQueryTries, tries); return(queryResponse); } catch (DnsResponseException ex) { audit.AuditException(ex); ex.AuditTrail = audit.Build(); throw; } catch (SocketException ex) when(ex.SocketErrorCode == SocketError.AddressFamilyNotSupported) { // this socket error might indicate the server endpoint is actually bad and should be ignored in future queries. DisableServer(serverInfo); break; } catch (Exception ex) when(ex is TimeoutException || handler.IsTransientException(ex)) { DisableServer(serverInfo); } catch (Exception ex) { DisableServer(serverInfo); if (ex is OperationCanceledException || ex is TaskCanceledException) { // timeout continue; } audit.AuditException(ex); throw new DnsResponseException(DnsResponseCode.Unassigned, "Unhandled exception", ex) { AuditTrail = audit.Build() }; } } while (tries <= Retries && serverInfo.Enabled); } throw new DnsResponseException(DnsResponseCode.ConnectionTimeout, $"No connection could be established to any of the following name servers: {string.Join(", ", NameServers)}.") { AuditTrail = audit.Build() }; }