]>
Commit | Line | Data |
---|---|---|
d19687d6 | 1 | /* Cache handling for host lookup. |
a4c7ea7b | 2 | Copyright (C) 2004-2008, 2009, 2010, 2011 Free Software Foundation, Inc. |
d19687d6 UD |
3 | This file is part of the GNU C Library. |
4 | Contributed by Ulrich Drepper <drepper@redhat.com>, 2004. | |
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. | |
d19687d6 | 10 | |
43bc8ac6 | 11 | This program is distributed in the hope that it will be useful, |
d19687d6 | 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. | |
d19687d6 | 15 | |
43bc8ac6 UD |
16 | You should have received a copy of the GNU General Public License |
17 | along with this program; if not, write to the Free Software Foundation, | |
18 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
d19687d6 UD |
19 | |
20 | #include <assert.h> | |
21 | #include <errno.h> | |
22 | #include <libintl.h> | |
23 | #include <netdb.h> | |
1eb946b9 | 24 | #include <nss.h> |
d19687d6 UD |
25 | #include <string.h> |
26 | #include <time.h> | |
27 | #include <unistd.h> | |
28 | #include <sys/mman.h> | |
eac10791 UD |
29 | |
30 | #include "dbg_log.h" | |
31 | #include "nscd.h" | |
32 | #ifdef HAVE_SENDFILE | |
33 | # include <kernel-features.h> | |
34 | #endif | |
d19687d6 UD |
35 | |
36 | ||
1eb946b9 UD |
37 | typedef enum nss_status (*nss_gethostbyname4_r) |
38 | (const char *name, struct gaih_addrtuple **pat, | |
39 | char *buffer, size_t buflen, int *errnop, | |
40 | int *h_errnop, int32_t *ttlp); | |
d1fe1f22 | 41 | typedef enum nss_status (*nss_gethostbyname3_r) |
d19687d6 UD |
42 | (const char *name, int af, struct hostent *host, |
43 | char *buffer, size_t buflen, int *errnop, | |
d1fe1f22 | 44 | int *h_errnop, int32_t *, char **); |
d19687d6 UD |
45 | typedef enum nss_status (*nss_getcanonname_r) |
46 | (const char *name, char *buffer, size_t buflen, char **result, | |
47 | int *errnop, int *h_errnop); | |
48 | ||
49 | ||
50 | static const ai_response_header notfound = | |
51 | { | |
52 | .version = NSCD_VERSION, | |
53 | .found = 0, | |
54 | .naddrs = 0, | |
55 | .addrslen = 0, | |
56 | .canonlen = 0, | |
57 | .error = 0 | |
58 | }; | |
59 | ||
60 | ||
a4c7ea7b | 61 | static time_t |
d19687d6 | 62 | addhstaiX (struct database_dyn *db, int fd, request_header *req, |
20e498bd UD |
63 | void *key, uid_t uid, struct hashentry *const he, |
64 | struct datahead *dh) | |
d19687d6 UD |
65 | { |
66 | /* Search for the entry matching the key. Please note that we don't | |
67 | look again in the table whether the dataset is now available. We | |
68 | simply insert it. It does not matter if it is in there twice. The | |
69 | pruning function only will look at the timestamp. */ | |
d19687d6 UD |
70 | |
71 | /* We allocate all data in one memory block: the iov vector, | |
72 | the response header and the dataset itself. */ | |
73 | struct dataset | |
74 | { | |
75 | struct datahead head; | |
76 | ai_response_header resp; | |
77 | char strdata[0]; | |
78 | } *dataset = NULL; | |
79 | ||
80 | if (__builtin_expect (debug_level > 0, 0)) | |
81 | { | |
82 | if (he == NULL) | |
83 | dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key); | |
84 | else | |
85 | dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key); | |
86 | } | |
87 | ||
d19687d6 UD |
88 | static service_user *hosts_database; |
89 | service_user *nip = NULL; | |
90 | int no_more; | |
91 | int rc6 = 0; | |
92 | int rc4 = 0; | |
93 | int herrno = 0; | |
94 | ||
95 | if (hosts_database != NULL) | |
96 | { | |
97 | nip = hosts_database; | |
98 | no_more = 0; | |
99 | } | |
100 | else | |
101 | no_more = __nss_database_lookup ("hosts", NULL, | |
102 | "dns [!UNAVAIL=return] files", &nip); | |
103 | ||
104 | if (__res_maybe_init (&_res, 0) == -1) | |
105 | no_more = 1; | |
106 | ||
107 | /* If we are looking for both IPv4 and IPv6 address we don't want | |
108 | the lookup functions to automatically promote IPv4 addresses to | |
109 | IPv6 addresses. Currently this is decided by setting the | |
110 | RES_USE_INET6 bit in _res.options. */ | |
111 | int old_res_options = _res.options; | |
112 | _res.options &= ~RES_USE_INET6; | |
113 | ||
ea42a20c | 114 | size_t tmpbuf6len = 1024; |
d19687d6 UD |
115 | char *tmpbuf6 = alloca (tmpbuf6len); |
116 | size_t tmpbuf4len = 0; | |
117 | char *tmpbuf4 = NULL; | |
384ca551 | 118 | int32_t ttl = INT32_MAX; |
d19687d6 UD |
119 | ssize_t total = 0; |
120 | char *key_copy = NULL; | |
121 | bool alloca_used = false; | |
a4c7ea7b | 122 | time_t timeout = MAX_TIMEOUT_VALUE; |
d19687d6 UD |
123 | |
124 | while (!no_more) | |
125 | { | |
1eb946b9 | 126 | void *cp; |
d19687d6 | 127 | int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL }; |
1eb946b9 UD |
128 | int naddrs = 0; |
129 | size_t addrslen = 0; | |
5c9629d2 | 130 | char *canon = NULL; |
1eb946b9 UD |
131 | size_t canonlen; |
132 | ||
133 | nss_gethostbyname4_r fct4 = __nss_lookup_function (nip, | |
134 | "gethostbyname4_r"); | |
135 | if (fct4 != NULL) | |
136 | { | |
ea42a20c UD |
137 | struct gaih_addrtuple atmem; |
138 | struct gaih_addrtuple *at; | |
1eb946b9 UD |
139 | while (1) |
140 | { | |
ea42a20c | 141 | at = &atmem; |
1eb946b9 | 142 | rc6 = 0; |
5c9629d2 UD |
143 | herrno = 0; |
144 | status[1] = DL_CALL_FCT (fct4, (key, &at, tmpbuf6, tmpbuf6len, | |
1eb946b9 | 145 | &rc6, &herrno, &ttl)); |
5c9629d2 UD |
146 | if (rc6 != ERANGE || (herrno != NETDB_INTERNAL |
147 | && herrno != TRY_AGAIN)) | |
1eb946b9 UD |
148 | break; |
149 | tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len); | |
150 | } | |
151 | ||
152 | if (rc6 != 0 && herrno == NETDB_INTERNAL) | |
153 | goto out; | |
d19687d6 | 154 | |
5c9629d2 | 155 | if (status[1] != NSS_STATUS_SUCCESS) |
1eb946b9 UD |
156 | goto next_nip; |
157 | ||
158 | /* We found the data. Count the addresses and the size. */ | |
ea42a20c | 159 | for (const struct gaih_addrtuple *at2 = at = &atmem; at2 != NULL; |
5c9629d2 | 160 | at2 = at2->next) |
1eb946b9 UD |
161 | { |
162 | ++naddrs; | |
5c9629d2 UD |
163 | /* We do not handle anything other than IPv4 and IPv6 |
164 | addresses. The getaddrinfo implementation does not | |
165 | either so it is not worth trying to do more. */ | |
1eb946b9 UD |
166 | if (at2->family == AF_INET) |
167 | addrslen += INADDRSZ; | |
5c9629d2 | 168 | else if (at2->family == AF_INET6) |
1eb946b9 | 169 | addrslen += IN6ADDRSZ; |
1eb946b9 UD |
170 | } |
171 | canon = at->name; | |
172 | canonlen = strlen (canon) + 1; | |
173 | ||
174 | total = sizeof (*dataset) + naddrs + addrslen + canonlen; | |
175 | ||
176 | /* Now we can allocate the data structure. If the TTL of the | |
177 | entry is reported as zero do not cache the entry at all. */ | |
178 | if (ttl != 0 && he == NULL) | |
20e498bd UD |
179 | dataset = (struct dataset *) mempool_alloc (db, total |
180 | + req->key_len, 1); | |
1eb946b9 UD |
181 | |
182 | if (dataset == NULL) | |
183 | { | |
184 | /* We cannot permanently add the result in the moment. But | |
185 | we can provide the result as is. Store the data in some | |
186 | temporary memory. */ | |
187 | dataset = (struct dataset *) alloca (total + req->key_len); | |
188 | ||
189 | /* We cannot add this record to the permanent database. */ | |
190 | alloca_used = true; | |
191 | } | |
d1fe1f22 | 192 | |
1eb946b9 | 193 | /* Fill in the address and address families. */ |
5c9629d2 | 194 | char *addrs = dataset->strdata; |
1eb946b9 UD |
195 | uint8_t *family = (uint8_t *) (addrs + addrslen); |
196 | ||
5c9629d2 UD |
197 | for (const struct gaih_addrtuple *at2 = at; at2 != NULL; |
198 | at2 = at2->next) | |
1eb946b9 UD |
199 | { |
200 | *family++ = at2->family; | |
201 | if (at2->family == AF_INET) | |
202 | addrs = mempcpy (addrs, at2->addr, INADDRSZ); | |
5c9629d2 | 203 | else if (at2->family == AF_INET6) |
1eb946b9 | 204 | addrs = mempcpy (addrs, at2->addr, IN6ADDRSZ); |
1eb946b9 UD |
205 | } |
206 | ||
207 | cp = family; | |
208 | } | |
209 | else | |
d19687d6 | 210 | { |
1eb946b9 UD |
211 | /* Prefer the function which also returns the TTL and |
212 | canonical name. */ | |
213 | nss_gethostbyname3_r fct = __nss_lookup_function (nip, | |
214 | "gethostbyname3_r"); | |
215 | if (fct == NULL) | |
216 | fct = __nss_lookup_function (nip, "gethostbyname2_r"); | |
217 | ||
218 | if (fct == NULL) | |
219 | goto next_nip; | |
220 | ||
d19687d6 UD |
221 | struct hostent th[2]; |
222 | ||
223 | /* Collect IPv6 information first. */ | |
224 | while (1) | |
225 | { | |
226 | rc6 = 0; | |
227 | status[0] = DL_CALL_FCT (fct, (key, AF_INET6, &th[0], tmpbuf6, | |
1eb946b9 UD |
228 | tmpbuf6len, &rc6, &herrno, &ttl, |
229 | &canon)); | |
d19687d6 UD |
230 | if (rc6 != ERANGE || herrno != NETDB_INTERNAL) |
231 | break; | |
232 | tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len); | |
233 | } | |
234 | ||
235 | if (rc6 != 0 && herrno == NETDB_INTERNAL) | |
236 | goto out; | |
237 | ||
238 | /* If the IPv6 lookup has been successful do not use the | |
239 | buffer used in that lookup, use a new one. */ | |
240 | if (status[0] == NSS_STATUS_SUCCESS && rc6 == 0) | |
241 | { | |
242 | tmpbuf4len = 512; | |
243 | tmpbuf4 = alloca (tmpbuf4len); | |
244 | } | |
245 | else | |
246 | { | |
247 | tmpbuf4len = tmpbuf6len; | |
248 | tmpbuf4 = tmpbuf6; | |
249 | } | |
250 | ||
ffb1b882 | 251 | /* Next collect IPv4 information. */ |
d19687d6 UD |
252 | while (1) |
253 | { | |
254 | rc4 = 0; | |
255 | status[1] = DL_CALL_FCT (fct, (key, AF_INET, &th[1], tmpbuf4, | |
d1fe1f22 | 256 | tmpbuf4len, &rc4, &herrno, |
384ca551 | 257 | ttl == INT32_MAX ? &ttl : NULL, |
d1fe1f22 | 258 | canon == NULL ? &canon : NULL)); |
d19687d6 UD |
259 | if (rc4 != ERANGE || herrno != NETDB_INTERNAL) |
260 | break; | |
d1fe1f22 | 261 | tmpbuf4 = extend_alloca (tmpbuf4, tmpbuf4len, 2 * tmpbuf4len); |
d19687d6 UD |
262 | } |
263 | ||
ffb1b882 | 264 | if (rc4 != 0 && herrno == NETDB_INTERNAL) |
d19687d6 UD |
265 | goto out; |
266 | ||
1eb946b9 UD |
267 | if (status[0] != NSS_STATUS_SUCCESS |
268 | && status[1] != NSS_STATUS_SUCCESS) | |
269 | goto next_nip; | |
270 | ||
271 | /* We found the data. Count the addresses and the size. */ | |
272 | for (int j = 0; j < 2; ++j) | |
273 | if (status[j] == NSS_STATUS_SUCCESS) | |
274 | for (int i = 0; th[j].h_addr_list[i] != NULL; ++i) | |
275 | { | |
276 | ++naddrs; | |
277 | addrslen += th[j].h_length; | |
278 | } | |
279 | ||
280 | if (canon == NULL) | |
d19687d6 | 281 | { |
1eb946b9 UD |
282 | /* Determine the canonical name. */ |
283 | nss_getcanonname_r cfct; | |
284 | cfct = __nss_lookup_function (nip, "getcanonname_r"); | |
285 | if (cfct != NULL) | |
286 | { | |
287 | const size_t max_fqdn_len = 256; | |
288 | char *buf = alloca (max_fqdn_len); | |
289 | char *s; | |
290 | int rc; | |
291 | ||
292 | if (DL_CALL_FCT (cfct, (key, buf, max_fqdn_len, &s, | |
293 | &rc, &herrno)) | |
294 | == NSS_STATUS_SUCCESS) | |
295 | canon = s; | |
296 | else | |
297 | /* Set to name now to avoid using gethostbyaddr. */ | |
298 | canon = key; | |
299 | } | |
300 | else | |
301 | { | |
20e498bd | 302 | struct hostent *hstent = NULL; |
1eb946b9 | 303 | int herrno; |
20e498bd | 304 | struct hostent hstent_mem; |
1eb946b9 UD |
305 | void *addr; |
306 | size_t addrlen; | |
307 | int addrfamily; | |
308 | ||
309 | if (status[1] == NSS_STATUS_SUCCESS) | |
310 | { | |
311 | addr = th[1].h_addr_list[0]; | |
312 | addrlen = sizeof (struct in_addr); | |
313 | addrfamily = AF_INET; | |
314 | } | |
315 | else | |
d19687d6 | 316 | { |
1eb946b9 UD |
317 | addr = th[0].h_addr_list[0]; |
318 | addrlen = sizeof (struct in6_addr); | |
319 | addrfamily = AF_INET6; | |
d19687d6 UD |
320 | } |
321 | ||
1eb946b9 UD |
322 | size_t tmpbuflen = 512; |
323 | char *tmpbuf = alloca (tmpbuflen); | |
324 | int rc; | |
325 | while (1) | |
d1fe1f22 | 326 | { |
1eb946b9 | 327 | rc = __gethostbyaddr2_r (addr, addrlen, addrfamily, |
20e498bd UD |
328 | &hstent_mem, tmpbuf, tmpbuflen, |
329 | &hstent, &herrno, NULL); | |
1eb946b9 UD |
330 | if (rc != ERANGE || herrno != NETDB_INTERNAL) |
331 | break; | |
332 | tmpbuf = extend_alloca (tmpbuf, tmpbuflen, | |
333 | tmpbuflen * 2); | |
d1fe1f22 | 334 | } |
1eb946b9 UD |
335 | |
336 | if (rc == 0) | |
d1fe1f22 | 337 | { |
20e498bd UD |
338 | if (hstent != NULL) |
339 | canon = hstent->h_name; | |
d1fe1f22 | 340 | else |
1eb946b9 | 341 | canon = key; |
d1fe1f22 | 342 | } |
d19687d6 | 343 | } |
1eb946b9 | 344 | } |
d19687d6 | 345 | |
1eb946b9 UD |
346 | canonlen = canon == NULL ? 0 : (strlen (canon) + 1); |
347 | ||
348 | total = sizeof (*dataset) + naddrs + addrslen + canonlen; | |
d19687d6 | 349 | |
d19687d6 | 350 | |
1eb946b9 UD |
351 | /* Now we can allocate the data structure. If the TTL of the |
352 | entry is reported as zero do not cache the entry at all. */ | |
353 | if (ttl != 0 && he == NULL) | |
20e498bd UD |
354 | dataset = (struct dataset *) mempool_alloc (db, total |
355 | + req->key_len, 1); | |
1eb946b9 UD |
356 | |
357 | if (dataset == NULL) | |
358 | { | |
359 | /* We cannot permanently add the result in the moment. But | |
360 | we can provide the result as is. Store the data in some | |
361 | temporary memory. */ | |
362 | dataset = (struct dataset *) alloca (total + req->key_len); | |
363 | ||
364 | /* We cannot add this record to the permanent database. */ | |
365 | alloca_used = true; | |
366 | } | |
d19687d6 | 367 | |
1eb946b9 | 368 | /* Fill in the address and address families. */ |
5c9629d2 | 369 | char *addrs = dataset->strdata; |
1eb946b9 UD |
370 | uint8_t *family = (uint8_t *) (addrs + addrslen); |
371 | ||
372 | for (int j = 0; j < 2; ++j) | |
373 | if (status[j] == NSS_STATUS_SUCCESS) | |
374 | for (int i = 0; th[j].h_addr_list[i] != NULL; ++i) | |
375 | { | |
376 | addrs = mempcpy (addrs, th[j].h_addr_list[i], | |
377 | th[j].h_length); | |
378 | *family++ = th[j].h_addrtype; | |
d19687d6 UD |
379 | } |
380 | ||
1eb946b9 UD |
381 | cp = family; |
382 | } | |
d19687d6 | 383 | |
1eb946b9 UD |
384 | /* Fill in the rest of the dataset. */ |
385 | dataset->head.allocsize = total + req->key_len; | |
386 | dataset->head.recsize = total - offsetof (struct dataset, resp); | |
387 | dataset->head.notfound = false; | |
388 | dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1); | |
389 | dataset->head.usable = true; | |
d19687d6 | 390 | |
1eb946b9 | 391 | /* Compute the timeout time. */ |
a4c7ea7b UD |
392 | dataset->head.ttl = ttl == INT32_MAX ? db->postimeout : ttl; |
393 | timeout = dataset->head.timeout = time (NULL) + dataset->head.ttl; | |
d19687d6 | 394 | |
1eb946b9 UD |
395 | dataset->resp.version = NSCD_VERSION; |
396 | dataset->resp.found = 1; | |
397 | dataset->resp.naddrs = naddrs; | |
398 | dataset->resp.addrslen = addrslen; | |
399 | dataset->resp.canonlen = canonlen; | |
400 | dataset->resp.error = NETDB_SUCCESS; | |
d19687d6 | 401 | |
1eb946b9 UD |
402 | if (canon != NULL) |
403 | cp = mempcpy (cp, canon, canonlen); | |
d19687d6 | 404 | |
1eb946b9 UD |
405 | key_copy = memcpy (cp, key, req->key_len); |
406 | ||
5c9629d2 UD |
407 | assert (cp == (char *) dataset + total); |
408 | ||
1eb946b9 UD |
409 | /* Now we can determine whether on refill we have to create a |
410 | new record or not. */ | |
411 | if (he != NULL) | |
412 | { | |
413 | assert (fd == -1); | |
414 | ||
415 | if (total + req->key_len == dh->allocsize | |
416 | && total - offsetof (struct dataset, resp) == dh->recsize | |
417 | && memcmp (&dataset->resp, dh->data, | |
418 | dh->allocsize - offsetof (struct dataset, | |
419 | resp)) == 0) | |
420 | { | |
421 | /* The data has not changed. We will just bump the | |
422 | timeout value. Note that the new record has been | |
423 | allocated on the stack and need not be freed. */ | |
424 | dh->timeout = dataset->head.timeout; | |
a4c7ea7b | 425 | dh->ttl = dataset->head.ttl; |
1eb946b9 UD |
426 | ++dh->nreloads; |
427 | } | |
428 | else | |
429 | { | |
430 | /* We have to create a new record. Just allocate | |
431 | appropriate memory and copy it. */ | |
432 | struct dataset *newp | |
433 | = (struct dataset *) mempool_alloc (db, total + req->key_len, | |
20e498bd | 434 | 1); |
1eb946b9 UD |
435 | if (__builtin_expect (newp != NULL, 1)) |
436 | { | |
437 | /* Adjust pointer into the memory block. */ | |
438 | key_copy = (char *) newp + (key_copy - (char *) dataset); | |
439 | ||
440 | dataset = memcpy (newp, dataset, total + req->key_len); | |
441 | alloca_used = false; | |
d19687d6 | 442 | } |
1eb946b9 UD |
443 | |
444 | /* Mark the old record as obsolete. */ | |
445 | dh->usable = false; | |
446 | } | |
447 | } | |
448 | else | |
449 | { | |
450 | /* We write the dataset before inserting it to the database | |
451 | since while inserting this thread might block and so | |
452 | would unnecessarily let the receiver wait. */ | |
453 | assert (fd != -1); | |
d19687d6 | 454 | |
eac10791 | 455 | #ifdef HAVE_SENDFILE |
1eb946b9 UD |
456 | if (__builtin_expect (db->mmap_used, 1) && !alloca_used) |
457 | { | |
458 | assert (db->wr_fd != -1); | |
459 | assert ((char *) &dataset->resp > (char *) db->data); | |
ea547a1a | 460 | assert ((char *) dataset - (char *) db->head + total |
1eb946b9 UD |
461 | <= (sizeof (struct database_pers_head) |
462 | + db->head->module * sizeof (ref_t) | |
463 | + db->head->data_size)); | |
464 | ssize_t written; | |
465 | written = sendfileall (fd, db->wr_fd, (char *) &dataset->resp | |
ea547a1a | 466 | - (char *) db->head, dataset->head.recsize); |
eac10791 | 467 | # ifndef __ASSUME_SENDFILE |
1eb946b9 UD |
468 | if (written == -1 && errno == ENOSYS) |
469 | goto use_write; | |
eac10791 | 470 | # endif |
1eb946b9 UD |
471 | } |
472 | else | |
eac10791 | 473 | # ifndef __ASSUME_SENDFILE |
1eb946b9 | 474 | use_write: |
eac10791 UD |
475 | # endif |
476 | #endif | |
ea547a1a | 477 | writeall (fd, &dataset->resp, dataset->head.recsize); |
d19687d6 UD |
478 | } |
479 | ||
1eb946b9 UD |
480 | goto out; |
481 | ||
482 | next_nip: | |
d19687d6 UD |
483 | if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN) |
484 | break; | |
485 | ||
486 | if (nip->next == NULL) | |
487 | no_more = -1; | |
488 | else | |
489 | nip = nip->next; | |
490 | } | |
491 | ||
492 | /* No result found. Create a negative result record. */ | |
493 | if (he != NULL && rc4 == EAGAIN) | |
494 | { | |
495 | /* If we have an old record available but cannot find one now | |
496 | because the service is not available we keep the old record | |
497 | and make sure it does not get removed. */ | |
498 | if (reload_count != UINT_MAX && dh->nreloads == reload_count) | |
499 | /* Do not reset the value if we never not reload the record. */ | |
500 | dh->nreloads = reload_count - 1; | |
a4c7ea7b UD |
501 | |
502 | /* Reload with the same time-to-live value. */ | |
503 | timeout = dh->timeout = time (NULL) + dh->ttl; | |
d19687d6 UD |
504 | } |
505 | else | |
506 | { | |
507 | /* We have no data. This means we send the standard reply for | |
508 | this case. */ | |
509 | total = sizeof (notfound); | |
510 | ||
511 | if (fd != -1) | |
2c210d1e | 512 | TEMP_FAILURE_RETRY (send (fd, ¬found, total, MSG_NOSIGNAL)); |
d19687d6 | 513 | |
20e498bd | 514 | dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len, 1); |
d19687d6 UD |
515 | /* If we cannot permanently store the result, so be it. */ |
516 | if (dataset != NULL) | |
517 | { | |
518 | dataset->head.allocsize = sizeof (struct dataset) + req->key_len; | |
519 | dataset->head.recsize = total; | |
520 | dataset->head.notfound = true; | |
521 | dataset->head.nreloads = 0; | |
522 | dataset->head.usable = true; | |
523 | ||
524 | /* Compute the timeout time. */ | |
a4c7ea7b UD |
525 | timeout = dataset->head.timeout = time (NULL) + db->negtimeout; |
526 | dataset->head.ttl = db->negtimeout; | |
d19687d6 UD |
527 | |
528 | /* This is the reply. */ | |
529 | memcpy (&dataset->resp, ¬found, total); | |
530 | ||
531 | /* Copy the key data. */ | |
532 | key_copy = memcpy (dataset->strdata, key, req->key_len); | |
533 | } | |
d19687d6 UD |
534 | } |
535 | ||
536 | out: | |
034807a9 | 537 | _res.options |= old_res_options & RES_USE_INET6; |
d19687d6 | 538 | |
d19687d6 UD |
539 | if (dataset != NULL && !alloca_used) |
540 | { | |
541 | /* If necessary, we also propagate the data to disk. */ | |
542 | if (db->persistent) | |
543 | { | |
544 | // XXX async OK? | |
545 | uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1; | |
546 | msync ((void *) pval, | |
547 | ((uintptr_t) dataset & pagesize_m1) + total + req->key_len, | |
548 | MS_ASYNC); | |
549 | } | |
550 | ||
7e71e55f | 551 | (void) cache_add (req->type, key_copy, req->key_len, &dataset->head, |
528741cb | 552 | true, db, uid, he == NULL); |
d19687d6 | 553 | |
00ebd7ed UD |
554 | pthread_rwlock_unlock (&db->lock); |
555 | ||
d19687d6 UD |
556 | /* Mark the old entry as obsolete. */ |
557 | if (dh != NULL) | |
558 | dh->usable = false; | |
559 | } | |
a4c7ea7b UD |
560 | |
561 | return timeout; | |
d19687d6 UD |
562 | } |
563 | ||
564 | ||
565 | void | |
566 | addhstai (struct database_dyn *db, int fd, request_header *req, void *key, | |
567 | uid_t uid) | |
568 | { | |
569 | addhstaiX (db, fd, req, key, uid, NULL, NULL); | |
570 | } | |
571 | ||
572 | ||
a4c7ea7b | 573 | time_t |
d19687d6 UD |
574 | readdhstai (struct database_dyn *db, struct hashentry *he, struct datahead *dh) |
575 | { | |
576 | request_header req = | |
577 | { | |
578 | .type = GETAI, | |
579 | .key_len = he->len | |
580 | }; | |
581 | ||
a4c7ea7b | 582 | return addhstaiX (db, -1, &req, db->data + he->key, he->owner, he, dh); |
d19687d6 | 583 | } |