]> git.ipfire.org Git - thirdparty/glibc.git/blame - nscd/hstcache.c
Fix build errors with -DNDEBUG.
[thirdparty/glibc.git] / nscd / hstcache.c
CommitLineData
67479a70 1/* Cache handling for host lookup.
f7a9f785 2 Copyright (C) 1998-2016 Free Software Foundation, Inc.
67479a70
UD
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
5
43bc8ac6 6 This program is free software; you can redistribute it and/or modify
2e2efe65
RM
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; version 2 of the License, or
9 (at your option) any later version.
67479a70 10
43bc8ac6 11 This program is distributed in the hope that it will be useful,
67479a70 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
43bc8ac6
UD
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
67479a70 15
43bc8ac6 16 You should have received a copy of the GNU General Public License
59ba27a6 17 along with this program; if not, see <http://www.gnu.org/licenses/>. */
67479a70 18
9caf4f1c 19#include <alloca.h>
67479a70
UD
20#include <assert.h>
21#include <errno.h>
22#include <error.h>
a95a08b4 23#include <libintl.h>
67479a70 24#include <netdb.h>
c7a9b6e2 25#include <stdbool.h>
67479a70
UD
26#include <stddef.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <time.h>
31#include <unistd.h>
e054f494 32#include <stdint.h>
67479a70 33#include <arpa/inet.h>
9e56c380 34#include <arpa/nameser.h>
a95a08b4 35#include <sys/mman.h>
c7a9b6e2 36#include <stackinfo.h>
67479a70
UD
37
38#include "nscd.h"
39#include "dbg_log.h"
eac10791
UD
40#ifdef HAVE_SENDFILE
41# include <kernel-features.h>
42#endif
67479a70 43
67479a70
UD
44
45/* This is the standard reply in case the service is disabled. */
46static const hst_response_header disabled =
47{
c2e13112
RM
48 .version = NSCD_VERSION,
49 .found = -1,
50 .h_name_len = 0,
51 .h_aliases_cnt = 0,
52 .h_addrtype = -1,
53 .h_length = -1,
54 .h_addr_list_cnt = 0,
55 .error = NETDB_INTERNAL
67479a70
UD
56};
57
58/* This is the struct describing how to write this record. */
59const struct iovec hst_iov_disabled =
60{
c2e13112
RM
61 .iov_base = (void *) &disabled,
62 .iov_len = sizeof (disabled)
67479a70
UD
63};
64
65
66/* This is the standard reply in case we haven't found the dataset. */
67static const hst_response_header notfound =
68{
c2e13112
RM
69 .version = NSCD_VERSION,
70 .found = 0,
71 .h_name_len = 0,
72 .h_aliases_cnt = 0,
73 .h_addrtype = -1,
74 .h_length = -1,
75 .h_addr_list_cnt = 0,
76 .error = HOST_NOT_FOUND
67479a70
UD
77};
78
67479a70 79
bc425b33
UD
80/* This is the standard reply in case there are temporary problems. */
81static const hst_response_header tryagain =
82{
83 .version = NSCD_VERSION,
84 .found = 0,
85 .h_name_len = 0,
86 .h_aliases_cnt = 0,
87 .h_addrtype = -1,
88 .h_length = -1,
89 .h_addr_list_cnt = 0,
90 .error = TRY_AGAIN
91};
92
93
a4c7ea7b 94static time_t
a95a08b4 95cache_addhst (struct database_dyn *db, int fd, request_header *req,
c207f23b 96 const void *key, struct hostent *hst, uid_t owner,
20e498bd 97 struct hashentry *const he, struct datahead *dh, int errval,
384ca551 98 int32_t ttl)
67479a70 99{
9ad58cc3 100 bool all_written = true;
67479a70
UD
101 time_t t = time (NULL);
102
a95a08b4
UD
103 /* We allocate all data in one memory block: the iov vector,
104 the response header and the dataset itself. */
105 struct dataset
106 {
107 struct datahead head;
108 hst_response_header resp;
109 char strdata[0];
110 } *dataset;
111
112 assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
113
a4c7ea7b 114 time_t timeout = MAX_TIMEOUT_VALUE;
67479a70
UD
115 if (hst == NULL)
116 {
a95a08b4
UD
117 if (he != NULL && errval == EAGAIN)
118 {
119 /* If we have an old record available but cannot find one
120 now because the service is not available we keep the old
121 record and make sure it does not get removed. */
122 if (reload_count != UINT_MAX)
123 /* Do not reset the value if we never not reload the record. */
124 dh->nreloads = reload_count - 1;
a4c7ea7b
UD
125
126 /* Reload with the same time-to-live value. */
127 timeout = dh->timeout = t + dh->ttl;
a95a08b4
UD
128 }
129 else
130 {
131 /* We have no data. This means we send the standard reply for this
bc425b33 132 case. Possibly this is only temporary. */
9ad58cc3 133 ssize_t total = sizeof (notfound);
bc425b33
UD
134 assert (sizeof (notfound) == sizeof (tryagain));
135
136 const hst_response_header *resp = (errval == EAGAIN
137 ? &tryagain : &notfound);
67479a70 138
9ad58cc3 139 if (fd != -1 &&
bc425b33 140 TEMP_FAILURE_RETRY (send (fd, resp, total,
9ad58cc3
UD
141 MSG_NOSIGNAL)) != total)
142 all_written = false;
67479a70 143
3e1aa84e
UD
144 /* If we have a transient error or cannot permanently store
145 the result, so be it. */
146 if (errval == EAGAIN || __builtin_expect (db->negtimeout == 0, 0))
445b4a53
TK
147 {
148 /* Mark the old entry as obsolete. */
149 if (dh != NULL)
150 dh->usable = false;
151 }
99231d9a
UD
152 else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
153 + req->key_len), 1)) != NULL)
a95a08b4 154 {
1cdeb237
SP
155 timeout = datahead_init_neg (&dataset->head,
156 (sizeof (struct dataset)
157 + req->key_len), total,
158 (ttl == INT32_MAX
159 ? db->negtimeout : ttl));
67479a70 160
a95a08b4 161 /* This is the reply. */
bc425b33 162 memcpy (&dataset->resp, resp, total);
67479a70 163
a95a08b4
UD
164 /* Copy the key data. */
165 memcpy (dataset->strdata, key, req->key_len);
67479a70 166
8c89236f
UD
167 /* If necessary, we also propagate the data to disk. */
168 if (db->persistent)
169 {
170 // XXX async OK?
171 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
172 msync ((void *) pval,
173 ((uintptr_t) dataset & pagesize_m1)
174 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
175 }
176
7e71e55f 177 (void) cache_add (req->type, &dataset->strdata, req->key_len,
528741cb 178 &dataset->head, true, db, owner, he == NULL);
a95a08b4 179
00ebd7ed
UD
180 pthread_rwlock_unlock (&db->lock);
181
a95a08b4
UD
182 /* Mark the old entry as obsolete. */
183 if (dh != NULL)
184 dh->usable = false;
185 }
99bb9f42 186 }
67479a70
UD
187 }
188 else
189 {
190 /* Determine the I/O structure. */
67479a70
UD
191 size_t h_name_len = strlen (hst->h_name) + 1;
192 size_t h_aliases_cnt;
3107c0c5 193 uint32_t *h_aliases_len;
67479a70 194 size_t h_addr_list_cnt;
67479a70
UD
195 char *addresses;
196 char *aliases;
197 char *key_copy = NULL;
198 char *cp;
199 size_t cnt;
9ad58cc3 200 ssize_t total;
67479a70
UD
201
202 /* Determine the number of aliases. */
203 h_aliases_cnt = 0;
204 for (cnt = 0; hst->h_aliases[cnt] != NULL; ++cnt)
205 ++h_aliases_cnt;
206 /* Determine the length of all aliases. */
3107c0c5 207 h_aliases_len = (uint32_t *) alloca (h_aliases_cnt * sizeof (uint32_t));
67479a70
UD
208 total = 0;
209 for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
210 {
211 h_aliases_len[cnt] = strlen (hst->h_aliases[cnt]) + 1;
212 total += h_aliases_len[cnt];
213 }
214
215 /* Determine the number of addresses. */
216 h_addr_list_cnt = 0;
1ce7d80d 217 while (hst->h_addr_list[h_addr_list_cnt] != NULL)
67479a70
UD
218 ++h_addr_list_cnt;
219
a95a08b4
UD
220 if (h_addr_list_cnt == 0)
221 /* Invalid entry. */
a4c7ea7b 222 return MAX_TIMEOUT_VALUE;
a95a08b4
UD
223
224 total += (sizeof (struct dataset)
67479a70 225 + h_name_len
3107c0c5 226 + h_aliases_cnt * sizeof (uint32_t)
488fb3c7 227 + h_addr_list_cnt * hst->h_length);
67479a70 228
a95a08b4
UD
229 /* If we refill the cache, first assume the reconrd did not
230 change. Allocate memory on the cache since it is likely
231 discarded anyway. If it turns out to be necessary to have a
232 new record we can still allocate real memory. */
233 bool alloca_used = false;
234 dataset = NULL;
67479a70 235
a95a08b4
UD
236 /* If the record contains more than one IP address (used for
237 load balancing etc) don't cache the entry. This is something
238 the current cache handling cannot handle and it is more than
239 questionable whether it is worthwhile complicating the cache
240 handling just for handling such a special case. */
a30d41c1 241 if (he == NULL && h_addr_list_cnt == 1)
20e498bd
UD
242 dataset = (struct dataset *) mempool_alloc (db, total + req->key_len,
243 1);
67479a70 244
a95a08b4
UD
245 if (dataset == NULL)
246 {
247 /* We cannot permanently add the result in the moment. But
248 we can provide the result as is. Store the data in some
249 temporary memory. */
250 dataset = (struct dataset *) alloca (total + req->key_len);
251
252 /* We cannot add this record to the permanent database. */
253 alloca_used = true;
254 }
255
1cdeb237
SP
256 timeout = datahead_init_pos (&dataset->head, total + req->key_len,
257 total - offsetof (struct dataset, resp),
258 he == NULL ? 0 : dh->nreloads + 1,
259 ttl == INT32_MAX ? db->postimeout : ttl);
a95a08b4
UD
260
261 dataset->resp.version = NSCD_VERSION;
262 dataset->resp.found = 1;
263 dataset->resp.h_name_len = h_name_len;
264 dataset->resp.h_aliases_cnt = h_aliases_cnt;
265 dataset->resp.h_addrtype = hst->h_addrtype;
266 dataset->resp.h_length = hst->h_length;
267 dataset->resp.h_addr_list_cnt = h_addr_list_cnt;
268 dataset->resp.error = NETDB_SUCCESS;
269
9ad58cc3
UD
270 /* Make sure there is no gap. */
271 assert ((char *) (&dataset->resp.error + 1) == dataset->strdata);
272
a95a08b4 273 cp = dataset->strdata;
67479a70
UD
274
275 cp = mempcpy (cp, hst->h_name, h_name_len);
3107c0c5 276 cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (uint32_t));
67479a70
UD
277
278 /* The normal addresses first. */
279 addresses = cp;
280 for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
281 cp = mempcpy (cp, hst->h_addr_list[cnt], hst->h_length);
282
67479a70
UD
283 /* Then the aliases. */
284 aliases = cp;
285 for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
286 cp = mempcpy (cp, hst->h_aliases[cnt], h_aliases_len[cnt]);
287
a95a08b4
UD
288 assert (cp
289 == dataset->strdata + total - offsetof (struct dataset,
290 strdata));
67479a70
UD
291
292 /* If we are adding a GETHOSTBYNAME{,v6} entry we must be prepared
293 that the answer we get from the NSS does not contain the key
294 itself. This is the case if the resolver is used and the name
295 is extended by the domainnames from /etc/resolv.conf. Therefore
296 we explicitly add the name here. */
c207f23b 297 key_copy = memcpy (cp, key, req->key_len);
67479a70 298
9ad58cc3
UD
299 assert ((char *) &dataset->resp + dataset->head.recsize == cp);
300
a95a08b4
UD
301 /* Now we can determine whether on refill we have to create a new
302 record or not. */
303 if (he != NULL)
304 {
305 assert (fd == -1);
67479a70 306
a95a08b4
UD
307 if (total + req->key_len == dh->allocsize
308 && total - offsetof (struct dataset, resp) == dh->recsize
309 && memcmp (&dataset->resp, dh->data,
310 dh->allocsize - offsetof (struct dataset, resp)) == 0)
311 {
8c89236f 312 /* The data has not changed. We will just bump the
a95a08b4
UD
313 timeout value. Note that the new record has been
314 allocated on the stack and need not be freed. */
c44d3bdf 315 assert (h_addr_list_cnt == 1);
a4c7ea7b 316 dh->ttl = dataset->head.ttl;
a95a08b4
UD
317 dh->timeout = dataset->head.timeout;
318 ++dh->nreloads;
319 }
320 else
321 {
c44d3bdf 322 if (h_addr_list_cnt == 1)
a95a08b4 323 {
c44d3bdf
UD
324 /* We have to create a new record. Just allocate
325 appropriate memory and copy it. */
326 struct dataset *newp
327 = (struct dataset *) mempool_alloc (db,
c52137d3 328 total + req->key_len,
20e498bd 329 1);
c44d3bdf
UD
330 if (newp != NULL)
331 {
332 /* Adjust pointers into the memory block. */
333 addresses = (char *) newp + (addresses
334 - (char *) dataset);
335 aliases = (char *) newp + (aliases - (char *) dataset);
336 assert (key_copy != NULL);
337 key_copy = (char *) newp + (key_copy - (char *) dataset);
338
339 dataset = memcpy (newp, dataset, total + req->key_len);
340 alloca_used = false;
341 }
a95a08b4
UD
342 }
343
344 /* Mark the old record as obsolete. */
345 dh->usable = false;
346 }
347 }
348 else
490998a5 349 {
a95a08b4
UD
350 /* We write the dataset before inserting it to the database
351 since while inserting this thread might block and so would
352 unnecessarily keep the receiver waiting. */
353 assert (fd != -1);
354
eac10791 355#ifdef HAVE_SENDFILE
74158740 356 if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
eac10791
UD
357 {
358 assert (db->wr_fd != -1);
359 assert ((char *) &dataset->resp > (char *) db->data);
ea547a1a 360 assert ((char *) dataset - (char *) db->head
eac10791
UD
361 + total
362 <= (sizeof (struct database_pers_head)
363 + db->head->module * sizeof (ref_t)
364 + db->head->data_size));
9ad58cc3
UD
365 ssize_t written = sendfileall (fd, db->wr_fd,
366 (char *) &dataset->resp
367 - (char *) db->head,
368 dataset->head.recsize);
369 if (written != dataset->head.recsize)
370 {
eac10791 371# ifndef __ASSUME_SENDFILE
9ad58cc3
UD
372 if (written == -1 && errno == ENOSYS)
373 goto use_write;
eac10791 374# endif
9ad58cc3
UD
375 all_written = false;
376 }
eac10791
UD
377 }
378 else
379# ifndef __ASSUME_SENDFILE
380 use_write:
381# endif
382#endif
9ad58cc3
UD
383 if (writeall (fd, &dataset->resp, dataset->head.recsize)
384 != dataset->head.recsize)
385 all_written = false;
490998a5
UD
386 }
387
a95a08b4
UD
388 /* Add the record to the database. But only if it has not been
389 stored on the stack.
67479a70 390
a95a08b4
UD
391 If the record contains more than one IP address (used for
392 load balancing etc) don't cache the entry. This is something
393 the current cache handling cannot handle and it is more than
394 questionable whether it is worthwhile complicating the cache
395 handling just for handling such a special case. */
396 if (! alloca_used)
67479a70 397 {
a95a08b4
UD
398 /* If necessary, we also propagate the data to disk. */
399 if (db->persistent)
3418007e
UD
400 {
401 // XXX async OK?
402 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
403 msync ((void *) pval,
404 ((uintptr_t) dataset & pagesize_m1)
405 + total + req->key_len, MS_ASYNC);
406 }
a95a08b4 407
a95a08b4
UD
408 /* NB: the following code is really complicated. It has
409 seemlingly duplicated code paths which do the same. The
410 problem is that we always must add the hash table entry
411 with the FIRST flag set first. Otherwise we get dangling
412 pointers in case memory allocation fails. */
c207f23b 413 assert (hst->h_addr_list[1] == NULL);
a95a08b4 414
a95a08b4
UD
415 /* Avoid adding names if more than one address is available. See
416 above for more info. */
c207f23b
UD
417 assert (req->type == GETHOSTBYNAME
418 || req->type == GETHOSTBYNAMEv6
419 || req->type == GETHOSTBYADDR
420 || req->type == GETHOSTBYADDRv6);
a95a08b4 421
7e71e55f 422 (void) cache_add (req->type, key_copy, req->key_len,
528741cb 423 &dataset->head, true, db, owner, he == NULL);
00ebd7ed
UD
424
425 pthread_rwlock_unlock (&db->lock);
67479a70 426 }
67479a70
UD
427 }
428
9ad58cc3 429 if (__builtin_expect (!all_written, 0) && debug_level > 0)
67479a70
UD
430 {
431 char buf[256];
432 dbg_log (_("short write in %s: %s"), __FUNCTION__,
433 strerror_r (errno, buf, sizeof (buf)));
434 }
a4c7ea7b
UD
435
436 return timeout;
67479a70
UD
437}
438
439
a95a08b4
UD
440static int
441lookup (int type, void *key, struct hostent *resultbufp, char *buffer,
384ca551 442 size_t buflen, struct hostent **hst, int32_t *ttlp)
a95a08b4
UD
443{
444 if (type == GETHOSTBYNAME)
384ca551
UD
445 return __gethostbyname3_r (key, AF_INET, resultbufp, buffer, buflen, hst,
446 &h_errno, ttlp, NULL);
8c89236f 447 if (type == GETHOSTBYNAMEv6)
384ca551
UD
448 return __gethostbyname3_r (key, AF_INET6, resultbufp, buffer, buflen, hst,
449 &h_errno, ttlp, NULL);
8c89236f 450 if (type == GETHOSTBYADDR)
384ca551
UD
451 return __gethostbyaddr2_r (key, NS_INADDRSZ, AF_INET, resultbufp, buffer,
452 buflen, hst, &h_errno, ttlp);
453 return __gethostbyaddr2_r (key, NS_IN6ADDRSZ, AF_INET6, resultbufp, buffer,
454 buflen, hst, &h_errno, ttlp);
a95a08b4
UD
455}
456
457
a4c7ea7b 458static time_t
a95a08b4
UD
459addhstbyX (struct database_dyn *db, int fd, request_header *req,
460 void *key, uid_t uid, struct hashentry *he, struct datahead *dh)
67479a70
UD
461{
462 /* Search for the entry matching the key. Please note that we don't
463 look again in the table whether the dataset is now available. We
464 simply insert it. It does not matter if it is in there twice. The
465 pruning function only will look at the timestamp. */
c7a9b6e2
UD
466 int buflen = 1024;
467 char *buffer = (char *) alloca (buflen);
67479a70
UD
468 struct hostent resultbuf;
469 struct hostent *hst;
c7a9b6e2 470 bool use_malloc = false;
a95a08b4 471 int errval = 0;
384ca551 472 int32_t ttl = INT32_MAX;
67479a70 473
a1ffb40e 474 if (__glibc_unlikely (debug_level > 0))
a95a08b4 475 {
c207f23b
UD
476 const char *str;
477 char buf[INET6_ADDRSTRLEN + 1];
478 if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
479 str = key;
480 else
481 str = inet_ntop (req->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
482 key, buf, sizeof (buf));
483
a95a08b4 484 if (he == NULL)
c207f23b 485 dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) str);
a95a08b4 486 else
c207f23b 487 dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) str);
a95a08b4 488 }
67479a70 489
384ca551 490 while (lookup (req->type, key, &resultbuf, buffer, buflen, &hst, &ttl) != 0
67479a70 491 && h_errno == NETDB_INTERNAL
a95a08b4 492 && (errval = errno) == ERANGE)
67479a70
UD
493 {
494 errno = 0;
c7a9b6e2 495
a1ffb40e 496 if (__glibc_unlikely (buflen > 32768))
c7a9b6e2 497 {
b21fa963 498 char *old_buffer = buffer;
4379b403 499 buflen *= 2;
c7a9b6e2
UD
500 buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
501 if (buffer == NULL)
502 {
503 /* We ran out of memory. We cannot do anything but
504 sending a negative response. In reality this should
505 never happen. */
506 hst = NULL;
507 buffer = old_buffer;
a95a08b4
UD
508
509 /* We set the error to indicate this is (possibly) a
510 temporary error and that it does not mean the entry
511 is not available at all. */
bc425b33 512 h_errno = TRY_AGAIN;
a95a08b4 513 errval = EAGAIN;
c7a9b6e2
UD
514 break;
515 }
516 use_malloc = true;
517 }
518 else
9caf4f1c
UD
519 /* Allocate a new buffer on the stack. If possible combine it
520 with the previously allocated buffer. */
4379b403 521 buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
67479a70
UD
522 }
523
a4c7ea7b
UD
524 time_t timeout = cache_addhst (db, fd, req, key, hst, uid, he, dh,
525 h_errno == TRY_AGAIN ? errval : 0, ttl);
c7a9b6e2
UD
526
527 if (use_malloc)
528 free (buffer);
a4c7ea7b
UD
529
530 return timeout;
67479a70
UD
531}
532
533
534void
a95a08b4 535addhstbyname (struct database_dyn *db, int fd, request_header *req,
a1c542bf 536 void *key, uid_t uid)
67479a70 537{
a95a08b4
UD
538 addhstbyX (db, fd, req, key, uid, NULL, NULL);
539}
67479a70 540
67479a70 541
a4c7ea7b 542time_t
a95a08b4
UD
543readdhstbyname (struct database_dyn *db, struct hashentry *he,
544 struct datahead *dh)
545{
546 request_header req =
a1c542bf 547 {
a95a08b4
UD
548 .type = GETHOSTBYNAME,
549 .key_len = he->len
550 };
a1c542bf 551
a4c7ea7b 552 return addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
a95a08b4 553}
c7a9b6e2 554
67479a70 555
a95a08b4
UD
556void
557addhstbyaddr (struct database_dyn *db, int fd, request_header *req,
558 void *key, uid_t uid)
559{
560 addhstbyX (db, fd, req, key, uid, NULL, NULL);
561}
a1c542bf 562
c7a9b6e2 563
a4c7ea7b 564time_t
a95a08b4
UD
565readdhstbyaddr (struct database_dyn *db, struct hashentry *he,
566 struct datahead *dh)
567{
568 request_header req =
569 {
570 .type = GETHOSTBYADDR,
571 .key_len = he->len
572 };
573
a4c7ea7b 574 return addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
67479a70
UD
575}
576
577
578void
a95a08b4 579addhstbynamev6 (struct database_dyn *db, int fd, request_header *req,
a1c542bf 580 void *key, uid_t uid)
67479a70 581{
a95a08b4
UD
582 addhstbyX (db, fd, req, key, uid, NULL, NULL);
583}
67479a70 584
a1c542bf 585
a4c7ea7b 586time_t
a95a08b4
UD
587readdhstbynamev6 (struct database_dyn *db, struct hashentry *he,
588 struct datahead *dh)
589{
590 request_header req =
67479a70 591 {
a95a08b4
UD
592 .type = GETHOSTBYNAMEv6,
593 .key_len = he->len
594 };
c7a9b6e2 595
a4c7ea7b 596 return addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
67479a70
UD
597}
598
599
600void
a95a08b4 601addhstbyaddrv6 (struct database_dyn *db, int fd, request_header *req,
a1c542bf 602 void *key, uid_t uid)
67479a70 603{
a95a08b4
UD
604 addhstbyX (db, fd, req, key, uid, NULL, NULL);
605}
67479a70 606
67479a70 607
a4c7ea7b 608time_t
a95a08b4
UD
609readdhstbyaddrv6 (struct database_dyn *db, struct hashentry *he,
610 struct datahead *dh)
611{
612 request_header req =
67479a70 613 {
a95a08b4
UD
614 .type = GETHOSTBYADDRv6,
615 .key_len = he->len
616 };
c7a9b6e2 617
a4c7ea7b 618 return addhstbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
67479a70 619}