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