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