]> git.ipfire.org Git - thirdparty/glibc.git/blob - nscd/nscd_gethst_r.c
Rename sys/ucontext.h to bits/ucontext.h.
[thirdparty/glibc.git] / nscd / nscd_gethst_r.c
1 /* Copyright (C) 1998-2020 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
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 <https://www.gnu.org/licenses/>. */
18
19 #include <errno.h>
20 #include <resolv/resolv-internal.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdint.h>
24 #include <sys/socket.h>
25 #include <arpa/nameser.h>
26 #include <not-cancel.h>
27
28 #include "nscd-client.h"
29 #include "nscd_proto.h"
30
31 int __nss_not_use_nscd_hosts;
32
33 static int nscd_gethst_r (const char *key, size_t keylen, request_type type,
34 struct hostent *resultbuf, char *buffer,
35 size_t buflen, struct hostent **result,
36 int *h_errnop);
37
38
39 int
40 __nscd_gethostbyname_r (const char *name, struct hostent *resultbuf,
41 char *buffer, size_t buflen, struct hostent **result,
42 int *h_errnop)
43 {
44 return nscd_gethst_r (name, strlen (name) + 1, GETHOSTBYNAME, resultbuf,
45 buffer, buflen, result, h_errnop);
46 }
47
48
49 int
50 __nscd_gethostbyname2_r (const char *name, int af, struct hostent *resultbuf,
51 char *buffer, size_t buflen, struct hostent **result,
52 int *h_errnop)
53 {
54 request_type reqtype;
55
56 reqtype = af == AF_INET6 ? GETHOSTBYNAMEv6 : GETHOSTBYNAME;
57
58 return nscd_gethst_r (name, strlen (name) + 1, reqtype, resultbuf,
59 buffer, buflen, result, h_errnop);
60 }
61
62
63 int
64 __nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type,
65 struct hostent *resultbuf, char *buffer, size_t buflen,
66 struct hostent **result, int *h_errnop)
67 {
68 request_type reqtype;
69
70 if (!((len == INADDRSZ && type == AF_INET)
71 || (len == IN6ADDRSZ && type == AF_INET6)))
72 /* LEN and TYPE do not match. */
73 return -1;
74
75 reqtype = type == AF_INET6 ? GETHOSTBYADDRv6 : GETHOSTBYADDR;
76
77 return nscd_gethst_r (addr, len, reqtype, resultbuf, buffer, buflen, result,
78 h_errnop);
79 }
80
81
82 libc_locked_map_ptr (, __hst_map_handle) attribute_hidden;
83 /* Note that we only free the structure if necessary. The memory
84 mapping is not removed since it is not visible to the malloc
85 handling. */
86 libc_freeres_fn (hst_map_free)
87 {
88 if (__hst_map_handle.mapped != NO_MAPPING)
89 {
90 void *p = __hst_map_handle.mapped;
91 __hst_map_handle.mapped = NO_MAPPING;
92 free (p);
93 }
94 }
95
96
97 uint32_t
98 __nscd_get_nl_timestamp (void)
99 {
100 uint32_t retval;
101 if (__nss_not_use_nscd_hosts != 0)
102 return 0;
103
104 /* __nscd_get_mapping can change hst_map_handle.mapped to NO_MAPPING.
105 However, __nscd_get_mapping assumes the prior value was not NO_MAPPING.
106 Thus we have to acquire the lock to prevent this thread from changing
107 hst_map_handle.mapped to NO_MAPPING while another thread is inside
108 __nscd_get_mapping. */
109 if (!__nscd_acquire_maplock (&__hst_map_handle))
110 return 0;
111
112 struct mapped_database *map = __hst_map_handle.mapped;
113
114 if (map == NULL
115 || (map != NO_MAPPING
116 && map->head->nscd_certainly_running == 0
117 && map->head->timestamp + MAPPING_TIMEOUT < time_now ()))
118 map = __nscd_get_mapping (GETFDHST, "hosts", &__hst_map_handle.mapped);
119
120 if (map == NO_MAPPING)
121 retval = 0;
122 else
123 retval = map->head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP];
124
125 /* Release the lock. */
126 __hst_map_handle.lock = 0;
127
128 return retval;
129 }
130
131
132 int __nss_have_localdomain attribute_hidden;
133
134 static int
135 nscd_gethst_r (const char *key, size_t keylen, request_type type,
136 struct hostent *resultbuf, char *buffer, size_t buflen,
137 struct hostent **result, int *h_errnop)
138 {
139 if (__glibc_unlikely (__nss_have_localdomain >= 0))
140 {
141 if (__nss_have_localdomain == 0)
142 __nss_have_localdomain = getenv ("LOCALDOMAIN") != NULL ? 1 : -1;
143 if (__nss_have_localdomain > 0)
144 {
145 __nss_not_use_nscd_hosts = 1;
146 return -1;
147 }
148 }
149
150 int gc_cycle;
151 int nretries = 0;
152
153 /* If the mapping is available, try to search there instead of
154 communicating with the nscd. */
155 struct mapped_database *mapped;
156 mapped = __nscd_get_map_ref (GETFDHST, "hosts", &__hst_map_handle,
157 &gc_cycle);
158
159 retry:;
160 const char *h_name = NULL;
161 const uint32_t *aliases_len = NULL;
162 const char *addr_list = NULL;
163 size_t addr_list_len = 0;
164 int retval = -1;
165 const char *recend = (const char *) ~UINTMAX_C (0);
166 int sock = -1;
167 hst_response_header hst_resp;
168 if (mapped != NO_MAPPING)
169 {
170 /* No const qualifier, as it can change during garbage collection. */
171 struct datahead *found = __nscd_cache_search (type, key, keylen, mapped,
172 sizeof hst_resp);
173 if (found != NULL)
174 {
175 h_name = (char *) (&found->data[0].hstdata + 1);
176 hst_resp = found->data[0].hstdata;
177 aliases_len = (uint32_t *) (h_name + hst_resp.h_name_len);
178 addr_list = ((char *) aliases_len
179 + hst_resp.h_aliases_cnt * sizeof (uint32_t));
180 addr_list_len = hst_resp.h_addr_list_cnt * INADDRSZ;
181 recend = (const char *) found->data + found->recsize;
182 /* Now check if we can trust hst_resp fields. If GC is
183 in progress, it can contain anything. */
184 if (mapped->head->gc_cycle != gc_cycle)
185 {
186 retval = -2;
187 goto out;
188 }
189
190 #if !_STRING_ARCH_unaligned
191 /* The aliases_len array in the mapped database might very
192 well be unaligned. We will access it word-wise so on
193 platforms which do not tolerate unaligned accesses we
194 need to make an aligned copy. */
195 if (((uintptr_t) aliases_len & (__alignof__ (*aliases_len) - 1))
196 != 0)
197 {
198 uint32_t *tmp = alloca (hst_resp.h_aliases_cnt
199 * sizeof (uint32_t));
200 aliases_len = memcpy (tmp, aliases_len,
201 hst_resp.h_aliases_cnt
202 * sizeof (uint32_t));
203 }
204 #endif
205 if (type != GETHOSTBYADDR && type != GETHOSTBYNAME)
206 {
207 if (hst_resp.h_length == INADDRSZ)
208 addr_list += addr_list_len;
209 addr_list_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
210 }
211 if (__builtin_expect ((const char *) addr_list + addr_list_len
212 > recend, 0))
213 goto out;
214 }
215 }
216
217 if (h_name == NULL)
218 {
219 sock = __nscd_open_socket (key, keylen, type, &hst_resp,
220 sizeof (hst_resp));
221 if (sock == -1)
222 {
223 __nss_not_use_nscd_hosts = 1;
224 goto out;
225 }
226 }
227
228 /* No value found so far. */
229 *result = NULL;
230
231 if (__glibc_unlikely (hst_resp.found == -1))
232 {
233 /* The daemon does not cache this database. */
234 __nss_not_use_nscd_hosts = 1;
235 goto out_close;
236 }
237
238 if (hst_resp.found == 1)
239 {
240 char *cp = buffer;
241 uintptr_t align1;
242 uintptr_t align2;
243 size_t total_len;
244 ssize_t cnt;
245 char *ignore;
246 int n;
247
248 /* A first check whether the buffer is sufficiently large is possible. */
249 /* Now allocate the buffer the array for the group members. We must
250 align the pointer and the base of the h_addr_list pointers. */
251 align1 = ((__alignof__ (char *) - (cp - ((char *) 0)))
252 & (__alignof__ (char *) - 1));
253 align2 = ((__alignof__ (char *) - ((cp + align1 + hst_resp.h_name_len)
254 - ((char *) 0)))
255 & (__alignof__ (char *) - 1));
256 if (buflen < (align1 + hst_resp.h_name_len + align2
257 + ((hst_resp.h_aliases_cnt + hst_resp.h_addr_list_cnt
258 + 2)
259 * sizeof (char *))
260 + hst_resp.h_addr_list_cnt * (type == AF_INET
261 ? INADDRSZ : IN6ADDRSZ)))
262 {
263 no_room:
264 *h_errnop = NETDB_INTERNAL;
265 __set_errno (ERANGE);
266 retval = ERANGE;
267 goto out_close;
268 }
269 cp += align1;
270
271 /* Prepare the result as far as we can. */
272 resultbuf->h_aliases = (char **) cp;
273 cp += (hst_resp.h_aliases_cnt + 1) * sizeof (char *);
274 resultbuf->h_addr_list = (char **) cp;
275 cp += (hst_resp.h_addr_list_cnt + 1) * sizeof (char *);
276
277 resultbuf->h_name = cp;
278 cp += hst_resp.h_name_len + align2;
279
280 if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
281 {
282 resultbuf->h_addrtype = AF_INET;
283 resultbuf->h_length = INADDRSZ;
284 }
285 else
286 {
287 resultbuf->h_addrtype = AF_INET6;
288 resultbuf->h_length = IN6ADDRSZ;
289 }
290 for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
291 {
292 resultbuf->h_addr_list[cnt] = cp;
293 cp += resultbuf->h_length;
294 }
295 resultbuf->h_addr_list[cnt] = NULL;
296
297 if (h_name == NULL)
298 {
299 struct iovec vec[4];
300
301 vec[0].iov_base = resultbuf->h_name;
302 vec[0].iov_len = hst_resp.h_name_len;
303 total_len = hst_resp.h_name_len;
304 n = 1;
305
306 if (hst_resp.h_aliases_cnt > 0)
307 {
308 aliases_len = alloca (hst_resp.h_aliases_cnt
309 * sizeof (uint32_t));
310 vec[n].iov_base = (void *) aliases_len;
311 vec[n].iov_len = hst_resp.h_aliases_cnt * sizeof (uint32_t);
312
313 total_len += hst_resp.h_aliases_cnt * sizeof (uint32_t);
314 ++n;
315 }
316
317 if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
318 {
319 vec[n].iov_base = resultbuf->h_addr_list[0];
320 vec[n].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
321
322 total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
323
324 ++n;
325 }
326 else
327 {
328 if (hst_resp.h_length == INADDRSZ)
329 {
330 ignore = alloca (hst_resp.h_addr_list_cnt * INADDRSZ);
331 vec[n].iov_base = ignore;
332 vec[n].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
333
334 total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
335
336 ++n;
337 }
338
339 vec[n].iov_base = resultbuf->h_addr_list[0];
340 vec[n].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
341
342 total_len += hst_resp.h_addr_list_cnt * IN6ADDRSZ;
343
344 ++n;
345 }
346
347 if ((size_t) __readvall (sock, vec, n) != total_len)
348 goto out_close;
349 }
350 else
351 {
352 memcpy (resultbuf->h_name, h_name, hst_resp.h_name_len);
353 memcpy (resultbuf->h_addr_list[0], addr_list, addr_list_len);
354 }
355
356 /* Now we also can read the aliases. */
357 total_len = 0;
358 for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt)
359 {
360 resultbuf->h_aliases[cnt] = cp;
361 cp += aliases_len[cnt];
362 total_len += aliases_len[cnt];
363 }
364 resultbuf->h_aliases[cnt] = NULL;
365
366 if (__builtin_expect ((const char *) addr_list + addr_list_len
367 + total_len > recend, 0))
368 {
369 /* aliases_len array might contain garbage during nscd GC cycle,
370 retry rather than fail in that case. */
371 if (addr_list != NULL && mapped->head->gc_cycle != gc_cycle)
372 retval = -2;
373 goto out_close;
374 }
375 /* See whether this would exceed the buffer capacity. */
376 if (__glibc_unlikely (cp > buffer + buflen))
377 {
378 /* aliases_len array might contain garbage during nscd GC cycle,
379 retry rather than fail in that case. */
380 if (addr_list != NULL && mapped->head->gc_cycle != gc_cycle)
381 {
382 retval = -2;
383 goto out_close;
384 }
385 goto no_room;
386 }
387
388 /* And finally read the aliases. */
389 if (addr_list == NULL)
390 {
391 if (total_len == 0
392 || ((size_t) __readall (sock, resultbuf->h_aliases[0], total_len)
393 == total_len))
394 {
395 retval = 0;
396 *result = resultbuf;
397 }
398 }
399 else
400 {
401 memcpy (resultbuf->h_aliases[0],
402 (const char *) addr_list + addr_list_len, total_len);
403
404 /* Try to detect corrupt databases. */
405 if (resultbuf->h_name[hst_resp.h_name_len - 1] != '\0'
406 || ({for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt)
407 if (resultbuf->h_aliases[cnt][aliases_len[cnt] - 1]
408 != '\0')
409 break;
410 cnt < hst_resp.h_aliases_cnt; }))
411 {
412 /* We cannot use the database. */
413 if (mapped->head->gc_cycle != gc_cycle)
414 retval = -2;
415 goto out_close;
416 }
417
418 retval = 0;
419 *result = resultbuf;
420 }
421 }
422 else
423 {
424 /* Store the error number. */
425 *h_errnop = hst_resp.error;
426
427 /* Set errno to 0 to indicate no error, just no found record. */
428 __set_errno (0);
429 /* Even though we have not found anything, the result is zero. */
430 retval = 0;
431 }
432
433 out_close:
434 if (sock != -1)
435 __close_nocancel_nostatus (sock);
436 out:
437 if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
438 {
439 /* When we come here this means there has been a GC cycle while we
440 were looking for the data. This means the data might have been
441 inconsistent. Retry if possible. */
442 if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
443 {
444 /* nscd is just running gc now. Disable using the mapping. */
445 if (atomic_decrement_val (&mapped->counter) == 0)
446 __nscd_unmap (mapped);
447 mapped = NO_MAPPING;
448 }
449
450 if (retval != -1)
451 goto retry;
452 }
453
454 return retval;
455 }