Coverage Report

Created: 2023-05-19 06:16

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