1/* Temporary, thread-local resolver state.
2 Copyright (C) 2017-2018 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 <http://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. */
52static __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. */
59static bool
60replicated_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. */
70static __attribute__ ((warn_unused_result)) bool
71maybe_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. */
130static struct resolv_context *
131context_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. */
146static void
147context_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. */
157static struct resolv_context *
158context_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. */
175static struct resolv_context *
176context_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
192struct resolv_context *
193__resolv_context_get (void)
194{
195 return context_get (false);
196}
197libc_hidden_def (__resolv_context_get)
198
199struct resolv_context *
200__resolv_context_get_preinit (void)
201{
202 return context_get (true);
203}
204libc_hidden_def (__resolv_context_get_preinit)
205
206struct 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}
218libc_hidden_def (__resolv_context_get_override)
219
220void
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}
238libc_hidden_def (__resolv_context_put)
239
240void
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