]> git.ipfire.org Git - thirdparty/glibc.git/blob - nscd/hstcache.c
Update.
[thirdparty/glibc.git] / nscd / hstcache.c
1 /* Cache handling for host lookup.
2 Copyright (C) 1998, 1999 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 Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 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 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <error.h>
24 #include <netdb.h>
25 #include <stddef.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <libintl.h>
32 #include <arpa/inet.h>
33
34 #include "nscd.h"
35 #include "dbg_log.h"
36
37 /* Get implementation for some internal functions. */
38 #include "../resolv/mapv4v6addr.h"
39
40
41 /* This is the standard reply in case the service is disabled. */
42 static const hst_response_header disabled =
43 {
44 version: NSCD_VERSION,
45 found: -1,
46 h_name_len: 0,
47 h_aliases_cnt: 0,
48 h_addrtype: -1,
49 h_length: -1,
50 h_addr_list_cnt: 0,
51 error: NETDB_INTERNAL
52 };
53
54 /* This is the struct describing how to write this record. */
55 const struct iovec hst_iov_disabled =
56 {
57 iov_base: (void *) &disabled,
58 iov_len: sizeof (disabled)
59 };
60
61
62 /* This is the standard reply in case we haven't found the dataset. */
63 static const hst_response_header notfound =
64 {
65 version: NSCD_VERSION,
66 found: 0,
67 h_name_len: 0,
68 h_aliases_cnt: 0,
69 h_addrtype: -1,
70 h_length: -1,
71 h_addr_list_cnt: 0,
72 error: HOST_NOT_FOUND
73 };
74
75 /* This is the struct describing how to write this record. */
76 static const struct iovec iov_notfound =
77 {
78 iov_base: (void *) &notfound,
79 iov_len: sizeof (notfound)
80 };
81
82
83 struct hostdata
84 {
85 hst_response_header resp;
86 char strdata[0];
87 };
88
89
90 static void
91 cache_addhst (struct database *db, int fd, request_header *req, void *key,
92 struct hostent *hst, uid_t owner)
93 {
94 ssize_t total;
95 ssize_t written;
96 time_t t = time (NULL);
97
98 if (hst == NULL)
99 {
100 /* We have no data. This means we send the standard reply for this
101 case. */
102 void *copy;
103
104 total = sizeof (notfound);
105
106 written = writev (fd, &iov_notfound, 1);
107
108 copy = malloc (req->key_len);
109 if (copy == NULL)
110 error (EXIT_FAILURE, errno, _("while allocating key copy"));
111 memcpy (copy, key, req->key_len);
112
113 /* Compute the timeout time. */
114 t += db->negtimeout;
115
116 /* Now get the lock to safely insert the records. */
117 pthread_rwlock_rdlock (&db->lock);
118
119 cache_add (req->type, copy, req->key_len, &iov_notfound,
120 sizeof (notfound), (void *) -1, 0, t, db, owner);
121
122 pthread_rwlock_unlock (&db->lock);
123 }
124 else
125 {
126 /* Determine the I/O structure. */
127 struct hostdata *data;
128 size_t h_name_len = strlen (hst->h_name) + 1;
129 size_t h_aliases_cnt;
130 size_t *h_aliases_len;
131 size_t h_addr_list_cnt;
132 int addr_list_type;
133 char *addresses;
134 char *aliases;
135 char *key_copy = NULL;
136 char *cp;
137 size_t cnt;
138
139 /* Determine the number of aliases. */
140 h_aliases_cnt = 0;
141 for (cnt = 0; hst->h_aliases[cnt] != NULL; ++cnt)
142 ++h_aliases_cnt;
143 /* Determine the length of all aliases. */
144 h_aliases_len = alloca (h_aliases_cnt * sizeof (size_t));
145 total = 0;
146 for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
147 {
148 h_aliases_len[cnt] = strlen (hst->h_aliases[cnt]) + 1;
149 total += h_aliases_len[cnt];
150 }
151
152 /* Determine the number of addresses. */
153 h_addr_list_cnt = 0;
154 for (cnt = 0; hst->h_addr_list[cnt]; ++cnt)
155 ++h_addr_list_cnt;
156
157 /* We allocate all data in one memory block: the iov vector,
158 the response header and the dataset itself. */
159 total += (sizeof (struct hostdata)
160 + h_name_len
161 + h_aliases_cnt * sizeof (size_t)
162 + h_addr_list_cnt * (hst->h_length
163 + (hst->h_length == INADDRSZ
164 ? IN6ADDRSZ : 0)));
165
166 data = (struct hostdata *) malloc (total + req->key_len);
167 if (data == NULL)
168 /* There is no reason to go on. */
169 error (EXIT_FAILURE, errno, _("while allocating cache entry"));
170
171 data->resp.found = 1;
172 data->resp.h_name_len = h_name_len;
173 data->resp.h_aliases_cnt = h_aliases_cnt;
174 data->resp.h_addrtype = hst->h_addrtype;
175 data->resp.h_length = hst->h_length;
176 data->resp.h_addr_list_cnt = h_addr_list_cnt;
177 data->resp.error = NETDB_SUCCESS;
178
179 cp = data->strdata;
180
181 cp = mempcpy (cp, hst->h_name, h_name_len);
182 cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (size_t));
183
184 /* The normal addresses first. */
185 addresses = cp;
186 for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
187 cp = mempcpy (cp, hst->h_addr_list[cnt], hst->h_length);
188
189 /* And the generated IPv6 addresses if necessary. */
190 if (hst->h_length == INADDRSZ)
191 {
192 /* Generate the IPv6 addresses. */
193 for (cnt = 0; cnt < h_addr_list_cnt; cp += IN6ADDRSZ, ++cnt)
194 map_v4v6_address (hst->h_addr_list[cnt], cp);
195 }
196
197 /* Then the aliases. */
198 aliases = cp;
199 for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
200 cp = mempcpy (cp, hst->h_aliases[cnt], h_aliases_len[cnt]);
201
202 assert (cp == data->strdata + total - sizeof (hst_response_header));
203
204 /* If we are adding a GETHOSTBYNAME{,v6} entry we must be prepared
205 that the answer we get from the NSS does not contain the key
206 itself. This is the case if the resolver is used and the name
207 is extended by the domainnames from /etc/resolv.conf. Therefore
208 we explicitly add the name here. */
209 if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
210 key_copy = memcpy (cp, key, req->key_len);
211
212 /* We write the dataset before inserting it to the database
213 since while inserting this thread might block and so would
214 unnecessarily let the receiver wait. */
215 written = write (fd, data, total);
216
217 addr_list_type = (hst->h_length == INADDRSZ
218 ? GETHOSTBYADDR : GETHOSTBYADDRv6);
219
220 /* Compute the timeout time. */
221 t += db->postimeout;
222
223 /* Now get the lock to safely insert the records. */
224 pthread_rwlock_rdlock (&db->lock);
225
226 /* First add all the aliases. */
227 for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
228 {
229 if (addr_list_type == GETHOSTBYADDR)
230 cache_add (GETHOSTBYNAME, aliases, h_aliases_len[cnt], data, total,
231 data, 0, t, db, owner);
232
233 cache_add (GETHOSTBYNAMEv6, aliases, h_aliases_len[cnt], data, total,
234 data, 0, t, db, owner);
235
236 aliases += h_aliases_len[cnt];
237 }
238
239 /* Next the normal addresses. */
240 for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
241 {
242 cache_add (addr_list_type, addresses, hst->h_length, data, total,
243 data, 0, t, db, owner);
244 addresses += hst->h_length;
245 }
246
247 /* If necessary the IPv6 addresses. */
248 if (addr_list_type == GETHOSTBYADDR)
249 for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
250 {
251 cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ, data, total,
252 data, 0, t, db, owner);
253 addresses += IN6ADDRSZ;
254 }
255
256 /* If necessary add the key for this request. */
257 if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
258 {
259 if (addr_list_type == GETHOSTBYADDR)
260 cache_add (GETHOSTBYNAME, key_copy, req->key_len, data, total,
261 data, 0, t, db, owner);
262 cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len, data,
263 total, data, 0, t, db, owner);
264 }
265
266 /* And finally the name. We mark this as the last entry. */
267 if (addr_list_type == GETHOSTBYADDR)
268 cache_add (GETHOSTBYNAME, data->strdata, h_name_len, data, total, data,
269 0, t, db, owner);
270 cache_add (GETHOSTBYNAMEv6, data->strdata, h_name_len, data,
271 total, data, 1, t, db, owner);
272
273 pthread_rwlock_unlock (&db->lock);
274 }
275
276 if (written != total)
277 {
278 char buf[256];
279 dbg_log (_("short write in %s: %s"), __FUNCTION__,
280 strerror_r (errno, buf, sizeof (buf)));
281 }
282 }
283
284
285 void
286 addhstbyname (struct database *db, int fd, request_header *req,
287 void *key, uid_t uid)
288 {
289 /* Search for the entry matching the key. Please note that we don't
290 look again in the table whether the dataset is now available. We
291 simply insert it. It does not matter if it is in there twice. The
292 pruning function only will look at the timestamp. */
293 int buflen = 512;
294 char *buffer = alloca (buflen);
295 struct hostent resultbuf;
296 struct hostent *hst;
297 uid_t oldeuid = 0;
298
299 if (debug_level > 0)
300 dbg_log (_("Haven't found \"%s\" in hosts cache!"), key);
301
302 if (secure[hstdb])
303 {
304 oldeuid = geteuid ();
305 seteuid (uid);
306 }
307
308 while (gethostbyname2_r (key, AF_INET, &resultbuf, buffer, buflen, &hst,
309 &h_errno) != 0
310 && h_errno == NETDB_INTERNAL
311 && errno == ERANGE)
312 {
313 errno = 0;
314 buflen += 256;
315 buffer = alloca (buflen);
316 }
317
318 if (secure[hstdb])
319 seteuid (uid);
320
321 cache_addhst (db, fd, req, key, hst, uid);
322 }
323
324
325 void
326 addhstbyaddr (struct database *db, int fd, request_header *req,
327 void *key, uid_t uid)
328 {
329 /* Search for the entry matching the key. Please note that we don't
330 look again in the table whether the dataset is now available. We
331 simply insert it. It does not matter if it is in there twice. The
332 pruning function only will look at the timestamp. */
333 int buflen = 512;
334 char *buffer = alloca (buflen);
335 struct hostent resultbuf;
336 struct hostent *hst;
337 uid_t oldeuid = 0;
338
339 if (debug_level > 0)
340 {
341 char buf[INET_ADDRSTRLEN];
342 dbg_log (_("Haven't found \"%s\" in hosts cache!"),
343 inet_ntop (AF_INET, key, buf, sizeof (buf)));
344 }
345
346 if (secure[hstdb])
347 {
348 oldeuid = geteuid ();
349 seteuid (uid);
350 }
351
352 while (gethostbyaddr_r (key, INADDRSZ, AF_INET, &resultbuf, buffer, buflen,
353 &hst, &h_errno) != 0
354 && h_errno == NETDB_INTERNAL
355 && errno == ERANGE)
356 {
357 errno = 0;
358 buflen += 256;
359 buffer = alloca (buflen);
360 }
361
362 if (secure[hstdb])
363 seteuid (oldeuid);
364
365 cache_addhst (db, fd, req, key, hst, uid);
366 }
367
368
369 void
370 addhstbynamev6 (struct database *db, int fd, request_header *req,
371 void *key, uid_t uid)
372 {
373 /* Search for the entry matching the key. Please note that we don't
374 look again in the table whether the dataset is now available. We
375 simply insert it. It does not matter if it is in there twice. The
376 pruning function only will look at the timestamp. */
377 int buflen = 512;
378 char *buffer = alloca (buflen);
379 struct hostent resultbuf;
380 struct hostent *hst;
381 uid_t oldeuid = 0;
382
383 if (debug_level > 0)
384 {
385 char buf[INET6_ADDRSTRLEN];
386
387 dbg_log (_("Haven't found \"%s\" in hosts cache!"),
388 inet_ntop (AF_INET6, key, buf, sizeof (buf)));
389 }
390
391 if (secure[hstdb])
392 {
393 oldeuid = geteuid ();
394 seteuid (uid);
395 }
396
397 while (gethostbyname2_r (key, AF_INET6, &resultbuf, buffer, buflen, &hst,
398 &h_errno) != 0
399 && h_errno == NETDB_INTERNAL
400 && errno == ERANGE)
401 {
402 errno = 0;
403 buflen += 256;
404 buffer = alloca (buflen);
405 }
406
407 if (secure[hstdb])
408 seteuid (oldeuid);
409
410 cache_addhst (db, fd, req, key, hst, uid);
411 }
412
413
414 void
415 addhstbyaddrv6 (struct database *db, int fd, request_header *req,
416 void *key, uid_t uid)
417 {
418 /* Search for the entry matching the key. Please note that we don't
419 look again in the table whether the dataset is now available. We
420 simply insert it. It does not matter if it is in there twice. The
421 pruning function only will look at the timestamp. */
422 int buflen = 512;
423 char *buffer = alloca (buflen);
424 struct hostent resultbuf;
425 struct hostent *hst;
426 uid_t oldeuid = 0;
427
428 if (debug_level > 0)
429 {
430 char buf[INET6_ADDRSTRLEN];
431 dbg_log (_("Haven't found \"%s\" in hosts cache!"),
432 inet_ntop (AF_INET6, key, buf, sizeof (buf)));
433 }
434
435 if (secure[hstdb])
436 {
437 oldeuid = geteuid ();
438 seteuid (uid);
439 }
440
441 while (gethostbyaddr_r (key, IN6ADDRSZ, AF_INET6, &resultbuf, buffer, buflen,
442 &hst, &h_errno) != 0
443 && h_errno == NETDB_INTERNAL
444 && errno == ERANGE)
445 {
446 errno = 0;
447 buflen += 256;
448 buffer = alloca (buflen);
449 }
450
451 if (secure[hstdb])
452 seteuid (oldeuid);
453
454 cache_addhst (db, fd, req, key, hst, uid);
455 }