Subject: | background TCP query logic expects to read entire response at once |
Date: | Wed, 20 Mar 2019 13:06:22 -0400 |
To: | bug-Net-DNS [...] rt.cpan.org |
From: | Felipe Gasper <felipe [...] felipegasper.com> |
The DNS server at 95.110.220.5 appears to send the initial 2 bytes (i.e., the length prefix) of a TCP DNS response separately from the rest of the response.
You can see this behavior in action here:
-----
perl -e'print "\0(\346\254\0\0\0\1\0\0\0\0\0\0\22campingmalibubeach\3com\0\0\1\0\1"' | strace -yy -s512 nc 95.110.220.5 53
-----
Relevant strace excerpt:
-----
recvfrom(3<TCP:[10.215.218.116:44780->95.110.220.5:53]>, "\0\210", 8192, 0, 0x7fffcd9cd260, [0]) = 2
write(1</dev/pts/0>, "\0\210", 2�) = 2
select(4, [3<TCP:[10.215.218.116:44780->95.110.220.5:53]>], [], [], NULL) = 1 (in [3])
recvfrom(3<TCP:[10.215.218.116:44780->95.110.220.5:53]>, "\346\254\204\0\0\1\0\6\0\0\0\0\22campingmalibubeach\3com\0\0\1\0\1\300\f\0\1\0\1\0\1Q\200\0\4>\225\200\227\300\f\0\1\0\1\0\1Q\200\0\4>\225\200\232\300\f\0\1\0\1\0\1Q\200\0\4>\225\200\235\300\f\0\1\0\1\0\1Q\200\0\4>\225\200\240\300\f\0\1\0\1\0\1Q\200\0\4>\225\200\243\300\f\0\1\0\1\0\1Q\200\0\4>\225\200\246", 8192, 0, 0x7fffcd9cd260, [0]) = 136
-----
This behavior, however, makes the following Net::DNS::Resolver query fail because it’s expecting the full DNS packet to be ready along with the 2-byte length header:
-----
strace -s1024 -yy perl -MIO::Select -MNet::DNS::Resolver -e'my $r = Net::DNS::Resolver->new(); $r->nameservers("95.110.220.5"); $r->recurse(0); $r->usevc(1); my $req = $r->bgsend("campingmalibubeach.com", "A"); 1 while !$r->bgisready($req); my $pkt = $r->bgread($req)'
-----
Relevant excerpt of the strace:
-----
recvfrom(3<TCP:[10.215.218.116:36500->95.110.220.5:53]>, "\0\210", 2, 0, 0x7ffc92358888, [0]) = 2
recvfrom(3<TCP:[10.215.218.116:36500->95.110.220.5:53]>, 0x215e690, 0, 0, 0x7ffc92358888, 0x7ffc9235887c) = -1 EAGAIN (Resource temporarily unavailable)
recvfrom(3<TCP:[10.215.218.116:36500->95.110.220.5:53]>, 0x215e6d0, 136, 0, 0x7ffc92358888, 0x7ffc9235887c) = -1 EAGAIN (Resource temporarily unavailable)
-----
I thought maybe if I redo the loop that would fix the issue, but it doesn’t appear to; each time through the loop there’s a recvfrom() for 2 bytes, then maybe it receives the rest of the payload, but of course at that point it’s not a valid DNS packet, and the process just tight-loops indefinitely:
-----
strace -s1024 -yy perl -MIO::Select -MNet::DNS::Resolver -e'my $r = Net::DNS::Resolver->new(); $r->nameservers("95.110.220.5"); $r->recurse(0); $r->usevc(1); my $req = $r->bgsend("campingmalibubeach.com", "A"); my $pkt; while (!$pkt) { 1 while !$r->bgisready($req); $pkt = $r->bgread($req) }'
-----
Going by the POD for Net::DNS::Resolver, it looks like bgread() should block until the entire response is received?
Thank you!
-Felipe Gasper
Mississauga, Ontario