I've got a friend who is a security specialist and he uses all kinds of
tools OTHER than Delphi to write his utilities. I ask him why he's not
using Delphi, when it should be able to do everything he's doing with
all these various tools.
So, he asks me to build a sample utility for him that looks up an IP
address, gets the domain name for it, and calls Whois to find out the
country information for the domain.
I figure this is a snap. 2 hours tops with Delphi and Indy. Then I find
out I am woefully ignorant in this area of Indy functionality and can't
find helpful reference information online.
When I get stuck on the first step, a reverse DNS to retrieve the
domain name from the IP address, I spend the entire weekend trying to
find current information and demos for Indy, including the mythically
referenced "DNSResolver demo".
The closest I get is some 8+ year old conversations about DNSResolver,
and a mention on Stack Overflow that simply doesn't resolve any IP
(reversed or not) into the actual domain name for me.
http://stackoverflow.com/questions/8277903/use-indy-to-perform-an-ipv6-reverse-dns-lookup
Perhaps I have some gaps in understanding that code snippet:
1. What is RSCodeQueryName supposed to be?
2. What does ParseReverseDNSResponse do?
So, I figured this is the best place to ask:
1. Is there a current Indy DNSResolver demo?
2. What's wrong with this code, if anything? I changed the code
referenced above in an attempt to simplify/update and work around the
missing info above:
function ReverseDNSLookup(IPAddress: String; DNSServer: String =
SDefaultDNS; Timeout: Integer = 30; Retries: Integer = 3) : string;
var
AIdDNSResolver: TIdDNSResolver;
RetryCount: Integer;
begin
Result := '';
IPAddress := ReverseIP(IPAddress);
AIdDNSResolver := TIdDNSResolver.Create(nil);
try
AIdDNSResolver.QueryResult.Clear;
AIdDNSResolver.WaitingTime := Timeout;
AIdDNSResolver.QueryType := [qtPTR];
AIdDNSResolver.Host := DNSServer;
RetryCount := Retries;
repeat
try
dec(RetryCount);
AIdDNSResolver.Resolve(IPAddress);
Break;
except
on e: Exception do
begin
if RetryCount <= 0 then
begin
// if SameText(e.Message, RSCodeQueryName) then
// Result := FALSE
// else
raise Exception.Create(e.Message);
Break;
end;
end;
end;
until false;
if AIdDNSResolver.QueryResult.Count > 0 then
Result := AIdDNSResolver.QueryResult.DomainName;
finally
FreeAndNil(AIdDNSResolver);
end;
end;
Am I misunderstanding the purpose of QueryResult.DomainName? Shouldn't
that be the name of the domain that gets resolved? I'm seeing responses
like this for Google.com (using IP address 74.125.224.72):
'72.224.125.74.in-addr.arpa'
3. Comcast is my internet provider. Is something in my network setup
prohibiting me from successfully calling ReverseDNS?
Any pointers are greatly appreciated. I am afraid Delphi's reputation
is suffering already because of my ignorance in this area and I greatly
appreciate any pointers to good reference information!
--
John Kaster http://johnkaster.wordpress.com
http://transactis.com Seamless paper-free transactions
John wrote:
> 1. What is RSCodeQueryName supposed to be?
It is a resource string defined in the IdResourceStringsProtocols unit:
{code:delphi}
resourcestring
...
RSCodeQueryName = 'DNS Server Reports Query Name Error';
...
{code}
EIdDnsResolverError
> 2. What does ParseReverseDNSResponse do?
It is a user-defined function that "norgepaul" omitted from his SO question.
The TResultRecord.RData property contains the raw bytes of the a Resolve()
result. However, TIdDNSResolver already parses that data for you. For a
qtPTR request, the QueryResult will contain a TPTRRecord object (which is
derived from TResultRecord), which has a HostName parameter, eg:
{code:delphi}
HostName := (AIdDNSResolver.QueryResult[0] as TPTRRecord).HostName;
{code}
> 1. Is there a current Indy DNSResolver demo?
No.
> 2. What's wrong with this code, if anything? I changed the code
> referenced above in an attempt to simplify/update and work around the
> missing info above:
Try this:
function ReverseDNSLookup(IPAddress: String; DNSServer: String = SDefaultDNS;
Timeout: Integer = 30; Retries: Integer = 3) : string;
var
AIdDNSResolver: TIdDNSResolver;
begin
Result := '';
IPAddress := ReverseIP(IPAddress); // what is this doing?
AIdDNSResolver := TIdDNSResolver.Create(nil);
try
AIdDNSResolver.WaitingTime := Timeout;
AIdDNSResolver.QueryType := [qtPTR];
AIdDNSResolver.Host := DNSServer;
repeat
try
Dec(Retries);
AIdDNSResolver.Resolve(IPAddress);
Result := (AIdDNSResolver.QueryResult[0] as TPTRResult).HostName;
Exit;
except
on e: EIdDnsResolverError do
begin
if (Retries <= 0) and TextIsSame(e.Message, RSCodeQueryName) then
Exit;
raise;
end;
end;
until Retries <= 0;
finally
FreeAndNil(AIdDNSResolver);
end;
end;
{code}
> Am I misunderstanding the purpose of QueryResult.DomainName?
> Shouldn't that be the name of the domain that gets resolved?
> I'm seeing responses like this for Google.com (using IP address 74.125.224.72):
>
> '72.224.125.74.in-addr.arpa'
What you are missing is the fact that your DNS is returning an intermediate
gateway address that then needs to be resolved further. Have a look at RFC
1035 (http://tools.ietf.org/html/rfc1035) Section 3.5. It talks about the
'in-addr.arpa' domain and how it relates to PTR queries.
> 3. Comcast is my internet provider. Is something in my network setup
> prohibiting me from successfully calling ReverseDNS?
No.
--
Remy Lebeau (TeamB)
Remy Lebeau (TeamB) wrote:
> It is a resource string defined in the IdResourceStringsProtocols
> unit:
>
> {code:delphi}
> resourcestring
> ...
> RSCodeQueryName = 'DNS Server Reports Query Name Error';
> ...
> {code}
.... thanks ...
> {code:delphi}
> HostName := (AIdDNSResolver.QueryResult[0] as TPTRRecord).HostName;
> {code}
.... thanks ...
> > 1. Is there a current Indy DNSResolver demo?
>
> No.
Bummer :)
> > 2. What's wrong with this code, if anything? I changed the code
> > referenced above in an attempt to simplify/update and work around
> > the missing info above:
>
> Try this:
Thanks again. And ReverseIP() was basically doing the
'72.224.125.74.in-addr.arpa' code in a Delphi routine. Shouldn't have
been in the posted snippet, sorry for the distraction.
New version (changed TPTRResult in your code to TPTRRecord. I think
that was correct):
{code:delphi}
function ReverseDNSLookup(IPAddress: String; DNSServer: String =
SDefaultDNS;
Timeout: Integer = 30; Retries: Integer = 3) : string;
var
AIdDNSResolver: TIdDNSResolver;
begin
Result := '';
AIdDNSResolver := TIdDNSResolver.Create(nil);
try
AIdDNSResolver.WaitingTime := Timeout;
AIdDNSResolver.QueryType := [qtPTR];
AIdDNSResolver.Host := DNSServer;
repeat
try
Dec(Retries);
AIdDNSResolver.Resolve(IPAddress);
Result := (AIdDNSResolver.QueryResult[0] as
TPTRRecord).HostName;
Exit;
except
on e: EIdDnsResolverError do
begin
if (Retries <= 0) and SameText(e.Message, RSCodeQueryName)
then
Exit;
raise;
end;
end;
until Retries <= 0;
finally
FreeAndNil(AIdDNSResolver);
end;
end;
{code}
> What you are missing is the fact that your DNS is returning an
> intermediate gateway address that then needs to be resolved further.
> Have a look at RFC 1035 (http://tools.ietf.org/html/rfc1035) Section
> 3.5. It talks about the 'in-addr.arpa' domain and how it relates to
> PTR queries.
I was wondering if I was going to have to keep going down the chain.
Thanks for verifying. I'll read up on the RFC.
--
John Kaster http://johnkaster.wordpress.com
http://transactis.com Seamless paper-free transactions
Panagiotis Drivilas wrote:
> I don't know about that specific component but you can lookup IPv4 to
> hostname with the following:
Thanks very much for the sample code. I will preserve it in case I need
to take this route. I'm planning for something I can eventually build
for both Linux and Windows, so I'm hoping Indy usage will get me closer
to that goal ;)
--
John Kaster http://johnkaster.wordpress.com
http://transactis.com Seamless paper-free transactions
John Kaster wrote:
> I was wondering if I was going to have to keep going down the chain.
> Thanks for verifying. I'll read up on the RFC.
Thanks to both of you for this thread; I'm bookmarking it for when I
revisit DNS resolution.
--
Dave Nottage [TeamB]
Dave Nottage wrote:
> Thanks to both of you for this thread; I'm bookmarking it for when I
> revisit DNS resolution.
I may need to read section 3.5 multiple times ;)
--
John Kaster http://johnkaster.wordpress.com
http://transactis.com Seamless paper-free transactions
John wrote:
> Thanks very much for the sample code. I will preserve it in case I
> need to take this route. I'm planning for something I can eventually
> build for both Linux and Windows, so I'm hoping Indy usage will get me
> closer to that goal ;)
The Indy equivilent to Panagiotis's code would be the TIdStack.HostByAddress()
method, eg:
{code:delphi}
uses
..., IdGlobal, IdStack;
function ReverseDNSLookup(const IPAddress: string; const IPVersion: TIdIPVersion):
string;
begin
Result := '';
TIdStack.IncUsage;
try
Result := GStack.HostByAddress(Address, IPVersion);
finally
TIdStack.DecUsage;
end;
end;
{code}
{code}
Hostname := ReverseDNSLookup('74.125.224.72', Id_IPv4);
{code}
Or, you can utilize the TIdIPAddress class to hellp determine the IP version:
{code:delphi}
uses
..., IdIPAddress, IdStack;
function ReverseDNSLookup(const IPAddress: string): string;
var
Addr: TIdIPAddress;
begin
Result := '';
TIdStack.IncUsage;
try
Addr := TIdIPAddress.MakeAddressObject(IPAddress);
if Addr <> nil then
try
Result := GStack.HostByAddress(Address, Addr.AddrType);
finally
Addr.Free;
end;
finally
TIdStack.DecUsage;
end;
end;
{code}
{code}
Hostname := ReverseDNSLookup('74.125.224.72');
{code}
--
Remy Lebeau (TeamB)
Remy Lebeau (TeamB) wrote:
> Hostname := ReverseDNSLookup('74.125.224.72');
Thanks very much. That does indeed work. Is this more efficient than
the code I was using? Seems to be cleaner.
Perhaps I asked the wrong question or provided a detour with sample
code I was using in my original post. I want to get from 74.125.224.72
to "google.com". What's the best path to get there with Indy?
I tried using 'nuq04s07-in-f8.1e100.net' as the DNS server name for the
second DNS resolution request, but that just returns no result. So I
guess I'm still not understanding how to walk through the servers
listed in section 3.5. I've read it a few times and it's not clicking
yet, which is quite annoying. :)
--
John Kaster http://johnkaster.wordpress.com
http://transactis.com Seamless paper-free transactions
John wrote:
> Thanks very much. That does indeed work. Is this more efficient than
> the code I was using? Seems to be cleaner.
It relies on the OS performing the actual DNS query instead of Indy. So
on the one hand, it is simpler and cleaner, but on the other hand, you lose
control over the behavior of the query. You cannot implement timeouts, retries,
recursive results, etc. It is whatever the OS decides to use. For most
use cases, that is fine. Just be aware that it is a blocking operation,
like any other.
> I tried using 'nuq04s07-in-f8.1e100.net' as the DNS server name for
> the second DNS resolution request, but that just returns no result. So
> I guess I'm still not understanding how to walk through the servers
> listed in section 3.5.
You use the same DNS server every time, you simply Resolve() each returned
address until you reach the result you are looking for.
--
Remy Lebeau (TeamB)
Remy Lebeau (TeamB) wrote:
> You use the same DNS server every time, you simply Resolve() each
> returned address until you reach the result you are looking for.
Heh. Ok. seems simple enough. Seemed like the returned address was not
any different than the one I had before, but I'll try it. Thanks.
--
John Kaster http://johnkaster.wordpress.com
http://transactis.com Seamless paper-free transactions