1 | /* Temporary, thread-local resolver state. |
2 | Copyright (C) 2017-2023 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 mismatch 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 |
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 |
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 * |
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 |
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 * |
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 * |
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 * |
193 | __resolv_context_get (void) |
194 | { |
195 | return context_get (false); |
196 | } |
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 | } |
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 | } |
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 | } |
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 | |