ce426f
commit 16b293a7a6f65d8ff348a603d19e8fd4372fa3a9
ce426f
Author: Siddhesh Poyarekar <siddhesh@redhat.com>
ce426f
Date:   Wed Apr 30 11:48:43 2014 +0530
ce426f
ce426f
    Do not fail if one of the two responses to AF_UNSPEC fails (BZ #14308)
ce426f
    
ce426f
    [Fixes BZ #14308, #12994, #13651]
ce426f
    
ce426f
    AF_UNSPEC results in sending two queries in parallel, one for the A
ce426f
    record and the other for the AAAA record.  If one of these is a
ce426f
    referral, then the query fails, which is wrong.  It should return at
ce426f
    least the one successful response.
ce426f
    
ce426f
    The fix has two parts.  The first part makes the referral fall back to
ce426f
    the SERVFAIL path, which results in using the successful response.
ce426f
    There is a bug in that path however, due to which the second part is
ce426f
    necessary.  The bug here is that if the first response is a failure
ce426f
    and the second succeeds, __libc_res_nsearch does not detect that and
ce426f
    assumes a failure.  The case where the first response is a success and
ce426f
    the second fails, works correctly.
ce426f
    
ce426f
    This condition is produced by buggy routers, so here's a crude
ce426f
    interposable library that can simulate such a condition.  The library
ce426f
    overrides the recvfrom syscall and modifies the header of the packet
ce426f
    received to reproduce this scenario.  It has two key variables:
ce426f
    mod_packet and first_error.
ce426f
    
ce426f
    The mod_packet variable when set to 0, results in odd packets being
ce426f
    modified to be a referral.  When set to 1, even packets are modified
ce426f
    to be a referral.
ce426f
    
ce426f
    The first_error causes the first response to be a failure so that a
ce426f
    domain-appended search is performed to test the second part of the
ce426f
    __libc_nsearch fix.
ce426f
    
ce426f
    The driver for this fix is a simple getaddrinfo program that does an
ce426f
    AF_UNSPEC query.  I have omitted this since it should be easy to
ce426f
    implement.
ce426f
    
ce426f
    I have tested this on x86_64.
ce426f
    
ce426f
    The interceptor library source:
ce426f
    
ce426f
    /* Override recvfrom and modify the header of the first DNS response to make it
ce426f
       a referral and reproduce bz #845218.  We have to resort to this ugly hack
ce426f
       because we cannot make bind return the buggy response of a referral for the
ce426f
       AAAA record and an authoritative response for the A record.  */
ce426f
     #define _GNU_SOURCE
ce426f
     #include <sys/types.h>
ce426f
     #include <sys/socket.h>
ce426f
     #include <netinet/in.h>
ce426f
     #include <arpa/inet.h>
ce426f
     #include <stdio.h>
ce426f
     #include <stdbool.h>
ce426f
     #include <endian.h>
ce426f
     #include <dlfcn.h>
ce426f
     #include <stdlib.h>
ce426f
    
ce426f
    /* Lifted from resolv/arpa/nameser_compat.h.  */
ce426f
    typedef struct {
ce426f
        unsigned        id :16;         /*%< query identification number */
ce426f
     #if BYTE_ORDER == BIG_ENDIAN
ce426f
        /* fields in third byte */
ce426f
        unsigned        qr: 1;          /*%< response flag */
ce426f
        unsigned        opcode: 4;      /*%< purpose of message */
ce426f
        unsigned        aa: 1;          /*%< authoritive answer */
ce426f
        unsigned        tc: 1;          /*%< truncated message */
ce426f
        unsigned        rd: 1;          /*%< recursion desired */
ce426f
        /* fields
ce426f
         * in
ce426f
         * fourth
ce426f
         * byte
ce426f
         * */
ce426f
        unsigned        ra: 1;          /*%< recursion available */
ce426f
        unsigned        unused :1;      /*%< unused bits (MBZ as of 4.9.3a3) */
ce426f
        unsigned        ad: 1;          /*%< authentic data from named */
ce426f
        unsigned        cd: 1;          /*%< checking disabled by resolver */
ce426f
        unsigned        rcode :4;       /*%< response code */
ce426f
     #endif
ce426f
     #if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
ce426f
        /* fields
ce426f
         * in
ce426f
         * third
ce426f
         * byte
ce426f
         * */
ce426f
        unsigned        rd :1;          /*%< recursion desired */
ce426f
        unsigned        tc :1;          /*%< truncated message */
ce426f
        unsigned        aa :1;          /*%< authoritive answer */
ce426f
        unsigned        opcode :4;      /*%< purpose of message */
ce426f
        unsigned        qr :1;          /*%< response flag */
ce426f
        /* fields
ce426f
         * in
ce426f
         * fourth
ce426f
         * byte
ce426f
         * */
ce426f
        unsigned        rcode :4;       /*%< response code */
ce426f
        unsigned        cd: 1;          /*%< checking disabled by resolver */
ce426f
        unsigned        ad: 1;          /*%< authentic data from named */
ce426f
        unsigned        unused :1;      /*%< unused bits (MBZ as of 4.9.3a3) */
ce426f
        unsigned        ra :1;          /*%< recursion available */
ce426f
     #endif
ce426f
        /* remaining
ce426f
         * bytes
ce426f
         * */
ce426f
        unsigned        qdcount :16;    /*%< number of question entries */
ce426f
        unsigned        ancount :16;    /*%< number of answer entries */
ce426f
        unsigned        nscount :16;    /*%< number of authority entries */
ce426f
        unsigned        arcount :16;    /*%< number of resource entries */
ce426f
    } HEADER;
ce426f
    
ce426f
    static int done = 0;
ce426f
    
ce426f
    /* Packets to modify.  0 for the odd packets and 1 for even packets.  */
ce426f
    static const int mod_packet = 0;
ce426f
    
ce426f
    /* Set to true if the first request should result in an error, resulting in a
ce426f
       search query.  */
ce426f
    static bool first_error = true;
ce426f
    
ce426f
    static ssize_t (*real_recvfrom) (int sockfd, void *buf, size_t len, int flags,
ce426f
    			  struct sockaddr *src_addr, socklen_t *addrlen);
ce426f
    
ce426f
    void
ce426f
    __attribute__ ((constructor))
ce426f
    init (void)
ce426f
    {
ce426f
      real_recvfrom = dlsym (RTLD_NEXT, "recvfrom");
ce426f
    
ce426f
      if (real_recvfrom == NULL)
ce426f
        {
ce426f
          printf ("Failed to get reference to recvfrom: %s\n", dlerror ());
ce426f
          printf ("Cannot simulate test\n");
ce426f
          abort ();
ce426f
        }
ce426f
    }
ce426f
    
ce426f
    /* Modify the second packet that we receive to set the header in a manner as to
ce426f
       reproduce BZ #845218.  */
ce426f
    static void
ce426f
    mod_buf (HEADER *h, int port)
ce426f
    {
ce426f
      if (done % 2 == mod_packet || (first_error && done == 1))
ce426f
        {
ce426f
          printf ("(Modifying header)");
ce426f
    
ce426f
          if (first_error && done == 1)
ce426f
    	h->rcode = 3;
ce426f
          else
ce426f
    	h->rcode = 0;	/* NOERROR == 0.  */
ce426f
          h->ancount = 0;
ce426f
          h->aa = 0;
ce426f
          h->ra = 0;
ce426f
          h->arcount = 0;
ce426f
        }
ce426f
      done++;
ce426f
    }
ce426f
    
ce426f
    ssize_t
ce426f
    recvfrom (int sockfd, void *buf, size_t len, int flags,
ce426f
    	  struct sockaddr *src_addr, socklen_t *addrlen)
ce426f
    {
ce426f
      ssize_t ret = real_recvfrom (sockfd, buf, len, flags, src_addr, addrlen);
ce426f
      int port = htons (((struct sockaddr_in *) src_addr)->sin_port);
ce426f
      struct in_addr addr = ((struct sockaddr_in *) src_addr)->sin_addr;
ce426f
      const char *host = inet_ntoa (addr);
ce426f
      printf ("\n*** From %s:%d: ", host, port);
ce426f
    
ce426f
      mod_buf (buf, port);
ce426f
    
ce426f
      printf ("returned %zd\n", ret);
ce426f
      return ret;
ce426f
    }
ce426f
ce426f
 ChangeLog          | 11 +++++++++++
ce426f
 NEWS               | 14 +++++++-------
ce426f
 resolv/res_query.c |  7 +++++--
ce426f
 resolv/res_send.c  |  2 +-
ce426f
 4 files changed, 24 insertions(+), 10 deletions(-)
ce426f
ce426f
commit e35c53e397e7abbd41fedacdedcfa5af7b5c2c52
ce426f
Author: Siddhesh Poyarekar <siddhesh@redhat.com>
ce426f
Date:   Tue Jul 8 16:40:24 2014 +0530
ce426f
ce426f
    Check value at resplen2 if it is not NULL
ce426f
    
ce426f
    There was a typo in the previous patch due to which resplen2 was
ce426f
    checked for non-zero instead of the value at resplen2.  Fix that and
ce426f
    improve the condition by checking resplen2 for non-NULL (instead of
ce426f
    answerp2) and also adding the check in a third place.
ce426f
ce426f
 ChangeLog          | 3 +++
ce426f
 resolv/res_query.c | 9 +++++----
ce426f
 2 files changed, 8 insertions(+), 4 deletions(-)
ce426f
ce426f
diff -pruN glibc-2.17-c758a686/resolv/res_query.c glibc-2.17-c758a686/resolv/res_query.c
ce426f
--- glibc-2.17-c758a686/resolv/res_query.c	2012-12-25 08:32:13.000000000 +0530
ce426f
+++ glibc-2.17-c758a686/resolv/res_query.c	2014-09-05 14:28:06.439191017 +0530
ce426f
@@ -378,7 +378,9 @@ __libc_res_nsearch(res_state statp,
ce426f
 		ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
ce426f
 					      answer, anslen, answerp,
ce426f
 					      answerp2, nanswerp2, resplen2);
ce426f
-		if (ret > 0 || trailing_dot)
ce426f
+		if (ret > 0 || trailing_dot
ce426f
+		    /* If the second response is valid then we use that.  */
ce426f
+		    || (ret == 0 && resplen2 != NULL && *resplen2 > 0))
ce426f
 			return (ret);
ce426f
 		saved_herrno = h_errno;
ce426f
 		tried_as_is++;
ce426f
@@ -418,7 +420,8 @@ __libc_res_nsearch(res_state statp,
ce426f
 						      answer, anslen, answerp,
ce426f
 						      answerp2, nanswerp2,
ce426f
 						      resplen2);
ce426f
-			if (ret > 0)
ce426f
+			if (ret > 0 || (ret == 0 && resplen2 != NULL
ce426f
+					&& *resplen2 > 0))
ce426f
 				return (ret);
ce426f
 
ce426f
 			if (answerp && *answerp != answer) {
ce426f
@@ -487,7 +490,8 @@ __libc_res_nsearch(res_state statp,
ce426f
 		ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
ce426f
 					      answer, anslen, answerp,
ce426f
 					      answerp2, nanswerp2, resplen2);
ce426f
-		if (ret > 0)
ce426f
+		if (ret > 0 || (ret == 0 && resplen2 != NULL
ce426f
+				&& *resplen2 > 0))
ce426f
 			return (ret);
ce426f
 	}
ce426f
 
ce426f
diff -pruN glibc-2.17-c758a686/resolv/res_send.c glibc-2.17-c758a686/resolv/res_send.c
ce426f
--- glibc-2.17-c758a686/resolv/res_send.c	2014-09-05 14:28:30.039337246 +0530
ce426f
+++ glibc-2.17-c758a686/resolv/res_send.c	2014-09-05 14:28:06.439191017 +0530
ce426f
@@ -1343,6 +1343,7 @@ send_dg(res_state statp,
ce426f
 				(*thisresplenp > *thisanssizp)
ce426f
 				? *thisanssizp : *thisresplenp);
ce426f
 
ce426f
+		next_ns:
ce426f
 			if (recvresp1 || (buf2 != NULL && recvresp2)) {
ce426f
 			  *resplen2 = 0;
ce426f
 			  return resplen;
ce426f
@@ -1360,7 +1361,6 @@ send_dg(res_state statp,
ce426f
 			    goto wait;
ce426f
 			  }
ce426f
 
ce426f
-		next_ns:
ce426f
 			__res_iclose(statp, false);
ce426f
 			/* don't retry if called from dig */
ce426f
 			if (!statp->pfcode)