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