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