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