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