xref: /freebsd/contrib/ntp/libntp/ntp_intres.c (revision 397e83df75e0fcd0d3fcb95ae4d794cb7600fc89)
1 /*
2  * ntp_intres.c - Implements a generic blocking worker child or thread,
3  *		  initially to provide a nonblocking solution for DNS
4  *		  name to address lookups available with getaddrinfo().
5  *
6  * This is a new implementation as of 2009 sharing the filename and
7  * very little else with the prior implementation, which used a
8  * temporary file to receive a single set of requests from the parent,
9  * and a NTP mode 7 authenticated request to push back responses.
10  *
11  * A primary goal in rewriting this code was the need to support the
12  * pool configuration directive's requirement to retrieve multiple
13  * addresses resolving a single name, which has previously been
14  * satisfied with blocking resolver calls from the ntpd mainline code.
15  *
16  * A secondary goal is to provide a generic mechanism for other
17  * blocking operations to be delegated to a worker using a common
18  * model for both Unix and Windows ntpd.  ntp_worker.c, work_fork.c,
19  * and work_thread.c implement the generic mechanism.  This file
20  * implements the two current consumers, getaddrinfo_sometime() and the
21  * presently unused getnameinfo_sometime().
22  *
23  * Both routines deliver results to a callback and manage memory
24  * allocation, meaning there is no freeaddrinfo_sometime().
25  *
26  * The initial implementation for Unix uses a pair of unidirectional
27  * pipes, one each for requests and responses, connecting the forked
28  * blocking child worker with the ntpd mainline.  The threaded code
29  * uses arrays of pointers to queue requests and responses.
30  *
31  * The parent drives the process, including scheduling sleeps between
32  * retries.
33  *
34  * Memory is managed differently for a child process, which mallocs
35  * request buffers to read from the pipe into, whereas the threaded
36  * code mallocs a copy of the request to hand off to the worker via
37  * the queueing array.  The resulting request buffer is free()d by
38  * platform-independent code.  A wrinkle is the request needs to be
39  * available to the requestor during response processing.
40  *
41  * Response memory allocation is also platform-dependent.  With a
42  * separate process and pipes, the response is free()d after being
43  * written to the pipe.  With threads, the same memory is handed
44  * over and the requestor frees it after processing is completed.
45  *
46  * The code should be generalized to support threads on Unix using
47  * much of the same code used for Windows initially.
48  *
49  */
50 #ifdef HAVE_CONFIG_H
51 # include <config.h>
52 #endif
53 
54 #include "ntp_workimpl.h"
55 
56 #ifdef WORKER
57 
58 #include <stdio.h>
59 #include <ctype.h>
60 #include <signal.h>
61 
62 /**/
63 #ifdef HAVE_SYS_TYPES_H
64 # include <sys/types.h>
65 #endif
66 #ifdef HAVE_NETINET_IN_H
67 #include <netinet/in.h>
68 #endif
69 #include <arpa/inet.h>
70 /**/
71 #ifdef HAVE_SYS_PARAM_H
72 # include <sys/param.h>
73 #endif
74 
75 #if !defined(HAVE_RES_INIT) && defined(HAVE___RES_INIT)
76 # define HAVE_RES_INIT
77 #endif
78 
79 #if defined(HAVE_RESOLV_H) && defined(HAVE_RES_INIT)
80 # ifdef HAVE_ARPA_NAMESER_H
81 #  include <arpa/nameser.h> /* DNS HEADER struct */
82 # endif
83 # ifdef HAVE_NETDB_H
84 #  include <netdb.h>
85 # endif
86 # include <resolv.h>
87 #endif
88 
89 #include "ntp.h"
90 #include "ntp_debug.h"
91 #include "ntp_malloc.h"
92 #include "ntp_syslog.h"
93 #include "ntp_unixtime.h"
94 #include "ntp_intres.h"
95 #include "intreswork.h"
96 
97 
98 /*
99  * Following are implementations of getaddrinfo_sometime() and
100  * getnameinfo_sometime().  Each is implemented in three routines:
101  *
102  * getaddrinfo_sometime()		getnameinfo_sometime()
103  * blocking_getaddrinfo()		blocking_getnameinfo()
104  * getaddrinfo_sometime_complete()	getnameinfo_sometime_complete()
105  *
106  * The first runs in the parent and marshalls (or serializes) request
107  * parameters into a request blob which is processed in the child by
108  * the second routine, blocking_*(), which serializes the results into
109  * a response blob unpacked by the third routine, *_complete(), which
110  * calls the callback routine provided with the request and frees
111  * _request_ memory allocated by the first routine.  Response memory
112  * is managed by the code which calls the *_complete routines.
113  */
114 
115 
116 /* === typedefs === */
117 typedef struct blocking_gai_req_tag {	/* marshalled args */
118 	size_t			octets;
119 	u_int			dns_idx;
120 	time_t			scheduled;
121 	time_t			earliest;
122 	int			retry;
123 	struct addrinfo		hints;
124 	u_int			qflags;
125 	gai_sometime_callback	callback;
126 	void *			context;
127 	size_t			nodesize;
128 	size_t			servsize;
129 } blocking_gai_req;
130 
131 typedef struct blocking_gai_resp_tag {
132 	size_t			octets;
133 	int			retcode;
134 	int			retry;
135 	int			gai_errno; /* for EAI_SYSTEM case */
136 	int			ai_count;
137 	/*
138 	 * Followed by ai_count struct addrinfo and then ai_count
139 	 * sockaddr_u and finally the canonical name strings.
140 	 */
141 } blocking_gai_resp;
142 
143 typedef struct blocking_gni_req_tag {
144 	size_t			octets;
145 	u_int			dns_idx;
146 	time_t			scheduled;
147 	time_t			earliest;
148 	int			retry;
149 	size_t			hostoctets;
150 	size_t			servoctets;
151 	int			flags;
152 	gni_sometime_callback	callback;
153 	void *			context;
154 	sockaddr_u		socku;
155 } blocking_gni_req;
156 
157 typedef struct blocking_gni_resp_tag {
158 	size_t			octets;
159 	int			retcode;
160 	int			gni_errno; /* for EAI_SYSTEM case */
161 	int			retry;
162 	size_t			hostoctets;
163 	size_t			servoctets;
164 	/*
165 	 * Followed by hostoctets bytes of null-terminated host,
166 	 * then servoctets bytes of null-terminated service.
167 	 */
168 } blocking_gni_resp;
169 
170 /* per-DNS-worker state in parent */
171 typedef struct dnschild_ctx_tag {
172 	u_int	index;
173 	time_t	next_dns_timeslot;
174 } dnschild_ctx;
175 
176 /* per-DNS-worker state in worker */
177 typedef struct dnsworker_ctx_tag {
178 	blocking_child *	c;
179 	time_t			ignore_scheduled_before;
180 #ifdef HAVE_RES_INIT
181 	time_t	next_res_init;
182 #endif
183 } dnsworker_ctx;
184 
185 
186 /* === variables === */
187 dnschild_ctx **		dnschild_contexts;		/* parent */
188 u_int			dnschild_contexts_alloc;
189 dnsworker_ctx **	dnsworker_contexts;		/* child */
190 u_int			dnsworker_contexts_alloc;
191 
192 #ifdef HAVE_RES_INIT
193 static	time_t		next_res_init;
194 #endif
195 
196 
197 /* === forward declarations === */
198 static	u_int		reserve_dnschild_ctx(void);
199 static	u_int		get_dnschild_ctx(void);
200 static	dnsworker_ctx *	get_worker_context(blocking_child *, u_int);
201 static	void		scheduled_sleep(time_t, time_t,
202 					dnsworker_ctx *);
203 static	void		manage_dns_retry_interval(time_t *, time_t *,
204 						  int *, time_t *,
205 						  int/*BOOL*/);
206 static	int		should_retry_dns(int, int);
207 #ifdef HAVE_RES_INIT
208 static	void		reload_resolv_conf(dnsworker_ctx *);
209 #else
210 # define		reload_resolv_conf(wc)		\
211 	do {						\
212 		(void)(wc);				\
213 	} while (FALSE)
214 #endif
215 static	void		getaddrinfo_sometime_complete(blocking_work_req,
216 						      void *, size_t,
217 						      void *);
218 static	void		getnameinfo_sometime_complete(blocking_work_req,
219 						      void *, size_t,
220 						      void *);
221 
222 
223 /* === functions === */
224 /*
225  * getaddrinfo_sometime - uses blocking child to call getaddrinfo then
226  *			  invokes provided callback completion function.
227  */
228 int
229 getaddrinfo_sometime_ex(
230 	const char *		node,
231 	const char *		service,
232 	const struct addrinfo *	hints,
233 	int			retry,
234 	gai_sometime_callback	callback,
235 	void *			context,
236 	u_int			qflags
237 	)
238 {
239 	blocking_gai_req *	gai_req;
240 	u_int			idx;
241 	dnschild_ctx *		child_ctx;
242 	size_t			req_size;
243 	size_t			nodesize;
244 	size_t			servsize;
245 	time_t			now;
246 
247 	REQUIRE(NULL != node);
248 	if (NULL != hints) {
249 		REQUIRE(0 == hints->ai_addrlen);
250 		REQUIRE(NULL == hints->ai_addr);
251 		REQUIRE(NULL == hints->ai_canonname);
252 		REQUIRE(NULL == hints->ai_next);
253 	}
254 
255 	idx = get_dnschild_ctx();
256 	child_ctx = dnschild_contexts[idx];
257 
258 	nodesize = strlen(node) + 1;
259 	servsize = strlen(service) + 1;
260 	req_size = sizeof(*gai_req) + nodesize + servsize;
261 
262 	gai_req = emalloc_zero(req_size);
263 
264 	gai_req->octets = req_size;
265 	gai_req->dns_idx = idx;
266 	now = time(NULL);
267 	gai_req->scheduled = now;
268 	gai_req->earliest = max(now, child_ctx->next_dns_timeslot);
269 	child_ctx->next_dns_timeslot = gai_req->earliest;
270 	if (hints != NULL)
271 		gai_req->hints = *hints;
272 	gai_req->retry = retry;
273 	gai_req->callback = callback;
274 	gai_req->context = context;
275 	gai_req->nodesize = nodesize;
276 	gai_req->servsize = servsize;
277 	gai_req->qflags = qflags;
278 
279 	memcpy((char *)gai_req + sizeof(*gai_req), node, nodesize);
280 	memcpy((char *)gai_req + sizeof(*gai_req) + nodesize, service,
281 	       servsize);
282 
283 	if (queue_blocking_request(
284 		BLOCKING_GETADDRINFO,
285 		gai_req,
286 		req_size,
287 		&getaddrinfo_sometime_complete,
288 		gai_req)) {
289 
290 		msyslog(LOG_ERR, "unable to queue getaddrinfo request");
291 		errno = EFAULT;
292 		return -1;
293 	}
294 
295 	return 0;
296 }
297 
298 int
299 blocking_getaddrinfo(
300 	blocking_child *	c,
301 	blocking_pipe_header *	req
302 	)
303 {
304 	blocking_gai_req *	gai_req;
305 	dnsworker_ctx *		worker_ctx;
306 	blocking_pipe_header *	resp;
307 	blocking_gai_resp *	gai_resp;
308 	char *			node;
309 	char *			service;
310 	struct addrinfo *	ai_res;
311 	struct addrinfo *	ai;
312 	struct addrinfo *	serialized_ai;
313 	size_t			canons_octets;
314 	size_t			this_octets;
315 	size_t			resp_octets;
316 	char *			cp;
317 	time_t			time_now;
318 
319 	gai_req = (void *)((char *)req + sizeof(*req));
320 	node = (char *)gai_req + sizeof(*gai_req);
321 	service = node + gai_req->nodesize;
322 
323 	worker_ctx = get_worker_context(c, gai_req->dns_idx);
324 	scheduled_sleep(gai_req->scheduled, gai_req->earliest,
325 			worker_ctx);
326 	reload_resolv_conf(worker_ctx);
327 
328 	/*
329 	 * Take a shot at the final size, better to overestimate
330 	 * at first and then realloc to a smaller size.
331 	 */
332 
333 	resp_octets = sizeof(*resp) + sizeof(*gai_resp) +
334 		      16 * (sizeof(struct addrinfo) +
335 			    sizeof(sockaddr_u)) +
336 		      256;
337 	resp = emalloc_zero(resp_octets);
338 	gai_resp = (void *)(resp + 1);
339 
340 	TRACE(2, ("blocking_getaddrinfo given node %s serv %s fam %d flags %x\n",
341 		  node, service, gai_req->hints.ai_family,
342 		  gai_req->hints.ai_flags));
343 #ifdef DEBUG
344 	if (debug >= 2)
345 		fflush(stdout);
346 #endif
347 	ai_res = NULL;
348 	gai_resp->retcode = getaddrinfo(node, service, &gai_req->hints,
349 					&ai_res);
350 	gai_resp->retry = gai_req->retry;
351 #ifdef EAI_SYSTEM
352 	if (EAI_SYSTEM == gai_resp->retcode)
353 		gai_resp->gai_errno = errno;
354 #endif
355 	canons_octets = 0;
356 
357 	if (0 == gai_resp->retcode) {
358 		ai = ai_res;
359 		while (NULL != ai) {
360 			gai_resp->ai_count++;
361 			if (ai->ai_canonname)
362 				canons_octets += strlen(ai->ai_canonname) + 1;
363 			ai = ai->ai_next;
364 		}
365 		/*
366 		 * If this query succeeded only after retrying, DNS may have
367 		 * just become responsive.  Ignore previously-scheduled
368 		 * retry sleeps once for each pending request, similar to
369 		 * the way scheduled_sleep() does when its worker_sleep()
370 		 * is interrupted.
371 		 */
372 		if (gai_resp->retry > INITIAL_DNS_RETRY) {
373 			time_now = time(NULL);
374 			worker_ctx->ignore_scheduled_before = time_now;
375 			TRACE(1, ("DNS success after retry, ignoring sleeps scheduled before now (%s)\n",
376 				  humantime(time_now)));
377 		}
378 	}
379 
380 	/*
381 	 * Our response consists of a header, followed by ai_count
382 	 * addrinfo structs followed by ai_count sockaddr_storage
383 	 * structs followed by the canonical names.
384 	 */
385 	gai_resp->octets = sizeof(*gai_resp)
386 			    + gai_resp->ai_count
387 				* (sizeof(gai_req->hints)
388 				   + sizeof(sockaddr_u))
389 			    + canons_octets;
390 
391 	resp_octets = sizeof(*resp) + gai_resp->octets;
392 	resp = erealloc(resp, resp_octets);
393 	gai_resp = (void *)(resp + 1);
394 
395 	/* cp serves as our current pointer while serializing */
396 	cp = (void *)(gai_resp + 1);
397 	canons_octets = 0;
398 
399 	if (0 == gai_resp->retcode) {
400 		ai = ai_res;
401 		while (NULL != ai) {
402 			memcpy(cp, ai, sizeof(*ai));
403 			serialized_ai = (void *)cp;
404 			cp += sizeof(*ai);
405 
406 			/* transform ai_canonname into offset */
407 			if (NULL != ai->ai_canonname) {
408 				serialized_ai->ai_canonname = (char *)canons_octets;
409 				canons_octets += strlen(ai->ai_canonname) + 1;
410 			}
411 
412 			/* leave fixup of ai_addr pointer for receiver */
413 
414 			ai = ai->ai_next;
415 		}
416 
417 		ai = ai_res;
418 		while (NULL != ai) {
419 			INSIST(ai->ai_addrlen <= sizeof(sockaddr_u));
420 			memcpy(cp, ai->ai_addr, ai->ai_addrlen);
421 			cp += sizeof(sockaddr_u);
422 
423 			ai = ai->ai_next;
424 		}
425 
426 		ai = ai_res;
427 		while (NULL != ai) {
428 			if (NULL != ai->ai_canonname) {
429 				this_octets = strlen(ai->ai_canonname) + 1;
430 				memcpy(cp, ai->ai_canonname, this_octets);
431 				cp += this_octets;
432 			}
433 
434 			ai = ai->ai_next;
435 		}
436 		freeaddrinfo(ai_res);
437 	}
438 
439 	/*
440 	 * make sure our walk and earlier calc match
441 	 */
442 	DEBUG_INSIST((size_t)(cp - (char *)resp) == resp_octets);
443 
444 	if (queue_blocking_response(c, resp, resp_octets, req)) {
445 		msyslog(LOG_ERR, "blocking_getaddrinfo can not queue response");
446 		return -1;
447 	}
448 
449 	return 0;
450 }
451 
452 int
453 getaddrinfo_sometime(
454 	const char *		node,
455 	const char *		service,
456 	const struct addrinfo *	hints,
457 	int			retry,
458 	gai_sometime_callback	callback,
459 	void *			context
460 	)
461 {
462 	return getaddrinfo_sometime_ex(node, service, hints, retry,
463 				       callback, context, 0);
464 }
465 
466 
467 static void
468 getaddrinfo_sometime_complete(
469 	blocking_work_req	rtype,
470 	void *			context,
471 	size_t			respsize,
472 	void *			resp
473 	)
474 {
475 	blocking_gai_req *	gai_req;
476 	blocking_gai_resp *	gai_resp;
477 	dnschild_ctx *		child_ctx;
478 	struct addrinfo *	ai;
479 	struct addrinfo *	next_ai;
480 	sockaddr_u *		psau;
481 	char *			node;
482 	char *			service;
483 	char *			canon_start;
484 	time_t			time_now;
485 	int			again, noerr;
486 	int			af;
487 	const char *		fam_spec;
488 	int			i;
489 
490 	gai_req = context;
491 	gai_resp = resp;
492 
493 	DEBUG_REQUIRE(BLOCKING_GETADDRINFO == rtype);
494 	DEBUG_REQUIRE(respsize == gai_resp->octets);
495 
496 	node = (char *)gai_req + sizeof(*gai_req);
497 	service = node + gai_req->nodesize;
498 
499 	child_ctx = dnschild_contexts[gai_req->dns_idx];
500 
501 	if (0 == gai_resp->retcode) {
502 		/*
503 		 * If this query succeeded only after retrying, DNS may have
504 		 * just become responsive.
505 		 */
506 		if (gai_resp->retry > INITIAL_DNS_RETRY) {
507 			time_now = time(NULL);
508 			child_ctx->next_dns_timeslot = time_now;
509 			TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n",
510 				  gai_req->dns_idx, humantime(time_now)));
511 		}
512 	} else {
513 		noerr = !!(gai_req->qflags & GAIR_F_IGNDNSERR);
514 		again = noerr || should_retry_dns(
515 					gai_resp->retcode, gai_resp->gai_errno);
516 		/*
517 		 * exponential backoff of DNS retries to 64s
518 		 */
519 		if (gai_req->retry > 0 && again) {
520 			/* log the first retry only */
521 			if (INITIAL_DNS_RETRY == gai_req->retry)
522 				NLOG(NLOG_SYSINFO) {
523 					af = gai_req->hints.ai_family;
524 					fam_spec = (AF_INET6 == af)
525 						       ? " (AAAA)"
526 						       : (AF_INET == af)
527 							     ? " (A)"
528 							     : "";
529 #ifdef EAI_SYSTEM
530 					if (EAI_SYSTEM == gai_resp->retcode) {
531 						errno = gai_resp->gai_errno;
532 						msyslog(LOG_INFO,
533 							"retrying DNS %s%s: EAI_SYSTEM %d: %m",
534 							node, fam_spec,
535 							gai_resp->gai_errno);
536 					} else
537 #endif
538 						msyslog(LOG_INFO,
539 							"retrying DNS %s%s: %s (%d)",
540 							node, fam_spec,
541 							gai_strerror(gai_resp->retcode),
542 							gai_resp->retcode);
543 				}
544 			manage_dns_retry_interval(
545 				&gai_req->scheduled, &gai_req->earliest,
546 				&gai_req->retry, &child_ctx->next_dns_timeslot,
547 				noerr);
548 			if (!queue_blocking_request(
549 					BLOCKING_GETADDRINFO,
550 					gai_req,
551 					gai_req->octets,
552 					&getaddrinfo_sometime_complete,
553 					gai_req))
554 				return;
555 			else
556 				msyslog(LOG_ERR,
557 					"unable to retry hostname %s",
558 					node);
559 		}
560 	}
561 
562 	/*
563 	 * fixup pointers in returned addrinfo array
564 	 */
565 	ai = (void *)((char *)gai_resp + sizeof(*gai_resp));
566 	next_ai = NULL;
567 	for (i = gai_resp->ai_count - 1; i >= 0; i--) {
568 		ai[i].ai_next = next_ai;
569 		next_ai = &ai[i];
570 	}
571 
572 	psau = (void *)((char *)ai + gai_resp->ai_count * sizeof(*ai));
573 	canon_start = (char *)psau + gai_resp->ai_count * sizeof(*psau);
574 
575 	for (i = 0; i < gai_resp->ai_count; i++) {
576 		if (NULL != ai[i].ai_addr)
577 			ai[i].ai_addr = &psau->sa;
578 		psau++;
579 		if (NULL != ai[i].ai_canonname)
580 			ai[i].ai_canonname += (size_t)canon_start;
581 	}
582 
583 	ENSURE((char *)psau == canon_start);
584 
585 	if (!gai_resp->ai_count)
586 		ai = NULL;
587 
588 	(*gai_req->callback)(gai_resp->retcode, gai_resp->gai_errno,
589 			     gai_req->context, node, service,
590 			     &gai_req->hints, ai);
591 
592 	free(gai_req);
593 	/* gai_resp is part of block freed by process_blocking_resp() */
594 }
595 
596 
597 #ifdef TEST_BLOCKING_WORKER
598 void gai_test_callback(int rescode, int gai_errno, void *context, const char *name, const char *service, const struct addrinfo *hints, const struct addrinfo *ai_res)
599 {
600 	sockaddr_u addr;
601 
602 	if (rescode) {
603 		TRACE(1, ("gai_test_callback context %p error rescode %d %s serv %s\n",
604 			  context, rescode, name, service));
605 		return;
606 	}
607 	while (!rescode && NULL != ai_res) {
608 		ZERO_SOCK(&addr);
609 		memcpy(&addr, ai_res->ai_addr, ai_res->ai_addrlen);
610 		TRACE(1, ("ctx %p fam %d addr %s canon '%s' type %s at %p ai_addr %p ai_next %p\n",
611 			  context,
612 			  AF(&addr),
613 			  stoa(&addr),
614 			  (ai_res->ai_canonname)
615 			      ? ai_res->ai_canonname
616 			      : "",
617 			  (SOCK_DGRAM == ai_res->ai_socktype)
618 			      ? "DGRAM"
619 			      : (SOCK_STREAM == ai_res->ai_socktype)
620 				    ? "STREAM"
621 				    : "(other)",
622 			  ai_res,
623 			  ai_res->ai_addr,
624 			  ai_res->ai_next));
625 
626 		getnameinfo_sometime((sockaddr_u *)ai_res->ai_addr, 128, 32, 0, gni_test_callback, context);
627 
628 		ai_res = ai_res->ai_next;
629 	}
630 }
631 #endif	/* TEST_BLOCKING_WORKER */
632 
633 
634 int
635 getnameinfo_sometime(
636 	sockaddr_u *		psau,
637 	size_t			hostoctets,
638 	size_t			servoctets,
639 	int			flags,
640 	gni_sometime_callback	callback,
641 	void *			context
642 	)
643 {
644 	blocking_gni_req *	gni_req;
645 	u_int			idx;
646 	dnschild_ctx *		child_ctx;
647 	time_t			time_now;
648 
649 	REQUIRE(hostoctets);
650 	REQUIRE(hostoctets + servoctets < 1024);
651 
652 	idx = get_dnschild_ctx();
653 	child_ctx = dnschild_contexts[idx];
654 
655 	gni_req = emalloc_zero(sizeof(*gni_req));
656 
657 	gni_req->octets = sizeof(*gni_req);
658 	gni_req->dns_idx = idx;
659 	time_now = time(NULL);
660 	gni_req->scheduled = time_now;
661 	gni_req->earliest = max(time_now, child_ctx->next_dns_timeslot);
662 	child_ctx->next_dns_timeslot = gni_req->earliest;
663 	memcpy(&gni_req->socku, psau, SOCKLEN(psau));
664 	gni_req->hostoctets = hostoctets;
665 	gni_req->servoctets = servoctets;
666 	gni_req->flags = flags;
667 	gni_req->retry = INITIAL_DNS_RETRY;
668 	gni_req->callback = callback;
669 	gni_req->context = context;
670 
671 	if (queue_blocking_request(
672 		BLOCKING_GETNAMEINFO,
673 		gni_req,
674 		sizeof(*gni_req),
675 		&getnameinfo_sometime_complete,
676 		gni_req)) {
677 
678 		msyslog(LOG_ERR, "unable to queue getnameinfo request");
679 		errno = EFAULT;
680 		return -1;
681 	}
682 
683 	return 0;
684 }
685 
686 
687 int
688 blocking_getnameinfo(
689 	blocking_child *	c,
690 	blocking_pipe_header *	req
691 	)
692 {
693 	blocking_gni_req *	gni_req;
694 	dnsworker_ctx *		worker_ctx;
695 	blocking_pipe_header *	resp;
696 	blocking_gni_resp *	gni_resp;
697 	size_t			octets;
698 	size_t			resp_octets;
699 	char *			service;
700 	char *			cp;
701 	int			rc;
702 	time_t			time_now;
703 	char			host[1024];
704 
705 	gni_req = (void *)((char *)req + sizeof(*req));
706 
707 	octets = gni_req->hostoctets + gni_req->servoctets;
708 
709 	/*
710 	 * Some alloca() implementations are fragile regarding
711 	 * large allocations.  We only need room for the host
712 	 * and service names.
713 	 */
714 	REQUIRE(octets < sizeof(host));
715 	service = host + gni_req->hostoctets;
716 
717 	worker_ctx = get_worker_context(c, gni_req->dns_idx);
718 	scheduled_sleep(gni_req->scheduled, gni_req->earliest,
719 			worker_ctx);
720 	reload_resolv_conf(worker_ctx);
721 
722 	/*
723 	 * Take a shot at the final size, better to overestimate
724 	 * then realloc to a smaller size.
725 	 */
726 
727 	resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets;
728 	resp = emalloc_zero(resp_octets);
729 	gni_resp = (void *)((char *)resp + sizeof(*resp));
730 
731 	TRACE(2, ("blocking_getnameinfo given addr %s flags 0x%x hostlen %lu servlen %lu\n",
732 		  stoa(&gni_req->socku), gni_req->flags,
733 		  (u_long)gni_req->hostoctets, (u_long)gni_req->servoctets));
734 
735 	gni_resp->retcode = getnameinfo(&gni_req->socku.sa,
736 					SOCKLEN(&gni_req->socku),
737 					host,
738 					gni_req->hostoctets,
739 					service,
740 					gni_req->servoctets,
741 					gni_req->flags);
742 	gni_resp->retry = gni_req->retry;
743 #ifdef EAI_SYSTEM
744 	if (EAI_SYSTEM == gni_resp->retcode)
745 		gni_resp->gni_errno = errno;
746 #endif
747 
748 	if (0 != gni_resp->retcode) {
749 		gni_resp->hostoctets = 0;
750 		gni_resp->servoctets = 0;
751 	} else {
752 		gni_resp->hostoctets = strlen(host) + 1;
753 		gni_resp->servoctets = strlen(service) + 1;
754 		/*
755 		 * If this query succeeded only after retrying, DNS may have
756 		 * just become responsive.  Ignore previously-scheduled
757 		 * retry sleeps once for each pending request, similar to
758 		 * the way scheduled_sleep() does when its worker_sleep()
759 		 * is interrupted.
760 		 */
761 		if (gni_req->retry > INITIAL_DNS_RETRY) {
762 			time_now = time(NULL);
763 			worker_ctx->ignore_scheduled_before = time_now;
764 			TRACE(1, ("DNS success after retrying, ignoring sleeps scheduled before now (%s)\n",
765 				humantime(time_now)));
766 		}
767 	}
768 	octets = gni_resp->hostoctets + gni_resp->servoctets;
769 	/*
770 	 * Our response consists of a header, followed by the host and
771 	 * service strings, each null-terminated.
772 	 */
773 	resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets;
774 
775 	resp = erealloc(resp, resp_octets);
776 	gni_resp = (void *)(resp + 1);
777 
778 	gni_resp->octets = sizeof(*gni_resp) + octets;
779 
780 	/* cp serves as our current pointer while serializing */
781 	cp = (void *)(gni_resp + 1);
782 
783 	if (0 == gni_resp->retcode) {
784 		memcpy(cp, host, gni_resp->hostoctets);
785 		cp += gni_resp->hostoctets;
786 		memcpy(cp, service, gni_resp->servoctets);
787 		cp += gni_resp->servoctets;
788 	}
789 
790 	INSIST((size_t)(cp - (char *)resp) == resp_octets);
791 	INSIST(resp_octets - sizeof(*resp) == gni_resp->octets);
792 
793 	rc = queue_blocking_response(c, resp, resp_octets, req);
794 	if (rc)
795 		msyslog(LOG_ERR, "blocking_getnameinfo unable to queue response");
796 	return rc;
797 }
798 
799 
800 static void
801 getnameinfo_sometime_complete(
802 	blocking_work_req	rtype,
803 	void *			context,
804 	size_t			respsize,
805 	void *			resp
806 	)
807 {
808 	blocking_gni_req *	gni_req;
809 	blocking_gni_resp *	gni_resp;
810 	dnschild_ctx *		child_ctx;
811 	char *			host;
812 	char *			service;
813 	time_t			time_now;
814 	int			again;
815 
816 	gni_req = context;
817 	gni_resp = resp;
818 
819 	DEBUG_REQUIRE(BLOCKING_GETNAMEINFO == rtype);
820 	DEBUG_REQUIRE(respsize == gni_resp->octets);
821 
822 	child_ctx = dnschild_contexts[gni_req->dns_idx];
823 
824 	if (0 == gni_resp->retcode) {
825 		/*
826 		 * If this query succeeded only after retrying, DNS may have
827 		 * just become responsive.
828 		 */
829 		if (gni_resp->retry > INITIAL_DNS_RETRY) {
830 			time_now = time(NULL);
831 			child_ctx->next_dns_timeslot = time_now;
832 			TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n",
833 				  gni_req->dns_idx, humantime(time_now)));
834 		}
835 	} else {
836 		again = should_retry_dns(gni_resp->retcode, gni_resp->gni_errno);
837 		/*
838 		 * exponential backoff of DNS retries to 64s
839 		 */
840 		if (gni_req->retry > 0)
841 			manage_dns_retry_interval(&gni_req->scheduled,
842 			    &gni_req->earliest, &gni_req->retry,
843 						  &child_ctx->next_dns_timeslot, FALSE);
844 
845 		if (gni_req->retry > 0 && again) {
846 			if (!queue_blocking_request(
847 				BLOCKING_GETNAMEINFO,
848 				gni_req,
849 				gni_req->octets,
850 				&getnameinfo_sometime_complete,
851 				gni_req))
852 				return;
853 
854 			msyslog(LOG_ERR, "unable to retry reverse lookup of %s", stoa(&gni_req->socku));
855 		}
856 	}
857 
858 	if (!gni_resp->hostoctets) {
859 		host = NULL;
860 		service = NULL;
861 	} else {
862 		host = (char *)gni_resp + sizeof(*gni_resp);
863 		service = (gni_resp->servoctets)
864 			      ? host + gni_resp->hostoctets
865 			      : NULL;
866 	}
867 
868 	(*gni_req->callback)(gni_resp->retcode, gni_resp->gni_errno,
869 			     &gni_req->socku, gni_req->flags, host,
870 			     service, gni_req->context);
871 
872 	free(gni_req);
873 	/* gni_resp is part of block freed by process_blocking_resp() */
874 }
875 
876 
877 #ifdef TEST_BLOCKING_WORKER
878 void gni_test_callback(int rescode, int gni_errno, sockaddr_u *psau, int flags, const char *host, const char *service, void *context)
879 {
880 	if (!rescode)
881 		TRACE(1, ("gni_test_callback got host '%s' serv '%s' for addr %s context %p\n",
882 			  host, service, stoa(psau), context));
883 	else
884 		TRACE(1, ("gni_test_callback context %p rescode %d gni_errno %d flags 0x%x addr %s\n",
885 			  context, rescode, gni_errno, flags, stoa(psau)));
886 }
887 #endif	/* TEST_BLOCKING_WORKER */
888 
889 
890 #ifdef HAVE_RES_INIT
891 static void
892 reload_resolv_conf(
893 	dnsworker_ctx *	worker_ctx
894 	)
895 {
896 	time_t	time_now;
897 
898 	/*
899 	 * This is ad-hoc.  Reload /etc/resolv.conf once per minute
900 	 * to pick up on changes from the DHCP client.  [Bug 1226]
901 	 * When using threads for the workers, this needs to happen
902 	 * only once per minute process-wide.
903 	 */
904 	time_now = time(NULL);
905 # ifdef WORK_THREAD
906 	worker_ctx->next_res_init = next_res_init;
907 # endif
908 	if (worker_ctx->next_res_init <= time_now) {
909 		if (worker_ctx->next_res_init != 0)
910 			res_init();
911 		worker_ctx->next_res_init = time_now + 60;
912 # ifdef WORK_THREAD
913 		next_res_init = worker_ctx->next_res_init;
914 # endif
915 	}
916 }
917 #endif	/* HAVE_RES_INIT */
918 
919 
920 static u_int
921 reserve_dnschild_ctx(void)
922 {
923 	const size_t	ps = sizeof(dnschild_contexts[0]);
924 	const size_t	cs = sizeof(*dnschild_contexts[0]);
925 	u_int		c;
926 	u_int		new_alloc;
927 	size_t		octets;
928 	size_t		new_octets;
929 
930 	c = 0;
931 	while (TRUE) {
932 		for ( ; c < dnschild_contexts_alloc; c++) {
933 			if (NULL == dnschild_contexts[c]) {
934 				dnschild_contexts[c] = emalloc_zero(cs);
935 
936 				return c;
937 			}
938 		}
939 		new_alloc = dnschild_contexts_alloc + 20;
940 		new_octets = new_alloc * ps;
941 		octets = dnschild_contexts_alloc * ps;
942 		dnschild_contexts = erealloc_zero(dnschild_contexts,
943 						  new_octets, octets);
944 		dnschild_contexts_alloc = new_alloc;
945 	}
946 }
947 
948 
949 static u_int
950 get_dnschild_ctx(void)
951 {
952 	static u_int	shared_ctx = UINT_MAX;
953 
954 	if (worker_per_query)
955 		return reserve_dnschild_ctx();
956 
957 	if (UINT_MAX == shared_ctx)
958 		shared_ctx = reserve_dnschild_ctx();
959 
960 	return shared_ctx;
961 }
962 
963 
964 static dnsworker_ctx *
965 get_worker_context(
966 	blocking_child *	c,
967 	u_int			idx
968 	)
969 {
970 	u_int		min_new_alloc;
971 	u_int		new_alloc;
972 	size_t		octets;
973 	size_t		new_octets;
974 	dnsworker_ctx *	retv;
975 
976 	worker_global_lock(TRUE);
977 
978 	if (dnsworker_contexts_alloc <= idx) {
979 		min_new_alloc = 1 + idx;
980 		/* round new_alloc up to nearest multiple of 4 */
981 		new_alloc = (min_new_alloc + 4) & ~(4 - 1);
982 		new_octets = new_alloc * sizeof(dnsworker_ctx*);
983 		octets = dnsworker_contexts_alloc * sizeof(dnsworker_ctx*);
984 		dnsworker_contexts = erealloc_zero(dnsworker_contexts,
985 						   new_octets, octets);
986 		dnsworker_contexts_alloc = new_alloc;
987 		retv = emalloc_zero(sizeof(dnsworker_ctx));
988 		dnsworker_contexts[idx] = retv;
989 	} else if (NULL == (retv = dnsworker_contexts[idx])) {
990 		retv = emalloc_zero(sizeof(dnsworker_ctx));
991 		dnsworker_contexts[idx] = retv;
992 	}
993 
994 	worker_global_lock(FALSE);
995 
996 	ZERO(*retv);
997 	retv->c = c;
998 	return retv;
999 }
1000 
1001 
1002 static void
1003 scheduled_sleep(
1004 	time_t		scheduled,
1005 	time_t		earliest,
1006 	dnsworker_ctx *	worker_ctx
1007 	)
1008 {
1009 	time_t now;
1010 
1011 	if (scheduled < worker_ctx->ignore_scheduled_before) {
1012 		TRACE(1, ("ignoring sleep until %s scheduled at %s (before %s)\n",
1013 			  humantime(earliest), humantime(scheduled),
1014 			  humantime(worker_ctx->ignore_scheduled_before)));
1015 		return;
1016 	}
1017 
1018 	now = time(NULL);
1019 
1020 	if (now < earliest) {
1021 		TRACE(1, ("sleep until %s scheduled at %s (>= %s)\n",
1022 			  humantime(earliest), humantime(scheduled),
1023 			  humantime(worker_ctx->ignore_scheduled_before)));
1024 		if (-1 == worker_sleep(worker_ctx->c, earliest - now)) {
1025 			/* our sleep was interrupted */
1026 			now = time(NULL);
1027 			worker_ctx->ignore_scheduled_before = now;
1028 #ifdef HAVE_RES_INIT
1029 			worker_ctx->next_res_init = now + 60;
1030 			next_res_init = worker_ctx->next_res_init;
1031 			res_init();
1032 #endif
1033 			TRACE(1, ("sleep interrupted by daemon, ignoring sleeps scheduled before now (%s)\n",
1034 				  humantime(worker_ctx->ignore_scheduled_before)));
1035 		}
1036 	}
1037 }
1038 
1039 
1040 /*
1041  * manage_dns_retry_interval is a helper used by
1042  * getaddrinfo_sometime_complete and getnameinfo_sometime_complete
1043  * to calculate the new retry interval and schedule the next query.
1044  */
1045 static void
1046 manage_dns_retry_interval(
1047 	time_t *	pscheduled,
1048 	time_t *	pwhen,
1049 	int *		pretry,
1050 	time_t *	pnext_timeslot,
1051 	int		forever
1052 	)
1053 {
1054 	time_t	now;
1055 	time_t	when;
1056 	int	retry;
1057 	int	retmax;
1058 
1059 	now = time(NULL);
1060 	retry = *pretry;
1061 	when = max(now + retry, *pnext_timeslot);
1062 	*pnext_timeslot = when;
1063 
1064 	/* this exponential backoff is slower than doubling up: The
1065 	 * sequence goes 2-3-4-6-8-12-16-24-32... and the upper limit is
1066 	 * 64 seconds for things that should not repeat forever, and
1067 	 * 1024 when repeated forever.
1068 	 */
1069 	retmax = forever ? 1024 : 64;
1070 	retry <<= 1;
1071 	if (retry & (retry - 1))
1072 		retry &= (retry - 1);
1073 	else
1074 		retry -= (retry >> 2);
1075 	retry = min(retmax, retry);
1076 
1077 	*pscheduled = now;
1078 	*pwhen = when;
1079 	*pretry = retry;
1080 }
1081 
1082 /*
1083  * should_retry_dns is a helper used by getaddrinfo_sometime_complete
1084  * and getnameinfo_sometime_complete which implements ntpd's DNS retry
1085  * policy.
1086  */
1087 static int
1088 should_retry_dns(
1089 	int	rescode,
1090 	int	res_errno
1091 	)
1092 {
1093 	static int	eai_again_seen;
1094 	int		again;
1095 #if defined (EAI_SYSTEM) && defined(DEBUG)
1096 	char		msg[256];
1097 #endif
1098 
1099 	/*
1100 	 * If the resolver failed, see if the failure is
1101 	 * temporary. If so, return success.
1102 	 */
1103 	again = 0;
1104 
1105 	switch (rescode) {
1106 
1107 	case EAI_FAIL:
1108 		again = 1;
1109 		break;
1110 
1111 	case EAI_AGAIN:
1112 		again = 1;
1113 		eai_again_seen = 1;		/* [Bug 1178] */
1114 		break;
1115 
1116 	case EAI_NONAME:
1117 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
1118 	case EAI_NODATA:
1119 #endif
1120 		again = !eai_again_seen;	/* [Bug 1178] */
1121 		break;
1122 
1123 #ifdef EAI_SYSTEM
1124 	case EAI_SYSTEM:
1125 		/*
1126 		 * EAI_SYSTEM means the real error is in errno.  We should be more
1127 		 * discriminating about which errno values require retrying, but
1128 		 * this matches existing behavior.
1129 		 */
1130 		again = 1;
1131 # ifdef DEBUG
1132 		errno_to_str(res_errno, msg, sizeof(msg));
1133 		TRACE(1, ("intres: EAI_SYSTEM errno %d (%s) means try again, right?\n",
1134 			  res_errno, msg));
1135 # endif
1136 		break;
1137 #endif
1138 	}
1139 
1140 	TRACE(2, ("intres: resolver returned: %s (%d), %sretrying\n",
1141 		  gai_strerror(rescode), rescode, again ? "" : "not "));
1142 
1143 	return again;
1144 }
1145 
1146 #else	/* !WORKER follows */
1147 int ntp_intres_nonempty_compilation_unit;
1148 #endif
1149