Bug 162625

Summary: RFE: DNS resolver should treat empty answer referral as SERVFAIL
Product: [Fedora] Fedora Reporter: Jason Vas Dias <jvdias>
Component: glibcAssignee: Jakub Jelinek <jakub>
Status: CLOSED RAWHIDE QA Contact: Brian Brock <bbrock>
Severity: medium Docs Contact:
Priority: medium    
Version: rawhideCC: alford, drepper, fweimer
Target Milestone: ---Keywords: FutureFeature
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: 2.3.90-2 Doc Type: Enhancement
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2005-07-10 20:24:00 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 160914, 756458    
Attachments:
Description Flags
Patch to res_send.c causing resolver to try next server on "recursion denied" referrals none

Description Jason Vas Dias 2005-07-06 22:04:53 UTC
Description of problem:

When there is more than one nameserver in resolv.conf, and
a nameserver responds with an empty answer section (a "referral"
indicating that recursion is denied), then the resolver should
try the next nameserver in the list, as it would do for a SERVFAIL
response, instead of aborting the query with HOST_NOT_FOUND as it
does currently.

This issue was found to be the root cause of BIND bug 160914, where
the first nameserver returned a referral for a name, while the second
nameserver had an A record for the name - the BIND utilities and the 
glibc resolver did not try querying the second nameserver, and the 
user was unable to get a network connection. The user states:
"  The problem arose for me when I was visiting an institution abroad,
   where they set us up with a local wireless network connection.
   Everyone with Windows could connect to their home institutions, but   
   those with Linux could not. I finally traced the problem to this
   misconfiguration of the name servers. But the fact that Windows is
   robust against it and Linux isn't indicates that this is an area
   where Linux could be improved.
"
I tend to agree.

The BIND named nameserver, when operating as a resolver in forwarding
mode, will respond to an NXDOMAIN, SERVFAIL or a referral response
by trying the next nameserver in the forwarders list, as evidently
the Windows resolver does.

An argument could be made that the glibc resolver and BIND utilities
should behave in the same way as named in resolver mode; they should
at least treat a referral in the same way as a SERVFAIL, by trying the
next nameserver.

The BIND resolver utilities should not behave differently to the glibc
resolver - they should both implement an agreed behavior, which will
hopefully be improved in respect to this problem.

Version-Release number of selected component (if applicable):

ALL

How reproducible:

100%

Steps to Reproduce:
1. Set up 2 nameservers "a" and "b", "a" with 
   "options {...recursion no;...}",
   and "b" with a 'zone "name." IN {...};' definition, whose 
   zone file contains an A record for "a.name." .
 
2. Setup a client host, "c", with resolv.conf containing 
   "nameserver a
    nameserver b
   "

3. Lookup "a.name." on host c, using either gethostbyname() ,
   "nslookup", "dig", or "host".
  
Actual results:

"c" queries "a", which sends a referral - a dns response packet
with an empty answer section ; "c"s resolver then aborts the query
and does not query "b" for its existing "a.name." A record.

Expected results:

"c" should go on to query "b" when it receives "a"'s referral.

Additional info:

I am developing a patch to fix glibc resolver to treat an empty
answer as a SERVFAIL and will attach it to this bug when done.

Comment 1 Jason Vas Dias 2005-07-07 16:29:29 UTC
Created attachment 116480 [details]
Patch to res_send.c causing resolver to try next server on "recursion denied" referrals 

Here is a patch to glibc's resolv/res_send.c that fixes the problem,
in send_dg(...), where it was testing for a SERVFAIL and returning 0
to cause the next server to be tried, it now also tests for the condition :
 ((anhp->rcode == NOERROR) && (anhp->ancount == 0) && (anhp->aa == 0) &&
(anhp->ra == 0))) :  empty answer section, not authoritative, recursion not
allowed.
Note: the send_vc(...) function does not seem to be testing for ANY server
error conditions that should cause the next server to be tried except connect()

errors - it should also be modified to test for SERVFAIL, NOTIMP, REFUSED, or
recursion denied referrals, but this is beyond the scope of this bug report.

In general, the glibc DNS resolver needs upgrading from its BIND 8.2.3 baseline

to the latest BIND 8.4.6 resolver, and needs support for DNSSEC added  -  I'll
volunteer to undertake this job if desired.

Comment 2 Ulrich Drepper 2005-07-07 21:00:08 UTC
Show me the tcpdump output of the DNS reply which you try to recognize.  Just
having ancount==0 doesn't indicate a referral.

Comment 3 Jason Vas Dias 2005-07-07 21:45:30 UTC
he patch also checks that the AA (authoritative answer) and RA 
(recursion allowed) bits in the response header are 0, as well as
ancount==0 .

Here's the output of 
 # tcpdump -vvv -nl -s 2048 port domain
from bug 160914:

17:56:22.716456 IP (tos 0x0, ttl  64, id 0, offset 0, flags [DF], proto 17,
length: 62) 128.252.125.81.34038 > 192.135.10.4.domain: [udp sum ok]  11009+ A?
wuphys.wustl.edu. (34)
17:56:22.867290 IP (tos 0x0, ttl  47, id 0, offset 0, flags [DF], proto 17,
length: 207) 192.135.10.4.domain > 128.252.125.81.34038: [udp sum ok]  11009- q:
A? wuphys.wustl.edu. 0/8/0 ns: edu. NS L3.NSTLD.COM., edu. NS M3.NSTLD.COM.,
edu. NS A3.NSTLD.COM., edu. NS C3.NSTLD.COM., edu. NS D3.NSTLD.COM., edu. NS
E3.NSTLD.COM., edu. NS G3.NSTLD.COM., edu. NS H3.NSTLD.COM. (179)

And here's the full packet output from a duplication of the problem
on my name servers:
I set up 2 servers in resolv.conf:
nameserver 172.16.80.40
nameserver 172.16.80.118

172.16.80.40 has no "jvds.net." zone and has "options {... recursion no; ...};" .
172.16.80.118 has a "jvds.net." zone with an A record for "jvdias.jvds.net".

I use a C program that does a ' gethostbyname( "jvdias.jvds.net." ) ', while
tcpdump listens for domain packets.

Here's the tcpdump output without the res_send.c patch:

$ tcpdump -nl -i eth0 -vvv -s 4096 -X port domain
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 4096 bytes
17:31:00.704331 IP (tos 0x0, ttl  64, id 55784, offset 0, flags [DF], proto 17,
length: 61) 172.16.80.119.32805 > 172.16.80.40.domain: [bad udp cksum 73ae!] 
19217+ A? jvdias.jvds.net. (33)
        0x0000:  4500 003d d9e8 4000 4011 6807 ac10 5077  E..=..@.@.h...Pw
        0x0010:  ac10 5028 8025 0035 0029 f8fa 4b11 0100  ..P(.%.5.)..K...
        0x0020:  0001 0000 0000 0000 066a 7664 6961 7304  .........jvdias.
        0x0030:  6a76 6473 036e 6574 0000 0100 01         jvds.net.....
17:31:00.705064 IP (tos 0x0, ttl  64, id 41, offset 0, flags [DF], proto 17,
length: 269) 172.16.80.40.domain > 172.16.80.119.32805: [udp sum ok]  19217- q:
A? jvdias.jvds.net. 0/13/0 ns: . NS J.ROOT-SERVERS.net., . NS
K.ROOT-SERVERS.net., . NS L.ROOT-SERVERS.net., . NS M.ROOT-SERVERS.net., . NS
A.ROOT-SERVERS.net., . NS B.ROOT-SERVERS.net., . NS C.ROOT-SERVERS.net., . NS
D.ROOT-SERVERS.net., . NS E.ROOT-SERVERS.net., . NS F.ROOT-SERVERS.net., . NS
G.ROOT-SERVERS.net., . NS H.ROOT-SERVERS.net., . NS I.ROOT-SERVERS.net. (241)
        0x0000:  4500 010d 0029 4000 4011 40f7 ac10 5028  E....)@.@.@...P(
        0x0010:  ac10 5077 0035 8025 00f9 1ec1 4b11 8100  ..Pw.5.%....K...
        0x0020:  0001 0000 000d 0000 066a 7664 6961 7304  .........jvdias.
        0x0030:  6a76 6473 036e 6574 0000 0100 0100 0002  jvds.net........
        0x0040:  0001 0007 e900 0011 014a 0c52 4f4f 542d  .........J.ROOT-
        0x0050:  5345 5256 4552 53c0 1800 0002 0001 0007  SERVERS.........
        0x0060:  e900 0004 014b c02e 0000 0200 0100 07e9  .....K..........
        0x0070:  0000 0401 4cc0 2e00 0002 0001 0007 e900  ....L...........
        0x0080:  0004 014d c02e 0000 0200 0100 07e9 0000  ...M............
        0x0090:  0401 41c0 2e00 0002 0001 0007 e900 0004  ..A.............
        0x00a0:  0142 c02e 0000 0200 0100 07e9 0000 0401  .B..............
        0x00b0:  43c0 2e00 0002 0001 0007 e900 0004 0144  C..............D
        0x00c0:  c02e 0000 0200 0100 07e9 0000 0401 45c0  ..............E.
        0x00d0:  2e00 0002 0001 0007 e900 0004 0146 c02e  .............F..
        0x00e0:  0000 0200 0100 07e9 0000 0401 47c0 2e00  ............G...
        0x00f0:  0002 0001 0007 e900 0004 0148 c02e 0000  ...........H....
        0x0100:  0200 0100 07e9 0000 0401 49c0 2e         ..........I..

gethostbyname returns a HOST_NOT_FOUND error.

And then with the patched res_send.c in a rebuilt libresolv / libnss_dns :

$ tcpdump -nl -i eth0 -vvv -s 4096 -X port domain
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 4096 bytes
17:39:14.831410 IP (tos 0x0, ttl  64, id 25680, offset 0, flags [DF], proto 17,
length: 61) 172.16.80.119.32805 > 172.16.80.40.domain: [bad udp cksum 2597!] 
25183+ A? jvdias.jvds.net. (33)
        0x0000:  4500 003d 6450 4000 4011 dd9f ac10 5077  E..=dP@.@.....Pw
        0x0010:  ac10 5028 8025 0035 0029 f8fa 625f 0100  ..P(.%.5.)..b_..
        0x0020:  0001 0000 0000 0000 066a 7664 6961 7304  .........jvdias.
        0x0030:  6a76 6473 036e 6574 0000 0100 01         jvds.net.....
17:39:14.831907 IP (tos 0x0, ttl  64, id 42, offset 0, flags [DF], proto 17,
length: 269) 172.16.80.40.domain > 172.16.80.119.32805: [udp sum ok]  25183- q:
A? jvdias.jvds.net. 0/13/0 ns: . NS I.ROOT-SERVERS.net., . NS
J.ROOT-SERVERS.net., . NS K.ROOT-SERVERS.net., . NS L.ROOT-SERVERS.net., . NS
M.ROOT-SERVERS.net., . NS A.ROOT-SERVERS.net., . NS B.ROOT-SERVERS.net., . NS
C.ROOT-SERVERS.net., . NS D.ROOT-SERVERS.net., . NS E.ROOT-SERVERS.net., . NS
F.ROOT-SERVERS.net., . NS G.ROOT-SERVERS.net., . NS H.ROOT-SERVERS.net. (241)
        0x0000:  4500 010d 002a 4000 4011 40f6 ac10 5028  E....*@.@.@...P(
        0x0010:  ac10 5077 0035 8025 00f9 007a 625f 8100  ..Pw.5.%...zb_..
        0x0020:  0001 0000 000d 0000 066a 7664 6961 7304  .........jvdias.
        0x0030:  6a76 6473 036e 6574 0000 0100 0100 0002  jvds.net........
        0x0040:  0001 0007 e900 0011 0149 0c52 4f4f 542d  .........I.ROOT-
        0x0050:  5345 5256 4552 53c0 1800 0002 0001 0007  SERVERS.........
        0x0060:  e900 0004 014a c02e 0000 0200 0100 07e9  .....J..........
        0x0070:  0000 0401 4bc0 2e00 0002 0001 0007 e900  ....K...........
        0x0080:  0004 014c c02e 0000 0200 0100 07e9 0000  ...L............
        0x0090:  0401 4dc0 2e00 0002 0001 0007 e900 0004  ..M.............
        0x00a0:  0141 c02e 0000 0200 0100 07e9 0000 0401  .A..............
        0x00b0:  42c0 2e00 0002 0001 0007 e900 0004 0143  B..............C
        0x00c0:  c02e 0000 0200 0100 07e9 0000 0401 44c0  ..............D.
        0x00d0:  2e00 0002 0001 0007 e900 0004 0145 c02e  .............E..
        0x00e0:  0000 0200 0100 07e9 0000 0401 46c0 2e00  ............F...
        0x00f0:  0002 0001 0007 e900 0004 0147 c02e 0000  ...........G....
        0x0100:  0200 0100 07e9 0000 0401 48c0 2e         ..........H..
17:39:14.875649 IP (tos 0x0, ttl  64, id 25725, offset 0, flags [DF], proto 17,
length: 61) 172.16.80.119.32805 > 172.16.80.118.domain: [bad udp cksum 8996!] 
25183+ A? jvdias.jvds.net. (33)
        0x0000:  4500 003d 647d 4000 4011 dd24 ac10 5077  E..=d}@.@..$..Pw
        0x0010:  ac10 5076 8025 0035 0029 f948 625f 0100  ..Pv.%.5.).Hb_..
        0x0020:  0001 0000 0000 0000 066a 7664 6961 7304  .........jvdias.
        0x0030:  6a76 6473 036e 6574 0000 0100 01         jvds.net.....
17:39:14.876630 IP (tos 0x0, ttl  64, id 28, offset 0, flags [DF], proto 17,
length: 115) 172.16.80.118.domain > 172.16.80.119.32805: [udp sum ok]  25183* q:
A? jvdias.jvds.net. 1/1/0 jvdias.jvds.net. A 64.32.16.14 ns: jvds.net. NS
jvdspc.boston.redhat.com. (87)
        0x0000:  4500 0073 001c 4000 4011 4150 ac10 5076  E..s..@.@.AP..Pv
        0x0010:  ac10 5077 0035 8025 005f 53bf 625f 8580  ..Pw.5.%._S.b_..
        0x0020:  0001 0001 0001 0000 066a 7664 6961 7304  .........jvdias.
        0x0030:  6a76 6473 036e 6574 0000 0100 01c0 0c00  jvds.net........
        0x0040:  0100 0100 0151 8000 0440 2010 0ec0 1300  .....Q...@......
        0x0050:  0200 0100 0151 8000 1a06 6a76 6473 7063  .....Q....jvdspc
        0x0060:  0662 6f73 746f 6e06 7265 6468 6174 0363  .boston.redhat.c
        0x0070:  6f6d 00                                  om.




Comment 4 Ulrich Drepper 2005-07-08 06:45:45 UTC
I think I would feel better if we also add

&& anhp->arcount == 0

This works in our example and should further limit wrong triggering of the code.
 There never should be an additional RR added if the server cannot provide any
information.  I'll apply this modified change upstream and we'll see how it works.

Comment 5 Jakub Jelinek 2005-07-10 20:24:00 UTC
Patch is in glibc-2.3.90-2.