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