fdwalk should return 0 on an empty directory
[kopensolaris-gnu/glibc.git] / nss / nss_files / files-hosts.c
1 /* Hosts file parser in nss_files module.
2    Copyright (C) 1996-2001, 2003, 2004, 2006, 2007
3    Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
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 <assert.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <arpa/nameser.h>
25 #include <netdb.h>
26 #include <resolv.h>
27
28
29 /* Get implementation for some internal functions.  */
30 #include "../resolv/mapv4v6addr.h"
31 #include "../resolv/res_hconf.h"
32
33
34 #define ENTNAME         hostent
35 #define DATABASE        "hosts"
36 #define NEED_H_ERRNO
37
38 #define EXTRA_ARGS       , af, flags
39 #define EXTRA_ARGS_DECL  , int af, int flags
40
41 #define ENTDATA hostent_data
42 struct hostent_data
43   {
44     unsigned char host_addr[16]; /* IPv4 or IPv6 address.  */
45     char *h_addr_ptrs[2];       /* Points to that and null terminator.  */
46   };
47
48 #define TRAILING_LIST_MEMBER            h_aliases
49 #define TRAILING_LIST_SEPARATOR_P       isspace
50 #include "files-parse.c"
51 LINE_PARSER
52 ("#",
53  {
54    char *addr;
55
56    STRING_FIELD (addr, isspace, 1);
57
58    /* Parse address.  */
59    if (inet_pton (af, addr, entdata->host_addr) <= 0)
60      {
61        if (af == AF_INET6 && (flags & AI_V4MAPPED) != 0
62            && inet_pton (AF_INET, addr, entdata->host_addr) > 0)
63          map_v4v6_address ((char *) entdata->host_addr,
64                            (char *) entdata->host_addr);
65        else if (af == AF_INET
66                 && inet_pton (AF_INET6, addr, entdata->host_addr) > 0)
67          {
68            if (IN6_IS_ADDR_V4MAPPED (entdata->host_addr))
69              memcpy (entdata->host_addr, entdata->host_addr + 12, INADDRSZ);
70            else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
71              {
72                in_addr_t localhost = htonl (INADDR_LOOPBACK);
73                memcpy (entdata->host_addr, &localhost, sizeof (localhost));
74              }
75            else
76              /* Illegal address: ignore line.  */
77              return 0;
78          }
79        else
80          /* Illegal address: ignore line.  */
81          return 0;
82      }
83
84    /* We always return entries of the requested form.  */
85    result->h_addrtype = af;
86    result->h_length = af == AF_INET ? INADDRSZ : IN6ADDRSZ;
87
88    /* Store a pointer to the address in the expected form.  */
89    entdata->h_addr_ptrs[0] = (char *) entdata->host_addr;
90    entdata->h_addr_ptrs[1] = NULL;
91    result->h_addr_list = entdata->h_addr_ptrs;
92
93    STRING_FIELD (result->h_name, isspace, 1);
94  })
95
96
97
98 #define HOST_DB_LOOKUP(name, keysize, keypattern, break_if_match, proto...) \
99 enum nss_status                                                               \
100 _nss_files_get##name##_r (proto,                                              \
101                           struct STRUCTURE *result, char *buffer,             \
102                           size_t buflen, int *errnop H_ERRNO_PROTO)           \
103 {                                                                             \
104   enum nss_status status;                                                     \
105                                                                               \
106   uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);    \
107   buffer += pad;                                                              \
108   buflen = buflen > pad ? buflen - pad : 0;                                   \
109                                                                               \
110   __libc_lock_lock (lock);                                                    \
111                                                                               \
112   /* Reset file pointer to beginning or open file.  */                        \
113   status = internal_setent (keep_stream);                                     \
114                                                                               \
115   if (status == NSS_STATUS_SUCCESS)                                           \
116     {                                                                         \
117       /* Tell getent function that we have repositioned the file pointer.  */ \
118       last_use = getby;                                                       \
119                                                                               \
120       while ((status = internal_getent (result, buffer, buflen, errnop        \
121                                         H_ERRNO_ARG EXTRA_ARGS_VALUE))        \
122              == NSS_STATUS_SUCCESS)                                           \
123         { break_if_match }                                                    \
124                                                                               \
125       if (status == NSS_STATUS_SUCCESS                                        \
126           && _res_hconf.flags & HCONF_FLAG_MULTI)                             \
127         {                                                                     \
128           /* We have to get all host entries from the file.  */               \
129           const size_t tmp_buflen = MIN (buflen, 4096);                       \
130           char tmp_buffer[tmp_buflen]                                         \
131             __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));\
132           struct hostent tmp_result_buf;                                      \
133           int naddrs = 1;                                                     \
134           int naliases = 0;                                                   \
135           char *bufferend;                                                    \
136                                                                               \
137           while (result->h_aliases[naliases] != NULL)                         \
138             ++naliases;                                                       \
139                                                                               \
140           bufferend = (char *) &result->h_aliases[naliases + 1];              \
141                                                                               \
142           while ((status = internal_getent (&tmp_result_buf, tmp_buffer,      \
143                                             tmp_buflen, errnop H_ERRNO_ARG    \
144                                             EXTRA_ARGS_VALUE))                \
145                  == NSS_STATUS_SUCCESS)                                       \
146             {                                                                 \
147               int matches = 1;                                                \
148               struct hostent *old_result = result;                            \
149               result = &tmp_result_buf;                                       \
150               /* The following piece is a bit clumsy but we want to use the   \
151                  `break_if_match' value.  The optimizer should do its         \
152                  job.  */                                                     \
153               do                                                              \
154                 {                                                             \
155                   break_if_match                                              \
156                   result = old_result;                                        \
157                 }                                                             \
158               while ((matches = 0));                                          \
159                                                                               \
160               if (matches)                                                    \
161                 {                                                             \
162                   /* We could be very clever and try to recycle a few bytes   \
163                      in the buffer instead of generating new arrays.  But     \
164                      we are not doing this here since it's more work than     \
165                      it's worth.  Simply let the user provide a bit bigger    \
166                      buffer.  */                                              \
167                   char **new_h_addr_list;                                     \
168                   char **new_h_aliases;                                       \
169                   int newaliases = 0;                                         \
170                   size_t newstrlen = 0;                                       \
171                   int cnt;                                                    \
172                                                                               \
173                   /* Count the new aliases and the length of the strings.  */ \
174                   while (tmp_result_buf.h_aliases[newaliases] != NULL)        \
175                     {                                                         \
176                       char *cp = tmp_result_buf.h_aliases[newaliases];        \
177                       ++newaliases;                                           \
178                       newstrlen += strlen (cp) + 1;                           \
179                     }                                                         \
180                   /* If the real name is different add it also to the         \
181                      aliases.  This means that there is a duplication         \
182                      in the alias list but this is really the users           \
183                      problem.  */                                             \
184                   if (strcmp (old_result->h_name,                             \
185                               tmp_result_buf.h_name) != 0)                    \
186                     {                                                         \
187                       ++newaliases;                                           \
188                       newstrlen += strlen (tmp_result_buf.h_name) + 1;        \
189                     }                                                         \
190                                                                               \
191                   /* Make sure bufferend is aligned.  */                      \
192                   assert ((bufferend - (char *) 0) % sizeof (char *) == 0);   \
193                                                                               \
194                   /* Now we can check whether the buffer is large enough.     \
195                      16 is the maximal size of the IP address.  */            \
196                   if (bufferend + 16 + (naddrs + 2) * sizeof (char *)         \
197                       + roundup (newstrlen, sizeof (char *))                  \
198                       + (naliases + newaliases + 1) * sizeof (char *)         \
199                       >= buffer + buflen)                                     \
200                     {                                                         \
201                       *errnop = ERANGE;                                       \
202                       *herrnop = NETDB_INTERNAL;                              \
203                       status = NSS_STATUS_TRYAGAIN;                           \
204                       break;                                                  \
205                     }                                                         \
206                                                                               \
207                   new_h_addr_list =                                           \
208                     (char **) (bufferend                                      \
209                                + roundup (newstrlen, sizeof (char *))         \
210                                + 16);                                         \
211                   new_h_aliases =                                             \
212                     (char **) ((char *) new_h_addr_list                       \
213                                + (naddrs + 2) * sizeof (char *));             \
214                                                                               \
215                   /* Copy the old data in the new arrays.  */                 \
216                   for (cnt = 0; cnt < naddrs; ++cnt)                          \
217                     new_h_addr_list[cnt] = old_result->h_addr_list[cnt];      \
218                                                                               \
219                   for (cnt = 0; cnt < naliases; ++cnt)                        \
220                     new_h_aliases[cnt] = old_result->h_aliases[cnt];          \
221                                                                               \
222                   /* Store the new strings.  */                               \
223                   cnt = 0;                                                    \
224                   while (tmp_result_buf.h_aliases[cnt] != NULL)               \
225                     {                                                         \
226                       new_h_aliases[naliases++] = bufferend;                  \
227                       bufferend = (__stpcpy (bufferend,                       \
228                                              tmp_result_buf.h_aliases[cnt])   \
229                                    + 1);                                      \
230                       ++cnt;                                                  \
231                     }                                                         \
232                                                                               \
233                   if (cnt < newaliases)                                       \
234                     {                                                         \
235                       new_h_aliases[naliases++] = bufferend;                  \
236                       bufferend = __stpcpy (bufferend,                        \
237                                             tmp_result_buf.h_name) + 1;       \
238                     }                                                         \
239                                                                               \
240                   /* Final NULL pointer.  */                                  \
241                   new_h_aliases[naliases] = NULL;                             \
242                                                                               \
243                   /* Round up the buffer end address.  */                     \
244                   bufferend += (sizeof (char *)                               \
245                                 - ((bufferend - (char *) 0)                   \
246                                    % sizeof (char *))) % sizeof (char *);     \
247                                                                               \
248                   /* Now the new address.  */                                 \
249                   new_h_addr_list[naddrs++] =                                 \
250                     memcpy (bufferend, tmp_result_buf.h_addr,                 \
251                             tmp_result_buf.h_length);                         \
252                                                                               \
253                   /* Also here a final NULL pointer.  */                      \
254                   new_h_addr_list[naddrs] = NULL;                             \
255                                                                               \
256                   /* Store the new array pointers.  */                        \
257                   old_result->h_aliases = new_h_aliases;                      \
258                   old_result->h_addr_list = new_h_addr_list;                  \
259                                                                               \
260                   /* Compute the new buffer end.  */                          \
261                   bufferend = (char *) &new_h_aliases[naliases + 1];          \
262                   assert (bufferend <= buffer + buflen);                      \
263                                                                               \
264                   result = old_result;                                        \
265                 }                                                             \
266             }                                                                 \
267                                                                               \
268           if (status != NSS_STATUS_TRYAGAIN)                                  \
269             status = NSS_STATUS_SUCCESS;                                      \
270         }                                                                     \
271                                                                               \
272                                                                               \
273       if (! keep_stream)                                                      \
274         internal_endent ();                                                   \
275     }                                                                         \
276                                                                               \
277   __libc_lock_unlock (lock);                                                  \
278                                                                               \
279   return status;                                                              \
280 }
281
282
283 #define EXTRA_ARGS_VALUE \
284   , ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET),                    \
285   ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
286 #include "files-XXX.c"
287 HOST_DB_LOOKUP (hostbyname, ,,
288                 {
289                   LOOKUP_NAME_CASE (h_name, h_aliases)
290                 }, const char *name)
291
292
293 #undef EXTRA_ARGS_VALUE
294 /* XXX Is using _res to determine whether we want to convert IPv4 addresses
295    to IPv6 addresses really the right thing to do?  */
296 #define EXTRA_ARGS_VALUE \
297   , af, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
298 HOST_DB_LOOKUP (hostbyname2, ,,
299                 {
300                   LOOKUP_NAME_CASE (h_name, h_aliases)
301                 }, const char *name, int af)
302
303 #undef EXTRA_ARGS_VALUE
304 /* We only need to consider IPv4 mapped addresses if the input to the
305    gethostbyaddr() function is an IPv6 address.  */
306 #define EXTRA_ARGS_VALUE \
307   , af, (len == IN6ADDRSZ ? AI_V4MAPPED : 0)
308 DB_LOOKUP (hostbyaddr, ,,
309            {
310              if (result->h_length == (int) len
311                  && ! memcmp (addr, result->h_addr_list[0], len))
312                break;
313            }, const void *addr, socklen_t len, int af)