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