1 /* Extended resolver state separate from struct __res_state.
2 Copyright (C) 2017-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
19 #include <resolv_conf.h>
21 #include <alloc_buffer.h>
23 #include <libc-lock.h>
24 #include <resolv-internal.h>
26 #include <libc-symbols.h>
27 #include <file_change_detection.h>
29 /* _res._u._ext.__glibc_extension_index is used as an index into a
30 struct resolv_conf_array object. The intent of this construction
31 is to make reasonably sure that even if struct __res_state objects
32 are copied around and patched by applications, we can still detect
33 accesses to stale extended resolver state. The array elements are
34 either struct resolv_conf * pointers (if the LSB is cleared) or
35 free list entries (if the LSB is set). The free list is used to
36 speed up finding available entries in the array. */
37 #define DYNARRAY_STRUCT resolv_conf_array
38 #define DYNARRAY_ELEMENT uintptr_t
39 #define DYNARRAY_PREFIX resolv_conf_array_
40 #define DYNARRAY_INITIAL_SIZE 0
41 #include <malloc/dynarray-skeleton.c>
43 /* A magic constant for XORing the extension index
44 (_res._u._ext.__glibc_extension_index). This makes it less likely
45 that a valid index is created by accident. In particular, a zero
46 value leads to an invalid index. */
47 #define INDEX_MAGIC 0x26a8fa5e48af8061ULL
49 /* Global resolv.conf-related state. */
50 struct resolv_conf_global
52 /* struct __res_state objects contain the extension index
53 (_res._u._ext.__glibc_extension_index ^ INDEX_MAGIC), which
54 refers to an element of this array. When a struct resolv_conf
55 object (extended resolver state) is associated with a struct
56 __res_state object (legacy resolver state), its reference count
57 is increased and added to this array. Conversely, if the
58 extended state is detached from the basic state (during
59 reinitialization or deallocation), the index is decremented, and
60 the array element is overwritten with NULL. */
61 struct resolv_conf_array array
;
63 /* Start of the free list in the array. Zero if the free list is
64 empty. Otherwise, free_list_start >> 1 is the first element of
65 the free list (and the free list entries all have their LSB set
66 and are shifted one to the left). */
67 uintptr_t free_list_start
;
69 /* Cached current configuration object for /etc/resolv.conf. */
70 struct resolv_conf
*conf_current
;
72 /* File system identification for /etc/resolv.conf. */
73 struct file_change_detection file_resolve_conf
;
76 /* Lazily allocated storage for struct resolv_conf_global. */
77 static struct resolv_conf_global
*global
;
79 /* The lock synchronizes access to global and *global. It also
80 protects the __refcount member of struct resolv_conf. */
81 __libc_lock_define_initialized (static, lock
);
83 /* Ensure that GLOBAL is allocated and lock it. Return NULL if
84 memory allocation failes. */
85 static struct resolv_conf_global
*
86 get_locked_global (void)
88 __libc_lock_lock (lock
);
89 /* Use relaxed MO through because of load outside the lock in
90 __resolv_conf_detach. */
91 struct resolv_conf_global
*global_copy
= atomic_load_relaxed (&global
);
92 if (global_copy
== NULL
)
94 global_copy
= calloc (1, sizeof (*global
));
95 if (global_copy
== NULL
)
97 atomic_store_relaxed (&global
, global_copy
);
98 resolv_conf_array_init (&global_copy
->array
);
103 /* Relinquish the lock acquired by get_locked_global. */
105 put_locked_global (struct resolv_conf_global
*global_copy
)
107 __libc_lock_unlock (lock
);
110 /* Decrement the reference counter. The caller must acquire the lock
111 around the function call. */
113 conf_decrement (struct resolv_conf
*conf
)
115 assert (conf
->__refcount
> 0);
116 if (--conf
->__refcount
== 0)
121 __resolv_conf_get_current (void)
123 struct file_change_detection initial
;
124 if (!__file_change_detection_for_path (&initial
, _PATH_RESCONF
))
127 struct resolv_conf_global
*global_copy
= get_locked_global ();
128 if (global_copy
== NULL
)
130 struct resolv_conf
*conf
;
131 if (global_copy
->conf_current
!= NULL
132 && __file_is_unchanged (&initial
, &global_copy
->file_resolve_conf
))
133 /* We can reuse the cached configuration object. */
134 conf
= global_copy
->conf_current
;
137 /* Parse configuration while holding the lock. This avoids
139 struct file_change_detection after_load
;
140 conf
= __resolv_conf_load (NULL
, &after_load
);
143 if (global_copy
->conf_current
!= NULL
)
144 conf_decrement (global_copy
->conf_current
);
145 global_copy
->conf_current
= conf
; /* Takes ownership. */
147 /* Update file change detection data, but only if it matches
148 the initial measurement. This avoids an ABA race in case
149 /etc/resolv.conf is temporarily replaced while the file
150 is read (after the initial measurement), and restored to
151 the initial version later. */
152 if (__file_is_unchanged (&initial
, &after_load
))
153 global_copy
->file_resolve_conf
= after_load
;
155 /* If there is a discrepancy, trigger a reload during the
157 global_copy
->file_resolve_conf
.size
= -1;
163 /* Return an additional reference. */
164 assert (conf
->__refcount
> 0);
166 assert (conf
->__refcount
> 0);
168 put_locked_global (global_copy
);
172 /* Internal implementation of __resolv_conf_get, without validation
174 static struct resolv_conf
*
175 resolv_conf_get_1 (const struct __res_state
*resp
)
177 /* Not initialized, and therefore no assoicated context. */
178 if (!(resp
->options
& RES_INIT
))
181 struct resolv_conf_global
*global_copy
= get_locked_global ();
182 if (global_copy
== NULL
)
183 /* A memory allocation failure here means that no associated
184 contexts exists, so returning NULL is correct. */
186 size_t index
= resp
->_u
._ext
.__glibc_extension_index
^ INDEX_MAGIC
;
187 struct resolv_conf
*conf
= NULL
;
188 if (index
< resolv_conf_array_size (&global_copy
->array
))
190 uintptr_t *slot
= resolv_conf_array_at (&global_copy
->array
, index
);
193 conf
= (struct resolv_conf
*) *slot
;
194 assert (conf
->__refcount
> 0);
198 put_locked_global (global_copy
);
202 /* Return true if both IPv4 addresses are equal. */
204 same_address_v4 (const struct sockaddr_in
*left
,
205 const struct sockaddr_in
*right
)
207 return left
->sin_addr
.s_addr
== right
->sin_addr
.s_addr
208 && left
->sin_port
== right
->sin_port
;
211 /* Return true if both IPv6 addresses are equal. This ignores the
214 same_address_v6 (const struct sockaddr_in6
*left
,
215 const struct sockaddr_in6
*right
)
217 return memcmp (&left
->sin6_addr
, &right
->sin6_addr
,
218 sizeof (left
->sin6_addr
)) == 0
219 && left
->sin6_port
== right
->sin6_port
220 && left
->sin6_scope_id
== right
->sin6_scope_id
;
224 same_address (const struct sockaddr
*left
, const struct sockaddr
*right
)
226 if (left
->sa_family
!= right
->sa_family
)
228 switch (left
->sa_family
)
231 return same_address_v4 ((const struct sockaddr_in
*) left
,
232 (const struct sockaddr_in
*) right
);
234 return same_address_v6 ((const struct sockaddr_in6
*) left
,
235 (const struct sockaddr_in6
*) right
);
240 /* Check that *RESP and CONF match. Used by __resolv_conf_get. */
242 resolv_conf_matches (const struct __res_state
*resp
,
243 const struct resolv_conf
*conf
)
245 /* NB: Do not compare the options, retrans, retry, ndots. These can
246 be changed by applicaiton. */
248 /* Check that the name servers in *RESP have not been modified by
251 size_t nserv
= conf
->nameserver_list_size
;
254 /* _ext.nscount is 0 until initialized by res_send.c. */
255 if (resp
->nscount
!= nserv
256 || (resp
->_u
._ext
.nscount
!= 0 && resp
->_u
._ext
.nscount
!= nserv
))
258 for (size_t i
= 0; i
< nserv
; ++i
)
260 if (resp
->nsaddr_list
[i
].sin_family
== 0)
262 if (resp
->_u
._ext
.nsaddrs
[i
]->sin6_family
!= AF_INET6
)
264 if (!same_address ((struct sockaddr
*) resp
->_u
._ext
.nsaddrs
[i
],
265 conf
->nameserver_list
[i
]))
268 else if (resp
->nsaddr_list
[i
].sin_family
!= AF_INET
)
270 else if (!same_address ((struct sockaddr
*) &resp
->nsaddr_list
[i
],
271 conf
->nameserver_list
[i
]))
276 /* Check that the search list in *RESP has not been modified by the
279 if (resp
->dnsrch
[0] == NULL
)
281 /* Empty search list. No default domain name. */
282 return conf
->search_list_size
== 0 && resp
->defdname
[0] == '\0';
285 if (resp
->dnsrch
[0] != resp
->defdname
)
286 /* If the search list is not empty, it must start with the
287 default domain name. */
291 for (nsearch
= 0; nsearch
< MAXDNSRCH
; ++nsearch
)
292 if (resp
->dnsrch
[nsearch
] == NULL
)
294 if (nsearch
> MAXDNSRCH
)
295 /* Search list is not null-terminated. */
298 size_t search_list_size
= 0;
299 for (size_t i
= 0; i
< conf
->search_list_size
; ++i
)
301 if (resp
->dnsrch
[i
] != NULL
)
303 search_list_size
+= strlen (resp
->dnsrch
[i
]) + 1;
304 if (strcmp (resp
->dnsrch
[i
], conf
->search_list
[i
]) != 0)
309 /* resp->dnsrch is truncated if the number of elements
310 exceeds MAXDNSRCH, or if the combined storage space for
311 the search list exceeds what can be stored in
313 if (i
== MAXDNSRCH
|| search_list_size
> sizeof (resp
->dnsrch
))
315 /* Otherwise, a mismatch indicates a match failure. */
321 /* Check that the sort list has not been modified. */
323 size_t nsort
= conf
->sort_list_size
;
324 if (nsort
> MAXRESOLVSORT
)
325 nsort
= MAXRESOLVSORT
;
326 if (resp
->nsort
!= nsort
)
328 for (size_t i
= 0; i
< nsort
; ++i
)
329 if (resp
->sort_list
[i
].addr
.s_addr
!= conf
->sort_list
[i
].addr
.s_addr
330 || resp
->sort_list
[i
].mask
!= conf
->sort_list
[i
].mask
)
338 __resolv_conf_get (struct __res_state
*resp
)
340 struct resolv_conf
*conf
= resolv_conf_get_1 (resp
);
343 if (resolv_conf_matches (resp
, conf
))
345 __resolv_conf_put (conf
);
350 __resolv_conf_put (struct resolv_conf
*conf
)
355 __libc_lock_lock (lock
);
356 conf_decrement (conf
);
357 __libc_lock_unlock (lock
);
361 __resolv_conf_allocate (const struct resolv_conf
*init
)
363 /* Allocate in decreasing order of alignment. */
364 _Static_assert (__alignof__ (const char *const *)
365 <= __alignof__ (struct resolv_conf
), "alignment");
366 _Static_assert (__alignof__ (struct sockaddr_in6
)
367 <= __alignof__ (const char *const *), "alignment");
368 _Static_assert (__alignof__ (struct sockaddr_in
)
369 == __alignof__ (struct sockaddr_in6
), "alignment");
370 _Static_assert (__alignof__ (struct resolv_sortlist_entry
)
371 <= __alignof__ (struct sockaddr_in
), "alignment");
373 /* Space needed by the nameserver addresses. */
374 size_t address_space
= 0;
375 for (size_t i
= 0; i
< init
->nameserver_list_size
; ++i
)
376 if (init
->nameserver_list
[i
]->sa_family
== AF_INET
)
377 address_space
+= sizeof (struct sockaddr_in
);
380 assert (init
->nameserver_list
[i
]->sa_family
== AF_INET6
);
381 address_space
+= sizeof (struct sockaddr_in6
);
384 /* Space needed by the search list strings. */
385 size_t string_space
= 0;
386 for (size_t i
= 0; i
< init
->search_list_size
; ++i
)
387 string_space
+= strlen (init
->search_list
[i
]) + 1;
389 /* Allocate the buffer. */
391 struct alloc_buffer buffer
= alloc_buffer_allocate
392 (sizeof (struct resolv_conf
)
393 + init
->nameserver_list_size
* sizeof (init
->nameserver_list
[0])
395 + init
->search_list_size
* sizeof (init
->search_list
[0])
396 + init
->sort_list_size
* sizeof (init
->sort_list
[0])
399 struct resolv_conf
*conf
400 = alloc_buffer_alloc (&buffer
, struct resolv_conf
);
402 /* Memory allocation failure. */
404 assert (conf
== ptr
);
406 /* Initialize the contents. */
407 conf
->__refcount
= 1;
408 conf
->retrans
= init
->retrans
;
409 conf
->retry
= init
->retry
;
410 conf
->options
= init
->options
;
411 conf
->ndots
= init
->ndots
;
413 /* Allocate the arrays with pointers. These must come first because
414 they have the highets alignment. */
415 conf
->nameserver_list_size
= init
->nameserver_list_size
;
416 const struct sockaddr
**nameserver_array
= alloc_buffer_alloc_array
417 (&buffer
, const struct sockaddr
*, init
->nameserver_list_size
);
418 conf
->nameserver_list
= nameserver_array
;
420 conf
->search_list_size
= init
->search_list_size
;
421 const char **search_array
= alloc_buffer_alloc_array
422 (&buffer
, const char *, init
->search_list_size
);
423 conf
->search_list
= search_array
;
425 /* Fill the name server list array. */
426 for (size_t i
= 0; i
< init
->nameserver_list_size
; ++i
)
427 if (init
->nameserver_list
[i
]->sa_family
== AF_INET
)
429 struct sockaddr_in
*sa
= alloc_buffer_alloc
430 (&buffer
, struct sockaddr_in
);
431 *sa
= *(struct sockaddr_in
*) init
->nameserver_list
[i
];
432 nameserver_array
[i
] = (struct sockaddr
*) sa
;
436 struct sockaddr_in6
*sa
= alloc_buffer_alloc
437 (&buffer
, struct sockaddr_in6
);
438 *sa
= *(struct sockaddr_in6
*) init
->nameserver_list
[i
];
439 nameserver_array
[i
] = (struct sockaddr
*) sa
;
442 /* Allocate and fill the sort list array. */
444 conf
->sort_list_size
= init
->sort_list_size
;
445 struct resolv_sortlist_entry
*array
= alloc_buffer_alloc_array
446 (&buffer
, struct resolv_sortlist_entry
, init
->sort_list_size
);
447 conf
->sort_list
= array
;
448 for (size_t i
= 0; i
< init
->sort_list_size
; ++i
)
449 array
[i
] = init
->sort_list
[i
];
452 /* Fill the search list array. This must come last because the
453 strings are the least aligned part of the allocation. */
455 for (size_t i
= 0; i
< init
->search_list_size
; ++i
)
456 search_array
[i
] = alloc_buffer_copy_string
457 (&buffer
, init
->search_list
[i
]);
460 assert (!alloc_buffer_has_failed (&buffer
));
464 /* Update *RESP from the extended state. */
465 static __attribute__ ((nonnull (1, 2), warn_unused_result
)) bool
466 update_from_conf (struct __res_state
*resp
, const struct resolv_conf
*conf
)
468 resp
->defdname
[0] = '\0';
472 resp
->ipv6_unavail
= false;
473 resp
->__glibc_unused_qhook
= NULL
;
474 resp
->__glibc_unused_rhook
= NULL
;
476 resp
->retrans
= conf
->retrans
;
477 resp
->retry
= conf
->retry
;
478 resp
->options
= conf
->options
;
479 resp
->ndots
= conf
->ndots
;
481 /* Copy the name server addresses. */
484 resp
->_u
._ext
.nscount
= 0;
485 size_t nserv
= conf
->nameserver_list_size
;
488 for (size_t i
= 0; i
< nserv
; i
++)
490 if (conf
->nameserver_list
[i
]->sa_family
== AF_INET
)
493 = *(struct sockaddr_in
*)conf
->nameserver_list
[i
];
494 resp
->_u
._ext
.nsaddrs
[i
] = NULL
;
498 assert (conf
->nameserver_list
[i
]->sa_family
== AF_INET6
);
499 resp
->nsaddr_list
[i
].sin_family
= 0;
500 /* Make a defensive copy of the name server address, in
501 case the application overwrites it. */
502 struct sockaddr_in6
*sa
= malloc (sizeof (*sa
));
505 for (size_t j
= 0; j
< i
; ++j
)
506 free (resp
->_u
._ext
.nsaddrs
[j
]);
509 *sa
= *(struct sockaddr_in6
*)conf
->nameserver_list
[i
];
510 resp
->_u
._ext
.nsaddrs
[i
] = sa
;
512 resp
->_u
._ext
.nssocks
[i
] = -1;
514 resp
->nscount
= nserv
;
515 /* Leave resp->_u._ext.nscount at 0. res_send.c handles this. */
518 /* Fill in the prefix of the search list. It is truncated either at
519 MAXDNSRCH, or if reps->defdname has insufficient space. */
521 struct alloc_buffer buffer
522 = alloc_buffer_create (resp
->defdname
, sizeof (resp
->defdname
));
523 size_t size
= conf
->search_list_size
;
525 for (i
= 0; i
< size
&& i
< MAXDNSRCH
; ++i
)
527 resp
->dnsrch
[i
] = alloc_buffer_copy_string
528 (&buffer
, conf
->search_list
[i
]);
529 if (resp
->dnsrch
[i
] == NULL
)
530 /* No more space in resp->defdname. Truncate. */
533 resp
->dnsrch
[i
] = NULL
;
536 /* Copy the sort list. */
538 size_t nsort
= conf
->sort_list_size
;
539 if (nsort
> MAXRESOLVSORT
)
540 nsort
= MAXRESOLVSORT
;
541 for (size_t i
= 0; i
< nsort
; ++i
)
543 resp
->sort_list
[i
].addr
= conf
->sort_list
[i
].addr
;
544 resp
->sort_list
[i
].mask
= conf
->sort_list
[i
].mask
;
549 /* The overlapping parts of both configurations should agree after
551 assert (resolv_conf_matches (resp
, conf
));
555 /* Decrement the configuration object at INDEX and free it if the
556 reference counter reaches 0. *GLOBAL_COPY must be locked and
559 decrement_at_index (struct resolv_conf_global
*global_copy
, size_t index
)
561 if (index
< resolv_conf_array_size (&global_copy
->array
))
564 uintptr_t *slot
= resolv_conf_array_at (&global_copy
->array
, index
);
565 /* Check that the slot is not already part of the free list. */
568 struct resolv_conf
*conf
= (struct resolv_conf
*) *slot
;
569 conf_decrement (conf
);
570 /* Put the slot onto the free list. */
571 *slot
= global_copy
->free_list_start
;
572 global_copy
->free_list_start
= (index
<< 1) | 1;
578 __resolv_conf_attach (struct __res_state
*resp
, struct resolv_conf
*conf
)
580 assert (conf
->__refcount
> 0);
582 struct resolv_conf_global
*global_copy
= get_locked_global ();
583 if (global_copy
== NULL
)
586 /* Try to find an unused index in the array. */
589 if (global_copy
->free_list_start
& 1)
591 /* Unlink from the free list. */
592 index
= global_copy
->free_list_start
>> 1;
593 uintptr_t *slot
= resolv_conf_array_at (&global_copy
->array
, index
);
594 global_copy
->free_list_start
= *slot
;
595 assert (global_copy
->free_list_start
== 0
596 || global_copy
->free_list_start
& 1);
597 /* Install the configuration pointer. */
598 *slot
= (uintptr_t) conf
;
602 size_t size
= resolv_conf_array_size (&global_copy
->array
);
603 /* No usable index found. Increase the array size. */
604 resolv_conf_array_add (&global_copy
->array
, (uintptr_t) conf
);
605 if (resolv_conf_array_has_failed (&global_copy
->array
))
607 put_locked_global (global_copy
);
608 __set_errno (ENOMEM
);
611 /* The new array element was added at the end. */
616 /* We have added a new reference to the object. */
618 assert (conf
->__refcount
> 0);
619 put_locked_global (global_copy
);
621 if (!update_from_conf (resp
, conf
))
623 /* Drop the reference we acquired. Reacquire the lock. The
624 object has already been allocated, so it cannot be NULL this
626 global_copy
= get_locked_global ();
627 decrement_at_index (global_copy
, index
);
628 put_locked_global (global_copy
);
631 resp
->_u
._ext
.__glibc_extension_index
= index
^ INDEX_MAGIC
;
637 __resolv_conf_detach (struct __res_state
*resp
)
639 if (atomic_load_relaxed (&global
) == NULL
)
640 /* Detach operation after a shutdown, or without any prior
641 attachment. We cannot free the data (and there might not be
642 anything to free anyway). */
645 struct resolv_conf_global
*global_copy
= get_locked_global ();
646 size_t index
= resp
->_u
._ext
.__glibc_extension_index
^ INDEX_MAGIC
;
647 decrement_at_index (global_copy
, index
);
649 /* Clear the index field, so that accidental reuse is less
651 resp
->_u
._ext
.__glibc_extension_index
= 0;
653 put_locked_global (global_copy
);
656 /* Deallocate the global data. */
657 libc_freeres_fn (freeres
)
659 /* No locking because this function is supposed to be called when
660 the process has turned single-threaded. */
664 if (global
->conf_current
!= NULL
)
666 conf_decrement (global
->conf_current
);
667 global
->conf_current
= NULL
;
670 /* Note that this frees only the array itself. The pointed-to
671 configuration objects should have been deallocated by res_nclose
672 and per-thread cleanup functions. */
673 resolv_conf_array_free (&global
->array
);
677 /* Stop potential future __resolv_conf_detach calls from accessing
678 deallocated memory. */