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.  */
215       for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
216         {
217           if (addr_list_type == GETHOSTBYADDR)
218             cache_add (GETHOSTBYNAME, aliases, h_aliases_len[cnt], data, total,
219                        data, 0, t, db, owner);
220
221           cache_add (GETHOSTBYNAMEv6, aliases, h_aliases_len[cnt], data, total,
222                      data, 0, t, db, owner);
223
224           aliases += h_aliases_len[cnt];
225         }
226
227       /* Next the normal addresses.  */
228       for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
229         {
230           cache_add (addr_list_type, addresses, hst->h_length, data, total,
231                      data, 0, t, db, owner);
232           addresses += hst->h_length;
233         }
234
235       /* If necessary the IPv6 addresses.  */
236       if (addr_list_type == GETHOSTBYADDR)
237         for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
238           {
239             cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ, data, total,
240                        data, 0, t, db, owner);
241             addresses += IN6ADDRSZ;
242           }
243
244       /* If necessary add the key for this request.  */
245       if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
246         {
247           if (addr_list_type == GETHOSTBYADDR)
248             cache_add (GETHOSTBYNAME, key_copy, req->key_len, data, total,
249                        data, 0, t, db, owner);
250           cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len, data,
251                      total, data, 0, t, db, owner);
252         }
253
254       /* And finally the name.  We mark this as the last entry.  */
255       if (addr_list_type == GETHOSTBYADDR)
256         cache_add (GETHOSTBYNAME, data->strdata, h_name_len, data, total, data,
257                    0, t, db, owner);
258       cache_add (GETHOSTBYNAMEv6, data->strdata, h_name_len, data,
259                  total, data, 1, t, db, owner);
260
261       pthread_rwlock_unlock (&db->lock);
262     }
263
264   if (written != total)
265     {
266       char buf[256];
267       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
268                strerror_r (errno, buf, sizeof (buf)));
269     }
270 }
271
272
273 void
274 addhstbyname (struct database *db, int fd, request_header *req,
275               void *key, uid_t uid)
276 {
277   /* Search for the entry matching the key.  Please note that we don't
278      look again in the table whether the dataset is now available.  We
279      simply insert it.  It does not matter if it is in there twice.  The
280      pruning function only will look at the timestamp.  */
281   int buflen = 512;
282   char *buffer = alloca (buflen);
283   struct hostent resultbuf;
284   struct hostent *hst;
285   uid_t oldeuid = 0;
286
287   if (debug_level > 0)
288     dbg_log (_("Haven't found \"%s\" in hosts cache!"), key);
289
290   if (secure[hstdb])
291     {
292       oldeuid = geteuid ();
293       seteuid (uid);
294     }
295
296   while (__gethostbyname2_r (key, AF_INET, &resultbuf, buffer, buflen,
297                              &hst, &h_errno) != 0
298          && h_errno == NETDB_INTERNAL
299          && errno == ERANGE)
300     {
301       errno = 0;
302       buflen += 256;
303       buffer = alloca (buflen);
304     }
305
306   if (secure[hstdb])
307     seteuid (uid);
308
309   cache_addhst (db, fd, req, key, hst, uid);
310 }
311
312
313 void
314 addhstbyaddr (struct database *db, int fd, request_header *req,
315               void *key, uid_t uid)
316 {
317   /* Search for the entry matching the key.  Please note that we don't
318      look again in the table whether the dataset is now available.  We
319      simply insert it.  It does not matter if it is in there twice.  The
320      pruning function only will look at the timestamp.  */
321   int buflen = 512;
322   char *buffer = alloca (buflen);
323   struct hostent resultbuf;
324   struct hostent *hst;
325   uid_t oldeuid = 0;
326
327   if (debug_level > 0)
328     {
329       char buf[INET_ADDRSTRLEN];
330       dbg_log (_("Haven't found \"%s\" in hosts cache!"),
331                inet_ntop (AF_INET, key, buf, sizeof (buf)));
332     }
333
334   if (secure[hstdb])
335     {
336       oldeuid = geteuid ();
337       seteuid (uid);
338     }
339
340   while (__gethostbyaddr_r (key, NS_INADDRSZ, AF_INET, &resultbuf, buffer,
341                             buflen, &hst, &h_errno) != 0
342          && h_errno == NETDB_INTERNAL
343          && errno == ERANGE)
344     {
345       errno = 0;
346       buflen += 256;
347       buffer = alloca (buflen);
348     }
349
350   if (secure[hstdb])
351     seteuid (oldeuid);
352
353   cache_addhst (db, fd, req, key, hst, uid);
354 }
355
356
357 void
358 addhstbynamev6 (struct database *db, int fd, request_header *req,
359                 void *key, uid_t uid)
360 {
361   /* Search for the entry matching the key.  Please note that we don't
362      look again in the table whether the dataset is now available.  We
363      simply insert it.  It does not matter if it is in there twice.  The
364      pruning function only will look at the timestamp.  */
365   int buflen = 512;
366   char *buffer = alloca (buflen);
367   struct hostent resultbuf;
368   struct hostent *hst;
369   uid_t oldeuid = 0;
370
371   if (debug_level > 0)
372     {
373       char buf[INET6_ADDRSTRLEN];
374
375       dbg_log (_("Haven't found \"%s\" in hosts cache!"),
376                inet_ntop (AF_INET6, key, buf, sizeof (buf)));
377     }
378
379   if (secure[hstdb])
380     {
381       oldeuid = geteuid ();
382       seteuid (uid);
383     }
384
385   while (__gethostbyname2_r (key, AF_INET6, &resultbuf, buffer, buflen,
386                              &hst, &h_errno) != 0
387          && h_errno == NETDB_INTERNAL
388          && errno == ERANGE)
389     {
390       errno = 0;
391       buflen += 256;
392       buffer = alloca (buflen);
393     }
394
395   if (secure[hstdb])
396     seteuid (oldeuid);
397
398   cache_addhst (db, fd, req, key, hst, uid);
399 }
400
401
402 void
403 addhstbyaddrv6 (struct database *db, int fd, request_header *req,
404                 void *key, uid_t uid)
405 {
406   /* Search for the entry matching the key.  Please note that we don't
407      look again in the table whether the dataset is now available.  We
408      simply insert it.  It does not matter if it is in there twice.  The
409      pruning function only will look at the timestamp.  */
410   int buflen = 512;
411   char *buffer = alloca (buflen);
412   struct hostent resultbuf;
413   struct hostent *hst;
414   uid_t oldeuid = 0;
415
416   if (debug_level > 0)
417     {
418       char buf[INET6_ADDRSTRLEN];
419       dbg_log (_("Haven't found \"%s\" in hosts cache!"),
420                inet_ntop (AF_INET6, key, buf, sizeof (buf)));
421     }
422
423   if (secure[hstdb])
424     {
425       oldeuid = geteuid ();
426       seteuid (uid);
427     }
428
429   while (__gethostbyaddr_r (key, NS_IN6ADDRSZ, AF_INET6, &resultbuf,
430                             buffer, buflen, &hst, &h_errno) != 0
431          && h_errno == NETDB_INTERNAL
432          && errno == ERANGE)
433     {
434       errno = 0;
435       buflen += 256;
436       buffer = alloca (buflen);
437     }
438
439   if (secure[hstdb])
440     seteuid (oldeuid);
441
442   cache_addhst (db, fd, req, key, hst, uid);
443 }