xref: /freebsd/contrib/ntp/libntp/ntp_intres.c (revision 4990d495fcc77c51b3f46c91ba3a064b565afae0)
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 # ifdef HAVE_INT32_ONLY_WITH_DNS
882b15cb3dSCy Schubert #  define HAVE_INT32
892b15cb3dSCy Schubert # endif
902b15cb3dSCy Schubert # ifdef HAVE_U_INT32_ONLY_WITH_DNS
912b15cb3dSCy Schubert #  define HAVE_U_INT32
922b15cb3dSCy Schubert # endif
932b15cb3dSCy Schubert #endif
942b15cb3dSCy Schubert 
952b15cb3dSCy Schubert #include "ntp.h"
962b15cb3dSCy Schubert #include "ntp_debug.h"
972b15cb3dSCy Schubert #include "ntp_malloc.h"
982b15cb3dSCy Schubert #include "ntp_syslog.h"
992b15cb3dSCy Schubert #include "ntp_unixtime.h"
1002b15cb3dSCy Schubert #include "ntp_intres.h"
1012b15cb3dSCy Schubert #include "intreswork.h"
1022b15cb3dSCy Schubert 
1032b15cb3dSCy Schubert 
1042b15cb3dSCy Schubert /*
1052b15cb3dSCy Schubert  * Following are implementations of getaddrinfo_sometime() and
1062b15cb3dSCy Schubert  * getnameinfo_sometime().  Each is implemented in three routines:
1072b15cb3dSCy Schubert  *
1082b15cb3dSCy Schubert  * getaddrinfo_sometime()		getnameinfo_sometime()
1092b15cb3dSCy Schubert  * blocking_getaddrinfo()		blocking_getnameinfo()
1102b15cb3dSCy Schubert  * getaddrinfo_sometime_complete()	getnameinfo_sometime_complete()
1112b15cb3dSCy Schubert  *
1122b15cb3dSCy Schubert  * The first runs in the parent and marshalls (or serializes) request
1132b15cb3dSCy Schubert  * parameters into a request blob which is processed in the child by
1142b15cb3dSCy Schubert  * the second routine, blocking_*(), which serializes the results into
1152b15cb3dSCy Schubert  * a response blob unpacked by the third routine, *_complete(), which
1162b15cb3dSCy Schubert  * calls the callback routine provided with the request and frees
1172b15cb3dSCy Schubert  * _request_ memory allocated by the first routine.  Response memory
1182b15cb3dSCy Schubert  * is managed by the code which calls the *_complete routines.
1192b15cb3dSCy Schubert  */
1202b15cb3dSCy Schubert 
1212b15cb3dSCy Schubert /* === typedefs === */
1222b15cb3dSCy Schubert typedef struct blocking_gai_req_tag {	/* marshalled args */
1232b15cb3dSCy Schubert 	size_t			octets;
1242b15cb3dSCy Schubert 	u_int			dns_idx;
1252b15cb3dSCy Schubert 	time_t			scheduled;
1262b15cb3dSCy Schubert 	time_t			earliest;
1272b15cb3dSCy Schubert 	struct addrinfo		hints;
1282b15cb3dSCy Schubert 	int			retry;
1292b15cb3dSCy Schubert 	gai_sometime_callback	callback;
1302b15cb3dSCy Schubert 	void *			context;
1312b15cb3dSCy Schubert 	size_t			nodesize;
1322b15cb3dSCy Schubert 	size_t			servsize;
1332b15cb3dSCy Schubert } blocking_gai_req;
1342b15cb3dSCy Schubert 
1352b15cb3dSCy Schubert typedef struct blocking_gai_resp_tag {
1362b15cb3dSCy Schubert 	size_t			octets;
1372b15cb3dSCy Schubert 	int			retcode;
1382b15cb3dSCy Schubert 	int			retry;
1392b15cb3dSCy Schubert 	int			gai_errno; /* for EAI_SYSTEM case */
1402b15cb3dSCy Schubert 	int			ai_count;
1412b15cb3dSCy Schubert 	/*
1422b15cb3dSCy Schubert 	 * Followed by ai_count struct addrinfo and then ai_count
1432b15cb3dSCy Schubert 	 * sockaddr_u and finally the canonical name strings.
1442b15cb3dSCy Schubert 	 */
1452b15cb3dSCy Schubert } blocking_gai_resp;
1462b15cb3dSCy Schubert 
1472b15cb3dSCy Schubert typedef struct blocking_gni_req_tag {
1482b15cb3dSCy Schubert 	size_t			octets;
1492b15cb3dSCy Schubert 	u_int			dns_idx;
1502b15cb3dSCy Schubert 	time_t			scheduled;
1512b15cb3dSCy Schubert 	time_t			earliest;
1522b15cb3dSCy Schubert 	int			retry;
1532b15cb3dSCy Schubert 	size_t			hostoctets;
1542b15cb3dSCy Schubert 	size_t			servoctets;
1552b15cb3dSCy Schubert 	int			flags;
1562b15cb3dSCy Schubert 	gni_sometime_callback	callback;
1572b15cb3dSCy Schubert 	void *			context;
1582b15cb3dSCy Schubert 	sockaddr_u		socku;
1592b15cb3dSCy Schubert } blocking_gni_req;
1602b15cb3dSCy Schubert 
1612b15cb3dSCy Schubert typedef struct blocking_gni_resp_tag {
1622b15cb3dSCy Schubert 	size_t			octets;
1632b15cb3dSCy Schubert 	int			retcode;
1642b15cb3dSCy Schubert 	int			gni_errno; /* for EAI_SYSTEM case */
1652b15cb3dSCy Schubert 	int			retry;
1662b15cb3dSCy Schubert 	size_t			hostoctets;
1672b15cb3dSCy Schubert 	size_t			servoctets;
1682b15cb3dSCy Schubert 	/*
1692b15cb3dSCy Schubert 	 * Followed by hostoctets bytes of null-terminated host,
1702b15cb3dSCy Schubert 	 * then servoctets bytes of null-terminated service.
1712b15cb3dSCy Schubert 	 */
1722b15cb3dSCy Schubert } blocking_gni_resp;
1732b15cb3dSCy Schubert 
1742b15cb3dSCy Schubert /* per-DNS-worker state in parent */
1752b15cb3dSCy Schubert typedef struct dnschild_ctx_tag {
1762b15cb3dSCy Schubert 	u_int	index;
1772b15cb3dSCy Schubert 	time_t	next_dns_timeslot;
1782b15cb3dSCy Schubert } dnschild_ctx;
1792b15cb3dSCy Schubert 
1802b15cb3dSCy Schubert /* per-DNS-worker state in worker */
1812b15cb3dSCy Schubert typedef struct dnsworker_ctx_tag {
1822b15cb3dSCy Schubert 	blocking_child *	c;
1832b15cb3dSCy Schubert 	time_t			ignore_scheduled_before;
1842b15cb3dSCy Schubert #ifdef HAVE_RES_INIT
1852b15cb3dSCy Schubert 	time_t	next_res_init;
1862b15cb3dSCy Schubert #endif
1872b15cb3dSCy Schubert } dnsworker_ctx;
1882b15cb3dSCy Schubert 
1892b15cb3dSCy Schubert 
1902b15cb3dSCy Schubert /* === variables === */
1912b15cb3dSCy Schubert dnschild_ctx **		dnschild_contexts;		/* parent */
1922b15cb3dSCy Schubert u_int			dnschild_contexts_alloc;
1932b15cb3dSCy Schubert dnsworker_ctx **	dnsworker_contexts;		/* child */
1942b15cb3dSCy Schubert u_int			dnsworker_contexts_alloc;
1952b15cb3dSCy Schubert 
1962b15cb3dSCy Schubert #ifdef HAVE_RES_INIT
1972b15cb3dSCy Schubert static	time_t		next_res_init;
1982b15cb3dSCy Schubert #endif
1992b15cb3dSCy Schubert 
2002b15cb3dSCy Schubert 
2012b15cb3dSCy Schubert /* === forward declarations === */
2022b15cb3dSCy Schubert static	u_int		reserve_dnschild_ctx(void);
2032b15cb3dSCy Schubert static	u_int		get_dnschild_ctx(void);
2042b15cb3dSCy Schubert static	dnsworker_ctx *	get_worker_context(blocking_child *, u_int);
2052b15cb3dSCy Schubert static	void		scheduled_sleep(time_t, time_t,
2062b15cb3dSCy Schubert 					dnsworker_ctx *);
2072b15cb3dSCy Schubert static	void		manage_dns_retry_interval(time_t *, time_t *,
2082b15cb3dSCy Schubert 						  int *,
2092b15cb3dSCy Schubert 						  time_t *);
2102b15cb3dSCy Schubert static	int		should_retry_dns(int, int);
2112b15cb3dSCy Schubert #ifdef HAVE_RES_INIT
2122b15cb3dSCy Schubert static	void		reload_resolv_conf(dnsworker_ctx *);
2132b15cb3dSCy Schubert #else
2142b15cb3dSCy Schubert # define		reload_resolv_conf(wc)		\
2152b15cb3dSCy Schubert 	do {						\
2162b15cb3dSCy Schubert 		(void)(wc);				\
2172b15cb3dSCy Schubert 	} while (FALSE)
2182b15cb3dSCy Schubert #endif
2192b15cb3dSCy Schubert static	void		getaddrinfo_sometime_complete(blocking_work_req,
2202b15cb3dSCy Schubert 						      void *, size_t,
2212b15cb3dSCy Schubert 						      void *);
2222b15cb3dSCy Schubert static	void		getnameinfo_sometime_complete(blocking_work_req,
2232b15cb3dSCy Schubert 						      void *, size_t,
2242b15cb3dSCy Schubert 						      void *);
2252b15cb3dSCy Schubert 
2262b15cb3dSCy Schubert 
2272b15cb3dSCy Schubert /* === functions === */
2282b15cb3dSCy Schubert /*
2292b15cb3dSCy Schubert  * getaddrinfo_sometime - uses blocking child to call getaddrinfo then
2302b15cb3dSCy Schubert  *			  invokes provided callback completion function.
2312b15cb3dSCy Schubert  */
2322b15cb3dSCy Schubert int
2332b15cb3dSCy Schubert getaddrinfo_sometime(
2342b15cb3dSCy Schubert 	const char *		node,
2352b15cb3dSCy Schubert 	const char *		service,
2362b15cb3dSCy Schubert 	const struct addrinfo *	hints,
2372b15cb3dSCy Schubert 	int			retry,
2382b15cb3dSCy Schubert 	gai_sometime_callback	callback,
2392b15cb3dSCy Schubert 	void *			context
2402b15cb3dSCy Schubert 	)
2412b15cb3dSCy Schubert {
2422b15cb3dSCy Schubert 	blocking_gai_req *	gai_req;
2432b15cb3dSCy Schubert 	u_int			idx;
2442b15cb3dSCy Schubert 	dnschild_ctx *		child_ctx;
2452b15cb3dSCy Schubert 	size_t			req_size;
2462b15cb3dSCy Schubert 	size_t			nodesize;
2472b15cb3dSCy Schubert 	size_t			servsize;
2482b15cb3dSCy Schubert 	time_t			now;
2492b15cb3dSCy Schubert 
2509034852cSGleb Smirnoff 	REQUIRE(NULL != node);
2512b15cb3dSCy Schubert 	if (NULL != hints) {
2529034852cSGleb Smirnoff 		REQUIRE(0 == hints->ai_addrlen);
2539034852cSGleb Smirnoff 		REQUIRE(NULL == hints->ai_addr);
2549034852cSGleb Smirnoff 		REQUIRE(NULL == hints->ai_canonname);
2559034852cSGleb Smirnoff 		REQUIRE(NULL == hints->ai_next);
2562b15cb3dSCy Schubert 	}
2572b15cb3dSCy Schubert 
2582b15cb3dSCy Schubert 	idx = get_dnschild_ctx();
2592b15cb3dSCy Schubert 	child_ctx = dnschild_contexts[idx];
2602b15cb3dSCy Schubert 
2612b15cb3dSCy Schubert 	nodesize = strlen(node) + 1;
2622b15cb3dSCy Schubert 	servsize = strlen(service) + 1;
2632b15cb3dSCy Schubert 	req_size = sizeof(*gai_req) + nodesize + servsize;
2642b15cb3dSCy Schubert 
2652b15cb3dSCy Schubert 	gai_req = emalloc_zero(req_size);
2662b15cb3dSCy Schubert 
2672b15cb3dSCy Schubert 	gai_req->octets = req_size;
2682b15cb3dSCy Schubert 	gai_req->dns_idx = idx;
2692b15cb3dSCy Schubert 	now = time(NULL);
2702b15cb3dSCy Schubert 	gai_req->scheduled = now;
2712b15cb3dSCy Schubert 	gai_req->earliest = max(now, child_ctx->next_dns_timeslot);
2722b15cb3dSCy Schubert 	child_ctx->next_dns_timeslot = gai_req->earliest;
2732b15cb3dSCy Schubert 	if (hints != NULL)
2742b15cb3dSCy Schubert 		gai_req->hints = *hints;
2752b15cb3dSCy Schubert 	gai_req->retry = retry;
2762b15cb3dSCy Schubert 	gai_req->callback = callback;
2772b15cb3dSCy Schubert 	gai_req->context = context;
2782b15cb3dSCy Schubert 	gai_req->nodesize = nodesize;
2792b15cb3dSCy Schubert 	gai_req->servsize = servsize;
2802b15cb3dSCy Schubert 
2812b15cb3dSCy Schubert 	memcpy((char *)gai_req + sizeof(*gai_req), node, nodesize);
2822b15cb3dSCy Schubert 	memcpy((char *)gai_req + sizeof(*gai_req) + nodesize, service,
2832b15cb3dSCy Schubert 	       servsize);
2842b15cb3dSCy Schubert 
2852b15cb3dSCy Schubert 	if (queue_blocking_request(
2862b15cb3dSCy Schubert 		BLOCKING_GETADDRINFO,
2872b15cb3dSCy Schubert 		gai_req,
2882b15cb3dSCy Schubert 		req_size,
2892b15cb3dSCy Schubert 		&getaddrinfo_sometime_complete,
2902b15cb3dSCy Schubert 		gai_req)) {
2912b15cb3dSCy Schubert 
2922b15cb3dSCy Schubert 		msyslog(LOG_ERR, "unable to queue getaddrinfo request");
2932b15cb3dSCy Schubert 		errno = EFAULT;
2942b15cb3dSCy Schubert 		return -1;
2952b15cb3dSCy Schubert 	}
2962b15cb3dSCy Schubert 
2972b15cb3dSCy Schubert 	return 0;
2982b15cb3dSCy Schubert }
2992b15cb3dSCy Schubert 
3002b15cb3dSCy Schubert int
3012b15cb3dSCy Schubert blocking_getaddrinfo(
3022b15cb3dSCy Schubert 	blocking_child *	c,
3032b15cb3dSCy Schubert 	blocking_pipe_header *	req
3042b15cb3dSCy Schubert 	)
3052b15cb3dSCy Schubert {
3062b15cb3dSCy Schubert 	blocking_gai_req *	gai_req;
3072b15cb3dSCy Schubert 	dnsworker_ctx *		worker_ctx;
3082b15cb3dSCy Schubert 	blocking_pipe_header *	resp;
3092b15cb3dSCy Schubert 	blocking_gai_resp *	gai_resp;
3102b15cb3dSCy Schubert 	char *			node;
3112b15cb3dSCy Schubert 	char *			service;
3122b15cb3dSCy Schubert 	struct addrinfo *	ai_res;
3132b15cb3dSCy Schubert 	struct addrinfo *	ai;
3142b15cb3dSCy Schubert 	struct addrinfo *	serialized_ai;
3152b15cb3dSCy Schubert 	size_t			canons_octets;
3162b15cb3dSCy Schubert 	size_t			this_octets;
3172b15cb3dSCy Schubert 	size_t			resp_octets;
3182b15cb3dSCy Schubert 	char *			cp;
3192b15cb3dSCy Schubert 	time_t			time_now;
3202b15cb3dSCy Schubert 
3212b15cb3dSCy Schubert 	gai_req = (void *)((char *)req + sizeof(*req));
3222b15cb3dSCy Schubert 	node = (char *)gai_req + sizeof(*gai_req);
3232b15cb3dSCy Schubert 	service = node + gai_req->nodesize;
3242b15cb3dSCy Schubert 
3252b15cb3dSCy Schubert 	worker_ctx = get_worker_context(c, gai_req->dns_idx);
3262b15cb3dSCy Schubert 	scheduled_sleep(gai_req->scheduled, gai_req->earliest,
3272b15cb3dSCy Schubert 			worker_ctx);
3282b15cb3dSCy Schubert 	reload_resolv_conf(worker_ctx);
3292b15cb3dSCy Schubert 
3302b15cb3dSCy Schubert 	/*
3312b15cb3dSCy Schubert 	 * Take a shot at the final size, better to overestimate
3322b15cb3dSCy Schubert 	 * at first and then realloc to a smaller size.
3332b15cb3dSCy Schubert 	 */
3342b15cb3dSCy Schubert 
3352b15cb3dSCy Schubert 	resp_octets = sizeof(*resp) + sizeof(*gai_resp) +
3362b15cb3dSCy Schubert 		      16 * (sizeof(struct addrinfo) +
3372b15cb3dSCy Schubert 			    sizeof(sockaddr_u)) +
3382b15cb3dSCy Schubert 		      256;
3392b15cb3dSCy Schubert 	resp = emalloc_zero(resp_octets);
3402b15cb3dSCy Schubert 	gai_resp = (void *)(resp + 1);
3412b15cb3dSCy Schubert 
3422b15cb3dSCy Schubert 	TRACE(2, ("blocking_getaddrinfo given node %s serv %s fam %d flags %x\n",
3432b15cb3dSCy Schubert 		  node, service, gai_req->hints.ai_family,
3442b15cb3dSCy Schubert 		  gai_req->hints.ai_flags));
3452b15cb3dSCy Schubert #ifdef DEBUG
3462b15cb3dSCy Schubert 	if (debug >= 2)
3472b15cb3dSCy Schubert 		fflush(stdout);
3482b15cb3dSCy Schubert #endif
3492b15cb3dSCy Schubert 	ai_res = NULL;
3502b15cb3dSCy Schubert 	gai_resp->retcode = getaddrinfo(node, service, &gai_req->hints,
3512b15cb3dSCy Schubert 					&ai_res);
3522b15cb3dSCy Schubert 	gai_resp->retry = gai_req->retry;
3532b15cb3dSCy Schubert #ifdef EAI_SYSTEM
3542b15cb3dSCy Schubert 	if (EAI_SYSTEM == gai_resp->retcode)
3552b15cb3dSCy Schubert 		gai_resp->gai_errno = errno;
3562b15cb3dSCy Schubert #endif
3572b15cb3dSCy Schubert 	canons_octets = 0;
3582b15cb3dSCy Schubert 
3592b15cb3dSCy Schubert 	if (0 == gai_resp->retcode) {
3602b15cb3dSCy Schubert 		ai = ai_res;
3612b15cb3dSCy Schubert 		while (NULL != ai) {
3622b15cb3dSCy Schubert 			gai_resp->ai_count++;
3632b15cb3dSCy Schubert 			if (ai->ai_canonname)
3642b15cb3dSCy Schubert 				canons_octets += strlen(ai->ai_canonname) + 1;
3652b15cb3dSCy Schubert 			ai = ai->ai_next;
3662b15cb3dSCy Schubert 		}
3672b15cb3dSCy Schubert 		/*
3682b15cb3dSCy Schubert 		 * If this query succeeded only after retrying, DNS may have
3692b15cb3dSCy Schubert 		 * just become responsive.  Ignore previously-scheduled
3702b15cb3dSCy Schubert 		 * retry sleeps once for each pending request, similar to
3712b15cb3dSCy Schubert 		 * the way scheduled_sleep() does when its worker_sleep()
3722b15cb3dSCy Schubert 		 * is interrupted.
3732b15cb3dSCy Schubert 		 */
3742b15cb3dSCy Schubert 		if (gai_resp->retry > INITIAL_DNS_RETRY) {
3752b15cb3dSCy Schubert 			time_now = time(NULL);
3762b15cb3dSCy Schubert 			worker_ctx->ignore_scheduled_before = time_now;
3772b15cb3dSCy Schubert 			TRACE(1, ("DNS success after retry, ignoring sleeps scheduled before now (%s)\n",
3782b15cb3dSCy Schubert 				  humantime(time_now)));
3792b15cb3dSCy Schubert 		}
3802b15cb3dSCy Schubert 	}
3812b15cb3dSCy Schubert 
3822b15cb3dSCy Schubert 	/*
3832b15cb3dSCy Schubert 	 * Our response consists of a header, followed by ai_count
3842b15cb3dSCy Schubert 	 * addrinfo structs followed by ai_count sockaddr_storage
3852b15cb3dSCy Schubert 	 * structs followed by the canonical names.
3862b15cb3dSCy Schubert 	 */
3872b15cb3dSCy Schubert 	gai_resp->octets = sizeof(*gai_resp)
3882b15cb3dSCy Schubert 			    + gai_resp->ai_count
3892b15cb3dSCy Schubert 				* (sizeof(gai_req->hints)
3902b15cb3dSCy Schubert 				   + sizeof(sockaddr_u))
3912b15cb3dSCy Schubert 			    + canons_octets;
3922b15cb3dSCy Schubert 
3932b15cb3dSCy Schubert 	resp_octets = sizeof(*resp) + gai_resp->octets;
3942b15cb3dSCy Schubert 	resp = erealloc(resp, resp_octets);
3952b15cb3dSCy Schubert 	gai_resp = (void *)(resp + 1);
3962b15cb3dSCy Schubert 
3972b15cb3dSCy Schubert 	/* cp serves as our current pointer while serializing */
3982b15cb3dSCy Schubert 	cp = (void *)(gai_resp + 1);
3992b15cb3dSCy Schubert 	canons_octets = 0;
4002b15cb3dSCy Schubert 
4012b15cb3dSCy Schubert 	if (0 == gai_resp->retcode) {
4022b15cb3dSCy Schubert 		ai = ai_res;
4032b15cb3dSCy Schubert 		while (NULL != ai) {
4042b15cb3dSCy Schubert 			memcpy(cp, ai, sizeof(*ai));
4052b15cb3dSCy Schubert 			serialized_ai = (void *)cp;
4062b15cb3dSCy Schubert 			cp += sizeof(*ai);
4072b15cb3dSCy Schubert 
4082b15cb3dSCy Schubert 			/* transform ai_canonname into offset */
4092b15cb3dSCy Schubert 			if (NULL != serialized_ai->ai_canonname) {
4102b15cb3dSCy Schubert 				serialized_ai->ai_canonname = (char *)canons_octets;
4112b15cb3dSCy Schubert 				canons_octets += strlen(ai->ai_canonname) + 1;
4122b15cb3dSCy Schubert 			}
4132b15cb3dSCy Schubert 
4142b15cb3dSCy Schubert 			/* leave fixup of ai_addr pointer for receiver */
4152b15cb3dSCy Schubert 
4162b15cb3dSCy Schubert 			ai = ai->ai_next;
4172b15cb3dSCy Schubert 		}
4182b15cb3dSCy Schubert 
4192b15cb3dSCy Schubert 		ai = ai_res;
4202b15cb3dSCy Schubert 		while (NULL != ai) {
4219034852cSGleb Smirnoff 			INSIST(ai->ai_addrlen <= sizeof(sockaddr_u));
4222b15cb3dSCy Schubert 			memcpy(cp, ai->ai_addr, ai->ai_addrlen);
4232b15cb3dSCy Schubert 			cp += sizeof(sockaddr_u);
4242b15cb3dSCy Schubert 
4252b15cb3dSCy Schubert 			ai = ai->ai_next;
4262b15cb3dSCy Schubert 		}
4272b15cb3dSCy Schubert 
4282b15cb3dSCy Schubert 		ai = ai_res;
4292b15cb3dSCy Schubert 		while (NULL != ai) {
4302b15cb3dSCy Schubert 			if (NULL != ai->ai_canonname) {
4312b15cb3dSCy Schubert 				this_octets = strlen(ai->ai_canonname) + 1;
4322b15cb3dSCy Schubert 				memcpy(cp, ai->ai_canonname, this_octets);
4332b15cb3dSCy Schubert 				cp += this_octets;
4342b15cb3dSCy Schubert 			}
4352b15cb3dSCy Schubert 
4362b15cb3dSCy Schubert 			ai = ai->ai_next;
4372b15cb3dSCy Schubert 		}
4382b15cb3dSCy Schubert 		freeaddrinfo(ai_res);
4392b15cb3dSCy Schubert 	}
4402b15cb3dSCy Schubert 
4412b15cb3dSCy Schubert 	/*
4422b15cb3dSCy Schubert 	 * make sure our walk and earlier calc match
4432b15cb3dSCy Schubert 	 */
4442b15cb3dSCy Schubert 	DEBUG_INSIST((size_t)(cp - (char *)resp) == resp_octets);
4452b15cb3dSCy Schubert 
4462b15cb3dSCy Schubert 	if (queue_blocking_response(c, resp, resp_octets, req)) {
4472b15cb3dSCy Schubert 		msyslog(LOG_ERR, "blocking_getaddrinfo can not queue response");
4482b15cb3dSCy Schubert 		return -1;
4492b15cb3dSCy Schubert 	}
4502b15cb3dSCy Schubert 
4512b15cb3dSCy Schubert 	return 0;
4522b15cb3dSCy Schubert }
4532b15cb3dSCy Schubert 
4542b15cb3dSCy Schubert 
4552b15cb3dSCy Schubert static void
4562b15cb3dSCy Schubert getaddrinfo_sometime_complete(
4572b15cb3dSCy Schubert 	blocking_work_req	rtype,
4582b15cb3dSCy Schubert 	void *			context,
4592b15cb3dSCy Schubert 	size_t			respsize,
4602b15cb3dSCy Schubert 	void *			resp
4612b15cb3dSCy Schubert 	)
4622b15cb3dSCy Schubert {
4632b15cb3dSCy Schubert 	blocking_gai_req *	gai_req;
4642b15cb3dSCy Schubert 	blocking_gai_resp *	gai_resp;
4652b15cb3dSCy Schubert 	dnschild_ctx *		child_ctx;
4662b15cb3dSCy Schubert 	struct addrinfo *	ai;
4672b15cb3dSCy Schubert 	struct addrinfo *	next_ai;
4682b15cb3dSCy Schubert 	sockaddr_u *		psau;
4692b15cb3dSCy Schubert 	char *			node;
4702b15cb3dSCy Schubert 	char *			service;
4712b15cb3dSCy Schubert 	char *			canon_start;
4722b15cb3dSCy Schubert 	time_t			time_now;
4732b15cb3dSCy Schubert 	int			again;
4742b15cb3dSCy Schubert 	int			af;
4752b15cb3dSCy Schubert 	const char *		fam_spec;
4762b15cb3dSCy Schubert 	int			i;
4772b15cb3dSCy Schubert 
4782b15cb3dSCy Schubert 	gai_req = context;
4792b15cb3dSCy Schubert 	gai_resp = resp;
4802b15cb3dSCy Schubert 
4812b15cb3dSCy Schubert 	DEBUG_REQUIRE(BLOCKING_GETADDRINFO == rtype);
4822b15cb3dSCy Schubert 	DEBUG_REQUIRE(respsize == gai_resp->octets);
4832b15cb3dSCy Schubert 
4842b15cb3dSCy Schubert 	node = (char *)gai_req + sizeof(*gai_req);
4852b15cb3dSCy Schubert 	service = node + gai_req->nodesize;
4862b15cb3dSCy Schubert 
4872b15cb3dSCy Schubert 	child_ctx = dnschild_contexts[gai_req->dns_idx];
4882b15cb3dSCy Schubert 
4892b15cb3dSCy Schubert 	if (0 == gai_resp->retcode) {
4902b15cb3dSCy Schubert 		/*
4912b15cb3dSCy Schubert 		 * If this query succeeded only after retrying, DNS may have
4922b15cb3dSCy Schubert 		 * just become responsive.
4932b15cb3dSCy Schubert 		 */
4942b15cb3dSCy Schubert 		if (gai_resp->retry > INITIAL_DNS_RETRY) {
4952b15cb3dSCy Schubert 			time_now = time(NULL);
4962b15cb3dSCy Schubert 			child_ctx->next_dns_timeslot = time_now;
4972b15cb3dSCy Schubert 			TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n",
4982b15cb3dSCy Schubert 				  gai_req->dns_idx, humantime(time_now)));
4992b15cb3dSCy Schubert 		}
5002b15cb3dSCy Schubert 	} else {
5012b15cb3dSCy Schubert 		again = should_retry_dns(gai_resp->retcode,
5022b15cb3dSCy Schubert 					 gai_resp->gai_errno);
5032b15cb3dSCy Schubert 		/*
5042b15cb3dSCy Schubert 		 * exponential backoff of DNS retries to 64s
5052b15cb3dSCy Schubert 		 */
5062b15cb3dSCy Schubert 		if (gai_req->retry > 0 && again) {
5072b15cb3dSCy Schubert 			/* log the first retry only */
5082b15cb3dSCy Schubert 			if (INITIAL_DNS_RETRY == gai_req->retry)
5092b15cb3dSCy Schubert 				NLOG(NLOG_SYSINFO) {
5102b15cb3dSCy Schubert 					af = gai_req->hints.ai_family;
5112b15cb3dSCy Schubert 					fam_spec = (AF_INET6 == af)
5122b15cb3dSCy Schubert 						       ? " (AAAA)"
5132b15cb3dSCy Schubert 						       : (AF_INET == af)
5142b15cb3dSCy Schubert 							     ? " (A)"
5152b15cb3dSCy Schubert 							     : "";
5162b15cb3dSCy Schubert #ifdef EAI_SYSTEM
5172b15cb3dSCy Schubert 					if (EAI_SYSTEM == gai_resp->retcode) {
5182b15cb3dSCy Schubert 						errno = gai_resp->gai_errno;
5192b15cb3dSCy Schubert 						msyslog(LOG_INFO,
5202b15cb3dSCy Schubert 							"retrying DNS %s%s: EAI_SYSTEM %d: %m",
5212b15cb3dSCy Schubert 							node, fam_spec,
5222b15cb3dSCy Schubert 							gai_resp->gai_errno);
5232b15cb3dSCy Schubert 					} else
5242b15cb3dSCy Schubert #endif
5252b15cb3dSCy Schubert 						msyslog(LOG_INFO,
5262b15cb3dSCy Schubert 							"retrying DNS %s%s: %s (%d)",
5272b15cb3dSCy Schubert 							node, fam_spec,
5282b15cb3dSCy Schubert 							gai_strerror(gai_resp->retcode),
5292b15cb3dSCy Schubert 							gai_resp->retcode);
5302b15cb3dSCy Schubert 				}
5312b15cb3dSCy Schubert 			manage_dns_retry_interval(&gai_req->scheduled,
5322b15cb3dSCy Schubert 			    &gai_req->earliest, &gai_req->retry,
5332b15cb3dSCy Schubert 			    &child_ctx->next_dns_timeslot);
5342b15cb3dSCy Schubert 			if (!queue_blocking_request(
5352b15cb3dSCy Schubert 					BLOCKING_GETADDRINFO,
5362b15cb3dSCy Schubert 					gai_req,
5372b15cb3dSCy Schubert 					gai_req->octets,
5382b15cb3dSCy Schubert 					&getaddrinfo_sometime_complete,
5392b15cb3dSCy Schubert 					gai_req))
5402b15cb3dSCy Schubert 				return;
5412b15cb3dSCy Schubert 			else
5422b15cb3dSCy Schubert 				msyslog(LOG_ERR,
5432b15cb3dSCy Schubert 					"unable to retry hostname %s",
5442b15cb3dSCy Schubert 					node);
5452b15cb3dSCy Schubert 		}
5462b15cb3dSCy Schubert 	}
5472b15cb3dSCy Schubert 
5482b15cb3dSCy Schubert 	/*
5492b15cb3dSCy Schubert 	 * fixup pointers in returned addrinfo array
5502b15cb3dSCy Schubert 	 */
5512b15cb3dSCy Schubert 	ai = (void *)((char *)gai_resp + sizeof(*gai_resp));
5522b15cb3dSCy Schubert 	next_ai = NULL;
5532b15cb3dSCy Schubert 	for (i = gai_resp->ai_count - 1; i >= 0; i--) {
5542b15cb3dSCy Schubert 		ai[i].ai_next = next_ai;
5552b15cb3dSCy Schubert 		next_ai = &ai[i];
5562b15cb3dSCy Schubert 	}
5572b15cb3dSCy Schubert 
5582b15cb3dSCy Schubert 	psau = (void *)((char *)ai + gai_resp->ai_count * sizeof(*ai));
5592b15cb3dSCy Schubert 	canon_start = (char *)psau + gai_resp->ai_count * sizeof(*psau);
5602b15cb3dSCy Schubert 
5612b15cb3dSCy Schubert 	for (i = 0; i < gai_resp->ai_count; i++) {
5622b15cb3dSCy Schubert 		if (NULL != ai[i].ai_addr)
5632b15cb3dSCy Schubert 			ai[i].ai_addr = &psau->sa;
5642b15cb3dSCy Schubert 		psau++;
5652b15cb3dSCy Schubert 		if (NULL != ai[i].ai_canonname)
5662b15cb3dSCy Schubert 			ai[i].ai_canonname += (size_t)canon_start;
5672b15cb3dSCy Schubert 	}
5682b15cb3dSCy Schubert 
5699034852cSGleb Smirnoff 	ENSURE((char *)psau == canon_start);
5702b15cb3dSCy Schubert 
5712b15cb3dSCy Schubert 	if (!gai_resp->ai_count)
5722b15cb3dSCy Schubert 		ai = NULL;
5732b15cb3dSCy Schubert 
5742b15cb3dSCy Schubert 	(*gai_req->callback)(gai_resp->retcode, gai_resp->gai_errno,
5752b15cb3dSCy Schubert 			     gai_req->context, node, service,
5762b15cb3dSCy Schubert 			     &gai_req->hints, ai);
5772b15cb3dSCy Schubert 
5782b15cb3dSCy Schubert 	free(gai_req);
5792b15cb3dSCy Schubert 	/* gai_resp is part of block freed by process_blocking_resp() */
5802b15cb3dSCy Schubert }
5812b15cb3dSCy Schubert 
5822b15cb3dSCy Schubert 
5832b15cb3dSCy Schubert #ifdef TEST_BLOCKING_WORKER
5842b15cb3dSCy 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)
5852b15cb3dSCy Schubert {
5862b15cb3dSCy Schubert 	sockaddr_u addr;
5872b15cb3dSCy Schubert 
5882b15cb3dSCy Schubert 	if (rescode) {
5892b15cb3dSCy Schubert 		TRACE(1, ("gai_test_callback context %p error rescode %d %s serv %s\n",
5902b15cb3dSCy Schubert 			  context, rescode, name, service));
5912b15cb3dSCy Schubert 		return;
5922b15cb3dSCy Schubert 	}
5932b15cb3dSCy Schubert 	while (!rescode && NULL != ai_res) {
5942b15cb3dSCy Schubert 		ZERO_SOCK(&addr);
5952b15cb3dSCy Schubert 		memcpy(&addr, ai_res->ai_addr, ai_res->ai_addrlen);
5962b15cb3dSCy Schubert 		TRACE(1, ("ctx %p fam %d addr %s canon '%s' type %s at %p ai_addr %p ai_next %p\n",
5972b15cb3dSCy Schubert 			  context,
5982b15cb3dSCy Schubert 			  AF(&addr),
5992b15cb3dSCy Schubert 			  stoa(&addr),
6002b15cb3dSCy Schubert 			  (ai_res->ai_canonname)
6012b15cb3dSCy Schubert 			      ? ai_res->ai_canonname
6022b15cb3dSCy Schubert 			      : "",
6032b15cb3dSCy Schubert 			  (SOCK_DGRAM == ai_res->ai_socktype)
6042b15cb3dSCy Schubert 			      ? "DGRAM"
6052b15cb3dSCy Schubert 			      : (SOCK_STREAM == ai_res->ai_socktype)
6062b15cb3dSCy Schubert 				    ? "STREAM"
6072b15cb3dSCy Schubert 				    : "(other)",
6082b15cb3dSCy Schubert 			  ai_res,
6092b15cb3dSCy Schubert 			  ai_res->ai_addr,
6102b15cb3dSCy Schubert 			  ai_res->ai_next));
6112b15cb3dSCy Schubert 
6122b15cb3dSCy Schubert 		getnameinfo_sometime((sockaddr_u *)ai_res->ai_addr, 128, 32, 0, gni_test_callback, context);
6132b15cb3dSCy Schubert 
6142b15cb3dSCy Schubert 		ai_res = ai_res->ai_next;
6152b15cb3dSCy Schubert 	}
6162b15cb3dSCy Schubert }
6172b15cb3dSCy Schubert #endif	/* TEST_BLOCKING_WORKER */
6182b15cb3dSCy Schubert 
6192b15cb3dSCy Schubert 
6202b15cb3dSCy Schubert int
6212b15cb3dSCy Schubert getnameinfo_sometime(
6222b15cb3dSCy Schubert 	sockaddr_u *		psau,
6232b15cb3dSCy Schubert 	size_t			hostoctets,
6242b15cb3dSCy Schubert 	size_t			servoctets,
6252b15cb3dSCy Schubert 	int			flags,
6262b15cb3dSCy Schubert 	gni_sometime_callback	callback,
6272b15cb3dSCy Schubert 	void *			context
6282b15cb3dSCy Schubert 	)
6292b15cb3dSCy Schubert {
6302b15cb3dSCy Schubert 	blocking_gni_req *	gni_req;
6312b15cb3dSCy Schubert 	u_int			idx;
6322b15cb3dSCy Schubert 	dnschild_ctx *		child_ctx;
6332b15cb3dSCy Schubert 	time_t			time_now;
6342b15cb3dSCy Schubert 
6359034852cSGleb Smirnoff 	REQUIRE(hostoctets);
6369034852cSGleb Smirnoff 	REQUIRE(hostoctets + servoctets < 1024);
6372b15cb3dSCy Schubert 
6382b15cb3dSCy Schubert 	idx = get_dnschild_ctx();
6392b15cb3dSCy Schubert 	child_ctx = dnschild_contexts[idx];
6402b15cb3dSCy Schubert 
6412b15cb3dSCy Schubert 	gni_req = emalloc_zero(sizeof(*gni_req));
6422b15cb3dSCy Schubert 
6432b15cb3dSCy Schubert 	gni_req->octets = sizeof(*gni_req);
6442b15cb3dSCy Schubert 	gni_req->dns_idx = idx;
6452b15cb3dSCy Schubert 	time_now = time(NULL);
6462b15cb3dSCy Schubert 	gni_req->scheduled = time_now;
6472b15cb3dSCy Schubert 	gni_req->earliest = max(time_now, child_ctx->next_dns_timeslot);
6482b15cb3dSCy Schubert 	child_ctx->next_dns_timeslot = gni_req->earliest;
6492b15cb3dSCy Schubert 	memcpy(&gni_req->socku, psau, SOCKLEN(psau));
6502b15cb3dSCy Schubert 	gni_req->hostoctets = hostoctets;
6512b15cb3dSCy Schubert 	gni_req->servoctets = servoctets;
6522b15cb3dSCy Schubert 	gni_req->flags = flags;
6532b15cb3dSCy Schubert 	gni_req->retry = INITIAL_DNS_RETRY;
6542b15cb3dSCy Schubert 	gni_req->callback = callback;
6552b15cb3dSCy Schubert 	gni_req->context = context;
6562b15cb3dSCy Schubert 
6572b15cb3dSCy Schubert 	if (queue_blocking_request(
6582b15cb3dSCy Schubert 		BLOCKING_GETNAMEINFO,
6592b15cb3dSCy Schubert 		gni_req,
6602b15cb3dSCy Schubert 		sizeof(*gni_req),
6612b15cb3dSCy Schubert 		&getnameinfo_sometime_complete,
6622b15cb3dSCy Schubert 		gni_req)) {
6632b15cb3dSCy Schubert 
6642b15cb3dSCy Schubert 		msyslog(LOG_ERR, "unable to queue getnameinfo request");
6652b15cb3dSCy Schubert 		errno = EFAULT;
6662b15cb3dSCy Schubert 		return -1;
6672b15cb3dSCy Schubert 	}
6682b15cb3dSCy Schubert 
6692b15cb3dSCy Schubert 	return 0;
6702b15cb3dSCy Schubert }
6712b15cb3dSCy Schubert 
6722b15cb3dSCy Schubert 
6732b15cb3dSCy Schubert int
6742b15cb3dSCy Schubert blocking_getnameinfo(
6752b15cb3dSCy Schubert 	blocking_child *	c,
6762b15cb3dSCy Schubert 	blocking_pipe_header *	req
6772b15cb3dSCy Schubert 	)
6782b15cb3dSCy Schubert {
6792b15cb3dSCy Schubert 	blocking_gni_req *	gni_req;
6802b15cb3dSCy Schubert 	dnsworker_ctx *		worker_ctx;
6812b15cb3dSCy Schubert 	blocking_pipe_header *	resp;
6822b15cb3dSCy Schubert 	blocking_gni_resp *	gni_resp;
6832b15cb3dSCy Schubert 	size_t			octets;
6842b15cb3dSCy Schubert 	size_t			resp_octets;
6852b15cb3dSCy Schubert 	char *			service;
6862b15cb3dSCy Schubert 	char *			cp;
6872b15cb3dSCy Schubert 	int			rc;
6882b15cb3dSCy Schubert 	time_t			time_now;
6892b15cb3dSCy Schubert 	char			host[1024];
6902b15cb3dSCy Schubert 
6912b15cb3dSCy Schubert 	gni_req = (void *)((char *)req + sizeof(*req));
6922b15cb3dSCy Schubert 
6932b15cb3dSCy Schubert 	octets = gni_req->hostoctets + gni_req->servoctets;
6942b15cb3dSCy Schubert 
6952b15cb3dSCy Schubert 	/*
6962b15cb3dSCy Schubert 	 * Some alloca() implementations are fragile regarding
6972b15cb3dSCy Schubert 	 * large allocations.  We only need room for the host
6982b15cb3dSCy Schubert 	 * and service names.
6992b15cb3dSCy Schubert 	 */
7009034852cSGleb Smirnoff 	REQUIRE(octets < sizeof(host));
7012b15cb3dSCy Schubert 	service = host + gni_req->hostoctets;
7022b15cb3dSCy Schubert 
7032b15cb3dSCy Schubert 	worker_ctx = get_worker_context(c, gni_req->dns_idx);
7042b15cb3dSCy Schubert 	scheduled_sleep(gni_req->scheduled, gni_req->earliest,
7052b15cb3dSCy Schubert 			worker_ctx);
7062b15cb3dSCy Schubert 	reload_resolv_conf(worker_ctx);
7072b15cb3dSCy Schubert 
7082b15cb3dSCy Schubert 	/*
7092b15cb3dSCy Schubert 	 * Take a shot at the final size, better to overestimate
7102b15cb3dSCy Schubert 	 * then realloc to a smaller size.
7112b15cb3dSCy Schubert 	 */
7122b15cb3dSCy Schubert 
7132b15cb3dSCy Schubert 	resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets;
7142b15cb3dSCy Schubert 	resp = emalloc_zero(resp_octets);
7152b15cb3dSCy Schubert 	gni_resp = (void *)((char *)resp + sizeof(*resp));
7162b15cb3dSCy Schubert 
7172b15cb3dSCy Schubert 	TRACE(2, ("blocking_getnameinfo given addr %s flags 0x%x hostlen %lu servlen %lu\n",
7182b15cb3dSCy Schubert 		  stoa(&gni_req->socku), gni_req->flags,
7192b15cb3dSCy Schubert 		  (u_long)gni_req->hostoctets, (u_long)gni_req->servoctets));
7202b15cb3dSCy Schubert 
7212b15cb3dSCy Schubert 	gni_resp->retcode = getnameinfo(&gni_req->socku.sa,
7222b15cb3dSCy Schubert 					SOCKLEN(&gni_req->socku),
7232b15cb3dSCy Schubert 					host,
7242b15cb3dSCy Schubert 					gni_req->hostoctets,
7252b15cb3dSCy Schubert 					service,
7262b15cb3dSCy Schubert 					gni_req->servoctets,
7272b15cb3dSCy Schubert 					gni_req->flags);
7282b15cb3dSCy Schubert 	gni_resp->retry = gni_req->retry;
7292b15cb3dSCy Schubert #ifdef EAI_SYSTEM
7302b15cb3dSCy Schubert 	if (EAI_SYSTEM == gni_resp->retcode)
7312b15cb3dSCy Schubert 		gni_resp->gni_errno = errno;
7322b15cb3dSCy Schubert #endif
7332b15cb3dSCy Schubert 
7342b15cb3dSCy Schubert 	if (0 != gni_resp->retcode) {
7352b15cb3dSCy Schubert 		gni_resp->hostoctets = 0;
7362b15cb3dSCy Schubert 		gni_resp->servoctets = 0;
7372b15cb3dSCy Schubert 	} else {
7382b15cb3dSCy Schubert 		gni_resp->hostoctets = strlen(host) + 1;
7392b15cb3dSCy Schubert 		gni_resp->servoctets = strlen(service) + 1;
7402b15cb3dSCy Schubert 		/*
7412b15cb3dSCy Schubert 		 * If this query succeeded only after retrying, DNS may have
7422b15cb3dSCy Schubert 		 * just become responsive.  Ignore previously-scheduled
7432b15cb3dSCy Schubert 		 * retry sleeps once for each pending request, similar to
7442b15cb3dSCy Schubert 		 * the way scheduled_sleep() does when its worker_sleep()
7452b15cb3dSCy Schubert 		 * is interrupted.
7462b15cb3dSCy Schubert 		 */
7472b15cb3dSCy Schubert 		if (gni_req->retry > INITIAL_DNS_RETRY) {
7482b15cb3dSCy Schubert 			time_now = time(NULL);
7492b15cb3dSCy Schubert 			worker_ctx->ignore_scheduled_before = time_now;
7502b15cb3dSCy Schubert 			TRACE(1, ("DNS success after retrying, ignoring sleeps scheduled before now (%s)\n",
7512b15cb3dSCy Schubert 				humantime(time_now)));
7522b15cb3dSCy Schubert 		}
7532b15cb3dSCy Schubert 	}
7542b15cb3dSCy Schubert 	octets = gni_resp->hostoctets + gni_resp->servoctets;
7552b15cb3dSCy Schubert 	/*
7562b15cb3dSCy Schubert 	 * Our response consists of a header, followed by the host and
7572b15cb3dSCy Schubert 	 * service strings, each null-terminated.
7582b15cb3dSCy Schubert 	 */
7592b15cb3dSCy Schubert 	resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets;
7602b15cb3dSCy Schubert 
7612b15cb3dSCy Schubert 	resp = erealloc(resp, resp_octets);
7622b15cb3dSCy Schubert 	gni_resp = (void *)(resp + 1);
7632b15cb3dSCy Schubert 
7642b15cb3dSCy Schubert 	gni_resp->octets = sizeof(*gni_resp) + octets;
7652b15cb3dSCy Schubert 
7662b15cb3dSCy Schubert 	/* cp serves as our current pointer while serializing */
7672b15cb3dSCy Schubert 	cp = (void *)(gni_resp + 1);
7682b15cb3dSCy Schubert 
7692b15cb3dSCy Schubert 	if (0 == gni_resp->retcode) {
7702b15cb3dSCy Schubert 		memcpy(cp, host, gni_resp->hostoctets);
7712b15cb3dSCy Schubert 		cp += gni_resp->hostoctets;
7722b15cb3dSCy Schubert 		memcpy(cp, service, gni_resp->servoctets);
7732b15cb3dSCy Schubert 		cp += gni_resp->servoctets;
7742b15cb3dSCy Schubert 	}
7752b15cb3dSCy Schubert 
7769034852cSGleb Smirnoff 	INSIST((size_t)(cp - (char *)resp) == resp_octets);
7779034852cSGleb Smirnoff 	INSIST(resp_octets - sizeof(*resp) == gni_resp->octets);
7782b15cb3dSCy Schubert 
7792b15cb3dSCy Schubert 	rc = queue_blocking_response(c, resp, resp_octets, req);
7802b15cb3dSCy Schubert 	if (rc)
7812b15cb3dSCy Schubert 		msyslog(LOG_ERR, "blocking_getnameinfo unable to queue response");
7822b15cb3dSCy Schubert 	return rc;
7832b15cb3dSCy Schubert }
7842b15cb3dSCy Schubert 
7852b15cb3dSCy Schubert 
7862b15cb3dSCy Schubert static void
7872b15cb3dSCy Schubert getnameinfo_sometime_complete(
7882b15cb3dSCy Schubert 	blocking_work_req	rtype,
7892b15cb3dSCy Schubert 	void *			context,
7902b15cb3dSCy Schubert 	size_t			respsize,
7912b15cb3dSCy Schubert 	void *			resp
7922b15cb3dSCy Schubert 	)
7932b15cb3dSCy Schubert {
7942b15cb3dSCy Schubert 	blocking_gni_req *	gni_req;
7952b15cb3dSCy Schubert 	blocking_gni_resp *	gni_resp;
7962b15cb3dSCy Schubert 	dnschild_ctx *		child_ctx;
7972b15cb3dSCy Schubert 	char *			host;
7982b15cb3dSCy Schubert 	char *			service;
7992b15cb3dSCy Schubert 	time_t			time_now;
8002b15cb3dSCy Schubert 	int			again;
8012b15cb3dSCy Schubert 
8022b15cb3dSCy Schubert 	gni_req = context;
8032b15cb3dSCy Schubert 	gni_resp = resp;
8042b15cb3dSCy Schubert 
8052b15cb3dSCy Schubert 	DEBUG_REQUIRE(BLOCKING_GETNAMEINFO == rtype);
8062b15cb3dSCy Schubert 	DEBUG_REQUIRE(respsize == gni_resp->octets);
8072b15cb3dSCy Schubert 
8082b15cb3dSCy Schubert 	child_ctx = dnschild_contexts[gni_req->dns_idx];
8092b15cb3dSCy Schubert 
8102b15cb3dSCy Schubert 	if (0 == gni_resp->retcode) {
8112b15cb3dSCy Schubert 		/*
8122b15cb3dSCy Schubert 		 * If this query succeeded only after retrying, DNS may have
8132b15cb3dSCy Schubert 		 * just become responsive.
8142b15cb3dSCy Schubert 		 */
8152b15cb3dSCy Schubert 		if (gni_resp->retry > INITIAL_DNS_RETRY) {
8162b15cb3dSCy Schubert 			time_now = time(NULL);
8172b15cb3dSCy Schubert 			child_ctx->next_dns_timeslot = time_now;
8182b15cb3dSCy Schubert 			TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n",
8192b15cb3dSCy Schubert 				  gni_req->dns_idx, humantime(time_now)));
8202b15cb3dSCy Schubert 		}
8212b15cb3dSCy Schubert 	} else {
8222b15cb3dSCy Schubert 		again = should_retry_dns(gni_resp->retcode, gni_resp->gni_errno);
8232b15cb3dSCy Schubert 		/*
8242b15cb3dSCy Schubert 		 * exponential backoff of DNS retries to 64s
8252b15cb3dSCy Schubert 		 */
8262b15cb3dSCy Schubert 		if (gni_req->retry > 0)
8272b15cb3dSCy Schubert 			manage_dns_retry_interval(&gni_req->scheduled,
8282b15cb3dSCy Schubert 			    &gni_req->earliest, &gni_req->retry,
8292b15cb3dSCy Schubert 			    &child_ctx->next_dns_timeslot);
8302b15cb3dSCy Schubert 
8312b15cb3dSCy Schubert 		if (gni_req->retry > 0 && again) {
8322b15cb3dSCy Schubert 			if (!queue_blocking_request(
8332b15cb3dSCy Schubert 				BLOCKING_GETNAMEINFO,
8342b15cb3dSCy Schubert 				gni_req,
8352b15cb3dSCy Schubert 				gni_req->octets,
8362b15cb3dSCy Schubert 				&getnameinfo_sometime_complete,
8372b15cb3dSCy Schubert 				gni_req))
8382b15cb3dSCy Schubert 				return;
8392b15cb3dSCy Schubert 
8402b15cb3dSCy Schubert 			msyslog(LOG_ERR, "unable to retry reverse lookup of %s", stoa(&gni_req->socku));
8412b15cb3dSCy Schubert 		}
8422b15cb3dSCy Schubert 	}
8432b15cb3dSCy Schubert 
8442b15cb3dSCy Schubert 	if (!gni_resp->hostoctets) {
8452b15cb3dSCy Schubert 		host = NULL;
8462b15cb3dSCy Schubert 		service = NULL;
8472b15cb3dSCy Schubert 	} else {
8482b15cb3dSCy Schubert 		host = (char *)gni_resp + sizeof(*gni_resp);
8492b15cb3dSCy Schubert 		service = (gni_resp->servoctets)
8502b15cb3dSCy Schubert 			      ? host + gni_resp->hostoctets
8512b15cb3dSCy Schubert 			      : NULL;
8522b15cb3dSCy Schubert 	}
8532b15cb3dSCy Schubert 
8542b15cb3dSCy Schubert 	(*gni_req->callback)(gni_resp->retcode, gni_resp->gni_errno,
8552b15cb3dSCy Schubert 			     &gni_req->socku, gni_req->flags, host,
8562b15cb3dSCy Schubert 			     service, gni_req->context);
8572b15cb3dSCy Schubert 
8582b15cb3dSCy Schubert 	free(gni_req);
8592b15cb3dSCy Schubert 	/* gni_resp is part of block freed by process_blocking_resp() */
8602b15cb3dSCy Schubert }
8612b15cb3dSCy Schubert 
8622b15cb3dSCy Schubert 
8632b15cb3dSCy Schubert #ifdef TEST_BLOCKING_WORKER
8642b15cb3dSCy Schubert void gni_test_callback(int rescode, int gni_errno, sockaddr_u *psau, int flags, const char *host, const char *service, void *context)
8652b15cb3dSCy Schubert {
8662b15cb3dSCy Schubert 	if (!rescode)
8672b15cb3dSCy Schubert 		TRACE(1, ("gni_test_callback got host '%s' serv '%s' for addr %s context %p\n",
8682b15cb3dSCy Schubert 			  host, service, stoa(psau), context));
8692b15cb3dSCy Schubert 	else
8702b15cb3dSCy Schubert 		TRACE(1, ("gni_test_callback context %p rescode %d gni_errno %d flags 0x%x addr %s\n",
8712b15cb3dSCy Schubert 			  context, rescode, gni_errno, flags, stoa(psau)));
8722b15cb3dSCy Schubert }
8732b15cb3dSCy Schubert #endif	/* TEST_BLOCKING_WORKER */
8742b15cb3dSCy Schubert 
8752b15cb3dSCy Schubert 
8762b15cb3dSCy Schubert #ifdef HAVE_RES_INIT
8772b15cb3dSCy Schubert static void
8782b15cb3dSCy Schubert reload_resolv_conf(
8792b15cb3dSCy Schubert 	dnsworker_ctx *	worker_ctx
8802b15cb3dSCy Schubert 	)
8812b15cb3dSCy Schubert {
8822b15cb3dSCy Schubert 	time_t	time_now;
8832b15cb3dSCy Schubert 
8842b15cb3dSCy Schubert 	/*
8852b15cb3dSCy Schubert 	 * This is ad-hoc.  Reload /etc/resolv.conf once per minute
8862b15cb3dSCy Schubert 	 * to pick up on changes from the DHCP client.  [Bug 1226]
8872b15cb3dSCy Schubert 	 * When using threads for the workers, this needs to happen
8882b15cb3dSCy Schubert 	 * only once per minute process-wide.
8892b15cb3dSCy Schubert 	 */
8902b15cb3dSCy Schubert 	time_now = time(NULL);
8912b15cb3dSCy Schubert # ifdef WORK_THREAD
8922b15cb3dSCy Schubert 	worker_ctx->next_res_init = next_res_init;
8932b15cb3dSCy Schubert # endif
8942b15cb3dSCy Schubert 	if (worker_ctx->next_res_init <= time_now) {
8952b15cb3dSCy Schubert 		if (worker_ctx->next_res_init != 0)
8962b15cb3dSCy Schubert 			res_init();
8972b15cb3dSCy Schubert 		worker_ctx->next_res_init = time_now + 60;
8982b15cb3dSCy Schubert # ifdef WORK_THREAD
8992b15cb3dSCy Schubert 		next_res_init = worker_ctx->next_res_init;
9002b15cb3dSCy Schubert # endif
9012b15cb3dSCy Schubert 	}
9022b15cb3dSCy Schubert }
9032b15cb3dSCy Schubert #endif	/* HAVE_RES_INIT */
9042b15cb3dSCy Schubert 
9052b15cb3dSCy Schubert 
9062b15cb3dSCy Schubert static u_int
9072b15cb3dSCy Schubert reserve_dnschild_ctx(void)
9082b15cb3dSCy Schubert {
9092b15cb3dSCy Schubert 	const size_t	ps = sizeof(dnschild_contexts[0]);
9102b15cb3dSCy Schubert 	const size_t	cs = sizeof(*dnschild_contexts[0]);
9112b15cb3dSCy Schubert 	u_int		c;
9122b15cb3dSCy Schubert 	u_int		new_alloc;
9132b15cb3dSCy Schubert 	size_t		octets;
9142b15cb3dSCy Schubert 	size_t		new_octets;
9152b15cb3dSCy Schubert 
9162b15cb3dSCy Schubert 	c = 0;
9172b15cb3dSCy Schubert 	while (TRUE) {
9182b15cb3dSCy Schubert 		for ( ; c < dnschild_contexts_alloc; c++) {
9192b15cb3dSCy Schubert 			if (NULL == dnschild_contexts[c]) {
9202b15cb3dSCy Schubert 				dnschild_contexts[c] = emalloc_zero(cs);
9212b15cb3dSCy Schubert 
9222b15cb3dSCy Schubert 				return c;
9232b15cb3dSCy Schubert 			}
9242b15cb3dSCy Schubert 		}
9252b15cb3dSCy Schubert 		new_alloc = dnschild_contexts_alloc + 20;
9262b15cb3dSCy Schubert 		new_octets = new_alloc * ps;
9272b15cb3dSCy Schubert 		octets = dnschild_contexts_alloc * ps;
9282b15cb3dSCy Schubert 		dnschild_contexts = erealloc_zero(dnschild_contexts,
9292b15cb3dSCy Schubert 						  new_octets, octets);
9302b15cb3dSCy Schubert 		dnschild_contexts_alloc = new_alloc;
9312b15cb3dSCy Schubert 	}
9322b15cb3dSCy Schubert }
9332b15cb3dSCy Schubert 
9342b15cb3dSCy Schubert 
9352b15cb3dSCy Schubert static u_int
9362b15cb3dSCy Schubert get_dnschild_ctx(void)
9372b15cb3dSCy Schubert {
9382b15cb3dSCy Schubert 	static u_int	shared_ctx = UINT_MAX;
9392b15cb3dSCy Schubert 
9402b15cb3dSCy Schubert 	if (worker_per_query)
9412b15cb3dSCy Schubert 		return reserve_dnschild_ctx();
9422b15cb3dSCy Schubert 
9432b15cb3dSCy Schubert 	if (UINT_MAX == shared_ctx)
9442b15cb3dSCy Schubert 		shared_ctx = reserve_dnschild_ctx();
9452b15cb3dSCy Schubert 
9462b15cb3dSCy Schubert 	return shared_ctx;
9472b15cb3dSCy Schubert }
9482b15cb3dSCy Schubert 
9492b15cb3dSCy Schubert 
9502b15cb3dSCy Schubert static dnsworker_ctx *
9512b15cb3dSCy Schubert get_worker_context(
9522b15cb3dSCy Schubert 	blocking_child *	c,
9532b15cb3dSCy Schubert 	u_int			idx
9542b15cb3dSCy Schubert 	)
9552b15cb3dSCy Schubert {
9562b15cb3dSCy Schubert 	u_int		min_new_alloc;
9572b15cb3dSCy Schubert 	u_int		new_alloc;
9582b15cb3dSCy Schubert 	size_t		octets;
9592b15cb3dSCy Schubert 	size_t		new_octets;
960*4990d495SXin LI 	dnsworker_ctx *	retv;
961*4990d495SXin LI 
962*4990d495SXin LI 	worker_global_lock(TRUE);
9632b15cb3dSCy Schubert 
9642b15cb3dSCy Schubert 	if (dnsworker_contexts_alloc <= idx) {
9652b15cb3dSCy Schubert 		min_new_alloc = 1 + idx;
9662b15cb3dSCy Schubert 		/* round new_alloc up to nearest multiple of 4 */
9672b15cb3dSCy Schubert 		new_alloc = (min_new_alloc + 4) & ~(4 - 1);
968*4990d495SXin LI 		new_octets = new_alloc * sizeof(dnsworker_ctx*);
969*4990d495SXin LI 		octets = dnsworker_contexts_alloc * sizeof(dnsworker_ctx*);
9702b15cb3dSCy Schubert 		dnsworker_contexts = erealloc_zero(dnsworker_contexts,
9712b15cb3dSCy Schubert 						   new_octets, octets);
9722b15cb3dSCy Schubert 		dnsworker_contexts_alloc = new_alloc;
973*4990d495SXin LI 		retv = emalloc_zero(sizeof(dnsworker_ctx));
974*4990d495SXin LI 		dnsworker_contexts[idx] = retv;
975*4990d495SXin LI 	} else if (NULL == (retv = dnsworker_contexts[idx])) {
976*4990d495SXin LI 		retv = emalloc_zero(sizeof(dnsworker_ctx));
977*4990d495SXin LI 		dnsworker_contexts[idx] = retv;
9782b15cb3dSCy Schubert 	}
9792b15cb3dSCy Schubert 
980*4990d495SXin LI 	worker_global_lock(FALSE);
9812b15cb3dSCy Schubert 
982*4990d495SXin LI 	ZERO(*retv);
983*4990d495SXin LI 	retv->c = c;
984*4990d495SXin LI 	return retv;
9852b15cb3dSCy Schubert }
9862b15cb3dSCy Schubert 
9872b15cb3dSCy Schubert 
9882b15cb3dSCy Schubert static void
9892b15cb3dSCy Schubert scheduled_sleep(
9902b15cb3dSCy Schubert 	time_t		scheduled,
9912b15cb3dSCy Schubert 	time_t		earliest,
9922b15cb3dSCy Schubert 	dnsworker_ctx *	worker_ctx
9932b15cb3dSCy Schubert 	)
9942b15cb3dSCy Schubert {
9952b15cb3dSCy Schubert 	time_t now;
9962b15cb3dSCy Schubert 
9972b15cb3dSCy Schubert 	if (scheduled < worker_ctx->ignore_scheduled_before) {
9982b15cb3dSCy Schubert 		TRACE(1, ("ignoring sleep until %s scheduled at %s (before %s)\n",
9992b15cb3dSCy Schubert 			  humantime(earliest), humantime(scheduled),
10002b15cb3dSCy Schubert 			  humantime(worker_ctx->ignore_scheduled_before)));
10012b15cb3dSCy Schubert 		return;
10022b15cb3dSCy Schubert 	}
10032b15cb3dSCy Schubert 
10042b15cb3dSCy Schubert 	now = time(NULL);
10052b15cb3dSCy Schubert 
10062b15cb3dSCy Schubert 	if (now < earliest) {
10072b15cb3dSCy Schubert 		TRACE(1, ("sleep until %s scheduled at %s (>= %s)\n",
10082b15cb3dSCy Schubert 			  humantime(earliest), humantime(scheduled),
10092b15cb3dSCy Schubert 			  humantime(worker_ctx->ignore_scheduled_before)));
10102b15cb3dSCy Schubert 		if (-1 == worker_sleep(worker_ctx->c, earliest - now)) {
10112b15cb3dSCy Schubert 			/* our sleep was interrupted */
10122b15cb3dSCy Schubert 			now = time(NULL);
10132b15cb3dSCy Schubert 			worker_ctx->ignore_scheduled_before = now;
10142b15cb3dSCy Schubert #ifdef HAVE_RES_INIT
10152b15cb3dSCy Schubert 			worker_ctx->next_res_init = now + 60;
10162b15cb3dSCy Schubert 			next_res_init = worker_ctx->next_res_init;
10172b15cb3dSCy Schubert 			res_init();
10182b15cb3dSCy Schubert #endif
10192b15cb3dSCy Schubert 			TRACE(1, ("sleep interrupted by daemon, ignoring sleeps scheduled before now (%s)\n",
10202b15cb3dSCy Schubert 				  humantime(worker_ctx->ignore_scheduled_before)));
10212b15cb3dSCy Schubert 		}
10222b15cb3dSCy Schubert 	}
10232b15cb3dSCy Schubert }
10242b15cb3dSCy Schubert 
10252b15cb3dSCy Schubert 
10262b15cb3dSCy Schubert /*
10272b15cb3dSCy Schubert  * manage_dns_retry_interval is a helper used by
10282b15cb3dSCy Schubert  * getaddrinfo_sometime_complete and getnameinfo_sometime_complete
10292b15cb3dSCy Schubert  * to calculate the new retry interval and schedule the next query.
10302b15cb3dSCy Schubert  */
10312b15cb3dSCy Schubert static void
10322b15cb3dSCy Schubert manage_dns_retry_interval(
10332b15cb3dSCy Schubert 	time_t *	pscheduled,
10342b15cb3dSCy Schubert 	time_t *	pwhen,
10352b15cb3dSCy Schubert 	int *		pretry,
10362b15cb3dSCy Schubert 	time_t *	pnext_timeslot
10372b15cb3dSCy Schubert 	)
10382b15cb3dSCy Schubert {
10392b15cb3dSCy Schubert 	time_t	now;
10402b15cb3dSCy Schubert 	time_t	when;
10412b15cb3dSCy Schubert 	int	retry;
10422b15cb3dSCy Schubert 
10432b15cb3dSCy Schubert 	now = time(NULL);
10442b15cb3dSCy Schubert 	retry = *pretry;
10452b15cb3dSCy Schubert 	when = max(now + retry, *pnext_timeslot);
10462b15cb3dSCy Schubert 	*pnext_timeslot = when;
10472b15cb3dSCy Schubert 	retry = min(64, retry << 1);
10482b15cb3dSCy Schubert 
10492b15cb3dSCy Schubert 	*pscheduled = now;
10502b15cb3dSCy Schubert 	*pwhen = when;
10512b15cb3dSCy Schubert 	*pretry = retry;
10522b15cb3dSCy Schubert }
10532b15cb3dSCy Schubert 
10542b15cb3dSCy Schubert /*
10552b15cb3dSCy Schubert  * should_retry_dns is a helper used by getaddrinfo_sometime_complete
10562b15cb3dSCy Schubert  * and getnameinfo_sometime_complete which implements ntpd's DNS retry
10572b15cb3dSCy Schubert  * policy.
10582b15cb3dSCy Schubert  */
10592b15cb3dSCy Schubert static int
10602b15cb3dSCy Schubert should_retry_dns(
10612b15cb3dSCy Schubert 	int	rescode,
10622b15cb3dSCy Schubert 	int	res_errno
10632b15cb3dSCy Schubert 	)
10642b15cb3dSCy Schubert {
10652b15cb3dSCy Schubert 	static int	eai_again_seen;
10662b15cb3dSCy Schubert 	int		again;
10672b15cb3dSCy Schubert #if defined (EAI_SYSTEM) && defined(DEBUG)
10682b15cb3dSCy Schubert 	char		msg[256];
10692b15cb3dSCy Schubert #endif
10702b15cb3dSCy Schubert 
10712b15cb3dSCy Schubert 	/*
10722b15cb3dSCy Schubert 	 * If the resolver failed, see if the failure is
10732b15cb3dSCy Schubert 	 * temporary. If so, return success.
10742b15cb3dSCy Schubert 	 */
10752b15cb3dSCy Schubert 	again = 0;
10762b15cb3dSCy Schubert 
10772b15cb3dSCy Schubert 	switch (rescode) {
10782b15cb3dSCy Schubert 
10792b15cb3dSCy Schubert 	case EAI_FAIL:
10802b15cb3dSCy Schubert 		again = 1;
10812b15cb3dSCy Schubert 		break;
10822b15cb3dSCy Schubert 
10832b15cb3dSCy Schubert 	case EAI_AGAIN:
10842b15cb3dSCy Schubert 		again = 1;
10852b15cb3dSCy Schubert 		eai_again_seen = 1;		/* [Bug 1178] */
10862b15cb3dSCy Schubert 		break;
10872b15cb3dSCy Schubert 
10882b15cb3dSCy Schubert 	case EAI_NONAME:
10892b15cb3dSCy Schubert #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
10902b15cb3dSCy Schubert 	case EAI_NODATA:
10912b15cb3dSCy Schubert #endif
10922b15cb3dSCy Schubert 		again = !eai_again_seen;	/* [Bug 1178] */
10932b15cb3dSCy Schubert 		break;
10942b15cb3dSCy Schubert 
10952b15cb3dSCy Schubert #ifdef EAI_SYSTEM
10962b15cb3dSCy Schubert 	case EAI_SYSTEM:
10972b15cb3dSCy Schubert 		/*
10982b15cb3dSCy Schubert 		 * EAI_SYSTEM means the real error is in errno.  We should be more
10992b15cb3dSCy Schubert 		 * discriminating about which errno values require retrying, but
11002b15cb3dSCy Schubert 		 * this matches existing behavior.
11012b15cb3dSCy Schubert 		 */
11022b15cb3dSCy Schubert 		again = 1;
11032b15cb3dSCy Schubert # ifdef DEBUG
11042b15cb3dSCy Schubert 		errno_to_str(res_errno, msg, sizeof(msg));
11052b15cb3dSCy Schubert 		TRACE(1, ("intres: EAI_SYSTEM errno %d (%s) means try again, right?\n",
11062b15cb3dSCy Schubert 			  res_errno, msg));
11072b15cb3dSCy Schubert # endif
11082b15cb3dSCy Schubert 		break;
11092b15cb3dSCy Schubert #endif
11102b15cb3dSCy Schubert 	}
11112b15cb3dSCy Schubert 
11122b15cb3dSCy Schubert 	TRACE(2, ("intres: resolver returned: %s (%d), %sretrying\n",
11132b15cb3dSCy Schubert 		  gai_strerror(rescode), rescode, again ? "" : "not "));
11142b15cb3dSCy Schubert 
11152b15cb3dSCy Schubert 	return again;
11162b15cb3dSCy Schubert }
11172b15cb3dSCy Schubert 
11182b15cb3dSCy Schubert #else	/* !WORKER follows */
11192b15cb3dSCy Schubert int ntp_intres_nonempty_compilation_unit;
11202b15cb3dSCy Schubert #endif
1121