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