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