From 352f4ff9a268b81ef5d4b2413f582565806e4790 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Fri, 30 Jun 2017 21:10:23 +0200 Subject: 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. --- ChangeLog | 85 ++++++++++++ include/resolv.h | 23 ---- nscd/aicache.c | 21 +-- nss/digits_dots.c | 21 ++- nss/getXXbyYY.c | 28 +++- nss/getXXbyYY_r.c | 34 +++-- nss/getnssent_r.c | 42 ++++-- nss/nsswitch.h | 10 ++ resolv/Makefile | 3 +- resolv/Versions | 10 +- resolv/compat-gethnamaddr.c | 95 +++++++++----- resolv/nss_dns/dns-canon.c | 19 ++- resolv/nss_dns/dns-host.c | 104 +++++++++------ resolv/nss_dns/dns-network.c | 21 ++- resolv/res-close.c | 3 + resolv/res_libc.c | 31 ----- resolv/res_mkquery.c | 92 +++++++------ resolv/res_query.c | 298 +++++++++++++++++++++++++------------------ resolv/res_send.c | 44 +++++-- resolv/res_use_inet6.h | 49 +++++++ resolv/resolv-internal.h | 36 +++++- resolv/resolv_context.c | 201 +++++++++++++++++++++++++++++ resolv/resolv_context.h | 95 ++++++++++++++ sysdeps/posix/getaddrinfo.c | 39 +++--- 24 files changed, 1033 insertions(+), 371 deletions(-) create mode 100644 resolv/res_use_inet6.h create mode 100644 resolv/resolv_context.c create mode 100644 resolv/resolv_context.h diff --git a/ChangeLog b/ChangeLog index 74dc23e987..edd0e69491 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,88 @@ +2017-06-30 Florian Weimer + + [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 * resolv/tst-resolv-res_init-skeleton.c diff --git a/include/resolv.h b/include/resolv.h index 2938506d75..634f5525fe 100644 --- a/include/resolv.h +++ b/include/resolv.h @@ -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) diff --git a/nscd/aicache.c b/nscd/aicache.c index f1f9284f6d..a3de792cc4 100644 --- a/nscd/aicache.c +++ b/nscd/aicache.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #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) { diff --git a/nss/digits_dots.c b/nss/digits_dots.c index 8dcbf9eb0a..0c1fa97e39 100644 --- a/nss/digits_dots.c +++ b/nss/digits_dots.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #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 diff --git a/nss/getXXbyYY.c b/nss/getXXbyYY.c index d027b14250..a439b816f7 100644 --- a/nss/getXXbyYY.c +++ b/nss/getXXbyYY.c @@ -47,6 +47,11 @@ |* *| \*******************************************************************/ + +#ifdef HANDLE_DIGITS_DOTS +# include +#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); diff --git a/nss/getXXbyYY_r.c b/nss/getXXbyYY_r.c index 7cab825cf0..6c547ea1ca 100644 --- a/nss/getXXbyYY_r.c +++ b/nss/getXXbyYY_r.c @@ -26,7 +26,7 @@ # include #endif #ifdef NEED__RES -# include +# include #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; diff --git a/nss/getnssent_r.c b/nss/getnssent_r.c index 5fdbf3be00..d85065b6cc 100644 --- a/nss/getnssent_r.c +++ b/nss/getnssent_r.c @@ -18,6 +18,7 @@ #include #include #include "nsswitch.h" +#include /* 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 diff --git a/nss/nsswitch.h b/nss/nsswitch.h index f3e756b684..bd3fbcb082 100644 --- a/nss/nsswitch.h +++ b/nss/nsswitch.h @@ -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, 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 # include # include +# include # include # include # include @@ -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 #include #include - +#include +#include #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 +#include #include #include @@ -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) 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 #include +#include +#include /* 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 +#include #include /* 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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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 + . */ + +#ifndef _RES_USE_INET6_H +#define _RES_USE_INET6_H + +#include +#include + +/* 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 + . */ + +#include +#include + +#include +#include +#include + +/* 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 + . */ + +/* 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 +#include +#include + +/* 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 */ diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c index 4fb1eaef79..efa7118498 100644 --- a/sysdeps/posix/getaddrinfo.c +++ b/sysdeps/posix/getaddrinfo.c @@ -60,6 +60,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include +#include #include #include #include @@ -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) { -- cgit v1.2.3