xref: /freebsd/contrib/ntp/libntp/ntp_intres.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
12b15cb3dSCy Schubert /*
22b15cb3dSCy Schubert  * ntp_intres.c - Implements a generic blocking worker child or thread,
32b15cb3dSCy Schubert  *		  initially to provide a nonblocking solution for DNS
42b15cb3dSCy Schubert  *		  name to address lookups available with getaddrinfo().
52b15cb3dSCy Schubert  *
62b15cb3dSCy Schubert  * This is a new implementation as of 2009 sharing the filename and
72b15cb3dSCy Schubert  * very little else with the prior implementation, which used a
82b15cb3dSCy Schubert  * temporary file to receive a single set of requests from the parent,
92b15cb3dSCy Schubert  * and a NTP mode 7 authenticated request to push back responses.
102b15cb3dSCy Schubert  *
112b15cb3dSCy Schubert  * A primary goal in rewriting this code was the need to support the
122b15cb3dSCy Schubert  * pool configuration directive's requirement to retrieve multiple
132b15cb3dSCy Schubert  * addresses resolving a single name, which has previously been
142b15cb3dSCy Schubert  * satisfied with blocking resolver calls from the ntpd mainline code.
152b15cb3dSCy Schubert  *
162b15cb3dSCy Schubert  * A secondary goal is to provide a generic mechanism for other
172b15cb3dSCy Schubert  * blocking operations to be delegated to a worker using a common
182b15cb3dSCy Schubert  * model for both Unix and Windows ntpd.  ntp_worker.c, work_fork.c,
192b15cb3dSCy Schubert  * and work_thread.c implement the generic mechanism.  This file
202b15cb3dSCy Schubert  * implements the two current consumers, getaddrinfo_sometime() and the
212b15cb3dSCy Schubert  * presently unused getnameinfo_sometime().
222b15cb3dSCy Schubert  *
232b15cb3dSCy Schubert  * Both routines deliver results to a callback and manage memory
242b15cb3dSCy Schubert  * allocation, meaning there is no freeaddrinfo_sometime().
252b15cb3dSCy Schubert  *
262b15cb3dSCy Schubert  * The initial implementation for Unix uses a pair of unidirectional
272b15cb3dSCy Schubert  * pipes, one each for requests and responses, connecting the forked
282b15cb3dSCy Schubert  * blocking child worker with the ntpd mainline.  The threaded code
292b15cb3dSCy Schubert  * uses arrays of pointers to queue requests and responses.
302b15cb3dSCy Schubert  *
312b15cb3dSCy Schubert  * The parent drives the process, including scheduling sleeps between
322b15cb3dSCy Schubert  * retries.
332b15cb3dSCy Schubert  *
342b15cb3dSCy Schubert  * Memory is managed differently for a child process, which mallocs
352b15cb3dSCy Schubert  * request buffers to read from the pipe into, whereas the threaded
362b15cb3dSCy Schubert  * code mallocs a copy of the request to hand off to the worker via
372b15cb3dSCy Schubert  * the queueing array.  The resulting request buffer is free()d by
382b15cb3dSCy Schubert  * platform-independent code.  A wrinkle is the request needs to be
392b15cb3dSCy Schubert  * available to the requestor during response processing.
402b15cb3dSCy Schubert  *
412b15cb3dSCy Schubert  * Response memory allocation is also platform-dependent.  With a
422b15cb3dSCy Schubert  * separate process and pipes, the response is free()d after being
432b15cb3dSCy Schubert  * written to the pipe.  With threads, the same memory is handed
442b15cb3dSCy Schubert  * over and the requestor frees it after processing is completed.
452b15cb3dSCy Schubert  *
462b15cb3dSCy Schubert  * The code should be generalized to support threads on Unix using
472b15cb3dSCy Schubert  * much of the same code used for Windows initially.
482b15cb3dSCy Schubert  *
492b15cb3dSCy Schubert  */
502b15cb3dSCy Schubert #ifdef HAVE_CONFIG_H
512b15cb3dSCy Schubert # include <config.h>
522b15cb3dSCy Schubert #endif
532b15cb3dSCy Schubert 
542b15cb3dSCy Schubert #include "ntp_workimpl.h"
552b15cb3dSCy Schubert 
562b15cb3dSCy Schubert #ifdef WORKER
572b15cb3dSCy Schubert 
582b15cb3dSCy Schubert #include <stdio.h>
592b15cb3dSCy Schubert #include <ctype.h>
602b15cb3dSCy Schubert #include <signal.h>
612b15cb3dSCy Schubert 
622b15cb3dSCy Schubert /**/
632b15cb3dSCy Schubert #ifdef HAVE_SYS_TYPES_H
642b15cb3dSCy Schubert # include <sys/types.h>
652b15cb3dSCy Schubert #endif
662b15cb3dSCy Schubert #ifdef HAVE_NETINET_IN_H
672b15cb3dSCy Schubert #include <netinet/in.h>
682b15cb3dSCy Schubert #endif
692b15cb3dSCy Schubert #include <arpa/inet.h>
702b15cb3dSCy Schubert /**/
712b15cb3dSCy Schubert #ifdef HAVE_SYS_PARAM_H
722b15cb3dSCy Schubert # include <sys/param.h>
732b15cb3dSCy Schubert #endif
742b15cb3dSCy Schubert 
752b15cb3dSCy Schubert #if !defined(HAVE_RES_INIT) && defined(HAVE___RES_INIT)
762b15cb3dSCy Schubert # define HAVE_RES_INIT
772b15cb3dSCy Schubert #endif
782b15cb3dSCy Schubert 
792b15cb3dSCy Schubert #if defined(HAVE_RESOLV_H) && defined(HAVE_RES_INIT)
802b15cb3dSCy Schubert # ifdef HAVE_ARPA_NAMESER_H
812b15cb3dSCy Schubert #  include <arpa/nameser.h> /* DNS HEADER struct */
822b15cb3dSCy Schubert # endif
832b15cb3dSCy Schubert # ifdef HAVE_NETDB_H
842b15cb3dSCy Schubert #  include <netdb.h>
852b15cb3dSCy Schubert # endif
862b15cb3dSCy Schubert # include <resolv.h>
872b15cb3dSCy Schubert #endif
882b15cb3dSCy Schubert 
892b15cb3dSCy Schubert #include "ntp.h"
902b15cb3dSCy Schubert #include "ntp_debug.h"
912b15cb3dSCy Schubert #include "ntp_malloc.h"
922b15cb3dSCy Schubert #include "ntp_syslog.h"
932b15cb3dSCy Schubert #include "ntp_unixtime.h"
942b15cb3dSCy Schubert #include "ntp_intres.h"
952b15cb3dSCy Schubert #include "intreswork.h"
962b15cb3dSCy Schubert 
972b15cb3dSCy Schubert 
982b15cb3dSCy Schubert /*
992b15cb3dSCy Schubert  * Following are implementations of getaddrinfo_sometime() and
1002b15cb3dSCy Schubert  * getnameinfo_sometime().  Each is implemented in three routines:
1012b15cb3dSCy Schubert  *
1022b15cb3dSCy Schubert  * getaddrinfo_sometime()		getnameinfo_sometime()
1032b15cb3dSCy Schubert  * blocking_getaddrinfo()		blocking_getnameinfo()
1042b15cb3dSCy Schubert  * getaddrinfo_sometime_complete()	getnameinfo_sometime_complete()
1052b15cb3dSCy Schubert  *
1062b15cb3dSCy Schubert  * The first runs in the parent and marshalls (or serializes) request
1072b15cb3dSCy Schubert  * parameters into a request blob which is processed in the child by
1082b15cb3dSCy Schubert  * the second routine, blocking_*(), which serializes the results into
1092b15cb3dSCy Schubert  * a response blob unpacked by the third routine, *_complete(), which
1102b15cb3dSCy Schubert  * calls the callback routine provided with the request and frees
1112b15cb3dSCy Schubert  * _request_ memory allocated by the first routine.  Response memory
1122b15cb3dSCy Schubert  * is managed by the code which calls the *_complete routines.
1132b15cb3dSCy Schubert  */
1142b15cb3dSCy Schubert 
115f391d6bcSXin LI 
1162b15cb3dSCy Schubert /* === typedefs === */
1172b15cb3dSCy Schubert typedef struct blocking_gai_req_tag {	/* marshalled args */
1182b15cb3dSCy Schubert 	size_t			octets;
1192b15cb3dSCy Schubert 	u_int			dns_idx;
1202b15cb3dSCy Schubert 	time_t			scheduled;
1212b15cb3dSCy Schubert 	time_t			earliest;
1222b15cb3dSCy Schubert 	int			retry;
123f391d6bcSXin LI 	struct addrinfo		hints;
124f391d6bcSXin LI 	u_int			qflags;
1252b15cb3dSCy Schubert 	gai_sometime_callback	callback;
1262b15cb3dSCy Schubert 	void *			context;
1272b15cb3dSCy Schubert 	size_t			nodesize;
1282b15cb3dSCy Schubert 	size_t			servsize;
1292b15cb3dSCy Schubert } blocking_gai_req;
1302b15cb3dSCy Schubert 
1312b15cb3dSCy Schubert typedef struct blocking_gai_resp_tag {
1322b15cb3dSCy Schubert 	size_t			octets;
1332b15cb3dSCy Schubert 	int			retcode;
1342b15cb3dSCy Schubert 	int			retry;
1352b15cb3dSCy Schubert 	int			gai_errno; /* for EAI_SYSTEM case */
1362b15cb3dSCy Schubert 	int			ai_count;
1372b15cb3dSCy Schubert 	/*
1382b15cb3dSCy Schubert 	 * Followed by ai_count struct addrinfo and then ai_count
1392b15cb3dSCy Schubert 	 * sockaddr_u and finally the canonical name strings.
1402b15cb3dSCy Schubert 	 */
1412b15cb3dSCy Schubert } blocking_gai_resp;
1422b15cb3dSCy Schubert 
1432b15cb3dSCy Schubert typedef struct blocking_gni_req_tag {
1442b15cb3dSCy Schubert 	size_t			octets;
1452b15cb3dSCy Schubert 	u_int			dns_idx;
1462b15cb3dSCy Schubert 	time_t			scheduled;
1472b15cb3dSCy Schubert 	time_t			earliest;
1482b15cb3dSCy Schubert 	int			retry;
1492b15cb3dSCy Schubert 	size_t			hostoctets;
1502b15cb3dSCy Schubert 	size_t			servoctets;
1512b15cb3dSCy Schubert 	int			flags;
1522b15cb3dSCy Schubert 	gni_sometime_callback	callback;
1532b15cb3dSCy Schubert 	void *			context;
1542b15cb3dSCy Schubert 	sockaddr_u		socku;
1552b15cb3dSCy Schubert } blocking_gni_req;
1562b15cb3dSCy Schubert 
1572b15cb3dSCy Schubert typedef struct blocking_gni_resp_tag {
1582b15cb3dSCy Schubert 	size_t			octets;
1592b15cb3dSCy Schubert 	int			retcode;
1602b15cb3dSCy Schubert 	int			gni_errno; /* for EAI_SYSTEM case */
1612b15cb3dSCy Schubert 	int			retry;
1622b15cb3dSCy Schubert 	size_t			hostoctets;
1632b15cb3dSCy Schubert 	size_t			servoctets;
1642b15cb3dSCy Schubert 	/*
1652b15cb3dSCy Schubert 	 * Followed by hostoctets bytes of null-terminated host,
1662b15cb3dSCy Schubert 	 * then servoctets bytes of null-terminated service.
1672b15cb3dSCy Schubert 	 */
1682b15cb3dSCy Schubert } blocking_gni_resp;
1692b15cb3dSCy Schubert 
1702b15cb3dSCy Schubert /* per-DNS-worker state in parent */
1712b15cb3dSCy Schubert typedef struct dnschild_ctx_tag {
1722b15cb3dSCy Schubert 	u_int	index;
1732b15cb3dSCy Schubert 	time_t	next_dns_timeslot;
1742b15cb3dSCy Schubert } dnschild_ctx;
1752b15cb3dSCy Schubert 
1762b15cb3dSCy Schubert /* per-DNS-worker state in worker */
1772b15cb3dSCy Schubert typedef struct dnsworker_ctx_tag {
1782b15cb3dSCy Schubert 	blocking_child *	c;
1792b15cb3dSCy Schubert 	time_t			ignore_scheduled_before;
1802b15cb3dSCy Schubert #ifdef HAVE_RES_INIT
1812b15cb3dSCy Schubert 	time_t	next_res_init;
1822b15cb3dSCy Schubert #endif
1832b15cb3dSCy Schubert } dnsworker_ctx;
1842b15cb3dSCy Schubert 
1852b15cb3dSCy Schubert 
1862b15cb3dSCy Schubert /* === variables === */
1872b15cb3dSCy Schubert dnschild_ctx **		dnschild_contexts;		/* parent */
1882b15cb3dSCy Schubert u_int			dnschild_contexts_alloc;
1892b15cb3dSCy Schubert dnsworker_ctx **	dnsworker_contexts;		/* child */
1902b15cb3dSCy Schubert u_int			dnsworker_contexts_alloc;
1912b15cb3dSCy Schubert 
1922b15cb3dSCy Schubert #ifdef HAVE_RES_INIT
1932b15cb3dSCy Schubert static	time_t		next_res_init;
1942b15cb3dSCy Schubert #endif
1952b15cb3dSCy Schubert 
1962b15cb3dSCy Schubert 
1972b15cb3dSCy Schubert /* === forward declarations === */
1982b15cb3dSCy Schubert static	u_int		reserve_dnschild_ctx(void);
1992b15cb3dSCy Schubert static	u_int		get_dnschild_ctx(void);
2002b15cb3dSCy Schubert static	dnsworker_ctx *	get_worker_context(blocking_child *, u_int);
2012b15cb3dSCy Schubert static	void		scheduled_sleep(time_t, time_t,
2022b15cb3dSCy Schubert 					dnsworker_ctx *);
2032b15cb3dSCy Schubert static	void		manage_dns_retry_interval(time_t *, time_t *,
204f391d6bcSXin LI 						  int *, time_t *,
205f391d6bcSXin LI 						  int/*BOOL*/);
2062b15cb3dSCy Schubert static	int		should_retry_dns(int, int);
2072b15cb3dSCy Schubert #ifdef HAVE_RES_INIT
2082b15cb3dSCy Schubert static	void		reload_resolv_conf(dnsworker_ctx *);
2092b15cb3dSCy Schubert #else
2102b15cb3dSCy Schubert # define		reload_resolv_conf(wc)		\
2112b15cb3dSCy Schubert 	do {						\
2122b15cb3dSCy Schubert 		(void)(wc);				\
2132b15cb3dSCy Schubert 	} while (FALSE)
2142b15cb3dSCy Schubert #endif
2152b15cb3dSCy Schubert static	void		getaddrinfo_sometime_complete(blocking_work_req,
2162b15cb3dSCy Schubert 						      void *, size_t,
2172b15cb3dSCy Schubert 						      void *);
2182b15cb3dSCy Schubert static	void		getnameinfo_sometime_complete(blocking_work_req,
2192b15cb3dSCy Schubert 						      void *, size_t,
2202b15cb3dSCy Schubert 						      void *);
2212b15cb3dSCy Schubert 
2222b15cb3dSCy Schubert 
2232b15cb3dSCy Schubert /* === functions === */
2242b15cb3dSCy Schubert /*
2252b15cb3dSCy Schubert  * getaddrinfo_sometime - uses blocking child to call getaddrinfo then
2262b15cb3dSCy Schubert  *			  invokes provided callback completion function.
2272b15cb3dSCy Schubert  */
2282b15cb3dSCy Schubert int
229f391d6bcSXin LI getaddrinfo_sometime_ex(
2302b15cb3dSCy Schubert 	const char *		node,
2312b15cb3dSCy Schubert 	const char *		service,
2322b15cb3dSCy Schubert 	const struct addrinfo *	hints,
2332b15cb3dSCy Schubert 	int			retry,
2342b15cb3dSCy Schubert 	gai_sometime_callback	callback,
235f391d6bcSXin LI 	void *			context,
236f391d6bcSXin LI 	u_int			qflags
2372b15cb3dSCy Schubert 	)
2382b15cb3dSCy Schubert {
2392b15cb3dSCy Schubert 	blocking_gai_req *	gai_req;
2402b15cb3dSCy Schubert 	u_int			idx;
2412b15cb3dSCy Schubert 	dnschild_ctx *		child_ctx;
2422b15cb3dSCy Schubert 	size_t			req_size;
2432b15cb3dSCy Schubert 	size_t			nodesize;
2442b15cb3dSCy Schubert 	size_t			servsize;
2452b15cb3dSCy Schubert 	time_t			now;
2462b15cb3dSCy Schubert 
2479034852cSGleb Smirnoff 	REQUIRE(NULL != node);
2482b15cb3dSCy Schubert 	if (NULL != hints) {
2499034852cSGleb Smirnoff 		REQUIRE(0 == hints->ai_addrlen);
2509034852cSGleb Smirnoff 		REQUIRE(NULL == hints->ai_addr);
2519034852cSGleb Smirnoff 		REQUIRE(NULL == hints->ai_canonname);
2529034852cSGleb Smirnoff 		REQUIRE(NULL == hints->ai_next);
2532b15cb3dSCy Schubert 	}
2542b15cb3dSCy Schubert 
2552b15cb3dSCy Schubert 	idx = get_dnschild_ctx();
2562b15cb3dSCy Schubert 	child_ctx = dnschild_contexts[idx];
2572b15cb3dSCy Schubert 
2582b15cb3dSCy Schubert 	nodesize = strlen(node) + 1;
2592b15cb3dSCy Schubert 	servsize = strlen(service) + 1;
2602b15cb3dSCy Schubert 	req_size = sizeof(*gai_req) + nodesize + servsize;
2612b15cb3dSCy Schubert 
2622b15cb3dSCy Schubert 	gai_req = emalloc_zero(req_size);
2632b15cb3dSCy Schubert 
2642b15cb3dSCy Schubert 	gai_req->octets = req_size;
2652b15cb3dSCy Schubert 	gai_req->dns_idx = idx;
2662b15cb3dSCy Schubert 	now = time(NULL);
2672b15cb3dSCy Schubert 	gai_req->scheduled = now;
2682b15cb3dSCy Schubert 	gai_req->earliest = max(now, child_ctx->next_dns_timeslot);
2692b15cb3dSCy Schubert 	child_ctx->next_dns_timeslot = gai_req->earliest;
2702b15cb3dSCy Schubert 	if (hints != NULL)
2712b15cb3dSCy Schubert 		gai_req->hints = *hints;
2722b15cb3dSCy Schubert 	gai_req->retry = retry;
2732b15cb3dSCy Schubert 	gai_req->callback = callback;
2742b15cb3dSCy Schubert 	gai_req->context = context;
2752b15cb3dSCy Schubert 	gai_req->nodesize = nodesize;
2762b15cb3dSCy Schubert 	gai_req->servsize = servsize;
277f391d6bcSXin LI 	gai_req->qflags = qflags;
2782b15cb3dSCy Schubert 
2792b15cb3dSCy Schubert 	memcpy((char *)gai_req + sizeof(*gai_req), node, nodesize);
2802b15cb3dSCy Schubert 	memcpy((char *)gai_req + sizeof(*gai_req) + nodesize, service,
2812b15cb3dSCy Schubert 	       servsize);
2822b15cb3dSCy Schubert 
2832b15cb3dSCy Schubert 	if (queue_blocking_request(
2842b15cb3dSCy Schubert 		BLOCKING_GETADDRINFO,
2852b15cb3dSCy Schubert 		gai_req,
2862b15cb3dSCy Schubert 		req_size,
2872b15cb3dSCy Schubert 		&getaddrinfo_sometime_complete,
2882b15cb3dSCy Schubert 		gai_req)) {
2892b15cb3dSCy Schubert 
2902b15cb3dSCy Schubert 		msyslog(LOG_ERR, "unable to queue getaddrinfo request");
2912b15cb3dSCy Schubert 		errno = EFAULT;
2922b15cb3dSCy Schubert 		return -1;
2932b15cb3dSCy Schubert 	}
2942b15cb3dSCy Schubert 
2952b15cb3dSCy Schubert 	return 0;
2962b15cb3dSCy Schubert }
2972b15cb3dSCy Schubert 
2982b15cb3dSCy Schubert int
2992b15cb3dSCy Schubert blocking_getaddrinfo(
3002b15cb3dSCy Schubert 	blocking_child *	c,
3012b15cb3dSCy Schubert 	blocking_pipe_header *	req
3022b15cb3dSCy Schubert 	)
3032b15cb3dSCy Schubert {
3042b15cb3dSCy Schubert 	blocking_gai_req *	gai_req;
3052b15cb3dSCy Schubert 	dnsworker_ctx *		worker_ctx;
3062b15cb3dSCy Schubert 	blocking_pipe_header *	resp;
3072b15cb3dSCy Schubert 	blocking_gai_resp *	gai_resp;
3082b15cb3dSCy Schubert 	char *			node;
3092b15cb3dSCy Schubert 	char *			service;
3102b15cb3dSCy Schubert 	struct addrinfo *	ai_res;
3112b15cb3dSCy Schubert 	struct addrinfo *	ai;
3122b15cb3dSCy Schubert 	struct addrinfo *	serialized_ai;
3132b15cb3dSCy Schubert 	size_t			canons_octets;
3142b15cb3dSCy Schubert 	size_t			this_octets;
3152b15cb3dSCy Schubert 	size_t			resp_octets;
3162b15cb3dSCy Schubert 	char *			cp;
3172b15cb3dSCy Schubert 	time_t			time_now;
3182b15cb3dSCy Schubert 
3192b15cb3dSCy Schubert 	gai_req = (void *)((char *)req + sizeof(*req));
3202b15cb3dSCy Schubert 	node = (char *)gai_req + sizeof(*gai_req);
3212b15cb3dSCy Schubert 	service = node + gai_req->nodesize;
3222b15cb3dSCy Schubert 
3232b15cb3dSCy Schubert 	worker_ctx = get_worker_context(c, gai_req->dns_idx);
3242b15cb3dSCy Schubert 	scheduled_sleep(gai_req->scheduled, gai_req->earliest,
3252b15cb3dSCy Schubert 			worker_ctx);
3262b15cb3dSCy Schubert 	reload_resolv_conf(worker_ctx);
3272b15cb3dSCy Schubert 
3282b15cb3dSCy Schubert 	/*
3292b15cb3dSCy Schubert 	 * Take a shot at the final size, better to overestimate
3302b15cb3dSCy Schubert 	 * at first and then realloc to a smaller size.
3312b15cb3dSCy Schubert 	 */
3322b15cb3dSCy Schubert 
3332b15cb3dSCy Schubert 	resp_octets = sizeof(*resp) + sizeof(*gai_resp) +
3342b15cb3dSCy Schubert 		      16 * (sizeof(struct addrinfo) +
3352b15cb3dSCy Schubert 			    sizeof(sockaddr_u)) +
3362b15cb3dSCy Schubert 		      256;
3372b15cb3dSCy Schubert 	resp = emalloc_zero(resp_octets);
3382b15cb3dSCy Schubert 	gai_resp = (void *)(resp + 1);
3392b15cb3dSCy Schubert 
3402b15cb3dSCy Schubert 	TRACE(2, ("blocking_getaddrinfo given node %s serv %s fam %d flags %x\n",
3412b15cb3dSCy Schubert 		  node, service, gai_req->hints.ai_family,
3422b15cb3dSCy Schubert 		  gai_req->hints.ai_flags));
3432b15cb3dSCy Schubert #ifdef DEBUG
3442b15cb3dSCy Schubert 	if (debug >= 2)
3452b15cb3dSCy Schubert 		fflush(stdout);
3462b15cb3dSCy Schubert #endif
3472b15cb3dSCy Schubert 	ai_res = NULL;
3482b15cb3dSCy Schubert 	gai_resp->retcode = getaddrinfo(node, service, &gai_req->hints,
3492b15cb3dSCy Schubert 					&ai_res);
3502b15cb3dSCy Schubert 	gai_resp->retry = gai_req->retry;
3512b15cb3dSCy Schubert #ifdef EAI_SYSTEM
3522b15cb3dSCy Schubert 	if (EAI_SYSTEM == gai_resp->retcode)
3532b15cb3dSCy Schubert 		gai_resp->gai_errno = errno;
3542b15cb3dSCy Schubert #endif
3552b15cb3dSCy Schubert 	canons_octets = 0;
3562b15cb3dSCy Schubert 
3572b15cb3dSCy Schubert 	if (0 == gai_resp->retcode) {
3582b15cb3dSCy Schubert 		ai = ai_res;
3592b15cb3dSCy Schubert 		while (NULL != ai) {
3602b15cb3dSCy Schubert 			gai_resp->ai_count++;
3612b15cb3dSCy Schubert 			if (ai->ai_canonname)
3622b15cb3dSCy Schubert 				canons_octets += strlen(ai->ai_canonname) + 1;
3632b15cb3dSCy Schubert 			ai = ai->ai_next;
3642b15cb3dSCy Schubert 		}
3652b15cb3dSCy Schubert 		/*
3662b15cb3dSCy Schubert 		 * If this query succeeded only after retrying, DNS may have
3672b15cb3dSCy Schubert 		 * just become responsive.  Ignore previously-scheduled
3682b15cb3dSCy Schubert 		 * retry sleeps once for each pending request, similar to
3692b15cb3dSCy Schubert 		 * the way scheduled_sleep() does when its worker_sleep()
3702b15cb3dSCy Schubert 		 * is interrupted.
3712b15cb3dSCy Schubert 		 */
3722b15cb3dSCy Schubert 		if (gai_resp->retry > INITIAL_DNS_RETRY) {
3732b15cb3dSCy Schubert 			time_now = time(NULL);
3742b15cb3dSCy Schubert 			worker_ctx->ignore_scheduled_before = time_now;
3752b15cb3dSCy Schubert 			TRACE(1, ("DNS success after retry, ignoring sleeps scheduled before now (%s)\n",
3762b15cb3dSCy Schubert 				  humantime(time_now)));
3772b15cb3dSCy Schubert 		}
3782b15cb3dSCy Schubert 	}
3792b15cb3dSCy Schubert 
3802b15cb3dSCy Schubert 	/*
3812b15cb3dSCy Schubert 	 * Our response consists of a header, followed by ai_count
3822b15cb3dSCy Schubert 	 * addrinfo structs followed by ai_count sockaddr_storage
3832b15cb3dSCy Schubert 	 * structs followed by the canonical names.
3842b15cb3dSCy Schubert 	 */
3852b15cb3dSCy Schubert 	gai_resp->octets = sizeof(*gai_resp)
3862b15cb3dSCy Schubert 			    + gai_resp->ai_count
3872b15cb3dSCy Schubert 				* (sizeof(gai_req->hints)
3882b15cb3dSCy Schubert 				   + sizeof(sockaddr_u))
3892b15cb3dSCy Schubert 			    + canons_octets;
3902b15cb3dSCy Schubert 
3912b15cb3dSCy Schubert 	resp_octets = sizeof(*resp) + gai_resp->octets;
3922b15cb3dSCy Schubert 	resp = erealloc(resp, resp_octets);
3932b15cb3dSCy Schubert 	gai_resp = (void *)(resp + 1);
3942b15cb3dSCy Schubert 
3952b15cb3dSCy Schubert 	/* cp serves as our current pointer while serializing */
3962b15cb3dSCy Schubert 	cp = (void *)(gai_resp + 1);
3972b15cb3dSCy Schubert 	canons_octets = 0;
3982b15cb3dSCy Schubert 
3992b15cb3dSCy Schubert 	if (0 == gai_resp->retcode) {
4002b15cb3dSCy Schubert 		ai = ai_res;
4012b15cb3dSCy Schubert 		while (NULL != ai) {
4022b15cb3dSCy Schubert 			memcpy(cp, ai, sizeof(*ai));
4032b15cb3dSCy Schubert 			serialized_ai = (void *)cp;
4042b15cb3dSCy Schubert 			cp += sizeof(*ai);
4052b15cb3dSCy Schubert 
4062b15cb3dSCy Schubert 			/* transform ai_canonname into offset */
407*f0574f5cSXin LI 			if (NULL != ai->ai_canonname) {
4082b15cb3dSCy Schubert 				serialized_ai->ai_canonname = (char *)canons_octets;
4092b15cb3dSCy Schubert 				canons_octets += strlen(ai->ai_canonname) + 1;
4102b15cb3dSCy Schubert 			}
4112b15cb3dSCy Schubert 
4122b15cb3dSCy Schubert 			/* leave fixup of ai_addr pointer for receiver */
4132b15cb3dSCy Schubert 
4142b15cb3dSCy Schubert 			ai = ai->ai_next;
4152b15cb3dSCy Schubert 		}
4162b15cb3dSCy Schubert 
4172b15cb3dSCy Schubert 		ai = ai_res;
4182b15cb3dSCy Schubert 		while (NULL != ai) {
4199034852cSGleb Smirnoff 			INSIST(ai->ai_addrlen <= sizeof(sockaddr_u));
4202b15cb3dSCy Schubert 			memcpy(cp, ai->ai_addr, ai->ai_addrlen);
4212b15cb3dSCy Schubert 			cp += sizeof(sockaddr_u);
4222b15cb3dSCy Schubert 
4232b15cb3dSCy Schubert 			ai = ai->ai_next;
4242b15cb3dSCy Schubert 		}
4252b15cb3dSCy Schubert 
4262b15cb3dSCy Schubert 		ai = ai_res;
4272b15cb3dSCy Schubert 		while (NULL != ai) {
4282b15cb3dSCy Schubert 			if (NULL != ai->ai_canonname) {
4292b15cb3dSCy Schubert 				this_octets = strlen(ai->ai_canonname) + 1;
4302b15cb3dSCy Schubert 				memcpy(cp, ai->ai_canonname, this_octets);
4312b15cb3dSCy Schubert 				cp += this_octets;
4322b15cb3dSCy Schubert 			}
4332b15cb3dSCy Schubert 
4342b15cb3dSCy Schubert 			ai = ai->ai_next;
4352b15cb3dSCy Schubert 		}
4362b15cb3dSCy Schubert 		freeaddrinfo(ai_res);
4372b15cb3dSCy Schubert 	}
4382b15cb3dSCy Schubert 
4392b15cb3dSCy Schubert 	/*
4402b15cb3dSCy Schubert 	 * make sure our walk and earlier calc match
4412b15cb3dSCy Schubert 	 */
4422b15cb3dSCy Schubert 	DEBUG_INSIST((size_t)(cp - (char *)resp) == resp_octets);
4432b15cb3dSCy Schubert 
4442b15cb3dSCy Schubert 	if (queue_blocking_response(c, resp, resp_octets, req)) {
4452b15cb3dSCy Schubert 		msyslog(LOG_ERR, "blocking_getaddrinfo can not queue response");
4462b15cb3dSCy Schubert 		return -1;
4472b15cb3dSCy Schubert 	}
4482b15cb3dSCy Schubert 
4492b15cb3dSCy Schubert 	return 0;
4502b15cb3dSCy Schubert }
4512b15cb3dSCy Schubert 
452f391d6bcSXin LI int
453f391d6bcSXin LI getaddrinfo_sometime(
454f391d6bcSXin LI 	const char *		node,
455f391d6bcSXin LI 	const char *		service,
456f391d6bcSXin LI 	const struct addrinfo *	hints,
457f391d6bcSXin LI 	int			retry,
458f391d6bcSXin LI 	gai_sometime_callback	callback,
459f391d6bcSXin LI 	void *			context
460f391d6bcSXin LI 	)
461f391d6bcSXin LI {
462f391d6bcSXin LI 	return getaddrinfo_sometime_ex(node, service, hints, retry,
463f391d6bcSXin LI 				       callback, context, 0);
464f391d6bcSXin LI }
465f391d6bcSXin LI 
4662b15cb3dSCy Schubert 
4672b15cb3dSCy Schubert static void
4682b15cb3dSCy Schubert getaddrinfo_sometime_complete(
4692b15cb3dSCy Schubert 	blocking_work_req	rtype,
4702b15cb3dSCy Schubert 	void *			context,
4712b15cb3dSCy Schubert 	size_t			respsize,
4722b15cb3dSCy Schubert 	void *			resp
4732b15cb3dSCy Schubert 	)
4742b15cb3dSCy Schubert {
4752b15cb3dSCy Schubert 	blocking_gai_req *	gai_req;
4762b15cb3dSCy Schubert 	blocking_gai_resp *	gai_resp;
4772b15cb3dSCy Schubert 	dnschild_ctx *		child_ctx;
4782b15cb3dSCy Schubert 	struct addrinfo *	ai;
4792b15cb3dSCy Schubert 	struct addrinfo *	next_ai;
4802b15cb3dSCy Schubert 	sockaddr_u *		psau;
4812b15cb3dSCy Schubert 	char *			node;
4822b15cb3dSCy Schubert 	char *			service;
4832b15cb3dSCy Schubert 	char *			canon_start;
4842b15cb3dSCy Schubert 	time_t			time_now;
485f391d6bcSXin LI 	int			again, noerr;
4862b15cb3dSCy Schubert 	int			af;
4872b15cb3dSCy Schubert 	const char *		fam_spec;
4882b15cb3dSCy Schubert 	int			i;
4892b15cb3dSCy Schubert 
4902b15cb3dSCy Schubert 	gai_req = context;
4912b15cb3dSCy Schubert 	gai_resp = resp;
4922b15cb3dSCy Schubert 
4932b15cb3dSCy Schubert 	DEBUG_REQUIRE(BLOCKING_GETADDRINFO == rtype);
4942b15cb3dSCy Schubert 	DEBUG_REQUIRE(respsize == gai_resp->octets);
4952b15cb3dSCy Schubert 
4962b15cb3dSCy Schubert 	node = (char *)gai_req + sizeof(*gai_req);
4972b15cb3dSCy Schubert 	service = node + gai_req->nodesize;
4982b15cb3dSCy Schubert 
4992b15cb3dSCy Schubert 	child_ctx = dnschild_contexts[gai_req->dns_idx];
5002b15cb3dSCy Schubert 
5012b15cb3dSCy Schubert 	if (0 == gai_resp->retcode) {
5022b15cb3dSCy Schubert 		/*
5032b15cb3dSCy Schubert 		 * If this query succeeded only after retrying, DNS may have
5042b15cb3dSCy Schubert 		 * just become responsive.
5052b15cb3dSCy Schubert 		 */
5062b15cb3dSCy Schubert 		if (gai_resp->retry > INITIAL_DNS_RETRY) {
5072b15cb3dSCy Schubert 			time_now = time(NULL);
5082b15cb3dSCy Schubert 			child_ctx->next_dns_timeslot = time_now;
5092b15cb3dSCy Schubert 			TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n",
5102b15cb3dSCy Schubert 				  gai_req->dns_idx, humantime(time_now)));
5112b15cb3dSCy Schubert 		}
5122b15cb3dSCy Schubert 	} else {
513f391d6bcSXin LI 		noerr = !!(gai_req->qflags & GAIR_F_IGNDNSERR);
514f391d6bcSXin LI 		again = noerr || should_retry_dns(
515f391d6bcSXin LI 					gai_resp->retcode, gai_resp->gai_errno);
5162b15cb3dSCy Schubert 		/*
5172b15cb3dSCy Schubert 		 * exponential backoff of DNS retries to 64s
5182b15cb3dSCy Schubert 		 */
5192b15cb3dSCy Schubert 		if (gai_req->retry > 0 && again) {
5202b15cb3dSCy Schubert 			/* log the first retry only */
5212b15cb3dSCy Schubert 			if (INITIAL_DNS_RETRY == gai_req->retry)
5222b15cb3dSCy Schubert 				NLOG(NLOG_SYSINFO) {
5232b15cb3dSCy Schubert 					af = gai_req->hints.ai_family;
5242b15cb3dSCy Schubert 					fam_spec = (AF_INET6 == af)
5252b15cb3dSCy Schubert 						       ? " (AAAA)"
5262b15cb3dSCy Schubert 						       : (AF_INET == af)
5272b15cb3dSCy Schubert 							     ? " (A)"
5282b15cb3dSCy Schubert 							     : "";
5292b15cb3dSCy Schubert #ifdef EAI_SYSTEM
5302b15cb3dSCy Schubert 					if (EAI_SYSTEM == gai_resp->retcode) {
5312b15cb3dSCy Schubert 						errno = gai_resp->gai_errno;
5322b15cb3dSCy Schubert 						msyslog(LOG_INFO,
5332b15cb3dSCy Schubert 							"retrying DNS %s%s: EAI_SYSTEM %d: %m",
5342b15cb3dSCy Schubert 							node, fam_spec,
5352b15cb3dSCy Schubert 							gai_resp->gai_errno);
5362b15cb3dSCy Schubert 					} else
5372b15cb3dSCy Schubert #endif
5382b15cb3dSCy Schubert 						msyslog(LOG_INFO,
5392b15cb3dSCy Schubert 							"retrying DNS %s%s: %s (%d)",
5402b15cb3dSCy Schubert 							node, fam_spec,
5412b15cb3dSCy Schubert 							gai_strerror(gai_resp->retcode),
5422b15cb3dSCy Schubert 							gai_resp->retcode);
5432b15cb3dSCy Schubert 				}
544f391d6bcSXin LI 			manage_dns_retry_interval(
545f391d6bcSXin LI 				&gai_req->scheduled, &gai_req->earliest,
546f391d6bcSXin LI 				&gai_req->retry, &child_ctx->next_dns_timeslot,
547f391d6bcSXin LI 				noerr);
5482b15cb3dSCy Schubert 			if (!queue_blocking_request(
5492b15cb3dSCy Schubert 					BLOCKING_GETADDRINFO,
5502b15cb3dSCy Schubert 					gai_req,
5512b15cb3dSCy Schubert 					gai_req->octets,
5522b15cb3dSCy Schubert 					&getaddrinfo_sometime_complete,
5532b15cb3dSCy Schubert 					gai_req))
5542b15cb3dSCy Schubert 				return;
5552b15cb3dSCy Schubert 			else
5562b15cb3dSCy Schubert 				msyslog(LOG_ERR,
5572b15cb3dSCy Schubert 					"unable to retry hostname %s",
5582b15cb3dSCy Schubert 					node);
5592b15cb3dSCy Schubert 		}
5602b15cb3dSCy Schubert 	}
5612b15cb3dSCy Schubert 
5622b15cb3dSCy Schubert 	/*
5632b15cb3dSCy Schubert 	 * fixup pointers in returned addrinfo array
5642b15cb3dSCy Schubert 	 */
5652b15cb3dSCy Schubert 	ai = (void *)((char *)gai_resp + sizeof(*gai_resp));
5662b15cb3dSCy Schubert 	next_ai = NULL;
5672b15cb3dSCy Schubert 	for (i = gai_resp->ai_count - 1; i >= 0; i--) {
5682b15cb3dSCy Schubert 		ai[i].ai_next = next_ai;
5692b15cb3dSCy Schubert 		next_ai = &ai[i];
5702b15cb3dSCy Schubert 	}
5712b15cb3dSCy Schubert 
5722b15cb3dSCy Schubert 	psau = (void *)((char *)ai + gai_resp->ai_count * sizeof(*ai));
5732b15cb3dSCy Schubert 	canon_start = (char *)psau + gai_resp->ai_count * sizeof(*psau);
5742b15cb3dSCy Schubert 
5752b15cb3dSCy Schubert 	for (i = 0; i < gai_resp->ai_count; i++) {
5762b15cb3dSCy Schubert 		if (NULL != ai[i].ai_addr)
5772b15cb3dSCy Schubert 			ai[i].ai_addr = &psau->sa;
5782b15cb3dSCy Schubert 		psau++;
5792b15cb3dSCy Schubert 		if (NULL != ai[i].ai_canonname)
5802b15cb3dSCy Schubert 			ai[i].ai_canonname += (size_t)canon_start;
5812b15cb3dSCy Schubert 	}
5822b15cb3dSCy Schubert 
5839034852cSGleb Smirnoff 	ENSURE((char *)psau == canon_start);
5842b15cb3dSCy Schubert 
5852b15cb3dSCy Schubert 	if (!gai_resp->ai_count)
5862b15cb3dSCy Schubert 		ai = NULL;
5872b15cb3dSCy Schubert 
5882b15cb3dSCy Schubert 	(*gai_req->callback)(gai_resp->retcode, gai_resp->gai_errno,
5892b15cb3dSCy Schubert 			     gai_req->context, node, service,
5902b15cb3dSCy Schubert 			     &gai_req->hints, ai);
5912b15cb3dSCy Schubert 
5922b15cb3dSCy Schubert 	free(gai_req);
5932b15cb3dSCy Schubert 	/* gai_resp is part of block freed by process_blocking_resp() */
5942b15cb3dSCy Schubert }
5952b15cb3dSCy Schubert 
5962b15cb3dSCy Schubert 
5972b15cb3dSCy Schubert #ifdef TEST_BLOCKING_WORKER
5982b15cb3dSCy Schubert 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)
5992b15cb3dSCy Schubert {
6002b15cb3dSCy Schubert 	sockaddr_u addr;
6012b15cb3dSCy Schubert 
6022b15cb3dSCy Schubert 	if (rescode) {
6032b15cb3dSCy Schubert 		TRACE(1, ("gai_test_callback context %p error rescode %d %s serv %s\n",
6042b15cb3dSCy Schubert 			  context, rescode, name, service));
6052b15cb3dSCy Schubert 		return;
6062b15cb3dSCy Schubert 	}
6072b15cb3dSCy Schubert 	while (!rescode && NULL != ai_res) {
6082b15cb3dSCy Schubert 		ZERO_SOCK(&addr);
6092b15cb3dSCy Schubert 		memcpy(&addr, ai_res->ai_addr, ai_res->ai_addrlen);
6102b15cb3dSCy Schubert 		TRACE(1, ("ctx %p fam %d addr %s canon '%s' type %s at %p ai_addr %p ai_next %p\n",
6112b15cb3dSCy Schubert 			  context,
6122b15cb3dSCy Schubert 			  AF(&addr),
6132b15cb3dSCy Schubert 			  stoa(&addr),
6142b15cb3dSCy Schubert 			  (ai_res->ai_canonname)
6152b15cb3dSCy Schubert 			      ? ai_res->ai_canonname
6162b15cb3dSCy Schubert 			      : "",
6172b15cb3dSCy Schubert 			  (SOCK_DGRAM == ai_res->ai_socktype)
6182b15cb3dSCy Schubert 			      ? "DGRAM"
6192b15cb3dSCy Schubert 			      : (SOCK_STREAM == ai_res->ai_socktype)
6202b15cb3dSCy Schubert 				    ? "STREAM"
6212b15cb3dSCy Schubert 				    : "(other)",
6222b15cb3dSCy Schubert 			  ai_res,
6232b15cb3dSCy Schubert 			  ai_res->ai_addr,
6242b15cb3dSCy Schubert 			  ai_res->ai_next));
6252b15cb3dSCy Schubert 
6262b15cb3dSCy Schubert 		getnameinfo_sometime((sockaddr_u *)ai_res->ai_addr, 128, 32, 0, gni_test_callback, context);
6272b15cb3dSCy Schubert 
6282b15cb3dSCy Schubert 		ai_res = ai_res->ai_next;
6292b15cb3dSCy Schubert 	}
6302b15cb3dSCy Schubert }
6312b15cb3dSCy Schubert #endif	/* TEST_BLOCKING_WORKER */
6322b15cb3dSCy Schubert 
6332b15cb3dSCy Schubert 
6342b15cb3dSCy Schubert int
6352b15cb3dSCy Schubert getnameinfo_sometime(
6362b15cb3dSCy Schubert 	sockaddr_u *		psau,
6372b15cb3dSCy Schubert 	size_t			hostoctets,
6382b15cb3dSCy Schubert 	size_t			servoctets,
6392b15cb3dSCy Schubert 	int			flags,
6402b15cb3dSCy Schubert 	gni_sometime_callback	callback,
6412b15cb3dSCy Schubert 	void *			context
6422b15cb3dSCy Schubert 	)
6432b15cb3dSCy Schubert {
6442b15cb3dSCy Schubert 	blocking_gni_req *	gni_req;
6452b15cb3dSCy Schubert 	u_int			idx;
6462b15cb3dSCy Schubert 	dnschild_ctx *		child_ctx;
6472b15cb3dSCy Schubert 	time_t			time_now;
6482b15cb3dSCy Schubert 
6499034852cSGleb Smirnoff 	REQUIRE(hostoctets);
6509034852cSGleb Smirnoff 	REQUIRE(hostoctets + servoctets < 1024);
6512b15cb3dSCy Schubert 
6522b15cb3dSCy Schubert 	idx = get_dnschild_ctx();
6532b15cb3dSCy Schubert 	child_ctx = dnschild_contexts[idx];
6542b15cb3dSCy Schubert 
6552b15cb3dSCy Schubert 	gni_req = emalloc_zero(sizeof(*gni_req));
6562b15cb3dSCy Schubert 
6572b15cb3dSCy Schubert 	gni_req->octets = sizeof(*gni_req);
6582b15cb3dSCy Schubert 	gni_req->dns_idx = idx;
6592b15cb3dSCy Schubert 	time_now = time(NULL);
6602b15cb3dSCy Schubert 	gni_req->scheduled = time_now;
6612b15cb3dSCy Schubert 	gni_req->earliest = max(time_now, child_ctx->next_dns_timeslot);
6622b15cb3dSCy Schubert 	child_ctx->next_dns_timeslot = gni_req->earliest;
6632b15cb3dSCy Schubert 	memcpy(&gni_req->socku, psau, SOCKLEN(psau));
6642b15cb3dSCy Schubert 	gni_req->hostoctets = hostoctets;
6652b15cb3dSCy Schubert 	gni_req->servoctets = servoctets;
6662b15cb3dSCy Schubert 	gni_req->flags = flags;
6672b15cb3dSCy Schubert 	gni_req->retry = INITIAL_DNS_RETRY;
6682b15cb3dSCy Schubert 	gni_req->callback = callback;
6692b15cb3dSCy Schubert 	gni_req->context = context;
6702b15cb3dSCy Schubert 
6712b15cb3dSCy Schubert 	if (queue_blocking_request(
6722b15cb3dSCy Schubert 		BLOCKING_GETNAMEINFO,
6732b15cb3dSCy Schubert 		gni_req,
6742b15cb3dSCy Schubert 		sizeof(*gni_req),
6752b15cb3dSCy Schubert 		&getnameinfo_sometime_complete,
6762b15cb3dSCy Schubert 		gni_req)) {
6772b15cb3dSCy Schubert 
6782b15cb3dSCy Schubert 		msyslog(LOG_ERR, "unable to queue getnameinfo request");
6792b15cb3dSCy Schubert 		errno = EFAULT;
6802b15cb3dSCy Schubert 		return -1;
6812b15cb3dSCy Schubert 	}
6822b15cb3dSCy Schubert 
6832b15cb3dSCy Schubert 	return 0;
6842b15cb3dSCy Schubert }
6852b15cb3dSCy Schubert 
6862b15cb3dSCy Schubert 
6872b15cb3dSCy Schubert int
6882b15cb3dSCy Schubert blocking_getnameinfo(
6892b15cb3dSCy Schubert 	blocking_child *	c,
6902b15cb3dSCy Schubert 	blocking_pipe_header *	req
6912b15cb3dSCy Schubert 	)
6922b15cb3dSCy Schubert {
6932b15cb3dSCy Schubert 	blocking_gni_req *	gni_req;
6942b15cb3dSCy Schubert 	dnsworker_ctx *		worker_ctx;
6952b15cb3dSCy Schubert 	blocking_pipe_header *	resp;
6962b15cb3dSCy Schubert 	blocking_gni_resp *	gni_resp;
6972b15cb3dSCy Schubert 	size_t			octets;
6982b15cb3dSCy Schubert 	size_t			resp_octets;
6992b15cb3dSCy Schubert 	char *			service;
7002b15cb3dSCy Schubert 	char *			cp;
7012b15cb3dSCy Schubert 	int			rc;
7022b15cb3dSCy Schubert 	time_t			time_now;
7032b15cb3dSCy Schubert 	char			host[1024];
7042b15cb3dSCy Schubert 
7052b15cb3dSCy Schubert 	gni_req = (void *)((char *)req + sizeof(*req));
7062b15cb3dSCy Schubert 
7072b15cb3dSCy Schubert 	octets = gni_req->hostoctets + gni_req->servoctets;
7082b15cb3dSCy Schubert 
7092b15cb3dSCy Schubert 	/*
7102b15cb3dSCy Schubert 	 * Some alloca() implementations are fragile regarding
7112b15cb3dSCy Schubert 	 * large allocations.  We only need room for the host
7122b15cb3dSCy Schubert 	 * and service names.
7132b15cb3dSCy Schubert 	 */
7149034852cSGleb Smirnoff 	REQUIRE(octets < sizeof(host));
7152b15cb3dSCy Schubert 	service = host + gni_req->hostoctets;
7162b15cb3dSCy Schubert 
7172b15cb3dSCy Schubert 	worker_ctx = get_worker_context(c, gni_req->dns_idx);
7182b15cb3dSCy Schubert 	scheduled_sleep(gni_req->scheduled, gni_req->earliest,
7192b15cb3dSCy Schubert 			worker_ctx);
7202b15cb3dSCy Schubert 	reload_resolv_conf(worker_ctx);
7212b15cb3dSCy Schubert 
7222b15cb3dSCy Schubert 	/*
7232b15cb3dSCy Schubert 	 * Take a shot at the final size, better to overestimate
7242b15cb3dSCy Schubert 	 * then realloc to a smaller size.
7252b15cb3dSCy Schubert 	 */
7262b15cb3dSCy Schubert 
7272b15cb3dSCy Schubert 	resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets;
7282b15cb3dSCy Schubert 	resp = emalloc_zero(resp_octets);
7292b15cb3dSCy Schubert 	gni_resp = (void *)((char *)resp + sizeof(*resp));
7302b15cb3dSCy Schubert 
7312b15cb3dSCy Schubert 	TRACE(2, ("blocking_getnameinfo given addr %s flags 0x%x hostlen %lu servlen %lu\n",
7322b15cb3dSCy Schubert 		  stoa(&gni_req->socku), gni_req->flags,
7332b15cb3dSCy Schubert 		  (u_long)gni_req->hostoctets, (u_long)gni_req->servoctets));
7342b15cb3dSCy Schubert 
7352b15cb3dSCy Schubert 	gni_resp->retcode = getnameinfo(&gni_req->socku.sa,
7362b15cb3dSCy Schubert 					SOCKLEN(&gni_req->socku),
7372b15cb3dSCy Schubert 					host,
7382b15cb3dSCy Schubert 					gni_req->hostoctets,
7392b15cb3dSCy Schubert 					service,
7402b15cb3dSCy Schubert 					gni_req->servoctets,
7412b15cb3dSCy Schubert 					gni_req->flags);
7422b15cb3dSCy Schubert 	gni_resp->retry = gni_req->retry;
7432b15cb3dSCy Schubert #ifdef EAI_SYSTEM
7442b15cb3dSCy Schubert 	if (EAI_SYSTEM == gni_resp->retcode)
7452b15cb3dSCy Schubert 		gni_resp->gni_errno = errno;
7462b15cb3dSCy Schubert #endif
7472b15cb3dSCy Schubert 
7482b15cb3dSCy Schubert 	if (0 != gni_resp->retcode) {
7492b15cb3dSCy Schubert 		gni_resp->hostoctets = 0;
7502b15cb3dSCy Schubert 		gni_resp->servoctets = 0;
7512b15cb3dSCy Schubert 	} else {
7522b15cb3dSCy Schubert 		gni_resp->hostoctets = strlen(host) + 1;
7532b15cb3dSCy Schubert 		gni_resp->servoctets = strlen(service) + 1;
7542b15cb3dSCy Schubert 		/*
7552b15cb3dSCy Schubert 		 * If this query succeeded only after retrying, DNS may have
7562b15cb3dSCy Schubert 		 * just become responsive.  Ignore previously-scheduled
7572b15cb3dSCy Schubert 		 * retry sleeps once for each pending request, similar to
7582b15cb3dSCy Schubert 		 * the way scheduled_sleep() does when its worker_sleep()
7592b15cb3dSCy Schubert 		 * is interrupted.
7602b15cb3dSCy Schubert 		 */
7612b15cb3dSCy Schubert 		if (gni_req->retry > INITIAL_DNS_RETRY) {
7622b15cb3dSCy Schubert 			time_now = time(NULL);
7632b15cb3dSCy Schubert 			worker_ctx->ignore_scheduled_before = time_now;
7642b15cb3dSCy Schubert 			TRACE(1, ("DNS success after retrying, ignoring sleeps scheduled before now (%s)\n",
7652b15cb3dSCy Schubert 				humantime(time_now)));
7662b15cb3dSCy Schubert 		}
7672b15cb3dSCy Schubert 	}
7682b15cb3dSCy Schubert 	octets = gni_resp->hostoctets + gni_resp->servoctets;
7692b15cb3dSCy Schubert 	/*
7702b15cb3dSCy Schubert 	 * Our response consists of a header, followed by the host and
7712b15cb3dSCy Schubert 	 * service strings, each null-terminated.
7722b15cb3dSCy Schubert 	 */
7732b15cb3dSCy Schubert 	resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets;
7742b15cb3dSCy Schubert 
7752b15cb3dSCy Schubert 	resp = erealloc(resp, resp_octets);
7762b15cb3dSCy Schubert 	gni_resp = (void *)(resp + 1);
7772b15cb3dSCy Schubert 
7782b15cb3dSCy Schubert 	gni_resp->octets = sizeof(*gni_resp) + octets;
7792b15cb3dSCy Schubert 
7802b15cb3dSCy Schubert 	/* cp serves as our current pointer while serializing */
7812b15cb3dSCy Schubert 	cp = (void *)(gni_resp + 1);
7822b15cb3dSCy Schubert 
7832b15cb3dSCy Schubert 	if (0 == gni_resp->retcode) {
7842b15cb3dSCy Schubert 		memcpy(cp, host, gni_resp->hostoctets);
7852b15cb3dSCy Schubert 		cp += gni_resp->hostoctets;
7862b15cb3dSCy Schubert 		memcpy(cp, service, gni_resp->servoctets);
7872b15cb3dSCy Schubert 		cp += gni_resp->servoctets;
7882b15cb3dSCy Schubert 	}
7892b15cb3dSCy Schubert 
7909034852cSGleb Smirnoff 	INSIST((size_t)(cp - (char *)resp) == resp_octets);
7919034852cSGleb Smirnoff 	INSIST(resp_octets - sizeof(*resp) == gni_resp->octets);
7922b15cb3dSCy Schubert 
7932b15cb3dSCy Schubert 	rc = queue_blocking_response(c, resp, resp_octets, req);
7942b15cb3dSCy Schubert 	if (rc)
7952b15cb3dSCy Schubert 		msyslog(LOG_ERR, "blocking_getnameinfo unable to queue response");
7962b15cb3dSCy Schubert 	return rc;
7972b15cb3dSCy Schubert }
7982b15cb3dSCy Schubert 
7992b15cb3dSCy Schubert 
8002b15cb3dSCy Schubert static void
8012b15cb3dSCy Schubert getnameinfo_sometime_complete(
8022b15cb3dSCy Schubert 	blocking_work_req	rtype,
8032b15cb3dSCy Schubert 	void *			context,
8042b15cb3dSCy Schubert 	size_t			respsize,
8052b15cb3dSCy Schubert 	void *			resp
8062b15cb3dSCy Schubert 	)
8072b15cb3dSCy Schubert {
8082b15cb3dSCy Schubert 	blocking_gni_req *	gni_req;
8092b15cb3dSCy Schubert 	blocking_gni_resp *	gni_resp;
8102b15cb3dSCy Schubert 	dnschild_ctx *		child_ctx;
8112b15cb3dSCy Schubert 	char *			host;
8122b15cb3dSCy Schubert 	char *			service;
8132b15cb3dSCy Schubert 	time_t			time_now;
8142b15cb3dSCy Schubert 	int			again;
8152b15cb3dSCy Schubert 
8162b15cb3dSCy Schubert 	gni_req = context;
8172b15cb3dSCy Schubert 	gni_resp = resp;
8182b15cb3dSCy Schubert 
8192b15cb3dSCy Schubert 	DEBUG_REQUIRE(BLOCKING_GETNAMEINFO == rtype);
8202b15cb3dSCy Schubert 	DEBUG_REQUIRE(respsize == gni_resp->octets);
8212b15cb3dSCy Schubert 
8222b15cb3dSCy Schubert 	child_ctx = dnschild_contexts[gni_req->dns_idx];
8232b15cb3dSCy Schubert 
8242b15cb3dSCy Schubert 	if (0 == gni_resp->retcode) {
8252b15cb3dSCy Schubert 		/*
8262b15cb3dSCy Schubert 		 * If this query succeeded only after retrying, DNS may have
8272b15cb3dSCy Schubert 		 * just become responsive.
8282b15cb3dSCy Schubert 		 */
8292b15cb3dSCy Schubert 		if (gni_resp->retry > INITIAL_DNS_RETRY) {
8302b15cb3dSCy Schubert 			time_now = time(NULL);
8312b15cb3dSCy Schubert 			child_ctx->next_dns_timeslot = time_now;
8322b15cb3dSCy Schubert 			TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n",
8332b15cb3dSCy Schubert 				  gni_req->dns_idx, humantime(time_now)));
8342b15cb3dSCy Schubert 		}
8352b15cb3dSCy Schubert 	} else {
8362b15cb3dSCy Schubert 		again = should_retry_dns(gni_resp->retcode, gni_resp->gni_errno);
8372b15cb3dSCy Schubert 		/*
8382b15cb3dSCy Schubert 		 * exponential backoff of DNS retries to 64s
8392b15cb3dSCy Schubert 		 */
8402b15cb3dSCy Schubert 		if (gni_req->retry > 0)
8412b15cb3dSCy Schubert 			manage_dns_retry_interval(&gni_req->scheduled,
8422b15cb3dSCy Schubert 			    &gni_req->earliest, &gni_req->retry,
843f391d6bcSXin LI 						  &child_ctx->next_dns_timeslot, FALSE);
8442b15cb3dSCy Schubert 
8452b15cb3dSCy Schubert 		if (gni_req->retry > 0 && again) {
8462b15cb3dSCy Schubert 			if (!queue_blocking_request(
8472b15cb3dSCy Schubert 				BLOCKING_GETNAMEINFO,
8482b15cb3dSCy Schubert 				gni_req,
8492b15cb3dSCy Schubert 				gni_req->octets,
8502b15cb3dSCy Schubert 				&getnameinfo_sometime_complete,
8512b15cb3dSCy Schubert 				gni_req))
8522b15cb3dSCy Schubert 				return;
8532b15cb3dSCy Schubert 
8542b15cb3dSCy Schubert 			msyslog(LOG_ERR, "unable to retry reverse lookup of %s", stoa(&gni_req->socku));
8552b15cb3dSCy Schubert 		}
8562b15cb3dSCy Schubert 	}
8572b15cb3dSCy Schubert 
8582b15cb3dSCy Schubert 	if (!gni_resp->hostoctets) {
8592b15cb3dSCy Schubert 		host = NULL;
8602b15cb3dSCy Schubert 		service = NULL;
8612b15cb3dSCy Schubert 	} else {
8622b15cb3dSCy Schubert 		host = (char *)gni_resp + sizeof(*gni_resp);
8632b15cb3dSCy Schubert 		service = (gni_resp->servoctets)
8642b15cb3dSCy Schubert 			      ? host + gni_resp->hostoctets
8652b15cb3dSCy Schubert 			      : NULL;
8662b15cb3dSCy Schubert 	}
8672b15cb3dSCy Schubert 
8682b15cb3dSCy Schubert 	(*gni_req->callback)(gni_resp->retcode, gni_resp->gni_errno,
8692b15cb3dSCy Schubert 			     &gni_req->socku, gni_req->flags, host,
8702b15cb3dSCy Schubert 			     service, gni_req->context);
8712b15cb3dSCy Schubert 
8722b15cb3dSCy Schubert 	free(gni_req);
8732b15cb3dSCy Schubert 	/* gni_resp is part of block freed by process_blocking_resp() */
8742b15cb3dSCy Schubert }
8752b15cb3dSCy Schubert 
8762b15cb3dSCy Schubert 
8772b15cb3dSCy Schubert #ifdef TEST_BLOCKING_WORKER
8782b15cb3dSCy Schubert void gni_test_callback(int rescode, int gni_errno, sockaddr_u *psau, int flags, const char *host, const char *service, void *context)
8792b15cb3dSCy Schubert {
8802b15cb3dSCy Schubert 	if (!rescode)
8812b15cb3dSCy Schubert 		TRACE(1, ("gni_test_callback got host '%s' serv '%s' for addr %s context %p\n",
8822b15cb3dSCy Schubert 			  host, service, stoa(psau), context));
8832b15cb3dSCy Schubert 	else
8842b15cb3dSCy Schubert 		TRACE(1, ("gni_test_callback context %p rescode %d gni_errno %d flags 0x%x addr %s\n",
8852b15cb3dSCy Schubert 			  context, rescode, gni_errno, flags, stoa(psau)));
8862b15cb3dSCy Schubert }
8872b15cb3dSCy Schubert #endif	/* TEST_BLOCKING_WORKER */
8882b15cb3dSCy Schubert 
8892b15cb3dSCy Schubert 
8902b15cb3dSCy Schubert #ifdef HAVE_RES_INIT
8912b15cb3dSCy Schubert static void
8922b15cb3dSCy Schubert reload_resolv_conf(
8932b15cb3dSCy Schubert 	dnsworker_ctx *	worker_ctx
8942b15cb3dSCy Schubert 	)
8952b15cb3dSCy Schubert {
8962b15cb3dSCy Schubert 	time_t	time_now;
8972b15cb3dSCy Schubert 
8982b15cb3dSCy Schubert 	/*
8992b15cb3dSCy Schubert 	 * This is ad-hoc.  Reload /etc/resolv.conf once per minute
9002b15cb3dSCy Schubert 	 * to pick up on changes from the DHCP client.  [Bug 1226]
9012b15cb3dSCy Schubert 	 * When using threads for the workers, this needs to happen
9022b15cb3dSCy Schubert 	 * only once per minute process-wide.
9032b15cb3dSCy Schubert 	 */
9042b15cb3dSCy Schubert 	time_now = time(NULL);
9052b15cb3dSCy Schubert # ifdef WORK_THREAD
9062b15cb3dSCy Schubert 	worker_ctx->next_res_init = next_res_init;
9072b15cb3dSCy Schubert # endif
9082b15cb3dSCy Schubert 	if (worker_ctx->next_res_init <= time_now) {
9092b15cb3dSCy Schubert 		if (worker_ctx->next_res_init != 0)
9102b15cb3dSCy Schubert 			res_init();
9112b15cb3dSCy Schubert 		worker_ctx->next_res_init = time_now + 60;
9122b15cb3dSCy Schubert # ifdef WORK_THREAD
9132b15cb3dSCy Schubert 		next_res_init = worker_ctx->next_res_init;
9142b15cb3dSCy Schubert # endif
9152b15cb3dSCy Schubert 	}
9162b15cb3dSCy Schubert }
9172b15cb3dSCy Schubert #endif	/* HAVE_RES_INIT */
9182b15cb3dSCy Schubert 
9192b15cb3dSCy Schubert 
9202b15cb3dSCy Schubert static u_int
9212b15cb3dSCy Schubert reserve_dnschild_ctx(void)
9222b15cb3dSCy Schubert {
9232b15cb3dSCy Schubert 	const size_t	ps = sizeof(dnschild_contexts[0]);
9242b15cb3dSCy Schubert 	const size_t	cs = sizeof(*dnschild_contexts[0]);
9252b15cb3dSCy Schubert 	u_int		c;
9262b15cb3dSCy Schubert 	u_int		new_alloc;
9272b15cb3dSCy Schubert 	size_t		octets;
9282b15cb3dSCy Schubert 	size_t		new_octets;
9292b15cb3dSCy Schubert 
9302b15cb3dSCy Schubert 	c = 0;
9312b15cb3dSCy Schubert 	while (TRUE) {
9322b15cb3dSCy Schubert 		for ( ; c < dnschild_contexts_alloc; c++) {
9332b15cb3dSCy Schubert 			if (NULL == dnschild_contexts[c]) {
9342b15cb3dSCy Schubert 				dnschild_contexts[c] = emalloc_zero(cs);
9352b15cb3dSCy Schubert 
9362b15cb3dSCy Schubert 				return c;
9372b15cb3dSCy Schubert 			}
9382b15cb3dSCy Schubert 		}
9392b15cb3dSCy Schubert 		new_alloc = dnschild_contexts_alloc + 20;
9402b15cb3dSCy Schubert 		new_octets = new_alloc * ps;
9412b15cb3dSCy Schubert 		octets = dnschild_contexts_alloc * ps;
9422b15cb3dSCy Schubert 		dnschild_contexts = erealloc_zero(dnschild_contexts,
9432b15cb3dSCy Schubert 						  new_octets, octets);
9442b15cb3dSCy Schubert 		dnschild_contexts_alloc = new_alloc;
9452b15cb3dSCy Schubert 	}
9462b15cb3dSCy Schubert }
9472b15cb3dSCy Schubert 
9482b15cb3dSCy Schubert 
9492b15cb3dSCy Schubert static u_int
9502b15cb3dSCy Schubert get_dnschild_ctx(void)
9512b15cb3dSCy Schubert {
9522b15cb3dSCy Schubert 	static u_int	shared_ctx = UINT_MAX;
9532b15cb3dSCy Schubert 
9542b15cb3dSCy Schubert 	if (worker_per_query)
9552b15cb3dSCy Schubert 		return reserve_dnschild_ctx();
9562b15cb3dSCy Schubert 
9572b15cb3dSCy Schubert 	if (UINT_MAX == shared_ctx)
9582b15cb3dSCy Schubert 		shared_ctx = reserve_dnschild_ctx();
9592b15cb3dSCy Schubert 
9602b15cb3dSCy Schubert 	return shared_ctx;
9612b15cb3dSCy Schubert }
9622b15cb3dSCy Schubert 
9632b15cb3dSCy Schubert 
9642b15cb3dSCy Schubert static dnsworker_ctx *
9652b15cb3dSCy Schubert get_worker_context(
9662b15cb3dSCy Schubert 	blocking_child *	c,
9672b15cb3dSCy Schubert 	u_int			idx
9682b15cb3dSCy Schubert 	)
9692b15cb3dSCy Schubert {
9702b15cb3dSCy Schubert 	u_int		min_new_alloc;
9712b15cb3dSCy Schubert 	u_int		new_alloc;
9722b15cb3dSCy Schubert 	size_t		octets;
9732b15cb3dSCy Schubert 	size_t		new_octets;
9744990d495SXin LI 	dnsworker_ctx *	retv;
9754990d495SXin LI 
9764990d495SXin LI 	worker_global_lock(TRUE);
9772b15cb3dSCy Schubert 
9782b15cb3dSCy Schubert 	if (dnsworker_contexts_alloc <= idx) {
9792b15cb3dSCy Schubert 		min_new_alloc = 1 + idx;
9802b15cb3dSCy Schubert 		/* round new_alloc up to nearest multiple of 4 */
9812b15cb3dSCy Schubert 		new_alloc = (min_new_alloc + 4) & ~(4 - 1);
9824990d495SXin LI 		new_octets = new_alloc * sizeof(dnsworker_ctx*);
9834990d495SXin LI 		octets = dnsworker_contexts_alloc * sizeof(dnsworker_ctx*);
9842b15cb3dSCy Schubert 		dnsworker_contexts = erealloc_zero(dnsworker_contexts,
9852b15cb3dSCy Schubert 						   new_octets, octets);
9862b15cb3dSCy Schubert 		dnsworker_contexts_alloc = new_alloc;
9874990d495SXin LI 		retv = emalloc_zero(sizeof(dnsworker_ctx));
9884990d495SXin LI 		dnsworker_contexts[idx] = retv;
9894990d495SXin LI 	} else if (NULL == (retv = dnsworker_contexts[idx])) {
9904990d495SXin LI 		retv = emalloc_zero(sizeof(dnsworker_ctx));
9914990d495SXin LI 		dnsworker_contexts[idx] = retv;
9922b15cb3dSCy Schubert 	}
9932b15cb3dSCy Schubert 
9944990d495SXin LI 	worker_global_lock(FALSE);
9952b15cb3dSCy Schubert 
9964990d495SXin LI 	ZERO(*retv);
9974990d495SXin LI 	retv->c = c;
9984990d495SXin LI 	return retv;
9992b15cb3dSCy Schubert }
10002b15cb3dSCy Schubert 
10012b15cb3dSCy Schubert 
10022b15cb3dSCy Schubert static void
10032b15cb3dSCy Schubert scheduled_sleep(
10042b15cb3dSCy Schubert 	time_t		scheduled,
10052b15cb3dSCy Schubert 	time_t		earliest,
10062b15cb3dSCy Schubert 	dnsworker_ctx *	worker_ctx
10072b15cb3dSCy Schubert 	)
10082b15cb3dSCy Schubert {
10092b15cb3dSCy Schubert 	time_t now;
10102b15cb3dSCy Schubert 
10112b15cb3dSCy Schubert 	if (scheduled < worker_ctx->ignore_scheduled_before) {
10122b15cb3dSCy Schubert 		TRACE(1, ("ignoring sleep until %s scheduled at %s (before %s)\n",
10132b15cb3dSCy Schubert 			  humantime(earliest), humantime(scheduled),
10142b15cb3dSCy Schubert 			  humantime(worker_ctx->ignore_scheduled_before)));
10152b15cb3dSCy Schubert 		return;
10162b15cb3dSCy Schubert 	}
10172b15cb3dSCy Schubert 
10182b15cb3dSCy Schubert 	now = time(NULL);
10192b15cb3dSCy Schubert 
10202b15cb3dSCy Schubert 	if (now < earliest) {
10212b15cb3dSCy Schubert 		TRACE(1, ("sleep until %s scheduled at %s (>= %s)\n",
10222b15cb3dSCy Schubert 			  humantime(earliest), humantime(scheduled),
10232b15cb3dSCy Schubert 			  humantime(worker_ctx->ignore_scheduled_before)));
10242b15cb3dSCy Schubert 		if (-1 == worker_sleep(worker_ctx->c, earliest - now)) {
10252b15cb3dSCy Schubert 			/* our sleep was interrupted */
10262b15cb3dSCy Schubert 			now = time(NULL);
10272b15cb3dSCy Schubert 			worker_ctx->ignore_scheduled_before = now;
10282b15cb3dSCy Schubert #ifdef HAVE_RES_INIT
10292b15cb3dSCy Schubert 			worker_ctx->next_res_init = now + 60;
10302b15cb3dSCy Schubert 			next_res_init = worker_ctx->next_res_init;
10312b15cb3dSCy Schubert 			res_init();
10322b15cb3dSCy Schubert #endif
10332b15cb3dSCy Schubert 			TRACE(1, ("sleep interrupted by daemon, ignoring sleeps scheduled before now (%s)\n",
10342b15cb3dSCy Schubert 				  humantime(worker_ctx->ignore_scheduled_before)));
10352b15cb3dSCy Schubert 		}
10362b15cb3dSCy Schubert 	}
10372b15cb3dSCy Schubert }
10382b15cb3dSCy Schubert 
10392b15cb3dSCy Schubert 
10402b15cb3dSCy Schubert /*
10412b15cb3dSCy Schubert  * manage_dns_retry_interval is a helper used by
10422b15cb3dSCy Schubert  * getaddrinfo_sometime_complete and getnameinfo_sometime_complete
10432b15cb3dSCy Schubert  * to calculate the new retry interval and schedule the next query.
10442b15cb3dSCy Schubert  */
10452b15cb3dSCy Schubert static void
10462b15cb3dSCy Schubert manage_dns_retry_interval(
10472b15cb3dSCy Schubert 	time_t *	pscheduled,
10482b15cb3dSCy Schubert 	time_t *	pwhen,
10492b15cb3dSCy Schubert 	int *		pretry,
1050f391d6bcSXin LI 	time_t *	pnext_timeslot,
1051f391d6bcSXin LI 	int		forever
10522b15cb3dSCy Schubert 	)
10532b15cb3dSCy Schubert {
10542b15cb3dSCy Schubert 	time_t	now;
10552b15cb3dSCy Schubert 	time_t	when;
10562b15cb3dSCy Schubert 	int	retry;
1057f391d6bcSXin LI 	int	retmax;
10582b15cb3dSCy Schubert 
10592b15cb3dSCy Schubert 	now = time(NULL);
10602b15cb3dSCy Schubert 	retry = *pretry;
10612b15cb3dSCy Schubert 	when = max(now + retry, *pnext_timeslot);
10622b15cb3dSCy Schubert 	*pnext_timeslot = when;
1063f391d6bcSXin LI 
1064f391d6bcSXin LI 	/* this exponential backoff is slower than doubling up: The
1065f391d6bcSXin LI 	 * sequence goes 2-3-4-6-8-12-16-24-32... and the upper limit is
1066f391d6bcSXin LI 	 * 64 seconds for things that should not repeat forever, and
1067f391d6bcSXin LI 	 * 1024 when repeated forever.
1068f391d6bcSXin LI 	 */
1069f391d6bcSXin LI 	retmax = forever ? 1024 : 64;
1070f391d6bcSXin LI 	retry <<= 1;
1071f391d6bcSXin LI 	if (retry & (retry - 1))
1072f391d6bcSXin LI 		retry &= (retry - 1);
1073f391d6bcSXin LI 	else
1074f391d6bcSXin LI 		retry -= (retry >> 2);
1075f391d6bcSXin LI 	retry = min(retmax, retry);
10762b15cb3dSCy Schubert 
10772b15cb3dSCy Schubert 	*pscheduled = now;
10782b15cb3dSCy Schubert 	*pwhen = when;
10792b15cb3dSCy Schubert 	*pretry = retry;
10802b15cb3dSCy Schubert }
10812b15cb3dSCy Schubert 
10822b15cb3dSCy Schubert /*
10832b15cb3dSCy Schubert  * should_retry_dns is a helper used by getaddrinfo_sometime_complete
10842b15cb3dSCy Schubert  * and getnameinfo_sometime_complete which implements ntpd's DNS retry
10852b15cb3dSCy Schubert  * policy.
10862b15cb3dSCy Schubert  */
10872b15cb3dSCy Schubert static int
10882b15cb3dSCy Schubert should_retry_dns(
10892b15cb3dSCy Schubert 	int	rescode,
10902b15cb3dSCy Schubert 	int	res_errno
10912b15cb3dSCy Schubert 	)
10922b15cb3dSCy Schubert {
10932b15cb3dSCy Schubert 	static int	eai_again_seen;
10942b15cb3dSCy Schubert 	int		again;
10952b15cb3dSCy Schubert #if defined (EAI_SYSTEM) && defined(DEBUG)
10962b15cb3dSCy Schubert 	char		msg[256];
10972b15cb3dSCy Schubert #endif
10982b15cb3dSCy Schubert 
10992b15cb3dSCy Schubert 	/*
11002b15cb3dSCy Schubert 	 * If the resolver failed, see if the failure is
11012b15cb3dSCy Schubert 	 * temporary. If so, return success.
11022b15cb3dSCy Schubert 	 */
11032b15cb3dSCy Schubert 	again = 0;
11042b15cb3dSCy Schubert 
11052b15cb3dSCy Schubert 	switch (rescode) {
11062b15cb3dSCy Schubert 
11072b15cb3dSCy Schubert 	case EAI_FAIL:
11082b15cb3dSCy Schubert 		again = 1;
11092b15cb3dSCy Schubert 		break;
11102b15cb3dSCy Schubert 
11112b15cb3dSCy Schubert 	case EAI_AGAIN:
11122b15cb3dSCy Schubert 		again = 1;
11132b15cb3dSCy Schubert 		eai_again_seen = 1;		/* [Bug 1178] */
11142b15cb3dSCy Schubert 		break;
11152b15cb3dSCy Schubert 
11162b15cb3dSCy Schubert 	case EAI_NONAME:
11172b15cb3dSCy Schubert #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
11182b15cb3dSCy Schubert 	case EAI_NODATA:
11192b15cb3dSCy Schubert #endif
11202b15cb3dSCy Schubert 		again = !eai_again_seen;	/* [Bug 1178] */
11212b15cb3dSCy Schubert 		break;
11222b15cb3dSCy Schubert 
11232b15cb3dSCy Schubert #ifdef EAI_SYSTEM
11242b15cb3dSCy Schubert 	case EAI_SYSTEM:
11252b15cb3dSCy Schubert 		/*
11262b15cb3dSCy Schubert 		 * EAI_SYSTEM means the real error is in errno.  We should be more
11272b15cb3dSCy Schubert 		 * discriminating about which errno values require retrying, but
11282b15cb3dSCy Schubert 		 * this matches existing behavior.
11292b15cb3dSCy Schubert 		 */
11302b15cb3dSCy Schubert 		again = 1;
11312b15cb3dSCy Schubert # ifdef DEBUG
11322b15cb3dSCy Schubert 		errno_to_str(res_errno, msg, sizeof(msg));
11332b15cb3dSCy Schubert 		TRACE(1, ("intres: EAI_SYSTEM errno %d (%s) means try again, right?\n",
11342b15cb3dSCy Schubert 			  res_errno, msg));
11352b15cb3dSCy Schubert # endif
11362b15cb3dSCy Schubert 		break;
11372b15cb3dSCy Schubert #endif
11382b15cb3dSCy Schubert 	}
11392b15cb3dSCy Schubert 
11402b15cb3dSCy Schubert 	TRACE(2, ("intres: resolver returned: %s (%d), %sretrying\n",
11412b15cb3dSCy Schubert 		  gai_strerror(rescode), rescode, again ? "" : "not "));
11422b15cb3dSCy Schubert 
11432b15cb3dSCy Schubert 	return again;
11442b15cb3dSCy Schubert }
11452b15cb3dSCy Schubert 
11462b15cb3dSCy Schubert #else	/* !WORKER follows */
11472b15cb3dSCy Schubert int ntp_intres_nonempty_compilation_unit;
11482b15cb3dSCy Schubert #endif
1149