]> git.ipfire.org Git - thirdparty/glibc.git/blame - resolv/resolv_conf.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / resolv / resolv_conf.c
CommitLineData
f30a54b2 1/* Extended resolver state separate from struct __res_state.
04277e02 2 Copyright (C) 2017-2019 Free Software Foundation, Inc.
f30a54b2
FW
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_conf.h>
20
21#include <alloc_buffer.h>
22#include <assert.h>
23#include <libc-lock.h>
24#include <resolv-internal.h>
aef16cc8 25#include <sys/stat.h>
2827ab99 26#include <libc-symbols.h>
f30a54b2
FW
27
28/* _res._u._ext.__glibc_extension_index is used as an index into a
29 struct resolv_conf_array object. The intent of this construction
30 is to make reasonably sure that even if struct __res_state objects
31 are copied around and patched by applications, we can still detect
e237357a
FW
32 accesses to stale extended resolver state. The array elements are
33 either struct resolv_conf * pointers (if the LSB is cleared) or
34 free list entries (if the LSB is set). The free list is used to
35 speed up finding available entries in the array. */
f30a54b2 36#define DYNARRAY_STRUCT resolv_conf_array
e237357a 37#define DYNARRAY_ELEMENT uintptr_t
f30a54b2
FW
38#define DYNARRAY_PREFIX resolv_conf_array_
39#define DYNARRAY_INITIAL_SIZE 0
40#include <malloc/dynarray-skeleton.c>
41
42/* A magic constant for XORing the extension index
43 (_res._u._ext.__glibc_extension_index). This makes it less likely
44 that a valid index is created by accident. In particular, a zero
45 value leads to an invalid index. */
46#define INDEX_MAGIC 0x26a8fa5e48af8061ULL
47
48/* Global resolv.conf-related state. */
49struct resolv_conf_global
50{
51 /* struct __res_state objects contain the extension index
52 (_res._u._ext.__glibc_extension_index ^ INDEX_MAGIC), which
53 refers to an element of this array. When a struct resolv_conf
54 object (extended resolver state) is associated with a struct
55 __res_state object (legacy resolver state), its reference count
56 is increased and added to this array. Conversely, if the
57 extended state is detached from the basic state (during
58 reinitialization or deallocation), the index is decremented, and
59 the array element is overwritten with NULL. */
60 struct resolv_conf_array array;
61
89f6307c
FW
62 /* Start of the free list in the array. Zero if the free list is
63 empty. Otherwise, free_list_start >> 1 is the first element of
64 the free list (and the free list entries all have their LSB set
65 and are shifted one to the left). */
e237357a
FW
66 uintptr_t free_list_start;
67
aef16cc8
FW
68 /* Cached current configuration object for /etc/resolv.conf. */
69 struct resolv_conf *conf_current;
70
71 /* These properties of /etc/resolv.conf are used to check if the
72 configuration needs reloading. */
73 struct timespec conf_mtime;
74 struct timespec conf_ctime;
75 off64_t conf_size;
76 ino64_t conf_ino;
f30a54b2
FW
77};
78
79/* Lazily allocated storage for struct resolv_conf_global. */
80static struct resolv_conf_global *global;
81
82/* The lock synchronizes access to global and *global. It also
83 protects the __refcount member of struct resolv_conf. */
84__libc_lock_define_initialized (static, lock);
85
86/* Ensure that GLOBAL is allocated and lock it. Return NULL if
87 memory allocation failes. */
88static struct resolv_conf_global *
89get_locked_global (void)
90{
91 __libc_lock_lock (lock);
92 /* Use relaxed MO through because of load outside the lock in
93 __resolv_conf_detach. */
94 struct resolv_conf_global *global_copy = atomic_load_relaxed (&global);
95 if (global_copy == NULL)
96 {
97 global_copy = calloc (1, sizeof (*global));
98 if (global_copy == NULL)
99 return NULL;
100 atomic_store_relaxed (&global, global_copy);
101 resolv_conf_array_init (&global_copy->array);
102 }
103 return global_copy;
104}
105
106/* Relinquish the lock acquired by get_locked_global. */
107static void
108put_locked_global (struct resolv_conf_global *global_copy)
109{
110 __libc_lock_unlock (lock);
111}
112
113/* Decrement the reference counter. The caller must acquire the lock
114 around the function call. */
115static void
116conf_decrement (struct resolv_conf *conf)
117{
118 assert (conf->__refcount > 0);
119 if (--conf->__refcount == 0)
120 free (conf);
121}
122
aef16cc8
FW
123struct resolv_conf *
124__resolv_conf_get_current (void)
125{
126 struct stat64 st;
127 if (stat64 (_PATH_RESCONF, &st) != 0)
128 {
129 switch (errno)
130 {
131 case EACCES:
132 case EISDIR:
133 case ELOOP:
134 case ENOENT:
135 case ENOTDIR:
136 case EPERM:
137 /* Ignore errors due to file system contents. */
138 memset (&st, 0, sizeof (st));
139 break;
140 default:
141 /* Other errors are fatal. */
142 return NULL;
143 }
144 }
145
146 struct resolv_conf_global *global_copy = get_locked_global ();
147 if (global_copy == NULL)
148 return NULL;
149 struct resolv_conf *conf;
150 if (global_copy->conf_current != NULL
151 && (global_copy->conf_mtime.tv_sec == st.st_mtim.tv_sec
152 && global_copy->conf_mtime.tv_nsec == st.st_mtim.tv_nsec
153 && global_copy->conf_ctime.tv_sec == st.st_ctim.tv_sec
154 && global_copy->conf_ctime.tv_nsec == st.st_ctim.tv_nsec
155 && global_copy->conf_ino == st.st_ino
156 && global_copy->conf_size == st.st_size))
157 /* We can reuse the cached configuration object. */
158 conf = global_copy->conf_current;
159 else
160 {
161 /* Parse configuration while holding the lock. This avoids
162 duplicate work. */
163 conf = __resolv_conf_load (NULL);
164 if (conf != NULL)
165 {
166 if (global_copy->conf_current != NULL)
167 conf_decrement (global_copy->conf_current);
168 global_copy->conf_current = conf; /* Takes ownership. */
169
170 /* Update file modification stamps. The configuration we
171 read could be a newer version of the file, but this does
172 not matter because this will lead to an extraneous reload
173 later. */
174 global_copy->conf_mtime = st.st_mtim;
175 global_copy->conf_ctime = st.st_ctim;
176 global_copy->conf_ino = st.st_ino;
177 global_copy->conf_size = st.st_size;
178 }
179 }
180
181 if (conf != NULL)
182 {
183 /* Return an additional reference. */
184 assert (conf->__refcount > 0);
185 ++conf->__refcount;
186 assert (conf->__refcount > 0);
187 }
188 put_locked_global (global_copy);
189 return conf;
190}
191
f30a54b2
FW
192/* Internal implementation of __resolv_conf_get, without validation
193 against *RESP. */
194static struct resolv_conf *
195resolv_conf_get_1 (const struct __res_state *resp)
196{
197 /* Not initialized, and therefore no assoicated context. */
198 if (!(resp->options & RES_INIT))
199 return NULL;
200
201 struct resolv_conf_global *global_copy = get_locked_global ();
202 if (global_copy == NULL)
203 /* A memory allocation failure here means that no associated
204 contexts exists, so returning NULL is correct. */
205 return NULL;
206 size_t index = resp->_u._ext.__glibc_extension_index ^ INDEX_MAGIC;
e237357a 207 struct resolv_conf *conf = NULL;
f30a54b2
FW
208 if (index < resolv_conf_array_size (&global_copy->array))
209 {
e237357a
FW
210 uintptr_t *slot = resolv_conf_array_at (&global_copy->array, index);
211 if (!(*slot & 1))
212 {
213 conf = (struct resolv_conf *) *slot;
214 assert (conf->__refcount > 0);
215 ++conf->__refcount;
216 }
f30a54b2 217 }
f30a54b2
FW
218 put_locked_global (global_copy);
219 return conf;
220}
221
a1c4eb87
FW
222/* Return true if both IPv4 addresses are equal. */
223static bool
224same_address_v4 (const struct sockaddr_in *left,
225 const struct sockaddr_in *right)
226{
227 return left->sin_addr.s_addr == right->sin_addr.s_addr
228 && left->sin_port == right->sin_port;
229}
230
231/* Return true if both IPv6 addresses are equal. This ignores the
232 flow label. */
233static bool
234same_address_v6 (const struct sockaddr_in6 *left,
235 const struct sockaddr_in6 *right)
236{
237 return memcmp (&left->sin6_addr, &right->sin6_addr,
238 sizeof (left->sin6_addr)) == 0
239 && left->sin6_port == right->sin6_port
240 && left->sin6_scope_id == right->sin6_scope_id;
241}
242
243static bool
244same_address (const struct sockaddr *left, const struct sockaddr *right)
245{
246 if (left->sa_family != right->sa_family)
247 return false;
248 switch (left->sa_family)
249 {
250 case AF_INET:
251 return same_address_v4 ((const struct sockaddr_in *) left,
252 (const struct sockaddr_in *) right);
253 case AF_INET6:
254 return same_address_v6 ((const struct sockaddr_in6 *) left,
255 (const struct sockaddr_in6 *) right);
256 }
257 return false;
258}
259
f30a54b2
FW
260/* Check that *RESP and CONF match. Used by __resolv_conf_get. */
261static bool
262resolv_conf_matches (const struct __res_state *resp,
263 const struct resolv_conf *conf)
264{
a1c4eb87
FW
265 /* NB: Do not compare the options, retrans, retry, ndots. These can
266 be changed by applicaiton. */
267
268 /* Check that the name servers in *RESP have not been modified by
269 the application. */
270 {
271 size_t nserv = conf->nameserver_list_size;
272 if (nserv > MAXNS)
273 nserv = MAXNS;
274 /* _ext.nscount is 0 until initialized by res_send.c. */
275 if (resp->nscount != nserv
4446a885 276 || (resp->_u._ext.nscount != 0 && resp->_u._ext.nscount != nserv))
a1c4eb87
FW
277 return false;
278 for (size_t i = 0; i < nserv; ++i)
279 {
280 if (resp->nsaddr_list[i].sin_family == 0)
281 {
282 if (resp->_u._ext.nsaddrs[i]->sin6_family != AF_INET6)
283 return false;
284 if (!same_address ((struct sockaddr *) resp->_u._ext.nsaddrs[i],
285 conf->nameserver_list[i]))
286 return false;
287 }
288 else if (resp->nsaddr_list[i].sin_family != AF_INET)
289 return false;
290 else if (!same_address ((struct sockaddr *) &resp->nsaddr_list[i],
291 conf->nameserver_list[i]))
292 return false;
293 }
294 }
295
3f853f22
FW
296 /* Check that the search list in *RESP has not been modified by the
297 application. */
298 {
4446a885
FW
299 if (resp->dnsrch[0] == NULL)
300 {
301 /* Empty search list. No default domain name. */
302 return conf->search_list_size == 0 && resp->defdname[0] == '\0';
303 }
304
305 if (resp->dnsrch[0] != resp->defdname)
306 /* If the search list is not empty, it must start with the
307 default domain name. */
308 return false;
309
310 size_t nsearch;
311 for (nsearch = 0; nsearch < MAXDNSRCH; ++nsearch)
312 if (resp->dnsrch[nsearch] == NULL)
313 break;
314 if (nsearch > MAXDNSRCH)
315 /* Search list is not null-terminated. */
3f853f22 316 return false;
4446a885 317
3f853f22
FW
318 size_t search_list_size = 0;
319 for (size_t i = 0; i < conf->search_list_size; ++i)
320 {
321 if (resp->dnsrch[i] != NULL)
322 {
323 search_list_size += strlen (resp->dnsrch[i]) + 1;
324 if (strcmp (resp->dnsrch[i], conf->search_list[i]) != 0)
325 return false;
326 }
327 else
328 {
329 /* resp->dnsrch is truncated if the number of elements
330 exceeds MAXDNSRCH, or if the combined storage space for
331 the search list exceeds what can be stored in
332 resp->defdname. */
333 if (i == MAXDNSRCH || search_list_size > sizeof (resp->dnsrch))
334 break;
335 /* Otherwise, a mismatch indicates a match failure. */
336 return false;
337 }
338 }
339 }
a1c4eb87
FW
340
341 /* Check that the sort list has not been modified. */
342 {
343 size_t nsort = conf->sort_list_size;
344 if (nsort > MAXRESOLVSORT)
345 nsort = MAXRESOLVSORT;
4446a885
FW
346 if (resp->nsort != nsort)
347 return false;
a1c4eb87
FW
348 for (size_t i = 0; i < nsort; ++i)
349 if (resp->sort_list[i].addr.s_addr != conf->sort_list[i].addr.s_addr
350 || resp->sort_list[i].mask != conf->sort_list[i].mask)
351 return false;
352 }
353
f30a54b2
FW
354 return true;
355}
356
357struct resolv_conf *
358__resolv_conf_get (struct __res_state *resp)
359{
360 struct resolv_conf *conf = resolv_conf_get_1 (resp);
361 if (conf == NULL)
362 return NULL;
363 if (resolv_conf_matches (resp, conf))
364 return conf;
365 __resolv_conf_put (conf);
366 return NULL;
367}
368
369void
370__resolv_conf_put (struct resolv_conf *conf)
371{
372 if (conf == NULL)
373 return;
374
375 __libc_lock_lock (lock);
376 conf_decrement (conf);
377 __libc_lock_unlock (lock);
378}
379
380struct resolv_conf *
381__resolv_conf_allocate (const struct resolv_conf *init)
382{
a1c4eb87
FW
383 /* Allocate in decreasing order of alignment. */
384 _Static_assert (__alignof__ (const char *const *)
385 <= __alignof__ (struct resolv_conf), "alignment");
386 _Static_assert (__alignof__ (struct sockaddr_in6)
387 <= __alignof__ (const char *const *), "alignment");
388 _Static_assert (__alignof__ (struct sockaddr_in)
389 == __alignof__ (struct sockaddr_in6), "alignment");
390 _Static_assert (__alignof__ (struct resolv_sortlist_entry)
391 <= __alignof__ (struct sockaddr_in), "alignment");
392
393 /* Space needed by the nameserver addresses. */
394 size_t address_space = 0;
395 for (size_t i = 0; i < init->nameserver_list_size; ++i)
396 if (init->nameserver_list[i]->sa_family == AF_INET)
397 address_space += sizeof (struct sockaddr_in);
398 else
399 {
400 assert (init->nameserver_list[i]->sa_family == AF_INET6);
401 address_space += sizeof (struct sockaddr_in6);
402 }
403
404 /* Space needed by the search list strings. */
3f853f22
FW
405 size_t string_space = 0;
406 for (size_t i = 0; i < init->search_list_size; ++i)
407 string_space += strlen (init->search_list[i]) + 1;
408
f30a54b2
FW
409 /* Allocate the buffer. */
410 void *ptr;
411 struct alloc_buffer buffer = alloc_buffer_allocate
3f853f22 412 (sizeof (struct resolv_conf)
a1c4eb87
FW
413 + init->nameserver_list_size * sizeof (init->nameserver_list[0])
414 + address_space
3f853f22 415 + init->search_list_size * sizeof (init->search_list[0])
a1c4eb87 416 + init->sort_list_size * sizeof (init->sort_list[0])
3f853f22 417 + string_space,
f30a54b2
FW
418 &ptr);
419 struct resolv_conf *conf
420 = alloc_buffer_alloc (&buffer, struct resolv_conf);
421 if (conf == NULL)
422 /* Memory allocation failure. */
423 return NULL;
424 assert (conf == ptr);
425
426 /* Initialize the contents. */
427 conf->__refcount = 1;
a1c4eb87
FW
428 conf->retrans = init->retrans;
429 conf->retry = init->retry;
430 conf->options = init->options;
431 conf->ndots = init->ndots;
f30a54b2 432
a1c4eb87
FW
433 /* Allocate the arrays with pointers. These must come first because
434 they have the highets alignment. */
435 conf->nameserver_list_size = init->nameserver_list_size;
436 const struct sockaddr **nameserver_array = alloc_buffer_alloc_array
437 (&buffer, const struct sockaddr *, init->nameserver_list_size);
438 conf->nameserver_list = nameserver_array;
439
440 conf->search_list_size = init->search_list_size;
441 const char **search_array = alloc_buffer_alloc_array
442 (&buffer, const char *, init->search_list_size);
443 conf->search_list = search_array;
444
445 /* Fill the name server list array. */
446 for (size_t i = 0; i < init->nameserver_list_size; ++i)
447 if (init->nameserver_list[i]->sa_family == AF_INET)
448 {
449 struct sockaddr_in *sa = alloc_buffer_alloc
450 (&buffer, struct sockaddr_in);
451 *sa = *(struct sockaddr_in *) init->nameserver_list[i];
452 nameserver_array[i] = (struct sockaddr *) sa;
453 }
454 else
455 {
456 struct sockaddr_in6 *sa = alloc_buffer_alloc
457 (&buffer, struct sockaddr_in6);
458 *sa = *(struct sockaddr_in6 *) init->nameserver_list[i];
459 nameserver_array[i] = (struct sockaddr *) sa;
460 }
461
462 /* Allocate and fill the sort list array. */
463 {
464 conf->sort_list_size = init->sort_list_size;
465 struct resolv_sortlist_entry *array = alloc_buffer_alloc_array
466 (&buffer, struct resolv_sortlist_entry, init->sort_list_size);
467 conf->sort_list = array;
468 for (size_t i = 0; i < init->sort_list_size; ++i)
469 array[i] = init->sort_list[i];
470 }
471
472 /* Fill the search list array. This must come last because the
473 strings are the least aligned part of the allocation. */
3f853f22 474 {
3f853f22 475 for (size_t i = 0; i < init->search_list_size; ++i)
a1c4eb87
FW
476 search_array[i] = alloc_buffer_copy_string
477 (&buffer, init->search_list[i]);
3f853f22
FW
478 }
479
f30a54b2
FW
480 assert (!alloc_buffer_has_failed (&buffer));
481 return conf;
482}
483
484/* Update *RESP from the extended state. */
485static __attribute__ ((nonnull (1, 2), warn_unused_result)) bool
486update_from_conf (struct __res_state *resp, const struct resolv_conf *conf)
487{
a1c4eb87
FW
488 resp->defdname[0] = '\0';
489 resp->pfcode = 0;
490 resp->_vcsock = -1;
491 resp->_flags = 0;
492 resp->ipv6_unavail = false;
493 resp->__glibc_unused_qhook = NULL;
494 resp->__glibc_unused_rhook = NULL;
495
496 resp->retrans = conf->retrans;
497 resp->retry = conf->retry;
498 resp->options = conf->options;
499 resp->ndots = conf->ndots;
500
501 /* Copy the name server addresses. */
502 {
503 resp->nscount = 0;
504 resp->_u._ext.nscount = 0;
505 size_t nserv = conf->nameserver_list_size;
506 if (nserv > MAXNS)
507 nserv = MAXNS;
508 for (size_t i = 0; i < nserv; i++)
509 {
510 if (conf->nameserver_list[i]->sa_family == AF_INET)
511 {
512 resp->nsaddr_list[i]
513 = *(struct sockaddr_in *)conf->nameserver_list[i];
514 resp->_u._ext.nsaddrs[i] = NULL;
515 }
516 else
517 {
518 assert (conf->nameserver_list[i]->sa_family == AF_INET6);
519 resp->nsaddr_list[i].sin_family = 0;
520 /* Make a defensive copy of the name server address, in
521 case the application overwrites it. */
522 struct sockaddr_in6 *sa = malloc (sizeof (*sa));
523 if (sa == NULL)
524 {
525 for (size_t j = 0; j < i; ++j)
526 free (resp->_u._ext.nsaddrs[j]);
527 return false;
528 }
529 *sa = *(struct sockaddr_in6 *)conf->nameserver_list[i];
530 resp->_u._ext.nsaddrs[i] = sa;
531 }
532 resp->_u._ext.nssocks[i] = -1;
533 }
534 resp->nscount = nserv;
535 /* Leave resp->_u._ext.nscount at 0. res_send.c handles this. */
536 }
537
3f853f22
FW
538 /* Fill in the prefix of the search list. It is truncated either at
539 MAXDNSRCH, or if reps->defdname has insufficient space. */
540 {
541 struct alloc_buffer buffer
542 = alloc_buffer_create (resp->defdname, sizeof (resp->defdname));
543 size_t size = conf->search_list_size;
544 size_t i;
545 for (i = 0; i < size && i < MAXDNSRCH; ++i)
546 {
547 resp->dnsrch[i] = alloc_buffer_copy_string
548 (&buffer, conf->search_list[i]);
549 if (resp->dnsrch[i] == NULL)
550 /* No more space in resp->defdname. Truncate. */
551 break;
552 }
553 resp->dnsrch[i] = NULL;
554 }
555
a1c4eb87
FW
556 /* Copy the sort list. */
557 {
558 size_t nsort = conf->sort_list_size;
559 if (nsort > MAXRESOLVSORT)
560 nsort = MAXRESOLVSORT;
561 for (size_t i = 0; i < nsort; ++i)
562 {
563 resp->sort_list[i].addr = conf->sort_list[i].addr;
564 resp->sort_list[i].mask = conf->sort_list[i].mask;
565 }
566 resp->nsort = nsort;
567 }
568
f30a54b2
FW
569 /* The overlapping parts of both configurations should agree after
570 initialization. */
571 assert (resolv_conf_matches (resp, conf));
572 return true;
573}
574
575/* Decrement the configuration object at INDEX and free it if the
576 reference counter reaches 0. *GLOBAL_COPY must be locked and
577 remains so. */
578static void
579decrement_at_index (struct resolv_conf_global *global_copy, size_t index)
580{
581 if (index < resolv_conf_array_size (&global_copy->array))
582 {
e237357a
FW
583 /* Index found. */
584 uintptr_t *slot = resolv_conf_array_at (&global_copy->array, index);
585 /* Check that the slot is not already part of the free list. */
586 if (!(*slot & 1))
f30a54b2 587 {
e237357a 588 struct resolv_conf *conf = (struct resolv_conf *) *slot;
f30a54b2 589 conf_decrement (conf);
e237357a 590 /* Put the slot onto the free list. */
89f6307c 591 *slot = global_copy->free_list_start;
e237357a 592 global_copy->free_list_start = (index << 1) | 1;
f30a54b2
FW
593 }
594 }
595}
596
597bool
598__resolv_conf_attach (struct __res_state *resp, struct resolv_conf *conf)
599{
600 assert (conf->__refcount > 0);
601
602 struct resolv_conf_global *global_copy = get_locked_global ();
603 if (global_copy == NULL)
a8304730 604 return false;
f30a54b2
FW
605
606 /* Try to find an unused index in the array. */
607 size_t index;
608 {
e237357a 609 if (global_copy->free_list_start & 1)
f30a54b2 610 {
e237357a
FW
611 /* Unlink from the free list. */
612 index = global_copy->free_list_start >> 1;
613 uintptr_t *slot = resolv_conf_array_at (&global_copy->array, index);
614 global_copy->free_list_start = *slot;
89f6307c
FW
615 assert (global_copy->free_list_start == 0
616 || global_copy->free_list_start & 1);
e237357a
FW
617 /* Install the configuration pointer. */
618 *slot = (uintptr_t) conf;
f30a54b2 619 }
e237357a 620 else
f30a54b2 621 {
e237357a 622 size_t size = resolv_conf_array_size (&global_copy->array);
f30a54b2 623 /* No usable index found. Increase the array size. */
e237357a 624 resolv_conf_array_add (&global_copy->array, (uintptr_t) conf);
f30a54b2
FW
625 if (resolv_conf_array_has_failed (&global_copy->array))
626 {
627 put_locked_global (global_copy);
628 __set_errno (ENOMEM);
629 return false;
630 }
631 /* The new array element was added at the end. */
632 index = size;
633 }
634 }
635
636 /* We have added a new reference to the object. */
637 ++conf->__refcount;
638 assert (conf->__refcount > 0);
639 put_locked_global (global_copy);
640
641 if (!update_from_conf (resp, conf))
642 {
643 /* Drop the reference we acquired. Reacquire the lock. The
644 object has already been allocated, so it cannot be NULL this
645 time. */
646 global_copy = get_locked_global ();
647 decrement_at_index (global_copy, index);
648 put_locked_global (global_copy);
649 return false;
650 }
651 resp->_u._ext.__glibc_extension_index = index ^ INDEX_MAGIC;
652
653 return true;
654}
655
656void
657__resolv_conf_detach (struct __res_state *resp)
658{
659 if (atomic_load_relaxed (&global) == NULL)
660 /* Detach operation after a shutdown, or without any prior
661 attachment. We cannot free the data (and there might not be
662 anything to free anyway). */
663 return;
664
665 struct resolv_conf_global *global_copy = get_locked_global ();
666 size_t index = resp->_u._ext.__glibc_extension_index ^ INDEX_MAGIC;
667 decrement_at_index (global_copy, index);
668
669 /* Clear the index field, so that accidental reuse is less
670 likely. */
671 resp->_u._ext.__glibc_extension_index = 0;
672
673 put_locked_global (global_copy);
674}
675
676/* Deallocate the global data. */
124e0258 677libc_freeres_fn (freeres)
f30a54b2
FW
678{
679 /* No locking because this function is supposed to be called when
680 the process has turned single-threaded. */
681 if (global == NULL)
682 return;
683
aef16cc8
FW
684 if (global->conf_current != NULL)
685 {
686 conf_decrement (global->conf_current);
687 global->conf_current = NULL;
688 }
689
f30a54b2
FW
690 /* Note that this frees only the array itself. The pointed-to
691 configuration objects should have been deallocated by res_nclose
692 and per-thread cleanup functions. */
693 resolv_conf_array_free (&global->array);
694
695 free (global);
696
697 /* Stop potential future __resolv_conf_detach calls from accessing
698 deallocated memory. */
699 global = NULL;
700}