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.
This commit is contained in:
Florian Weimer 2017-06-30 21:10:23 +02:00
parent 4e45d83c92
commit 352f4ff9a2
24 changed files with 1032 additions and 370 deletions

View File

@ -1,3 +1,88 @@
2017-06-30 Florian Weimer <fweimer@redhat.com>
[BZ #21668]
Introduce temporary resolver contexts (struct resolv_conf).
* resolv/resolv-internal.h (__res_context_mkquery)
(__res_context_searchl __res_context_query, __res_context_send)
(__res_context_hostalias): Declare.
(__res_nopt): Switch to struct resolv_context.
* resolv/res_use_inet6.h: New file.
* resolv/resolv_context.h: Likewise.
* resolv/resolv_context.c: Likewise.
* resolv/compat-gethnamaddr.c (res_gethostbyname2_context):
Renamed from res_gethostbyname2. Use struct resolv_context.
(res_gethostbyname2): New function. Implement using
res_gethostbyname2_context.
(res_gethostbyaddr_context): Renamed from res_gethostbyaddr. Use
struct resolv_context.
(res_gethostbyaddr): New function. Implement using
res_gethostbyaddr_context.
* resolv/nss_dns/dns-canon.c (_nss_dns_getcanonname_r): Use struct
resolv_context.
* resolv/nss_dns/dns-host.c (gethostbyname3_context): Renamed from
_nss_dns_gethostbyname3_r. Use struct resolv_context.
(_nss_dns_gethostbyname3_r): Implement using gethostbyname3_context.
(_nss_dns_gethostbyname_r, _nss_dns_gethostbyname4_r): Likewise.
(_nss_dns_gethostbyaddr2_r): Use struct resolv_context.
* resolv/nss_dns/dns-network.c (_nss_dns_getnetbyname_r)
(_nss_dns_getnetbyaddr_r): Likewise.
* resolv/res-close.c (res_thread_freeres): Call
__resolv_context_freeres.
* resolv/res_libc.c (__res_maybe_init): Remove function. Moved to
maybe_init in resolv/resolv_context.c.
* resolv/res_mkquery.c (__res_context_mkquery): Rename from
res_nmkquery. Use struct resolv_context.
(context_mkquery_common): New function.
(res_nmkquery, res_mkquery): Use it.
(res_nopt): Switch to struct resolv_context.
* resolv/res_query.c (__res_context_querydomain): Renamed from
__libc_res_nquerydomain. Use struct resolv_context.
(__res_context_query): Renamed from __libc_res_nquery. Use struct
resolv_context.
(context_query_common): New function.
(res_nquery, res_query): Use it.
(__res_context_search): Renamed from __libc_res_nsearch. Use
struct resolv_context.
(context_search_common): New function.
(res_nsearch, res_search): Use it.
(__res_context_querydomain): Rename from __libc_res_nquerydomain.
Use struct resolv_context.
(context_querydomain_common): New function.
(res_nquerydomain, res_querydomain): Use it.
(__res_context_hostalias): Rename from res_hostalias. Use struct
resolv_context.
(context_hostalias_common): New function.
(res_hostalias, hostalias): Use it.
* resolv/res_send.c (__res_context_send): Renamed from
__libc_res_nsend. Use struct resolv_context.
(context_send_common): New function.
(res_nsend, res_send): Use it.
* resolv/Makefile (routines): Add resolv_context.
* resolv/Versions (libc): Export __resolv_context_get,
__resolv_context_get_preinit, __resolv_context_get_override,
__resolv_context_put. Remove __res_maybe_init.
(libresolv): Export __res_context_query, __res_context_search,
__res_context_hostalias. Remove __libc_res_nquery,
__libc_res_nsearch.
* include/resolv.h (__res_maybe_init, __libc_res_nquery)
(__libc_res_nsearch, __libc_res_nsend): Remove declaration.
(__hostalias, __res_nmkquery, __res_nquery, __res_nquerydomain)
(__res_hostalias, __res_nsearch, __res_nsend): Remove hidden
prototypes.
* nss/nsswitch.h (__nss_hostname_digits_dots_context): Declare.
* nss/digits_dots.c (__nss_hostname_digits_dots_context): Renamed
from __nss_hostname_digits_dots. Use struct resolv_context.
(__nss_hostname_digits_dots): New function.
* nss/getXXbyYY.c [HANDLE_DIGITS_DOTS] (FUNCTION_NAME): Acquire
struct resolv_context object. Call new function
__nss_hostname_digits_dots_context.
* nss/getXXbyYY_r.c (REENTRANT_NAME): Use struct resolv_context.
* nss/getnssent_r.c (__nss_setent): Likewise.
* nscd/aicache.c (addhstaiX): Use struct resolv_context,
__resolv_context_disable_inet6 and __resolv_context_enable_inet6
instead of direct _res manipulation.
* sysdeps/posix/getaddrinfo.c (gethosts, gaih_inet): Likewise.
2017-07-03 Florian Weimer <fweimer@redhat.com>
* resolv/tst-resolv-res_init-skeleton.c

View File

@ -24,7 +24,6 @@ extern __thread struct __res_state *__resp attribute_tls_model_ie;
/* Now define the internal interfaces. */
extern int __res_vinit (res_state, int) attribute_hidden;
extern int __res_maybe_init (res_state, int);
extern void _sethtent (int);
extern struct hostent *_gethtent (void);
extern struct hostent *_gethtbyname (const char *__name);
@ -36,24 +35,11 @@ extern int res_ourserver_p (const res_state __statp,
const struct sockaddr_in6 *__inp);
extern void __res_iclose (res_state statp, bool free_addr);
libc_hidden_proto (__res_ninit)
libc_hidden_proto (__res_maybe_init)
libc_hidden_proto (__res_nclose)
libc_hidden_proto (__res_iclose)
libc_hidden_proto (__res_randomid)
libc_hidden_proto (__res_state)
int __libc_res_nquery (res_state, const char *, int, int,
unsigned char *, int, unsigned char **,
unsigned char **, int *, int *, int *);
int __libc_res_nsearch (res_state, const char *, int, int,
unsigned char *, int, unsigned char **,
unsigned char **, int *, int *, int *);
int __libc_res_nsend (res_state, const unsigned char *, int,
const unsigned char *, int, unsigned char *,
int, unsigned char **, unsigned char **,
int *, int *, int *)
attribute_hidden;
libresolv_hidden_proto (_sethtent)
libresolv_hidden_proto (_gethtent)
libresolv_hidden_proto (_gethtbyaddr)
@ -75,17 +61,8 @@ libresolv_hidden_proto (__p_type)
libresolv_hidden_proto (__loc_ntoa)
libresolv_hidden_proto (__fp_nquery)
libresolv_hidden_proto (__fp_query)
libresolv_hidden_proto (__hostalias)
libresolv_hidden_proto (__res_nmkquery)
libresolv_hidden_proto (__libc_res_nquery)
libresolv_hidden_proto (__res_nquery)
libresolv_hidden_proto (__res_nquerydomain)
libresolv_hidden_proto (__res_hostalias)
libresolv_hidden_proto (__libc_res_nsearch)
libresolv_hidden_proto (__res_nsearch)
libresolv_hidden_proto (__res_nameinquery)
libresolv_hidden_proto (__res_queriesmatch)
libresolv_hidden_proto (__res_nsend)
libresolv_hidden_proto (__b64_ntop)
libresolv_hidden_proto (__dn_count_labels)
libresolv_hidden_proto (__p_secstodate)

View File

@ -26,6 +26,8 @@
#include <unistd.h>
#include <sys/mman.h>
#include <resolv/resolv-internal.h>
#include <resolv/resolv_context.h>
#include <resolv/res_use_inet6.h>
#include "dbg_log.h"
#include "nscd.h"
@ -100,17 +102,15 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
no_more = 0;
nip = hosts_database;
/* Initialize configurations. */
if (__res_maybe_init (&_res, 0) == -1)
/* Initialize configurations. If we are looking for both IPv4 and
IPv6 address we don't want the lookup functions to automatically
promote IPv4 addresses to IPv6 addresses. Therefore, use the
_no_inet6 variant. */
struct resolv_context *ctx = __resolv_context_get ();
bool enable_inet6 = __resolv_context_disable_inet6 (ctx);
if (ctx == NULL)
no_more = 1;
/* If we are looking for both IPv4 and IPv6 address we don't want
the lookup functions to automatically promote IPv4 addresses to
IPv6 addresses. Currently this is decided by setting the
RES_USE_INET6 bit in _res.options. */
int old_res_options = _res.options;
_res.options &= ~DEPRECATED_RES_USE_INET6;
size_t tmpbuf6len = 1024;
char *tmpbuf6 = alloca (tmpbuf6len);
size_t tmpbuf4len = 0;
@ -534,7 +534,8 @@ next_nip:
}
out:
_res.options |= old_res_options & DEPRECATED_RES_USE_INET6;
__resolv_context_enable_inet6 (ctx, enable_inet6);
__resolv_context_put (ctx);
if (dataset != NULL && !alloca_used)
{

View File

@ -23,6 +23,7 @@
#include <ctype.h>
#include <wctype.h>
#include <resolv/resolv-internal.h>
#include <resolv/resolv_context.h>
#include <netdb.h>
#include <arpa/inet.h>
#include "nsswitch.h"
@ -38,11 +39,10 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
size_t buflen, struct hostent **result,
enum nss_status *status, int af, int *h_errnop)
{
int save;
/* We have to test for the use of IPv6 which can only be done by
examining `_res'. */
if (__res_maybe_init (&_res, 0) == -1)
struct resolv_context *ctx = __resolv_context_get ();
if (ctx == NULL)
{
if (h_errnop)
*h_errnop = NETDB_INTERNAL;
@ -52,6 +52,21 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
*result = NULL;
return -1;
}
int ret = __nss_hostname_digits_dots_context
(ctx, name, resbuf, buffer, buffer_size, buflen,
result, status, af, h_errnop);
__resolv_context_put (ctx);
return ret;
}
int
__nss_hostname_digits_dots_context (struct resolv_context *ctx,
const char *name, struct hostent *resbuf,
char **buffer, size_t *buffer_size,
size_t buflen, struct hostent **result,
enum nss_status *status, int af, int *h_errnop)
{
int save;
/*
* disallow names consisting only of digits/dots, unless

View File

@ -47,6 +47,11 @@
|* *|
\*******************************************************************/
#ifdef HANDLE_DIGITS_DOTS
# include <resolv/resolv_context.h>
#endif
/* To make the real sources a bit prettier. */
#define REENTRANT_NAME APPEND_R (FUNCTION_NAME)
#define APPEND_R(name) APPEND_R1 (name)
@ -93,6 +98,19 @@ FUNCTION_NAME (ADD_PARAMS)
int h_errno_tmp = 0;
#endif
#ifdef HANDLE_DIGITS_DOTS
/* Wrap both __nss_hostname_digits_dots and the actual lookup
function call in the same context. */
struct resolv_context *res_ctx = __resolv_context_get ();
if (res_ctx == NULL)
{
# if NEED_H_ERRNO
__set_h_errno (NETDB_INTERNAL);
# endif
return NULL;
}
#endif
/* Get lock. */
__libc_lock_lock (lock);
@ -105,9 +123,9 @@ FUNCTION_NAME (ADD_PARAMS)
#ifdef HANDLE_DIGITS_DOTS
if (buffer != NULL)
{
if (__nss_hostname_digits_dots (name, &resbuf, &buffer,
&buffer_size, 0, &result, NULL, AF_VAL,
H_ERRNO_VAR_P))
if (__nss_hostname_digits_dots_context
(res_ctx, name, &resbuf, &buffer, &buffer_size, 0, &result, NULL,
AF_VAL, H_ERRNO_VAR_P))
goto done;
}
#endif
@ -143,6 +161,10 @@ done:
/* Release lock. */
__libc_lock_unlock (lock);
#ifdef HANDLE_DIGITS_DOTS
__resolv_context_put (res_ctx);
#endif
#ifdef NEED_H_ERRNO
if (h_errno_tmp != 0)
__set_h_errno (h_errno_tmp);

View File

@ -26,7 +26,7 @@
# include <nscd/nscd_proto.h>
#endif
#ifdef NEED__RES
# include <resolv.h>
# include <resolv/resolv_context.h>
#endif
/*******************************************************************\
|* Here we assume several symbols to be defined: *|
@ -53,8 +53,7 @@
|* NEED_H_ERRNO - an extra parameter will be passed to point to *|
|* the global `h_errno' variable. *|
|* *|
|* NEED__RES - the global _res variable might be used so we *|
|* will have to initialize it if necessary *|
|* NEED__RES - obtain a struct resolv_context resolver context *|
|* *|
|* PREPROCESS - code run before anything else *|
|* *|
@ -213,6 +212,18 @@ INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
bool any_service = false;
#endif
#ifdef NEED__RES
/* The HANDLE_DIGITS_DOTS case below already needs the resolver
configuration, so this has to happen early. */
struct resolv_context *res_ctx = __resolv_context_get ();
if (res_ctx == NULL)
{
*h_errnop = NETDB_INTERNAL;
*result = NULL;
return errno;
}
#endif /* NEED__RES */
#ifdef PREPROCESS
PREPROCESS;
#endif
@ -260,17 +271,6 @@ INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
}
else
{
#ifdef NEED__RES
/* The resolver code will really be used so we have to
initialize it. */
if (__res_maybe_init (&_res, 0) == -1)
{
*h_errnop = NETDB_INTERNAL;
*result = NULL;
return errno;
}
#endif /* need _res */
void *tmp_ptr = fct.l;
#ifdef PTR_MANGLE
PTR_MANGLE (tmp_ptr);
@ -399,6 +399,12 @@ done:
POSTPROCESS;
#endif
#ifdef NEED__RES
/* This has to happen late because the POSTPROCESS stage above might
need the resolver context. */
__resolv_context_put (res_ctx);
#endif /* NEED__RES */
int res;
if (status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND)
res = 0;

View File

@ -18,6 +18,7 @@
#include <errno.h>
#include <netdb.h>
#include "nsswitch.h"
#include <resolv/resolv_context.h>
/* Set up NIP to run through the services. If ALL is zero, use NIP's
current location if it's not nil. Return nonzero if there are no
@ -59,10 +60,15 @@ __nss_setent (const char *func_name, db_lookup_function lookup_fct,
} fct;
int no_more;
if (res && __res_maybe_init (&_res, 0) == -1)
struct resolv_context *res_ctx = NULL;
if (res)
{
__set_h_errno (NETDB_INTERNAL);
return;
res_ctx = __resolv_context_get ();
if (res_ctx == NULL)
{
__set_h_errno (NETDB_INTERNAL);
return;
}
}
/* Cycle through the services and run their `setXXent' functions until
@ -95,6 +101,8 @@ __nss_setent (const char *func_name, db_lookup_function lookup_fct,
*last_nip = *nip;
}
__resolv_context_put (res_ctx);
if (stayopen_tmp)
*stayopen_tmp = stayopen;
}
@ -112,10 +120,15 @@ __nss_endent (const char *func_name, db_lookup_function lookup_fct,
} fct;
int no_more;
if (res && __res_maybe_init (&_res, 0) == -1)
struct resolv_context *res_ctx = NULL;
if (res)
{
__set_h_errno (NETDB_INTERNAL);
return;
res_ctx = __resolv_context_get ();
if (res_ctx == NULL)
{
__set_h_errno (NETDB_INTERNAL);
return;
}
}
/* Cycle through all the services and run their endXXent functions. */
@ -132,6 +145,8 @@ __nss_endent (const char *func_name, db_lookup_function lookup_fct,
no_more = __nss_next2 (nip, func_name, NULL, &fct.ptr, 0, 1);
}
*last_nip = *nip = NULL;
__resolv_context_put (res_ctx);
}
@ -152,11 +167,16 @@ __nss_getent_r (const char *getent_func_name,
int no_more;
enum nss_status status;
if (res && __res_maybe_init (&_res, 0) == -1)
struct resolv_context *res_ctx = NULL;
if (res)
{
*h_errnop = NETDB_INTERNAL;
*result = NULL;
return errno;
res_ctx = __resolv_context_get ();
if (res_ctx == NULL)
{
*h_errnop = NETDB_INTERNAL;
*result = NULL;
return errno;
}
}
/* Initialize status to return if no more functions are found. */
@ -227,6 +247,8 @@ __nss_getent_r (const char *getent_func_name,
while (! no_more && status != NSS_STATUS_SUCCESS);
}
__resolv_context_put (res_ctx);
*result = status == NSS_STATUS_SUCCESS ? resbuf : NULL;
return (status == NSS_STATUS_SUCCESS ? 0
: status != NSS_STATUS_TRYAGAIN ? ENOENT

View File

@ -197,7 +197,17 @@ extern int __nss_getent_r (const char *getent_func_name,
extern void *__nss_getent (getent_r_function func,
void **resbuf, char **buffer, size_t buflen,
size_t *buffer_size, int *h_errnop);
struct resolv_context;
struct hostent;
extern int __nss_hostname_digits_dots_context (struct resolv_context *,
const char *name,
struct hostent *resbuf,
char **buffer,
size_t *buffer_size,
size_t buflen,
struct hostent **result,
enum nss_status *status, int af,
int *h_errnop) attribute_hidden;
extern int __nss_hostname_digits_dots (const char *name,
struct hostent *resbuf, char **buffer,
size_t *buffer_size, size_t buflen,

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
struct resolv_context *ctx = __resolv_context_get ();
if (ctx == NULL)
{
__set_h_errno (NETDB_INTERNAL);
return NULL;
}
if (__res_maybe_init (&_res, 0) == -1) {
__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;
}
if (res_use_inet6 ()) {
hp = res_gethostbyname2(name, AF_INET6);
if (hp)
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

View File

@ -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;
}

View File

@ -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,10 +146,30 @@ 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
{
@ -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)

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);
__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.
*/
/* 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

View File

@ -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);
}
int
res_nsend(res_state statp,
const u_char *buf, int buflen, u_char *ans, int anssiz)
/* 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)
{
return __libc_res_nsend(statp, buf, buflen, NULL, 0, ans, anssiz,
NULL, NULL, NULL, NULL, NULL);
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 unsigned char *buf, int buflen,
unsigned char *ans, int anssiz)
{
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 */

49
resolv/res_use_inet6.h Normal file
View File

@ -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

View File

@ -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

201
resolv/resolv_context.c Normal file
View File

@ -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;
}
}

95
resolv/resolv_context.h Normal file
View File

@ -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 */

View File

@ -60,6 +60,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <netdb.h>
#include <nss.h>
#include <resolv/resolv-internal.h>
#include <resolv/resolv_context.h>
#include <resolv/res_use_inet6.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdio_ext.h>
@ -266,7 +268,8 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
if (herrno == NETDB_INTERNAL) \
{ \
__set_h_errno (herrno); \
_res.options |= old_res_options & DEPRECATED_RES_USE_INET6; \
__resolv_context_enable_inet6 (res_ctx, res_enable_inet6); \
__resolv_context_put (res_ctx); \
result = -EAI_SYSTEM; \
goto free_and_return; \
} \
@ -279,7 +282,8 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
{ \
if (!convert_hostent_to_gaih_addrtuple (req, _family,h, &addrmem)) \
{ \
_res.options |= old_res_options & DEPRECATED_RES_USE_INET6; \
__resolv_context_enable_inet6 (res_ctx, res_enable_inet6); \
__resolv_context_put (res_ctx); \
result = -EAI_SYSTEM; \
goto free_and_return; \
} \
@ -582,7 +586,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
enum nss_status inet6_status = NSS_STATUS_UNAVAIL;
enum nss_status status = NSS_STATUS_UNAVAIL;
int no_more;
int old_res_options;
struct resolv_context *res_ctx = NULL;
bool res_enable_inet6 = false;
/* If we do not have to look for IPv6 addresses or the canonical
name, use the simple, old functions, which do not support
@ -765,16 +770,14 @@ gaih_inet (const char *name, const struct gaih_service *service,
no_more = 0;
nip = __nss_hosts_database;
/* Initialize configurations. */
if (__res_maybe_init (&_res, 0) == -1)
no_more = 1;
/* If we are looking for both IPv4 and IPv6 address we don't
want the lookup functions to automatically promote IPv4
addresses to IPv6 addresses. Currently this is decided
by setting the RES_USE_INET6 bit in _res.options. */
old_res_options = _res.options;
_res.options &= ~DEPRECATED_RES_USE_INET6;
addresses to IPv6 addresses, so we use the no_inet6
function variant. */
res_ctx = __resolv_context_get ();
res_enable_inet6 = __resolv_context_disable_inet6 (res_ctx);
if (res_ctx == NULL)
no_more = 1;
while (!no_more)
{
@ -811,8 +814,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
if (!scratch_buffer_grow (tmpbuf))
{
_res.options
|= old_res_options & DEPRECATED_RES_USE_INET6;
__resolv_context_enable_inet6
(res_ctx, res_enable_inet6);
__resolv_context_put (res_ctx);
result = -EAI_MEMORY;
goto free_and_return;
}
@ -911,9 +915,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
canonbuf = getcanonname (nip, at, name);
if (canonbuf == NULL)
{
_res.options
|= old_res_options
& DEPRECATED_RES_USE_INET6;
__resolv_context_enable_inet6
(res_ctx, res_enable_inet6);
__resolv_context_put (res_ctx);
result = -EAI_MEMORY;
goto free_and_return;
}
@ -953,7 +957,8 @@ gaih_inet (const char *name, const struct gaih_service *service,
nip = nip->next;
}
_res.options |= old_res_options & DEPRECATED_RES_USE_INET6;
__resolv_context_enable_inet6 (res_ctx, res_enable_inet6);
__resolv_context_put (res_ctx);
if (h_errno == NETDB_INTERNAL)
{