1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
/* 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_conf.h>
#include <resolv-internal.h>
#include <assert.h>
#include <errno.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 resolv_context *ctx, bool preinit)
{
struct __res_state *resp = ctx->resp;
if (resp->options & RES_INIT)
{
/* If there is no associated resolv_conf object despite the
initialization, something modified *ctx->resp. Do not
override those changes. */
if (ctx->conf != NULL && ctx->conf->initstamp != __res_initstamp)
{
if (resp->nscount > 0)
/* This call will detach the extended resolver state. */
__res_iclose (resp, true);
/* And this call will attach it again. */
if (__res_vinit (resp, 1) < 0)
{
/* The configuration no longer matches after failed
initialization. */
__resolv_conf_put (ctx->conf);
ctx->conf = NULL;
return false;
}
/* Delay the release of the old configuration until this
point, so that __res_vinit can reuse it if possible. */
__resolv_conf_put (ctx->conf);
ctx->conf = __resolv_conf_get (ctx->resp);
}
return true;
}
assert (ctx->conf == NULL);
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 ();
}
if (__res_vinit (resp, preinit) < 0)
return false;
ctx->conf = __resolv_conf_get (ctx->resp);
return true;
}
/* 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->conf = __resolv_conf_get (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)
{
int error_code = errno;
current = ctx->__next;
__resolv_conf_put (ctx->conf);
free (ctx);
__set_errno (error_code);
}
/* 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, 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;
}
}
|