Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Bug - IdDNSResolver TXT query causes infinite loop



Permlink Replies: 4 - Last Post: Mar 13, 2017 12:46 PM Last Post By: Roberto Frances...
Roberto Frances...

Posts: 6
Registered: 5/14/02
Bug - IdDNSResolver TXT query causes infinite loop
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 9, 2017 6:46 PM
I have verified this in 10.6.2.5263, but suspect the issue persists in the latest nightly snapshot as the code for IdDNSResolver.pas and IdDNSCommon.pas is very similar.

The issue occurs when a buggy DNS server provides malformed results for a TXT query that contains more that 255 characters. I have replicated it when querying these two DNS servers for the TXT record of ups.com:
67.69.184.199
67.69.184.7

A wireshark capture of the DNS query made from terminal with dig/nslookup will show a malformed packet in the UDP response. This causes this code to freeze and end up in an infinite loop:

procedure TForm1.Button1Click(Sender: TObject);
var
IdDNSResolver1:TIdDNSResolver;
begin
IdDNSResolver1:=TIdDNSResolver.Create(nil);
IdDNSResolver1.Host:='67.69.184.199';
IdDNSResolver1.WaitingTime:=5000;
IdDNSResolver1.QueryType:= [qtTXT];
IdDNSResolver1.Resolve('ups.com');
IdDNSResolver1.Free;
end;

I am not competent enough with the Indy code to figure out exactly where in the IdDNSResolver code the fix should be made. What worked in my case was to cheat and simply add a loop counter to exit the function that loops as follows:

procedure TTextRecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
var
LStart: Integer;
Buffer: string;
LoopCounter:Integer;
begin
FText.Clear;
LoopCounter:=1;

LStart := APos;
while APos < (LStart+RDataLength) do
begin
Buffer := NextDNSLabel(CompleteMessage, APos);
if Buffer <> '' then begin {Do not Localize}
FText.Add(Buffer);
end;
Inc(LoopCounter);
if LoopCounter > 50 then
raise EIdDnsResolverError.Create(GetErrorStr(2, 3));
end;

inherited Parse(CompleteMessage, APos);
end;

Everyone should be able to quickly see where the loop occurs as long as bell.ca does not fix their DNS servers. Basically this condition:

while APos < (LStart+RDataLength) do

is always true, because the NextDNSLabel(CompleteMessage, APos) function that is called never increments the APos variable, because in turn in:

function NextDNSLabel(const DNSStr: TIdBytes; var VPos: Integer): string;
var
LabelLength: Byte;
begin
if Length(DNSStr) > VPos then begin
LabelLength := DNSStr[VPos];
Inc(VPos);
//VV Shouldn't be pointers in Text messages
if LabelLength > 0 then begin
Result := BytesToString(DNSStr, VPos, LabelLength);
Inc(VPos, LabelLength);
Exit;
end;
end;
Result := ''; {Do not Localize}
end;

in the test
if Length(DNSStr) > VPos then begin

VPos is bigger than Length(DNSStr) , so the function returns a '' and without incrementing VPos.

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Bug - IdDNSResolver TXT query causes infinite loop
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 11, 2017 11:58 PM   in response to: Roberto Frances... in response to: Roberto Frances...
Roberto wrote:

The issue occurs when a buggy DNS server provides malformed
results for a TXT query that contains more that 255 characters. I
have replicated it when querying these two DNS servers for the
TXT record of ups.com:

67.69.184.199

67.69.184.7

A wireshark capture of the DNS query made from terminal with
dig/nslookup will show a malformed packet in the UDP response.

That is actually not a bug at all. The DNS response is simply being truncated
to 512 bytes, which is DNS's default max message size in UDP (per RFC 1035).
If you look closer at the response data, you will see the TC (truncation)
flag is set in the response's header. Most DNS resolvers would detect that
flag and retry the request over TCP (DNS has a max message size of 65535
bytes over TCP). However, TIdDNSResolver does not currently implement that
retry. The only time TIdDNSResolver performs a TCP-based request is when
querying either AXFR or IXFR records (I did verify that a TXT request over
TCP on the two DNS servers above does work, though).

On the other hand, EDNS (RFC 6891) defines an extension mechanism for DNS,
and one of the available extensions allows for a larger UDP message size.
I updated TIdDNSResolver to report a UDP receive size of 1280 bytes, more
than twice the default, and was able to receive a non-truncated response
from both DNS servers above without resorting to TCP.

This causes this code to freeze and end up in an infinite loop:

Yes, and that is a little tricky to address without changing the behavior
of TIdDNSResolver. I've updated TIdDNSResolver to handle the case where
it tries to read data beyond the end of the message, and if the response's
TC flag is set than just ignore the error so it can parse as much data as
it can. The caller can then decide what to do with truncated data.

--
Remy Lebeau (TeamB)
Roberto Frances...

Posts: 6
Registered: 5/14/02
Re: Bug - IdDNSResolver TXT query causes infinite loop
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 12, 2017 6:39 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks for the explanation on the truncation flag Remy, I didn't know how that worked.

FYI there's probably still issues when dealing with whatever software is running on that DNS server, but at least these do not crash/freeze Indy.

For example, issue this against a "good" DNS server, like 208.67.222.222

dig txt salesforce.com @208.67.222.222

you'll get the reply in a single UDP packet back from the server. The TXT records are larger than 255 characters, but less than 512. Indy handles this just fine.

Now go back to what I call the "bad" server, and issue:
dig txt salesforce.com @67.69.184.199

Now that I know what that is and what to look for thanks to you... For some unknown reason to me the server replies with a truncated flag in the UDP packet even though the reply does not exceed the 512 size, and this causes the dig client as you said to proceed to make a TCP request for the query, which is successful.

However Indy fails to handle that TXT query - once it sees that truncated packet, which has a size of < 512, it throws an exception for "Invalid packet size 3"

In my application I don't care :-) as long as I don't see an infinite loop freeze, but again, as I'm not very versed in the TCP/DNS calls, I don't know what the correct way to handle these responses from these DNS servers are. I just wanted it to mention it here for you to look at if you deem it necessary.

Remy Lebeau (TeamB) wrote:
Roberto wrote:

The issue occurs when a buggy DNS server provides malformed
results for a TXT query that contains more that 255 characters. I
have replicated it when querying these two DNS servers for the
TXT record of ups.com:

67.69.184.199

67.69.184.7

A wireshark capture of the DNS query made from terminal with
dig/nslookup will show a malformed packet in the UDP response.

That is actually not a bug at all. The DNS response is simply being truncated
to 512 bytes, which is DNS's default max message size in UDP (per RFC 1035).
If you look closer at the response data, you will see the TC (truncation)
flag is set in the response's header. Most DNS resolvers would detect that
flag and retry the request over TCP (DNS has a max message size of 65535
bytes over TCP). However, TIdDNSResolver does not currently implement that
retry. The only time TIdDNSResolver performs a TCP-based request is when
querying either AXFR or IXFR records (I did verify that a TXT request over
TCP on the two DNS servers above does work, though).

On the other hand, EDNS (RFC 6891) defines an extension mechanism for DNS,
and one of the available extensions allows for a larger UDP message size.
I updated TIdDNSResolver to report a UDP receive size of 1280 bytes, more
than twice the default, and was able to receive a non-truncated response
from both DNS servers above without resorting to TCP.

This causes this code to freeze and end up in an infinite loop:

Yes, and that is a little tricky to address without changing the behavior
of TIdDNSResolver. I've updated TIdDNSResolver to handle the case where
it tries to read data beyond the end of the message, and if the response's
TC flag is set than just ignore the error so it can parse as much data as
it can. The caller can then decide what to do with truncated data.

--
Remy Lebeau (TeamB)

Edited by: Roberto Franceschetti on Mar 12, 2017 6:39 AM
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Bug - IdDNSResolver TXT query causes infinite loop [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 13, 2017 12:18 PM   in response to: Roberto Frances... in response to: Roberto Frances...
Roberto wrote:

dig txt salesforce.com @208.67.222.222

you'll get the reply in a single UDP packet back from the server. The
TXT records are larger than 255 characters, but less than 512. Indy
handles this just fine.

Now go back to what I call the "bad" server, and issue: dig txt
salesforce.com @67.69.184.199

Now that I know what that is and what to look for thanks to you...
For some unknown reason to me the server replies with a truncated
flag in the UDP packet even though the reply does not exceed the
512 size, and this causes the dig client as you said to proceed to
make a TCP request for the query, which is successful.

67.69.184.7 and 67.69.184.199 both report 2 answers, and both contain 2 (non-truncated)
TXT records, a SPF record and a globalsign verification. However, the TC
flag is set in the response header because the response also contains a list
of (10) authoritive nameservers, and that list causes the response to exceed
512 bytes.

208.67.222.222 reports the same 2 answers, and contains the same 2 (non-truncated)
TXT records, but does not report any authoritive nameservers. So the overall
message does not exceed 512 bytes and thus is not truncated (the TC flag
is not set).

Now, go back and re-test ups.com again, and you get some really varied results:

67.69.184.7 reports 3 answers, but only contains 1 TXT record, the (truncated)
SPF record. The other two TXT records are completely missing.

67.69.184.199 reports 3 answers, and contains 3 TXT records, where the first
2 TXT records are docusign guids, and the 3rd TXT record is the (truncated)
SPF record.

208.67.222.222 reports 2 answers, and contains 2 TXT records, both are the
docusign guids. The SPF record is completely missing.

In all three cases, the TC flag is set in the response header.

However Indy fails to handle that TXT query - once it sees that
truncated packet, which has a size of < 512

Look again. When truncation occurs, the packet size is 512 bytes.

it throws an exception for "Invalid packet size 3"

TIdDNSResolver throws the assertion while trying to parse the (truncated)
list of nameservers. I have fixed that now.

With the EDNS extension added to increase the UDP response message size,
all is right with the world on all 3 servers.

--
Remy Lebeau (TeamB)
Roberto Frances...

Posts: 6
Registered: 5/14/02
Re: Bug - IdDNSResolver TXT query causes infinite loop [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 13, 2017 12:46 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Perfect, and thanks for the detailed explanation of what I didn't understand and the DNS lesson - greatly appreciated!

Remy Lebeau (TeamB) wrote:
Roberto wrote:

dig txt salesforce.com @208.67.222.222

you'll get the reply in a single UDP packet back from the server. The
TXT records are larger than 255 characters, but less than 512. Indy
handles this just fine.

Now go back to what I call the "bad" server, and issue: dig txt
salesforce.com @67.69.184.199

Now that I know what that is and what to look for thanks to you...
For some unknown reason to me the server replies with a truncated
flag in the UDP packet even though the reply does not exceed the
512 size, and this causes the dig client as you said to proceed to
make a TCP request for the query, which is successful.

67.69.184.7 and 67.69.184.199 both report 2 answers, and both contain 2 (non-truncated)
TXT records, a SPF record and a globalsign verification. However, the TC
flag is set in the response header because the response also contains a list
of (10) authoritive nameservers, and that list causes the response to exceed
512 bytes.

208.67.222.222 reports the same 2 answers, and contains the same 2 (non-truncated)
TXT records, but does not report any authoritive nameservers. So the overall
message does not exceed 512 bytes and thus is not truncated (the TC flag
is not set).

Now, go back and re-test ups.com again, and you get some really varied results:

67.69.184.7 reports 3 answers, but only contains 1 TXT record, the (truncated)
SPF record. The other two TXT records are completely missing.

67.69.184.199 reports 3 answers, and contains 3 TXT records, where the first
2 TXT records are docusign guids, and the 3rd TXT record is the (truncated)
SPF record.

208.67.222.222 reports 2 answers, and contains 2 TXT records, both are the
docusign guids. The SPF record is completely missing.

In all three cases, the TC flag is set in the response header.

However Indy fails to handle that TXT query - once it sees that
truncated packet, which has a size of < 512

Look again. When truncation occurs, the packet size is 512 bytes.

it throws an exception for "Invalid packet size 3"

TIdDNSResolver throws the assertion while trying to parse the (truncated)
list of nameservers. I have fixed that now.

With the EDNS extension added to increase the UDP response message size,
all is right with the world on all 3 servers.

--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02