1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20: #include <assert.h>
21: #include <errno.h>
22: #include <libintl.h>
23: #include <netdb.h>
24: #include <string.h>
25: #include <time.h>
26: #include <unistd.h>
27: #include <sys/mman.h>
28:
29: #include "dbg_log.h"
30: #include "nscd.h"
31: #ifdef HAVE_SENDFILE
32: # include <kernel-features.h>
33: #endif
34:
35:
36: typedef enum nss_status (*nss_gethostbyname3_r)
37: (const char *name, int af, struct hostent *host,
38: char *buffer, size_t buflen, int *errnop,
39: int *h_errnop, int32_t *, char **);
40: typedef enum nss_status (*nss_getcanonname_r)
41: (const char *name, char *buffer, size_t buflen, char **result,
42: int *errnop, int *h_errnop);
43:
44:
45: static const ai_response_header notfound =
46: {
47: .version = NSCD_VERSION,
48: .found = 0,
49: .naddrs = 0,
50: .addrslen = 0,
51: .canonlen = 0,
52: .error = 0
53: };
54:
55:
56: static void
57: addhstaiX (struct database_dyn *db, int fd, request_header *req,
58: void *key, uid_t uid, struct hashentry *he, struct datahead *dh)
59: {
60:
61:
62:
63:
64:
65:
66:
67: struct dataset
68: {
69: struct datahead head;
70: ai_response_header resp;
71: char strdata[0];
72: } *dataset = NULL;
73:
74: if (__builtin_expect (debug_level > 0, 0))
75: {
76: if (he == NULL)
77: dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key);
78: else
79: dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key);
80: }
81:
82: static service_user *hosts_database;
83: service_user *nip = NULL;
84: int no_more;
85: int rc6 = 0;
86: int rc4 = 0;
87: int herrno = 0;
88:
89: if (hosts_database != NULL)
90: {
91: nip = hosts_database;
92: no_more = 0;
93: }
94: else
95: no_more = __nss_database_lookup ("hosts", NULL,
96: "dns [!UNAVAIL=return] files", &nip);
97:
98: if (__res_maybe_init (&_res, 0) == -1)
99: no_more = 1;
100:
101:
102:
103:
104:
105: int old_res_options = _res.options;
106: _res.options &= ~RES_USE_INET6;
107:
108: size_t tmpbuf6len = 512;
109: char *tmpbuf6 = alloca (tmpbuf6len);
110: size_t tmpbuf4len = 0;
111: char *tmpbuf4 = NULL;
112: char *canon = NULL;
113: int32_t ttl = UINT32_MAX;
114: ssize_t total = 0;
115: char *key_copy = NULL;
116: bool alloca_used = false;
117:
118: while (!no_more)
119: {
120: int status[2] = { NSS_STATUS_UNAVAIL, NSS_STATUS_UNAVAIL };
121:
122:
123: nss_gethostbyname3_r fct = __nss_lookup_function (nip,
124: "gethostbyname3_r");
125: if (fct == NULL)
126: fct = __nss_lookup_function (nip, "gethostbyname2_r");
127:
128: if (fct != NULL)
129: {
130: struct hostent th[2];
131:
132:
133: while (1)
134: {
135: rc6 = 0;
136: status[0] = DL_CALL_FCT (fct, (key, AF_INET6, &th[0], tmpbuf6,
137: tmpbuf6len, &rc6, &herrno,
138: &ttl, &canon));
139: if (rc6 != ERANGE || herrno != NETDB_INTERNAL)
140: break;
141: tmpbuf6 = extend_alloca (tmpbuf6, tmpbuf6len, 2 * tmpbuf6len);
142: }
143:
144: if (rc6 != 0 && herrno == NETDB_INTERNAL)
145: goto out;
146:
147:
148:
149: if (status[0] == NSS_STATUS_SUCCESS && rc6 == 0)
150: {
151: tmpbuf4len = 512;
152: tmpbuf4 = alloca (tmpbuf4len);
153: }
154: else
155: {
156: tmpbuf4len = tmpbuf6len;
157: tmpbuf4 = tmpbuf6;
158: }
159:
160:
161: while (1)
162: {
163: rc4 = 0;
164: status[1] = DL_CALL_FCT (fct, (key, AF_INET, &th[1], tmpbuf4,
165: tmpbuf4len, &rc4, &herrno,
166: ttl == UINT32_MAX ? &ttl : NULL,
167: canon == NULL ? &canon : NULL));
168: if (rc4 != ERANGE || herrno != NETDB_INTERNAL)
169: break;
170: tmpbuf4 = extend_alloca (tmpbuf4, tmpbuf4len, 2 * tmpbuf4len);
171: }
172:
173: if (rc4 != 0 || herrno == NETDB_INTERNAL)
174: goto out;
175:
176: if (status[0] == NSS_STATUS_SUCCESS
177: || status[1] == NSS_STATUS_SUCCESS)
178: {
179:
180: int naddrs = 0;
181: size_t addrslen = 0;
182: for (int j = 0; j < 2; ++j)
183: if (status[j] == NSS_STATUS_SUCCESS)
184: for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
185: {
186: ++naddrs;
187: addrslen += th[j].h_length;
188: }
189:
190: if (canon == NULL)
191: {
192:
193: nss_getcanonname_r cfct;
194: cfct = __nss_lookup_function (nip, "getcanonname_r");
195: if (cfct != NULL)
196: {
197: const size_t max_fqdn_len = 256;
198: char *buf = alloca (max_fqdn_len);
199: char *s;
200: int rc;
201:
202: if (DL_CALL_FCT (cfct, (key, buf, max_fqdn_len, &s, &rc,
203: &herrno)) == NSS_STATUS_SUCCESS)
204: canon = s;
205: else
206:
207: canon = key;
208: }
209: else
210: {
211: struct hostent *he = NULL;
212: int herrno;
213: struct hostent he_mem;
214: void *addr;
215: size_t addrlen;
216: int addrfamily;
217:
218: if (status[1] == NSS_STATUS_SUCCESS)
219: {
220: addr = th[1].h_addr_list[0];
221: addrlen = sizeof (struct in_addr);
222: addrfamily = AF_INET;
223: }
224: else
225: {
226: addr = th[0].h_addr_list[0];
227: addrlen = sizeof (struct in6_addr);
228: addrfamily = AF_INET6;
229: }
230:
231: size_t tmpbuflen = 512;
232: char *tmpbuf = alloca (tmpbuflen);
233: int rc;
234: while (1)
235: {
236: rc = __gethostbyaddr_r (addr, addrlen, addrfamily,
237: &he_mem, tmpbuf, tmpbuflen,
238: &he, &herrno);
239: if (rc != ERANGE || herrno != NETDB_INTERNAL)
240: break;
241: tmpbuf = extend_alloca (tmpbuf, tmpbuflen,
242: tmpbuflen * 2);
243: }
244:
245: if (rc == 0)
246: {
247: if (he != NULL)
248: canon = he->h_name;
249: else
250: canon = key;
251: }
252: }
253: }
254: size_t canonlen = canon == NULL ? 0 : (strlen (canon) + 1);
255:
256: total = sizeof (*dataset) + naddrs + addrslen + canonlen;
257:
258:
259:
260:
261: if (ttl != 0 && he == NULL)
262: {
263: dataset = (struct dataset *) mempool_alloc (db,
264: total
265: + req->key_len);
266: if (dataset == NULL)
267: ++db->head->addfailed;
268: }
269:
270: if (dataset == NULL)
271: {
272:
273:
274:
275: dataset = (struct dataset *) alloca (total + req->key_len);
276:
277:
278: alloca_used = true;
279: }
280:
281: dataset->head.allocsize = total + req->key_len;
282: dataset->head.recsize = total - offsetof (struct dataset, resp);
283: dataset->head.notfound = false;
284: dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
285: dataset->head.usable = true;
286:
287:
288: dataset->head.timeout = time (NULL) + MIN (db->postimeout, ttl);
289:
290: dataset->resp.version = NSCD_VERSION;
291: dataset->resp.found = 1;
292: dataset->resp.naddrs = naddrs;
293: dataset->resp.addrslen = addrslen;
294: dataset->resp.canonlen = canonlen;
295: dataset->resp.error = NETDB_SUCCESS;
296:
297: char *addrs = (char *) (&dataset->resp + 1);
298: uint8_t *family = (uint8_t *) (addrs + addrslen);
299:
300: for (int j = 0; j < 2; ++j)
301: if (status[j] == NSS_STATUS_SUCCESS)
302: for (int i = 0; th[j].h_addr_list[i] != NULL; ++i)
303: {
304: addrs = mempcpy (addrs, th[j].h_addr_list[i],
305: th[j].h_length);
306: *family++ = th[j].h_addrtype;
307: }
308:
309: void *cp = family;
310: if (canon != NULL)
311: cp = mempcpy (cp, canon, canonlen);
312:
313: key_copy = memcpy (cp, key, req->key_len);
314:
315:
316:
317: if (he != NULL)
318: {
319: assert (fd == -1);
320:
321: if (total + req->key_len == dh->allocsize
322: && total - offsetof (struct dataset, resp) == dh->recsize
323: && memcmp (&dataset->resp, dh->data,
324: dh->allocsize
325: - offsetof (struct dataset, resp)) == 0)
326: {
327:
328:
329:
330: dh->timeout = dataset->head.timeout;
331: ++dh->nreloads;
332: }
333: else
334: {
335:
336:
337: struct dataset *newp
338: = (struct dataset *) mempool_alloc (db,
339: total
340: + req->key_len);
341: if (newp != NULL)
342: {
343:
344: key_copy = (char *) newp + (key_copy
345: - (char *) dataset);
346:
347: dataset = memcpy (newp, dataset,
348: total + req->key_len);
349: alloca_used = false;
350: }
351:
352:
353: dh->usable = false;
354: }
355: }
356: else
357: {
358:
359:
360:
361:
362: assert (fd != -1);
363:
364: #ifdef HAVE_SENDFILE
365: if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
366: {
367: assert (db->wr_fd != -1);
368: assert ((char *) &dataset->resp > (char *) db->data);
369: assert ((char *) &dataset->resp - (char *) db->head
370: + total
371: <= (sizeof (struct database_pers_head)
372: + db->head->module * sizeof (ref_t)
373: + db->head->data_size));
374: ssize_t written;
375: written = sendfileall (fd, db->wr_fd,
376: (char *) &dataset->resp
377: - (char *) db->head, total);
378: # ifndef __ASSUME_SENDFILE
379: if (written == -1 && errno == ENOSYS)
380: goto use_write;
381: # endif
382: }
383: else
384: # ifndef __ASSUME_SENDFILE
385: use_write:
386: # endif
387: #endif
388: writeall (fd, &dataset->resp, total);
389: }
390:
391: goto out;
392: }
393:
394: }
395:
396: if (nss_next_action (nip, status[1]) == NSS_ACTION_RETURN)
397: break;
398:
399: if (nip->next == NULL)
400: no_more = -1;
401: else
402: nip = nip->next;
403: }
404:
405:
406: if (he != NULL && rc4 == EAGAIN)
407: {
408:
409:
410:
411: if (reload_count != UINT_MAX && dh->nreloads == reload_count)
412:
413: dh->nreloads = reload_count - 1;
414: }
415: else
416: {
417:
418:
419: total = sizeof (notfound);
420:
421: if (fd != -1)
422: TEMP_FAILURE_RETRY (send (fd, ¬found, total, MSG_NOSIGNAL));
423:
424: dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
425:
426: if (dataset != NULL)
427: {
428: dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
429: dataset->head.recsize = total;
430: dataset->head.notfound = true;
431: dataset->head.nreloads = 0;
432: dataset->head.usable = true;
433:
434:
435: dataset->head.timeout = time (NULL) + db->negtimeout;
436:
437:
438: memcpy (&dataset->resp, ¬found, total);
439:
440:
441: key_copy = memcpy (dataset->strdata, key, req->key_len);
442: }
443: else
444: ++db->head->addfailed;
445: }
446:
447: out:
448: _res.options = old_res_options;
449:
450: if (dataset != NULL && !alloca_used)
451: {
452:
453: if (db->persistent)
454: {
455:
456: uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
457: msync ((void *) pval,
458: ((uintptr_t) dataset & pagesize_m1) + total + req->key_len,
459: MS_ASYNC);
460: }
461:
462:
463: pthread_rwlock_rdlock (&db->lock);
464:
465: if (cache_add (req->type, key_copy, req->key_len, &dataset->head, true,
466: db, uid) < 0)
467:
468: dataset->head.usable = false;
469:
470: pthread_rwlock_unlock (&db->lock);
471:
472:
473: if (dh != NULL)
474: dh->usable = false;
475: }
476: }
477:
478:
479: void
480: addhstai (struct database_dyn *db, int fd, request_header *req, void *key,
481: uid_t uid)
482: {
483: addhstaiX (db, fd, req, key, uid, NULL, NULL);
484: }
485:
486:
487: void
488: readdhstai (struct database_dyn *db, struct hashentry *he, struct datahead *dh)
489: {
490: request_header req =
491: {
492: .type = GETAI,
493: .key_len = he->len
494: };
495:
496: addhstaiX (db, -1, &req, db->data + he->key, he->owner, he, dh);
497: }