1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20: #include <alloca.h>
21: #include <assert.h>
22: #include <errno.h>
23: #include <libintl.h>
24: #include <netdb.h>
25: #include <unistd.h>
26: #include <sys/mman.h>
27: #include <kernel-features.h>
28:
29: #include "nscd.h"
30: #include "dbg_log.h"
31:
32:
33:
34: static const serv_response_header disabled =
35: {
36: .version = NSCD_VERSION,
37: .found = -1,
38: .s_name_len = 0,
39: .s_proto_len = 0,
40: .s_aliases_cnt = 0,
41: .s_port = -1
42: };
43:
44:
45: const struct iovec serv_iov_disabled =
46: {
47: .iov_base = (void *) &disabled,
48: .iov_len = sizeof (disabled)
49: };
50:
51:
52:
53: static const serv_response_header notfound =
54: {
55: .version = NSCD_VERSION,
56: .found = 0,
57: .s_name_len = 0,
58: .s_proto_len = 0,
59: .s_aliases_cnt = 0,
60: .s_port = -1
61: };
62:
63:
64: static void
65: cache_addserv (struct database_dyn *db, int fd, request_header *req,
66: const void *key, struct servent *serv, uid_t owner,
67: struct hashentry *he, struct datahead *dh, int errval)
68: {
69: ssize_t total;
70: ssize_t written;
71: time_t t = time (NULL);
72:
73:
74:
75: struct dataset
76: {
77: struct datahead head;
78: serv_response_header resp;
79: char strdata[0];
80: } *dataset;
81:
82: assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
83:
84: if (serv == NULL)
85: {
86: if (he != NULL && errval == EAGAIN)
87: {
88:
89:
90:
91: if (reload_count != UINT_MAX)
92:
93: dh->nreloads = reload_count - 1;
94:
95: written = total = 0;
96: }
97: else
98: {
99:
100:
101: total = sizeof (notfound);
102:
103: written = TEMP_FAILURE_RETRY (send (fd, ¬found, total,
104: MSG_NOSIGNAL));
105:
106: dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
107:
108: if (dataset != NULL)
109: {
110: dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
111: dataset->head.recsize = total;
112: dataset->head.notfound = true;
113: dataset->head.nreloads = 0;
114: dataset->head.usable = true;
115:
116:
117: dataset->head.timeout = t + db->negtimeout;
118:
119:
120: memcpy (&dataset->resp, ¬found, total);
121:
122:
123: memcpy (dataset->strdata, key, req->key_len);
124:
125:
126: if (db->persistent)
127: {
128:
129: uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
130: msync ((void *) pval,
131: ((uintptr_t) dataset & pagesize_m1)
132: + sizeof (struct dataset) + req->key_len, MS_ASYNC);
133: }
134:
135:
136: pthread_rwlock_rdlock (&db->lock);
137:
138: if (cache_add (req->type, &dataset->strdata, req->key_len,
139: &dataset->head, true, db, owner) < 0)
140:
141: dataset->head.usable = false;
142:
143: pthread_rwlock_unlock (&db->lock);
144:
145:
146: if (dh != NULL)
147: dh->usable = false;
148: }
149: else
150: ++db->head->addfailed;
151: }
152: }
153: else
154: {
155:
156: size_t s_name_len = strlen (serv->s_name) + 1;
157: size_t s_proto_len = strlen (serv->s_proto) + 1;
158: uint32_t *s_aliases_len;
159: size_t s_aliases_cnt;
160: char *aliases;
161: char *cp;
162: size_t cnt;
163:
164:
165: s_aliases_cnt = 0;
166: for (cnt = 0; serv->s_aliases[cnt] != NULL; ++cnt)
167: ++s_aliases_cnt;
168:
169: s_aliases_len = (uint32_t *) alloca (s_aliases_cnt * sizeof (uint32_t));
170: total = 0;
171: for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
172: {
173: s_aliases_len[cnt] = strlen (serv->s_aliases[cnt]) + 1;
174: total += s_aliases_len[cnt];
175: }
176:
177: total += (sizeof (struct dataset)
178: + s_name_len
179: + s_proto_len
180: + s_aliases_cnt * sizeof (uint32_t));
181: written = total;
182:
183:
184:
185:
186:
187: bool alloca_used = false;
188: dataset = NULL;
189:
190: if (he == NULL)
191: {
192: dataset = (struct dataset *) mempool_alloc (db,
193: total + req->key_len);
194: if (dataset == NULL)
195: ++db->head->addfailed;
196: }
197:
198: if (dataset == NULL)
199: {
200:
201:
202:
203: dataset = (struct dataset *) alloca (total + req->key_len);
204:
205:
206: alloca_used = true;
207: }
208:
209: dataset->head.allocsize = total + req->key_len;
210: dataset->head.recsize = total - offsetof (struct dataset, resp);
211: dataset->head.notfound = false;
212: dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
213: dataset->head.usable = true;
214:
215:
216: dataset->head.timeout = t + db->postimeout;
217:
218: dataset->resp.version = NSCD_VERSION;
219: dataset->resp.found = 1;
220: dataset->resp.s_name_len = s_name_len;
221: dataset->resp.s_proto_len = s_proto_len;
222: dataset->resp.s_port = serv->s_port;
223: dataset->resp.s_aliases_cnt = s_aliases_cnt;
224:
225: cp = dataset->strdata;
226:
227: cp = mempcpy (cp, serv->s_name, s_name_len);
228: cp = mempcpy (cp, serv->s_proto, s_proto_len);
229: cp = mempcpy (cp, s_aliases_len, s_aliases_cnt * sizeof (uint32_t));
230:
231:
232: aliases = cp;
233: for (cnt = 0; cnt < s_aliases_cnt; ++cnt)
234: cp = mempcpy (cp, serv->s_aliases[cnt], s_aliases_len[cnt]);
235:
236: assert (cp
237: == dataset->strdata + total - offsetof (struct dataset,
238: strdata));
239:
240: char *key_copy = memcpy (cp, key, req->key_len);
241:
242:
243:
244: if (he != NULL)
245: {
246: assert (fd == -1);
247:
248: if (total + req->key_len == dh->allocsize
249: && total - offsetof (struct dataset, resp) == dh->recsize
250: && memcmp (&dataset->resp, dh->data,
251: dh->allocsize - offsetof (struct dataset, resp)) == 0)
252: {
253:
254:
255:
256: dh->timeout = dataset->head.timeout;
257: ++dh->nreloads;
258: }
259: else
260: {
261:
262:
263: struct dataset *newp
264: = (struct dataset *) mempool_alloc (db, total + req->key_len);
265: if (newp != NULL)
266: {
267:
268: aliases = (char *) newp + (aliases - (char *) dataset);
269: assert (key_copy != NULL);
270: key_copy = (char *) newp + (key_copy - (char *) dataset);
271:
272: dataset = memcpy (newp, dataset, total + req->key_len);
273: alloca_used = false;
274: }
275:
276:
277: dh->usable = false;
278: }
279: }
280: else
281: {
282:
283:
284:
285: assert (fd != -1);
286:
287: #ifdef HAVE_SENDFILE
288: if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
289: {
290: assert (db->wr_fd != -1);
291: assert ((char *) &dataset->resp > (char *) db->data);
292: assert ((char *) &dataset->resp - (char *) db->head
293: + total
294: <= (sizeof (struct database_pers_head)
295: + db->head->module * sizeof (ref_t)
296: + db->head->data_size));
297: written = sendfileall (fd, db->wr_fd,
298: (char *) &dataset->resp
299: - (char *) db->head, total);
300: # ifndef __ASSUME_SENDFILE
301: if (written == -1 && errno == ENOSYS)
302: goto use_write;
303: # endif
304: }
305: else
306: # ifndef __ASSUME_SENDFILE
307: use_write:
308: # endif
309: #endif
310: written = writeall (fd, &dataset->resp, total);
311: }
312:
313:
314:
315: if (! alloca_used)
316: {
317:
318: if (db->persistent)
319: {
320:
321: uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
322: msync ((void *) pval,
323: ((uintptr_t) dataset & pagesize_m1)
324: + total + req->key_len, MS_ASYNC);
325: }
326:
327:
328: pthread_rwlock_rdlock (&db->lock);
329:
330: if (cache_add (req->type, key_copy, req->key_len,
331: &dataset->head, true, db, owner) < 0)
332:
333:
334: dataset->head.usable = false;
335:
336: pthread_rwlock_unlock (&db->lock);
337: }
338: }
339:
340: if (__builtin_expect (written != total, 0) && debug_level > 0)
341: {
342: char buf[256];
343: dbg_log (_("short write in %s: %s"), __FUNCTION__,
344: strerror_r (errno, buf, sizeof (buf)));
345: }
346: }
347:
348:
349: static int
350: lookup (int type, char *key, struct servent *resultbufp, char *buffer,
351: size_t buflen, struct servent **serv)
352: {
353: char *proto = strrchr (key, '/');
354: if (proto != NULL && proto != key)
355: {
356: key = strndupa (key, proto - key);
357: if (proto[1] == '\0')
358: proto = NULL;
359: else
360: ++proto;
361: }
362:
363: if (type == GETSERVBYNAME)
364: return __getservbyname_r (key, proto, resultbufp, buffer, buflen, serv);
365:
366: assert (type == GETSERVBYPORT);
367: return __getservbyport_r (atol (key), proto, resultbufp, buffer, buflen,
368: serv);
369: }
370:
371:
372: static void
373: addservbyX (struct database_dyn *db, int fd, request_header *req,
374: char *key, uid_t uid, struct hashentry *he, struct datahead *dh)
375: {
376:
377:
378:
379:
380: size_t buflen = 1024;
381: char *buffer = (char *) alloca (buflen);
382: struct servent resultbuf;
383: struct servent *serv;
384: bool use_malloc = false;
385: int errval = 0;
386:
387: if (__builtin_expect (debug_level > 0, 0))
388: {
389: if (he == NULL)
390: dbg_log (_("Haven't found \"%s\" in services cache!"), key);
391: else
392: dbg_log (_("Reloading \"%s\" in services cache!"), key);
393: }
394:
395: while (lookup (req->type, key, &resultbuf, buffer, buflen, &serv) != 0
396: && (errval = errno) == ERANGE)
397: {
398: errno = 0;
399:
400: if (__builtin_expect (buflen > 32768, 0))
401: {
402: char *old_buffer = buffer;
403: buflen *= 2;
404: buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
405: if (buffer == NULL)
406: {
407:
408:
409:
410: serv = NULL;
411: buffer = old_buffer;
412:
413:
414:
415:
416: errval = EAGAIN;
417: break;
418: }
419: use_malloc = true;
420: }
421: else
422:
423:
424: buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
425: }
426:
427: cache_addserv (db, fd, req, key, serv, uid, he, dh, errval);
428:
429: if (use_malloc)
430: free (buffer);
431: }
432:
433:
434: void
435: addservbyname (struct database_dyn *db, int fd, request_header *req,
436: void *key, uid_t uid)
437: {
438: addservbyX (db, fd, req, key, uid, NULL, NULL);
439: }
440:
441:
442: void
443: readdservbyname (struct database_dyn *db, struct hashentry *he,
444: struct datahead *dh)
445: {
446: request_header req =
447: {
448: .type = GETSERVBYNAME,
449: .key_len = he->len
450: };
451:
452: addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
453: }
454:
455:
456: void
457: addservbyport (struct database_dyn *db, int fd, request_header *req,
458: void *key, uid_t uid)
459: {
460: addservbyX (db, fd, req, key, uid, NULL, NULL);
461: }
462:
463:
464: void
465: readdservbyport (struct database_dyn *db, struct hashentry *he,
466: struct datahead *dh)
467: {
468: request_header req =
469: {
470: .type = GETSERVBYPORT,
471: .key_len = he->len
472: };
473:
474: addservbyX (db, -1, &req, db->data + he->key, he->owner, he, dh);
475: }