1 /* Extended resolver state separate from struct __res_state.
2 Copyright (C) 2017-2018 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>
26 #include <libc-symbols.h>
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
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. */
36 #define DYNARRAY_STRUCT resolv_conf_array
37 #define DYNARRAY_ELEMENT uintptr_t
38 #define DYNARRAY_PREFIX resolv_conf_array_
39 #define DYNARRAY_INITIAL_SIZE 0
40 #include <malloc/dynarray-skeleton.c>
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
48 /* Global resolv.conf-related state. */
49 struct resolv_conf_global
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
;
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). */
66 uintptr_t free_list_start
;
68 /* Cached current configuration object for /etc/resolv.conf. */
69 struct resolv_conf
*conf_current
;
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
;
79 /* Lazily allocated storage for struct resolv_conf_global. */
80 static struct resolv_conf_global
*global
;
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
);
86 /* Ensure that GLOBAL is allocated and lock it. Return NULL if
87 memory allocation failes. */
88 static struct resolv_conf_global
*
89 get_locked_global (void)
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
)
97 global_copy
= calloc (1, sizeof (*global
));
98 if (global_copy
== NULL
)
100 atomic_store_relaxed (&global
, global_copy
);
101 resolv_conf_array_init (&global_copy
->array
);
106 /* Relinquish the lock acquired by get_locked_global. */
108 put_locked_global (struct resolv_conf_global
*global_copy
)
110 __libc_lock_unlock (lock
);
113 /* Decrement the reference counter. The caller must acquire the lock
114 around the function call. */
116 conf_decrement (struct resolv_conf
*conf
)
118 assert (conf
->__refcount
> 0);
119 if (--conf
->__refcount
== 0)
124 __resolv_conf_get_current (void)
127 if (stat64 (_PATH_RESCONF
, &st
) != 0)
137 /* Ignore errors due to file system contents. */
138 memset (&st
, 0, sizeof (st
));
141 /* Other errors are fatal. */
146 struct resolv_conf_global
*global_copy
= get_locked_global ();
147 if (global_copy
== 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
;
161 /* Parse configuration while holding the lock. This avoids
163 conf
= __resolv_conf_load (NULL
);
166 if (global_copy
->conf_current
!= NULL
)
167 conf_decrement (global_copy
->conf_current
);
168 global_copy
->conf_current
= conf
; /* Takes ownership. */
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
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
;
183 /* Return an additional reference. */
184 assert (conf
->__refcount
> 0);
186 assert (conf
->__refcount
> 0);
188 put_locked_global (global_copy
);
192 /* Internal implementation of __resolv_conf_get, without validation
194 static struct resolv_conf
*
195 resolv_conf_get_1 (const struct __res_state
*resp
)
197 /* Not initialized, and therefore no assoicated context. */
198 if (!(resp
->options
& RES_INIT
))
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. */
206 size_t index
= resp
->_u
._ext
.__glibc_extension_index
^ INDEX_MAGIC
;
207 struct resolv_conf
*conf
= NULL
;
208 if (index
< resolv_conf_array_size (&global_copy
->array
))
210 uintptr_t *slot
= resolv_conf_array_at (&global_copy
->array
, index
);
213 conf
= (struct resolv_conf
*) *slot
;
214 assert (conf
->__refcount
> 0);
218 put_locked_global (global_copy
);
222 /* Return true if both IPv4 addresses are equal. */
224 same_address_v4 (const struct sockaddr_in
*left
,
225 const struct sockaddr_in
*right
)
227 return left
->sin_addr
.s_addr
== right
->sin_addr
.s_addr
228 && left
->sin_port
== right
->sin_port
;
231 /* Return true if both IPv6 addresses are equal. This ignores the
234 same_address_v6 (const struct sockaddr_in6
*left
,
235 const struct sockaddr_in6
*right
)
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
;
244 same_address (const struct sockaddr
*left
, const struct sockaddr
*right
)
246 if (left
->sa_family
!= right
->sa_family
)
248 switch (left
->sa_family
)
251 return same_address_v4 ((const struct sockaddr_in
*) left
,
252 (const struct sockaddr_in
*) right
);
254 return same_address_v6 ((const struct sockaddr_in6
*) left
,
255 (const struct sockaddr_in6
*) right
);
260 /* Check that *RESP and CONF match. Used by __resolv_conf_get. */
262 resolv_conf_matches (const struct __res_state
*resp
,
263 const struct resolv_conf
*conf
)
265 /* NB: Do not compare the options, retrans, retry, ndots. These can
266 be changed by applicaiton. */
268 /* Check that the name servers in *RESP have not been modified by
271 size_t nserv
= conf
->nameserver_list_size
;
274 /* _ext.nscount is 0 until initialized by res_send.c. */
275 if (resp
->nscount
!= nserv
276 || (resp
->_u
._ext
.nscount
!= 0 && resp
->_u
._ext
.nscount
!= nserv
))
278 for (size_t i
= 0; i
< nserv
; ++i
)
280 if (resp
->nsaddr_list
[i
].sin_family
== 0)
282 if (resp
->_u
._ext
.nsaddrs
[i
]->sin6_family
!= AF_INET6
)
284 if (!same_address ((struct sockaddr
*) resp
->_u
._ext
.nsaddrs
[i
],
285 conf
->nameserver_list
[i
]))
288 else if (resp
->nsaddr_list
[i
].sin_family
!= AF_INET
)
290 else if (!same_address ((struct sockaddr
*) &resp
->nsaddr_list
[i
],
291 conf
->nameserver_list
[i
]))
296 /* Check that the search list in *RESP has not been modified by the
299 if (resp
->dnsrch
[0] == NULL
)
301 /* Empty search list. No default domain name. */
302 return conf
->search_list_size
== 0 && resp
->defdname
[0] == '\0';
305 if (resp
->dnsrch
[0] != resp
->defdname
)
306 /* If the search list is not empty, it must start with the
307 default domain name. */
311 for (nsearch
= 0; nsearch
< MAXDNSRCH
; ++nsearch
)
312 if (resp
->dnsrch
[nsearch
] == NULL
)
314 if (nsearch
> MAXDNSRCH
)
315 /* Search list is not null-terminated. */
318 size_t search_list_size
= 0;
319 for (size_t i
= 0; i
< conf
->search_list_size
; ++i
)
321 if (resp
->dnsrch
[i
] != NULL
)
323 search_list_size
+= strlen (resp
->dnsrch
[i
]) + 1;
324 if (strcmp (resp
->dnsrch
[i
], conf
->search_list
[i
]) != 0)
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
333 if (i
== MAXDNSRCH
|| search_list_size
> sizeof (resp
->dnsrch
))
335 /* Otherwise, a mismatch indicates a match failure. */
341 /* Check that the sort list has not been modified. */
343 size_t nsort
= conf
->sort_list_size
;
344 if (nsort
> MAXRESOLVSORT
)
345 nsort
= MAXRESOLVSORT
;
346 if (resp
->nsort
!= nsort
)
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
)
358 __resolv_conf_get (struct __res_state
*resp
)
360 struct resolv_conf
*conf
= resolv_conf_get_1 (resp
);
363 if (resolv_conf_matches (resp
, conf
))
365 __resolv_conf_put (conf
);
370 __resolv_conf_put (struct resolv_conf
*conf
)
375 __libc_lock_lock (lock
);
376 conf_decrement (conf
);
377 __libc_lock_unlock (lock
);
381 __resolv_conf_allocate (const struct resolv_conf
*init
)
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");
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
);
400 assert (init
->nameserver_list
[i
]->sa_family
== AF_INET6
);
401 address_space
+= sizeof (struct sockaddr_in6
);
404 /* Space needed by the search list strings. */
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;
409 /* Allocate the buffer. */
411 struct alloc_buffer buffer
= alloc_buffer_allocate
412 (sizeof (struct resolv_conf
)
413 + init
->nameserver_list_size
* sizeof (init
->nameserver_list
[0])
415 + init
->search_list_size
* sizeof (init
->search_list
[0])
416 + init
->sort_list_size
* sizeof (init
->sort_list
[0])
419 struct resolv_conf
*conf
420 = alloc_buffer_alloc (&buffer
, struct resolv_conf
);
422 /* Memory allocation failure. */
424 assert (conf
== ptr
);
426 /* Initialize the contents. */
427 conf
->__refcount
= 1;
428 conf
->retrans
= init
->retrans
;
429 conf
->retry
= init
->retry
;
430 conf
->options
= init
->options
;
431 conf
->ndots
= init
->ndots
;
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
;
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
;
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
)
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
;
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
;
462 /* Allocate and fill the sort list array. */
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
];
472 /* Fill the search list array. This must come last because the
473 strings are the least aligned part of the allocation. */
475 for (size_t i
= 0; i
< init
->search_list_size
; ++i
)
476 search_array
[i
] = alloc_buffer_copy_string
477 (&buffer
, init
->search_list
[i
]);
480 assert (!alloc_buffer_has_failed (&buffer
));
484 /* Update *RESP from the extended state. */
485 static __attribute__ ((nonnull (1, 2), warn_unused_result
)) bool
486 update_from_conf (struct __res_state
*resp
, const struct resolv_conf
*conf
)
488 resp
->defdname
[0] = '\0';
492 resp
->ipv6_unavail
= false;
493 resp
->__glibc_unused_qhook
= NULL
;
494 resp
->__glibc_unused_rhook
= NULL
;
496 resp
->retrans
= conf
->retrans
;
497 resp
->retry
= conf
->retry
;
498 resp
->options
= conf
->options
;
499 resp
->ndots
= conf
->ndots
;
501 /* Copy the name server addresses. */
504 resp
->_u
._ext
.nscount
= 0;
505 size_t nserv
= conf
->nameserver_list_size
;
508 for (size_t i
= 0; i
< nserv
; i
++)
510 if (conf
->nameserver_list
[i
]->sa_family
== AF_INET
)
513 = *(struct sockaddr_in
*)conf
->nameserver_list
[i
];
514 resp
->_u
._ext
.nsaddrs
[i
] = NULL
;
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
));
525 for (size_t j
= 0; j
< i
; ++j
)
526 free (resp
->_u
._ext
.nsaddrs
[j
]);
529 *sa
= *(struct sockaddr_in6
*)conf
->nameserver_list
[i
];
530 resp
->_u
._ext
.nsaddrs
[i
] = sa
;
532 resp
->_u
._ext
.nssocks
[i
] = -1;
534 resp
->nscount
= nserv
;
535 /* Leave resp->_u._ext.nscount at 0. res_send.c handles this. */
538 /* Fill in the prefix of the search list. It is truncated either at
539 MAXDNSRCH, or if reps->defdname has insufficient space. */
541 struct alloc_buffer buffer
542 = alloc_buffer_create (resp
->defdname
, sizeof (resp
->defdname
));
543 size_t size
= conf
->search_list_size
;
545 for (i
= 0; i
< size
&& i
< MAXDNSRCH
; ++i
)
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. */
553 resp
->dnsrch
[i
] = NULL
;
556 /* Copy the sort list. */
558 size_t nsort
= conf
->sort_list_size
;
559 if (nsort
> MAXRESOLVSORT
)
560 nsort
= MAXRESOLVSORT
;
561 for (size_t i
= 0; i
< nsort
; ++i
)
563 resp
->sort_list
[i
].addr
= conf
->sort_list
[i
].addr
;
564 resp
->sort_list
[i
].mask
= conf
->sort_list
[i
].mask
;
569 /* The overlapping parts of both configurations should agree after
571 assert (resolv_conf_matches (resp
, conf
));
575 /* Decrement the configuration object at INDEX and free it if the
576 reference counter reaches 0. *GLOBAL_COPY must be locked and
579 decrement_at_index (struct resolv_conf_global
*global_copy
, size_t index
)
581 if (index
< resolv_conf_array_size (&global_copy
->array
))
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. */
588 struct resolv_conf
*conf
= (struct resolv_conf
*) *slot
;
589 conf_decrement (conf
);
590 /* Put the slot onto the free list. */
591 *slot
= global_copy
->free_list_start
;
592 global_copy
->free_list_start
= (index
<< 1) | 1;
598 __resolv_conf_attach (struct __res_state
*resp
, struct resolv_conf
*conf
)
600 assert (conf
->__refcount
> 0);
602 struct resolv_conf_global
*global_copy
= get_locked_global ();
603 if (global_copy
== NULL
)
606 /* Try to find an unused index in the array. */
609 if (global_copy
->free_list_start
& 1)
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
;
615 assert (global_copy
->free_list_start
== 0
616 || global_copy
->free_list_start
& 1);
617 /* Install the configuration pointer. */
618 *slot
= (uintptr_t) conf
;
622 size_t size
= resolv_conf_array_size (&global_copy
->array
);
623 /* No usable index found. Increase the array size. */
624 resolv_conf_array_add (&global_copy
->array
, (uintptr_t) conf
);
625 if (resolv_conf_array_has_failed (&global_copy
->array
))
627 put_locked_global (global_copy
);
628 __set_errno (ENOMEM
);
631 /* The new array element was added at the end. */
636 /* We have added a new reference to the object. */
638 assert (conf
->__refcount
> 0);
639 put_locked_global (global_copy
);
641 if (!update_from_conf (resp
, conf
))
643 /* Drop the reference we acquired. Reacquire the lock. The
644 object has already been allocated, so it cannot be NULL this
646 global_copy
= get_locked_global ();
647 decrement_at_index (global_copy
, index
);
648 put_locked_global (global_copy
);
651 resp
->_u
._ext
.__glibc_extension_index
= index
^ INDEX_MAGIC
;
657 __resolv_conf_detach (struct __res_state
*resp
)
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). */
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
);
669 /* Clear the index field, so that accidental reuse is less
671 resp
->_u
._ext
.__glibc_extension_index
= 0;
673 put_locked_global (global_copy
);
676 /* Deallocate the global data. */
677 libc_freeres_fn (freeres
)
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. */