mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-21 12:30:06 +00:00
resolv: Implement no-aaaa stub resolver option
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
This commit is contained in:
parent
62a321b12d
commit
f282cdbe7f
12
NEWS
12
NEWS
@ -29,6 +29,18 @@ Major new features:
|
||||
memory is carried out in the context of the caller, using the caller's
|
||||
CPU affinity, and priority with CPU usage accounted to the caller.
|
||||
|
||||
* The “no-aaaa” DNS stub resolver option has been added. System
|
||||
administrators can use it to suppress AAAA queries made by the stub
|
||||
resolver, including AAAA lookups triggered by NSS-based interfaces
|
||||
such as getaddrinfo. Only DNS lookups are affected: IPv6 data in
|
||||
/etc/hosts is still used, getaddrinfo with AI_PASSIVE will still
|
||||
produce IPv6 addresses, and configured IPv6 name servers are still
|
||||
used. To produce correct Name Error (NXDOMAIN) results, AAAA queries
|
||||
are translated to A queries. The new resolver option is intended
|
||||
primarily for diagnostic purposes, to rule out that AAAA DNS queries
|
||||
have adverse impact. It is incompatible with EDNS0 usage and DNSSEC
|
||||
validation by applications.
|
||||
|
||||
Deprecated and removed features, and other changes affecting compatibility:
|
||||
|
||||
* Support for prelink will be removed in the next release; this includes
|
||||
|
@ -51,6 +51,7 @@ routines := \
|
||||
nss_dns_functions \
|
||||
res-close \
|
||||
res-name-checking \
|
||||
res-noaaaa \
|
||||
res-state \
|
||||
res_context_hostalias \
|
||||
res_enable_icmp \
|
||||
@ -92,6 +93,7 @@ tests += \
|
||||
tst-resolv-binary \
|
||||
tst-resolv-edns \
|
||||
tst-resolv-network \
|
||||
tst-resolv-noaaaa \
|
||||
tst-resolv-nondecimal \
|
||||
tst-resolv-res_init-multi \
|
||||
tst-resolv-search \
|
||||
@ -265,6 +267,7 @@ $(objpfx)tst-resolv-res_init-multi: $(objpfx)libresolv.so \
|
||||
$(shared-thread-library)
|
||||
$(objpfx)tst-resolv-res_init-thread: $(objpfx)libresolv.so \
|
||||
$(shared-thread-library)
|
||||
$(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
$(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
$(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
$(objpfx)tst-resolv-rotate: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
|
@ -124,6 +124,14 @@ static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1,
|
||||
char *buffer, size_t buflen,
|
||||
int *errnop, int *h_errnop,
|
||||
int32_t *ttlp);
|
||||
static enum nss_status gaih_getanswer_noaaaa (const querybuf *answer1,
|
||||
int anslen1,
|
||||
const char *qname,
|
||||
struct gaih_addrtuple **pat,
|
||||
char *buffer, size_t buflen,
|
||||
int *errnop, int *h_errnop,
|
||||
int32_t *ttlp);
|
||||
|
||||
|
||||
static enum nss_status gethostbyname3_context (struct resolv_context *ctx,
|
||||
const char *name, int af,
|
||||
@ -369,17 +377,31 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
|
||||
int resplen2 = 0;
|
||||
int ans2p_malloced = 0;
|
||||
|
||||
|
||||
int olderr = errno;
|
||||
int n = __res_context_search (ctx, name, C_IN, T_QUERY_A_AND_AAAA,
|
||||
int n;
|
||||
|
||||
if ((ctx->resp->options & RES_NOAAAA) == 0)
|
||||
{
|
||||
n = __res_context_search (ctx, name, C_IN, T_QUERY_A_AND_AAAA,
|
||||
host_buffer.buf->buf, 2048, &host_buffer.ptr,
|
||||
&ans2p, &nans2p, &resplen2, &ans2p_malloced);
|
||||
if (n >= 0)
|
||||
{
|
||||
status = gaih_getanswer (host_buffer.buf, n, (const querybuf *) ans2p,
|
||||
resplen2, name, pat, buffer, buflen,
|
||||
errnop, herrnop, ttlp);
|
||||
if (n >= 0)
|
||||
status = gaih_getanswer (host_buffer.buf, n, (const querybuf *) ans2p,
|
||||
resplen2, name, pat, buffer, buflen,
|
||||
errnop, herrnop, ttlp);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = __res_context_search (ctx, name, C_IN, T_A,
|
||||
host_buffer.buf->buf, 2048, NULL,
|
||||
NULL, NULL, NULL, NULL);
|
||||
if (n >= 0)
|
||||
status = gaih_getanswer_noaaaa (host_buffer.buf, n,
|
||||
name, pat, buffer, buflen,
|
||||
errnop, herrnop, ttlp);
|
||||
}
|
||||
if (n < 0)
|
||||
{
|
||||
switch (errno)
|
||||
{
|
||||
@ -1387,3 +1409,21 @@ gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2,
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Variant of gaih_getanswer without a second (AAAA) response. */
|
||||
static enum nss_status
|
||||
gaih_getanswer_noaaaa (const querybuf *answer1, int anslen1, const char *qname,
|
||||
struct gaih_addrtuple **pat,
|
||||
char *buffer, size_t buflen,
|
||||
int *errnop, int *h_errnop, int32_t *ttlp)
|
||||
{
|
||||
int first = 1;
|
||||
|
||||
enum nss_status status = NSS_STATUS_NOTFOUND;
|
||||
if (anslen1 > 0)
|
||||
status = gaih_getanswer_slice (answer1, anslen1, qname,
|
||||
&pat, &buffer, &buflen,
|
||||
errnop, h_errnop, ttlp,
|
||||
&first);
|
||||
return status;
|
||||
}
|
||||
|
143
resolv/res-noaaaa.c
Normal file
143
resolv/res-noaaaa.c
Normal file
@ -0,0 +1,143 @@
|
||||
/* Implement suppression of AAAA queries.
|
||||
Copyright (C) 2022 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <resolv.h>
|
||||
#include <string.h>
|
||||
#include <resolv-internal.h>
|
||||
#include <resolv_context.h>
|
||||
#include <arpa/nameser.h>
|
||||
|
||||
/* Returns true if the question type at P matches EXPECTED, and the
|
||||
class is IN. */
|
||||
static bool
|
||||
qtype_matches (const unsigned char *p, int expected)
|
||||
{
|
||||
/* This assumes that T_A/C_IN constants are less than 256, which
|
||||
they are. */
|
||||
return p[0] == 0 && p[1] == expected && p[2] == 0 && p[3] == C_IN;
|
||||
}
|
||||
|
||||
/* Handle RES_NOAAAA translation of AAAA queries. To produce a Name
|
||||
Error (NXDOMAIN) repsonse for domain names that do not exist, it is
|
||||
still necessary to send a query. Using question type A is a
|
||||
conservative choice. In the returned answer, it is necessary to
|
||||
switch back the question type to AAAA. */
|
||||
bool
|
||||
__res_handle_no_aaaa (struct resolv_context *ctx,
|
||||
const unsigned char *buf, int buflen,
|
||||
unsigned char *ans, int anssiz, int *result)
|
||||
{
|
||||
/* AAAA mode is not active, or the query looks invalid (will not be
|
||||
able to be parsed). */
|
||||
if ((ctx->resp->options & RES_NOAAAA) == 0
|
||||
|| buflen <= sizeof (HEADER))
|
||||
return false;
|
||||
|
||||
/* The replacement A query is produced here. */
|
||||
struct
|
||||
{
|
||||
HEADER header;
|
||||
unsigned char question[NS_MAXCDNAME + 4];
|
||||
} replacement;
|
||||
memcpy (&replacement.header, buf, sizeof (replacement.header));
|
||||
|
||||
if (replacement.header.qr
|
||||
|| replacement.header.opcode != 0
|
||||
|| replacement.header.rcode != 0
|
||||
|| ntohs (replacement.header.qdcount) != 1
|
||||
|| ntohs (replacement.header.ancount) != 0
|
||||
|| ntohs (replacement.header.nscount) != 0)
|
||||
/* Not a well-formed question. Let the core resolver code produce
|
||||
the proper error. */
|
||||
return false;
|
||||
|
||||
/* Disable EDNS0. */
|
||||
replacement.header.arcount = htons (0);
|
||||
|
||||
/* Extract the QNAME. */
|
||||
int ret = __ns_name_unpack (buf, buf + buflen, buf + sizeof (HEADER),
|
||||
replacement.question, NS_MAXCDNAME);
|
||||
if (ret < 0)
|
||||
/* Format error. */
|
||||
return false;
|
||||
|
||||
/* Compute the end of the question name. */
|
||||
const unsigned char *after_question = buf + sizeof (HEADER) + ret;
|
||||
|
||||
/* Check that we are dealing with an AAAA query. */
|
||||
if (buf + buflen - after_question < 4
|
||||
|| !qtype_matches (after_question, T_AAAA))
|
||||
return false;
|
||||
|
||||
/* Find the place to store the type/class data in the replacement
|
||||
query. */
|
||||
after_question = replacement.question;
|
||||
/* This cannot fail because __ns_name_unpack above produced a valid
|
||||
domain name. */
|
||||
(void) __ns_name_skip (&after_question, &replacement.question[NS_MAXCDNAME]);
|
||||
unsigned char *start_of_query = (unsigned char *) &replacement;
|
||||
const unsigned char *end_of_query = after_question + 4;
|
||||
|
||||
/* Produce an A/IN query. */
|
||||
{
|
||||
unsigned char *p = (unsigned char *) after_question;
|
||||
p[0] = 0;
|
||||
p[1] = T_A;
|
||||
p[2] = 0;
|
||||
p[3] = C_IN;
|
||||
}
|
||||
|
||||
/* Clear the output buffer, to avoid reading undefined data when
|
||||
rewriting the result from A to AAAA. */
|
||||
memset (ans, 0, anssiz);
|
||||
|
||||
/* Always perform the message translation, independent of the error
|
||||
code. */
|
||||
ret = __res_context_send (ctx,
|
||||
start_of_query, end_of_query - start_of_query,
|
||||
NULL, 0, ans, anssiz,
|
||||
NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
/* Patch in the AAAA question type if there is room and the A query
|
||||
type was received. */
|
||||
after_question = ans + sizeof (HEADER);
|
||||
if (__ns_name_skip (&after_question, ans + anssiz) == 0
|
||||
&& ans + anssiz - after_question >= 4
|
||||
&& qtype_matches (after_question, T_A))
|
||||
{
|
||||
((unsigned char *) after_question)[1] = T_AAAA;
|
||||
|
||||
/* Create an aligned copy of the header. Hide all data except
|
||||
the question from the response. Put back the header. There is
|
||||
no need to change the response code. The zero answer count turns
|
||||
a positive response with data into a no-data response. */
|
||||
memcpy (&replacement.header, ans, sizeof (replacement.header));
|
||||
replacement.header.ancount = htons (0);
|
||||
replacement.header.nscount = htons (0);
|
||||
replacement.header.arcount = htons (0);
|
||||
memcpy (ans, &replacement.header, sizeof (replacement.header));
|
||||
|
||||
/* Truncate the reply. */
|
||||
if (ret <= 0)
|
||||
*result = ret;
|
||||
else
|
||||
*result = after_question - ans + 4;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -613,6 +613,7 @@ p_option(u_long option) {
|
||||
case RES_NOTLDQUERY: return "no-tld-query";
|
||||
case RES_NORELOAD: return "no-reload";
|
||||
case RES_TRUSTAD: return "trust-ad";
|
||||
case RES_NOAAAA: return "no-aaaa";
|
||||
/* XXX nonreentrant */
|
||||
default: sprintf(nbuf, "?0x%lx?", (u_long)option);
|
||||
return (nbuf);
|
||||
|
@ -695,6 +695,7 @@ res_setoptions (struct resolv_conf_parser *parser, const char *options)
|
||||
{ STRnLEN ("no-reload"), 0, RES_NORELOAD },
|
||||
{ STRnLEN ("use-vc"), 0, RES_USEVC },
|
||||
{ STRnLEN ("trust-ad"), 0, RES_TRUSTAD },
|
||||
{ STRnLEN ("no-aaaa"), 0, RES_NOAAAA },
|
||||
};
|
||||
#define noptions (sizeof (options) / sizeof (options[0]))
|
||||
for (int i = 0; i < noptions; ++i)
|
||||
|
@ -204,10 +204,26 @@ __res_context_query (struct resolv_context *ctx, const char *name,
|
||||
free (buf);
|
||||
return (n);
|
||||
}
|
||||
assert (answerp == NULL || (void *) *answerp == (void *) answer);
|
||||
n = __res_context_send (ctx, query1, nquery1, query2, nquery2, answer,
|
||||
anslen, answerp, answerp2, nanswerp2, resplen2,
|
||||
answerp2_malloced);
|
||||
|
||||
/* Suppress AAAA lookups if required. __res_handle_no_aaaa
|
||||
checks RES_NOAAAA first, so avoids parsing the
|
||||
just-generated query packet in most cases. nss_dns avoids
|
||||
using T_QUERY_A_AND_AAAA in RES_NOAAAA mode, so there is no
|
||||
need to handle it here. */
|
||||
if (type == T_AAAA && __res_handle_no_aaaa (ctx, query1, nquery1,
|
||||
answer, anslen, &n))
|
||||
/* There must be no second query for AAAA queries. The code
|
||||
below is still needed to translate NODATA responses. */
|
||||
assert (query2 == NULL);
|
||||
else
|
||||
{
|
||||
assert (answerp == NULL || (void *) *answerp == (void *) answer);
|
||||
n = __res_context_send (ctx, query1, nquery1, query2, nquery2,
|
||||
answer, anslen,
|
||||
answerp, answerp2, nanswerp2, resplen2,
|
||||
answerp2_malloced);
|
||||
}
|
||||
|
||||
if (use_malloc)
|
||||
free (buf);
|
||||
if (n < 0) {
|
||||
|
@ -438,8 +438,13 @@ context_send_common (struct resolv_context *ctx,
|
||||
RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
|
||||
return -1;
|
||||
}
|
||||
int result = __res_context_send (ctx, buf, buflen, NULL, 0, ans, anssiz,
|
||||
NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
int result;
|
||||
if (__res_handle_no_aaaa (ctx, buf, buflen, ans, anssiz, &result))
|
||||
return result;
|
||||
|
||||
result = __res_context_send (ctx, buf, buflen, NULL, 0, ans, anssiz,
|
||||
NULL, NULL, NULL, NULL, NULL);
|
||||
__resolv_context_put (ctx);
|
||||
return result;
|
||||
}
|
||||
|
@ -85,6 +85,14 @@ int __res_context_send (struct resolv_context *, const unsigned char *, int,
|
||||
int *, int *, int *);
|
||||
libc_hidden_proto (__res_context_send)
|
||||
|
||||
/* Return true if the query has been handled in RES_NOAAAA mode. For
|
||||
that, RES_NOAAAA must be active, and the question type must be AAAA.
|
||||
The caller is expected to return *RESULT as the return value. */
|
||||
bool __res_handle_no_aaaa (struct resolv_context *ctx,
|
||||
const unsigned char *buf, int buflen,
|
||||
unsigned char *ans, int anssiz, int *result)
|
||||
attribute_hidden;
|
||||
|
||||
/* Internal function similar to res_hostalias. */
|
||||
const char *__res_context_hostalias (struct resolv_context *,
|
||||
const char *, char *, size_t);
|
||||
|
@ -132,6 +132,7 @@ struct res_sym {
|
||||
as a TLD. */
|
||||
#define RES_NORELOAD 0x02000000 /* No automatic configuration reload. */
|
||||
#define RES_TRUSTAD 0x04000000 /* Request AD bit, keep it in responses. */
|
||||
#define RES_NOAAAA 0x08000000 /* Suppress AAAA queries. */
|
||||
|
||||
#define RES_DEFAULT (RES_RECURSE|RES_DEFNAMES|RES_DNSRCH)
|
||||
|
||||
|
533
resolv/tst-resolv-noaaaa.c
Normal file
533
resolv/tst-resolv-noaaaa.c
Normal file
@ -0,0 +1,533 @@
|
||||
/* Test the RES_NOAAAA resolver option.
|
||||
Copyright (C) 2022 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <resolv.h>
|
||||
#include <stdlib.h>
|
||||
#include <support/check.h>
|
||||
#include <support/check_nss.h>
|
||||
#include <support/resolv_test.h>
|
||||
#include <support/support.h>
|
||||
|
||||
/* Used to keep track of the number of queries. */
|
||||
static volatile unsigned int queries;
|
||||
|
||||
static void
|
||||
response (const struct resolv_response_context *ctx,
|
||||
struct resolv_response_builder *b,
|
||||
const char *qname, uint16_t qclass, uint16_t qtype)
|
||||
{
|
||||
/* Each test should only send one query. */
|
||||
++queries;
|
||||
TEST_COMPARE (queries, 1);
|
||||
|
||||
/* AAAA queries are supposed to be disabled. */
|
||||
TEST_VERIFY (qtype != T_AAAA);
|
||||
TEST_COMPARE (qclass, C_IN);
|
||||
|
||||
/* The only other query type besides A is PTR. */
|
||||
if (qtype != T_A)
|
||||
TEST_COMPARE (qtype, T_PTR);
|
||||
|
||||
int an, ns, ar;
|
||||
char *tail;
|
||||
if (sscanf (qname, "an%d.ns%d.ar%d.%ms", &an, &ns, &ar, &tail) != 4)
|
||||
FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
|
||||
TEST_COMPARE_STRING (tail, "example");
|
||||
free (tail);
|
||||
|
||||
if (an < 0 || ns < 0 || ar < 0)
|
||||
{
|
||||
struct resolv_response_flags flags = { .rcode = NXDOMAIN, };
|
||||
resolv_response_init (b, flags);
|
||||
resolv_response_add_question (b, qname, qclass, qtype);
|
||||
return;
|
||||
}
|
||||
|
||||
struct resolv_response_flags flags = {};
|
||||
resolv_response_init (b, flags);
|
||||
resolv_response_add_question (b, qname, qclass, qtype);
|
||||
|
||||
resolv_response_section (b, ns_s_an);
|
||||
for (int i = 0; i < an; ++i)
|
||||
{
|
||||
resolv_response_open_record (b, qname, qclass, qtype, 60);
|
||||
switch (qtype)
|
||||
{
|
||||
case T_A:
|
||||
char ipv4[4] = {192, 0, 2, i + 1};
|
||||
resolv_response_add_data (b, &ipv4, sizeof (ipv4));
|
||||
break;
|
||||
|
||||
case T_PTR:
|
||||
char *name = xasprintf ("ptr-%d", i);
|
||||
resolv_response_add_name (b, name);
|
||||
free (name);
|
||||
break;
|
||||
}
|
||||
resolv_response_close_record (b);
|
||||
}
|
||||
|
||||
resolv_response_section (b, ns_s_ns);
|
||||
for (int i = 0; i < ns; ++i)
|
||||
{
|
||||
resolv_response_open_record (b, qname, qclass, T_NS, 60);
|
||||
char *name = xasprintf ("ns%d.example.net", i);
|
||||
resolv_response_add_name (b, name);
|
||||
free (name);
|
||||
resolv_response_close_record (b);
|
||||
}
|
||||
|
||||
resolv_response_section (b, ns_s_ar);
|
||||
int addr = 1;
|
||||
for (int i = 0; i < ns; ++i)
|
||||
{
|
||||
char *name = xasprintf ("ns%d.example.net", i);
|
||||
for (int j = 0; j < ar; ++j)
|
||||
{
|
||||
resolv_response_open_record (b, name, qclass, T_A, 60);
|
||||
char ipv4[4] = {192, 0, 2, addr};
|
||||
resolv_response_add_data (b, &ipv4, sizeof (ipv4));
|
||||
resolv_response_close_record (b);
|
||||
|
||||
resolv_response_open_record (b, name, qclass, T_AAAA, 60);
|
||||
char ipv6[16]
|
||||
= {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, addr};
|
||||
resolv_response_add_data (b, &ipv6, sizeof (ipv6));
|
||||
resolv_response_close_record (b);
|
||||
|
||||
++addr;
|
||||
}
|
||||
free (name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Number of modes. Lowest bit encodes *n* function vs implicit _res
|
||||
argument. The mode numbers themselves are arbitrary. */
|
||||
enum { mode_count = 8 };
|
||||
|
||||
/* res_send-like modes do not perform error translation. */
|
||||
enum { first_send_mode = 6 };
|
||||
|
||||
static int
|
||||
libresolv_query (unsigned int mode, const char *qname, uint16_t qtype,
|
||||
unsigned char *buf, size_t buflen)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
|
||||
TEST_VERIFY_EXIT (mode < mode_count);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
return res_query (qname, C_IN, qtype, buf, buflen);
|
||||
case 1:
|
||||
return res_nquery (&_res, qname, C_IN, qtype, buf, buflen);
|
||||
case 2:
|
||||
return res_search (qname, C_IN, qtype, buf, buflen);
|
||||
case 3:
|
||||
return res_nsearch (&_res, qname, C_IN, qtype, buf, buflen);
|
||||
case 4:
|
||||
return res_querydomain (qname, "", C_IN, qtype, buf, buflen);
|
||||
case 5:
|
||||
return res_nquerydomain (&_res, qname, "", C_IN, qtype, buf, buflen);
|
||||
case 6:
|
||||
{
|
||||
unsigned char querybuf[512];
|
||||
int ret = res_mkquery (QUERY, qname, C_IN, qtype,
|
||||
NULL, 0, NULL, querybuf, sizeof (querybuf));
|
||||
TEST_VERIFY_EXIT (ret > 0);
|
||||
errno = saved_errno;
|
||||
return res_send (querybuf, ret, buf, buflen);
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
unsigned char querybuf[512];
|
||||
int ret = res_nmkquery (&_res, QUERY, qname, C_IN, qtype,
|
||||
NULL, 0, NULL, querybuf, sizeof (querybuf));
|
||||
TEST_VERIFY_EXIT (ret > 0);
|
||||
errno = saved_errno;
|
||||
return res_nsend (&_res, querybuf, ret, buf, buflen);
|
||||
}
|
||||
}
|
||||
__builtin_unreachable ();
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct resolv_test *obj = resolv_test_start
|
||||
((struct resolv_redirect_config)
|
||||
{
|
||||
.response_callback = response
|
||||
});
|
||||
|
||||
_res.options |= RES_NOAAAA;
|
||||
|
||||
check_hostent ("an1.ns2.ar1.example",
|
||||
gethostbyname ("an1.ns2.ar1.example"),
|
||||
"name: an1.ns2.ar1.example\n"
|
||||
"address: 192.0.2.1\n");
|
||||
queries = 0;
|
||||
check_hostent ("an0.ns2.ar1.example",
|
||||
gethostbyname ("an0.ns2.ar1.example"),
|
||||
"error: NO_ADDRESS\n");
|
||||
queries = 0;
|
||||
check_hostent ("an-1.ns2.ar1.example",
|
||||
gethostbyname ("an-1.ns2.ar1.example"),
|
||||
"error: HOST_NOT_FOUND\n");
|
||||
queries = 0;
|
||||
|
||||
check_hostent ("an1.ns2.ar1.example AF_INET",
|
||||
gethostbyname2 ("an1.ns2.ar1.example", AF_INET),
|
||||
"name: an1.ns2.ar1.example\n"
|
||||
"address: 192.0.2.1\n");
|
||||
queries = 0;
|
||||
check_hostent ("an0.ns2.ar1.example AF_INET",
|
||||
gethostbyname2 ("an0.ns2.ar1.example", AF_INET),
|
||||
"error: NO_ADDRESS\n");
|
||||
queries = 0;
|
||||
check_hostent ("an-1.ns2.ar1.example AF_INET",
|
||||
gethostbyname2 ("an-1.ns2.ar1.example", AF_INET),
|
||||
"error: HOST_NOT_FOUND\n");
|
||||
queries = 0;
|
||||
|
||||
check_hostent ("an1.ns2.ar1.example AF_INET6",
|
||||
gethostbyname2 ("an1.ns2.ar1.example", AF_INET6),
|
||||
"error: NO_ADDRESS\n");
|
||||
queries = 0;
|
||||
check_hostent ("an0.ns2.ar1.example AF_INET6",
|
||||
gethostbyname2 ("an0.ns2.ar1.example", AF_INET6),
|
||||
"error: NO_ADDRESS\n");
|
||||
queries = 0;
|
||||
check_hostent ("an-1.ns2.ar1.example AF_INET6",
|
||||
gethostbyname2 ("an-1.ns2.ar1.example", AF_INET6),
|
||||
"error: HOST_NOT_FOUND\n");
|
||||
queries = 0;
|
||||
|
||||
/* Multiple addresses. */
|
||||
check_hostent ("an2.ns0.ar0.example",
|
||||
gethostbyname ("an2.ns0.ar0.example"),
|
||||
"name: an2.ns0.ar0.example\n"
|
||||
"address: 192.0.2.1\n"
|
||||
"address: 192.0.2.2\n");
|
||||
queries = 0;
|
||||
check_hostent ("an2.ns0.ar0.example AF_INET6",
|
||||
gethostbyname2 ("an2.ns0.ar0.example", AF_INET6),
|
||||
"error: NO_ADDRESS\n");
|
||||
queries = 0;
|
||||
|
||||
/* getaddrinfo checks with one address. */
|
||||
struct addrinfo *ai;
|
||||
int ret;
|
||||
ret = getaddrinfo ("an1.ns2.ar1.example", "80",
|
||||
&(struct addrinfo)
|
||||
{
|
||||
.ai_family = AF_INET,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
}, &ai);
|
||||
check_addrinfo ("an1.ns2.ar1.example (AF_INET)", ai, ret,
|
||||
"address: STREAM/TCP 192.0.2.1 80\n");
|
||||
freeaddrinfo (ai);
|
||||
queries = 0;
|
||||
ret = getaddrinfo ("an1.ns2.ar1.example", "80",
|
||||
&(struct addrinfo)
|
||||
{
|
||||
.ai_family = AF_INET6,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
}, &ai);
|
||||
check_addrinfo ("an1.ns2.ar1.example (AF_INET6)", ai, ret,
|
||||
"error: No address associated with hostname\n");
|
||||
queries = 0;
|
||||
ret = getaddrinfo ("an1.ns2.ar1.example", "80",
|
||||
&(struct addrinfo)
|
||||
{
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
}, &ai);
|
||||
check_addrinfo ("an1.ns2.ar1.example (AF_UNSPEC)", ai, ret,
|
||||
"address: STREAM/TCP 192.0.2.1 80\n");
|
||||
freeaddrinfo (ai);
|
||||
queries = 0;
|
||||
|
||||
/* getaddrinfo checks with three addresses. */
|
||||
ret = getaddrinfo ("an3.ns2.ar1.example", "80",
|
||||
&(struct addrinfo)
|
||||
{
|
||||
.ai_family = AF_INET,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
}, &ai);
|
||||
check_addrinfo ("an3.ns2.ar1.example (AF_INET)", ai, ret,
|
||||
"address: STREAM/TCP 192.0.2.1 80\n"
|
||||
"address: STREAM/TCP 192.0.2.2 80\n"
|
||||
"address: STREAM/TCP 192.0.2.3 80\n");
|
||||
freeaddrinfo (ai);
|
||||
queries = 0;
|
||||
ret = getaddrinfo ("an3.ns2.ar1.example", "80",
|
||||
&(struct addrinfo)
|
||||
{
|
||||
.ai_family = AF_INET6,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
}, &ai);
|
||||
check_addrinfo ("an3.ns2.ar1.example (AF_INET6)", ai, ret,
|
||||
"error: No address associated with hostname\n");
|
||||
queries = 0;
|
||||
ret = getaddrinfo ("an3.ns2.ar1.example", "80",
|
||||
&(struct addrinfo)
|
||||
{
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
}, &ai);
|
||||
check_addrinfo ("an3.ns2.ar1.example (AF_UNSPEC)", ai, ret,
|
||||
"address: STREAM/TCP 192.0.2.1 80\n"
|
||||
"address: STREAM/TCP 192.0.2.2 80\n"
|
||||
"address: STREAM/TCP 192.0.2.3 80\n");
|
||||
freeaddrinfo (ai);
|
||||
queries = 0;
|
||||
|
||||
/* getaddrinfo checks with no address. */
|
||||
ret = getaddrinfo ("an0.ns2.ar1.example", "80",
|
||||
&(struct addrinfo)
|
||||
{
|
||||
.ai_family = AF_INET,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
}, &ai);
|
||||
check_addrinfo ("an0.ns2.ar1.example (AF_INET)", ai, ret,
|
||||
"error: No address associated with hostname\n");
|
||||
queries = 0;
|
||||
ret = getaddrinfo ("an0.ns2.ar1.example", "80",
|
||||
&(struct addrinfo)
|
||||
{
|
||||
.ai_family = AF_INET6,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
}, &ai);
|
||||
check_addrinfo ("an0.ns2.ar1.example (AF_INET6)", ai, ret,
|
||||
"error: No address associated with hostname\n");
|
||||
queries = 0;
|
||||
ret = getaddrinfo ("an0.ns2.ar1.example", "80",
|
||||
&(struct addrinfo)
|
||||
{
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
}, &ai);
|
||||
check_addrinfo ("an-1.ns2.ar1.example (AF_UNSPEC)", ai, ret,
|
||||
"error: No address associated with hostname\n");
|
||||
queries = 0;
|
||||
|
||||
/* getaddrinfo checks with NXDOMAIN. */
|
||||
ret = getaddrinfo ("an-1.ns2.ar1.example", "80",
|
||||
&(struct addrinfo)
|
||||
{
|
||||
.ai_family = AF_INET,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
}, &ai);
|
||||
check_addrinfo ("an-1.ns2.ar1.example (AF_INET)", ai, ret,
|
||||
"error: Name or service not known\n");
|
||||
queries = 0;
|
||||
ret = getaddrinfo ("an-1.ns2.ar1.example", "80",
|
||||
&(struct addrinfo)
|
||||
{
|
||||
.ai_family = AF_INET6,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
}, &ai);
|
||||
check_addrinfo ("an-1.ns2.ar1.example (AF_INET6)", ai, ret,
|
||||
"error: Name or service not known\n");
|
||||
queries = 0;
|
||||
ret = getaddrinfo ("an-1.ns2.ar1.example", "80",
|
||||
&(struct addrinfo)
|
||||
{
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
}, &ai);
|
||||
check_addrinfo ("an-1.ns2.ar1.example (AF_UNSPEC)", ai, ret,
|
||||
"error: Name or service not known\n");
|
||||
queries = 0;
|
||||
|
||||
for (unsigned int mode = 0; mode < mode_count; ++mode)
|
||||
{
|
||||
unsigned char *buf;
|
||||
int ret;
|
||||
|
||||
/* Response for A. */
|
||||
buf = malloc (512);
|
||||
ret = libresolv_query (mode, "an1.ns2.ar1.example", T_A, buf, 512);
|
||||
TEST_VERIFY_EXIT (ret > 0);
|
||||
check_dns_packet ("an1.ns2.ar1.example A", buf, ret,
|
||||
"name: an1.ns2.ar1.example\n"
|
||||
"address: 192.0.2.1\n");
|
||||
free (buf);
|
||||
queries = 0;
|
||||
|
||||
/* NODATA response for A. */
|
||||
buf = malloc (512);
|
||||
errno = 0;
|
||||
ret = libresolv_query (mode, "an0.ns2.ar1.example", T_A, buf, 512);
|
||||
if (mode < first_send_mode)
|
||||
{
|
||||
TEST_COMPARE (ret, -1);
|
||||
TEST_COMPARE (errno, 0);
|
||||
TEST_COMPARE (h_errno, NO_ADDRESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_VERIFY_EXIT (ret > 0);
|
||||
TEST_COMPARE (((HEADER *)buf)->rcode, 0);
|
||||
check_dns_packet ("an1.ns2.ar1.example A", buf, ret,
|
||||
"name: an0.ns2.ar1.example\n");
|
||||
}
|
||||
free (buf);
|
||||
queries = 0;
|
||||
|
||||
/* NXDOMAIN response for A. */
|
||||
buf = malloc (512);
|
||||
errno = 0;
|
||||
ret = libresolv_query (mode, "an-1.ns2.ar1.example", T_A, buf, 512);
|
||||
if (mode < first_send_mode)
|
||||
{
|
||||
TEST_COMPARE (ret, -1);
|
||||
TEST_COMPARE (errno, 0);
|
||||
TEST_COMPARE (h_errno, HOST_NOT_FOUND);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_VERIFY_EXIT (ret > 0);
|
||||
TEST_COMPARE (((HEADER *)buf)->rcode, NXDOMAIN);
|
||||
check_dns_packet ("an1.ns2.ar1.example A", buf, ret,
|
||||
"name: an-1.ns2.ar1.example\n");
|
||||
}
|
||||
free (buf);
|
||||
queries = 0;
|
||||
|
||||
/* Response for PTR. */
|
||||
buf = malloc (512);
|
||||
ret = libresolv_query (mode, "an1.ns2.ar1.example", T_PTR, buf, 512);
|
||||
TEST_VERIFY_EXIT (ret > 0);
|
||||
check_dns_packet ("an1.ns2.ar1.example PTR", buf, ret,
|
||||
"name: an1.ns2.ar1.example\n"
|
||||
"data: an1.ns2.ar1.example PTR ptr-0\n");
|
||||
free (buf);
|
||||
queries = 0;
|
||||
|
||||
/* NODATA response for PTR. */
|
||||
buf = malloc (512);
|
||||
errno = 0;
|
||||
ret = libresolv_query (mode, "an0.ns2.ar1.example", T_PTR, buf, 512);
|
||||
if (mode < first_send_mode)
|
||||
{
|
||||
TEST_COMPARE (ret, -1);
|
||||
TEST_COMPARE (errno, 0);
|
||||
TEST_COMPARE (h_errno, NO_ADDRESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_VERIFY_EXIT (ret > 0);
|
||||
TEST_COMPARE (((HEADER *)buf)->rcode, 0);
|
||||
check_dns_packet ("an1.ns2.ar1.example PTR", buf, ret,
|
||||
"name: an0.ns2.ar1.example\n");
|
||||
}
|
||||
free (buf);
|
||||
queries = 0;
|
||||
|
||||
/* NXDOMAIN response for PTR. */
|
||||
buf = malloc (512);
|
||||
errno = 0;
|
||||
ret = libresolv_query (mode, "an-1.ns2.ar1.example", T_PTR, buf, 512);
|
||||
if (mode < first_send_mode)
|
||||
{
|
||||
TEST_COMPARE (ret, -1);
|
||||
TEST_COMPARE (errno, 0);
|
||||
TEST_COMPARE (h_errno, HOST_NOT_FOUND);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_VERIFY_EXIT (ret > 0);
|
||||
TEST_COMPARE (((HEADER *)buf)->rcode, NXDOMAIN);
|
||||
check_dns_packet ("an1.ns2.ar1.example PTR", buf, ret,
|
||||
"name: an-1.ns2.ar1.example\n");
|
||||
}
|
||||
free (buf);
|
||||
queries = 0;
|
||||
|
||||
/* NODATA response for AAAA. */
|
||||
buf = malloc (512);
|
||||
errno = 0;
|
||||
ret = libresolv_query (mode, "an1.ns2.ar1.example", T_AAAA, buf, 512);
|
||||
if (mode < first_send_mode)
|
||||
{
|
||||
TEST_COMPARE (ret, -1);
|
||||
TEST_COMPARE (errno, 0);
|
||||
TEST_COMPARE (h_errno, NO_ADDRESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_VERIFY_EXIT (ret > 0);
|
||||
TEST_COMPARE (((HEADER *)buf)->rcode, 0);
|
||||
check_dns_packet ("an1.ns2.ar1.example A", buf, ret,
|
||||
"name: an1.ns2.ar1.example\n");
|
||||
}
|
||||
free (buf);
|
||||
queries = 0;
|
||||
|
||||
/* NODATA response for AAAA (original is already NODATA). */
|
||||
buf = malloc (512);
|
||||
errno = 0;
|
||||
ret = libresolv_query (mode, "an0.ns2.ar1.example", T_AAAA, buf, 512);
|
||||
if (mode < first_send_mode)
|
||||
{
|
||||
TEST_COMPARE (ret, -1);
|
||||
TEST_COMPARE (errno, 0);
|
||||
TEST_COMPARE (h_errno, NO_ADDRESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_VERIFY_EXIT (ret > 0);
|
||||
TEST_COMPARE (((HEADER *)buf)->rcode, 0);
|
||||
check_dns_packet ("an0.ns2.ar1.example A", buf, ret,
|
||||
"name: an0.ns2.ar1.example\n");
|
||||
}
|
||||
free (buf);
|
||||
queries = 0;
|
||||
|
||||
/* NXDOMAIN response. */
|
||||
buf = malloc (512);
|
||||
errno = 0;
|
||||
ret = libresolv_query (mode, "an-1.ns2.ar1.example", T_AAAA, buf, 512);
|
||||
if (mode < first_send_mode)
|
||||
{
|
||||
TEST_COMPARE (ret, -1);
|
||||
TEST_COMPARE (errno, 0);
|
||||
TEST_COMPARE (h_errno, HOST_NOT_FOUND);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_VERIFY_EXIT (ret > 0);
|
||||
TEST_COMPARE (((HEADER *)buf)->rcode, NXDOMAIN);
|
||||
check_dns_packet ("an-1.ns2.ar1.example A", buf, ret,
|
||||
"name: an-1.ns2.ar1.example\n");
|
||||
}
|
||||
free (buf);
|
||||
queries = 0;
|
||||
}
|
||||
|
||||
resolv_test_end (obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
@ -128,6 +128,7 @@ print_resp (FILE *fp, res_state resp)
|
||||
print_option_flag (fp, &options, RES_NOTLDQUERY, "no-tld-query");
|
||||
print_option_flag (fp, &options, RES_NORELOAD, "no-reload");
|
||||
print_option_flag (fp, &options, RES_TRUSTAD, "trust-ad");
|
||||
print_option_flag (fp, &options, RES_NOAAAA, "no-aaaa");
|
||||
fputc ('\n', fp);
|
||||
if (options != 0)
|
||||
fprintf (fp, "; error: unresolved option bits: 0x%x\n", options);
|
||||
@ -721,6 +722,15 @@ struct test_case test_cases[] =
|
||||
"nameserver 192.0.2.1\n"
|
||||
"; nameserver[0]: [192.0.2.1]:53\n"
|
||||
},
|
||||
{.name = "no-aaaa flag",
|
||||
.conf = "options no-aaaa\n"
|
||||
"nameserver 192.0.2.1\n",
|
||||
.expected = "options no-aaaa\n"
|
||||
"search example.com\n"
|
||||
"; search[0]: example.com\n"
|
||||
"nameserver 192.0.2.1\n"
|
||||
"; nameserver[0]: [192.0.2.1]:53\n"
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user