Coverage Report

Created: 2026-08-31 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntp-dev/libntp/ntp_intres.c
Line
Count
Source
1
/*
2
 * ntp_intres.c - Implements a generic blocking worker child or thread,
3
 *      initially to provide a nonblocking solution for DNS
4
 *      name to address lookups available with getaddrinfo().
5
 *
6
 * This is a new implementation as of 2009 sharing the filename and
7
 * very little else with the prior implementation, which used a
8
 * temporary file to receive a single set of requests from the parent,
9
 * and a NTP mode 7 authenticated request to push back responses.
10
 *
11
 * A primary goal in rewriting this code was the need to support the
12
 * pool configuration directive's requirement to retrieve multiple
13
 * addresses resolving a single name, which has previously been
14
 * satisfied with blocking resolver calls from the ntpd mainline code.
15
 *
16
 * A secondary goal is to provide a generic mechanism for other
17
 * blocking operations to be delegated to a worker using a common
18
 * model for both Unix and Windows ntpd.  ntp_worker.c, work_fork.c,
19
 * and work_thread.c implement the generic mechanism.  This file
20
 * implements the two current consumers, getaddrinfo_sometime() and the
21
 * presently unused getnameinfo_sometime().
22
 *
23
 * Both routines deliver results to a callback and manage memory
24
 * allocation, meaning there is no freeaddrinfo_sometime().
25
 *
26
 * The initial implementation for Unix uses a pair of unidirectional
27
 * pipes, one each for requests and responses, connecting the forked
28
 * blocking child worker with the ntpd mainline.  The threaded code
29
 * uses arrays of pointers to queue requests and responses.
30
 *
31
 * The parent drives the process, including scheduling sleeps between
32
 * retries.
33
 *
34
 * Memory is managed differently for a child process, which mallocs
35
 * request buffers to read from the pipe into, whereas the threaded
36
 * code mallocs a copy of the request to hand off to the worker via
37
 * the queueing array.  The resulting request buffer is free()d by
38
 * platform-independent code.  A wrinkle is the request needs to be
39
 * available to the requestor during response processing.
40
 *
41
 * Response memory allocation is also platform-dependent.  With a
42
 * separate process and pipes, the response is free()d after being
43
 * written to the pipe.  With threads, the same memory is handed
44
 * over and the requestor frees it after processing is completed.
45
 *
46
 * The code should be generalized to support threads on Unix using
47
 * much of the same code used for Windows initially.
48
 *
49
 */
50
#ifdef HAVE_CONFIG_H
51
# include <config.h>
52
#endif
53
54
#include "ntp_workimpl.h"
55
56
#ifdef WORKER
57
58
#include <stdio.h>
59
#include <ctype.h>
60
#include <signal.h>
61
62
/**/
63
#ifdef HAVE_SYS_TYPES_H
64
# include <sys/types.h>
65
#endif
66
#ifdef HAVE_NETINET_IN_H
67
#include <netinet/in.h>
68
#endif
69
#include <arpa/inet.h>
70
/**/
71
#ifdef HAVE_SYS_PARAM_H
72
# include <sys/param.h>
73
#endif
74
75
#if !defined(HAVE_RES_INIT) && defined(HAVE___RES_INIT)
76
# define HAVE_RES_INIT
77
#endif
78
79
#if defined(HAVE_RESOLV_H) && defined(HAVE_RES_INIT)
80
# ifdef HAVE_ARPA_NAMESER_H
81
#  include <arpa/nameser.h> /* DNS HEADER struct */
82
# endif
83
# ifdef HAVE_NETDB_H
84
#  include <netdb.h>
85
# endif
86
# include <resolv.h>
87
#endif
88
89
#include "ntp.h"
90
#include "ntp_debug.h"
91
#include "ntp_malloc.h"
92
#include "ntp_syslog.h"
93
#include "ntp_unixtime.h"
94
#include "ntp_intres.h"
95
#include "intreswork.h"
96
97
98
/*
99
 * Following are implementations of getaddrinfo_sometime() and
100
 * getnameinfo_sometime().  Each is implemented in three routines:
101
 *
102
 * getaddrinfo_sometime()   getnameinfo_sometime()
103
 * blocking_getaddrinfo()   blocking_getnameinfo()
104
 * getaddrinfo_sometime_complete()  getnameinfo_sometime_complete()
105
 *
106
 * The first runs in the parent and marshalls (or serializes) request
107
 * parameters into a request blob which is processed in the child by
108
 * the second routine, blocking_*(), which serializes the results into
109
 * a response blob unpacked by the third routine, *_complete(), which
110
 * calls the callback routine provided with the request and frees
111
 * _request_ memory allocated by the first routine.  Response memory
112
 * is managed by the code which calls the *_complete routines.
113
 */
114
115
116
/* === typedefs === */
117
typedef struct blocking_gai_req_tag { /* marshalled args */
118
  size_t      octets;
119
  u_int     dns_idx;
120
  time_t      scheduled;
121
  time_t      earliest;
122
  int     retry;
123
  struct addrinfo   hints;
124
  u_int     qflags;
125
  gai_sometime_callback callback;
126
  void *      context;
127
  size_t      nodesize;
128
  size_t      servsize;
129
} blocking_gai_req;
130
131
typedef struct blocking_gai_resp_tag {
132
  size_t      octets;
133
  int     retcode;
134
  int     retry;
135
  int     gai_errno; /* for EAI_SYSTEM case */
136
  int     ai_count;
137
  /*
138
   * Followed by ai_count struct addrinfo and then ai_count
139
   * sockaddr_u and finally the canonical name strings.
140
   */
141
} blocking_gai_resp;
142
143
typedef struct blocking_gni_req_tag {
144
  size_t      octets;
145
  u_int     dns_idx;
146
  time_t      scheduled;
147
  time_t      earliest;
148
  int     retry;
149
  size_t      hostoctets;
150
  size_t      servoctets;
151
  int     flags;
152
  gni_sometime_callback callback;
153
  void *      context;
154
  sockaddr_u    socku;
155
} blocking_gni_req;
156
157
typedef struct blocking_gni_resp_tag {
158
  size_t      octets;
159
  int     retcode;
160
  int     gni_errno; /* for EAI_SYSTEM case */
161
  int     retry;
162
  size_t      hostoctets;
163
  size_t      servoctets;
164
  /*
165
   * Followed by hostoctets bytes of null-terminated host,
166
   * then servoctets bytes of null-terminated service.
167
   */
168
} blocking_gni_resp;
169
170
/* per-DNS-worker state in parent */
171
typedef struct dnschild_ctx_tag {
172
  u_int index;
173
  time_t  next_dns_timeslot;
174
} dnschild_ctx;
175
176
/* per-DNS-worker state in worker */
177
typedef struct dnsworker_ctx_tag {
178
  blocking_child *  c;
179
  time_t      ignore_scheduled_before;
180
#ifdef HAVE_RES_INIT
181
  time_t  next_res_init;
182
#endif
183
} dnsworker_ctx;
184
185
186
/* === variables === */
187
dnschild_ctx **   dnschild_contexts;    /* parent */
188
u_int     dnschild_contexts_alloc;
189
dnsworker_ctx **  dnsworker_contexts;   /* child */
190
u_int     dnsworker_contexts_alloc;
191
192
#ifdef HAVE_RES_INIT
193
static  time_t    next_res_init;
194
#endif
195
196
197
/* === forward declarations === */
198
static  u_int   reserve_dnschild_ctx(void);
199
static  u_int   get_dnschild_ctx(void);
200
static  dnsworker_ctx * get_worker_context(blocking_child *, u_int);
201
static  void    scheduled_sleep(time_t, time_t,
202
          dnsworker_ctx *);
203
static  void    manage_dns_retry_interval(time_t *, time_t *,
204
              int *, time_t *);
205
static  int   should_retry_dns(int, int);
206
#ifdef HAVE_RES_INIT
207
static  void    reload_resolv_conf(dnsworker_ctx *);
208
#else
209
# define    reload_resolv_conf(wc)    \
210
  do {            \
211
    (void)(wc);       \
212
  } while (FALSE)
213
#endif
214
static  void    getaddrinfo_sometime_complete(blocking_work_req,
215
                  void *, size_t,
216
                  void *);
217
static  void    getnameinfo_sometime_complete(blocking_work_req,
218
                  void *, size_t,
219
                  void *);
220
221
222
/* === functions === */
223
/*
224
 * getaddrinfo_sometime - uses blocking child to call getaddrinfo then
225
 *        invokes provided callback completion function.
226
 */
227
int
228
getaddrinfo_sometime_ex(
229
  const char *    node,
230
  const char *    service,
231
  const struct addrinfo * hints,
232
  int     retry,
233
  gai_sometime_callback callback,
234
  void *      context,
235
  u_int     qflags
236
  )
237
0
{
238
0
  blocking_gai_req *  gai_req;
239
0
  u_int     idx;
240
0
  dnschild_ctx *    child_ctx;
241
0
  size_t      req_size;
242
0
  size_t      nodesize;
243
0
  size_t      servsize;
244
0
  time_t      now;
245
  
246
0
  DEBUG_REQUIRE(NULL != node);
247
0
  if (NULL != hints) {
248
0
    DEBUG_REQUIRE(0 == hints->ai_addrlen);
249
0
    DEBUG_REQUIRE(NULL == hints->ai_addr);
250
0
    DEBUG_REQUIRE(NULL == hints->ai_canonname);
251
0
    DEBUG_REQUIRE(NULL == hints->ai_next);
252
0
  }
253
254
0
  idx = get_dnschild_ctx();
255
0
  child_ctx = dnschild_contexts[idx];
256
257
0
  nodesize = strlen(node) + 1;
258
0
  servsize = strlen(service) + 1;
259
0
  req_size = sizeof(*gai_req) + nodesize + servsize;
260
261
0
  gai_req = emalloc_zero(req_size);
262
263
0
  gai_req->octets = req_size;
264
0
  gai_req->dns_idx = idx;
265
0
  now = time(NULL);
266
0
  gai_req->scheduled = now;
267
0
  gai_req->earliest = max(now, child_ctx->next_dns_timeslot);
268
0
  child_ctx->next_dns_timeslot = gai_req->earliest;
269
0
  if (hints != NULL)
270
0
    gai_req->hints = *hints;
271
0
  gai_req->retry = retry;
272
0
  gai_req->callback = callback;
273
0
  gai_req->context = context;
274
0
  gai_req->nodesize = nodesize;
275
0
  gai_req->servsize = servsize;
276
0
  gai_req->qflags = qflags;
277
278
0
  memcpy((char *)gai_req + sizeof(*gai_req), node, nodesize);
279
0
  memcpy((char *)gai_req + sizeof(*gai_req) + nodesize, service,
280
0
         servsize);
281
282
0
  if (queue_blocking_request(
283
0
    BLOCKING_GETADDRINFO,
284
0
    gai_req,
285
0
    req_size, 
286
0
    &getaddrinfo_sometime_complete, 
287
0
    gai_req)) {
288
289
0
    msyslog(LOG_ERR, "unable to queue getaddrinfo request");
290
0
    errno = EFAULT;
291
0
    return -1;
292
0
  }
293
294
0
  return 0;
295
0
}
296
297
int
298
blocking_getaddrinfo(
299
  blocking_child *  c,
300
  blocking_pipe_header *  req
301
  )
302
0
{
303
0
  blocking_gai_req *  gai_req;
304
0
  dnsworker_ctx *   worker_ctx;
305
0
  blocking_pipe_header *  resp;
306
0
  blocking_gai_resp * gai_resp;
307
0
  char *      node;
308
0
  char *      service;
309
0
  struct addrinfo * ai_res;
310
0
  struct addrinfo * ai;
311
0
  struct addrinfo * serialized_ai;
312
0
  size_t      canons_octets;
313
0
  size_t      this_octets;
314
0
  size_t      resp_octets;
315
0
  char *      cp;
316
0
  time_t      time_now;
317
318
0
  gai_req = (void *)((char *)req + sizeof(*req));
319
0
  node = (char *)gai_req + sizeof(*gai_req);
320
0
  service = node + gai_req->nodesize;
321
322
0
  worker_ctx = get_worker_context(c, gai_req->dns_idx);
323
0
  scheduled_sleep(gai_req->scheduled, gai_req->earliest,
324
0
      worker_ctx);
325
0
  reload_resolv_conf(worker_ctx);
326
327
  /*
328
   * Take a shot at the final size, better to overestimate
329
   * at first and then realloc to a smaller size.
330
   */
331
332
0
  resp_octets = sizeof(*resp) + sizeof(*gai_resp) +
333
0
          16 * (sizeof(struct addrinfo) +
334
0
          sizeof(sockaddr_u)) +
335
0
          256;
336
0
  resp = emalloc_zero(resp_octets);
337
0
  gai_resp = (void *)(resp + 1);
338
339
0
  TRACE(2, ("blocking_getaddrinfo given node %s serv %s fam %d flags %x\n", 
340
0
      node, service, gai_req->hints.ai_family,
341
0
      gai_req->hints.ai_flags));
342
0
#ifdef DEBUG
343
0
  if (debug >= 2)
344
0
    fflush(stdout);
345
0
#endif  
346
0
  ai_res = NULL;
347
0
  gai_resp->retcode = getaddrinfo(node, service, &gai_req->hints,
348
0
          &ai_res);
349
0
  gai_resp->retry = gai_req->retry;
350
0
#ifdef EAI_SYSTEM
351
0
  if (EAI_SYSTEM == gai_resp->retcode)
352
0
    gai_resp->gai_errno = errno;
353
0
#endif
354
0
  canons_octets = 0;
355
356
0
  if (0 == gai_resp->retcode) {
357
0
    ai = ai_res;
358
0
    while (NULL != ai) {
359
0
      gai_resp->ai_count++;
360
0
      if (ai->ai_canonname)
361
0
        canons_octets += strlen(ai->ai_canonname) + 1;
362
0
      ai = ai->ai_next;
363
0
    }
364
    /*
365
     * If this query succeeded only after retrying, DNS may have
366
     * just become responsive.  Ignore previously-scheduled
367
     * retry sleeps once for each pending request, similar to
368
     * the way scheduled_sleep() does when its worker_sleep()
369
     * is interrupted.
370
     */
371
0
    if (gai_resp->retry > INITIAL_DNS_RETRY) {
372
0
      time_now = time(NULL);
373
0
      worker_ctx->ignore_scheduled_before = time_now;
374
0
      TRACE(1, ("DNS success after retry, ignoring sleeps scheduled before now (%s)\n",
375
0
          humantime(time_now)));
376
0
    }
377
0
  }
378
379
  /*
380
   * Our response consists of a header, followed by ai_count 
381
   * addrinfo structs followed by ai_count sockaddr_u structs
382
   * followed by the canonical names.
383
   */
384
0
  gai_resp->octets = sizeof(*gai_resp)
385
0
          + gai_resp->ai_count
386
0
        * (sizeof(gai_req->hints)
387
0
           + sizeof(sockaddr_u))
388
0
          + canons_octets;
389
390
0
  resp_octets = sizeof(*resp) + gai_resp->octets;
391
0
  resp = erealloc(resp, resp_octets);
392
0
  gai_resp = (void *)(resp + 1);
393
394
  /* cp serves as our current pointer while serializing */
395
0
  cp = (void *)(gai_resp + 1);
396
0
  canons_octets = 0;
397
398
0
  if (0 == gai_resp->retcode) {
399
0
    ai = ai_res;
400
0
    while (NULL != ai) {
401
0
      memcpy(cp, ai, sizeof(*ai));
402
0
      serialized_ai = (void *)cp;
403
0
      cp += sizeof(*ai);
404
405
      /* transform ai_canonname into offset */
406
0
      if (NULL != ai->ai_canonname) {
407
0
        serialized_ai->ai_canonname = (char *)canons_octets;
408
0
        canons_octets += strlen(ai->ai_canonname) + 1;
409
0
      }
410
      
411
      /* leave fixup of ai_addr pointer for receiver */
412
413
0
      ai = ai->ai_next;
414
0
    }
415
416
0
    ai = ai_res;
417
0
    while (NULL != ai) {
418
0
      INSIST(ai->ai_addrlen <= sizeof(sockaddr_u));
419
0
      memcpy(cp, ai->ai_addr, ai->ai_addrlen);
420
0
      cp += sizeof(sockaddr_u);
421
422
0
      ai = ai->ai_next;
423
0
    }
424
425
0
    ai = ai_res;
426
0
    while (NULL != ai) {
427
0
      if (NULL != ai->ai_canonname) {
428
0
        this_octets = strlen(ai->ai_canonname) + 1;
429
0
        memcpy(cp, ai->ai_canonname, this_octets);
430
0
        cp += this_octets;
431
0
      }
432
433
0
      ai = ai->ai_next;
434
0
    }
435
0
    freeaddrinfo(ai_res);
436
0
  }
437
438
  /*
439
   * make sure our walk and earlier calc match
440
   */
441
0
  DEBUG_INSIST((size_t)(cp - (char *)resp) == resp_octets);
442
443
0
  if (queue_blocking_response(c, resp, resp_octets, req)) {
444
0
    msyslog(LOG_ERR, "blocking_getaddrinfo can not queue response");
445
0
    return -1;
446
0
  }
447
448
0
  return 0;
449
0
}
450
451
int
452
getaddrinfo_sometime(
453
  const char *    node,
454
  const char *    service,
455
  const struct addrinfo * hints,
456
  int     retry,
457
  gai_sometime_callback callback,
458
  void *      context
459
  )
460
0
{
461
0
  return getaddrinfo_sometime_ex(node, service, hints, retry,
462
0
               callback, context, 0);
463
0
}
464
465
466
static void
467
getaddrinfo_sometime_complete(
468
  blocking_work_req rtype,
469
  void *      context,
470
  size_t      respsize,
471
  void *      resp
472
  )
473
0
{
474
0
  blocking_gai_req *  gai_req;
475
0
  blocking_gai_resp * gai_resp;
476
0
  dnschild_ctx *    child_ctx;
477
0
  struct addrinfo * ai;
478
0
  struct addrinfo * next_ai;
479
0
  sockaddr_u *    psau;
480
0
  char *      node;
481
0
  char *      service;
482
0
  char *      canon_start;
483
0
  time_t      time_now;
484
0
  int     again;
485
0
  int     af;
486
0
  const char *    fam_spec;
487
0
  int     i;
488
489
0
  gai_req = context;
490
0
  gai_resp = resp;
491
492
0
  DEBUG_REQUIRE(BLOCKING_GETADDRINFO == rtype);
493
0
  DEBUG_REQUIRE(respsize == gai_resp->octets);
494
495
0
  node = (char *)gai_req + sizeof(*gai_req);
496
0
  service = node + gai_req->nodesize;
497
498
0
  child_ctx = dnschild_contexts[gai_req->dns_idx];
499
500
0
  if (0 == gai_resp->retcode) {
501
    /*
502
     * If this query succeeded only after retrying, DNS may have
503
     * just become responsive.
504
     */
505
0
    if (gai_resp->retry > INITIAL_DNS_RETRY) {
506
0
      time_now = time(NULL);
507
0
      child_ctx->next_dns_timeslot = time_now;
508
0
      TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n",
509
0
          gai_req->dns_idx, humantime(time_now)));
510
0
    }
511
0
  } else {
512
0
    again =    !!(gai_req->qflags & GAIR_F_IGNDNSERR)
513
0
      || should_retry_dns(gai_resp->retcode,
514
0
              gai_resp->gai_errno);
515
0
    if (gai_req->retry > 0 && again) {
516
      /* log the first retry only */
517
0
      if (INITIAL_DNS_RETRY == gai_req->retry) {
518
0
        NLOG(NLOG_SYSINFO) {
519
0
          af = gai_req->hints.ai_family;
520
0
          fam_spec = (AF_INET6 == af)
521
0
            ? " (AAAA)"
522
0
            : (AF_INET == af)
523
0
            ? " (A)"
524
0
            : "";
525
0
#ifdef EAI_SYSTEM
526
0
          if (EAI_SYSTEM == gai_resp->retcode) {
527
0
            errno = gai_resp->gai_errno;
528
0
            msyslog(LOG_INFO,
529
0
              "retrying DNS %s%s: EAI_SYSTEM %d: %m",
530
0
              node, fam_spec,
531
0
              gai_resp->gai_errno);
532
0
          } else
533
0
#endif
534
0
          {
535
0
            msyslog(LOG_INFO,
536
0
              "retrying DNS %s%s: %s (%d)",
537
0
              node, fam_spec,
538
0
              gai_strerror(gai_resp->retcode),
539
0
              gai_resp->retcode);
540
0
          }
541
0
        }
542
0
      }
543
0
      manage_dns_retry_interval(
544
0
        &gai_req->scheduled, &gai_req->earliest,
545
0
        &gai_req->retry, &child_ctx->next_dns_timeslot);
546
0
      if (!queue_blocking_request(
547
0
          BLOCKING_GETADDRINFO,
548
0
          gai_req,
549
0
          gai_req->octets,
550
0
          &getaddrinfo_sometime_complete,
551
0
          gai_req)) {
552
0
        return;
553
0
      } else {
554
0
        msyslog(LOG_ERR,
555
0
          "unable to retry hostname %s", node);
556
0
      }
557
0
    }
558
0
  }
559
560
  /*
561
   * fixup pointers in returned addrinfo array
562
   */
563
0
  ai = (void *)((char *)gai_resp + sizeof(*gai_resp));
564
0
  next_ai = NULL;
565
0
  for (i = gai_resp->ai_count - 1; i >= 0; i--) {
566
0
    ai[i].ai_next = next_ai;
567
0
    next_ai = &ai[i];
568
0
  }
569
570
0
  psau = (void *)((char *)ai + gai_resp->ai_count * sizeof(*ai));
571
0
  canon_start = (char *)psau + gai_resp->ai_count * sizeof(*psau);
572
573
0
  for (i = 0; i < gai_resp->ai_count; i++) {
574
0
    if (NULL != ai[i].ai_addr)
575
0
      ai[i].ai_addr = &psau->sa;
576
0
    psau++;
577
0
    if (NULL != ai[i].ai_canonname)
578
0
      ai[i].ai_canonname += (size_t)canon_start;
579
0
  }
580
581
0
  ENSURE((char *)psau == canon_start);
582
583
0
  if (!gai_resp->ai_count)
584
0
    ai = NULL;
585
  
586
0
  (*gai_req->callback)(gai_resp->retcode, gai_resp->gai_errno,
587
0
           gai_req->context, node, service, 
588
0
           &gai_req->hints, ai);
589
590
0
  free(gai_req);
591
  /* gai_resp is part of block freed by process_blocking_resp() */
592
0
}
593
594
595
#ifdef TEST_BLOCKING_WORKER
596
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)
597
{
598
  sockaddr_u addr;
599
600
  if (rescode) {
601
    TRACE(1, ("gai_test_callback context %p error rescode %d %s serv %s\n",
602
        context, rescode, name, service));
603
    return;
604
  }
605
  while (!rescode && NULL != ai_res) {
606
    ZERO_SOCK(&addr);
607
    memcpy(&addr, ai_res->ai_addr, ai_res->ai_addrlen);
608
    TRACE(1, ("ctx %p fam %d addr %s canon '%s' type %s at %p ai_addr %p ai_next %p\n", 
609
        context,
610
        AF(&addr),
611
        stoa(&addr), 
612
        (ai_res->ai_canonname)
613
            ? ai_res->ai_canonname
614
            : "",
615
        (SOCK_DGRAM == ai_res->ai_socktype) 
616
            ? "DGRAM" 
617
            : (SOCK_STREAM == ai_res->ai_socktype) 
618
            ? "STREAM" 
619
            : "(other)",
620
        ai_res,
621
        ai_res->ai_addr,
622
        ai_res->ai_next));
623
624
    getnameinfo_sometime((sockaddr_u *)ai_res->ai_addr, 128, 32, 0, gni_test_callback, context);
625
626
    ai_res = ai_res->ai_next;
627
  }
628
}
629
#endif  /* TEST_BLOCKING_WORKER */
630
631
632
int
633
getnameinfo_sometime(
634
  sockaddr_u *    psau,
635
  size_t      hostoctets,
636
  size_t      servoctets,
637
  int     flags,
638
  gni_sometime_callback callback,
639
  void *      context
640
  )
641
0
{
642
0
  blocking_gni_req *  gni_req;
643
0
  u_int     idx;
644
0
  dnschild_ctx *    child_ctx;
645
0
  time_t      time_now;
646
  
647
0
  REQUIRE(hostoctets);
648
0
  REQUIRE(hostoctets + servoctets < 1024);
649
650
0
  idx = get_dnschild_ctx();
651
0
  child_ctx = dnschild_contexts[idx];
652
653
0
  gni_req = emalloc_zero(sizeof(*gni_req));
654
655
0
  gni_req->octets = sizeof(*gni_req);
656
0
  gni_req->dns_idx = idx;
657
0
  time_now = time(NULL);
658
0
  gni_req->scheduled = time_now;
659
0
  gni_req->earliest = max(time_now, child_ctx->next_dns_timeslot);
660
0
  child_ctx->next_dns_timeslot = gni_req->earliest;
661
0
  memcpy(&gni_req->socku, psau, SOCKLEN(psau));
662
0
  gni_req->hostoctets = hostoctets;
663
0
  gni_req->servoctets = servoctets;
664
0
  gni_req->flags = flags;
665
0
  gni_req->retry = INITIAL_DNS_RETRY;
666
0
  gni_req->callback = callback;
667
0
  gni_req->context = context;
668
669
0
  if (queue_blocking_request(
670
0
    BLOCKING_GETNAMEINFO,
671
0
    gni_req,
672
0
    sizeof(*gni_req), 
673
0
    &getnameinfo_sometime_complete, 
674
0
    gni_req)) {
675
676
0
    msyslog(LOG_ERR, "unable to queue getnameinfo request");
677
0
    errno = EFAULT;
678
0
    return -1;
679
0
  }
680
681
0
  return 0;
682
0
}
683
684
685
int
686
blocking_getnameinfo(
687
  blocking_child *  c,
688
  blocking_pipe_header *  req
689
  )
690
0
{
691
0
  blocking_gni_req *  gni_req;
692
0
  dnsworker_ctx *   worker_ctx;
693
0
  blocking_pipe_header *  resp;
694
0
  blocking_gni_resp * gni_resp;
695
0
  size_t      octets;
696
0
  size_t      resp_octets;
697
0
  char *      service;
698
0
  char *      cp;
699
0
  int     rc;
700
0
  time_t      time_now;
701
0
  char      host[1024];
702
703
0
  gni_req = (void *)((char *)req + sizeof(*req));
704
705
0
  octets = gni_req->hostoctets + gni_req->servoctets;
706
707
  /*
708
   * Some alloca() implementations are fragile regarding
709
   * large allocations.  We only need room for the host
710
   * and service names.
711
   */
712
0
  REQUIRE(octets < sizeof(host));
713
0
  service = host + gni_req->hostoctets;
714
715
0
  worker_ctx = get_worker_context(c, gni_req->dns_idx);
716
0
  scheduled_sleep(gni_req->scheduled, gni_req->earliest,
717
0
      worker_ctx);
718
0
  reload_resolv_conf(worker_ctx);
719
720
  /*
721
   * Take a shot at the final size, better to overestimate
722
   * then realloc to a smaller size.
723
   */
724
725
0
  resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets;
726
0
  resp = emalloc_zero(resp_octets);
727
0
  gni_resp = (void *)((char *)resp + sizeof(*resp));
728
729
0
  TRACE(2, ("blocking_getnameinfo given addr %s flags 0x%x hostlen %lu servlen %lu\n",
730
0
      stoa(&gni_req->socku), gni_req->flags,
731
0
      (u_long)gni_req->hostoctets, (u_long)gni_req->servoctets));
732
  
733
0
  gni_resp->retcode = getnameinfo(&gni_req->socku.sa,
734
0
          SOCKLEN(&gni_req->socku),
735
0
          host,
736
0
          gni_req->hostoctets,
737
0
          service,
738
0
          gni_req->servoctets,
739
0
          gni_req->flags);
740
0
  gni_resp->retry = gni_req->retry;
741
0
#ifdef EAI_SYSTEM
742
0
  if (EAI_SYSTEM == gni_resp->retcode)
743
0
    gni_resp->gni_errno = errno;
744
0
#endif
745
746
0
  if (0 != gni_resp->retcode) {
747
0
    gni_resp->hostoctets = 0;
748
0
    gni_resp->servoctets = 0;
749
0
  } else {
750
0
    gni_resp->hostoctets = strlen(host) + 1;
751
0
    gni_resp->servoctets = strlen(service) + 1;
752
    /*
753
     * If this query succeeded only after retrying, DNS may have
754
     * just become responsive.  Ignore previously-scheduled
755
     * retry sleeps once for each pending request, similar to
756
     * the way scheduled_sleep() does when its worker_sleep()
757
     * is interrupted.
758
     */
759
0
    if (gni_req->retry > INITIAL_DNS_RETRY) {
760
0
      time_now = time(NULL);
761
0
      worker_ctx->ignore_scheduled_before = time_now;
762
0
      TRACE(1, ("DNS success after retrying, ignoring sleeps scheduled before now (%s)\n",
763
0
        humantime(time_now)));
764
0
    }
765
0
  }
766
0
  octets = gni_resp->hostoctets + gni_resp->servoctets;
767
  /*
768
   * Our response consists of a header, followed by the host and
769
   * service strings, each null-terminated.
770
   */
771
0
  resp_octets = sizeof(*resp) + sizeof(*gni_resp) + octets;
772
773
0
  resp = erealloc(resp, resp_octets);
774
0
  gni_resp = (void *)(resp + 1);
775
776
0
  gni_resp->octets = sizeof(*gni_resp) + octets;
777
778
  /* cp serves as our current pointer while serializing */
779
0
  cp = (void *)(gni_resp + 1);
780
781
0
  if (0 == gni_resp->retcode) {
782
0
    memcpy(cp, host, gni_resp->hostoctets);
783
0
    cp += gni_resp->hostoctets;
784
0
    memcpy(cp, service, gni_resp->servoctets);
785
0
    cp += gni_resp->servoctets;
786
0
  }
787
788
0
  INSIST((size_t)(cp - (char *)resp) == resp_octets);
789
0
  INSIST(resp_octets - sizeof(*resp) == gni_resp->octets);
790
791
0
  rc = queue_blocking_response(c, resp, resp_octets, req);
792
0
  if (rc)
793
0
    msyslog(LOG_ERR, "blocking_getnameinfo unable to queue response");
794
0
  return rc;
795
0
}
796
797
798
static void
799
getnameinfo_sometime_complete(
800
  blocking_work_req rtype,
801
  void *      context,
802
  size_t      respsize,
803
  void *      resp
804
  )
805
0
{
806
0
  blocking_gni_req *  gni_req;
807
0
  blocking_gni_resp * gni_resp;
808
0
  dnschild_ctx *    child_ctx;
809
0
  char *      host;
810
0
  char *      service;
811
0
  time_t      time_now;
812
0
  int     again;
813
814
0
  gni_req = context;
815
0
  gni_resp = resp;
816
817
0
  DEBUG_REQUIRE(BLOCKING_GETNAMEINFO == rtype);
818
0
  DEBUG_REQUIRE(respsize == gni_resp->octets);
819
820
0
  child_ctx = dnschild_contexts[gni_req->dns_idx];
821
822
0
  if (0 == gni_resp->retcode) {
823
    /*
824
     * If this query succeeded only after retrying, DNS may have
825
     * just become responsive.
826
     */
827
0
    if (gni_resp->retry > INITIAL_DNS_RETRY) {
828
0
      time_now = time(NULL);
829
0
      child_ctx->next_dns_timeslot = time_now;
830
0
      TRACE(1, ("DNS success after retry, %u next_dns_timeslot reset (%s)\n",
831
0
          gni_req->dns_idx, humantime(time_now)));
832
0
    }
833
0
  } else {
834
0
    again = should_retry_dns(gni_resp->retcode, gni_resp->gni_errno);
835
    /*
836
     * exponential backoff of DNS retries to 64s
837
     */
838
0
    if (gni_req->retry > 0)
839
0
      manage_dns_retry_interval(&gni_req->scheduled,
840
0
        &gni_req->earliest, &gni_req->retry,
841
0
        &child_ctx->next_dns_timeslot);
842
843
0
    if (gni_req->retry > 0 && again) {
844
0
      if (!queue_blocking_request(
845
0
        BLOCKING_GETNAMEINFO,
846
0
        gni_req,
847
0
        gni_req->octets, 
848
0
        &getnameinfo_sometime_complete, 
849
0
        gni_req))
850
0
        return;
851
852
0
      msyslog(LOG_ERR, "unable to retry reverse lookup of %s", stoa(&gni_req->socku));
853
0
    }
854
0
  }
855
856
0
  if (!gni_resp->hostoctets) {
857
0
    host = NULL;
858
0
    service = NULL;
859
0
  } else {
860
0
    host = (char *)gni_resp + sizeof(*gni_resp);
861
0
    service = (gni_resp->servoctets) 
862
0
            ? host + gni_resp->hostoctets
863
0
            : NULL;
864
0
  }
865
866
0
  (*gni_req->callback)(gni_resp->retcode, gni_resp->gni_errno,
867
0
           &gni_req->socku, gni_req->flags, host,
868
0
           service, gni_req->context);
869
870
0
  free(gni_req);
871
  /* gni_resp is part of block freed by process_blocking_resp() */
872
0
}
873
874
875
#ifdef TEST_BLOCKING_WORKER
876
void gni_test_callback(int rescode, int gni_errno, sockaddr_u *psau, int flags, const char *host, const char *service, void *context)
877
{
878
  if (!rescode)
879
    TRACE(1, ("gni_test_callback got host '%s' serv '%s' for addr %s context %p\n", 
880
        host, service, stoa(psau), context));
881
  else
882
    TRACE(1, ("gni_test_callback context %p rescode %d gni_errno %d flags 0x%x addr %s\n",
883
        context, rescode, gni_errno, flags, stoa(psau)));
884
}
885
#endif  /* TEST_BLOCKING_WORKER */
886
887
888
#ifdef HAVE_RES_INIT
889
static void
890
reload_resolv_conf(
891
  dnsworker_ctx * worker_ctx
892
  )
893
0
{
894
0
  time_t  time_now;
895
896
  /*
897
   * This is ad-hoc.  Reload /etc/resolv.conf once per minute
898
   * to pick up on changes from the DHCP client.  [Bug 1226]
899
   * When using threads for the workers, this needs to happen
900
   * only once per minute process-wide.
901
   */
902
0
  time_now = time(NULL);
903
0
# ifdef WORK_THREAD
904
0
  worker_ctx->next_res_init = next_res_init;
905
0
# endif
906
0
  if (worker_ctx->next_res_init <= time_now) {
907
0
    if (worker_ctx->next_res_init != 0)
908
0
      res_init();
909
0
    worker_ctx->next_res_init = time_now + 60;
910
0
# ifdef WORK_THREAD
911
0
    next_res_init = worker_ctx->next_res_init;
912
0
# endif
913
0
  }
914
0
}
915
#endif  /* HAVE_RES_INIT */
916
917
918
static u_int
919
reserve_dnschild_ctx(void)
920
0
{
921
0
  const size_t  ps = sizeof(dnschild_contexts[0]);
922
0
  const size_t  cs = sizeof(*dnschild_contexts[0]);
923
0
  u_int   c;
924
0
  u_int   new_alloc;
925
0
  size_t    octets;
926
0
  size_t    new_octets;
927
928
0
  c = 0;
929
0
  while (TRUE) {
930
0
    for ( ; c < dnschild_contexts_alloc; c++) {
931
0
      if (NULL == dnschild_contexts[c]) {
932
0
        dnschild_contexts[c] = emalloc_zero(cs);
933
934
0
        return c;
935
0
      }
936
0
    }
937
0
    new_alloc = dnschild_contexts_alloc + 20;
938
0
    new_octets = new_alloc * ps;
939
0
    octets = dnschild_contexts_alloc * ps;
940
0
    dnschild_contexts = erealloc_zero(dnschild_contexts,
941
0
              new_octets, octets);
942
0
    dnschild_contexts_alloc = new_alloc;
943
0
  }
944
0
}
945
946
947
static u_int
948
get_dnschild_ctx(void)
949
0
{
950
0
  static u_int  shared_ctx = UINT_MAX;
951
952
0
  if (worker_per_query)
953
0
    return reserve_dnschild_ctx();
954
955
0
  if (UINT_MAX == shared_ctx)
956
0
    shared_ctx = reserve_dnschild_ctx();
957
958
0
  return shared_ctx;
959
0
}
960
961
962
static dnsworker_ctx *
963
get_worker_context(
964
  blocking_child *  c,
965
  u_int     idx
966
  )
967
0
{
968
0
  u_int   min_new_alloc;
969
0
  u_int   new_alloc;
970
0
  size_t    octets;
971
0
  size_t    new_octets;
972
0
  dnsworker_ctx * retv;
973
974
0
  worker_global_lock(TRUE);
975
  
976
0
  if (dnsworker_contexts_alloc <= idx) {
977
0
    min_new_alloc = 1 + idx;
978
    /* round new_alloc up to nearest multiple of 4 */
979
0
    new_alloc = (min_new_alloc + 4) & ~(4 - 1);
980
0
    new_octets = new_alloc * sizeof(dnsworker_ctx*);
981
0
    octets = dnsworker_contexts_alloc * sizeof(dnsworker_ctx*);
982
0
    dnsworker_contexts = erealloc_zero(dnsworker_contexts,
983
0
               new_octets, octets);
984
0
    dnsworker_contexts_alloc = new_alloc;
985
0
    retv = emalloc_zero(sizeof(dnsworker_ctx));
986
0
    dnsworker_contexts[idx] = retv;
987
0
  } else if (NULL == (retv = dnsworker_contexts[idx])) {
988
0
    retv = emalloc_zero(sizeof(dnsworker_ctx));
989
0
    dnsworker_contexts[idx] = retv;
990
0
  }
991
  
992
0
  worker_global_lock(FALSE);
993
  
994
0
  ZERO(*retv);
995
0
  retv->c = c;
996
0
  return retv;
997
0
}
998
999
1000
static void
1001
scheduled_sleep(
1002
  time_t    scheduled,
1003
  time_t    earliest,
1004
  dnsworker_ctx * worker_ctx
1005
  )
1006
0
{
1007
0
  time_t now;
1008
1009
0
  if (scheduled < worker_ctx->ignore_scheduled_before) {
1010
0
    TRACE(1, ("ignoring sleep until %s scheduled at %s (before %s)\n",
1011
0
        humantime(earliest), humantime(scheduled),
1012
0
        humantime(worker_ctx->ignore_scheduled_before)));
1013
0
    return;
1014
0
  }
1015
1016
0
  now = time(NULL);
1017
1018
0
  if (now < earliest) {
1019
0
    TRACE(1, ("sleep until %s scheduled at %s (>= %s)\n",
1020
0
        humantime(earliest), humantime(scheduled),
1021
0
        humantime(worker_ctx->ignore_scheduled_before)));
1022
0
    if (-1 == worker_sleep(worker_ctx->c, earliest - now)) {
1023
      /* our sleep was interrupted */
1024
0
      now = time(NULL);
1025
0
      worker_ctx->ignore_scheduled_before = now;
1026
0
#ifdef HAVE_RES_INIT
1027
0
      worker_ctx->next_res_init = now + 60;
1028
0
      next_res_init = worker_ctx->next_res_init;
1029
0
      res_init();
1030
0
#endif
1031
0
      TRACE(1, ("sleep interrupted by daemon, ignoring sleeps scheduled before now (%s)\n",
1032
0
          humantime(worker_ctx->ignore_scheduled_before)));
1033
0
    }
1034
0
  }
1035
0
}
1036
1037
1038
/*
1039
 * manage_dns_retry_interval is a helper used by
1040
 * getaddrinfo_sometime_complete and getnameinfo_sometime_complete
1041
 * to calculate the new retry interval and schedule the next query.
1042
 */
1043
static void
1044
manage_dns_retry_interval(
1045
  time_t *  pscheduled,
1046
  time_t *  pwhen,
1047
  int *   pretry,
1048
  time_t *  pnext_timeslot
1049
  )
1050
0
{
1051
0
  time_t  now;
1052
0
  time_t  when;
1053
0
  int retry;
1054
    
1055
0
  now = time(NULL);
1056
0
  retry = *pretry;
1057
0
  when = max(now + retry, *pnext_timeslot);
1058
0
  *pnext_timeslot = when;
1059
1060
  /*
1061
   * Increase retry slowly. The sequence is:
1062
   * 1-2-3-4-5-6-7-8-10-12-15-18-22-27-33...1024.
1063
   */
1064
0
  retry = (retry * 5) / 4;
1065
0
  retry = min3(retry, *pretry + 1, 1024);
1066
1067
0
  *pscheduled = now;
1068
0
  *pwhen = when;
1069
0
  *pretry = retry;
1070
0
}
1071
1072
/*
1073
 * should_retry_dns is a helper used by getaddrinfo_sometime_complete
1074
 * and getnameinfo_sometime_complete which implements ntpd's DNS retry
1075
 * policy.
1076
 */
1077
static int/*BOOL*/
1078
should_retry_dns(
1079
  int rescode,
1080
  int res_errno
1081
  )
1082
0
{
1083
0
  static int  eai_again_seen;
1084
0
  int   again;
1085
0
#if defined (EAI_SYSTEM) && defined(DEBUG)
1086
0
  char    msg[256];
1087
0
#endif
1088
1089
  /*
1090
   * If the resolver failed, see if the failure is
1091
   * temporary. If so, return success.
1092
   */
1093
0
  again = 0;
1094
1095
0
  switch (rescode) {
1096
1097
0
  case EAI_FAIL:
1098
0
    again = 1;
1099
0
    break;
1100
1101
0
  case EAI_AGAIN:
1102
0
    again = 1;
1103
0
    eai_again_seen = 1;   /* [Bug 1178] */
1104
0
    break;
1105
1106
  /*
1107
   * Windows 10 returns a permanent failure (WSANO_DATA) when there
1108
   * is an A or AAAA record available but no local address is up yet
1109
   * which might be able to communicate with the requested address.
1110
   * This is the documented behavior for Windows Vista and later when
1111
   * AI_ADDRCONFIG is used, but happens on Windows 10 even without
1112
   * AI_ADDRCONFIG.  This can cause ntpd to give up on resolving
1113
   * hostnames before a local address comes up during OS boot due
1114
   * to devices not yet up or DHCP not having yet completed.  To
1115
   * avoid this, always retry queries on Windows until the system
1116
   * has been up for 10 minutes.  GetTickCount() returns the number
1117
   * of milliseconds since boot, wrapping after 49.7 days.
1118
   */
1119
#if defined(SYS_WINNT) &&  (EAI_NODATA == EAI_NONAME)
1120
#undef EAI_NODATA  /* encrusted WS2tcpip.h idiocy */
1121
#define EAI_NODATA WSANO_DATA
1122
#endif
1123
1124
0
#ifdef EAI_NODATA
1125
0
  case EAI_NODATA:
1126
0
#endif
1127
0
  case EAI_NONAME:
1128
#ifdef SYS_WINNT
1129
    if (GetTickCount() < 10 * SECSPERMIN * 1000) {
1130
      again = 1;  /* https://bugs.ntp.org/3924 */
1131
      break;
1132
    }
1133
#endif
1134
0
    again = !eai_again_seen;  /* [Bug 1178] */
1135
0
    break;
1136
1137
0
#ifdef EAI_SYSTEM
1138
0
  case EAI_SYSTEM:
1139
    /* 
1140
     * EAI_SYSTEM means the real error is in errno.  We should be more
1141
     * discriminating about which errno values require retrying, but
1142
     * this matches existing behavior.
1143
     */
1144
0
    again = 1;
1145
0
# ifdef DEBUG
1146
0
    errno_to_str(res_errno, msg, sizeof(msg));
1147
0
    TRACE(1, ("intres: EAI_SYSTEM errno %d (%s) means try again, right?\n",
1148
0
        res_errno, msg));
1149
0
# endif
1150
0
    break;
1151
0
#endif
1152
0
  }
1153
1154
0
  TRACE(2, ("intres: resolver returned: %s (%d), %sretrying\n",
1155
0
      gai_strerror(rescode), rescode, again ? "" : "not "));
1156
1157
0
  return again;
1158
0
}
1159
1160
#else /* !WORKER follows */
1161
NONEMPTY_TRANSLATION_UNIT
1162
#endif