2 * ntp_intres.c - Implements a generic blocking worker child or thread,
3 * initially to provide a nonblocking solution for DNS
4 * name to address lookups available with getaddrinfo().
6 * This is a new implementation as of 2009 sharing the filename and
7 * very little else with the prior implementation, which used a
8 * temporary file to receive a single set of requests from the parent,
9 * and a NTP mode 7 authenticated request to push back responses.
11 * A primary goal in rewriting this code was the need to support the
12 * pool configuration directive's requirement to retrieve multiple
13 * addresses resolving a single name, which has previously been
14 * satisfied with blocking resolver calls from the ntpd mainline code.
16 * A secondary goal is to provide a generic mechanism for other
17 * blocking operations to be delegated to a worker using a common
18 * model for both Unix and Windows ntpd. ntp_worker.c, work_fork.c,
19 * and work_thread.c implement the generic mechanism. This file
20 * implements the two current consumers, getaddrinfo_sometime() and the
21 * presently unused getnameinfo_sometime().
23 * Both routines deliver results to a callback and manage memory
24 * allocation, meaning there is no freeaddrinfo_sometime().
26 * The initial implementation for Unix uses a pair of unidirectional
27 * pipes, one each for requests and responses, connecting the forked
28 * blocking child worker with the ntpd mainline. The threaded code
29 * uses arrays of pointers to queue requests and responses.
31 * The parent drives the process, including scheduling sleeps between
34 * Memory is managed differently for a child process, which mallocs
35 * request buffers to read from the pipe into, whereas the threaded
36 * code mallocs a copy of the request to hand off to the worker via
37 * the queueing array. The resulting request buffer is free()d by
38 * platform-independent code. A wrinkle is the request needs to be
39 * available to the requestor during response processing.
41 * Response memory allocation is also platform-dependent. With a
42 * separate process and pipes, the response is free()d after being
43 * written to the pipe. With threads, the same memory is handed
44 * over and the requestor frees it after processing is completed.
46 * The code should be generalized to support threads on Unix using
47 * much of the same code used for Windows initially.
54 #include "ntp_workimpl.h"
63 #ifdef HAVE_SYS_TYPES_H
64 # include <sys/types.h>
66 #ifdef HAVE_NETINET_IN_H
67 #include <netinet/in.h>
69 #include <arpa/inet.h>
71 #ifdef HAVE_SYS_PARAM_H
72 # include <sys/param.h>
75 #if !defined(HAVE_RES_INIT) && defined(HAVE___RES_INIT)
76 # define HAVE_RES_INIT
79 #if defined(HAVE_RESOLV_H) && defined(HAVE_RES_INIT)
80 # ifdef HAVE_ARPA_NAMESER_H
81 # include <arpa/nameser.h> /* DNS HEADER struct */
87 # ifdef HAVE_INT32_ONLY_WITH_DNS
90 # ifdef HAVE_U_INT32_ONLY_WITH_DNS
96 #include "ntp_debug.h"
97 #include "ntp_malloc.h"
98 #include "ntp_syslog.h"
99 #include "ntp_unixtime.h"
100 #include "ntp_intres.h"
101 #include "intreswork.h"
105 * Following are implementations of getaddrinfo_sometime() and
106 * getnameinfo_sometime(). Each is implemented in three routines:
108 * getaddrinfo_sometime() getnameinfo_sometime()
109 * blocking_getaddrinfo() blocking_getnameinfo()
110 * getaddrinfo_sometime_complete() getnameinfo_sometime_complete()
112 * The first runs in the parent and marshalls (or serializes) request
113 * parameters into a request blob which is processed in the child by
114 * the second routine, blocking_*(), which serializes the results into
115 * a response blob unpacked by the third routine, *_complete(), which
116 * calls the callback routine provided with the request and frees
117 * _request_ memory allocated by the first routine. Response memory
118 * is managed by the code which calls the *_complete routines.
121 /* === typedefs === */
122 typedef struct blocking_gai_req_tag { /* marshalled args */
127 struct addrinfo hints;
129 gai_sometime_callback callback;
135 typedef struct blocking_gai_resp_tag {
139 int gai_errno; /* for EAI_SYSTEM case */
142 * Followed by ai_count struct addrinfo and then ai_count
143 * sockaddr_u and finally the canonical name strings.
147 typedef struct blocking_gni_req_tag {
156 gni_sometime_callback callback;
161 typedef struct blocking_gni_resp_tag {
164 int gni_errno; /* for EAI_SYSTEM case */
169 * Followed by hostoctets bytes of null-terminated host,
170 * then servoctets bytes of null-terminated service.
174 /* per-DNS-worker state in parent */
175 typedef struct dnschild_ctx_tag {
177 time_t next_dns_timeslot;
180 /* per-DNS-worker state in worker */
181 typedef struct dnsworker_ctx_tag {
183 time_t ignore_scheduled_before;
185 time_t next_res_init;
190 /* === variables === */
191 dnschild_ctx ** dnschild_contexts; /* parent */
192 u_int dnschild_contexts_alloc;
193 dnsworker_ctx ** dnsworker_contexts; /* child */
194 u_int dnsworker_contexts_alloc;
197 static time_t next_res_init;
201 /* === forward declarations === */
202 static u_int reserve_dnschild_ctx(void);
203 static u_int get_dnschild_ctx(void);
204 static dnsworker_ctx * get_worker_context(blocking_child *, u_int);
205 static void scheduled_sleep(time_t, time_t,
207 static void manage_dns_retry_interval(time_t *, time_t *,
210 static int should_retry_dns(int, int);
212 static void reload_resolv_conf(dnsworker_ctx *);
214 # define reload_resolv_conf(wc) \
219 static void getaddrinfo_sometime_complete(blocking_work_req,
222 static void getnameinfo_sometime_complete(blocking_work_req,
227 /* === functions === */
229 * getaddrinfo_sometime - uses blocking child to call getaddrinfo then
230 * invokes provided callback completion function.
233 getaddrinfo_sometime(
235 const char * service,
236 const struct addrinfo * hints,
238 gai_sometime_callback callback,
242 blocking_gai_req * gai_req;
244 dnschild_ctx * child_ctx;
250 REQUIRE(NULL != node);
252 REQUIRE(0 == hints->ai_addrlen);
253 REQUIRE(NULL == hints->ai_addr);
254 REQUIRE(NULL == hints->ai_canonname);
255 REQUIRE(NULL == hints->ai_next);
258 idx = get_dnschild_ctx();
259 child_ctx = dnschild_contexts[idx];
261 nodesize = strlen(node) + 1;
262 servsize = strlen(service) + 1;
263 req_size = sizeof(*gai_req) + nodesize + servsize;
265 gai_req = emalloc_zero(req_size);
267 gai_req->octets = req_size;
268 gai_req->dns_idx = idx;
270 gai_req->scheduled = now;
271 gai_req->earliest = max(now, child_ctx->next_dns_timeslot);
272 child_ctx->next_dns_timeslot = gai_req->earliest;
274 gai_req->hints = *hints;
275 gai_req->retry = retry;
276 gai_req->callback = callback;
277 gai_req->context = context;
278 gai_req->nodesize = nodesize;
279 gai_req->servsize = servsize;
281 memcpy((char *)gai_req + sizeof(*gai_req), node, nodesize);
282 memcpy((char *)gai_req + sizeof(*gai_req) + nodesize, service,
285 if (queue_blocking_request(
286 BLOCKING_GETADDRINFO,
289 &getaddrinfo_sometime_complete,
292 msyslog(LOG_ERR, "unable to queue getaddrinfo request");
301 blocking_getaddrinfo(
303 blocking_pipe_header * req
306 blocking_gai_req * gai_req;
307 dnsworker_ctx * worker_ctx;
308 blocking_pipe_header * resp;
309 blocking_gai_resp * gai_resp;
312 struct addrinfo * ai_res;
313 struct addrinfo * ai;
314 struct addrinfo * serialized_ai;
315 size_t canons_octets;
321 gai_req = (void *)((char *)req + sizeof(*req));
322 node = (char *)gai_req + sizeof(*gai_req);
323 service = node + gai_req->nodesize;
325 worker_ctx = get_worker_context(c, gai_req->dns_idx);
326 scheduled_sleep(gai_req->scheduled, gai_req->earliest,
328 reload_resolv_conf(worker_ctx);
331 * Take a shot at the final size, better to overestimate
332 * at first and then realloc to a smaller size.
335 resp_octets = sizeof(*resp) + sizeof(*gai_resp) +
336 16 * (sizeof(struct addrinfo) +
337 sizeof(sockaddr_u)) +
339 resp = emalloc_zero(resp_octets);
340 gai_resp = (void *)(resp + 1);
342 TRACE(2, ("blocking_getaddrinfo given node %s serv %s fam %d flags %x\n",
343 node, service, gai_req->hints.ai_family,
344 gai_req->hints.ai_flags));
350 gai_resp->retcode = getaddrinfo(node, service, &gai_req->hints,
352 gai_resp->retry = gai_req->retry;
354 if (EAI_SYSTEM == gai_resp->retcode)
355 gai_resp->gai_errno = errno;
359 if (0 == gai_resp->retcode) {
362 gai_resp->ai_count++;
363 if (ai->ai_canonname)
364 canons_octets += strlen(ai->ai_canonname) + 1;
368 * If this query succeeded only after retrying, DNS may have
369 * just become responsive. Ignore previously-scheduled
370 * retry sleeps once for each pending request, similar to
371 * the way scheduled_sleep() does when its worker_sleep()
374 if (gai_resp->retry > INITIAL_DNS_RETRY) {
375 time_now = time(NULL);
376 worker_ctx->ignore_scheduled_before = time_now;
377 TRACE(1, ("DNS success after retry, ignoring sleeps scheduled before now (%s)\n",
378 humantime(time_now)));
383 * Our response consists of a header, followed by ai_count
384 * addrinfo structs followed by ai_count sockaddr_storage
385 * structs followed by the canonical names.
387 gai_resp->octets = sizeof(*gai_resp)
389 * (sizeof(gai_req->hints)
390 + sizeof(sockaddr_u))
393 resp_octets = sizeof(*resp) + gai_resp->octets;
394 resp = erealloc(resp, resp_octets);
395 gai_resp = (void *)(resp + 1);
397 /* cp serves as our current pointer while serializing */
398 cp = (void *)(gai_resp + 1);
401 if (0 == gai_resp->retcode) {
404 memcpy(cp, ai, sizeof(*ai));
405 serialized_ai = (void *)cp;
408 /* transform ai_canonname into offset */
409 if (NULL != serialized_ai->ai_canonname) {
410 serialized_ai->ai_canonname = (char *)canons_octets;
411 canons_octets += strlen(ai->ai_canonname) + 1;
414 /* leave fixup of ai_addr pointer for receiver */
421 INSIST(ai->ai_addrlen <= sizeof(sockaddr_u));
422 memcpy(cp, ai->ai_addr, ai->ai_addrlen);
423 cp += sizeof(sockaddr_u);
430 if (NULL != ai->ai_canonname) {
431 this_octets = strlen(ai->ai_canonname) + 1;
432 memcpy(cp, ai->ai_canonname, this_octets);
438 freeaddrinfo(ai_res);
442 * make sure our walk and earlier calc match
444 DEBUG_INSIST((size_t)(cp - (char *)resp) == resp_octets);
446 if (queue_blocking_response(c, resp, resp_octets, req)) {
447 msyslog(LOG_ERR, "blocking_getaddrinfo can not queue response");
456 getaddrinfo_sometime_complete(
457 blocking_work_req rtype,
463 blocking_gai_req * gai_req;
464 blocking_gai_resp * gai_resp;
465 dnschild_ctx * child_ctx;
466 struct addrinfo * ai;
467 struct addrinfo * next_ai;
475 const char * fam_spec;
481 DEBUG_REQUIRE(BLOCKING_GETADDRINFO == rtype);
482 DEBUG_REQUIRE(respsize == gai_resp->octets);
484 node = (char *)gai_req + sizeof(*gai_req);
485 service = node + gai_req->nodesize;
487 child_ctx = dnschild_contexts[gai_req->dns_idx];
489 if (0 == gai_resp->retcode) {
491 * If this query succeeded only after retrying, DNS may have
492 * just become responsive.
494 if (gai_resp->retry > INITIAL_DNS_RETRY) {
495 time_now = time(NULL);
496 child_ctx->next_dns_timeslot = time_now;
497 TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n",
498 gai_req->dns_idx, humantime(time_now)));
501 again = should_retry_dns(gai_resp->retcode,
502 gai_resp->gai_errno);
504 * exponential backoff of DNS retries to 64s
506 if (gai_req->retry > 0 && again) {
507 /* log the first retry only */
508 if (INITIAL_DNS_RETRY == gai_req->retry)
510 af = gai_req->hints.ai_family;
511 fam_spec = (AF_INET6 == af)
517 if (EAI_SYSTEM == gai_resp->retcode) {
518 errno = gai_resp->gai_errno;
520 "retrying DNS %s%s: EAI_SYSTEM %d: %m",
522 gai_resp->gai_errno);
526 "retrying DNS %s%s: %s (%d)",
528 gai_strerror(gai_resp->retcode),
531 manage_dns_retry_interval(&gai_req->scheduled,
532 &gai_req->earliest, &gai_req->retry,
533 &child_ctx->next_dns_timeslot);
534 if (!queue_blocking_request(
535 BLOCKING_GETADDRINFO,
538 &getaddrinfo_sometime_complete,
543 "unable to retry hostname %s",
549 * fixup pointers in returned addrinfo array
551 ai = (void *)((char *)gai_resp + sizeof(*gai_resp));
553 for (i = gai_resp->ai_count - 1; i >= 0; i--) {
554 ai[i].ai_next = next_ai;
558 psau = (void *)((char *)ai + gai_resp->ai_count * sizeof(*ai));
559 canon_start = (char *)psau + gai_resp->ai_count * sizeof(*psau);
561 for (i = 0; i < gai_resp->ai_count; i++) {
562 if (NULL != ai[i].ai_addr)
563 ai[i].ai_addr = &psau->sa;
565 if (NULL != ai[i].ai_canonname)
566 ai[i].ai_canonname += (size_t)canon_start;
569 ENSURE((char *)psau == canon_start);
571 if (!gai_resp->ai_count)
574 (*gai_req->callback)(gai_resp->retcode, gai_resp->gai_errno,
575 gai_req->context, node, service,
576 &gai_req->hints, ai);
579 /* gai_resp is part of block freed by process_blocking_resp() */
583 #ifdef TEST_BLOCKING_WORKER
584 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)
589 TRACE(1, ("gai_test_callback context %p error rescode %d %s serv %s\n",
590 context, rescode, name, service));
593 while (!rescode && NULL != ai_res) {
595 memcpy(&addr, ai_res->ai_addr, ai_res->ai_addrlen);
596 TRACE(1, ("ctx %p fam %d addr %s canon '%s' type %s at %p ai_addr %p ai_next %p\n",
600 (ai_res->ai_canonname)
601 ? ai_res->ai_canonname
603 (SOCK_DGRAM == ai_res->ai_socktype)
605 : (SOCK_STREAM == ai_res->ai_socktype)
612 getnameinfo_sometime((sockaddr_u *)ai_res->ai_addr, 128, 32, 0, gni_test_callback, context);
614 ai_res = ai_res->ai_next;
617 #endif /* TEST_BLOCKING_WORKER */
621 getnameinfo_sometime(
626 gni_sometime_callback callback,
630 blocking_gni_req * gni_req;
632 dnschild_ctx * child_ctx;
636 REQUIRE(hostoctets + servoctets < 1024);
638 idx = get_dnschild_ctx();
639 child_ctx = dnschild_contexts[idx];
641 gni_req = emalloc_zero(sizeof(*gni_req));
643 gni_req->octets = sizeof(*gni_req);
644 gni_req->dns_idx = idx;
645 time_now = time(NULL);
646 gni_req->scheduled = time_now;
647 gni_req->earliest = max(time_now, child_ctx->next_dns_timeslot);
648 child_ctx->next_dns_timeslot = gni_req->earliest;
649 memcpy(&gni_req->socku, psau, SOCKLEN(psau));
650 gni_req->hostoctets = hostoctets;
651 gni_req->servoctets = servoctets;
652 gni_req->flags = flags;
653 gni_req->retry = INITIAL_DNS_RETRY;
654 gni_req->callback = callback;
655 gni_req->context = context;
657 if (queue_blocking_request(
658 BLOCKING_GETNAMEINFO,
661 &getnameinfo_sometime_complete,
664 msyslog(LOG_ERR, "unable to queue getnameinfo request");
674 blocking_getnameinfo(
676 blocking_pipe_header * req
679 blocking_gni_req * gni_req;
680 dnsworker_ctx * worker_ctx;
681 blocking_pipe_header * resp;
682 blocking_gni_resp * gni_resp;
691 gni_req = (void *)((char *)req + sizeof(*req));
693 octets = gni_req->hostoctets + gni_req->servoctets;
696 * Some alloca() implementations are fragile regarding
697 * large allocations. We only need room for the host
700 REQUIRE(octets < sizeof(host));
701 service = host + gni_req->hostoctets;
703 worker_ctx = get_worker_context(c, gni_req->dns_idx);
704 scheduled_sleep(gni_req->scheduled, gni_req->earliest,
706 reload_resolv_conf(worker_ctx);
709 * Take a shot at the final size, better to overestimate
710 * then realloc to a smaller size.
713 resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets;
714 resp = emalloc_zero(resp_octets);
715 gni_resp = (void *)((char *)resp + sizeof(*resp));
717 TRACE(2, ("blocking_getnameinfo given addr %s flags 0x%x hostlen %lu servlen %lu\n",
718 stoa(&gni_req->socku), gni_req->flags,
719 (u_long)gni_req->hostoctets, (u_long)gni_req->servoctets));
721 gni_resp->retcode = getnameinfo(&gni_req->socku.sa,
722 SOCKLEN(&gni_req->socku),
728 gni_resp->retry = gni_req->retry;
730 if (EAI_SYSTEM == gni_resp->retcode)
731 gni_resp->gni_errno = errno;
734 if (0 != gni_resp->retcode) {
735 gni_resp->hostoctets = 0;
736 gni_resp->servoctets = 0;
738 gni_resp->hostoctets = strlen(host) + 1;
739 gni_resp->servoctets = strlen(service) + 1;
741 * If this query succeeded only after retrying, DNS may have
742 * just become responsive. Ignore previously-scheduled
743 * retry sleeps once for each pending request, similar to
744 * the way scheduled_sleep() does when its worker_sleep()
747 if (gni_req->retry > INITIAL_DNS_RETRY) {
748 time_now = time(NULL);
749 worker_ctx->ignore_scheduled_before = time_now;
750 TRACE(1, ("DNS success after retrying, ignoring sleeps scheduled before now (%s)\n",
751 humantime(time_now)));
754 octets = gni_resp->hostoctets + gni_resp->servoctets;
756 * Our response consists of a header, followed by the host and
757 * service strings, each null-terminated.
759 resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets;
761 resp = erealloc(resp, resp_octets);
762 gni_resp = (void *)(resp + 1);
764 gni_resp->octets = sizeof(*gni_resp) + octets;
766 /* cp serves as our current pointer while serializing */
767 cp = (void *)(gni_resp + 1);
769 if (0 == gni_resp->retcode) {
770 memcpy(cp, host, gni_resp->hostoctets);
771 cp += gni_resp->hostoctets;
772 memcpy(cp, service, gni_resp->servoctets);
773 cp += gni_resp->servoctets;
776 INSIST((size_t)(cp - (char *)resp) == resp_octets);
777 INSIST(resp_octets - sizeof(*resp) == gni_resp->octets);
779 rc = queue_blocking_response(c, resp, resp_octets, req);
781 msyslog(LOG_ERR, "blocking_getnameinfo unable to queue response");
787 getnameinfo_sometime_complete(
788 blocking_work_req rtype,
794 blocking_gni_req * gni_req;
795 blocking_gni_resp * gni_resp;
796 dnschild_ctx * child_ctx;
805 DEBUG_REQUIRE(BLOCKING_GETNAMEINFO == rtype);
806 DEBUG_REQUIRE(respsize == gni_resp->octets);
808 child_ctx = dnschild_contexts[gni_req->dns_idx];
810 if (0 == gni_resp->retcode) {
812 * If this query succeeded only after retrying, DNS may have
813 * just become responsive.
815 if (gni_resp->retry > INITIAL_DNS_RETRY) {
816 time_now = time(NULL);
817 child_ctx->next_dns_timeslot = time_now;
818 TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n",
819 gni_req->dns_idx, humantime(time_now)));
822 again = should_retry_dns(gni_resp->retcode, gni_resp->gni_errno);
824 * exponential backoff of DNS retries to 64s
826 if (gni_req->retry > 0)
827 manage_dns_retry_interval(&gni_req->scheduled,
828 &gni_req->earliest, &gni_req->retry,
829 &child_ctx->next_dns_timeslot);
831 if (gni_req->retry > 0 && again) {
832 if (!queue_blocking_request(
833 BLOCKING_GETNAMEINFO,
836 &getnameinfo_sometime_complete,
840 msyslog(LOG_ERR, "unable to retry reverse lookup of %s", stoa(&gni_req->socku));
844 if (!gni_resp->hostoctets) {
848 host = (char *)gni_resp + sizeof(*gni_resp);
849 service = (gni_resp->servoctets)
850 ? host + gni_resp->hostoctets
854 (*gni_req->callback)(gni_resp->retcode, gni_resp->gni_errno,
855 &gni_req->socku, gni_req->flags, host,
856 service, gni_req->context);
859 /* gni_resp is part of block freed by process_blocking_resp() */
863 #ifdef TEST_BLOCKING_WORKER
864 void gni_test_callback(int rescode, int gni_errno, sockaddr_u *psau, int flags, const char *host, const char *service, void *context)
867 TRACE(1, ("gni_test_callback got host '%s' serv '%s' for addr %s context %p\n",
868 host, service, stoa(psau), context));
870 TRACE(1, ("gni_test_callback context %p rescode %d gni_errno %d flags 0x%x addr %s\n",
871 context, rescode, gni_errno, flags, stoa(psau)));
873 #endif /* TEST_BLOCKING_WORKER */
879 dnsworker_ctx * worker_ctx
885 * This is ad-hoc. Reload /etc/resolv.conf once per minute
886 * to pick up on changes from the DHCP client. [Bug 1226]
887 * When using threads for the workers, this needs to happen
888 * only once per minute process-wide.
890 time_now = time(NULL);
892 worker_ctx->next_res_init = next_res_init;
894 if (worker_ctx->next_res_init <= time_now) {
895 if (worker_ctx->next_res_init != 0)
897 worker_ctx->next_res_init = time_now + 60;
899 next_res_init = worker_ctx->next_res_init;
903 #endif /* HAVE_RES_INIT */
907 reserve_dnschild_ctx(void)
909 const size_t ps = sizeof(dnschild_contexts[0]);
910 const size_t cs = sizeof(*dnschild_contexts[0]);
918 for ( ; c < dnschild_contexts_alloc; c++) {
919 if (NULL == dnschild_contexts[c]) {
920 dnschild_contexts[c] = emalloc_zero(cs);
925 new_alloc = dnschild_contexts_alloc + 20;
926 new_octets = new_alloc * ps;
927 octets = dnschild_contexts_alloc * ps;
928 dnschild_contexts = erealloc_zero(dnschild_contexts,
930 dnschild_contexts_alloc = new_alloc;
936 get_dnschild_ctx(void)
938 static u_int shared_ctx = UINT_MAX;
940 if (worker_per_query)
941 return reserve_dnschild_ctx();
943 if (UINT_MAX == shared_ctx)
944 shared_ctx = reserve_dnschild_ctx();
950 static dnsworker_ctx *
960 dnsworker_ctx * retv;
962 worker_global_lock(TRUE);
964 if (dnsworker_contexts_alloc <= idx) {
965 min_new_alloc = 1 + idx;
966 /* round new_alloc up to nearest multiple of 4 */
967 new_alloc = (min_new_alloc + 4) & ~(4 - 1);
968 new_octets = new_alloc * sizeof(dnsworker_ctx*);
969 octets = dnsworker_contexts_alloc * sizeof(dnsworker_ctx*);
970 dnsworker_contexts = erealloc_zero(dnsworker_contexts,
972 dnsworker_contexts_alloc = new_alloc;
973 retv = emalloc_zero(sizeof(dnsworker_ctx));
974 dnsworker_contexts[idx] = retv;
975 } else if (NULL == (retv = dnsworker_contexts[idx])) {
976 retv = emalloc_zero(sizeof(dnsworker_ctx));
977 dnsworker_contexts[idx] = retv;
980 worker_global_lock(FALSE);
992 dnsworker_ctx * worker_ctx
997 if (scheduled < worker_ctx->ignore_scheduled_before) {
998 TRACE(1, ("ignoring sleep until %s scheduled at %s (before %s)\n",
999 humantime(earliest), humantime(scheduled),
1000 humantime(worker_ctx->ignore_scheduled_before)));
1006 if (now < earliest) {
1007 TRACE(1, ("sleep until %s scheduled at %s (>= %s)\n",
1008 humantime(earliest), humantime(scheduled),
1009 humantime(worker_ctx->ignore_scheduled_before)));
1010 if (-1 == worker_sleep(worker_ctx->c, earliest - now)) {
1011 /* our sleep was interrupted */
1013 worker_ctx->ignore_scheduled_before = now;
1014 #ifdef HAVE_RES_INIT
1015 worker_ctx->next_res_init = now + 60;
1016 next_res_init = worker_ctx->next_res_init;
1019 TRACE(1, ("sleep interrupted by daemon, ignoring sleeps scheduled before now (%s)\n",
1020 humantime(worker_ctx->ignore_scheduled_before)));
1027 * manage_dns_retry_interval is a helper used by
1028 * getaddrinfo_sometime_complete and getnameinfo_sometime_complete
1029 * to calculate the new retry interval and schedule the next query.
1032 manage_dns_retry_interval(
1033 time_t * pscheduled,
1036 time_t * pnext_timeslot
1045 when = max(now + retry, *pnext_timeslot);
1046 *pnext_timeslot = when;
1047 retry = min(64, retry << 1);
1055 * should_retry_dns is a helper used by getaddrinfo_sometime_complete
1056 * and getnameinfo_sometime_complete which implements ntpd's DNS retry
1065 static int eai_again_seen;
1067 #if defined (EAI_SYSTEM) && defined(DEBUG)
1072 * If the resolver failed, see if the failure is
1073 * temporary. If so, return success.
1085 eai_again_seen = 1; /* [Bug 1178] */
1089 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
1092 again = !eai_again_seen; /* [Bug 1178] */
1098 * EAI_SYSTEM means the real error is in errno. We should be more
1099 * discriminating about which errno values require retrying, but
1100 * this matches existing behavior.
1104 errno_to_str(res_errno, msg, sizeof(msg));
1105 TRACE(1, ("intres: EAI_SYSTEM errno %d (%s) means try again, right?\n",
1112 TRACE(2, ("intres: resolver returned: %s (%d), %sretrying\n",
1113 gai_strerror(rescode), rescode, again ? "" : "not "));
1118 #else /* !WORKER follows */
1119 int ntp_intres_nonempty_compilation_unit;