aboutsummaryrefslogtreecommitdiff
path: root/resolv
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2017-06-30 21:10:23 +0200
committerFlorian Weimer <fweimer@redhat.com>2017-07-03 20:52:59 +0200
commit352f4ff9a268b81ef5d4b2413f582565806e4790 (patch)
treefb27056dfdeafe43c021f6127c9544c016e78019 /resolv
parentresolv: Add preinit tests to resolv/tst-resolv-res_init-skeleton.c (diff)
downloadglibc-352f4ff9a268b81ef5d4b2413f582565806e4790.tar.gz
glibc-352f4ff9a268b81ef5d4b2413f582565806e4790.tar.bz2
glibc-352f4ff9a268b81ef5d4b2413f582565806e4790.zip
resolv: Introduce struct resolv_context [BZ #21668]
struct resolv_context objects provide a temporary resolver context which does not change during a name lookup operation. Only when the outmost context is created, the stub resolver configuration is verified to be current (at present, only against previous res_init calls). Subsequent attempts to obtain the context will reuse the result of the initial verification operation. struct resolv_context can also be extended in the future to store data which needs to be deallocated during thread cancellation.
Diffstat (limited to 'resolv')
-rw-r--r--resolv/Makefile3
-rw-r--r--resolv/Versions10
-rw-r--r--resolv/compat-gethnamaddr.c95
-rw-r--r--resolv/nss_dns/dns-canon.c19
-rw-r--r--resolv/nss_dns/dns-host.c104
-rw-r--r--resolv/nss_dns/dns-network.c21
-rw-r--r--resolv/res-close.c3
-rw-r--r--resolv/res_libc.c31
-rw-r--r--resolv/res_mkquery.c92
-rw-r--r--resolv/res_query.c298
-rw-r--r--resolv/res_send.c44
-rw-r--r--resolv/res_use_inet6.h49
-rw-r--r--resolv/resolv-internal.h36
-rw-r--r--resolv/resolv_context.c201
-rw-r--r--resolv/resolv_context.h95
15 files changed, 810 insertions, 291 deletions
diff --git a/resolv/Makefile b/resolv/Makefile
index bab1ac24a6..126da0736a 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -28,7 +28,8 @@ headers := resolv.h bits/types/res_state.h \
sys/bitypes.h
routines := herror inet_addr inet_ntop inet_pton nsap_addr res_init \
- res_hconf res_libc res-state res_randomid res-close
+ res_hconf res_libc res-state res_randomid res-close \
+ resolv_context
tests = tst-aton tst-leaks tst-inet_ntop
xtests = tst-leaks2
diff --git a/resolv/Versions b/resolv/Versions
index f528ed51e8..b05778d965 100644
--- a/resolv/Versions
+++ b/resolv/Versions
@@ -26,8 +26,12 @@ libc {
__h_errno; __resp;
- __res_maybe_init; __res_iclose;
+ __res_iclose;
__inet_pton_length;
+ __resolv_context_get;
+ __resolv_context_get_preinit;
+ __resolv_context_get_override;
+ __resolv_context_put;
}
}
@@ -79,7 +83,9 @@ libresolv {
# Needed in libnss_dns.
__ns_name_unpack; __ns_name_ntop;
__ns_get16; __ns_get32;
- __libc_res_nquery; __libc_res_nsearch;
+ __res_context_query;
+ __res_context_search;
+ __res_context_hostalias;
}
}
diff --git a/resolv/compat-gethnamaddr.c b/resolv/compat-gethnamaddr.c
index 813c7d4e85..259378b2be 100644
--- a/resolv/compat-gethnamaddr.c
+++ b/resolv/compat-gethnamaddr.c
@@ -67,6 +67,7 @@
# include <stdio.h>
# include <netdb.h>
# include <resolv/resolv-internal.h>
+# include <resolv/resolv_context.h>
# include <ctype.h>
# include <errno.h>
# include <stdlib.h>
@@ -84,6 +85,9 @@ static u_char host_addr[16]; /* IPv4 or IPv6 */
static FILE *hostf = NULL;
static int stayopen = 0;
+static struct hostent *res_gethostbyname2_context (struct resolv_context *,
+ const char *name, int af);
+
static void map_v4v6_address (const char *src, char *dst) __THROW;
static void map_v4v6_hostent (struct hostent *hp, char **bp, int *len) __THROW;
@@ -428,23 +432,31 @@ libresolv_hidden_proto (res_gethostbyname2)
struct hostent *
res_gethostbyname (const char *name)
{
- struct hostent *hp;
-
- if (__res_maybe_init (&_res, 0) == -1) {
- __set_h_errno (NETDB_INTERNAL);
- return (NULL);
- }
- if (res_use_inet6 ()) {
- hp = res_gethostbyname2(name, AF_INET6);
- if (hp)
- return (hp);
+ struct resolv_context *ctx = __resolv_context_get ();
+ if (ctx == NULL)
+ {
+ __set_h_errno (NETDB_INTERNAL);
+ return NULL;
+ }
+
+ if (res_use_inet6 ())
+ {
+ struct hostent *hp = res_gethostbyname2_context (ctx, name, AF_INET6);
+ if (hp != NULL)
+ {
+ __resolv_context_put (ctx);
+ return hp;
}
- return (res_gethostbyname2(name, AF_INET));
+ }
+ struct hostent *hp = res_gethostbyname2_context (ctx, name, AF_INET);
+ __resolv_context_put (ctx);
+ return hp;
}
compat_symbol (libresolv, res_gethostbyname, res_gethostbyname, GLIBC_2_0);
-struct hostent *
-res_gethostbyname2 (const char *name, int af)
+static struct hostent *
+res_gethostbyname2_context (struct resolv_context *ctx,
+ const char *name, int af)
{
union
{
@@ -457,11 +469,6 @@ res_gethostbyname2 (const char *name, int af)
int n, size, type, len;
struct hostent *ret;
- if (__res_maybe_init (&_res, 0) == -1) {
- __set_h_errno (NETDB_INTERNAL);
- return (NULL);
- }
-
switch (af) {
case AF_INET:
size = INADDRSZ;
@@ -485,8 +492,10 @@ res_gethostbyname2 (const char *name, int af)
* this is also done in res_query() since we are not the only
* function that looks up host names.
*/
- if (!strchr(name, '.') && (cp = __hostalias(name)))
- name = cp;
+ char abuf[MAXDNAME];
+ if (strchr (name, '.') != NULL
+ && (cp = __res_context_hostalias (ctx, name, abuf, sizeof (abuf))))
+ name = cp;
/*
* disallow names consisting only of digits/dots, unless
@@ -558,8 +567,9 @@ res_gethostbyname2 (const char *name, int af)
buf.buf = origbuf = (querybuf *) alloca (1024);
- if ((n = __libc_res_nsearch(&_res, name, C_IN, type, buf.buf->buf, 1024,
- &buf.ptr, NULL, NULL, NULL, NULL)) < 0) {
+ if ((n = __res_context_search
+ (ctx, name, C_IN, type, buf.buf->buf, 1024,
+ &buf.ptr, NULL, NULL, NULL, NULL)) < 0) {
if (buf.buf != origbuf)
free (buf.buf);
Dprintf("res_nsearch failed (%d)\n", n);
@@ -572,11 +582,26 @@ res_gethostbyname2 (const char *name, int af)
free (buf.buf);
return ret;
}
+
+struct hostent *
+res_gethostbyname2 (const char *name, int af)
+{
+ struct resolv_context *ctx = __resolv_context_get ();
+ if (ctx == NULL)
+ {
+ __set_h_errno (NETDB_INTERNAL);
+ return NULL;
+ }
+ struct hostent *hp = res_gethostbyname2_context (ctx, name, AF_INET);
+ __resolv_context_put (ctx);
+ return hp;
+}
libresolv_hidden_def (res_gethostbyname2)
compat_symbol (libresolv, res_gethostbyname2, res_gethostbyname2, GLIBC_2_0);
-struct hostent *
-res_gethostbyaddr (const void *addr, socklen_t len, int af)
+static struct hostent *
+res_gethostbyaddr_context (struct resolv_context *ctx,
+ const void *addr, socklen_t len, int af)
{
const u_char *uaddr = (const u_char *)addr;
static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
@@ -592,10 +617,6 @@ res_gethostbyaddr (const void *addr, socklen_t len, int af)
struct hostent *hp;
char qbuf[MAXDNAME+1], *qp = NULL;
- if (__res_maybe_init (&_res, 0) == -1) {
- __set_h_errno (NETDB_INTERNAL);
- return (NULL);
- }
if (af == AF_INET6 && len == IN6ADDRSZ &&
(!memcmp(uaddr, mapped, sizeof mapped) ||
!memcmp(uaddr, tunnelled, sizeof tunnelled))) {
@@ -645,8 +666,8 @@ res_gethostbyaddr (const void *addr, socklen_t len, int af)
buf.buf = orig_buf = (querybuf *) alloca (1024);
- n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf, 1024,
- &buf.ptr, NULL, NULL, NULL, NULL);
+ n = __res_context_query (ctx, qbuf, C_IN, T_PTR, buf.buf->buf, 1024,
+ &buf.ptr, NULL, NULL, NULL, NULL);
if (n < 0) {
if (buf.buf != orig_buf)
free (buf.buf);
@@ -673,6 +694,20 @@ res_gethostbyaddr (const void *addr, socklen_t len, int af)
__set_h_errno (NETDB_SUCCESS);
return (hp);
}
+
+struct hostent *
+res_gethostbyaddr (const void *addr, socklen_t len, int af)
+{
+ struct resolv_context *ctx = __resolv_context_get ();
+ if (ctx == NULL)
+ {
+ __set_h_errno (NETDB_INTERNAL);
+ return NULL;
+ }
+ struct hostent *hp = res_gethostbyaddr_context (ctx, addr, len, af);
+ __resolv_context_put (ctx);
+ return hp;
+}
compat_symbol (libresolv, res_gethostbyaddr, res_gethostbyaddr, GLIBC_2_0);
void
diff --git a/resolv/nss_dns/dns-canon.c b/resolv/nss_dns/dns-canon.c
index 4276eb6542..7a5c39dc20 100644
--- a/resolv/nss_dns/dns-canon.c
+++ b/resolv/nss_dns/dns-canon.c
@@ -23,7 +23,8 @@
#include <stdint.h>
#include <arpa/nameser.h>
#include <nsswitch.h>
-
+#include <resolv/resolv_context.h>
+#include <resolv/resolv-internal.h>
#if PACKETSZ > 65536
# define MAXPACKET PACKETSZ
@@ -58,11 +59,19 @@ _nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen,
} ansp = { .ptr = buf };
enum nss_status status = NSS_STATUS_UNAVAIL;
+ struct resolv_context *ctx = __resolv_context_get ();
+ if (ctx == NULL)
+ {
+ *errnop = errno;
+ *h_errnop = NETDB_INTERNAL;
+ return NSS_STATUS_UNAVAIL;
+ }
+
for (int i = 0; i < nqtypes; ++i)
{
- int r = __libc_res_nquery (&_res, name, ns_c_in, qtypes[i],
- buf, sizeof (buf), &ansp.ptr, NULL, NULL,
- NULL, NULL);
+ int r = __res_context_query (ctx, name, ns_c_in, qtypes[i],
+ buf, sizeof (buf), &ansp.ptr, NULL, NULL,
+ NULL, NULL);
if (r > 0)
{
/* We need to decode the response. Just one question record.
@@ -168,6 +177,6 @@ _nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen,
if (ansp.ptr != buf)
free (ansp.ptr);
-
+ __resolv_context_put (ctx);
return status;
}
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index 206924de86..9d7ceb1691 100644
--- a/resolv/nss_dns/dns-host.c
+++ b/resolv/nss_dns/dns-host.c
@@ -84,6 +84,7 @@
/* Get implementeation for some internal functions. */
#include <resolv/resolv-internal.h>
+#include <resolv/resolv_context.h>
#include <resolv/mapv4v6addr.h>
#include <resolv/mapv4v6hostent.h>
@@ -121,13 +122,13 @@ static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1,
int *errnop, int *h_errnop,
int32_t *ttlp);
-extern enum nss_status _nss_dns_gethostbyname3_r (const char *name, int af,
- struct hostent *result,
- char *buffer, size_t buflen,
- int *errnop, int *h_errnop,
- int32_t *ttlp,
- char **canonp);
-hidden_proto (_nss_dns_gethostbyname3_r)
+static enum nss_status gethostbyname3_context (struct resolv_context *ctx,
+ const char *name, int af,
+ struct hostent *result,
+ char *buffer, size_t buflen,
+ int *errnop, int *h_errnop,
+ int32_t *ttlp,
+ char **canonp);
/* Return the expected RDATA length for an address record type (A or
AAAA). */
@@ -145,11 +146,31 @@ rrtype_to_rdata_length (int type)
}
}
+
enum nss_status
_nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
char *buffer, size_t buflen, int *errnop,
int *h_errnop, int32_t *ttlp, char **canonp)
{
+ struct resolv_context *ctx = __resolv_context_get ();
+ if (ctx == NULL)
+ {
+ *errnop = errno;
+ *h_errnop = NETDB_INTERNAL;
+ return NSS_STATUS_UNAVAIL;
+ }
+ enum nss_status status = gethostbyname3_context
+ (ctx, name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp);
+ __resolv_context_put (ctx);
+ return status;
+}
+
+static enum nss_status
+gethostbyname3_context (struct resolv_context *ctx,
+ const char *name, int af, struct hostent *result,
+ char *buffer, size_t buflen, int *errnop,
+ int *h_errnop, int32_t *ttlp, char **canonp)
+{
union
{
querybuf *buf;
@@ -163,13 +184,6 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
int olderr = errno;
enum nss_status status;
- if (__res_maybe_init (&_res, 0) == -1)
- {
- *errnop = errno;
- *h_errnop = NETDB_INTERNAL;
- return NSS_STATUS_UNAVAIL;
- }
-
switch (af) {
case AF_INET:
size = INADDRSZ;
@@ -194,13 +208,13 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
* function that looks up host names.
*/
if (strchr (name, '.') == NULL
- && (cp = res_hostalias (&_res, name, tmp, sizeof (tmp))) != NULL)
+ && (cp = __res_context_hostalias (ctx, name, tmp, sizeof (tmp))) != NULL)
name = cp;
host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024);
- n = __libc_res_nsearch (&_res, name, C_IN, type, host_buffer.buf->buf,
- 1024, &host_buffer.ptr, NULL, NULL, NULL, NULL);
+ n = __res_context_search (ctx, name, C_IN, type, host_buffer.buf->buf,
+ 1024, &host_buffer.ptr, NULL, NULL, NULL, NULL);
if (n < 0)
{
switch (errno)
@@ -232,10 +246,10 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
by having the RES_USE_INET6 bit in _res.options set, we try
another lookup. */
if (af == AF_INET6 && res_use_inet6 ())
- n = __libc_res_nsearch (&_res, name, C_IN, T_A, host_buffer.buf->buf,
- host_buffer.buf != orig_host_buffer
- ? MAXPACKET : 1024, &host_buffer.ptr,
- NULL, NULL, NULL, NULL);
+ n = __res_context_search (ctx, name, C_IN, T_A, host_buffer.buf->buf,
+ host_buffer.buf != orig_host_buffer
+ ? MAXPACKET : 1024, &host_buffer.ptr,
+ NULL, NULL, NULL, NULL);
if (n < 0)
{
@@ -256,8 +270,6 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
free (host_buffer.buf);
return status;
}
-hidden_def (_nss_dns_gethostbyname3_r)
-
enum nss_status
_nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
@@ -274,15 +286,21 @@ _nss_dns_gethostbyname_r (const char *name, struct hostent *result,
char *buffer, size_t buflen, int *errnop,
int *h_errnop)
{
+ struct resolv_context *ctx = __resolv_context_get ();
+ if (ctx == NULL)
+ {
+ *errnop = errno;
+ *h_errnop = NETDB_INTERNAL;
+ return NSS_STATUS_UNAVAIL;
+ }
enum nss_status status = NSS_STATUS_NOTFOUND;
-
if (res_use_inet6 ())
- status = _nss_dns_gethostbyname3_r (name, AF_INET6, result, buffer,
- buflen, errnop, h_errnop, NULL, NULL);
+ status = gethostbyname3_context (ctx, name, AF_INET6, result, buffer,
+ buflen, errnop, h_errnop, NULL, NULL);
if (status == NSS_STATUS_NOTFOUND)
- status = _nss_dns_gethostbyname3_r (name, AF_INET, result, buffer,
- buflen, errnop, h_errnop, NULL, NULL);
-
+ status = gethostbyname3_context (ctx, name, AF_INET, result, buffer,
+ buflen, errnop, h_errnop, NULL, NULL);
+ __resolv_context_put (ctx);
return status;
}
@@ -292,7 +310,8 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
char *buffer, size_t buflen, int *errnop,
int *herrnop, int32_t *ttlp)
{
- if (__res_maybe_init (&_res, 0) == -1)
+ struct resolv_context *ctx = __resolv_context_get ();
+ if (ctx == NULL)
{
*errnop = errno;
*herrnop = NETDB_INTERNAL;
@@ -307,7 +326,7 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
if (strchr (name, '.') == NULL)
{
char *tmp = alloca (NS_MAXDNAME);
- const char *cp = res_hostalias (&_res, name, tmp, NS_MAXDNAME);
+ const char *cp = __res_context_hostalias (ctx, name, tmp, NS_MAXDNAME);
if (cp != NULL)
name = cp;
}
@@ -326,9 +345,9 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
int olderr = errno;
enum nss_status status;
- int n = __libc_res_nsearch (&_res, name, C_IN, T_QUERY_A_AND_AAAA,
- host_buffer.buf->buf, 2048, &host_buffer.ptr,
- &ans2p, &nans2p, &resplen2, &ans2p_malloced);
+ int 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,
@@ -371,6 +390,7 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
if (host_buffer.buf != orig_host_buffer)
free (host_buffer.buf);
+ __resolv_context_put (ctx);
return status;
}
@@ -423,7 +443,8 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
host_data = (struct host_data *) buffer;
- if (__res_maybe_init (&_res, 0) == -1)
+ struct resolv_context *ctx = __resolv_context_get ();
+ if (ctx == NULL)
{
*errnop = errno;
*h_errnop = NETDB_INTERNAL;
@@ -453,12 +474,14 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
default:
*errnop = EAFNOSUPPORT;
*h_errnop = NETDB_INTERNAL;
+ __resolv_context_put (ctx);
return NSS_STATUS_UNAVAIL;
}
if (size > len)
{
*errnop = EAFNOSUPPORT;
*h_errnop = NETDB_INTERNAL;
+ __resolv_context_put (ctx);
return NSS_STATUS_UNAVAIL;
}
@@ -487,14 +510,15 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
break;
}
- n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf,
- 1024, &host_buffer.ptr, NULL, NULL, NULL, NULL);
+ n = __res_context_query (ctx, qbuf, C_IN, T_PTR, host_buffer.buf->buf,
+ 1024, &host_buffer.ptr, NULL, NULL, NULL, NULL);
if (n < 0)
{
*h_errnop = h_errno;
__set_errno (olderr);
if (host_buffer.buf != orig_host_buffer)
free (host_buffer.buf);
+ __resolv_context_put (ctx);
return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
}
@@ -503,7 +527,10 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
if (host_buffer.buf != orig_host_buffer)
free (host_buffer.buf);
if (status != NSS_STATUS_SUCCESS)
- return status;
+ {
+ __resolv_context_put (ctx);
+ return status;
+ }
result->h_addrtype = af;
result->h_length = len;
@@ -511,6 +538,7 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
host_data->h_addr_ptrs[0] = (char *) host_data->host_addr;
host_data->h_addr_ptrs[1] = NULL;
*h_errnop = NETDB_SUCCESS;
+ __resolv_context_put (ctx);
return NSS_STATUS_SUCCESS;
}
hidden_def (_nss_dns_gethostbyaddr2_r)
diff --git a/resolv/nss_dns/dns-network.c b/resolv/nss_dns/dns-network.c
index dc1599b471..f190eb2225 100644
--- a/resolv/nss_dns/dns-network.c
+++ b/resolv/nss_dns/dns-network.c
@@ -67,6 +67,8 @@
#include "nsswitch.h"
#include <arpa/inet.h>
#include <arpa/nameser.h>
+#include <resolv/resolv-internal.h>
+#include <resolv/resolv_context.h>
/* Maximum number of aliases we allow. */
#define MAX_NR_ALIASES 48
@@ -115,7 +117,8 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
int anslen;
enum nss_status status;
- if (__res_maybe_init (&_res, 0) == -1)
+ struct resolv_context *ctx = __resolv_context_get ();
+ if (ctx == NULL)
{
*errnop = errno;
*herrnop = NETDB_INTERNAL;
@@ -124,14 +127,16 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
- anslen = __libc_res_nsearch (&_res, name, C_IN, T_PTR, net_buffer.buf->buf,
- 1024, &net_buffer.ptr, NULL, NULL, NULL, NULL);
+ anslen = __res_context_search
+ (ctx, name, C_IN, T_PTR, net_buffer.buf->buf,
+ 1024, &net_buffer.ptr, NULL, NULL, NULL, NULL);
if (anslen < 0)
{
/* Nothing found. */
*errnop = errno;
if (net_buffer.buf != orig_net_buffer)
free (net_buffer.buf);
+ __resolv_context_put (ctx);
return (errno == ECONNREFUSED
|| errno == EPFNOSUPPORT
|| errno == EAFNOSUPPORT)
@@ -142,6 +147,7 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
errnop, herrnop, BYNAME);
if (net_buffer.buf != orig_net_buffer)
free (net_buffer.buf);
+ __resolv_context_put (ctx);
return status;
}
@@ -169,7 +175,8 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
if (type != AF_INET)
return NSS_STATUS_UNAVAIL;
- if (__res_maybe_init (&_res, 0) == -1)
+ struct resolv_context *ctx = __resolv_context_get ();
+ if (ctx == NULL)
{
*errnop = errno;
*herrnop = NETDB_INTERNAL;
@@ -204,8 +211,8 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
- anslen = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
- 1024, &net_buffer.ptr, NULL, NULL, NULL, NULL);
+ anslen = __res_context_query (ctx, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
+ 1024, &net_buffer.ptr, NULL, NULL, NULL, NULL);
if (anslen < 0)
{
/* Nothing found. */
@@ -213,6 +220,7 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
__set_errno (olderr);
if (net_buffer.buf != orig_net_buffer)
free (net_buffer.buf);
+ __resolv_context_put (ctx);
return (err == ECONNREFUSED
|| err == EPFNOSUPPORT
|| err == EAFNOSUPPORT)
@@ -233,6 +241,7 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
result->n_net = u_net;
}
+ __resolv_context_put (ctx);
return status;
}
diff --git a/resolv/res-close.c b/resolv/res-close.c
index 73f18d1525..97da73c99c 100644
--- a/resolv/res-close.c
+++ b/resolv/res-close.c
@@ -83,6 +83,7 @@
*/
#include <resolv-internal.h>
+#include <resolv_context.h>
#include <not-cancel.h>
/* Close all open sockets. If FREE_ADDR is true, deallocate any
@@ -124,6 +125,8 @@ libc_hidden_def (__res_nclose)
static void __attribute__ ((section ("__libc_thread_freeres_fn")))
res_thread_freeres (void)
{
+ __resolv_context_freeres ();
+
if (_res.nscount == 0)
/* Never called res_ninit. */
return;
diff --git a/resolv/res_libc.c b/resolv/res_libc.c
index 3d7b4f72d0..5066983ccf 100644
--- a/resolv/res_libc.c
+++ b/resolv/res_libc.c
@@ -98,37 +98,6 @@ res_init (void)
return __res_vinit (&_res, 1);
}
-
-/* Initialize *RESP if RES_INIT is not yet set in RESP->options, or if
- res_init in some other thread requested re-initializing. */
-int
-__res_maybe_init (res_state resp, int preinit)
-{
- if (resp->options & RES_INIT)
- {
- if (__res_initstamp != resp->_u._ext.initstamp)
- {
- if (resp->nscount > 0)
- __res_iclose (resp, true);
- return __res_vinit (resp, 1);
- }
- return 0;
- }
- else if (preinit)
- {
- if (!resp->retrans)
- resp->retrans = RES_TIMEOUT;
- if (!resp->retry)
- resp->retry = RES_DFLRETRY;
- resp->options = RES_DEFAULT;
- if (!resp->id)
- resp->id = res_randomid ();
- return __res_vinit (resp, 1);
- }
- else
- return __res_ninit (resp);
-}
-libc_hidden_def (__res_maybe_init)
/* This needs to be after the use of _res in res_init, above. */
#undef _res
diff --git a/resolv/res_mkquery.c b/resolv/res_mkquery.c
index 9afb410980..59fc5ab28c 100644
--- a/resolv/res_mkquery.c
+++ b/resolv/res_mkquery.c
@@ -88,6 +88,7 @@
#include <arpa/nameser.h>
#include <netdb.h>
#include <resolv/resolv-internal.h>
+#include <resolv/resolv_context.h>
#include <string.h>
#include <sys/time.h>
#include <shlib-compat.h>
@@ -98,22 +99,10 @@
# define RANDOM_BITS(Var) { uint64_t v64; HP_TIMING_NOW (v64); Var = v64; }
#endif
-/* Form all types of queries. Returns the size of the result or -1 on
- error.
-
- STATP points to an initialized resolver state. OP is the opcode of
- the query. DNAME is the domain. CLASS and TYPE are the DNS query
- class and type. DATA can be NULL; otherwise, it is a pointer to a
- domain name which is included in the generated packet (if op ==
- NS_NOTIFY_OP). BUF must point to the out buffer of BUFLEN bytes.
-
- DATALEN and NEWRR_IN are currently ignored. */
int
-res_nmkquery (res_state statp, int op, const char *dname,
- int class, int type,
- const unsigned char *data, int datalen,
- const unsigned char *newrr_in,
- unsigned char *buf, int buflen)
+__res_context_mkquery (struct resolv_context *ctx, int op, const char *dname,
+ int class, int type, const unsigned char *data,
+ unsigned char *buf, int buflen)
{
HEADER *hp;
unsigned char *cp;
@@ -132,22 +121,17 @@ res_nmkquery (res_state statp, int op, const char *dname,
by one after the initial randomization which still predictable if
the application does multiple requests. */
int randombits;
- do
- {
#ifdef RANDOM_BITS
- RANDOM_BITS (randombits);
+ RANDOM_BITS (randombits);
#else
- struct timeval tv;
- __gettimeofday (&tv, NULL);
- randombits = (tv.tv_sec << 8) ^ tv.tv_usec;
+ struct timeval tv;
+ __gettimeofday (&tv, NULL);
+ randombits = (tv.tv_sec << 8) ^ tv.tv_usec;
#endif
- }
- while ((randombits & 0xffff) == 0);
- statp->id = (statp->id + randombits) & 0xffff;
- hp->id = statp->id;
+ hp->id = randombits;
hp->opcode = op;
- hp->rd = (statp->options & RES_RECURSE) != 0;
+ hp->rd = (ctx->resp->options & RES_RECURSE) != 0;
hp->rcode = NOERROR;
cp = buf + HFIXEDSZ;
buflen -= HFIXEDSZ;
@@ -201,7 +185,45 @@ res_nmkquery (res_state statp, int op, const char *dname,
}
return cp - buf;
}
-libresolv_hidden_def (res_nmkquery)
+
+/* Common part of res_nmkquery and res_mkquery. */
+static int
+context_mkquery_common (struct resolv_context *ctx,
+ int op, const char *dname, int class, int type,
+ const unsigned char *data,
+ unsigned char *buf, int buflen)
+{
+ if (ctx == NULL)
+ return -1;
+ int result = __res_context_mkquery
+ (ctx, op, dname, class, type, data, buf, buflen);
+ if (result >= 2)
+ memcpy (&ctx->resp->id, buf, 2);
+ __resolv_context_put (ctx);
+ return result;
+}
+
+/* Form all types of queries. Returns the size of the result or -1 on
+ error.
+
+ STATP points to an initialized resolver state. OP is the opcode of
+ the query. DNAME is the domain. CLASS and TYPE are the DNS query
+ class and type. DATA can be NULL; otherwise, it is a pointer to a
+ domain name which is included in the generated packet (if op ==
+ NS_NOTIFY_OP). BUF must point to the out buffer of BUFLEN bytes.
+
+ DATALEN and NEWRR_IN are currently ignored. */
+int
+res_nmkquery (res_state statp, int op, const char *dname,
+ int class, int type,
+ const unsigned char *data, int datalen,
+ const unsigned char *newrr_in,
+ unsigned char *buf, int buflen)
+{
+ return context_mkquery_common
+ (__resolv_context_get_override (statp),
+ op, dname, class, type, data, buf, buflen);
+}
int
res_mkquery (int op, const char *dname, int class, int type,
@@ -209,13 +231,9 @@ res_mkquery (int op, const char *dname, int class, int type,
const unsigned char *newrr_in,
unsigned char *buf, int buflen)
{
- if (__res_maybe_init (&_res, 1) == -1)
- {
- RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
- return -1;
- }
- return res_nmkquery (&_res, op, dname, class, type,
- data, datalen, newrr_in, buf, buflen);
+ return context_mkquery_common
+ (__resolv_context_get_preinit (),
+ op, dname, class, type, data, buf, buflen);
}
/* Create an OPT resource record. Return the length of the final
@@ -227,8 +245,8 @@ res_mkquery (int op, const char *dname, int class, int type,
pointers to must be BUFLEN bytes long. ANSLEN is the advertised
EDNS buffer size (to be included in the OPT resource record). */
int
-__res_nopt (res_state statp, int n0, unsigned char *buf, int buflen,
- int anslen)
+__res_nopt (struct resolv_context *ctx,
+ int n0, unsigned char *buf, int buflen, int anslen)
{
uint16_t flags = 0;
HEADER *hp = (HEADER *) buf;
@@ -269,7 +287,7 @@ __res_nopt (res_state statp, int n0, unsigned char *buf, int buflen,
*cp++ = NOERROR; /* Extended RCODE. */
*cp++ = 0; /* EDNS version. */
- if (statp->options & RES_USE_DNSSEC)
+ if (ctx->resp->options & RES_USE_DNSSEC)
flags |= NS_OPT_DNSSEC_OK;
NS_PUT16 (flags, cp);
diff --git a/resolv/res_query.c b/resolv/res_query.c
index 760bf324e8..33249e36f5 100644
--- a/resolv/res_query.c
+++ b/resolv/res_query.c
@@ -75,6 +75,7 @@
#include <netdb.h>
#include <resolv.h>
#include <resolv/resolv-internal.h>
+#include <resolv/resolv_context.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -89,33 +90,28 @@
#define QUERYSIZE (HFIXEDSZ + QFIXEDSZ + MAXCDNAME + 1)
static int
-__libc_res_nquerydomain(res_state statp, const char *name, const char *domain,
- int class, int type, u_char *answer, int anslen,
- u_char **answerp, u_char **answerp2, int *nanswerp2,
- int *resplen2, int *answerp2_malloced);
-
-/*
- * Formulate a normal query, send, and await answer.
- * Returned answer is placed in supplied buffer "answer".
- * Perform preliminary check of answer, returning success only
- * if no error is indicated and the answer count is nonzero.
- * Return the size of the response on success, -1 on error.
- * Error number is left in H_ERRNO.
- *
- * Caller must parse answer and determine whether it answers the question.
- */
+__res_context_querydomain (struct resolv_context *,
+ const char *name, const char *domain,
+ int class, int type, unsigned char *answer, int anslen,
+ unsigned char **answerp, unsigned char **answerp2, int *nanswerp2,
+ int *resplen2, int *answerp2_malloced);
+
+/* Formulate a normal query, send, and await answer. Returned answer
+ is placed in supplied buffer ANSWER. Perform preliminary check of
+ answer, returning success only if no error is indicated and the
+ answer count is nonzero. Return the size of the response on
+ success, -1 on error. Error number is left in h_errno.
+
+ Caller must parse answer and determine whether it answers the
+ question. */
int
-__libc_res_nquery(res_state statp,
- const char *name, /* domain name */
- int class, int type, /* class and type of query */
- u_char *answer, /* buffer to put answer */
- int anslen, /* size of answer buffer */
- u_char **answerp, /* if buffer needs to be enlarged */
- u_char **answerp2,
- int *nanswerp2,
- int *resplen2,
- int *answerp2_malloced)
+__res_context_query (struct resolv_context *ctx, const char *name,
+ int class, int type,
+ unsigned char *answer, int anslen,
+ unsigned char **answerp, unsigned char **answerp2,
+ int *nanswerp2, int *resplen2, int *answerp2_malloced)
{
+ struct __res_state *statp = ctx->resp;
HEADER *hp = (HEADER *) answer;
HEADER *hp2;
int n, use_malloc = 0;
@@ -132,15 +128,15 @@ __libc_res_nquery(res_state statp,
if (type == T_QUERY_A_AND_AAAA)
{
- n = res_nmkquery(statp, QUERY, name, class, T_A, NULL, 0, NULL,
- query1, bufsize);
+ n = __res_context_mkquery (ctx, QUERY, name, class, T_A, NULL,
+ query1, bufsize);
if (n > 0)
{
if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
{
/* Use RESOLV_EDNS_BUFFER_SIZE because the receive
buffer can be reallocated. */
- n = __res_nopt (statp, n, query1, bufsize,
+ n = __res_nopt (ctx, n, query1, bufsize,
RESOLV_EDNS_BUFFER_SIZE);
if (n < 0)
goto unspec_nomem;
@@ -157,13 +153,13 @@ __libc_res_nquery(res_state statp,
}
int nused = n + npad;
query2 = buf + nused;
- n = res_nmkquery(statp, QUERY, name, class, T_AAAA, NULL, 0,
- NULL, query2, bufsize - nused);
+ n = __res_context_mkquery (ctx, QUERY, name, class, T_AAAA,
+ NULL, query2, bufsize - nused);
if (n > 0
&& (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
/* Use RESOLV_EDNS_BUFFER_SIZE because the receive
buffer can be reallocated. */
- n = __res_nopt (statp, n, query2, bufsize,
+ n = __res_nopt (ctx, n, query2, bufsize,
RESOLV_EDNS_BUFFER_SIZE);
nquery2 = n;
}
@@ -172,8 +168,8 @@ __libc_res_nquery(res_state statp,
}
else
{
- n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL,
- query1, bufsize);
+ n = __res_context_mkquery (ctx, QUERY, name, class, type, NULL,
+ query1, bufsize);
if (n > 0
&& (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
@@ -185,7 +181,7 @@ __libc_res_nquery(res_state statp,
advertise = anslen;
else
advertise = RESOLV_EDNS_BUFFER_SIZE;
- n = __res_nopt (statp, n, query1, bufsize, advertise);
+ n = __res_nopt (ctx, n, query1, bufsize, advertise);
}
nquery1 = n;
@@ -209,9 +205,9 @@ __libc_res_nquery(res_state statp,
return (n);
}
assert (answerp == NULL || (void *) *answerp == (void *) answer);
- n = __libc_res_nsend(statp, query1, nquery1, query2, nquery2, answer,
- anslen, answerp, answerp2, nanswerp2, resplen2,
- answerp2_malloced);
+ 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) {
@@ -220,7 +216,7 @@ __libc_res_nquery(res_state statp,
}
if (answerp != NULL)
- /* __libc_res_nsend might have reallocated the buffer. */
+ /* __res_context_send might have reallocated the buffer. */
hp = (HEADER *) *answerp;
/* We simplify the following tests by assigning HP to HP2 or
@@ -280,7 +276,24 @@ __libc_res_nquery(res_state statp,
success:
return (n);
}
-libresolv_hidden_def (__libc_res_nquery)
+libresolv_hidden_def (__res_context_query)
+
+/* Common part of res_nquery and res_query. */
+static int
+context_query_common (struct resolv_context *ctx,
+ const char *name, int class, int type,
+ unsigned char *answer, int anslen)
+{
+ if (ctx == NULL)
+ {
+ RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
+ return -1;
+ }
+ int result = __res_context_query (ctx, name, class, type, answer, anslen,
+ NULL, NULL, NULL, NULL, NULL);
+ __resolv_context_put (ctx);
+ return result;
+}
int
res_nquery(res_state statp,
@@ -289,41 +302,30 @@ res_nquery(res_state statp,
u_char *answer, /* buffer to put answer */
int anslen) /* size of answer buffer */
{
- return __libc_res_nquery(statp, name, class, type, answer, anslen,
- NULL, NULL, NULL, NULL, NULL);
+ return context_query_common
+ (__resolv_context_get_override (statp), name, class, type, answer, anslen);
}
-libresolv_hidden_def (res_nquery)
int
res_query (const char *name, int class, int type,
unsigned char *answer, int anslen)
{
- if (__res_maybe_init (&_res, 1) == -1)
- {
- RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
- return -1;
- }
- return res_nquery (&_res, name, class, type, answer, anslen);
+ return context_query_common
+ (__resolv_context_get (), name, class, type, answer, anslen);
}
-/*
- * Formulate a normal query, send, and retrieve answer in supplied buffer.
- * Return the size of the response on success, -1 on error.
- * If enabled, implement search rules until answer or unrecoverable failure
- * is detected. Error code, if any, is left in H_ERRNO.
- */
+/* Formulate a normal query, send, and retrieve answer in supplied
+ buffer. Return the size of the response on success, -1 on error.
+ If enabled, implement search rules until answer or unrecoverable
+ failure is detected. Error code, if any, is left in h_errno. */
int
-__libc_res_nsearch(res_state statp,
- const char *name, /* domain name */
- int class, int type, /* class and type of query */
- u_char *answer, /* buffer to put answer */
- int anslen, /* size of answer */
- u_char **answerp,
- u_char **answerp2,
- int *nanswerp2,
- int *resplen2,
- int *answerp2_malloced)
+__res_context_search (struct resolv_context *ctx,
+ const char *name, int class, int type,
+ unsigned char *answer, int anslen,
+ unsigned char **answerp, unsigned char **answerp2,
+ int *nanswerp2, int *resplen2, int *answerp2_malloced)
{
+ struct __res_state *statp = ctx->resp;
const char *cp, * const *domain;
HEADER *hp = (HEADER *) answer;
char tmp[NS_MAXDNAME];
@@ -344,10 +346,11 @@ __libc_res_nsearch(res_state statp,
trailing_dot++;
/* If there aren't any dots, it could be a user-level alias. */
- if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
- return (__libc_res_nquery(statp, cp, class, type, answer,
- anslen, answerp, answerp2,
- nanswerp2, resplen2, answerp2_malloced));
+ if (!dots && (cp = __res_context_hostalias
+ (ctx, name, tmp, sizeof tmp))!= NULL)
+ return __res_context_query (ctx, cp, class, type, answer,
+ anslen, answerp, answerp2,
+ nanswerp2, resplen2, answerp2_malloced);
/*
* If there are enough dots in the name, let's just give it a
@@ -356,10 +359,10 @@ __libc_res_nsearch(res_state statp,
*/
saved_herrno = -1;
if (dots >= statp->ndots || trailing_dot) {
- ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
- answer, anslen, answerp,
- answerp2, nanswerp2, resplen2,
- answerp2_malloced);
+ ret = __res_context_querydomain (ctx, name, NULL, class, type,
+ answer, anslen, answerp,
+ answerp2, nanswerp2, resplen2,
+ answerp2_malloced);
if (ret > 0 || trailing_dot
/* If the second response is valid then we use that. */
|| (ret == 0 && resplen2 != NULL && *resplen2 > 0))
@@ -395,7 +398,7 @@ __libc_res_nsearch(res_state statp,
const char *dname = domain[0];
searched = 1;
- /* __libc_res_nquerydoman concatenates name
+ /* __res_context_querydoman concatenates name
with dname with a "." in between. If we
pass it in dname the "." we got from the
configured default search path, we'll end
@@ -409,11 +412,10 @@ __libc_res_nsearch(res_state statp,
if (dname[0] == '\0')
root_on_list++;
- ret = __libc_res_nquerydomain(statp, name, dname,
- class, type,
- answer, anslen, answerp,
- answerp2, nanswerp2,
- resplen2, answerp2_malloced);
+ ret = __res_context_querydomain
+ (ctx, name, dname, class, type,
+ answer, anslen, answerp, answerp2, nanswerp2,
+ resplen2, answerp2_malloced);
if (ret > 0 || (ret == 0 && resplen2 != NULL
&& *resplen2 > 0))
return (ret);
@@ -481,10 +483,10 @@ __libc_res_nsearch(res_state statp,
*/
if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0)
&& !(tried_as_is || root_on_list)) {
- ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
- answer, anslen, answerp,
- answerp2, nanswerp2, resplen2,
- answerp2_malloced);
+ ret = __res_context_querydomain
+ (ctx, name, NULL, class, type,
+ answer, anslen, answerp, answerp2, nanswerp2,
+ resplen2, answerp2_malloced);
if (ret > 0 || (ret == 0 && resplen2 != NULL
&& *resplen2 > 0))
return (ret);
@@ -512,7 +514,24 @@ __libc_res_nsearch(res_state statp,
RES_SET_H_ERRNO(statp, TRY_AGAIN);
return (-1);
}
-libresolv_hidden_def (__libc_res_nsearch)
+libresolv_hidden_def (__res_context_search)
+
+/* Common part of res_nsearch and res_search. */
+static int
+context_search_common (struct resolv_context *ctx,
+ const char *name, int class, int type,
+ unsigned char *answer, int anslen)
+{
+ if (ctx == NULL)
+ {
+ RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
+ return -1;
+ }
+ int result = __res_context_search (ctx, name, class, type, answer, anslen,
+ NULL, NULL, NULL, NULL, NULL);
+ __resolv_context_put (ctx);
+ return result;
+}
int
res_nsearch(res_state statp,
@@ -521,40 +540,30 @@ res_nsearch(res_state statp,
u_char *answer, /* buffer to put answer */
int anslen) /* size of answer */
{
- return __libc_res_nsearch(statp, name, class, type, answer,
- anslen, NULL, NULL, NULL, NULL, NULL);
+ return context_search_common
+ (__resolv_context_get_override (statp), name, class, type, answer, anslen);
}
-libresolv_hidden_def (res_nsearch)
int
res_search (const char *name, int class, int type,
unsigned char *answer, int anslen)
{
- if (__res_maybe_init (&_res, 1) == -1)
- {
- RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
- return -1;
- }
-
- return res_nsearch (&_res, name, class, type, answer, anslen);
+ return context_search_common
+ (__resolv_context_get (), name, class, type, answer, anslen);
}
-/*
- * Perform a call on res_query on the concatenation of name and domain.
- */
+/* Perform a call on res_query on the concatenation of name and
+ domain. */
static int
-__libc_res_nquerydomain(res_state statp,
- const char *name,
- const char *domain,
- int class, int type, /* class and type of query */
- u_char *answer, /* buffer to put answer */
- int anslen, /* size of answer */
- u_char **answerp,
- u_char **answerp2,
- int *nanswerp2,
- int *resplen2,
- int *answerp2_malloced)
+__res_context_querydomain (struct resolv_context *ctx,
+ const char *name, const char *domain,
+ int class, int type,
+ unsigned char *answer, int anslen,
+ unsigned char **answerp, unsigned char **answerp2,
+ int *nanswerp2, int *resplen2,
+ int *answerp2_malloced)
{
+ struct __res_state *statp = ctx->resp;
char nbuf[MAXDNAME];
const char *longname = nbuf;
size_t n, d;
@@ -580,9 +589,28 @@ __libc_res_nquerydomain(res_state statp,
}
sprintf(nbuf, "%s.%s", name, domain);
}
- return (__libc_res_nquery(statp, longname, class, type, answer,
- anslen, answerp, answerp2, nanswerp2,
- resplen2, answerp2_malloced));
+ return __res_context_query (ctx, longname, class, type, answer,
+ anslen, answerp, answerp2, nanswerp2,
+ resplen2, answerp2_malloced);
+}
+
+/* Common part of res_nquerydomain and res_querydomain. */
+static int
+context_querydomain_common (struct resolv_context *ctx,
+ const char *name, const char *domain,
+ int class, int type,
+ unsigned char *answer, int anslen)
+{
+ if (ctx == NULL)
+ {
+ RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
+ return -1;
+ }
+ int result = __res_context_querydomain (ctx, name, domain, class, type,
+ answer, anslen,
+ NULL, NULL, NULL, NULL, NULL);
+ __resolv_context_put (ctx);
+ return result;
}
int
@@ -593,32 +621,28 @@ res_nquerydomain(res_state statp,
u_char *answer, /* buffer to put answer */
int anslen) /* size of answer */
{
- return __libc_res_nquerydomain(statp, name, domain, class, type,
- answer, anslen, NULL, NULL, NULL, NULL,
- NULL);
+ return context_querydomain_common
+ (__resolv_context_get_override (statp),
+ name, domain, class, type, answer, anslen);
}
-libresolv_hidden_def (res_nquerydomain)
int
res_querydomain (const char *name, const char *domain, int class, int type,
unsigned char *answer, int anslen)
{
- if (__res_maybe_init (&_res, 1) == -1)
- {
- RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
- return -1;
- }
-
- return res_nquerydomain (&_res, name, domain, class, type, answer, anslen);
+ return context_querydomain_common
+ (__resolv_context_get (), name, domain, class, type, answer, anslen);
}
const char *
-res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) {
+__res_context_hostalias (struct resolv_context *ctx,
+ const char *name, char *dst, size_t siz)
+{
char *file, *cp1, *cp2;
char buf[BUFSIZ];
FILE *fp;
- if (statp->options & RES_NOALIASES)
+ if (ctx->resp->options & RES_NOALIASES)
return (NULL);
file = getenv("HOSTALIASES");
if (file == NULL || (fp = fopen(file, "rce")) == NULL)
@@ -648,15 +672,37 @@ res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) {
fclose(fp);
return (NULL);
}
-libresolv_hidden_def (res_hostalias)
+libresolv_hidden_def (__res_context_hostalias)
+
+/* Common part of res_hostalias and hostalias. */
+static const char *
+context_hostalias_common (struct resolv_context *ctx,
+ const char *name, char *dst, size_t siz)
+{
+ if (ctx == NULL)
+ {
+ RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
+ return NULL;
+ }
+ const char *result = __res_context_hostalias (ctx, name, dst, siz);
+ __resolv_context_put (ctx);
+ return result;
+}
+
+const char *
+res_hostalias (res_state statp, const char *name, char *dst, size_t siz)
+{
+ return context_hostalias_common
+ (__resolv_context_get_override (statp), name, dst, siz);
+}
const char *
hostalias (const char *name)
{
static char abuf[MAXDNAME];
- return res_hostalias (&_res, name, abuf, sizeof abuf);
+ return context_hostalias_common
+ (__resolv_context_get (), name, abuf, sizeof (abuf));
}
-libresolv_hidden_def (hostalias)
#if SHLIB_COMPAT (libresolv, GLIBC_2_0, GLIBC_2_2)
# undef res_query
diff --git a/resolv/res_send.c b/resolv/res_send.c
index a7daae8a06..b396aae03c 100644
--- a/resolv/res_send.c
+++ b/resolv/res_send.c
@@ -102,6 +102,7 @@
#include <fcntl.h>
#include <netdb.h>
#include <resolv/resolv-internal.h>
+#include <resolv/resolv_context.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
@@ -400,11 +401,14 @@ res_queriesmatch(const u_char *buf1, const u_char *eom1,
libresolv_hidden_def (res_queriesmatch)
int
-__libc_res_nsend(res_state statp, const u_char *buf, int buflen,
- const u_char *buf2, int buflen2,
- u_char *ans, int anssiz, u_char **ansp, u_char **ansp2,
- int *nansp2, int *resplen2, int *ansp2_malloced)
+__res_context_send (struct resolv_context *ctx,
+ const unsigned char *buf, int buflen,
+ const unsigned char *buf2, int buflen2,
+ unsigned char *ans, int anssiz,
+ unsigned char **ansp, unsigned char **ansp2,
+ int *nansp2, int *resplen2, int *ansp2_malloced)
{
+ struct __res_state *statp = ctx->resp;
int gotsomewhere, terrno, try, v_circuit, resplen, n;
if (statp->nscount == 0) {
@@ -541,22 +545,36 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
return (-1);
}
+/* Common part of res_nsend and res_send. */
+static int
+context_send_common (struct resolv_context *ctx,
+ const unsigned char *buf, int buflen,
+ unsigned char *ans, int anssiz)
+{
+ if (ctx == NULL)
+ {
+ 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);
+ __resolv_context_put (ctx);
+ return result;
+}
+
int
-res_nsend(res_state statp,
- const u_char *buf, int buflen, u_char *ans, int anssiz)
+res_nsend (res_state statp, const unsigned char *buf, int buflen,
+ unsigned char *ans, int anssiz)
{
- return __libc_res_nsend(statp, buf, buflen, NULL, 0, ans, anssiz,
- NULL, NULL, NULL, NULL, NULL);
+ return context_send_common
+ (__resolv_context_get_override (statp), buf, buflen, ans, anssiz);
}
-libresolv_hidden_def (res_nsend)
int
res_send (const unsigned char *buf, int buflen, unsigned char *ans, int anssiz)
{
- if (__res_maybe_init (&_res, 1) == -1)
- /* errno should have been set by res_init in this case. */
- return -1;
- return res_nsend (&_res, buf, buflen, ans, anssiz);
+ return context_send_common
+ (__resolv_context_get (), buf, buflen, ans, anssiz);
}
/* Private */
diff --git a/resolv/res_use_inet6.h b/resolv/res_use_inet6.h
new file mode 100644
index 0000000000..8649833072
--- /dev/null
+++ b/resolv/res_use_inet6.h
@@ -0,0 +1,49 @@
+/* Support functions for handling RES_USE_INET6 in getaddrinfo/nscd.
+ Copyright (C) 2017 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
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _RES_USE_INET6_H
+#define _RES_USE_INET6_H
+
+#include <resolv/resolv_context.h>
+#include <resolv/resolv-internal.h>
+
+/* Ensure that RES_USE_INET6 is disabled in *CTX. Return true if
+ __resolv_context_enable_inet6 below should enable RES_USE_INET6
+ again. */
+static inline bool
+__resolv_context_disable_inet6 (struct resolv_context *ctx)
+{
+ if (ctx != NULL && ctx->resp->options & DEPRECATED_RES_USE_INET6)
+ {
+ ctx->resp->options &= ~DEPRECATED_RES_USE_INET6;
+ return true;
+ }
+ else
+ return false;
+}
+
+/* If ENABLE, re-enable RES_USE_INET6 in *CTX. To be paired with
+ __resolv_context_disable_inet6. */
+static inline void
+__resolv_context_enable_inet6 (struct resolv_context *ctx, bool enable)
+{
+ if (ctx != NULL && enable)
+ ctx->resp->options |= DEPRECATED_RES_USE_INET6;
+}
+
+#endif
diff --git a/resolv/resolv-internal.h b/resolv/resolv-internal.h
index 5a9faf8de9..9246497196 100644
--- a/resolv/resolv-internal.h
+++ b/resolv/resolv-internal.h
@@ -52,9 +52,41 @@ enum
RESOLV_EDNS_BUFFER_SIZE = 1200,
};
+struct resolv_context;
+
+/* Internal function for implementing res_nmkquery and res_mkquery.
+ Also used by __res_context_query. */
+int __res_context_mkquery (struct resolv_context *, int op, const char *dname,
+ int class, int type, const unsigned char *data,
+ unsigned char *buf, int buflen) attribute_hidden;
+
+/* Main resolver query function for use within glibc. */
+int __res_context_search (struct resolv_context *, const char *, int, int,
+ unsigned char *, int, unsigned char **,
+ unsigned char **, int *, int *, int *);
+libresolv_hidden_proto (__res_context_search)
+
+/* Main resolver query function for use within glibc. */
+int __res_context_query (struct resolv_context *, const char *, int, int,
+ unsigned char *, int, unsigned char **,
+ unsigned char **, int *, int *, int *);
+libresolv_hidden_proto (__res_context_query)
+
+/* Internal function used to implement the query and search
+ functions. */
+int __res_context_send (struct resolv_context *, const unsigned char *, int,
+ const unsigned char *, int, unsigned char *,
+ int, unsigned char **, unsigned char **,
+ int *, int *, int *) attribute_hidden;
+
+/* Internal function similar to res_hostalias. */
+const char *__res_context_hostalias (struct resolv_context *,
+ const char *, char *, size_t);
+libresolv_hidden_proto (__res_context_hostalias);
+
/* Add an OPT record to a DNS query. */
-int __res_nopt (res_state, int n0, unsigned char *buf, int buflen,
- int anslen) attribute_hidden;
+int __res_nopt (struct resolv_context *, int n0,
+ unsigned char *buf, int buflen, int anslen) attribute_hidden;
/* Convert from presentation format (which usually means ASCII
printable) to network format (which is usually some kind of binary
diff --git a/resolv/resolv_context.c b/resolv/resolv_context.c
new file mode 100644
index 0000000000..5083a40419
--- /dev/null
+++ b/resolv/resolv_context.c
@@ -0,0 +1,201 @@
+/* Temporary, thread-local resolver state.
+ Copyright (C) 2017 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
+ <http://www.gnu.org/licenses/>. */
+
+#include <resolv_context.h>
+#include <resolv-internal.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/* Currently active struct resolv_context object. This pointer forms
+ the start of a single-linked list, using the __next member of
+ struct resolv_context. This list serves two purposes:
+
+ (a) A subsequent call to __resolv_context_get will only increment
+ the reference counter and will not allocate a new object. The
+ _res state freshness check is skipped in this case, too.
+
+ (b) The per-thread cleanup function defined by the resolver calls
+ __resolv_context_freeres, which will deallocate all the context
+ objects. This avoids the need for cancellation handlers and
+ the complexity they bring, but it requires heap allocation of
+ the context object because the per-thread cleanup functions run
+ only after the stack has been fully unwound (and all on-stack
+ objects have been deallocated at this point).
+
+ The TLS variable current is updated even in
+ __resolv_context_get_override, to support case (b) above. This does
+ not override the per-thread resolver state (as obtained by the
+ non-res_state function such as __resolv_context_get) in an
+ observable way because the wrapped context is only used to
+ implement the res_n* functions in the resolver, and those do not
+ call back into user code which could indirectly use the per-thread
+ resolver state. */
+static __thread struct resolv_context *current attribute_tls_model_ie;
+
+/* Initialize *RESP if RES_INIT is not yet set in RESP->options, or if
+ res_init in some other thread requested re-initializing. */
+static __attribute__ ((warn_unused_result)) bool
+maybe_init (struct __res_state *resp, bool preinit)
+{
+ if (resp->options & RES_INIT)
+ {
+ if (__res_initstamp != resp->_u._ext.initstamp)
+ {
+ if (resp->nscount > 0)
+ __res_iclose (resp, true);
+ return __res_vinit (resp, 1) == 0;
+ }
+ return true;
+ }
+
+ if (preinit)
+ {
+ if (!resp->retrans)
+ resp->retrans = RES_TIMEOUT;
+ if (!resp->retry)
+ resp->retry = RES_DFLRETRY;
+ resp->options = RES_DEFAULT;
+ if (!resp->id)
+ resp->id = res_randomid ();
+ }
+ return __res_vinit (resp, preinit) == 0;
+}
+
+/* Allocate a new context object and initialize it. The object is put
+ on the current list. */
+static struct resolv_context *
+context_alloc (struct __res_state *resp)
+{
+ struct resolv_context *ctx = malloc (sizeof (*ctx));
+ if (ctx == NULL)
+ return NULL;
+ ctx->resp = resp;
+ ctx->__refcount = 1;
+ ctx->__from_res = true;
+ ctx->__next = current;
+ current = ctx;
+ return ctx;
+}
+
+/* Deallocate the context object and all the state within. */
+static void
+context_free (struct resolv_context *ctx)
+{
+ current = ctx->__next;
+ free (ctx);
+}
+
+/* Reuse the current context object. */
+static struct resolv_context *
+context_reuse (void)
+{
+ /* A context object created by __resolv_context_get_override cannot
+ be reused. */
+ assert (current->__from_res);
+
+ ++current->__refcount;
+
+ /* Check for reference counter wraparound. This can only happen if
+ the get/put functions are not properly paired. */
+ assert (current->__refcount > 0);
+
+ return current;
+}
+
+/* Backing function for the __resolv_context_get family of
+ functions. */
+static struct resolv_context *
+context_get (bool preinit)
+{
+ if (current != NULL)
+ return context_reuse ();
+
+ struct resolv_context *ctx = context_alloc (&_res);
+ if (ctx == NULL)
+ return NULL;
+ if (!maybe_init (ctx->resp, preinit))
+ {
+ context_free (ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+struct resolv_context *
+__resolv_context_get (void)
+{
+ return context_get (false);
+}
+libc_hidden_def (__resolv_context_get)
+
+struct resolv_context *
+__resolv_context_get_preinit (void)
+{
+ return context_get (true);
+}
+libc_hidden_def (__resolv_context_get_preinit)
+
+struct resolv_context *
+__resolv_context_get_override (struct __res_state *resp)
+{
+ /* NB: As explained asbove, context_alloc will put the context on
+ the current list. */
+ struct resolv_context *ctx = context_alloc (resp);
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->__from_res = false;
+ return ctx;
+}
+libc_hidden_def (__resolv_context_get_override)
+
+void
+__resolv_context_put (struct resolv_context *ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ /* NB: Callers assume that this function preserves errno and
+ h_errno. */
+
+ assert (current == ctx);
+ assert (ctx->__refcount > 0);
+
+ if (ctx->__from_res && --ctx->__refcount > 0)
+ /* Do not pop this context yet. */
+ return;
+
+ context_free (ctx);
+}
+libc_hidden_def (__resolv_context_put)
+
+void
+__resolv_context_freeres (void)
+{
+ /* Deallocate the entire chain of context objects. */
+ struct resolv_context *ctx = current;
+ current = NULL;
+ while (ctx != NULL)
+ {
+ struct resolv_context *next = ctx->__next;
+ context_free (ctx);
+ ctx = next;
+ }
+}
diff --git a/resolv/resolv_context.h b/resolv/resolv_context.h
new file mode 100644
index 0000000000..27c8d56b36
--- /dev/null
+++ b/resolv/resolv_context.h
@@ -0,0 +1,95 @@
+/* Temporary, thread-local resolver state.
+ Copyright (C) 2017 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
+ <http://www.gnu.org/licenses/>. */
+
+/* struct resolv_context objects are allocated on the heap,
+ initialized by __resolv_context_get (and its variants), and
+ destroyed by __resolv_context_put.
+
+ A nested call to __resolv_context_get (after another call to
+ __resolv_context_get without a matching __resolv_context_put call,
+ on the same thread) returns the original pointer, instead of
+ allocating a new context. This prevents unexpected reloading of
+ the resolver configuration. Care is taken to keep the context in
+ sync with the thread-local _res object. (This does not happen with
+ __resolv_context_get_override, and __resolv_context_get_no_inet6 may
+ also interpose another context object if RES_USE_INET6 needs to be
+ disabled.)
+
+ In contrast to struct __res_state, struct resolv_context is not
+ affected by ABI compatibility concerns.
+
+ For the benefit of the res_n* functions, a struct __res_state
+ pointer is included in the context object, and a separate
+ initialization function is provided. */
+
+#ifndef _RESOLV_CONTEXT_H
+#define _RESOLV_CONTEXT_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <bits/types/res_state.h>
+
+/* Temporary resolver state. */
+struct resolv_context
+{
+ struct __res_state *resp; /* Backing resolver state. */
+
+
+ /* The following fields are for internal use within the
+ resolv_context module. */
+ size_t __refcount; /* Count of reusages by the get functions. */
+ bool __from_res; /* True if created from _res. */
+
+ /* If RES_USE_INET6 was disabled at this level, this field points to
+ the previous context. */
+ struct resolv_context *__next;
+};
+
+/* Return the current temporary resolver context, or NULL if there was
+ an error (indicated by errno). A call to this function must be
+ paired with a call to __resolv_context_put. */
+struct resolv_context *__resolv_context_get (void)
+ __attribute__ ((warn_unused_result));
+libc_hidden_proto (__resolv_context_get)
+
+/* Deallocate the temporary resolver context. Converse of
+ __resolv_context_get. Restore the RES_USE_INET6 flag if necessary.
+ Do nothing if CTX is NULL. */
+void __resolv_context_put (struct resolv_context *ctx);
+libc_hidden_proto (__resolv_context_put)
+
+/* Like __resolv_context_get, but the _res structure can be partially
+ initialzed and those changes will not be overwritten. */
+struct resolv_context *__resolv_context_get_preinit (void)
+ __attribute__ ((warn_unused_result));
+libc_hidden_proto (__resolv_context_get_preinit)
+
+/* Wrap a struct __res_state object in a struct resolv_context object.
+ A call to this function must be paired with a call to
+ __resolv_context_put. */
+struct resolv_context *__resolv_context_get_override (struct __res_state *)
+ __attribute__ ((nonnull (1), warn_unused_result));
+libc_hidden_proto (__resolv_context_get_override)
+
+/* Called during thread shutdown to free the associated resolver
+ context (mostly in response to cancellation, otherwise the
+ __resolv_context_get/__resolv_context_put pairing will already have
+ deallocated the context object). */
+void __resolv_context_freeres (void) attribute_hidden;
+
+#endif /* _RESOLV_CONTEXT_H */