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                                      + (hst->h_length == NS_INADDRSZ
162                                         ? NS_IN6ADDRSZ : 0)));
163
164       data = (struct hostdata *) malloc (total + req->key_len);
165       if (data == NULL)
166         /* There is no reason to go on.  */
167         error (EXIT_FAILURE, errno, _("while allocating cache entry"));
168
169       data->resp.found = 1;
170       data->resp.h_name_len = h_name_len;
171       data->resp.h_aliases_cnt = h_aliases_cnt;
172       data->resp.h_addrtype = hst->h_addrtype;
173       data->resp.h_length = hst->h_length;
174       data->resp.h_addr_list_cnt = h_addr_list_cnt;
175       data->resp.error = NETDB_SUCCESS;
176
177       cp = data->strdata;
178
179       cp = mempcpy (cp, hst->h_name, h_name_len);
180       cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (size_t));
181
182       /* The normal addresses first.  */
183       addresses = cp;
184       for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
185         cp = mempcpy (cp, hst->h_addr_list[cnt], hst->h_length);
186
187       /* Then the aliases.  */
188       aliases = cp;
189       for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
190         cp = mempcpy (cp, hst->h_aliases[cnt], h_aliases_len[cnt]);
191
192       assert (cp == data->strdata + total - sizeof (hst_response_header));
193
194       /* If we are adding a GETHOSTBYNAME{,v6} entry we must be prepared
195          that the answer we get from the NSS does not contain the key
196          itself.  This is the case if the resolver is used and the name
197          is extended by the domainnames from /etc/resolv.conf.  Therefore
198          we explicitly add the name here.  */
199       if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
200         key_copy = memcpy (cp, key, req->key_len);
201
202       /* We write the dataset before inserting it to the database
203          since while inserting this thread might block and so would
204          unnecessarily let the receiver wait.  */
205       written = write (fd, data, total);
206
207       addr_list_type = (hst->h_length == NS_INADDRSZ
208                         ? GETHOSTBYADDR : GETHOSTBYADDRv6);
209
210       /* Compute the timeout time.  */
211       t += db->postimeout;
212
213       /* Now get the lock to safely insert the records.  */
214       pthread_rwlock_rdlock (&db->lock);
215
216       /* First add all the aliases.  */
217       for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
218         {
219           if (addr_list_type == GETHOSTBYADDR)
220             cache_add (GETHOSTBYNAME, aliases, h_aliases_len[cnt], data, total,
221                        data, 0, t, db, owner);
222
223           cache_add (GETHOSTBYNAMEv6, aliases, h_aliases_len[cnt], data, total,
224                      data, 0, t, db, owner);
225
226           aliases += h_aliases_len[cnt];
227         }
228
229       /* Next the normal addresses.  */
230       for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
231         {
232           cache_add (addr_list_type, addresses, hst->h_length, data, total,
233                      data, 0, t, db, owner);
234           addresses += hst->h_length;
235         }
236
237       /* If necessary the IPv6 addresses.  */
238       if (addr_list_type == GETHOSTBYADDR)
239         for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
240           {
241             cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ, data, total,
242                        data, 0, t, db, owner);
243             addresses += IN6ADDRSZ;
244           }
245
246       /* If necessary add the key for this request.  */
247       if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
248         {
249           if (addr_list_type == GETHOSTBYADDR)
250             cache_add (GETHOSTBYNAME, key_copy, req->key_len, data, total,
251                        data, 0, t, db, owner);
252           cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len, data,
253                      total, data, 0, t, db, owner);
254         }
255
256       /* And finally the name.  We mark this as the last entry.  */
257       if (addr_list_type == GETHOSTBYADDR)
258         cache_add (GETHOSTBYNAME, data->strdata, h_name_len, data, total, data,
259                    0, t, db, owner);
260       cache_add (GETHOSTBYNAMEv6, data->strdata, h_name_len, data,
261                  total, data, 1, t, db, owner);
262
263       pthread_rwlock_unlock (&db->lock);
264     }
265
266   if (written != total)
267     {
268       char buf[256];
269       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
270                strerror_r (errno, buf, sizeof (buf)));
271     }
272 }
273
274
275 void
276 addhstbyname (struct database *db, int fd, request_header *req,
277               void *key, uid_t uid)
278 {
279   /* Search for the entry matching the key.  Please note that we don't
280      look again in the table whether the dataset is now available.  We
281      simply insert it.  It does not matter if it is in there twice.  The
282      pruning function only will look at the timestamp.  */
283   int buflen = 512;
284   char *buffer = alloca (buflen);
285   struct hostent resultbuf;
286   struct hostent *hst;
287   uid_t oldeuid = 0;
288
289   if (debug_level > 0)
290     dbg_log (_("Haven't found \"%s\" in hosts cache!"), key);
291
292   if (secure[hstdb])
293     {
294       oldeuid = geteuid ();
295       seteuid (uid);
296     }
297
298   while (__gethostbyname2_r (key, AF_INET, &resultbuf, buffer, buflen,
299                              &hst, &h_errno) != 0
300          && h_errno == NETDB_INTERNAL
301          && errno == ERANGE)
302     {
303       errno = 0;
304       buflen += 256;
305       buffer = alloca (buflen);
306     }
307
308   if (secure[hstdb])
309     seteuid (uid);
310
311   cache_addhst (db, fd, req, key, hst, uid);
312 }
313
314
315 void
316 addhstbyaddr (struct database *db, int fd, request_header *req,
317               void *key, uid_t uid)
318 {
319   /* Search for the entry matching the key.  Please note that we don't
320      look again in the table whether the dataset is now available.  We
321      simply insert it.  It does not matter if it is in there twice.  The
322      pruning function only will look at the timestamp.  */
323   int buflen = 512;
324   char *buffer = alloca (buflen);
325   struct hostent resultbuf;
326   struct hostent *hst;
327   uid_t oldeuid = 0;
328
329   if (debug_level > 0)
330     {
331       char buf[INET_ADDRSTRLEN];
332       dbg_log (_("Haven't found \"%s\" in hosts cache!"),
333                inet_ntop (AF_INET, key, buf, sizeof (buf)));
334     }
335
336   if (secure[hstdb])
337     {
338       oldeuid = geteuid ();
339       seteuid (uid);
340     }
341
342   while (__gethostbyaddr_r (key, NS_INADDRSZ, AF_INET, &resultbuf, buffer,
343                             buflen, &hst, &h_errno) != 0
344          && h_errno == NETDB_INTERNAL
345          && errno == ERANGE)
346     {
347       errno = 0;
348       buflen += 256;
349       buffer = alloca (buflen);
350     }
351
352   if (secure[hstdb])
353     seteuid (oldeuid);
354
355   cache_addhst (db, fd, req, key, hst, uid);
356 }
357
358
359 void
360 addhstbynamev6 (struct database *db, int fd, request_header *req,
361                 void *key, uid_t uid)
362 {
363   /* Search for the entry matching the key.  Please note that we don't
364      look again in the table whether the dataset is now available.  We
365      simply insert it.  It does not matter if it is in there twice.  The
366      pruning function only will look at the timestamp.  */
367   int buflen = 512;
368   char *buffer = alloca (buflen);
369   struct hostent resultbuf;
370   struct hostent *hst;
371   uid_t oldeuid = 0;
372
373   if (debug_level > 0)
374     {
375       char buf[INET6_ADDRSTRLEN];
376
377       dbg_log (_("Haven't found \"%s\" in hosts cache!"),
378                inet_ntop (AF_INET6, key, buf, sizeof (buf)));
379     }
380
381   if (secure[hstdb])
382     {
383       oldeuid = geteuid ();
384       seteuid (uid);
385     }
386
387   while (__gethostbyname2_r (key, AF_INET6, &resultbuf, buffer, buflen,
388                              &hst, &h_errno) != 0
389          && h_errno == NETDB_INTERNAL
390          && errno == ERANGE)
391     {
392       errno = 0;
393       buflen += 256;
394       buffer = alloca (buflen);
395     }
396
397   if (secure[hstdb])
398     seteuid (oldeuid);
399
400   cache_addhst (db, fd, req, key, hst, uid);
401 }
402
403
404 void
405 addhstbyaddrv6 (struct database *db, int fd, request_header *req,
406                 void *key, uid_t uid)
407 {
408   /* Search for the entry matching the key.  Please note that we don't
409      look again in the table whether the dataset is now available.  We
410      simply insert it.  It does not matter if it is in there twice.  The
411      pruning function only will look at the timestamp.  */
412   int buflen = 512;
413   char *buffer = alloca (buflen);
414   struct hostent resultbuf;
415   struct hostent *hst;
416   uid_t oldeuid = 0;
417
418   if (debug_level > 0)
419     {
420       char buf[INET6_ADDRSTRLEN];
421       dbg_log (_("Haven't found \"%s\" in hosts cache!"),
422                inet_ntop (AF_INET6, key, buf, sizeof (buf)));
423     }
424
425   if (secure[hstdb])
426     {
427       oldeuid = geteuid ();
428       seteuid (uid);
429     }
430
431   while (__gethostbyaddr_r (key, NS_IN6ADDRSZ, AF_INET6, &resultbuf,
432                             buffer, buflen, &hst, &h_errno) != 0
433          && h_errno == NETDB_INTERNAL
434          && errno == ERANGE)
435     {
436       errno = 0;
437       buflen += 256;
438       buffer = alloca (buflen);
439     }
440
441   if (secure[hstdb])
442     seteuid (oldeuid);
443
444   cache_addhst (db, fd, req, key, hst, uid);
445 }