NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

lib/41955: poor retry/next-ns logic in the DNS resolver hides certain error conditions



>Number:         41955
>Category:       lib
>Synopsis:       poor retry/next-ns logic in the DNS resolver hides certain 
>error conditions
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Aug 28 23:20:01 +0000 2009
>Originator:     Greg A. Woods
>Release:        all
>Organization:
Planix, Inc.; Toronto, Ontario; Canada
>Environment:
System: NetBSD
>Description:

        While trying to decipher some DNS responses I discovered that
        queries done via TCP returned the proper response code to the
        caller, while queries done via UDP hid this response code from
        the caller.

>How-To-Repeat:

        Make a query using res_send() to a nameserver using a question
        which will result in a "REFUSED" response and note the
        difference between using a UDP query and a TCP query.

        An easy way to do this is to request an A RR for a zone where
        the answer will contain a character which is not valid for
        hostnames and where the queried nameserver is BIND-8 (or
        equivalent) with the various "check-names" options enabled.  For
        example a "/" in a CIDR-style PTR delegation CNAME record.  Note
        that despite the fact the answer is a CNAME, if an A RR is
        requested then BIND-8 will assume the right-hand side points to
        an A RR and will apply the check-names logic to the result
        before sending it, and will end up refusing the query even when
        the result is a CNAME that points to a PTR.

        (note the version of "host" I'm using here has not yet been
        released -- the released version still contains yet another bug
        which exacerbates this problem, fixes are pending.  also note
        that libc has been compiled with -DRESOLVDEBUG, something that
        should normally be the default.)

            $ host -d -t a 10.161.29.204.in-addr.arpa 204.92.254.5   
            ;; res_nmkquery(QUERY, 10.161.29.204.in-addr.arpa, IN, A)
            ;; res_send()
            ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16691
            ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
            ;;      10.161.29.204.in-addr.arpa, type = A, class = IN
            ;; Querying server (# 1) address = 204.92.254.5
            ;; new DG socket
            server rejected query:
            ;; ns_initparse: Message too long
            ;; Querying server (# 1) address = 204.92.254.5
            ;; new DG socket
            server rejected query:
            ;; ns_initparse: Message too long
            ;; res_send failed
             !!! Nameserver ns.weird.com not responding
             !!! 10.161.29.204.in-addr.arpa A record not found at ns.weird.com, 
try again

        notice that the ultimate result is "not responding", but the
        response came back nearly immediately.

        try again, but this time force TCP:

            $ host -u -d -t a 10.161.29.204.in-addr.arpa 204.92.254.5
            ;; res_nmkquery(QUERY, 10.161.29.204.in-addr.arpa, IN, A)
            ;; res_send()
            ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49
            ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
            ;;      10.161.29.204.in-addr.arpa, type = A, class = IN
            ;; Querying server (# 1) address = 204.92.254.5
            ;; got answer:
            ;; ns_initparse: Message too long
            ;; Query for A records failed, 1 answer, authoritative, status: 
query refused
            get_info(10.161.29.204.in-addr.arpa): qdcount = 1, ancount = 1, 
nscount = 0, arcount = 0
            ;; res_send returned with REFUSED
             !!! The server at ns.weird.com does not allow recursion.
             !!! 10.161.29.204.in-addr.arpa A record query refused by 
ns.weird.com

        With the fix below the UDP result now matches the TCP result:

            $ ./host -d -t a 10.161.29.204.in-addr.arpa 204.92.254.5    
            ;; res_nmkquery(QUERY, 10.161.29.204.in-addr.arpa, IN, A)
            ;; res_send()
            ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14698
            ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
            ;;      10.161.29.204.in-addr.arpa, type = A, class = IN
            ;; Querying server (# 1) address = 204.92.254.5
            ;; new DG socket
            server rejected query returning REFUSED:
            ;; ns_initparse: Message too long
            ;; got answer:
            ;; ns_initparse: Message too long
            ;; Query for A records failed, 1 answer, authoritative, status: 
query refused
            get_info(10.161.29.204.in-addr.arpa): qdcount = 1, ancount = 1, 
nscount = 0, arcount = 0
            ;; res_send returned with REFUSED
             !!! The server at ns.weird.com does not allow recursion.
             !!! 10.161.29.204.in-addr.arpa A record query refused by 
ns.weird.com


        (the curious are invited to try the proper "PTR" query to see
        the expected result)

        (I was also going to try to fix the bogus debug claim from
        ns_initparse() about "Message too long", but that's probably not
        worthwhile doing.)

>Fix:

        I think the following fix, shown for both -current and netbsd-4,
        seems to be sufficient -- i.e. it works for my test case.

        I'm not comfortable with the "ns+1 <" expression, but that's
        what worked and so I didn't try to explore any other options.

        I will probably try poking this fix into the BIND libresolv
        sources too, but I'm not sure they will bother updating this old
        resolver code.

Index: lib/libc/resolv/res_send.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/lib/libc/resolv/res_send.c,v
retrieving revision 1.18
diff -u -r1.18 res_send.c
--- lib/libc/resolv/res_send.c  12 Apr 2009 17:07:17 -0000      1.18
+++ lib/libc/resolv/res_send.c  28 Aug 2009 23:05:56 -0000
@@ -1033,12 +1033,12 @@
            anhp->rcode == NOTIMP ||
            anhp->rcode == REFUSED) {
                DprintQ(statp->options & RES_DEBUG,
-                       (stdout, "server rejected query:\n"),
+                       (stdout, "server rejected query returning %s:\n", 
p_rcode(anhp->rcode)),
                        ans, (resplen > anssiz) ? anssiz : resplen);
                res_nclose(statp);
-               /* don't retry if called from dig */
-               if (!statp->pfcode)
-                       return (0);
+               /* don't retry if called from dig, or no more NS's to try */
+               if (!statp->pfcode && ns+1 < statp->nscount)
+                       return (0);     /* otherwise try next NS */
        }
        if (!(statp->options & RES_IGNTC) && anhp->tc) {
                /*

Index: lib/libc/resolv/res_send.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/lib/libc/resolv/res_send.c,v
retrieving revision 1.9.4.2
diff -u -r1.9.4.2 res_send.c
--- lib/libc/resolv/res_send.c  17 May 2007 21:25:19 -0000      1.9.4.2
+++ lib/libc/resolv/res_send.c  25 Aug 2009 22:59:13 -0000
@@ -1000,12 +1000,12 @@
            anhp->rcode == NOTIMP ||
            anhp->rcode == REFUSED) {
                DprintQ(statp->options & RES_DEBUG,
-                       (stdout, "server rejected query:\n"),
+                       (stdout, "server rejected query returning %s:\n", 
p_rcode(anhp->rcode)),
                        ans, (resplen > anssiz) ? anssiz : resplen);
                res_nclose(statp);
-               /* don't retry if called from dig */
-               if (!statp->pfcode)
-                       return (0);
+               /* don't retry if called from dig, or no more NS's to try */
+               if (!statp->pfcode && ns+1 < statp->nscount)
+                       return (0);     /* otherwise try next NS */
        }
        if (!(statp->options & RES_IGNTC) && anhp->tc) {
                /*



Home | Main Index | Thread Index | Old Index