(nscd_gethst_r): Make sure resultbuf->h_addr_list addresses are
[kopensolaris-gnu/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, &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 }