ntp_intres.c revision 280849
1275970Scy/*
2275970Scy * ntp_intres.c - Implements a generic blocking worker child or thread,
3275970Scy *		  initially to provide a nonblocking solution for DNS
4275970Scy *		  name to address lookups available with getaddrinfo().
5275970Scy *
6275970Scy * This is a new implementation as of 2009 sharing the filename and
7275970Scy * very little else with the prior implementation, which used a
8275970Scy * temporary file to receive a single set of requests from the parent,
9275970Scy * and a NTP mode 7 authenticated request to push back responses.
10275970Scy *
11275970Scy * A primary goal in rewriting this code was the need to support the
12275970Scy * pool configuration directive's requirement to retrieve multiple
13275970Scy * addresses resolving a single name, which has previously been
14275970Scy * satisfied with blocking resolver calls from the ntpd mainline code.
15275970Scy *
16275970Scy * A secondary goal is to provide a generic mechanism for other
17275970Scy * blocking operations to be delegated to a worker using a common
18275970Scy * model for both Unix and Windows ntpd.  ntp_worker.c, work_fork.c,
19275970Scy * and work_thread.c implement the generic mechanism.  This file
20275970Scy * implements the two current consumers, getaddrinfo_sometime() and the
21275970Scy * presently unused getnameinfo_sometime().
22275970Scy *
23275970Scy * Both routines deliver results to a callback and manage memory
24275970Scy * allocation, meaning there is no freeaddrinfo_sometime().
25275970Scy *
26275970Scy * The initial implementation for Unix uses a pair of unidirectional
27275970Scy * pipes, one each for requests and responses, connecting the forked
28275970Scy * blocking child worker with the ntpd mainline.  The threaded code
29275970Scy * uses arrays of pointers to queue requests and responses.
30275970Scy *
31275970Scy * The parent drives the process, including scheduling sleeps between
32275970Scy * retries.
33275970Scy *
34275970Scy * Memory is managed differently for a child process, which mallocs
35275970Scy * request buffers to read from the pipe into, whereas the threaded
36275970Scy * code mallocs a copy of the request to hand off to the worker via
37275970Scy * the queueing array.  The resulting request buffer is free()d by
38275970Scy * platform-independent code.  A wrinkle is the request needs to be
39275970Scy * available to the requestor during response processing.
40275970Scy *
41275970Scy * Response memory allocation is also platform-dependent.  With a
42275970Scy * separate process and pipes, the response is free()d after being
43275970Scy * written to the pipe.  With threads, the same memory is handed
44275970Scy * over and the requestor frees it after processing is completed.
45275970Scy *
46275970Scy * The code should be generalized to support threads on Unix using
47275970Scy * much of the same code used for Windows initially.
48275970Scy *
49275970Scy */
50275970Scy#ifdef HAVE_CONFIG_H
51275970Scy# include <config.h>
52275970Scy#endif
53275970Scy
54275970Scy#include "ntp_workimpl.h"
55275970Scy
56275970Scy#ifdef WORKER
57275970Scy
58275970Scy#include <stdio.h>
59275970Scy#include <ctype.h>
60275970Scy#include <signal.h>
61275970Scy
62275970Scy/**/
63275970Scy#ifdef HAVE_SYS_TYPES_H
64275970Scy# include <sys/types.h>
65275970Scy#endif
66275970Scy#ifdef HAVE_NETINET_IN_H
67275970Scy#include <netinet/in.h>
68275970Scy#endif
69275970Scy#include <arpa/inet.h>
70275970Scy/**/
71275970Scy#ifdef HAVE_SYS_PARAM_H
72275970Scy# include <sys/param.h>
73275970Scy#endif
74275970Scy
75275970Scy#if !defined(HAVE_RES_INIT) && defined(HAVE___RES_INIT)
76275970Scy# define HAVE_RES_INIT
77275970Scy#endif
78275970Scy
79275970Scy#if defined(HAVE_RESOLV_H) && defined(HAVE_RES_INIT)
80275970Scy# ifdef HAVE_ARPA_NAMESER_H
81275970Scy#  include <arpa/nameser.h> /* DNS HEADER struct */
82275970Scy# endif
83275970Scy# ifdef HAVE_NETDB_H
84275970Scy#  include <netdb.h>
85275970Scy# endif
86275970Scy# include <resolv.h>
87275970Scy# ifdef HAVE_INT32_ONLY_WITH_DNS
88275970Scy#  define HAVE_INT32
89275970Scy# endif
90275970Scy# ifdef HAVE_U_INT32_ONLY_WITH_DNS
91275970Scy#  define HAVE_U_INT32
92275970Scy# endif
93275970Scy#endif
94275970Scy
95275970Scy#include "ntp.h"
96275970Scy#include "ntp_debug.h"
97275970Scy#include "ntp_malloc.h"
98275970Scy#include "ntp_syslog.h"
99275970Scy#include "ntp_unixtime.h"
100275970Scy#include "ntp_intres.h"
101275970Scy#include "intreswork.h"
102275970Scy
103275970Scy
104275970Scy/*
105275970Scy * Following are implementations of getaddrinfo_sometime() and
106275970Scy * getnameinfo_sometime().  Each is implemented in three routines:
107275970Scy *
108275970Scy * getaddrinfo_sometime()		getnameinfo_sometime()
109275970Scy * blocking_getaddrinfo()		blocking_getnameinfo()
110275970Scy * getaddrinfo_sometime_complete()	getnameinfo_sometime_complete()
111275970Scy *
112275970Scy * The first runs in the parent and marshalls (or serializes) request
113275970Scy * parameters into a request blob which is processed in the child by
114275970Scy * the second routine, blocking_*(), which serializes the results into
115275970Scy * a response blob unpacked by the third routine, *_complete(), which
116275970Scy * calls the callback routine provided with the request and frees
117275970Scy * _request_ memory allocated by the first routine.  Response memory
118275970Scy * is managed by the code which calls the *_complete routines.
119275970Scy */
120275970Scy
121275970Scy/* === typedefs === */
122275970Scytypedef struct blocking_gai_req_tag {	/* marshalled args */
123275970Scy	size_t			octets;
124275970Scy	u_int			dns_idx;
125275970Scy	time_t			scheduled;
126275970Scy	time_t			earliest;
127275970Scy	struct addrinfo		hints;
128275970Scy	int			retry;
129275970Scy	gai_sometime_callback	callback;
130275970Scy	void *			context;
131275970Scy	size_t			nodesize;
132275970Scy	size_t			servsize;
133275970Scy} blocking_gai_req;
134275970Scy
135275970Scytypedef struct blocking_gai_resp_tag {
136275970Scy	size_t			octets;
137275970Scy	int			retcode;
138275970Scy	int			retry;
139275970Scy	int			gai_errno; /* for EAI_SYSTEM case */
140275970Scy	int			ai_count;
141275970Scy	/*
142275970Scy	 * Followed by ai_count struct addrinfo and then ai_count
143275970Scy	 * sockaddr_u and finally the canonical name strings.
144275970Scy	 */
145275970Scy} blocking_gai_resp;
146275970Scy
147275970Scytypedef struct blocking_gni_req_tag {
148275970Scy	size_t			octets;
149275970Scy	u_int			dns_idx;
150275970Scy	time_t			scheduled;
151275970Scy	time_t			earliest;
152275970Scy	int			retry;
153275970Scy	size_t			hostoctets;
154275970Scy	size_t			servoctets;
155275970Scy	int			flags;
156275970Scy	gni_sometime_callback	callback;
157275970Scy	void *			context;
158275970Scy	sockaddr_u		socku;
159275970Scy} blocking_gni_req;
160275970Scy
161275970Scytypedef struct blocking_gni_resp_tag {
162275970Scy	size_t			octets;
163275970Scy	int			retcode;
164275970Scy	int			gni_errno; /* for EAI_SYSTEM case */
165275970Scy	int			retry;
166275970Scy	size_t			hostoctets;
167275970Scy	size_t			servoctets;
168275970Scy	/*
169275970Scy	 * Followed by hostoctets bytes of null-terminated host,
170275970Scy	 * then servoctets bytes of null-terminated service.
171275970Scy	 */
172275970Scy} blocking_gni_resp;
173275970Scy
174275970Scy/* per-DNS-worker state in parent */
175275970Scytypedef struct dnschild_ctx_tag {
176275970Scy	u_int	index;
177275970Scy	time_t	next_dns_timeslot;
178275970Scy} dnschild_ctx;
179275970Scy
180275970Scy/* per-DNS-worker state in worker */
181275970Scytypedef struct dnsworker_ctx_tag {
182275970Scy	blocking_child *	c;
183275970Scy	time_t			ignore_scheduled_before;
184275970Scy#ifdef HAVE_RES_INIT
185275970Scy	time_t	next_res_init;
186275970Scy#endif
187275970Scy} dnsworker_ctx;
188275970Scy
189275970Scy
190275970Scy/* === variables === */
191275970Scydnschild_ctx **		dnschild_contexts;		/* parent */
192275970Scyu_int			dnschild_contexts_alloc;
193275970Scydnsworker_ctx **	dnsworker_contexts;		/* child */
194275970Scyu_int			dnsworker_contexts_alloc;
195275970Scy
196275970Scy#ifdef HAVE_RES_INIT
197275970Scystatic	time_t		next_res_init;
198275970Scy#endif
199275970Scy
200275970Scy
201275970Scy/* === forward declarations === */
202275970Scystatic	u_int		reserve_dnschild_ctx(void);
203275970Scystatic	u_int		get_dnschild_ctx(void);
204275970Scystatic	void		alloc_dnsworker_context(u_int);
205275970Scy/* static	void		free_dnsworker_context(u_int); */
206275970Scystatic	dnsworker_ctx *	get_worker_context(blocking_child *, u_int);
207275970Scystatic	void		scheduled_sleep(time_t, time_t,
208275970Scy					dnsworker_ctx *);
209275970Scystatic	void		manage_dns_retry_interval(time_t *, time_t *,
210275970Scy						  int *,
211275970Scy						  time_t *);
212275970Scystatic	int		should_retry_dns(int, int);
213275970Scy#ifdef HAVE_RES_INIT
214275970Scystatic	void		reload_resolv_conf(dnsworker_ctx *);
215275970Scy#else
216275970Scy# define		reload_resolv_conf(wc)		\
217275970Scy	do {						\
218275970Scy		(void)(wc);				\
219275970Scy	} while (FALSE)
220275970Scy#endif
221275970Scystatic	void		getaddrinfo_sometime_complete(blocking_work_req,
222275970Scy						      void *, size_t,
223275970Scy						      void *);
224275970Scystatic	void		getnameinfo_sometime_complete(blocking_work_req,
225275970Scy						      void *, size_t,
226275970Scy						      void *);
227275970Scy
228275970Scy
229275970Scy/* === functions === */
230275970Scy/*
231275970Scy * getaddrinfo_sometime - uses blocking child to call getaddrinfo then
232275970Scy *			  invokes provided callback completion function.
233275970Scy */
234275970Scyint
235275970Scygetaddrinfo_sometime(
236275970Scy	const char *		node,
237275970Scy	const char *		service,
238275970Scy	const struct addrinfo *	hints,
239275970Scy	int			retry,
240275970Scy	gai_sometime_callback	callback,
241275970Scy	void *			context
242275970Scy	)
243275970Scy{
244275970Scy	blocking_gai_req *	gai_req;
245275970Scy	u_int			idx;
246275970Scy	dnschild_ctx *		child_ctx;
247275970Scy	size_t			req_size;
248275970Scy	size_t			nodesize;
249275970Scy	size_t			servsize;
250275970Scy	time_t			now;
251275970Scy
252275970Scy	NTP_REQUIRE(NULL != node);
253275970Scy	if (NULL != hints) {
254275970Scy		NTP_REQUIRE(0 == hints->ai_addrlen);
255275970Scy		NTP_REQUIRE(NULL == hints->ai_addr);
256275970Scy		NTP_REQUIRE(NULL == hints->ai_canonname);
257275970Scy		NTP_REQUIRE(NULL == hints->ai_next);
258275970Scy	}
259275970Scy
260275970Scy	idx = get_dnschild_ctx();
261275970Scy	child_ctx = dnschild_contexts[idx];
262275970Scy
263275970Scy	nodesize = strlen(node) + 1;
264275970Scy	servsize = strlen(service) + 1;
265275970Scy	req_size = sizeof(*gai_req) + nodesize + servsize;
266275970Scy
267275970Scy	gai_req = emalloc_zero(req_size);
268275970Scy
269275970Scy	gai_req->octets = req_size;
270275970Scy	gai_req->dns_idx = idx;
271275970Scy	now = time(NULL);
272275970Scy	gai_req->scheduled = now;
273275970Scy	gai_req->earliest = max(now, child_ctx->next_dns_timeslot);
274275970Scy	child_ctx->next_dns_timeslot = gai_req->earliest;
275275970Scy	if (hints != NULL)
276275970Scy		gai_req->hints = *hints;
277275970Scy	gai_req->retry = retry;
278275970Scy	gai_req->callback = callback;
279275970Scy	gai_req->context = context;
280275970Scy	gai_req->nodesize = nodesize;
281275970Scy	gai_req->servsize = servsize;
282275970Scy
283275970Scy	memcpy((char *)gai_req + sizeof(*gai_req), node, nodesize);
284275970Scy	memcpy((char *)gai_req + sizeof(*gai_req) + nodesize, service,
285275970Scy	       servsize);
286275970Scy
287275970Scy	if (queue_blocking_request(
288275970Scy		BLOCKING_GETADDRINFO,
289275970Scy		gai_req,
290275970Scy		req_size,
291275970Scy		&getaddrinfo_sometime_complete,
292275970Scy		gai_req)) {
293275970Scy
294275970Scy		msyslog(LOG_ERR, "unable to queue getaddrinfo request");
295275970Scy		errno = EFAULT;
296275970Scy		return -1;
297275970Scy	}
298275970Scy
299275970Scy	return 0;
300275970Scy}
301275970Scy
302275970Scyint
303275970Scyblocking_getaddrinfo(
304275970Scy	blocking_child *	c,
305275970Scy	blocking_pipe_header *	req
306275970Scy	)
307275970Scy{
308275970Scy	blocking_gai_req *	gai_req;
309275970Scy	dnsworker_ctx *		worker_ctx;
310275970Scy	blocking_pipe_header *	resp;
311275970Scy	blocking_gai_resp *	gai_resp;
312275970Scy	char *			node;
313275970Scy	char *			service;
314275970Scy	struct addrinfo *	ai_res;
315275970Scy	struct addrinfo *	ai;
316275970Scy	struct addrinfo *	serialized_ai;
317275970Scy	size_t			canons_octets;
318275970Scy	size_t			this_octets;
319275970Scy	size_t			resp_octets;
320275970Scy	char *			cp;
321275970Scy	time_t			time_now;
322275970Scy
323275970Scy	gai_req = (void *)((char *)req + sizeof(*req));
324275970Scy	node = (char *)gai_req + sizeof(*gai_req);
325275970Scy	service = node + gai_req->nodesize;
326275970Scy
327275970Scy	worker_ctx = get_worker_context(c, gai_req->dns_idx);
328275970Scy	scheduled_sleep(gai_req->scheduled, gai_req->earliest,
329275970Scy			worker_ctx);
330275970Scy	reload_resolv_conf(worker_ctx);
331275970Scy
332275970Scy	/*
333275970Scy	 * Take a shot at the final size, better to overestimate
334275970Scy	 * at first and then realloc to a smaller size.
335275970Scy	 */
336275970Scy
337275970Scy	resp_octets = sizeof(*resp) + sizeof(*gai_resp) +
338275970Scy		      16 * (sizeof(struct addrinfo) +
339275970Scy			    sizeof(sockaddr_u)) +
340275970Scy		      256;
341275970Scy	resp = emalloc_zero(resp_octets);
342275970Scy	gai_resp = (void *)(resp + 1);
343275970Scy
344275970Scy	TRACE(2, ("blocking_getaddrinfo given node %s serv %s fam %d flags %x\n",
345275970Scy		  node, service, gai_req->hints.ai_family,
346275970Scy		  gai_req->hints.ai_flags));
347275970Scy#ifdef DEBUG
348275970Scy	if (debug >= 2)
349275970Scy		fflush(stdout);
350275970Scy#endif
351275970Scy	ai_res = NULL;
352275970Scy	gai_resp->retcode = getaddrinfo(node, service, &gai_req->hints,
353275970Scy					&ai_res);
354275970Scy	gai_resp->retry = gai_req->retry;
355275970Scy#ifdef EAI_SYSTEM
356275970Scy	if (EAI_SYSTEM == gai_resp->retcode)
357275970Scy		gai_resp->gai_errno = errno;
358275970Scy#endif
359275970Scy	canons_octets = 0;
360275970Scy
361275970Scy	if (0 == gai_resp->retcode) {
362275970Scy		ai = ai_res;
363275970Scy		while (NULL != ai) {
364275970Scy			gai_resp->ai_count++;
365275970Scy			if (ai->ai_canonname)
366275970Scy				canons_octets += strlen(ai->ai_canonname) + 1;
367275970Scy			ai = ai->ai_next;
368275970Scy		}
369275970Scy		/*
370275970Scy		 * If this query succeeded only after retrying, DNS may have
371275970Scy		 * just become responsive.  Ignore previously-scheduled
372275970Scy		 * retry sleeps once for each pending request, similar to
373275970Scy		 * the way scheduled_sleep() does when its worker_sleep()
374275970Scy		 * is interrupted.
375275970Scy		 */
376275970Scy		if (gai_resp->retry > INITIAL_DNS_RETRY) {
377275970Scy			time_now = time(NULL);
378275970Scy			worker_ctx->ignore_scheduled_before = time_now;
379275970Scy			TRACE(1, ("DNS success after retry, ignoring sleeps scheduled before now (%s)\n",
380275970Scy				  humantime(time_now)));
381275970Scy		}
382275970Scy	}
383275970Scy
384275970Scy	/*
385275970Scy	 * Our response consists of a header, followed by ai_count
386275970Scy	 * addrinfo structs followed by ai_count sockaddr_storage
387275970Scy	 * structs followed by the canonical names.
388275970Scy	 */
389275970Scy	gai_resp->octets = sizeof(*gai_resp)
390275970Scy			    + gai_resp->ai_count
391275970Scy				* (sizeof(gai_req->hints)
392275970Scy				   + sizeof(sockaddr_u))
393275970Scy			    + canons_octets;
394275970Scy
395275970Scy	resp_octets = sizeof(*resp) + gai_resp->octets;
396275970Scy	resp = erealloc(resp, resp_octets);
397275970Scy	gai_resp = (void *)(resp + 1);
398275970Scy
399275970Scy	/* cp serves as our current pointer while serializing */
400275970Scy	cp = (void *)(gai_resp + 1);
401275970Scy	canons_octets = 0;
402275970Scy
403275970Scy	if (0 == gai_resp->retcode) {
404275970Scy		ai = ai_res;
405275970Scy		while (NULL != ai) {
406275970Scy			memcpy(cp, ai, sizeof(*ai));
407275970Scy			serialized_ai = (void *)cp;
408275970Scy			cp += sizeof(*ai);
409275970Scy
410275970Scy			/* transform ai_canonname into offset */
411275970Scy			if (NULL != serialized_ai->ai_canonname) {
412275970Scy				serialized_ai->ai_canonname = (char *)canons_octets;
413275970Scy				canons_octets += strlen(ai->ai_canonname) + 1;
414275970Scy			}
415275970Scy
416275970Scy			/* leave fixup of ai_addr pointer for receiver */
417275970Scy
418275970Scy			ai = ai->ai_next;
419275970Scy		}
420275970Scy
421275970Scy		ai = ai_res;
422275970Scy		while (NULL != ai) {
423275970Scy			NTP_INSIST(ai->ai_addrlen <= sizeof(sockaddr_u));
424275970Scy			memcpy(cp, ai->ai_addr, ai->ai_addrlen);
425275970Scy			cp += sizeof(sockaddr_u);
426275970Scy
427275970Scy			ai = ai->ai_next;
428275970Scy		}
429275970Scy
430275970Scy		ai = ai_res;
431275970Scy		while (NULL != ai) {
432275970Scy			if (NULL != ai->ai_canonname) {
433275970Scy				this_octets = strlen(ai->ai_canonname) + 1;
434275970Scy				memcpy(cp, ai->ai_canonname, this_octets);
435275970Scy				cp += this_octets;
436275970Scy			}
437275970Scy
438275970Scy			ai = ai->ai_next;
439275970Scy		}
440275970Scy		freeaddrinfo(ai_res);
441275970Scy	}
442275970Scy
443275970Scy	/*
444275970Scy	 * make sure our walk and earlier calc match
445275970Scy	 */
446275970Scy	DEBUG_INSIST((size_t)(cp - (char *)resp) == resp_octets);
447275970Scy
448275970Scy	if (queue_blocking_response(c, resp, resp_octets, req)) {
449275970Scy		msyslog(LOG_ERR, "blocking_getaddrinfo can not queue response");
450275970Scy		return -1;
451275970Scy	}
452275970Scy
453275970Scy	return 0;
454275970Scy}
455275970Scy
456275970Scy
457275970Scystatic void
458275970Scygetaddrinfo_sometime_complete(
459275970Scy	blocking_work_req	rtype,
460275970Scy	void *			context,
461275970Scy	size_t			respsize,
462275970Scy	void *			resp
463275970Scy	)
464275970Scy{
465275970Scy	blocking_gai_req *	gai_req;
466275970Scy	blocking_gai_resp *	gai_resp;
467275970Scy	dnschild_ctx *		child_ctx;
468275970Scy	struct addrinfo *	ai;
469275970Scy	struct addrinfo *	next_ai;
470275970Scy	sockaddr_u *		psau;
471275970Scy	char *			node;
472275970Scy	char *			service;
473275970Scy	char *			canon_start;
474275970Scy	time_t			time_now;
475275970Scy	int			again;
476275970Scy	int			af;
477275970Scy	const char *		fam_spec;
478275970Scy	int			i;
479275970Scy
480275970Scy	gai_req = context;
481275970Scy	gai_resp = resp;
482275970Scy
483275970Scy	DEBUG_REQUIRE(BLOCKING_GETADDRINFO == rtype);
484275970Scy	DEBUG_REQUIRE(respsize == gai_resp->octets);
485275970Scy
486275970Scy	node = (char *)gai_req + sizeof(*gai_req);
487275970Scy	service = node + gai_req->nodesize;
488275970Scy
489275970Scy	child_ctx = dnschild_contexts[gai_req->dns_idx];
490275970Scy
491275970Scy	if (0 == gai_resp->retcode) {
492275970Scy		/*
493275970Scy		 * If this query succeeded only after retrying, DNS may have
494275970Scy		 * just become responsive.
495275970Scy		 */
496275970Scy		if (gai_resp->retry > INITIAL_DNS_RETRY) {
497275970Scy			time_now = time(NULL);
498275970Scy			child_ctx->next_dns_timeslot = time_now;
499275970Scy			TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n",
500275970Scy				  gai_req->dns_idx, humantime(time_now)));
501275970Scy		}
502275970Scy	} else {
503275970Scy		again = should_retry_dns(gai_resp->retcode,
504275970Scy					 gai_resp->gai_errno);
505275970Scy		/*
506275970Scy		 * exponential backoff of DNS retries to 64s
507275970Scy		 */
508275970Scy		if (gai_req->retry > 0 && again) {
509275970Scy			/* log the first retry only */
510275970Scy			if (INITIAL_DNS_RETRY == gai_req->retry)
511275970Scy				NLOG(NLOG_SYSINFO) {
512275970Scy					af = gai_req->hints.ai_family;
513275970Scy					fam_spec = (AF_INET6 == af)
514275970Scy						       ? " (AAAA)"
515275970Scy						       : (AF_INET == af)
516275970Scy							     ? " (A)"
517275970Scy							     : "";
518275970Scy#ifdef EAI_SYSTEM
519275970Scy					if (EAI_SYSTEM == gai_resp->retcode) {
520275970Scy						errno = gai_resp->gai_errno;
521275970Scy						msyslog(LOG_INFO,
522275970Scy							"retrying DNS %s%s: EAI_SYSTEM %d: %m",
523275970Scy							node, fam_spec,
524275970Scy							gai_resp->gai_errno);
525275970Scy					} else
526275970Scy#endif
527275970Scy						msyslog(LOG_INFO,
528275970Scy							"retrying DNS %s%s: %s (%d)",
529275970Scy							node, fam_spec,
530275970Scy							gai_strerror(gai_resp->retcode),
531275970Scy							gai_resp->retcode);
532275970Scy				}
533275970Scy			manage_dns_retry_interval(&gai_req->scheduled,
534275970Scy			    &gai_req->earliest, &gai_req->retry,
535275970Scy			    &child_ctx->next_dns_timeslot);
536275970Scy			if (!queue_blocking_request(
537275970Scy					BLOCKING_GETADDRINFO,
538275970Scy					gai_req,
539275970Scy					gai_req->octets,
540275970Scy					&getaddrinfo_sometime_complete,
541275970Scy					gai_req))
542275970Scy				return;
543275970Scy			else
544275970Scy				msyslog(LOG_ERR,
545275970Scy					"unable to retry hostname %s",
546275970Scy					node);
547275970Scy		}
548275970Scy	}
549275970Scy
550275970Scy	/*
551275970Scy	 * fixup pointers in returned addrinfo array
552275970Scy	 */
553275970Scy	ai = (void *)((char *)gai_resp + sizeof(*gai_resp));
554275970Scy	next_ai = NULL;
555275970Scy	for (i = gai_resp->ai_count - 1; i >= 0; i--) {
556275970Scy		ai[i].ai_next = next_ai;
557275970Scy		next_ai = &ai[i];
558275970Scy	}
559275970Scy
560275970Scy	psau = (void *)((char *)ai + gai_resp->ai_count * sizeof(*ai));
561275970Scy	canon_start = (char *)psau + gai_resp->ai_count * sizeof(*psau);
562275970Scy
563275970Scy	for (i = 0; i < gai_resp->ai_count; i++) {
564275970Scy		if (NULL != ai[i].ai_addr)
565275970Scy			ai[i].ai_addr = &psau->sa;
566275970Scy		psau++;
567275970Scy		if (NULL != ai[i].ai_canonname)
568275970Scy			ai[i].ai_canonname += (size_t)canon_start;
569275970Scy	}
570275970Scy
571275970Scy	NTP_ENSURE((char *)psau == canon_start);
572275970Scy
573275970Scy	if (!gai_resp->ai_count)
574275970Scy		ai = NULL;
575275970Scy
576275970Scy	(*gai_req->callback)(gai_resp->retcode, gai_resp->gai_errno,
577275970Scy			     gai_req->context, node, service,
578275970Scy			     &gai_req->hints, ai);
579275970Scy
580275970Scy	free(gai_req);
581275970Scy	/* gai_resp is part of block freed by process_blocking_resp() */
582275970Scy}
583275970Scy
584275970Scy
585275970Scy#ifdef TEST_BLOCKING_WORKER
586275970Scyvoid 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)
587275970Scy{
588275970Scy	sockaddr_u addr;
589275970Scy
590275970Scy	if (rescode) {
591275970Scy		TRACE(1, ("gai_test_callback context %p error rescode %d %s serv %s\n",
592275970Scy			  context, rescode, name, service));
593275970Scy		return;
594275970Scy	}
595275970Scy	while (!rescode && NULL != ai_res) {
596275970Scy		ZERO_SOCK(&addr);
597275970Scy		memcpy(&addr, ai_res->ai_addr, ai_res->ai_addrlen);
598275970Scy		TRACE(1, ("ctx %p fam %d addr %s canon '%s' type %s at %p ai_addr %p ai_next %p\n",
599275970Scy			  context,
600275970Scy			  AF(&addr),
601275970Scy			  stoa(&addr),
602275970Scy			  (ai_res->ai_canonname)
603275970Scy			      ? ai_res->ai_canonname
604275970Scy			      : "",
605275970Scy			  (SOCK_DGRAM == ai_res->ai_socktype)
606275970Scy			      ? "DGRAM"
607275970Scy			      : (SOCK_STREAM == ai_res->ai_socktype)
608275970Scy				    ? "STREAM"
609275970Scy				    : "(other)",
610275970Scy			  ai_res,
611275970Scy			  ai_res->ai_addr,
612275970Scy			  ai_res->ai_next));
613275970Scy
614275970Scy		getnameinfo_sometime((sockaddr_u *)ai_res->ai_addr, 128, 32, 0, gni_test_callback, context);
615275970Scy
616275970Scy		ai_res = ai_res->ai_next;
617275970Scy	}
618275970Scy}
619275970Scy#endif	/* TEST_BLOCKING_WORKER */
620275970Scy
621275970Scy
622275970Scyint
623275970Scygetnameinfo_sometime(
624275970Scy	sockaddr_u *		psau,
625275970Scy	size_t			hostoctets,
626275970Scy	size_t			servoctets,
627275970Scy	int			flags,
628275970Scy	gni_sometime_callback	callback,
629275970Scy	void *			context
630275970Scy	)
631275970Scy{
632275970Scy	blocking_gni_req *	gni_req;
633275970Scy	u_int			idx;
634275970Scy	dnschild_ctx *		child_ctx;
635275970Scy	time_t			time_now;
636275970Scy
637275970Scy	NTP_REQUIRE(hostoctets);
638275970Scy	NTP_REQUIRE(hostoctets + servoctets < 1024);
639275970Scy
640275970Scy	idx = get_dnschild_ctx();
641275970Scy	child_ctx = dnschild_contexts[idx];
642275970Scy
643275970Scy	gni_req = emalloc_zero(sizeof(*gni_req));
644275970Scy
645275970Scy	gni_req->octets = sizeof(*gni_req);
646275970Scy	gni_req->dns_idx = idx;
647275970Scy	time_now = time(NULL);
648275970Scy	gni_req->scheduled = time_now;
649275970Scy	gni_req->earliest = max(time_now, child_ctx->next_dns_timeslot);
650275970Scy	child_ctx->next_dns_timeslot = gni_req->earliest;
651275970Scy	memcpy(&gni_req->socku, psau, SOCKLEN(psau));
652275970Scy	gni_req->hostoctets = hostoctets;
653275970Scy	gni_req->servoctets = servoctets;
654275970Scy	gni_req->flags = flags;
655275970Scy	gni_req->retry = INITIAL_DNS_RETRY;
656275970Scy	gni_req->callback = callback;
657275970Scy	gni_req->context = context;
658275970Scy
659275970Scy	if (queue_blocking_request(
660275970Scy		BLOCKING_GETNAMEINFO,
661275970Scy		gni_req,
662275970Scy		sizeof(*gni_req),
663275970Scy		&getnameinfo_sometime_complete,
664275970Scy		gni_req)) {
665275970Scy
666275970Scy		msyslog(LOG_ERR, "unable to queue getnameinfo request");
667275970Scy		errno = EFAULT;
668275970Scy		return -1;
669275970Scy	}
670275970Scy
671275970Scy	return 0;
672275970Scy}
673275970Scy
674275970Scy
675275970Scyint
676275970Scyblocking_getnameinfo(
677275970Scy	blocking_child *	c,
678275970Scy	blocking_pipe_header *	req
679275970Scy	)
680275970Scy{
681275970Scy	blocking_gni_req *	gni_req;
682275970Scy	dnsworker_ctx *		worker_ctx;
683275970Scy	blocking_pipe_header *	resp;
684275970Scy	blocking_gni_resp *	gni_resp;
685275970Scy	size_t			octets;
686275970Scy	size_t			resp_octets;
687275970Scy	char *			service;
688275970Scy	char *			cp;
689275970Scy	int			rc;
690275970Scy	time_t			time_now;
691280849Scy	char			host[1024];
692275970Scy
693275970Scy	gni_req = (void *)((char *)req + sizeof(*req));
694275970Scy
695275970Scy	octets = gni_req->hostoctets + gni_req->servoctets;
696275970Scy
697275970Scy	/*
698275970Scy	 * Some alloca() implementations are fragile regarding
699275970Scy	 * large allocations.  We only need room for the host
700275970Scy	 * and service names.
701275970Scy	 */
702280849Scy	NTP_REQUIRE(octets < sizeof(host));
703275970Scy	service = host + gni_req->hostoctets;
704275970Scy
705275970Scy	worker_ctx = get_worker_context(c, gni_req->dns_idx);
706275970Scy	scheduled_sleep(gni_req->scheduled, gni_req->earliest,
707275970Scy			worker_ctx);
708275970Scy	reload_resolv_conf(worker_ctx);
709275970Scy
710275970Scy	/*
711275970Scy	 * Take a shot at the final size, better to overestimate
712275970Scy	 * then realloc to a smaller size.
713275970Scy	 */
714275970Scy
715275970Scy	resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets;
716275970Scy	resp = emalloc_zero(resp_octets);
717275970Scy	gni_resp = (void *)((char *)resp + sizeof(*resp));
718275970Scy
719275970Scy	TRACE(2, ("blocking_getnameinfo given addr %s flags 0x%x hostlen %lu servlen %lu\n",
720275970Scy		  stoa(&gni_req->socku), gni_req->flags,
721275970Scy		  (u_long)gni_req->hostoctets, (u_long)gni_req->servoctets));
722275970Scy
723275970Scy	gni_resp->retcode = getnameinfo(&gni_req->socku.sa,
724275970Scy					SOCKLEN(&gni_req->socku),
725275970Scy					host,
726275970Scy					gni_req->hostoctets,
727275970Scy					service,
728275970Scy					gni_req->servoctets,
729275970Scy					gni_req->flags);
730275970Scy	gni_resp->retry = gni_req->retry;
731275970Scy#ifdef EAI_SYSTEM
732275970Scy	if (EAI_SYSTEM == gni_resp->retcode)
733275970Scy		gni_resp->gni_errno = errno;
734275970Scy#endif
735275970Scy
736275970Scy	if (0 != gni_resp->retcode) {
737275970Scy		gni_resp->hostoctets = 0;
738275970Scy		gni_resp->servoctets = 0;
739275970Scy	} else {
740275970Scy		gni_resp->hostoctets = strlen(host) + 1;
741275970Scy		gni_resp->servoctets = strlen(service) + 1;
742275970Scy		/*
743275970Scy		 * If this query succeeded only after retrying, DNS may have
744275970Scy		 * just become responsive.  Ignore previously-scheduled
745275970Scy		 * retry sleeps once for each pending request, similar to
746275970Scy		 * the way scheduled_sleep() does when its worker_sleep()
747275970Scy		 * is interrupted.
748275970Scy		 */
749275970Scy		if (gni_req->retry > INITIAL_DNS_RETRY) {
750275970Scy			time_now = time(NULL);
751275970Scy			worker_ctx->ignore_scheduled_before = time_now;
752275970Scy			TRACE(1, ("DNS success after retrying, ignoring sleeps scheduled before now (%s)\n",
753275970Scy				humantime(time_now)));
754275970Scy		}
755275970Scy	}
756275970Scy	octets = gni_resp->hostoctets + gni_resp->servoctets;
757275970Scy	/*
758275970Scy	 * Our response consists of a header, followed by the host and
759275970Scy	 * service strings, each null-terminated.
760275970Scy	 */
761275970Scy	resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets;
762275970Scy
763275970Scy	resp = erealloc(resp, resp_octets);
764275970Scy	gni_resp = (void *)(resp + 1);
765275970Scy
766275970Scy	gni_resp->octets = sizeof(*gni_resp) + octets;
767275970Scy
768275970Scy	/* cp serves as our current pointer while serializing */
769275970Scy	cp = (void *)(gni_resp + 1);
770275970Scy
771275970Scy	if (0 == gni_resp->retcode) {
772275970Scy		memcpy(cp, host, gni_resp->hostoctets);
773275970Scy		cp += gni_resp->hostoctets;
774275970Scy		memcpy(cp, service, gni_resp->servoctets);
775275970Scy		cp += gni_resp->servoctets;
776275970Scy	}
777275970Scy
778275970Scy	NTP_INSIST((size_t)(cp - (char *)resp) == resp_octets);
779275970Scy	NTP_INSIST(resp_octets - sizeof(*resp) == gni_resp->octets);
780275970Scy
781275970Scy	rc = queue_blocking_response(c, resp, resp_octets, req);
782275970Scy	if (rc)
783275970Scy		msyslog(LOG_ERR, "blocking_getnameinfo unable to queue response");
784275970Scy	return rc;
785275970Scy}
786275970Scy
787275970Scy
788275970Scystatic void
789275970Scygetnameinfo_sometime_complete(
790275970Scy	blocking_work_req	rtype,
791275970Scy	void *			context,
792275970Scy	size_t			respsize,
793275970Scy	void *			resp
794275970Scy	)
795275970Scy{
796275970Scy	blocking_gni_req *	gni_req;
797275970Scy	blocking_gni_resp *	gni_resp;
798275970Scy	dnschild_ctx *		child_ctx;
799275970Scy	char *			host;
800275970Scy	char *			service;
801275970Scy	time_t			time_now;
802275970Scy	int			again;
803275970Scy
804275970Scy	gni_req = context;
805275970Scy	gni_resp = resp;
806275970Scy
807275970Scy	DEBUG_REQUIRE(BLOCKING_GETNAMEINFO == rtype);
808275970Scy	DEBUG_REQUIRE(respsize == gni_resp->octets);
809275970Scy
810275970Scy	child_ctx = dnschild_contexts[gni_req->dns_idx];
811275970Scy
812275970Scy	if (0 == gni_resp->retcode) {
813275970Scy		/*
814275970Scy		 * If this query succeeded only after retrying, DNS may have
815275970Scy		 * just become responsive.
816275970Scy		 */
817275970Scy		if (gni_resp->retry > INITIAL_DNS_RETRY) {
818275970Scy			time_now = time(NULL);
819275970Scy			child_ctx->next_dns_timeslot = time_now;
820275970Scy			TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n",
821275970Scy				  gni_req->dns_idx, humantime(time_now)));
822275970Scy		}
823275970Scy	} else {
824275970Scy		again = should_retry_dns(gni_resp->retcode, gni_resp->gni_errno);
825275970Scy		/*
826275970Scy		 * exponential backoff of DNS retries to 64s
827275970Scy		 */
828275970Scy		if (gni_req->retry > 0)
829275970Scy			manage_dns_retry_interval(&gni_req->scheduled,
830275970Scy			    &gni_req->earliest, &gni_req->retry,
831275970Scy			    &child_ctx->next_dns_timeslot);
832275970Scy
833275970Scy		if (gni_req->retry > 0 && again) {
834275970Scy			if (!queue_blocking_request(
835275970Scy				BLOCKING_GETNAMEINFO,
836275970Scy				gni_req,
837275970Scy				gni_req->octets,
838275970Scy				&getnameinfo_sometime_complete,
839275970Scy				gni_req))
840275970Scy				return;
841275970Scy
842275970Scy			msyslog(LOG_ERR, "unable to retry reverse lookup of %s", stoa(&gni_req->socku));
843275970Scy		}
844275970Scy	}
845275970Scy
846275970Scy	if (!gni_resp->hostoctets) {
847275970Scy		host = NULL;
848275970Scy		service = NULL;
849275970Scy	} else {
850275970Scy		host = (char *)gni_resp + sizeof(*gni_resp);
851275970Scy		service = (gni_resp->servoctets)
852275970Scy			      ? host + gni_resp->hostoctets
853275970Scy			      : NULL;
854275970Scy	}
855275970Scy
856275970Scy	(*gni_req->callback)(gni_resp->retcode, gni_resp->gni_errno,
857275970Scy			     &gni_req->socku, gni_req->flags, host,
858275970Scy			     service, gni_req->context);
859275970Scy
860275970Scy	free(gni_req);
861275970Scy	/* gni_resp is part of block freed by process_blocking_resp() */
862275970Scy}
863275970Scy
864275970Scy
865275970Scy#ifdef TEST_BLOCKING_WORKER
866275970Scyvoid gni_test_callback(int rescode, int gni_errno, sockaddr_u *psau, int flags, const char *host, const char *service, void *context)
867275970Scy{
868275970Scy	if (!rescode)
869275970Scy		TRACE(1, ("gni_test_callback got host '%s' serv '%s' for addr %s context %p\n",
870275970Scy			  host, service, stoa(psau), context));
871275970Scy	else
872275970Scy		TRACE(1, ("gni_test_callback context %p rescode %d gni_errno %d flags 0x%x addr %s\n",
873275970Scy			  context, rescode, gni_errno, flags, stoa(psau)));
874275970Scy}
875275970Scy#endif	/* TEST_BLOCKING_WORKER */
876275970Scy
877275970Scy
878275970Scy#ifdef HAVE_RES_INIT
879275970Scystatic void
880275970Scyreload_resolv_conf(
881275970Scy	dnsworker_ctx *	worker_ctx
882275970Scy	)
883275970Scy{
884275970Scy	time_t	time_now;
885275970Scy
886275970Scy	/*
887275970Scy	 * This is ad-hoc.  Reload /etc/resolv.conf once per minute
888275970Scy	 * to pick up on changes from the DHCP client.  [Bug 1226]
889275970Scy	 * When using threads for the workers, this needs to happen
890275970Scy	 * only once per minute process-wide.
891275970Scy	 */
892275970Scy	time_now = time(NULL);
893275970Scy# ifdef WORK_THREAD
894275970Scy	worker_ctx->next_res_init = next_res_init;
895275970Scy# endif
896275970Scy	if (worker_ctx->next_res_init <= time_now) {
897275970Scy		if (worker_ctx->next_res_init != 0)
898275970Scy			res_init();
899275970Scy		worker_ctx->next_res_init = time_now + 60;
900275970Scy# ifdef WORK_THREAD
901275970Scy		next_res_init = worker_ctx->next_res_init;
902275970Scy# endif
903275970Scy	}
904275970Scy}
905275970Scy#endif	/* HAVE_RES_INIT */
906275970Scy
907275970Scy
908275970Scystatic u_int
909275970Scyreserve_dnschild_ctx(void)
910275970Scy{
911275970Scy	const size_t	ps = sizeof(dnschild_contexts[0]);
912275970Scy	const size_t	cs = sizeof(*dnschild_contexts[0]);
913275970Scy	u_int		c;
914275970Scy	u_int		new_alloc;
915275970Scy	size_t		octets;
916275970Scy	size_t		new_octets;
917275970Scy
918275970Scy	c = 0;
919275970Scy	while (TRUE) {
920275970Scy		for ( ; c < dnschild_contexts_alloc; c++) {
921275970Scy			if (NULL == dnschild_contexts[c]) {
922275970Scy				dnschild_contexts[c] = emalloc_zero(cs);
923275970Scy
924275970Scy				return c;
925275970Scy			}
926275970Scy		}
927275970Scy		new_alloc = dnschild_contexts_alloc + 20;
928275970Scy		new_octets = new_alloc * ps;
929275970Scy		octets = dnschild_contexts_alloc * ps;
930275970Scy		dnschild_contexts = erealloc_zero(dnschild_contexts,
931275970Scy						  new_octets, octets);
932275970Scy		dnschild_contexts_alloc = new_alloc;
933275970Scy	}
934275970Scy}
935275970Scy
936275970Scy
937275970Scystatic u_int
938275970Scyget_dnschild_ctx(void)
939275970Scy{
940275970Scy	static u_int	shared_ctx = UINT_MAX;
941275970Scy
942275970Scy	if (worker_per_query)
943275970Scy		return reserve_dnschild_ctx();
944275970Scy
945275970Scy	if (UINT_MAX == shared_ctx)
946275970Scy		shared_ctx = reserve_dnschild_ctx();
947275970Scy
948275970Scy	return shared_ctx;
949275970Scy}
950275970Scy
951275970Scy
952275970Scystatic void
953275970Scyalloc_dnsworker_context(
954275970Scy	u_int idx
955275970Scy	)
956275970Scy{
957275970Scy	const size_t worker_context_sz = sizeof(*dnsworker_contexts[0]);
958275970Scy
959275970Scy	REQUIRE(NULL == dnsworker_contexts[idx]);
960275970Scy	dnsworker_contexts[idx] = emalloc_zero(worker_context_sz);
961275970Scy}
962275970Scy
963275970Scy
964275970Scystatic dnsworker_ctx *
965275970Scyget_worker_context(
966275970Scy	blocking_child *	c,
967275970Scy	u_int			idx
968275970Scy	)
969275970Scy{
970275970Scy	static size_t	ps = sizeof(dnsworker_contexts[0]);
971275970Scy	u_int	min_new_alloc;
972275970Scy	u_int	new_alloc;
973275970Scy	size_t	octets;
974275970Scy	size_t	new_octets;
975275970Scy
976275970Scy	if (dnsworker_contexts_alloc <= idx) {
977275970Scy		min_new_alloc = 1 + idx;
978275970Scy		/* round new_alloc up to nearest multiple of 4 */
979275970Scy		new_alloc = (min_new_alloc + 4) & ~(4 - 1);
980275970Scy		new_octets = new_alloc * ps;
981275970Scy		octets = dnsworker_contexts_alloc * ps;
982275970Scy		dnsworker_contexts = erealloc_zero(dnsworker_contexts,
983275970Scy						   new_octets, octets);
984275970Scy		dnsworker_contexts_alloc = new_alloc;
985275970Scy	}
986275970Scy
987275970Scy	if (NULL == dnsworker_contexts[idx])
988275970Scy		alloc_dnsworker_context(idx);
989275970Scy	ZERO(*dnsworker_contexts[idx]);
990275970Scy	dnsworker_contexts[idx]->c = c;
991275970Scy
992275970Scy	return dnsworker_contexts[idx];
993275970Scy}
994275970Scy
995275970Scy
996275970Scystatic void
997275970Scyscheduled_sleep(
998275970Scy	time_t		scheduled,
999275970Scy	time_t		earliest,
1000275970Scy	dnsworker_ctx *	worker_ctx
1001275970Scy	)
1002275970Scy{
1003275970Scy	time_t now;
1004275970Scy
1005275970Scy	if (scheduled < worker_ctx->ignore_scheduled_before) {
1006275970Scy		TRACE(1, ("ignoring sleep until %s scheduled at %s (before %s)\n",
1007275970Scy			  humantime(earliest), humantime(scheduled),
1008275970Scy			  humantime(worker_ctx->ignore_scheduled_before)));
1009275970Scy		return;
1010275970Scy	}
1011275970Scy
1012275970Scy	now = time(NULL);
1013275970Scy
1014275970Scy	if (now < earliest) {
1015275970Scy		TRACE(1, ("sleep until %s scheduled at %s (>= %s)\n",
1016275970Scy			  humantime(earliest), humantime(scheduled),
1017275970Scy			  humantime(worker_ctx->ignore_scheduled_before)));
1018275970Scy		if (-1 == worker_sleep(worker_ctx->c, earliest - now)) {
1019275970Scy			/* our sleep was interrupted */
1020275970Scy			now = time(NULL);
1021275970Scy			worker_ctx->ignore_scheduled_before = now;
1022275970Scy#ifdef HAVE_RES_INIT
1023275970Scy			worker_ctx->next_res_init = now + 60;
1024275970Scy			next_res_init = worker_ctx->next_res_init;
1025275970Scy			res_init();
1026275970Scy#endif
1027275970Scy			TRACE(1, ("sleep interrupted by daemon, ignoring sleeps scheduled before now (%s)\n",
1028275970Scy				  humantime(worker_ctx->ignore_scheduled_before)));
1029275970Scy		}
1030275970Scy	}
1031275970Scy}
1032275970Scy
1033275970Scy
1034275970Scy/*
1035275970Scy * manage_dns_retry_interval is a helper used by
1036275970Scy * getaddrinfo_sometime_complete and getnameinfo_sometime_complete
1037275970Scy * to calculate the new retry interval and schedule the next query.
1038275970Scy */
1039275970Scystatic void
1040275970Scymanage_dns_retry_interval(
1041275970Scy	time_t *	pscheduled,
1042275970Scy	time_t *	pwhen,
1043275970Scy	int *		pretry,
1044275970Scy	time_t *	pnext_timeslot
1045275970Scy	)
1046275970Scy{
1047275970Scy	time_t	now;
1048275970Scy	time_t	when;
1049275970Scy	int	retry;
1050275970Scy
1051275970Scy	now = time(NULL);
1052275970Scy	retry = *pretry;
1053275970Scy	when = max(now + retry, *pnext_timeslot);
1054275970Scy	*pnext_timeslot = when;
1055275970Scy	retry = min(64, retry << 1);
1056275970Scy
1057275970Scy	*pscheduled = now;
1058275970Scy	*pwhen = when;
1059275970Scy	*pretry = retry;
1060275970Scy}
1061275970Scy
1062275970Scy/*
1063275970Scy * should_retry_dns is a helper used by getaddrinfo_sometime_complete
1064275970Scy * and getnameinfo_sometime_complete which implements ntpd's DNS retry
1065275970Scy * policy.
1066275970Scy */
1067275970Scystatic int
1068275970Scyshould_retry_dns(
1069275970Scy	int	rescode,
1070275970Scy	int	res_errno
1071275970Scy	)
1072275970Scy{
1073275970Scy	static int	eai_again_seen;
1074275970Scy	int		again;
1075275970Scy#if defined (EAI_SYSTEM) && defined(DEBUG)
1076275970Scy	char		msg[256];
1077275970Scy#endif
1078275970Scy
1079275970Scy	/*
1080275970Scy	 * If the resolver failed, see if the failure is
1081275970Scy	 * temporary. If so, return success.
1082275970Scy	 */
1083275970Scy	again = 0;
1084275970Scy
1085275970Scy	switch (rescode) {
1086275970Scy
1087275970Scy	case EAI_FAIL:
1088275970Scy		again = 1;
1089275970Scy		break;
1090275970Scy
1091275970Scy	case EAI_AGAIN:
1092275970Scy		again = 1;
1093275970Scy		eai_again_seen = 1;		/* [Bug 1178] */
1094275970Scy		break;
1095275970Scy
1096275970Scy	case EAI_NONAME:
1097275970Scy#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
1098275970Scy	case EAI_NODATA:
1099275970Scy#endif
1100275970Scy		again = !eai_again_seen;	/* [Bug 1178] */
1101275970Scy		break;
1102275970Scy
1103275970Scy#ifdef EAI_SYSTEM
1104275970Scy	case EAI_SYSTEM:
1105275970Scy		/*
1106275970Scy		 * EAI_SYSTEM means the real error is in errno.  We should be more
1107275970Scy		 * discriminating about which errno values require retrying, but
1108275970Scy		 * this matches existing behavior.
1109275970Scy		 */
1110275970Scy		again = 1;
1111275970Scy# ifdef DEBUG
1112275970Scy		errno_to_str(res_errno, msg, sizeof(msg));
1113275970Scy		TRACE(1, ("intres: EAI_SYSTEM errno %d (%s) means try again, right?\n",
1114275970Scy			  res_errno, msg));
1115275970Scy# endif
1116275970Scy		break;
1117275970Scy#endif
1118275970Scy	}
1119275970Scy
1120275970Scy	TRACE(2, ("intres: resolver returned: %s (%d), %sretrying\n",
1121275970Scy		  gai_strerror(rescode), rescode, again ? "" : "not "));
1122275970Scy
1123275970Scy	return again;
1124275970Scy}
1125275970Scy
1126275970Scy#else	/* !WORKER follows */
1127275970Scyint ntp_intres_nonempty_compilation_unit;
1128275970Scy#endif
1129