Hide Forgot
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.
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.
Show me the tcpdump output of the DNS reply which you try to recognize. Just having ancount==0 doesn't indicate a referral.
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.
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.
Patch is in glibc-2.3.90-2.