Esempio n. 1
0
 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 { }
                }
            }
        }
Esempio n. 3
0
 public abstract DnsResponseMessage Query(IPEndPoint endpoint, DnsRequestMessage request, TimeSpan timeout);
Esempio n. 4
0
        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 { }
                }
            }
        }
Esempio n. 6
0
        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()
                  };
        }
Esempio n. 7
0
        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);
            }
        }
Esempio n. 9
0
        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));
        }
Esempio n. 10
0
        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
                    }
                }
            }
        }