1 /* Temporary, thread-local resolver state.
2    Copyright (C) 2017-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <resolv_context.h>
20 #include <resolv_conf.h>
21 #include <resolv-internal.h>
22 
23 #include <assert.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 
28 /* Currently active struct resolv_context object.  This pointer forms
29    the start of a single-linked list, using the __next member of
30    struct resolv_context.  This list serves two purposes:
31 
32    (a) A subsequent call to __resolv_context_get will only increment
33        the reference counter and will not allocate a new object.  The
34        _res state freshness check is skipped in this case, too.
35 
36    (b) The per-thread cleanup function defined by the resolver calls
37        __resolv_context_freeres, which will deallocate all the context
38        objects.  This avoids the need for cancellation handlers and
39        the complexity they bring, but it requires heap allocation of
40        the context object because the per-thread cleanup functions run
41        only after the stack has been fully unwound (and all on-stack
42        objects have been deallocated at this point).
43 
44    The TLS variable current is updated even in
45    __resolv_context_get_override, to support case (b) above.  This does
46    not override the per-thread resolver state (as obtained by the
47    non-res_state function such as __resolv_context_get) in an
48    observable way because the wrapped context is only used to
49    implement the res_n* functions in the resolver, and those do not
50    call back into user code which could indirectly use the per-thread
51    resolver state.  */
52 static __thread struct resolv_context *current attribute_tls_model_ie;
53 
54 /* The resolv_conf handling will gives us a ctx->conf pointer even if
55    these fields do not match because a mis-match does not cause a loss
56    of state (_res objects can store the full information).  This
57    function checks to ensure that there is a full patch, to prevent
58    overwriting a patched configuration.  */
59 static bool
replicated_configuration_matches(const struct resolv_context * ctx)60 replicated_configuration_matches (const struct resolv_context *ctx)
61 {
62   return ctx->resp->options == ctx->conf->options
63     && ctx->resp->retrans == ctx->conf->retrans
64     && ctx->resp->retry == ctx->conf->retry
65     && ctx->resp->ndots == ctx->conf->ndots;
66 }
67 
68 /* Initialize *RESP if RES_INIT is not yet set in RESP->options, or if
69    res_init in some other thread requested re-initializing.  */
70 static __attribute__ ((warn_unused_result)) bool
maybe_init(struct resolv_context * ctx,bool preinit)71 maybe_init (struct resolv_context *ctx, bool preinit)
72 {
73   struct __res_state *resp = ctx->resp;
74   if (resp->options & RES_INIT)
75     {
76       if (resp->options & RES_NORELOAD)
77         /* Configuration reloading was explicitly disabled.  */
78         return true;
79 
80       /* If there is no associated resolv_conf object despite the
81          initialization, something modified *ctx->resp.  Do not
82          override those changes.  */
83       if (ctx->conf != NULL && replicated_configuration_matches (ctx))
84         {
85           struct resolv_conf *current = __resolv_conf_get_current ();
86           if (current == NULL)
87             return false;
88 
89           /* Check if the configuration changed.  */
90           if (current != ctx->conf)
91             {
92               /* This call will detach the extended resolver state.  */
93               if (resp->nscount > 0)
94                 __res_iclose (resp, true);
95               /* Reattach the current configuration.  */
96               if (__resolv_conf_attach (ctx->resp, current))
97                 {
98                   __resolv_conf_put (ctx->conf);
99                   /* ctx takes ownership, so we do not release current.  */
100                   ctx->conf = current;
101                 }
102             }
103           else
104             /* No change.  Drop the reference count for current.  */
105             __resolv_conf_put (current);
106         }
107       return true;
108     }
109 
110   assert (ctx->conf == NULL);
111   if (preinit)
112     {
113       if (!resp->retrans)
114         resp->retrans = RES_TIMEOUT;
115       if (!resp->retry)
116         resp->retry = RES_DFLRETRY;
117       resp->options = RES_DEFAULT;
118       if (!resp->id)
119         resp->id = res_randomid ();
120     }
121 
122   if (__res_vinit (resp, preinit) < 0)
123     return false;
124   ctx->conf = __resolv_conf_get (ctx->resp);
125   return true;
126 }
127 
128 /* Allocate a new context object and initialize it.  The object is put
129    on the current list.  */
130 static struct resolv_context *
context_alloc(struct __res_state * resp)131 context_alloc (struct __res_state *resp)
132 {
133   struct resolv_context *ctx = malloc (sizeof (*ctx));
134   if (ctx == NULL)
135     return NULL;
136   ctx->resp = resp;
137   ctx->conf = __resolv_conf_get (resp);
138   ctx->__refcount = 1;
139   ctx->__from_res = true;
140   ctx->__next = current;
141   current = ctx;
142   return ctx;
143 }
144 
145 /* Deallocate the context object and all the state within.   */
146 static void
context_free(struct resolv_context * ctx)147 context_free (struct resolv_context *ctx)
148 {
149   int error_code = errno;
150   current = ctx->__next;
151   __resolv_conf_put (ctx->conf);
152   free (ctx);
153   __set_errno (error_code);
154 }
155 
156 /* Reuse the current context object.  */
157 static struct resolv_context *
context_reuse(void)158 context_reuse (void)
159 {
160   /* A context object created by __resolv_context_get_override cannot
161      be reused.  */
162   assert (current->__from_res);
163 
164   ++current->__refcount;
165 
166   /* Check for reference counter wraparound.  This can only happen if
167      the get/put functions are not properly paired.  */
168   assert (current->__refcount > 0);
169 
170   return current;
171 }
172 
173 /* Backing function for the __resolv_context_get family of
174    functions.  */
175 static struct resolv_context *
context_get(bool preinit)176 context_get (bool preinit)
177 {
178   if (current != NULL)
179     return context_reuse ();
180 
181   struct resolv_context *ctx = context_alloc (&_res);
182   if (ctx == NULL)
183     return NULL;
184   if (!maybe_init (ctx, preinit))
185     {
186       context_free (ctx);
187       return NULL;
188     }
189   return ctx;
190 }
191 
192 struct resolv_context *
__resolv_context_get(void)193 __resolv_context_get (void)
194 {
195   return context_get (false);
196 }
libc_hidden_def(__resolv_context_get)197 libc_hidden_def (__resolv_context_get)
198 
199 struct resolv_context *
200 __resolv_context_get_preinit (void)
201 {
202   return context_get (true);
203 }
libc_hidden_def(__resolv_context_get_preinit)204 libc_hidden_def (__resolv_context_get_preinit)
205 
206 struct resolv_context *
207 __resolv_context_get_override (struct __res_state *resp)
208 {
209   /* NB: As explained asbove, context_alloc will put the context on
210      the current list.  */
211   struct resolv_context *ctx = context_alloc (resp);
212   if (ctx == NULL)
213     return NULL;
214 
215   ctx->__from_res = false;
216   return ctx;
217 }
libc_hidden_def(__resolv_context_get_override)218 libc_hidden_def (__resolv_context_get_override)
219 
220 void
221 __resolv_context_put (struct resolv_context *ctx)
222 {
223   if (ctx == NULL)
224     return;
225 
226   /* NB: Callers assume that this function preserves errno and
227      h_errno.  */
228 
229   assert (current == ctx);
230   assert (ctx->__refcount > 0);
231 
232   if (ctx->__from_res && --ctx->__refcount > 0)
233     /* Do not pop this context yet.  */
234     return;
235 
236   context_free (ctx);
237 }
libc_hidden_def(__resolv_context_put)238 libc_hidden_def (__resolv_context_put)
239 
240 void
241 __resolv_context_freeres (void)
242 {
243   /* Deallocate the entire chain of context objects.  */
244   struct resolv_context *ctx = current;
245   current = NULL;
246   while (ctx != NULL)
247     {
248       struct resolv_context *next = ctx->__next;
249       context_free (ctx);
250       ctx = next;
251     }
252 }
253