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