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 121310419Sdelphij 122275970Scy/* === typedefs === */ 123275970Scytypedef struct blocking_gai_req_tag { /* marshalled args */ 124275970Scy size_t octets; 125275970Scy u_int dns_idx; 126275970Scy time_t scheduled; 127275970Scy time_t earliest; 128310419Sdelphij int retry; 129275970Scy struct addrinfo hints; 130310419Sdelphij u_int qflags; 131275970Scy gai_sometime_callback callback; 132275970Scy void * context; 133275970Scy size_t nodesize; 134275970Scy size_t servsize; 135275970Scy} blocking_gai_req; 136275970Scy 137275970Scytypedef struct blocking_gai_resp_tag { 138275970Scy size_t octets; 139275970Scy int retcode; 140275970Scy int retry; 141275970Scy int gai_errno; /* for EAI_SYSTEM case */ 142275970Scy int ai_count; 143275970Scy /* 144275970Scy * Followed by ai_count struct addrinfo and then ai_count 145275970Scy * sockaddr_u and finally the canonical name strings. 146275970Scy */ 147275970Scy} blocking_gai_resp; 148275970Scy 149275970Scytypedef struct blocking_gni_req_tag { 150275970Scy size_t octets; 151275970Scy u_int dns_idx; 152275970Scy time_t scheduled; 153275970Scy time_t earliest; 154275970Scy int retry; 155275970Scy size_t hostoctets; 156275970Scy size_t servoctets; 157275970Scy int flags; 158275970Scy gni_sometime_callback callback; 159275970Scy void * context; 160275970Scy sockaddr_u socku; 161275970Scy} blocking_gni_req; 162275970Scy 163275970Scytypedef struct blocking_gni_resp_tag { 164275970Scy size_t octets; 165275970Scy int retcode; 166275970Scy int gni_errno; /* for EAI_SYSTEM case */ 167275970Scy int retry; 168275970Scy size_t hostoctets; 169275970Scy size_t servoctets; 170275970Scy /* 171275970Scy * Followed by hostoctets bytes of null-terminated host, 172275970Scy * then servoctets bytes of null-terminated service. 173275970Scy */ 174275970Scy} blocking_gni_resp; 175275970Scy 176275970Scy/* per-DNS-worker state in parent */ 177275970Scytypedef struct dnschild_ctx_tag { 178275970Scy u_int index; 179275970Scy time_t next_dns_timeslot; 180275970Scy} dnschild_ctx; 181275970Scy 182275970Scy/* per-DNS-worker state in worker */ 183275970Scytypedef struct dnsworker_ctx_tag { 184275970Scy blocking_child * c; 185275970Scy time_t ignore_scheduled_before; 186275970Scy#ifdef HAVE_RES_INIT 187275970Scy time_t next_res_init; 188275970Scy#endif 189275970Scy} dnsworker_ctx; 190275970Scy 191275970Scy 192275970Scy/* === variables === */ 193275970Scydnschild_ctx ** dnschild_contexts; /* parent */ 194275970Scyu_int dnschild_contexts_alloc; 195275970Scydnsworker_ctx ** dnsworker_contexts; /* child */ 196275970Scyu_int dnsworker_contexts_alloc; 197275970Scy 198275970Scy#ifdef HAVE_RES_INIT 199275970Scystatic time_t next_res_init; 200275970Scy#endif 201275970Scy 202275970Scy 203275970Scy/* === forward declarations === */ 204275970Scystatic u_int reserve_dnschild_ctx(void); 205275970Scystatic u_int get_dnschild_ctx(void); 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 *, 210310419Sdelphij int *, time_t *, 211310419Sdelphij int/*BOOL*/); 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 235310419Sdelphijgetaddrinfo_sometime_ex( 236275970Scy const char * node, 237275970Scy const char * service, 238275970Scy const struct addrinfo * hints, 239275970Scy int retry, 240275970Scy gai_sometime_callback callback, 241310419Sdelphij void * context, 242310419Sdelphij u_int qflags 243275970Scy ) 244275970Scy{ 245275970Scy blocking_gai_req * gai_req; 246275970Scy u_int idx; 247275970Scy dnschild_ctx * child_ctx; 248275970Scy size_t req_size; 249275970Scy size_t nodesize; 250275970Scy size_t servsize; 251275970Scy time_t now; 252275970Scy 253290000Sglebius REQUIRE(NULL != node); 254275970Scy if (NULL != hints) { 255290000Sglebius REQUIRE(0 == hints->ai_addrlen); 256290000Sglebius REQUIRE(NULL == hints->ai_addr); 257290000Sglebius REQUIRE(NULL == hints->ai_canonname); 258290000Sglebius REQUIRE(NULL == hints->ai_next); 259275970Scy } 260275970Scy 261275970Scy idx = get_dnschild_ctx(); 262275970Scy child_ctx = dnschild_contexts[idx]; 263275970Scy 264275970Scy nodesize = strlen(node) + 1; 265275970Scy servsize = strlen(service) + 1; 266275970Scy req_size = sizeof(*gai_req) + nodesize + servsize; 267275970Scy 268275970Scy gai_req = emalloc_zero(req_size); 269275970Scy 270275970Scy gai_req->octets = req_size; 271275970Scy gai_req->dns_idx = idx; 272275970Scy now = time(NULL); 273275970Scy gai_req->scheduled = now; 274275970Scy gai_req->earliest = max(now, child_ctx->next_dns_timeslot); 275275970Scy child_ctx->next_dns_timeslot = gai_req->earliest; 276275970Scy if (hints != NULL) 277275970Scy gai_req->hints = *hints; 278275970Scy gai_req->retry = retry; 279275970Scy gai_req->callback = callback; 280275970Scy gai_req->context = context; 281275970Scy gai_req->nodesize = nodesize; 282275970Scy gai_req->servsize = servsize; 283310419Sdelphij gai_req->qflags = qflags; 284275970Scy 285275970Scy memcpy((char *)gai_req + sizeof(*gai_req), node, nodesize); 286275970Scy memcpy((char *)gai_req + sizeof(*gai_req) + nodesize, service, 287275970Scy servsize); 288275970Scy 289275970Scy if (queue_blocking_request( 290275970Scy BLOCKING_GETADDRINFO, 291275970Scy gai_req, 292275970Scy req_size, 293275970Scy &getaddrinfo_sometime_complete, 294275970Scy gai_req)) { 295275970Scy 296275970Scy msyslog(LOG_ERR, "unable to queue getaddrinfo request"); 297275970Scy errno = EFAULT; 298275970Scy return -1; 299275970Scy } 300275970Scy 301275970Scy return 0; 302275970Scy} 303275970Scy 304275970Scyint 305275970Scyblocking_getaddrinfo( 306275970Scy blocking_child * c, 307275970Scy blocking_pipe_header * req 308275970Scy ) 309275970Scy{ 310275970Scy blocking_gai_req * gai_req; 311275970Scy dnsworker_ctx * worker_ctx; 312275970Scy blocking_pipe_header * resp; 313275970Scy blocking_gai_resp * gai_resp; 314275970Scy char * node; 315275970Scy char * service; 316275970Scy struct addrinfo * ai_res; 317275970Scy struct addrinfo * ai; 318275970Scy struct addrinfo * serialized_ai; 319275970Scy size_t canons_octets; 320275970Scy size_t this_octets; 321275970Scy size_t resp_octets; 322275970Scy char * cp; 323275970Scy time_t time_now; 324275970Scy 325275970Scy gai_req = (void *)((char *)req + sizeof(*req)); 326275970Scy node = (char *)gai_req + sizeof(*gai_req); 327275970Scy service = node + gai_req->nodesize; 328275970Scy 329275970Scy worker_ctx = get_worker_context(c, gai_req->dns_idx); 330275970Scy scheduled_sleep(gai_req->scheduled, gai_req->earliest, 331275970Scy worker_ctx); 332275970Scy reload_resolv_conf(worker_ctx); 333275970Scy 334275970Scy /* 335275970Scy * Take a shot at the final size, better to overestimate 336275970Scy * at first and then realloc to a smaller size. 337275970Scy */ 338275970Scy 339275970Scy resp_octets = sizeof(*resp) + sizeof(*gai_resp) + 340275970Scy 16 * (sizeof(struct addrinfo) + 341275970Scy sizeof(sockaddr_u)) + 342275970Scy 256; 343275970Scy resp = emalloc_zero(resp_octets); 344275970Scy gai_resp = (void *)(resp + 1); 345275970Scy 346275970Scy TRACE(2, ("blocking_getaddrinfo given node %s serv %s fam %d flags %x\n", 347275970Scy node, service, gai_req->hints.ai_family, 348275970Scy gai_req->hints.ai_flags)); 349275970Scy#ifdef DEBUG 350275970Scy if (debug >= 2) 351275970Scy fflush(stdout); 352275970Scy#endif 353275970Scy ai_res = NULL; 354275970Scy gai_resp->retcode = getaddrinfo(node, service, &gai_req->hints, 355275970Scy &ai_res); 356275970Scy gai_resp->retry = gai_req->retry; 357275970Scy#ifdef EAI_SYSTEM 358275970Scy if (EAI_SYSTEM == gai_resp->retcode) 359275970Scy gai_resp->gai_errno = errno; 360275970Scy#endif 361275970Scy canons_octets = 0; 362275970Scy 363275970Scy if (0 == gai_resp->retcode) { 364275970Scy ai = ai_res; 365275970Scy while (NULL != ai) { 366275970Scy gai_resp->ai_count++; 367275970Scy if (ai->ai_canonname) 368275970Scy canons_octets += strlen(ai->ai_canonname) + 1; 369275970Scy ai = ai->ai_next; 370275970Scy } 371275970Scy /* 372275970Scy * If this query succeeded only after retrying, DNS may have 373275970Scy * just become responsive. Ignore previously-scheduled 374275970Scy * retry sleeps once for each pending request, similar to 375275970Scy * the way scheduled_sleep() does when its worker_sleep() 376275970Scy * is interrupted. 377275970Scy */ 378275970Scy if (gai_resp->retry > INITIAL_DNS_RETRY) { 379275970Scy time_now = time(NULL); 380275970Scy worker_ctx->ignore_scheduled_before = time_now; 381275970Scy TRACE(1, ("DNS success after retry, ignoring sleeps scheduled before now (%s)\n", 382275970Scy humantime(time_now))); 383275970Scy } 384275970Scy } 385275970Scy 386275970Scy /* 387275970Scy * Our response consists of a header, followed by ai_count 388275970Scy * addrinfo structs followed by ai_count sockaddr_storage 389275970Scy * structs followed by the canonical names. 390275970Scy */ 391275970Scy gai_resp->octets = sizeof(*gai_resp) 392275970Scy + gai_resp->ai_count 393275970Scy * (sizeof(gai_req->hints) 394275970Scy + sizeof(sockaddr_u)) 395275970Scy + canons_octets; 396275970Scy 397275970Scy resp_octets = sizeof(*resp) + gai_resp->octets; 398275970Scy resp = erealloc(resp, resp_octets); 399275970Scy gai_resp = (void *)(resp + 1); 400275970Scy 401275970Scy /* cp serves as our current pointer while serializing */ 402275970Scy cp = (void *)(gai_resp + 1); 403275970Scy canons_octets = 0; 404275970Scy 405275970Scy if (0 == gai_resp->retcode) { 406275970Scy ai = ai_res; 407275970Scy while (NULL != ai) { 408275970Scy memcpy(cp, ai, sizeof(*ai)); 409275970Scy serialized_ai = (void *)cp; 410275970Scy cp += sizeof(*ai); 411275970Scy 412275970Scy /* transform ai_canonname into offset */ 413275970Scy if (NULL != serialized_ai->ai_canonname) { 414275970Scy serialized_ai->ai_canonname = (char *)canons_octets; 415275970Scy canons_octets += strlen(ai->ai_canonname) + 1; 416275970Scy } 417275970Scy 418275970Scy /* leave fixup of ai_addr pointer for receiver */ 419275970Scy 420275970Scy ai = ai->ai_next; 421275970Scy } 422275970Scy 423275970Scy ai = ai_res; 424275970Scy while (NULL != ai) { 425290000Sglebius INSIST(ai->ai_addrlen <= sizeof(sockaddr_u)); 426275970Scy memcpy(cp, ai->ai_addr, ai->ai_addrlen); 427275970Scy cp += sizeof(sockaddr_u); 428275970Scy 429275970Scy ai = ai->ai_next; 430275970Scy } 431275970Scy 432275970Scy ai = ai_res; 433275970Scy while (NULL != ai) { 434275970Scy if (NULL != ai->ai_canonname) { 435275970Scy this_octets = strlen(ai->ai_canonname) + 1; 436275970Scy memcpy(cp, ai->ai_canonname, this_octets); 437275970Scy cp += this_octets; 438275970Scy } 439275970Scy 440275970Scy ai = ai->ai_next; 441275970Scy } 442275970Scy freeaddrinfo(ai_res); 443275970Scy } 444275970Scy 445275970Scy /* 446275970Scy * make sure our walk and earlier calc match 447275970Scy */ 448275970Scy DEBUG_INSIST((size_t)(cp - (char *)resp) == resp_octets); 449275970Scy 450275970Scy if (queue_blocking_response(c, resp, resp_octets, req)) { 451275970Scy msyslog(LOG_ERR, "blocking_getaddrinfo can not queue response"); 452275970Scy return -1; 453275970Scy } 454275970Scy 455275970Scy return 0; 456275970Scy} 457275970Scy 458310419Sdelphijint 459310419Sdelphijgetaddrinfo_sometime( 460310419Sdelphij const char * node, 461310419Sdelphij const char * service, 462310419Sdelphij const struct addrinfo * hints, 463310419Sdelphij int retry, 464310419Sdelphij gai_sometime_callback callback, 465310419Sdelphij void * context 466310419Sdelphij ) 467310419Sdelphij{ 468310419Sdelphij return getaddrinfo_sometime_ex(node, service, hints, retry, 469310419Sdelphij callback, context, 0); 470310419Sdelphij} 471275970Scy 472310419Sdelphij 473275970Scystatic void 474275970Scygetaddrinfo_sometime_complete( 475275970Scy blocking_work_req rtype, 476275970Scy void * context, 477275970Scy size_t respsize, 478275970Scy void * resp 479275970Scy ) 480275970Scy{ 481275970Scy blocking_gai_req * gai_req; 482275970Scy blocking_gai_resp * gai_resp; 483275970Scy dnschild_ctx * child_ctx; 484275970Scy struct addrinfo * ai; 485275970Scy struct addrinfo * next_ai; 486275970Scy sockaddr_u * psau; 487275970Scy char * node; 488275970Scy char * service; 489275970Scy char * canon_start; 490275970Scy time_t time_now; 491310419Sdelphij int again, noerr; 492275970Scy int af; 493275970Scy const char * fam_spec; 494275970Scy int i; 495275970Scy 496275970Scy gai_req = context; 497275970Scy gai_resp = resp; 498275970Scy 499275970Scy DEBUG_REQUIRE(BLOCKING_GETADDRINFO == rtype); 500275970Scy DEBUG_REQUIRE(respsize == gai_resp->octets); 501275970Scy 502275970Scy node = (char *)gai_req + sizeof(*gai_req); 503275970Scy service = node + gai_req->nodesize; 504275970Scy 505275970Scy child_ctx = dnschild_contexts[gai_req->dns_idx]; 506275970Scy 507275970Scy if (0 == gai_resp->retcode) { 508275970Scy /* 509275970Scy * If this query succeeded only after retrying, DNS may have 510275970Scy * just become responsive. 511275970Scy */ 512275970Scy if (gai_resp->retry > INITIAL_DNS_RETRY) { 513275970Scy time_now = time(NULL); 514275970Scy child_ctx->next_dns_timeslot = time_now; 515275970Scy TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n", 516275970Scy gai_req->dns_idx, humantime(time_now))); 517275970Scy } 518275970Scy } else { 519310419Sdelphij noerr = !!(gai_req->qflags & GAIR_F_IGNDNSERR); 520310419Sdelphij again = noerr || should_retry_dns( 521310419Sdelphij gai_resp->retcode, gai_resp->gai_errno); 522275970Scy /* 523275970Scy * exponential backoff of DNS retries to 64s 524275970Scy */ 525275970Scy if (gai_req->retry > 0 && again) { 526275970Scy /* log the first retry only */ 527275970Scy if (INITIAL_DNS_RETRY == gai_req->retry) 528275970Scy NLOG(NLOG_SYSINFO) { 529275970Scy af = gai_req->hints.ai_family; 530275970Scy fam_spec = (AF_INET6 == af) 531275970Scy ? " (AAAA)" 532275970Scy : (AF_INET == af) 533275970Scy ? " (A)" 534275970Scy : ""; 535275970Scy#ifdef EAI_SYSTEM 536275970Scy if (EAI_SYSTEM == gai_resp->retcode) { 537275970Scy errno = gai_resp->gai_errno; 538275970Scy msyslog(LOG_INFO, 539275970Scy "retrying DNS %s%s: EAI_SYSTEM %d: %m", 540275970Scy node, fam_spec, 541275970Scy gai_resp->gai_errno); 542275970Scy } else 543275970Scy#endif 544275970Scy msyslog(LOG_INFO, 545275970Scy "retrying DNS %s%s: %s (%d)", 546275970Scy node, fam_spec, 547275970Scy gai_strerror(gai_resp->retcode), 548275970Scy gai_resp->retcode); 549275970Scy } 550310419Sdelphij manage_dns_retry_interval( 551310419Sdelphij &gai_req->scheduled, &gai_req->earliest, 552310419Sdelphij &gai_req->retry, &child_ctx->next_dns_timeslot, 553310419Sdelphij noerr); 554275970Scy if (!queue_blocking_request( 555275970Scy BLOCKING_GETADDRINFO, 556275970Scy gai_req, 557275970Scy gai_req->octets, 558275970Scy &getaddrinfo_sometime_complete, 559275970Scy gai_req)) 560275970Scy return; 561275970Scy else 562275970Scy msyslog(LOG_ERR, 563275970Scy "unable to retry hostname %s", 564275970Scy node); 565275970Scy } 566275970Scy } 567275970Scy 568275970Scy /* 569275970Scy * fixup pointers in returned addrinfo array 570275970Scy */ 571275970Scy ai = (void *)((char *)gai_resp + sizeof(*gai_resp)); 572275970Scy next_ai = NULL; 573275970Scy for (i = gai_resp->ai_count - 1; i >= 0; i--) { 574275970Scy ai[i].ai_next = next_ai; 575275970Scy next_ai = &ai[i]; 576275970Scy } 577275970Scy 578275970Scy psau = (void *)((char *)ai + gai_resp->ai_count * sizeof(*ai)); 579275970Scy canon_start = (char *)psau + gai_resp->ai_count * sizeof(*psau); 580275970Scy 581275970Scy for (i = 0; i < gai_resp->ai_count; i++) { 582275970Scy if (NULL != ai[i].ai_addr) 583275970Scy ai[i].ai_addr = &psau->sa; 584275970Scy psau++; 585275970Scy if (NULL != ai[i].ai_canonname) 586275970Scy ai[i].ai_canonname += (size_t)canon_start; 587275970Scy } 588275970Scy 589290000Sglebius ENSURE((char *)psau == canon_start); 590275970Scy 591275970Scy if (!gai_resp->ai_count) 592275970Scy ai = NULL; 593275970Scy 594275970Scy (*gai_req->callback)(gai_resp->retcode, gai_resp->gai_errno, 595275970Scy gai_req->context, node, service, 596275970Scy &gai_req->hints, ai); 597275970Scy 598275970Scy free(gai_req); 599275970Scy /* gai_resp is part of block freed by process_blocking_resp() */ 600275970Scy} 601275970Scy 602275970Scy 603275970Scy#ifdef TEST_BLOCKING_WORKER 604275970Scyvoid 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) 605275970Scy{ 606275970Scy sockaddr_u addr; 607275970Scy 608275970Scy if (rescode) { 609275970Scy TRACE(1, ("gai_test_callback context %p error rescode %d %s serv %s\n", 610275970Scy context, rescode, name, service)); 611275970Scy return; 612275970Scy } 613275970Scy while (!rescode && NULL != ai_res) { 614275970Scy ZERO_SOCK(&addr); 615275970Scy memcpy(&addr, ai_res->ai_addr, ai_res->ai_addrlen); 616275970Scy TRACE(1, ("ctx %p fam %d addr %s canon '%s' type %s at %p ai_addr %p ai_next %p\n", 617275970Scy context, 618275970Scy AF(&addr), 619275970Scy stoa(&addr), 620275970Scy (ai_res->ai_canonname) 621275970Scy ? ai_res->ai_canonname 622275970Scy : "", 623275970Scy (SOCK_DGRAM == ai_res->ai_socktype) 624275970Scy ? "DGRAM" 625275970Scy : (SOCK_STREAM == ai_res->ai_socktype) 626275970Scy ? "STREAM" 627275970Scy : "(other)", 628275970Scy ai_res, 629275970Scy ai_res->ai_addr, 630275970Scy ai_res->ai_next)); 631275970Scy 632275970Scy getnameinfo_sometime((sockaddr_u *)ai_res->ai_addr, 128, 32, 0, gni_test_callback, context); 633275970Scy 634275970Scy ai_res = ai_res->ai_next; 635275970Scy } 636275970Scy} 637275970Scy#endif /* TEST_BLOCKING_WORKER */ 638275970Scy 639275970Scy 640275970Scyint 641275970Scygetnameinfo_sometime( 642275970Scy sockaddr_u * psau, 643275970Scy size_t hostoctets, 644275970Scy size_t servoctets, 645275970Scy int flags, 646275970Scy gni_sometime_callback callback, 647275970Scy void * context 648275970Scy ) 649275970Scy{ 650275970Scy blocking_gni_req * gni_req; 651275970Scy u_int idx; 652275970Scy dnschild_ctx * child_ctx; 653275970Scy time_t time_now; 654275970Scy 655290000Sglebius REQUIRE(hostoctets); 656290000Sglebius REQUIRE(hostoctets + servoctets < 1024); 657275970Scy 658275970Scy idx = get_dnschild_ctx(); 659275970Scy child_ctx = dnschild_contexts[idx]; 660275970Scy 661275970Scy gni_req = emalloc_zero(sizeof(*gni_req)); 662275970Scy 663275970Scy gni_req->octets = sizeof(*gni_req); 664275970Scy gni_req->dns_idx = idx; 665275970Scy time_now = time(NULL); 666275970Scy gni_req->scheduled = time_now; 667275970Scy gni_req->earliest = max(time_now, child_ctx->next_dns_timeslot); 668275970Scy child_ctx->next_dns_timeslot = gni_req->earliest; 669275970Scy memcpy(&gni_req->socku, psau, SOCKLEN(psau)); 670275970Scy gni_req->hostoctets = hostoctets; 671275970Scy gni_req->servoctets = servoctets; 672275970Scy gni_req->flags = flags; 673275970Scy gni_req->retry = INITIAL_DNS_RETRY; 674275970Scy gni_req->callback = callback; 675275970Scy gni_req->context = context; 676275970Scy 677275970Scy if (queue_blocking_request( 678275970Scy BLOCKING_GETNAMEINFO, 679275970Scy gni_req, 680275970Scy sizeof(*gni_req), 681275970Scy &getnameinfo_sometime_complete, 682275970Scy gni_req)) { 683275970Scy 684275970Scy msyslog(LOG_ERR, "unable to queue getnameinfo request"); 685275970Scy errno = EFAULT; 686275970Scy return -1; 687275970Scy } 688275970Scy 689275970Scy return 0; 690275970Scy} 691275970Scy 692275970Scy 693275970Scyint 694275970Scyblocking_getnameinfo( 695275970Scy blocking_child * c, 696275970Scy blocking_pipe_header * req 697275970Scy ) 698275970Scy{ 699275970Scy blocking_gni_req * gni_req; 700275970Scy dnsworker_ctx * worker_ctx; 701275970Scy blocking_pipe_header * resp; 702275970Scy blocking_gni_resp * gni_resp; 703275970Scy size_t octets; 704275970Scy size_t resp_octets; 705275970Scy char * service; 706275970Scy char * cp; 707275970Scy int rc; 708275970Scy time_t time_now; 709280849Scy char host[1024]; 710275970Scy 711275970Scy gni_req = (void *)((char *)req + sizeof(*req)); 712275970Scy 713275970Scy octets = gni_req->hostoctets + gni_req->servoctets; 714275970Scy 715275970Scy /* 716275970Scy * Some alloca() implementations are fragile regarding 717275970Scy * large allocations. We only need room for the host 718275970Scy * and service names. 719275970Scy */ 720290000Sglebius REQUIRE(octets < sizeof(host)); 721275970Scy service = host + gni_req->hostoctets; 722275970Scy 723275970Scy worker_ctx = get_worker_context(c, gni_req->dns_idx); 724275970Scy scheduled_sleep(gni_req->scheduled, gni_req->earliest, 725275970Scy worker_ctx); 726275970Scy reload_resolv_conf(worker_ctx); 727275970Scy 728275970Scy /* 729275970Scy * Take a shot at the final size, better to overestimate 730275970Scy * then realloc to a smaller size. 731275970Scy */ 732275970Scy 733275970Scy resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets; 734275970Scy resp = emalloc_zero(resp_octets); 735275970Scy gni_resp = (void *)((char *)resp + sizeof(*resp)); 736275970Scy 737275970Scy TRACE(2, ("blocking_getnameinfo given addr %s flags 0x%x hostlen %lu servlen %lu\n", 738275970Scy stoa(&gni_req->socku), gni_req->flags, 739275970Scy (u_long)gni_req->hostoctets, (u_long)gni_req->servoctets)); 740275970Scy 741275970Scy gni_resp->retcode = getnameinfo(&gni_req->socku.sa, 742275970Scy SOCKLEN(&gni_req->socku), 743275970Scy host, 744275970Scy gni_req->hostoctets, 745275970Scy service, 746275970Scy gni_req->servoctets, 747275970Scy gni_req->flags); 748275970Scy gni_resp->retry = gni_req->retry; 749275970Scy#ifdef EAI_SYSTEM 750275970Scy if (EAI_SYSTEM == gni_resp->retcode) 751275970Scy gni_resp->gni_errno = errno; 752275970Scy#endif 753275970Scy 754275970Scy if (0 != gni_resp->retcode) { 755275970Scy gni_resp->hostoctets = 0; 756275970Scy gni_resp->servoctets = 0; 757275970Scy } else { 758275970Scy gni_resp->hostoctets = strlen(host) + 1; 759275970Scy gni_resp->servoctets = strlen(service) + 1; 760275970Scy /* 761275970Scy * If this query succeeded only after retrying, DNS may have 762275970Scy * just become responsive. Ignore previously-scheduled 763275970Scy * retry sleeps once for each pending request, similar to 764275970Scy * the way scheduled_sleep() does when its worker_sleep() 765275970Scy * is interrupted. 766275970Scy */ 767275970Scy if (gni_req->retry > INITIAL_DNS_RETRY) { 768275970Scy time_now = time(NULL); 769275970Scy worker_ctx->ignore_scheduled_before = time_now; 770275970Scy TRACE(1, ("DNS success after retrying, ignoring sleeps scheduled before now (%s)\n", 771275970Scy humantime(time_now))); 772275970Scy } 773275970Scy } 774275970Scy octets = gni_resp->hostoctets + gni_resp->servoctets; 775275970Scy /* 776275970Scy * Our response consists of a header, followed by the host and 777275970Scy * service strings, each null-terminated. 778275970Scy */ 779275970Scy resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets; 780275970Scy 781275970Scy resp = erealloc(resp, resp_octets); 782275970Scy gni_resp = (void *)(resp + 1); 783275970Scy 784275970Scy gni_resp->octets = sizeof(*gni_resp) + octets; 785275970Scy 786275970Scy /* cp serves as our current pointer while serializing */ 787275970Scy cp = (void *)(gni_resp + 1); 788275970Scy 789275970Scy if (0 == gni_resp->retcode) { 790275970Scy memcpy(cp, host, gni_resp->hostoctets); 791275970Scy cp += gni_resp->hostoctets; 792275970Scy memcpy(cp, service, gni_resp->servoctets); 793275970Scy cp += gni_resp->servoctets; 794275970Scy } 795275970Scy 796290000Sglebius INSIST((size_t)(cp - (char *)resp) == resp_octets); 797290000Sglebius INSIST(resp_octets - sizeof(*resp) == gni_resp->octets); 798275970Scy 799275970Scy rc = queue_blocking_response(c, resp, resp_octets, req); 800275970Scy if (rc) 801275970Scy msyslog(LOG_ERR, "blocking_getnameinfo unable to queue response"); 802275970Scy return rc; 803275970Scy} 804275970Scy 805275970Scy 806275970Scystatic void 807275970Scygetnameinfo_sometime_complete( 808275970Scy blocking_work_req rtype, 809275970Scy void * context, 810275970Scy size_t respsize, 811275970Scy void * resp 812275970Scy ) 813275970Scy{ 814275970Scy blocking_gni_req * gni_req; 815275970Scy blocking_gni_resp * gni_resp; 816275970Scy dnschild_ctx * child_ctx; 817275970Scy char * host; 818275970Scy char * service; 819275970Scy time_t time_now; 820275970Scy int again; 821275970Scy 822275970Scy gni_req = context; 823275970Scy gni_resp = resp; 824275970Scy 825275970Scy DEBUG_REQUIRE(BLOCKING_GETNAMEINFO == rtype); 826275970Scy DEBUG_REQUIRE(respsize == gni_resp->octets); 827275970Scy 828275970Scy child_ctx = dnschild_contexts[gni_req->dns_idx]; 829275970Scy 830275970Scy if (0 == gni_resp->retcode) { 831275970Scy /* 832275970Scy * If this query succeeded only after retrying, DNS may have 833275970Scy * just become responsive. 834275970Scy */ 835275970Scy if (gni_resp->retry > INITIAL_DNS_RETRY) { 836275970Scy time_now = time(NULL); 837275970Scy child_ctx->next_dns_timeslot = time_now; 838275970Scy TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n", 839275970Scy gni_req->dns_idx, humantime(time_now))); 840275970Scy } 841275970Scy } else { 842275970Scy again = should_retry_dns(gni_resp->retcode, gni_resp->gni_errno); 843275970Scy /* 844275970Scy * exponential backoff of DNS retries to 64s 845275970Scy */ 846275970Scy if (gni_req->retry > 0) 847275970Scy manage_dns_retry_interval(&gni_req->scheduled, 848275970Scy &gni_req->earliest, &gni_req->retry, 849310419Sdelphij &child_ctx->next_dns_timeslot, FALSE); 850275970Scy 851275970Scy if (gni_req->retry > 0 && again) { 852275970Scy if (!queue_blocking_request( 853275970Scy BLOCKING_GETNAMEINFO, 854275970Scy gni_req, 855275970Scy gni_req->octets, 856275970Scy &getnameinfo_sometime_complete, 857275970Scy gni_req)) 858275970Scy return; 859275970Scy 860275970Scy msyslog(LOG_ERR, "unable to retry reverse lookup of %s", stoa(&gni_req->socku)); 861275970Scy } 862275970Scy } 863275970Scy 864275970Scy if (!gni_resp->hostoctets) { 865275970Scy host = NULL; 866275970Scy service = NULL; 867275970Scy } else { 868275970Scy host = (char *)gni_resp + sizeof(*gni_resp); 869275970Scy service = (gni_resp->servoctets) 870275970Scy ? host + gni_resp->hostoctets 871275970Scy : NULL; 872275970Scy } 873275970Scy 874275970Scy (*gni_req->callback)(gni_resp->retcode, gni_resp->gni_errno, 875275970Scy &gni_req->socku, gni_req->flags, host, 876275970Scy service, gni_req->context); 877275970Scy 878275970Scy free(gni_req); 879275970Scy /* gni_resp is part of block freed by process_blocking_resp() */ 880275970Scy} 881275970Scy 882275970Scy 883275970Scy#ifdef TEST_BLOCKING_WORKER 884275970Scyvoid gni_test_callback(int rescode, int gni_errno, sockaddr_u *psau, int flags, const char *host, const char *service, void *context) 885275970Scy{ 886275970Scy if (!rescode) 887275970Scy TRACE(1, ("gni_test_callback got host '%s' serv '%s' for addr %s context %p\n", 888275970Scy host, service, stoa(psau), context)); 889275970Scy else 890275970Scy TRACE(1, ("gni_test_callback context %p rescode %d gni_errno %d flags 0x%x addr %s\n", 891275970Scy context, rescode, gni_errno, flags, stoa(psau))); 892275970Scy} 893275970Scy#endif /* TEST_BLOCKING_WORKER */ 894275970Scy 895275970Scy 896275970Scy#ifdef HAVE_RES_INIT 897275970Scystatic void 898275970Scyreload_resolv_conf( 899275970Scy dnsworker_ctx * worker_ctx 900275970Scy ) 901275970Scy{ 902275970Scy time_t time_now; 903275970Scy 904275970Scy /* 905275970Scy * This is ad-hoc. Reload /etc/resolv.conf once per minute 906275970Scy * to pick up on changes from the DHCP client. [Bug 1226] 907275970Scy * When using threads for the workers, this needs to happen 908275970Scy * only once per minute process-wide. 909275970Scy */ 910275970Scy time_now = time(NULL); 911275970Scy# ifdef WORK_THREAD 912275970Scy worker_ctx->next_res_init = next_res_init; 913275970Scy# endif 914275970Scy if (worker_ctx->next_res_init <= time_now) { 915275970Scy if (worker_ctx->next_res_init != 0) 916275970Scy res_init(); 917275970Scy worker_ctx->next_res_init = time_now + 60; 918275970Scy# ifdef WORK_THREAD 919275970Scy next_res_init = worker_ctx->next_res_init; 920275970Scy# endif 921275970Scy } 922275970Scy} 923275970Scy#endif /* HAVE_RES_INIT */ 924275970Scy 925275970Scy 926275970Scystatic u_int 927275970Scyreserve_dnschild_ctx(void) 928275970Scy{ 929275970Scy const size_t ps = sizeof(dnschild_contexts[0]); 930275970Scy const size_t cs = sizeof(*dnschild_contexts[0]); 931275970Scy u_int c; 932275970Scy u_int new_alloc; 933275970Scy size_t octets; 934275970Scy size_t new_octets; 935275970Scy 936275970Scy c = 0; 937275970Scy while (TRUE) { 938275970Scy for ( ; c < dnschild_contexts_alloc; c++) { 939275970Scy if (NULL == dnschild_contexts[c]) { 940275970Scy dnschild_contexts[c] = emalloc_zero(cs); 941275970Scy 942275970Scy return c; 943275970Scy } 944275970Scy } 945275970Scy new_alloc = dnschild_contexts_alloc + 20; 946275970Scy new_octets = new_alloc * ps; 947275970Scy octets = dnschild_contexts_alloc * ps; 948275970Scy dnschild_contexts = erealloc_zero(dnschild_contexts, 949275970Scy new_octets, octets); 950275970Scy dnschild_contexts_alloc = new_alloc; 951275970Scy } 952275970Scy} 953275970Scy 954275970Scy 955275970Scystatic u_int 956275970Scyget_dnschild_ctx(void) 957275970Scy{ 958275970Scy static u_int shared_ctx = UINT_MAX; 959275970Scy 960275970Scy if (worker_per_query) 961275970Scy return reserve_dnschild_ctx(); 962275970Scy 963275970Scy if (UINT_MAX == shared_ctx) 964275970Scy shared_ctx = reserve_dnschild_ctx(); 965275970Scy 966275970Scy return shared_ctx; 967275970Scy} 968275970Scy 969275970Scy 970275970Scystatic dnsworker_ctx * 971275970Scyget_worker_context( 972275970Scy blocking_child * c, 973275970Scy u_int idx 974275970Scy ) 975275970Scy{ 976298770Sdelphij u_int min_new_alloc; 977298770Sdelphij u_int new_alloc; 978298770Sdelphij size_t octets; 979298770Sdelphij size_t new_octets; 980298770Sdelphij dnsworker_ctx * retv; 981275970Scy 982298770Sdelphij worker_global_lock(TRUE); 983298770Sdelphij 984275970Scy if (dnsworker_contexts_alloc <= idx) { 985275970Scy min_new_alloc = 1 + idx; 986275970Scy /* round new_alloc up to nearest multiple of 4 */ 987275970Scy new_alloc = (min_new_alloc + 4) & ~(4 - 1); 988298770Sdelphij new_octets = new_alloc * sizeof(dnsworker_ctx*); 989298770Sdelphij octets = dnsworker_contexts_alloc * sizeof(dnsworker_ctx*); 990275970Scy dnsworker_contexts = erealloc_zero(dnsworker_contexts, 991275970Scy new_octets, octets); 992275970Scy dnsworker_contexts_alloc = new_alloc; 993298770Sdelphij retv = emalloc_zero(sizeof(dnsworker_ctx)); 994298770Sdelphij dnsworker_contexts[idx] = retv; 995298770Sdelphij } else if (NULL == (retv = dnsworker_contexts[idx])) { 996298770Sdelphij retv = emalloc_zero(sizeof(dnsworker_ctx)); 997298770Sdelphij dnsworker_contexts[idx] = retv; 998275970Scy } 999298770Sdelphij 1000298770Sdelphij worker_global_lock(FALSE); 1001298770Sdelphij 1002298770Sdelphij ZERO(*retv); 1003298770Sdelphij retv->c = c; 1004298770Sdelphij return retv; 1005275970Scy} 1006275970Scy 1007275970Scy 1008275970Scystatic void 1009275970Scyscheduled_sleep( 1010275970Scy time_t scheduled, 1011275970Scy time_t earliest, 1012275970Scy dnsworker_ctx * worker_ctx 1013275970Scy ) 1014275970Scy{ 1015275970Scy time_t now; 1016275970Scy 1017275970Scy if (scheduled < worker_ctx->ignore_scheduled_before) { 1018275970Scy TRACE(1, ("ignoring sleep until %s scheduled at %s (before %s)\n", 1019275970Scy humantime(earliest), humantime(scheduled), 1020275970Scy humantime(worker_ctx->ignore_scheduled_before))); 1021275970Scy return; 1022275970Scy } 1023275970Scy 1024275970Scy now = time(NULL); 1025275970Scy 1026275970Scy if (now < earliest) { 1027275970Scy TRACE(1, ("sleep until %s scheduled at %s (>= %s)\n", 1028275970Scy humantime(earliest), humantime(scheduled), 1029275970Scy humantime(worker_ctx->ignore_scheduled_before))); 1030275970Scy if (-1 == worker_sleep(worker_ctx->c, earliest - now)) { 1031275970Scy /* our sleep was interrupted */ 1032275970Scy now = time(NULL); 1033275970Scy worker_ctx->ignore_scheduled_before = now; 1034275970Scy#ifdef HAVE_RES_INIT 1035275970Scy worker_ctx->next_res_init = now + 60; 1036275970Scy next_res_init = worker_ctx->next_res_init; 1037275970Scy res_init(); 1038275970Scy#endif 1039275970Scy TRACE(1, ("sleep interrupted by daemon, ignoring sleeps scheduled before now (%s)\n", 1040275970Scy humantime(worker_ctx->ignore_scheduled_before))); 1041275970Scy } 1042275970Scy } 1043275970Scy} 1044275970Scy 1045275970Scy 1046275970Scy/* 1047275970Scy * manage_dns_retry_interval is a helper used by 1048275970Scy * getaddrinfo_sometime_complete and getnameinfo_sometime_complete 1049275970Scy * to calculate the new retry interval and schedule the next query. 1050275970Scy */ 1051275970Scystatic void 1052275970Scymanage_dns_retry_interval( 1053275970Scy time_t * pscheduled, 1054275970Scy time_t * pwhen, 1055275970Scy int * pretry, 1056310419Sdelphij time_t * pnext_timeslot, 1057310419Sdelphij int forever 1058275970Scy ) 1059275970Scy{ 1060275970Scy time_t now; 1061275970Scy time_t when; 1062275970Scy int retry; 1063310419Sdelphij int retmax; 1064275970Scy 1065275970Scy now = time(NULL); 1066275970Scy retry = *pretry; 1067275970Scy when = max(now + retry, *pnext_timeslot); 1068275970Scy *pnext_timeslot = when; 1069275970Scy 1070310419Sdelphij /* this exponential backoff is slower than doubling up: The 1071310419Sdelphij * sequence goes 2-3-4-6-8-12-16-24-32... and the upper limit is 1072310419Sdelphij * 64 seconds for things that should not repeat forever, and 1073310419Sdelphij * 1024 when repeated forever. 1074310419Sdelphij */ 1075310419Sdelphij retmax = forever ? 1024 : 64; 1076310419Sdelphij retry <<= 1; 1077310419Sdelphij if (retry & (retry - 1)) 1078310419Sdelphij retry &= (retry - 1); 1079310419Sdelphij else 1080310419Sdelphij retry -= (retry >> 2); 1081310419Sdelphij retry = min(retmax, retry); 1082310419Sdelphij 1083275970Scy *pscheduled = now; 1084275970Scy *pwhen = when; 1085275970Scy *pretry = retry; 1086275970Scy} 1087275970Scy 1088275970Scy/* 1089275970Scy * should_retry_dns is a helper used by getaddrinfo_sometime_complete 1090275970Scy * and getnameinfo_sometime_complete which implements ntpd's DNS retry 1091275970Scy * policy. 1092275970Scy */ 1093275970Scystatic int 1094275970Scyshould_retry_dns( 1095275970Scy int rescode, 1096275970Scy int res_errno 1097275970Scy ) 1098275970Scy{ 1099275970Scy static int eai_again_seen; 1100275970Scy int again; 1101275970Scy#if defined (EAI_SYSTEM) && defined(DEBUG) 1102275970Scy char msg[256]; 1103275970Scy#endif 1104275970Scy 1105275970Scy /* 1106275970Scy * If the resolver failed, see if the failure is 1107275970Scy * temporary. If so, return success. 1108275970Scy */ 1109275970Scy again = 0; 1110275970Scy 1111275970Scy switch (rescode) { 1112275970Scy 1113275970Scy case EAI_FAIL: 1114275970Scy again = 1; 1115275970Scy break; 1116275970Scy 1117275970Scy case EAI_AGAIN: 1118275970Scy again = 1; 1119275970Scy eai_again_seen = 1; /* [Bug 1178] */ 1120275970Scy break; 1121275970Scy 1122275970Scy case EAI_NONAME: 1123275970Scy#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) 1124275970Scy case EAI_NODATA: 1125275970Scy#endif 1126275970Scy again = !eai_again_seen; /* [Bug 1178] */ 1127275970Scy break; 1128275970Scy 1129275970Scy#ifdef EAI_SYSTEM 1130275970Scy case EAI_SYSTEM: 1131275970Scy /* 1132275970Scy * EAI_SYSTEM means the real error is in errno. We should be more 1133275970Scy * discriminating about which errno values require retrying, but 1134275970Scy * this matches existing behavior. 1135275970Scy */ 1136275970Scy again = 1; 1137275970Scy# ifdef DEBUG 1138275970Scy errno_to_str(res_errno, msg, sizeof(msg)); 1139275970Scy TRACE(1, ("intres: EAI_SYSTEM errno %d (%s) means try again, right?\n", 1140275970Scy res_errno, msg)); 1141275970Scy# endif 1142275970Scy break; 1143275970Scy#endif 1144275970Scy } 1145275970Scy 1146275970Scy TRACE(2, ("intres: resolver returned: %s (%d), %sretrying\n", 1147275970Scy gai_strerror(rescode), rescode, again ? "" : "not ")); 1148275970Scy 1149275970Scy return again; 1150275970Scy} 1151275970Scy 1152275970Scy#else /* !WORKER follows */ 1153275970Scyint ntp_intres_nonempty_compilation_unit; 1154275970Scy#endif 1155