public abstract Task <DnsResponseMessage> QueryAsync(IPEndPoint server, DnsRequestMessage request, CancellationToken cancellationToken, Action <Action> cancelationCallback);
public override async Task <DnsResponseMessage> QueryAsync( IPEndPoint server, DnsRequestMessage request, CancellationToken cancellationToken, Action <Action> cancelationCallback) { cancellationToken.ThrowIfCancellationRequested(); UdpClient udpClient = GetNextUdpClient(server.AddressFamily); bool mustDispose = false; try { // setup timeout cancelation, dispose socket (the only way to acutally cancel the request in async... cancelationCallback(() => { #if PORTABLE udpClient.Dispose(); #else udpClient.Close(); #endif }); using (var writer = new DnsDatagramWriter()) { GetRequestData(request, writer); await udpClient.SendAsync(writer.Data.Array, writer.Data.Count, server).ConfigureAwait(false); } var readSize = udpClient.Available > MaxSize ? udpClient.Available : MaxSize; using (var memory = new PooledBytes(readSize)) { #if PORTABLE int received = await udpClient.Client.ReceiveAsync(new ArraySegment <byte>(memory.Buffer), SocketFlags.None).ConfigureAwait(false); var response = GetResponseMessage(new ArraySegment <byte>(memory.Buffer, 0, received)); #else var result = await udpClient.ReceiveAsync().ConfigureAwait(false); var response = GetResponseMessage(new ArraySegment <byte>(result.Buffer, 0, result.Buffer.Length)); #endif if (request.Header.Id != response.Header.Id) { throw new DnsResponseException("Header id missmatch."); } Enqueue(server.AddressFamily, udpClient); return(response); } } catch (ObjectDisposedException) { // we disposed it in case of a timeout request, lets indicate it actually timed out... throw new TimeoutException(); } catch { mustDispose = true; throw; } finally { if (!_enableClientQueue || mustDispose) { try { #if PORTABLE udpClient.Dispose(); #else udpClient.Close(); #endif } catch { } } } }
public abstract DnsResponseMessage Query(IPEndPoint endpoint, DnsRequestMessage request, TimeSpan timeout);
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() }; }
public override DnsResponseMessage Query( IPEndPoint server, DnsRequestMessage request, TimeSpan timeout) { UdpClient udpClient = GetNextUdpClient(server.AddressFamily); // -1 indicates infinite int timeoutInMillis = timeout.TotalMilliseconds >= int.MaxValue ? -1 : (int)timeout.TotalMilliseconds; udpClient.Client.ReceiveTimeout = timeoutInMillis; udpClient.Client.SendTimeout = timeoutInMillis; bool mustDispose = false; try { using (var writer = new DnsDatagramWriter()) { GetRequestData(request, writer); udpClient.Client.SendTo(writer.Data.Array, writer.Data.Offset, writer.Data.Count, SocketFlags.None, server); } var readSize = udpClient.Available > MaxSize ? udpClient.Available : MaxSize; using (var memory = new PooledBytes(readSize)) { var received = udpClient.Client.Receive(memory.Buffer, 0, readSize, SocketFlags.None); var response = GetResponseMessage(new ArraySegment <byte>(memory.Buffer, 0, received)); if (request.Header.Id != response.Header.Id) { throw new DnsResponseException("Header id missmatch."); } Enqueue(server.AddressFamily, udpClient); return(response); } } catch { mustDispose = true; throw; } finally { if (!_enableClientQueue || mustDispose) { try { #if PORTABLE udpClient.Dispose(); #else udpClient.Close(); #endif } catch { } } } }
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() }; }
public override async Task <DnsResponseMessage> QueryAsync( IPEndPoint server, DnsRequestMessage request, CancellationToken cancellationToken, Action <Action> cancelationCallback) { cancellationToken.ThrowIfCancellationRequested(); using (var client = new TcpClient(server.AddressFamily)) { cancelationCallback(() => { #if PORTABLE client.Dispose(); #else client.Close(); #endif }); cancellationToken.ThrowIfCancellationRequested(); await client.ConnectAsync(server.Address, server.Port).ConfigureAwait(false); using (var stream = client.GetStream()) { // use a pooled buffer to writer the data + the length of the data later into the frist two bytes using (var memory = new PooledBytes(DnsDatagramWriter.BufferSize + 2)) using (var writer = new DnsDatagramWriter(new ArraySegment <byte>(memory.Buffer, 2, memory.Buffer.Length - 2))) { GetRequestData(request, writer); int dataLength = writer.Index; memory.Buffer[0] = (byte)((dataLength >> 8) & 0xff); memory.Buffer[1] = (byte)(dataLength & 0xff); //var sendData = new byte[dataLength + 2]; //sendData[0] = (byte)((dataLength >> 8) & 0xff); //sendData[1] = (byte)(dataLength & 0xff); //Array.Copy(data, 0, sendData, 2, dataLength); await stream.WriteAsync(memory.Buffer, 0, dataLength + 2, cancellationToken).ConfigureAwait(false); await stream.FlushAsync(cancellationToken).ConfigureAwait(false); } int length = stream.ReadByte() << 8 | stream.ReadByte(); if (length <= 0) { throw new DnsResponseException("Received no answer."); } var resultData = new byte[length]; int bytesReceived = 0; while (bytesReceived < length) { int read = await stream.ReadAsync(resultData, bytesReceived, length - bytesReceived, cancellationToken).ConfigureAwait(false); bytesReceived += read; if (read == 0 && bytesReceived < length) { // disconnected throw new SocketException(-1); } } var response = GetResponseMessage(new ArraySegment <byte>(resultData, 0, bytesReceived)); if (request.Header.Id != response.Header.Id) { throw new DnsResponseException("Header id missmatch."); } return(response); } } }
private async Task <DnsResponseMessage> QueryAsyncInternal( Stream stream, DnsRequestMessage request, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // use a pooled buffer to writer the data + the length of the data later into the frist two bytes using (var memory = new PooledBytes(DnsDatagramWriter.BufferSize + 2)) { var writer = new DnsDatagramWriter(new ArraySegment <byte>(memory.Buffer, 2, memory.Buffer.Length - 2)); GetRequestData(request, writer); var dataLength = writer.Index; memory.Buffer[0] = (byte)((dataLength >> 8) & 0xff); memory.Buffer[1] = (byte)(dataLength & 0xff); //await client.Client.SendAsync(new ArraySegment<byte>(memory.Buffer, 0, dataLength + 2), SocketFlags.None).ConfigureAwait(false); await stream.WriteAsync(memory.Buffer, 0, dataLength + 2, cancellationToken).ConfigureAwait(false); await stream.FlushAsync(cancellationToken).ConfigureAwait(false); } if (!stream.CanRead) { return(null); } cancellationToken.ThrowIfCancellationRequested(); int length; try { length = stream.ReadByte() << 8 | stream.ReadByte(); } catch (Exception ex) when(ex is IOException || ex is SocketException) { return(null); } if (length <= 0) { // server signals close/disconnecting return(null); } using (var memory = new PooledBytes(length)) { var bytesReceived = 0; var readSize = length > 4096 ? 4096 : length; while ((bytesReceived += await stream.ReadAsync(memory.Buffer, bytesReceived, readSize).ConfigureAwait(false)) < length) { if (bytesReceived <= 0) { // disconnected return(null); } if (bytesReceived + readSize > length) { readSize = length - bytesReceived; if (readSize <= 0) { break; } } } var response = GetResponseMessage(new ArraySegment <byte>(memory.Buffer, 0, bytesReceived)); if (request.Header.Id != response.Header.Id) { throw new DnsResponseException("Header id mismatch."); } return(response); } }
private async Task <DnsResponseMessage> QueryAsyncInternal(TcpClient client, DnsRequestMessage request, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var stream = client.GetStream(); // use a pooled buffer to writer the data + the length of the data later into the first two bytes using (var memory = new PooledBytes(DnsDatagramWriter.BufferSize + 2)) using (var writer = new DnsDatagramWriter(new ArraySegment <byte>(memory.Buffer, 2, memory.Buffer.Length - 2))) { GetRequestData(request, writer); int dataLength = writer.Index; memory.Buffer[0] = (byte)((dataLength >> 8) & 0xff); memory.Buffer[1] = (byte)(dataLength & 0xff); //await client.Client.SendAsync(new ArraySegment<byte>(memory.Buffer, 0, dataLength + 2), SocketFlags.None).ConfigureAwait(false); await stream.WriteAsync(memory.Buffer, 0, dataLength + 2, cancellationToken).ConfigureAwait(false); await stream.FlushAsync(cancellationToken).ConfigureAwait(false); } if (!stream.CanRead) { return(null); } cancellationToken.ThrowIfCancellationRequested(); var responses = new List <DnsResponseMessage>(); do { int length; try { //length = stream.ReadByte() << 8 | stream.ReadByte(); using (var lengthBuffer = new PooledBytes(2)) { int bytesReceived = 0, read; while ((bytesReceived += (read = await stream.ReadAsync(lengthBuffer.Buffer, bytesReceived, 2, cancellationToken).ConfigureAwait(false))) < 2) { if (read <= 0) { // disconnected, might retry return(null); } } length = lengthBuffer.Buffer[0] << 8 | lengthBuffer.Buffer[1]; } } catch (Exception ex) when(ex is IOException || ex is SocketException) { return(null); } if (length <= 0) { // server signals close/disconnecting return(null); } using (var memory = new PooledBytes(length)) { int bytesReceived = 0, read; int readSize = length > 4096 ? 4096 : length; while (!cancellationToken.IsCancellationRequested && (bytesReceived += (read = await stream.ReadAsync(memory.Buffer, bytesReceived, readSize, cancellationToken).ConfigureAwait(false))) < length) { if (read <= 0) { // disconnected return(null); } if (bytesReceived + readSize > length) { readSize = length - bytesReceived; if (readSize <= 0) { break; } } } DnsResponseMessage response = GetResponseMessage(new ArraySegment <byte>(memory.Buffer, 0, bytesReceived)); if (request.Header.Id != response.Header.Id) { throw new DnsResponseException("Header id mismatch."); } responses.Add(response); } } while (stream.DataAvailable && !cancellationToken.IsCancellationRequested); return(DnsResponseMessage.Combine(responses)); }
public override async Task <DnsResponseMessage> QueryAsync( IPEndPoint server, DnsRequestMessage request, CancellationToken cancellationToken, Action <Action> cancelationCallback) { cancellationToken.ThrowIfCancellationRequested(); var udpClient = GetNextUdpClient(server.AddressFamily); var mustDispose = false; try { // setup timeout cancelation, dispose socket (the only way to acutally cancel the request in async... cancelationCallback(() => { udpClient.Close(); }); using (var pool = new PooledBytes(DnsDatagramWriter.BufferSize)) { var writer = new DnsDatagramWriter(new ArraySegment <byte>(pool.Buffer)); GetRequestData(request, writer); await udpClient.SendAsync(writer.Data.Array, writer.Data.Count, server).ConfigureAwait(false); } var result = await udpClient.ReceiveAsync().ConfigureAwait(false); var response = GetResponseMessage(new ArraySegment <byte>(result.Buffer, 0, result.Buffer.Length)); if (request.Header.Id != response.Header.Id) { throw new DnsResponseException("Header id mismatch."); } Enqueue(server.AddressFamily, udpClient); return(response); } catch (ObjectDisposedException) { // we disposed it in case of a timeout request, lets indicate it actually timed out... throw new TimeoutException(); } catch { mustDispose = true; throw; } finally { if (!_enableClientQueue || mustDispose) { try { udpClient.Close(); } catch { // ignored } } } }