]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
resolv: Introduce struct resolv_context [BZ #21668]
authorFlorian Weimer <fweimer@redhat.com>
Fri, 30 Jun 2017 19:10:23 +0000 (21:10 +0200)
committerFlorian Weimer <fweimer@redhat.com>
Mon, 3 Jul 2017 18:52:59 +0000 (20:52 +0200)
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.

24 files changed:
ChangeLog
include/resolv.h
nscd/aicache.c
nss/digits_dots.c
nss/getXXbyYY.c
nss/getXXbyYY_r.c
nss/getnssent_r.c
nss/nsswitch.h
resolv/Makefile
resolv/Versions
resolv/compat-gethnamaddr.c
resolv/nss_dns/dns-canon.c
resolv/nss_dns/dns-host.c
resolv/nss_dns/dns-network.c
resolv/res-close.c
resolv/res_libc.c
resolv/res_mkquery.c
resolv/res_query.c
resolv/res_send.c
resolv/res_use_inet6.h [new file with mode: 0644]
resolv/resolv-internal.h
resolv/resolv_context.c [new file with mode: 0644]
resolv/resolv_context.h [new file with mode: 0644]
sysdeps/posix/getaddrinfo.c

index 74dc23e987337df1dd06736da44fe98c30807f31..edd0e694917ceb912b1ef968f38fc23e58997047 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
index 2938506d75ee5d43e89ec71c5ef11b28defd2358..634f5525fe9d357a64a249daa168670aa6390d33 100644 (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)
index f1f9284f6d905fcc60379e8bc4cafd2795da7d92..a3de792cc429b54664f0f42f2a836070434f7bc7 100644 (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)
     {
index 8dcbf9eb0aebf0f1c44be04a020fd5ddbdbf577c..0c1fa97e3977a81e2d13ab6783e9e11e1ed48422 100644 (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
index d027b1425080c6f71308add517f790d992a69239..a439b816f70aa2e71007b28668d27c9657e460b0 100644 (file)
 |*                                                                *|
 \*******************************************************************/
 
+
+#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);
index 7cab825cf05503f620fc8d327f093782e66342a4..6c547ea1ca6646c9b543fe1940da6514506570ce 100644 (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;
index 5fdbf3be0061aae172d2649efcf1390a8ce90a6c..d85065b6cc05dbcba45eed5637f4c6d08bb2208d 100644 (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
index f3e756b68448f6ea1cc6afbb9fd94e65cabfe92f..bd3fbcb08250c61c5987aca822e4775daec93d4d 100644 (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,
index bab1ac24a6c6fc1045cd8cc4967705f6e155a869..126da0736a09b816b75813861bf578c8c6508378 100644 (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
index f528ed51e898a61da37cc447143ccf724cc7c134..b05778d9654aa0f24ff46554952580b4c28e687e 100644 (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;
   }
 }
 
index 813c7d4e8508df34d65770ebbf1a751be85da8ba..259378b2be2d5f639531f032dd2d85588a2a49aa 100644 (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;
-
-       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
index 4276eb65424da5fe1752faa77098bdd20c467f6c..7a5c39dc20f6ebb8fc6a661d041daf9327484bd3 100644 (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;
 }
index 206924de8603b4ddf9243efbcc1e8cb0c88f8f3f..9d7ceb1691987b275974bfb26aea5a5b2908a112 100644 (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)
index dc1599b47122fea219a271c886b8bf68e074e8f5..f190eb2225d39d16c1c11a193cd1b14f3468526e 100644 (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;
 }
 
index 73f18d15256c6f87a8b56203dd6e87bb089304fa..97da73c99cfd0e12318934a2bd148dc22e4d5a9c 100644 (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;
index 3d7b4f72d0dda60393510749a5a4c3ab17aa2f2a..5066983ccf2d3058ec054c9fb1daed22489c5d87 100644 (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)
 \f
 /* This needs to be after the use of _res in res_init, above.  */
 #undef _res
index 9afb410980e47ca7b33f8432b9f37792d6831e50..59fc5ab28c0faa6666847b7f6823a0356229ba24 100644 (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>
 # 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);
index 760bf324e817c79e37e61a56f770fcaee69f6b45..33249e36f51bcf9a0b51a2fe7bc81731a25d7709 100644 (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>
 #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
index a7daae8a06c423adb60d18aecb4718512e31b5aa..b396aae03c9eeb6e2ada366bd0eeea61353b76b3 100644 (file)
 #include <fcntl.h>
 #include <netdb.h>
 #include <resolv/resolv-internal.h>
+#include <resolv/resolv_context.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
@@ -400,11 +401,14 @@ res_queriesmatch(const u_char *buf1, const u_char *eom1,
 libresolv_hidden_def (res_queriesmatch)
 
 int
-__libc_res_nsend(res_state statp, const u_char *buf, int buflen,
-                const u_char *buf2, int buflen2,
-                u_char *ans, int anssiz, u_char **ansp, u_char **ansp2,
-                int *nansp2, int *resplen2, int *ansp2_malloced)
+__res_context_send (struct resolv_context *ctx,
+                   const unsigned char *buf, int buflen,
+                   const unsigned char *buf2, int buflen2,
+                   unsigned char *ans, int anssiz,
+                   unsigned char **ansp, unsigned char **ansp2,
+                   int *nansp2, int *resplen2, int *ansp2_malloced)
 {
+       struct __res_state *statp = ctx->resp;
        int gotsomewhere, terrno, try, v_circuit, resplen, n;
 
        if (statp->nscount == 0) {
@@ -541,22 +545,36 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
        return (-1);
 }
 
+/* Common part of res_nsend and res_send.  */
+static int
+context_send_common (struct resolv_context *ctx,
+                    const unsigned char *buf, int buflen,
+                    unsigned char *ans, int anssiz)
+{
+  if (ctx == NULL)
+    {
+      RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
+      return -1;
+    }
+  int result = __res_context_send (ctx, buf, buflen, NULL, 0, ans, anssiz,
+                                  NULL, NULL, NULL, NULL, NULL);
+  __resolv_context_put (ctx);
+  return result;
+}
+
 int
-res_nsend(res_state statp,
-         const u_char *buf, int buflen, u_char *ans, int anssiz)
+res_nsend (res_state statp, const unsigned char *buf, int buflen,
+          unsigned char *ans, int anssiz)
 {
-  return __libc_res_nsend(statp, buf, buflen, NULL, 0, ans, anssiz,
-                         NULL, NULL, NULL, NULL, NULL);
+  return context_send_common
+    (__resolv_context_get_override (statp), buf, buflen, ans, anssiz);
 }
-libresolv_hidden_def (res_nsend)
 
 int
 res_send (const unsigned char *buf, int buflen, unsigned char *ans, int anssiz)
 {
-  if (__res_maybe_init (&_res, 1) == -1)
-    /* errno should have been set by res_init in this case.  */
-    return -1;
-  return res_nsend (&_res, buf, buflen, ans, anssiz);
+  return context_send_common
+    (__resolv_context_get (), buf, buflen, ans, anssiz);
 }
 
 /* Private */
diff --git a/resolv/res_use_inet6.h b/resolv/res_use_inet6.h
new file mode 100644 (file)
index 0000000..8649833
--- /dev/null
@@ -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
index 5a9faf8de975f316fdc2a6eb55594145b5d1ac4c..9246497196adab7d2b26dda0d062b12b0b2095ed 100644 (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
diff --git a/resolv/resolv_context.c b/resolv/resolv_context.c
new file mode 100644 (file)
index 0000000..5083a40
--- /dev/null
@@ -0,0 +1,201 @@
+/* Temporary, thread-local resolver state.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <resolv_context.h>
+#include <resolv-internal.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/* Currently active struct resolv_context object.  This pointer forms
+   the start of a single-linked list, using the __next member of
+   struct resolv_context.  This list serves two purposes:
+
+   (a) A subsequent call to __resolv_context_get will only increment
+       the reference counter and will not allocate a new object.  The
+       _res state freshness check is skipped in this case, too.
+
+   (b) The per-thread cleanup function defined by the resolver calls
+       __resolv_context_freeres, which will deallocate all the context
+       objects.  This avoids the need for cancellation handlers and
+       the complexity they bring, but it requires heap allocation of
+       the context object because the per-thread cleanup functions run
+       only after the stack has been fully unwound (and all on-stack
+       objects have been deallocated at this point).
+
+   The TLS variable current is updated even in
+   __resolv_context_get_override, to support case (b) above.  This does
+   not override the per-thread resolver state (as obtained by the
+   non-res_state function such as __resolv_context_get) in an
+   observable way because the wrapped context is only used to
+   implement the res_n* functions in the resolver, and those do not
+   call back into user code which could indirectly use the per-thread
+   resolver state.  */
+static __thread struct resolv_context *current attribute_tls_model_ie;
+
+/* Initialize *RESP if RES_INIT is not yet set in RESP->options, or if
+   res_init in some other thread requested re-initializing.  */
+static __attribute__ ((warn_unused_result)) bool
+maybe_init (struct __res_state *resp, bool preinit)
+{
+  if (resp->options & RES_INIT)
+    {
+      if (__res_initstamp != resp->_u._ext.initstamp)
+        {
+          if (resp->nscount > 0)
+            __res_iclose (resp, true);
+          return __res_vinit (resp, 1) == 0;
+        }
+      return true;
+    }
+
+  if (preinit)
+    {
+      if (!resp->retrans)
+        resp->retrans = RES_TIMEOUT;
+      if (!resp->retry)
+        resp->retry = RES_DFLRETRY;
+      resp->options = RES_DEFAULT;
+      if (!resp->id)
+        resp->id = res_randomid ();
+    }
+  return __res_vinit (resp, preinit) == 0;
+}
+
+/* Allocate a new context object and initialize it.  The object is put
+   on the current list.  */
+static struct resolv_context *
+context_alloc (struct __res_state *resp)
+{
+  struct resolv_context *ctx = malloc (sizeof (*ctx));
+  if (ctx == NULL)
+    return NULL;
+  ctx->resp = resp;
+  ctx->__refcount = 1;
+  ctx->__from_res = true;
+  ctx->__next = current;
+  current = ctx;
+  return ctx;
+}
+
+/* Deallocate the context object and all the state within.   */
+static void
+context_free (struct resolv_context *ctx)
+{
+  current = ctx->__next;
+  free (ctx);
+}
+
+/* Reuse the current context object.  */
+static struct resolv_context *
+context_reuse (void)
+{
+  /* A context object created by __resolv_context_get_override cannot
+     be reused.  */
+  assert (current->__from_res);
+
+  ++current->__refcount;
+
+  /* Check for reference counter wraparound.  This can only happen if
+     the get/put functions are not properly paired.  */
+  assert (current->__refcount > 0);
+
+  return current;
+}
+
+/* Backing function for the __resolv_context_get family of
+   functions.  */
+static struct resolv_context *
+context_get (bool preinit)
+{
+  if (current != NULL)
+    return context_reuse ();
+
+  struct resolv_context *ctx = context_alloc (&_res);
+  if (ctx == NULL)
+    return NULL;
+  if (!maybe_init (ctx->resp, preinit))
+    {
+      context_free (ctx);
+      return NULL;
+    }
+  return ctx;
+}
+
+struct resolv_context *
+__resolv_context_get (void)
+{
+  return context_get (false);
+}
+libc_hidden_def (__resolv_context_get)
+
+struct resolv_context *
+__resolv_context_get_preinit (void)
+{
+  return context_get (true);
+}
+libc_hidden_def (__resolv_context_get_preinit)
+
+struct resolv_context *
+__resolv_context_get_override (struct __res_state *resp)
+{
+  /* NB: As explained asbove, context_alloc will put the context on
+     the current list.  */
+  struct resolv_context *ctx = context_alloc (resp);
+  if (ctx == NULL)
+    return NULL;
+
+  ctx->__from_res = false;
+  return ctx;
+}
+libc_hidden_def (__resolv_context_get_override)
+
+void
+__resolv_context_put (struct resolv_context *ctx)
+{
+  if (ctx == NULL)
+    return;
+
+  /* NB: Callers assume that this function preserves errno and
+     h_errno.  */
+
+  assert (current == ctx);
+  assert (ctx->__refcount > 0);
+
+  if (ctx->__from_res && --ctx->__refcount > 0)
+    /* Do not pop this context yet.  */
+    return;
+
+  context_free (ctx);
+}
+libc_hidden_def (__resolv_context_put)
+
+void
+__resolv_context_freeres (void)
+{
+  /* Deallocate the entire chain of context objects.  */
+  struct resolv_context *ctx = current;
+  current = NULL;
+  while (ctx != NULL)
+    {
+      struct resolv_context *next = ctx->__next;
+      context_free (ctx);
+      ctx = next;
+    }
+}
diff --git a/resolv/resolv_context.h b/resolv/resolv_context.h
new file mode 100644 (file)
index 0000000..27c8d56
--- /dev/null
@@ -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 */
index 4fb1eaef79bc66a310f15cc00de3d063ae86d774..efa71184989900732ebc3fced4f1646b0927c727 100644 (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)
            {