1 /* Extended resolver state separate from struct __res_state.
2 Copyright (C) 2017 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 <http://www.gnu.org/licenses/>. */
19 #include <resolv_conf.h>
21 #include <alloc_buffer.h>
23 #include <libc-lock.h>
24 #include <resolv-internal.h>
27 /* _res._u._ext.__glibc_extension_index is used as an index into a
28 struct resolv_conf_array object. The intent of this construction
29 is to make reasonably sure that even if struct __res_state objects
30 are copied around and patched by applications, we can still detect
31 accesses to stale extended resolver state. The array elements are
32 either struct resolv_conf * pointers (if the LSB is cleared) or
33 free list entries (if the LSB is set). The free list is used to
34 speed up finding available entries in the array. */
35 #define DYNARRAY_STRUCT resolv_conf_array
36 #define DYNARRAY_ELEMENT uintptr_t
37 #define DYNARRAY_PREFIX resolv_conf_array_
38 #define DYNARRAY_INITIAL_SIZE 0
39 #include <malloc/dynarray-skeleton.c>
41 /* A magic constant for XORing the extension index
42 (_res._u._ext.__glibc_extension_index). This makes it less likely
43 that a valid index is created by accident. In particular, a zero
44 value leads to an invalid index. */
45 #define INDEX_MAGIC 0x26a8fa5e48af8061ULL
47 /* Global resolv.conf-related state. */
48 struct resolv_conf_global
50 /* struct __res_state objects contain the extension index
51 (_res._u._ext.__glibc_extension_index ^ INDEX_MAGIC), which
52 refers to an element of this array. When a struct resolv_conf
53 object (extended resolver state) is associated with a struct
54 __res_state object (legacy resolver state), its reference count
55 is increased and added to this array. Conversely, if the
56 extended state is detached from the basic state (during
57 reinitialization or deallocation), the index is decremented, and
58 the array element is overwritten with NULL. */
59 struct resolv_conf_array array
;
61 /* Start of the free list in the array. Zero if the free list is
62 empty. Otherwise, free_list_start >> 1 is the first element of
63 the free list (and the free list entries all have their LSB set
64 and are shifted one to the left). */
65 uintptr_t free_list_start
;
67 /* Cached current configuration object for /etc/resolv.conf. */
68 struct resolv_conf
*conf_current
;
70 /* These properties of /etc/resolv.conf are used to check if the
71 configuration needs reloading. */
72 struct timespec conf_mtime
;
73 struct timespec conf_ctime
;
78 /* Lazily allocated storage for struct resolv_conf_global. */
79 static struct resolv_conf_global
*global
;
81 /* The lock synchronizes access to global and *global. It also
82 protects the __refcount member of struct resolv_conf. */
83 __libc_lock_define_initialized (static, lock
);
85 /* Ensure that GLOBAL is allocated and lock it. Return NULL if
86 memory allocation failes. */
87 static struct resolv_conf_global
*
88 get_locked_global (void)
90 __libc_lock_lock (lock
);
91 /* Use relaxed MO through because of load outside the lock in
92 __resolv_conf_detach. */
93 struct resolv_conf_global
*global_copy
= atomic_load_relaxed (&global
);
94 if (global_copy
== NULL
)
96 global_copy
= calloc (1, sizeof (*global
));
97 if (global_copy
== NULL
)
99 atomic_store_relaxed (&global
, global_copy
);
100 resolv_conf_array_init (&global_copy
->array
);
105 /* Relinquish the lock acquired by get_locked_global. */
107 put_locked_global (struct resolv_conf_global
*global_copy
)
109 __libc_lock_unlock (lock
);
112 /* Decrement the reference counter. The caller must acquire the lock
113 around the function call. */
115 conf_decrement (struct resolv_conf
*conf
)
117 assert (conf
->__refcount
> 0);
118 if (--conf
->__refcount
== 0)
123 __resolv_conf_get_current (void)
126 if (stat64 (_PATH_RESCONF
, &st
) != 0)
136 /* Ignore errors due to file system contents. */
137 memset (&st
, 0, sizeof (st
));
140 /* Other errors are fatal. */
145 struct resolv_conf_global
*global_copy
= get_locked_global ();
146 if (global_copy
== NULL
)
148 struct resolv_conf
*conf
;
149 if (global_copy
->conf_current
!= NULL
150 && (global_copy
->conf_mtime
.tv_sec
== st
.st_mtim
.tv_sec
151 && global_copy
->conf_mtime
.tv_nsec
== st
.st_mtim
.tv_nsec
152 && global_copy
->conf_ctime
.tv_sec
== st
.st_ctim
.tv_sec
153 && global_copy
->conf_ctime
.tv_nsec
== st
.st_ctim
.tv_nsec
154 && global_copy
->conf_ino
== st
.st_ino
155 && global_copy
->conf_size
== st
.st_size
))
156 /* We can reuse the cached configuration object. */
157 conf
= global_copy
->conf_current
;
160 /* Parse configuration while holding the lock. This avoids
162 conf
= __resolv_conf_load (NULL
);
165 if (global_copy
->conf_current
!= NULL
)
166 conf_decrement (global_copy
->conf_current
);
167 global_copy
->conf_current
= conf
; /* Takes ownership. */
169 /* Update file modification stamps. The configuration we
170 read could be a newer version of the file, but this does
171 not matter because this will lead to an extraneous reload
173 global_copy
->conf_mtime
= st
.st_mtim
;
174 global_copy
->conf_ctime
= st
.st_ctim
;
175 global_copy
->conf_ino
= st
.st_ino
;
176 global_copy
->conf_size
= st
.st_size
;
182 /* Return an additional reference. */
183 assert (conf
->__refcount
> 0);
185 assert (conf
->__refcount
> 0);
187 put_locked_global (global_copy
);
191 /* Internal implementation of __resolv_conf_get, without validation
193 static struct resolv_conf
*
194 resolv_conf_get_1 (const struct __res_state
*resp
)
196 /* Not initialized, and therefore no assoicated context. */
197 if (!(resp
->options
& RES_INIT
))
200 struct resolv_conf_global
*global_copy
= get_locked_global ();
201 if (global_copy
== NULL
)
202 /* A memory allocation failure here means that no associated
203 contexts exists, so returning NULL is correct. */
205 size_t index
= resp
->_u
._ext
.__glibc_extension_index
^ INDEX_MAGIC
;
206 struct resolv_conf
*conf
= NULL
;
207 if (index
< resolv_conf_array_size (&global_copy
->array
))
209 uintptr_t *slot
= resolv_conf_array_at (&global_copy
->array
, index
);
212 conf
= (struct resolv_conf
*) *slot
;
213 assert (conf
->__refcount
> 0);
217 put_locked_global (global_copy
);
221 /* Return true if both IPv4 addresses are equal. */
223 same_address_v4 (const struct sockaddr_in
*left
,
224 const struct sockaddr_in
*right
)
226 return left
->sin_addr
.s_addr
== right
->sin_addr
.s_addr
227 && left
->sin_port
== right
->sin_port
;
230 /* Return true if both IPv6 addresses are equal. This ignores the
233 same_address_v6 (const struct sockaddr_in6
*left
,
234 const struct sockaddr_in6
*right
)
236 return memcmp (&left
->sin6_addr
, &right
->sin6_addr
,
237 sizeof (left
->sin6_addr
)) == 0
238 && left
->sin6_port
== right
->sin6_port
239 && left
->sin6_scope_id
== right
->sin6_scope_id
;
243 same_address (const struct sockaddr
*left
, const struct sockaddr
*right
)
245 if (left
->sa_family
!= right
->sa_family
)
247 switch (left
->sa_family
)
250 return same_address_v4 ((const struct sockaddr_in
*) left
,
251 (const struct sockaddr_in
*) right
);
253 return same_address_v6 ((const struct sockaddr_in6
*) left
,
254 (const struct sockaddr_in6
*) right
);
259 /* Check that *RESP and CONF match. Used by __resolv_conf_get. */
261 resolv_conf_matches (const struct __res_state
*resp
,
262 const struct resolv_conf
*conf
)
264 /* NB: Do not compare the options, retrans, retry, ndots. These can
265 be changed by applicaiton. */
267 /* Check that the name servers in *RESP have not been modified by
270 size_t nserv
= conf
->nameserver_list_size
;
273 /* _ext.nscount is 0 until initialized by res_send.c. */
274 if (resp
->nscount
!= nserv
275 || (resp
->_u
._ext
.nscount
!= 0 && resp
->_u
._ext
.nscount
!= nserv
))
277 for (size_t i
= 0; i
< nserv
; ++i
)
279 if (resp
->nsaddr_list
[i
].sin_family
== 0)
281 if (resp
->_u
._ext
.nsaddrs
[i
]->sin6_family
!= AF_INET6
)
283 if (!same_address ((struct sockaddr
*) resp
->_u
._ext
.nsaddrs
[i
],
284 conf
->nameserver_list
[i
]))
287 else if (resp
->nsaddr_list
[i
].sin_family
!= AF_INET
)
289 else if (!same_address ((struct sockaddr
*) &resp
->nsaddr_list
[i
],
290 conf
->nameserver_list
[i
]))
295 /* Check that the search list in *RESP has not been modified by the
298 if (resp
->dnsrch
[0] == NULL
)
300 /* Empty search list. No default domain name. */
301 return conf
->search_list_size
== 0 && resp
->defdname
[0] == '\0';
304 if (resp
->dnsrch
[0] != resp
->defdname
)
305 /* If the search list is not empty, it must start with the
306 default domain name. */
310 for (nsearch
= 0; nsearch
< MAXDNSRCH
; ++nsearch
)
311 if (resp
->dnsrch
[nsearch
] == NULL
)
313 if (nsearch
> MAXDNSRCH
)
314 /* Search list is not null-terminated. */
317 size_t search_list_size
= 0;
318 for (size_t i
= 0; i
< conf
->search_list_size
; ++i
)
320 if (resp
->dnsrch
[i
] != NULL
)
322 search_list_size
+= strlen (resp
->dnsrch
[i
]) + 1;
323 if (strcmp (resp
->dnsrch
[i
], conf
->search_list
[i
]) != 0)
328 /* resp->dnsrch is truncated if the number of elements
329 exceeds MAXDNSRCH, or if the combined storage space for
330 the search list exceeds what can be stored in
332 if (i
== MAXDNSRCH
|| search_list_size
> sizeof (resp
->dnsrch
))
334 /* Otherwise, a mismatch indicates a match failure. */
340 /* Check that the sort list has not been modified. */
342 size_t nsort
= conf
->sort_list_size
;
343 if (nsort
> MAXRESOLVSORT
)
344 nsort
= MAXRESOLVSORT
;
345 if (resp
->nsort
!= nsort
)
347 for (size_t i
= 0; i
< nsort
; ++i
)
348 if (resp
->sort_list
[i
].addr
.s_addr
!= conf
->sort_list
[i
].addr
.s_addr
349 || resp
->sort_list
[i
].mask
!= conf
->sort_list
[i
].mask
)
357 __resolv_conf_get (struct __res_state
*resp
)
359 struct resolv_conf
*conf
= resolv_conf_get_1 (resp
);
362 if (resolv_conf_matches (resp
, conf
))
364 __resolv_conf_put (conf
);
369 __resolv_conf_put (struct resolv_conf
*conf
)
374 __libc_lock_lock (lock
);
375 conf_decrement (conf
);
376 __libc_lock_unlock (lock
);
380 __resolv_conf_allocate (const struct resolv_conf
*init
)
382 /* Allocate in decreasing order of alignment. */
383 _Static_assert (__alignof__ (const char *const *)
384 <= __alignof__ (struct resolv_conf
), "alignment");
385 _Static_assert (__alignof__ (struct sockaddr_in6
)
386 <= __alignof__ (const char *const *), "alignment");
387 _Static_assert (__alignof__ (struct sockaddr_in
)
388 == __alignof__ (struct sockaddr_in6
), "alignment");
389 _Static_assert (__alignof__ (struct resolv_sortlist_entry
)
390 <= __alignof__ (struct sockaddr_in
), "alignment");
392 /* Space needed by the nameserver addresses. */
393 size_t address_space
= 0;
394 for (size_t i
= 0; i
< init
->nameserver_list_size
; ++i
)
395 if (init
->nameserver_list
[i
]->sa_family
== AF_INET
)
396 address_space
+= sizeof (struct sockaddr_in
);
399 assert (init
->nameserver_list
[i
]->sa_family
== AF_INET6
);
400 address_space
+= sizeof (struct sockaddr_in6
);
403 /* Space needed by the search list strings. */
404 size_t string_space
= 0;
405 for (size_t i
= 0; i
< init
->search_list_size
; ++i
)
406 string_space
+= strlen (init
->search_list
[i
]) + 1;
408 /* Allocate the buffer. */
410 struct alloc_buffer buffer
= alloc_buffer_allocate
411 (sizeof (struct resolv_conf
)
412 + init
->nameserver_list_size
* sizeof (init
->nameserver_list
[0])
414 + init
->search_list_size
* sizeof (init
->search_list
[0])
415 + init
->sort_list_size
* sizeof (init
->sort_list
[0])
418 struct resolv_conf
*conf
419 = alloc_buffer_alloc (&buffer
, struct resolv_conf
);
421 /* Memory allocation failure. */
423 assert (conf
== ptr
);
425 /* Initialize the contents. */
426 conf
->__refcount
= 1;
427 conf
->retrans
= init
->retrans
;
428 conf
->retry
= init
->retry
;
429 conf
->options
= init
->options
;
430 conf
->ndots
= init
->ndots
;
432 /* Allocate the arrays with pointers. These must come first because
433 they have the highets alignment. */
434 conf
->nameserver_list_size
= init
->nameserver_list_size
;
435 const struct sockaddr
**nameserver_array
= alloc_buffer_alloc_array
436 (&buffer
, const struct sockaddr
*, init
->nameserver_list_size
);
437 conf
->nameserver_list
= nameserver_array
;
439 conf
->search_list_size
= init
->search_list_size
;
440 const char **search_array
= alloc_buffer_alloc_array
441 (&buffer
, const char *, init
->search_list_size
);
442 conf
->search_list
= search_array
;
444 /* Fill the name server list array. */
445 for (size_t i
= 0; i
< init
->nameserver_list_size
; ++i
)
446 if (init
->nameserver_list
[i
]->sa_family
== AF_INET
)
448 struct sockaddr_in
*sa
= alloc_buffer_alloc
449 (&buffer
, struct sockaddr_in
);
450 *sa
= *(struct sockaddr_in
*) init
->nameserver_list
[i
];
451 nameserver_array
[i
] = (struct sockaddr
*) sa
;
455 struct sockaddr_in6
*sa
= alloc_buffer_alloc
456 (&buffer
, struct sockaddr_in6
);
457 *sa
= *(struct sockaddr_in6
*) init
->nameserver_list
[i
];
458 nameserver_array
[i
] = (struct sockaddr
*) sa
;
461 /* Allocate and fill the sort list array. */
463 conf
->sort_list_size
= init
->sort_list_size
;
464 struct resolv_sortlist_entry
*array
= alloc_buffer_alloc_array
465 (&buffer
, struct resolv_sortlist_entry
, init
->sort_list_size
);
466 conf
->sort_list
= array
;
467 for (size_t i
= 0; i
< init
->sort_list_size
; ++i
)
468 array
[i
] = init
->sort_list
[i
];
471 /* Fill the search list array. This must come last because the
472 strings are the least aligned part of the allocation. */
474 for (size_t i
= 0; i
< init
->search_list_size
; ++i
)
475 search_array
[i
] = alloc_buffer_copy_string
476 (&buffer
, init
->search_list
[i
]);
479 assert (!alloc_buffer_has_failed (&buffer
));
483 /* Update *RESP from the extended state. */
484 static __attribute__ ((nonnull (1, 2), warn_unused_result
)) bool
485 update_from_conf (struct __res_state
*resp
, const struct resolv_conf
*conf
)
487 resp
->defdname
[0] = '\0';
491 resp
->ipv6_unavail
= false;
492 resp
->__glibc_unused_qhook
= NULL
;
493 resp
->__glibc_unused_rhook
= NULL
;
495 resp
->retrans
= conf
->retrans
;
496 resp
->retry
= conf
->retry
;
497 resp
->options
= conf
->options
;
498 resp
->ndots
= conf
->ndots
;
500 /* Copy the name server addresses. */
503 resp
->_u
._ext
.nscount
= 0;
504 size_t nserv
= conf
->nameserver_list_size
;
507 for (size_t i
= 0; i
< nserv
; i
++)
509 if (conf
->nameserver_list
[i
]->sa_family
== AF_INET
)
512 = *(struct sockaddr_in
*)conf
->nameserver_list
[i
];
513 resp
->_u
._ext
.nsaddrs
[i
] = NULL
;
517 assert (conf
->nameserver_list
[i
]->sa_family
== AF_INET6
);
518 resp
->nsaddr_list
[i
].sin_family
= 0;
519 /* Make a defensive copy of the name server address, in
520 case the application overwrites it. */
521 struct sockaddr_in6
*sa
= malloc (sizeof (*sa
));
524 for (size_t j
= 0; j
< i
; ++j
)
525 free (resp
->_u
._ext
.nsaddrs
[j
]);
528 *sa
= *(struct sockaddr_in6
*)conf
->nameserver_list
[i
];
529 resp
->_u
._ext
.nsaddrs
[i
] = sa
;
531 resp
->_u
._ext
.nssocks
[i
] = -1;
533 resp
->nscount
= nserv
;
534 /* Leave resp->_u._ext.nscount at 0. res_send.c handles this. */
537 /* Fill in the prefix of the search list. It is truncated either at
538 MAXDNSRCH, or if reps->defdname has insufficient space. */
540 struct alloc_buffer buffer
541 = alloc_buffer_create (resp
->defdname
, sizeof (resp
->defdname
));
542 size_t size
= conf
->search_list_size
;
544 for (i
= 0; i
< size
&& i
< MAXDNSRCH
; ++i
)
546 resp
->dnsrch
[i
] = alloc_buffer_copy_string
547 (&buffer
, conf
->search_list
[i
]);
548 if (resp
->dnsrch
[i
] == NULL
)
549 /* No more space in resp->defdname. Truncate. */
552 resp
->dnsrch
[i
] = NULL
;
555 /* Copy the sort list. */
557 size_t nsort
= conf
->sort_list_size
;
558 if (nsort
> MAXRESOLVSORT
)
559 nsort
= MAXRESOLVSORT
;
560 for (size_t i
= 0; i
< nsort
; ++i
)
562 resp
->sort_list
[i
].addr
= conf
->sort_list
[i
].addr
;
563 resp
->sort_list
[i
].mask
= conf
->sort_list
[i
].mask
;
568 /* The overlapping parts of both configurations should agree after
570 assert (resolv_conf_matches (resp
, conf
));
574 /* Decrement the configuration object at INDEX and free it if the
575 reference counter reaches 0. *GLOBAL_COPY must be locked and
578 decrement_at_index (struct resolv_conf_global
*global_copy
, size_t index
)
580 if (index
< resolv_conf_array_size (&global_copy
->array
))
583 uintptr_t *slot
= resolv_conf_array_at (&global_copy
->array
, index
);
584 /* Check that the slot is not already part of the free list. */
587 struct resolv_conf
*conf
= (struct resolv_conf
*) *slot
;
588 conf_decrement (conf
);
589 /* Put the slot onto the free list. */
590 *slot
= global_copy
->free_list_start
;
591 global_copy
->free_list_start
= (index
<< 1) | 1;
597 __resolv_conf_attach (struct __res_state
*resp
, struct resolv_conf
*conf
)
599 assert (conf
->__refcount
> 0);
601 struct resolv_conf_global
*global_copy
= get_locked_global ();
602 if (global_copy
== NULL
)
605 /* Try to find an unused index in the array. */
608 if (global_copy
->free_list_start
& 1)
610 /* Unlink from the free list. */
611 index
= global_copy
->free_list_start
>> 1;
612 uintptr_t *slot
= resolv_conf_array_at (&global_copy
->array
, index
);
613 global_copy
->free_list_start
= *slot
;
614 assert (global_copy
->free_list_start
== 0
615 || global_copy
->free_list_start
& 1);
616 /* Install the configuration pointer. */
617 *slot
= (uintptr_t) conf
;
621 size_t size
= resolv_conf_array_size (&global_copy
->array
);
622 /* No usable index found. Increase the array size. */
623 resolv_conf_array_add (&global_copy
->array
, (uintptr_t) conf
);
624 if (resolv_conf_array_has_failed (&global_copy
->array
))
626 put_locked_global (global_copy
);
627 __set_errno (ENOMEM
);
630 /* The new array element was added at the end. */
635 /* We have added a new reference to the object. */
637 assert (conf
->__refcount
> 0);
638 put_locked_global (global_copy
);
640 if (!update_from_conf (resp
, conf
))
642 /* Drop the reference we acquired. Reacquire the lock. The
643 object has already been allocated, so it cannot be NULL this
645 global_copy
= get_locked_global ();
646 decrement_at_index (global_copy
, index
);
647 put_locked_global (global_copy
);
650 resp
->_u
._ext
.__glibc_extension_index
= index
^ INDEX_MAGIC
;
656 __resolv_conf_detach (struct __res_state
*resp
)
658 if (atomic_load_relaxed (&global
) == NULL
)
659 /* Detach operation after a shutdown, or without any prior
660 attachment. We cannot free the data (and there might not be
661 anything to free anyway). */
664 struct resolv_conf_global
*global_copy
= get_locked_global ();
665 size_t index
= resp
->_u
._ext
.__glibc_extension_index
^ INDEX_MAGIC
;
666 decrement_at_index (global_copy
, index
);
668 /* Clear the index field, so that accidental reuse is less
670 resp
->_u
._ext
.__glibc_extension_index
= 0;
672 put_locked_global (global_copy
);
675 /* Deallocate the global data. */
676 static void __attribute__ ((section ("__libc_thread_freeres_fn")))
679 /* No locking because this function is supposed to be called when
680 the process has turned single-threaded. */
684 if (global
->conf_current
!= NULL
)
686 conf_decrement (global
->conf_current
);
687 global
->conf_current
= NULL
;
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
);
697 /* Stop potential future __resolv_conf_detach calls from accessing
698 deallocated memory. */
701 text_set_element (__libc_subfreeres
, freeres
);