private async Task <List <ReverseDnsResponse> > GetDnsResponses <T>(List <string> hosts, QueryType queryType)
            where T : AddressRecord
        {
            List <ReverseDnsResponse> responses = new List <ReverseDnsResponse>();

            foreach (var host in hosts)
            {
                List <string> ipAddresses = new List <string>();

                if (!string.IsNullOrWhiteSpace(host))
                {
                    ReverseDnsQueryResponse forward = await _dnsResolver.QueryAddressAsync <T>(host, queryType);

                    if (!forward.HasError)
                    {
                        ipAddresses.AddRange(forward.Results);
                        responses.Add(new ReverseDnsResponse(host, ipAddresses));
                    }
                    else
                    {
                        _log.LogWarning("Failed to do dns query type: {QueryType} for {Host} with error: {Error}.",
                                        queryType, host, forward.ErrorMessage ?? "Unknown error");
                    }
                }
            }

            return(responses);
        }
        private async Task <ReverseDnsResult> GetReverseDnsResult(IPAddress originalAddress)
        {
            int attempt     = 0;
            int maxAttempts = 5;

            while (attempt < maxAttempts)
            {
                await Task.Delay(attempt *attempt * 1000);

                attempt++;

                try
                {
                    ReverseDnsQueryResponse response = await _dnsResolver.QueryPtrAsync(originalAddress);

                    if (response.HasError && attempt < maxAttempts)
                    {
                        continue;
                    }

                    if (response.HasError)
                    {
                        _log.LogWarning("Failed to do PTR look up for {IpAddress} with error: {Error}.", originalAddress, response.ErrorMessage ?? "Unknown error");
                        return(ReverseDnsResult.InvalidReverseDnsResult);
                    }

                    List <string> hosts = response.Results;

                    List <ReverseDnsResponse> forwardResponses = originalAddress.AddressFamily == AddressFamily.InterNetwork
                        ? await GetDnsResponses <ARecord>(hosts, QueryType.A)
                        : await GetDnsResponses <AaaaRecord>(hosts, QueryType.AAAA);

                    return(new ReverseDnsResult(originalAddress.ToString(), forwardResponses));
                }
                catch (Exception e)
                {
                    string errorMessage = $"Error occured performing PTR lookup for {originalAddress} (attempt {attempt} of {maxAttempts})";

                    if (attempt == maxAttempts)
                    {
                        _log.LogError(e, errorMessage);
                    }
                    else
                    {
                        _log.LogWarning(e, errorMessage);
                    }
                }
            }
            return(ReverseDnsResult.InvalidReverseDnsResult);
        }
        public async Task ItShouldLogErrorWhenUnableToDoLookupForPtr(string ipAddress)
        {
            ReverseDnsQueryResponse response = new ReverseDnsQueryResponse(true, "Something has gone wrong!", null);

            A.CallTo(() => _dnsResolver.QueryPtrAsync(A <IPAddress> ._)).Returns(response);

            var reverseDnsLookup = new ReverseDnsLookup(_dnsResolver, _log);

            ReverseDnsResult result = await reverseDnsLookup.Lookup(ipAddress);

            Assert.That(_log.Warnings.Count, Is.EqualTo(1));
            Assert.That(result.ForwardResponses.Count, Is.EqualTo(0));
            Assert.IsNull(result.OriginalIpAddress);
        }
        public async Task ItShouldProcessAllValidLookups(string ipAddress)
        {
            string googleHostName = "google.com";
            string googleIp1      = "192.168.0.1";
            string googleIp2      = "192.168.0.2";

            string yahooHostName = "yahoo.com";
            string yahooIp1      = "192.168.1.1";

            ReverseDnsQueryResponse ptrDnsQueryResponse = new ReverseDnsQueryResponse(false, string.Empty, new List <string>()
            {
                googleHostName, yahooHostName
            });
            ReverseDnsQueryResponse googleAddressDnsQueryResponse = new ReverseDnsQueryResponse(false, string.Empty, new List <string>()
            {
                googleIp1, googleIp2
            });
            ReverseDnsQueryResponse yahooAddressDnsQueryResponse = new ReverseDnsQueryResponse(false, string.Empty, new List <string>()
            {
                yahooIp1
            });

            A.CallTo(() => _dnsResolver.QueryPtrAsync(A <IPAddress> ._)).Returns(ptrDnsQueryResponse);
            A.CallTo(() => _dnsResolver.QueryAddressAsync <ARecord>(googleHostName, QueryType.A)).Returns(googleAddressDnsQueryResponse);
            A.CallTo(() => _dnsResolver.QueryAddressAsync <AaaaRecord>(googleHostName, QueryType.AAAA)).Returns(googleAddressDnsQueryResponse);
            A.CallTo(() => _dnsResolver.QueryAddressAsync <ARecord>(yahooHostName, QueryType.A)).Returns(yahooAddressDnsQueryResponse);
            A.CallTo(() => _dnsResolver.QueryAddressAsync <AaaaRecord>(yahooHostName, QueryType.AAAA)).Returns(yahooAddressDnsQueryResponse);

            var reverseDnsLookup = new ReverseDnsLookup(_dnsResolver, _log);

            ReverseDnsResult reverseDnsResult = await reverseDnsLookup.Lookup(ipAddress);

            Assert.That(_log.Errors.Count, Is.EqualTo(0));
            Assert.That(_log.Warnings.Count, Is.EqualTo(0));

            Assert.That(reverseDnsResult.ForwardResponses.Count, Is.EqualTo(2));
            Assert.That(reverseDnsResult.ForwardResponses[0].Host, Is.EqualTo(googleHostName));
            Assert.That(reverseDnsResult.ForwardResponses[0].IpAddresses.Count, Is.EqualTo(2));
            Assert.That(reverseDnsResult.ForwardResponses[0].IpAddresses[0], Is.EqualTo(googleIp1));
            Assert.That(reverseDnsResult.ForwardResponses[0].IpAddresses[1], Is.EqualTo(googleIp2));
            Assert.That(reverseDnsResult.ForwardResponses[1].Host, Is.EqualTo(yahooHostName));
            Assert.That(reverseDnsResult.ForwardResponses[1].IpAddresses.Count, Is.EqualTo(1));
            Assert.That(reverseDnsResult.ForwardResponses[1].IpAddresses[0], Is.EqualTo(yahooIp1));
        }
        public async Task ItShouldLogErrorWhenValidPtrButFailsOnForwardLookup(string ipAddress)
        {
            ReverseDnsQueryResponse ptrDnsQueryResponse = new ReverseDnsQueryResponse(false, string.Empty, new List <string>()
            {
                "google.com"
            });
            ReverseDnsQueryResponse addressDnsQueryResponse = new ReverseDnsQueryResponse(true, "Something has gone wrong!", null);

            A.CallTo(() => _dnsResolver.QueryPtrAsync(A <IPAddress> ._)).Returns(ptrDnsQueryResponse);
            A.CallTo(() => _dnsResolver.QueryAddressAsync <ARecord>(A <string> ._, QueryType.A)).Returns(addressDnsQueryResponse);
            A.CallTo(() => _dnsResolver.QueryAddressAsync <AaaaRecord>(A <string> ._, QueryType.AAAA)).Returns(addressDnsQueryResponse);

            var reverseDnsLookup = new ReverseDnsLookup(_dnsResolver, _log);

            ReverseDnsResult reverseDnsResult = await reverseDnsLookup.Lookup(ipAddress);

            Assert.That(_log.Warnings.Count, Is.EqualTo(1));
            Assert.That(reverseDnsResult.ForwardResponses.Count, Is.EqualTo(0));
        }
        public async Task ItShouldOnlyProcessValidLookupsAndNotStopWhenInvalid(string ipAddress)
        {
            string googleHostName = "google.com";
            string googleIp1      = "192.168.0.1";
            string yahooHostName  = "yahoo.com";

            ReverseDnsQueryResponse ptrDnsQueryResponse = new ReverseDnsQueryResponse(false, string.Empty, new List <string>()
            {
                googleHostName, yahooHostName
            });
            ReverseDnsQueryResponse host1AddressDnsQueryResponse = new ReverseDnsQueryResponse(false, string.Empty, new List <string>()
            {
                googleIp1
            });
            ReverseDnsQueryResponse host2AaddressDnsQueryResponse = new ReverseDnsQueryResponse(true, "Something has gone wrong!", null);

            A.CallTo(() => _dnsResolver.QueryPtrAsync(A <IPAddress> ._)).Returns(ptrDnsQueryResponse);

            //setup for only host1
            A.CallTo(() => _dnsResolver.QueryAddressAsync <ARecord>(googleHostName, QueryType.A)).Returns(host1AddressDnsQueryResponse);
            A.CallTo(() => _dnsResolver.QueryAddressAsync <AaaaRecord>(googleHostName, QueryType.AAAA)).Returns(host1AddressDnsQueryResponse);

            //setup for only host1
            A.CallTo(() => _dnsResolver.QueryAddressAsync <ARecord>(yahooHostName, QueryType.A)).Returns(host2AaddressDnsQueryResponse);
            A.CallTo(() => _dnsResolver.QueryAddressAsync <AaaaRecord>(yahooHostName, QueryType.AAAA)).Returns(host2AaddressDnsQueryResponse);

            var reverseDnsLookup = new ReverseDnsLookup(_dnsResolver, _log);

            ReverseDnsResult reverseDnsResults = await reverseDnsLookup.Lookup(ipAddress);

            Assert.That(_log.Warnings.Count, Is.EqualTo(1));

            Assert.That(reverseDnsResults.ForwardResponses.Count, Is.EqualTo(1));
            Assert.That(reverseDnsResults.ForwardResponses[0].Host, Is.EqualTo(googleHostName));
            Assert.That(reverseDnsResults.ForwardResponses[0].IpAddresses.Count, Is.EqualTo(1));
            Assert.That(reverseDnsResults.ForwardResponses[0].IpAddresses[0], Is.EqualTo(googleIp1));
        }