]>
Commit | Line | Data |
---|---|---|
5f0e6fc7 | 1 | /* Hosts file parser in nss_files module. |
0ecb606c JJ |
2 | Copyright (C) 1996-2001, 2003, 2004, 2007 |
3 | Free Software Foundation, Inc. | |
19361cb7 | 4 | This file is part of the GNU C Library. |
5f0e6fc7 | 5 | |
19361cb7 | 6 | The GNU C Library is free software; you can redistribute it and/or |
41bdb6e2 AJ |
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. | |
5f0e6fc7 | 10 | |
19361cb7 UD |
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 | |
41bdb6e2 | 14 | Lesser General Public License for more details. |
5f0e6fc7 | 15 | |
41bdb6e2 AJ |
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. */ | |
5f0e6fc7 | 20 | |
a1d4a3bc | 21 | #include <assert.h> |
5f0e6fc7 RM |
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" | |
a1d4a3bc | 31 | #include "../resolv/res_hconf.h" |
5f0e6fc7 RM |
32 | |
33 | ||
34 | #define ENTNAME hostent | |
96bda0ea | 35 | #define DATABASE "hosts" |
adc6ff7f | 36 | #define NEED_H_ERRNO |
5f0e6fc7 | 37 | |
71d3bda9 UD |
38 | #define EXTRA_ARGS , af, flags |
39 | #define EXTRA_ARGS_DECL , int af, int flags | |
40 | ||
3776d592 | 41 | #define ENTDATA hostent_data |
5f0e6fc7 RM |
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 | |
ffee1316 RM |
52 | ("#", |
53 | { | |
54 | char *addr; | |
55 | ||
56 | STRING_FIELD (addr, isspace, 1); | |
57 | ||
58 | /* Parse address. */ | |
b455972f UD |
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))) | |
ffee1316 RM |
65 | /* Illegal address: ignore line. */ |
66 | return 0; | |
67 | ||
b455972f UD |
68 | /* We always return entries of the requested form. */ |
69 | result->h_addrtype = af; | |
70 | result->h_length = af == AF_INET ? INADDRSZ : IN6ADDRSZ; | |
71 | ||
ffee1316 RM |
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; | |
76 | ||
ffee1316 RM |
77 | STRING_FIELD (result->h_name, isspace, 1); |
78 | }) | |
5f0e6fc7 | 79 | |
a1d4a3bc UD |
80 | |
81 | ||
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; \ | |
89 | \ | |
0ecb606c JJ |
90 | uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data); \ |
91 | buffer += pad; \ | |
92 | buflen = buflen > pad ? buflen - pad : 0; \ | |
93 | \ | |
a1d4a3bc UD |
94 | __libc_lock_lock (lock); \ |
95 | \ | |
96 | /* Reset file pointer to beginning or open file. */ \ | |
97 | status = internal_setent (keep_stream); \ | |
98 | \ | |
99 | if (status == NSS_STATUS_SUCCESS) \ | |
100 | { \ | |
101 | /* Tell getent function that we have repositioned the file pointer. */ \ | |
102 | last_use = getby; \ | |
103 | \ | |
104 | while ((status = internal_getent (result, buffer, buflen, errnop \ | |
105 | H_ERRNO_ARG EXTRA_ARGS_VALUE)) \ | |
106 | == NSS_STATUS_SUCCESS) \ | |
107 | { break_if_match } \ | |
108 | \ | |
109 | if (status == NSS_STATUS_SUCCESS \ | |
110 | && _res_hconf.flags & HCONF_FLAG_MULTI) \ | |
111 | { \ | |
112 | /* We have to get all host entries from the file. */ \ | |
113 | const size_t tmp_buflen = MIN (buflen, 4096); \ | |
0ecb606c JJ |
114 | char tmp_buffer[tmp_buflen] \ |
115 | __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));\ | |
a1d4a3bc UD |
116 | struct hostent tmp_result_buf; \ |
117 | int naddrs = 1; \ | |
118 | int naliases = 0; \ | |
119 | char *bufferend; \ | |
120 | \ | |
121 | while (result->h_aliases[naliases] != NULL) \ | |
122 | ++naliases; \ | |
123 | \ | |
124 | bufferend = (char *) &result->h_aliases[naliases + 1]; \ | |
125 | \ | |
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) \ | |
130 | { \ | |
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 \ | |
138 | { \ | |
139 | break_if_match \ | |
140 | result = old_result; \ | |
141 | } \ | |
142 | while ((matches = 0)); \ | |
143 | \ | |
144 | if (matches) \ | |
145 | { \ | |
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; \ | |
156 | \ | |
157 | /* Count the new aliases and the length of the strings. */ \ | |
158 | while (tmp_result_buf.h_aliases[newaliases] != NULL) \ | |
159 | { \ | |
160 | char *cp = tmp_result_buf.h_aliases[newaliases]; \ | |
161 | ++newaliases; \ | |
162 | newstrlen += strlen (cp) + 1; \ | |
163 | } \ | |
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) \ | |
170 | { \ | |
171 | ++newaliases; \ | |
172 | newstrlen += strlen (tmp_result_buf.h_name) + 1; \ | |
173 | } \ | |
174 | \ | |
969c9da7 UD |
175 | /* Make sure bufferend is aligned. */ \ |
176 | assert ((bufferend - (char *) 0) % sizeof (char *) == 0); \ | |
177 | \ | |
178 | /* Now we can check whether the buffer is large enough. \ | |
179 | 16 is the maximal size of the IP address. */ \ | |
a1d4a3bc UD |
180 | if (bufferend + 16 + (naddrs + 2) * sizeof (char *) \ |
181 | + roundup (newstrlen, sizeof (char *)) \ | |
182 | + (naliases + newaliases + 1) * sizeof (char *) \ | |
183 | >= buffer + buflen) \ | |
184 | { \ | |
185 | *errnop = ERANGE; \ | |
04484feb | 186 | *herrnop = NETDB_INTERNAL; \ |
a1d4a3bc UD |
187 | status = NSS_STATUS_TRYAGAIN; \ |
188 | break; \ | |
189 | } \ | |
190 | \ | |
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 *)); \ | |
198 | \ | |
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]; \ | |
202 | \ | |
203 | for (cnt = 0; cnt < naliases; ++cnt) \ | |
204 | new_h_aliases[cnt] = old_result->h_aliases[cnt]; \ | |
205 | \ | |
206 | /* Store the new strings. */ \ | |
207 | cnt = 0; \ | |
208 | while (tmp_result_buf.h_aliases[cnt] != NULL) \ | |
209 | { \ | |
210 | new_h_aliases[naliases++] = bufferend; \ | |
211 | bufferend = (__stpcpy (bufferend, \ | |
212 | tmp_result_buf.h_aliases[cnt]) \ | |
213 | + 1); \ | |
2e4581e4 | 214 | ++cnt; \ |
a1d4a3bc UD |
215 | } \ |
216 | \ | |
217 | if (cnt < newaliases) \ | |
218 | { \ | |
219 | new_h_aliases[naliases++] = bufferend; \ | |
220 | bufferend = __stpcpy (bufferend, \ | |
221 | tmp_result_buf.h_name) + 1; \ | |
222 | } \ | |
223 | \ | |
224 | /* Final NULL pointer. */ \ | |
225 | new_h_aliases[naliases] = NULL; \ | |
226 | \ | |
227 | /* Round up the buffer end address. */ \ | |
228 | bufferend += (sizeof (char *) \ | |
229 | - ((bufferend - (char *) 0) \ | |
969c9da7 | 230 | % sizeof (char *))) % sizeof (char *); \ |
a1d4a3bc UD |
231 | \ |
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); \ | |
236 | \ | |
237 | /* Also here a final NULL pointer. */ \ | |
238 | new_h_addr_list[naddrs] = NULL; \ | |
239 | \ | |
240 | /* Store the new array pointers. */ \ | |
241 | old_result->h_aliases = new_h_aliases; \ | |
242 | old_result->h_addr_list = new_h_addr_list; \ | |
243 | \ | |
244 | /* Compute the new buffer end. */ \ | |
245 | bufferend = (char *) &new_h_aliases[naliases + 1]; \ | |
246 | assert (bufferend <= buffer + buflen); \ | |
3accf5d1 UD |
247 | \ |
248 | result = old_result; \ | |
a1d4a3bc UD |
249 | } \ |
250 | } \ | |
251 | \ | |
252 | if (status != NSS_STATUS_TRYAGAIN) \ | |
253 | status = NSS_STATUS_SUCCESS; \ | |
254 | } \ | |
255 | \ | |
256 | \ | |
257 | if (! keep_stream) \ | |
258 | internal_endent (); \ | |
259 | } \ | |
260 | \ | |
261 | __libc_lock_unlock (lock); \ | |
262 | \ | |
263 | return status; \ | |
264 | } | |
265 | ||
266 | ||
71d3bda9 UD |
267 | #define EXTRA_ARGS_VALUE \ |
268 | , ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET), \ | |
269 | ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0) | |
5f0e6fc7 | 270 | #include "files-XXX.c" |
a1d4a3bc UD |
271 | HOST_DB_LOOKUP (hostbyname, ,, |
272 | { | |
273 | LOOKUP_NAME_CASE (h_name, h_aliases) | |
274 | }, const char *name) | |
5f0e6fc7 | 275 | |
5f0e6fc7 | 276 | |
71d3bda9 | 277 | #undef EXTRA_ARGS_VALUE |
b455972f UD |
278 | /* XXX Is using _res to determine whether we want to convert IPv4 addresses |
279 | to IPv6 addresses really the right thing to do? */ | |
71d3bda9 UD |
280 | #define EXTRA_ARGS_VALUE \ |
281 | , af, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0) | |
a1d4a3bc UD |
282 | HOST_DB_LOOKUP (hostbyname2, ,, |
283 | { | |
284 | LOOKUP_NAME_CASE (h_name, h_aliases) | |
285 | }, const char *name, int af) | |
0d8733c4 | 286 | |
4a691b06 UD |
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) | |
96bda0ea | 292 | DB_LOOKUP (hostbyaddr, ,, |
5f0e6fc7 | 293 | { |
addc92bd | 294 | if (result->h_length == (int) len |
71d3bda9 | 295 | && ! memcmp (addr, result->h_addr_list[0], len)) |
5f0e6fc7 | 296 | break; |
9d4d69b8 | 297 | }, const void *addr, socklen_t len, int af) |