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