Coverage Report

Created: 2025-11-11 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/notify.c
Line
Count
Source
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
/*! \file */
15
16
#include <isc/netmgr.h>
17
#include <isc/ratelimiter.h>
18
#include <isc/result.h>
19
20
#include <dns/adb.h>
21
#include <dns/notify.h>
22
#include <dns/peer.h>
23
#include <dns/rcode.h>
24
#include <dns/rdatalist.h>
25
#include <dns/request.h>
26
#include <dns/stats.h>
27
#include <dns/tsig.h>
28
#include <dns/zone.h>
29
30
#include "zone_p.h"
31
32
static void
33
0
notify_log(dns_notify_t *notify, int level, const char *fmt, ...) {
34
0
  va_list ap;
35
36
0
  va_start(ap, fmt);
37
0
  dns_zone_logv(notify->zone, DNS_LOGCATEGORY_NOTIFY, level, NULL, fmt,
38
0
          ap);
39
0
  va_end(ap);
40
0
}
41
42
void
43
0
dns_notify_create(isc_mem_t *mctx, unsigned int flags, dns_notify_t **notifyp) {
44
0
  dns_notify_t *notify;
45
46
0
  REQUIRE(notifyp != NULL && *notifyp == NULL);
47
48
0
  notify = isc_mem_get(mctx, sizeof(*notify));
49
0
  *notify = (dns_notify_t){
50
0
    .flags = flags,
51
0
  };
52
53
0
  isc_mem_attach(mctx, &notify->mctx);
54
0
  isc_sockaddr_any(&notify->src);
55
0
  isc_sockaddr_any(&notify->dst);
56
0
  dns_name_init(&notify->ns);
57
0
  ISC_LINK_INIT(notify, link);
58
0
  notify->magic = NOTIFY_MAGIC;
59
0
  *notifyp = notify;
60
0
}
61
62
void
63
0
dns_notify_destroy(dns_notify_t *notify, bool locked) {
64
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
65
66
0
  isc_mem_t *mctx;
67
0
  dns_notifyctx_t *nctx;
68
69
0
  if (notify->zone != NULL) {
70
0
    if (!locked) {
71
0
      dns__zone_lock(notify->zone);
72
0
    }
73
0
    REQUIRE(dns__zone_locked(notify->zone));
74
0
    nctx = dns__zone_getnotifyctx(notify->zone);
75
0
    if (ISC_LINK_LINKED(notify, link)) {
76
0
      ISC_LIST_UNLINK(nctx->notifies, notify, link);
77
0
    }
78
0
    if (!locked) {
79
0
      dns__zone_unlock(notify->zone);
80
0
    }
81
0
    if (locked) {
82
0
      dns__zone_idetach_locked(&notify->zone);
83
0
    } else {
84
0
      dns_zone_idetach(&notify->zone);
85
0
    }
86
0
  }
87
0
  if (notify->find != NULL) {
88
0
    dns_adb_destroyfind(&notify->find);
89
0
  }
90
0
  if (notify->request != NULL) {
91
0
    dns_request_destroy(&notify->request);
92
0
  }
93
0
  if (dns_name_dynamic(&notify->ns)) {
94
0
    dns_name_free(&notify->ns, notify->mctx);
95
0
  }
96
0
  if (notify->key != NULL) {
97
0
    dns_tsigkey_detach(&notify->key);
98
0
  }
99
0
  if (notify->transport != NULL) {
100
0
    dns_transport_detach(&notify->transport);
101
0
  }
102
0
  mctx = notify->mctx;
103
0
  isc_mem_put(notify->mctx, notify, sizeof(*notify));
104
0
  isc_mem_detach(&mctx);
105
0
}
106
107
static void
108
0
notify_done(void *arg) {
109
0
  dns_request_t *request = (dns_request_t *)arg;
110
0
  dns_notify_t *notify = dns_request_getarg(request);
111
0
  isc_result_t result;
112
0
  dns_message_t *message = NULL;
113
0
  isc_buffer_t buf;
114
0
  char rcode[128];
115
0
  char addrbuf[ISC_SOCKADDR_FORMATSIZE];
116
117
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
118
119
0
  isc_buffer_init(&buf, rcode, sizeof(rcode));
120
0
  isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
121
  /* WMM: This is changing the mctx from zone to notify. */
122
0
  dns_message_create(notify->mctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE,
123
0
         &message);
124
125
0
  result = dns_request_getresult(request);
126
0
  if (result != ISC_R_SUCCESS) {
127
0
    goto fail;
128
0
  }
129
130
0
  result = dns_request_getresponse(request, message,
131
0
           DNS_MESSAGEPARSE_PRESERVEORDER);
132
0
  if (result != ISC_R_SUCCESS) {
133
0
    goto fail;
134
0
  }
135
136
0
  result = dns_rcode_totext(message->rcode, &buf);
137
0
  if (result == ISC_R_SUCCESS) {
138
0
    notify_log(notify, ISC_LOG_DEBUG(3),
139
0
         "notify response from %s: %.*s", addrbuf,
140
0
         (int)buf.used, rcode);
141
0
  }
142
0
fail:
143
0
  dns_message_detach(&message);
144
145
0
  if (result == ISC_R_SUCCESS) {
146
0
    notify_log(notify, ISC_LOG_DEBUG(1), "notify to %s successful",
147
0
         addrbuf);
148
0
  } else if (result == ISC_R_SHUTTINGDOWN || result == ISC_R_CANCELED) {
149
    /* just destroy the notify */
150
0
  } else if ((notify->flags & DNS_NOTIFY_TCP) == 0) {
151
0
    notify_log(notify, ISC_LOG_NOTICE,
152
0
         "notify to %s failed: %s: retrying over TCP",
153
0
         addrbuf, isc_result_totext(result));
154
0
    notify->flags |= DNS_NOTIFY_TCP;
155
0
    dns_request_destroy(&notify->request);
156
0
    dns_notify_queue(notify, notify->flags & DNS_NOTIFY_STARTUP);
157
0
    return;
158
0
  } else if (result == ISC_R_TIMEDOUT) {
159
0
    notify_log(notify, ISC_LOG_WARNING,
160
0
         "notify to %s failed: %s: retries exceeded", addrbuf,
161
0
         isc_result_totext(result));
162
0
  } else {
163
0
    notify_log(notify, ISC_LOG_WARNING, "notify to %s failed: %s",
164
0
         addrbuf, isc_result_totext(result));
165
0
  }
166
0
  dns_notify_destroy(notify, false);
167
0
}
168
169
static isc_result_t
170
0
notify_createmessage(dns_notify_t *notify, dns_message_t **messagep) {
171
0
  dns_db_t *zonedb = NULL;
172
0
  dns_dbnode_t *node = NULL;
173
0
  dns_dbversion_t *version = NULL;
174
0
  dns_message_t *message = NULL;
175
0
  dns_rdataset_t rdataset;
176
0
  dns_rdata_t rdata = DNS_RDATA_INIT;
177
178
0
  dns_name_t *tempname = NULL;
179
0
  dns_rdata_t *temprdata = NULL;
180
0
  dns_rdatalist_t *temprdatalist = NULL;
181
0
  dns_rdataset_t *temprdataset = NULL;
182
183
0
  isc_result_t result;
184
0
  isc_region_t r;
185
0
  isc_buffer_t *b = NULL;
186
187
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
188
0
  REQUIRE(messagep != NULL && *messagep == NULL);
189
190
  /* WMM: This is changing the mctx from zone to notify. */
191
0
  dns_message_create(notify->mctx, NULL, NULL, DNS_MESSAGE_INTENTRENDER,
192
0
         &message);
193
194
0
  message->opcode = dns_opcode_notify;
195
0
  message->flags |= DNS_MESSAGEFLAG_AA;
196
0
  message->rdclass = dns_zone_getrdclass(notify->zone);
197
198
0
  dns_message_gettempname(message, &tempname);
199
0
  dns_message_gettemprdataset(message, &temprdataset);
200
201
  /*
202
   * Make question.
203
   */
204
0
  dns_name_clone(dns_zone_getorigin(notify->zone), tempname);
205
0
  dns_rdataset_makequestion(temprdataset,
206
0
          dns_zone_getrdclass(notify->zone),
207
0
          dns_rdatatype_soa);
208
0
  ISC_LIST_APPEND(tempname->list, temprdataset, link);
209
0
  dns_message_addname(message, tempname, DNS_SECTION_QUESTION);
210
0
  tempname = NULL;
211
0
  temprdataset = NULL;
212
213
0
  if ((notify->flags & DNS_NOTIFY_NOSOA) != 0) {
214
0
    goto done;
215
0
  }
216
217
0
  dns_message_gettempname(message, &tempname);
218
0
  dns_message_gettemprdata(message, &temprdata);
219
0
  dns_message_gettemprdataset(message, &temprdataset);
220
0
  dns_message_gettemprdatalist(message, &temprdatalist);
221
222
0
  result = dns_zone_getdb(notify->zone, &zonedb);
223
0
  INSIST(result == ISC_R_SUCCESS);
224
0
  INSIST(zonedb != NULL); /* XXXJT: is this assumption correct? */
225
226
0
  dns_name_clone(dns_zone_getorigin(notify->zone), tempname);
227
0
  dns_db_currentversion(zonedb, &version);
228
0
  result = dns_db_findnode(zonedb, tempname, false, &node);
229
0
  if (result != ISC_R_SUCCESS) {
230
0
    goto soa_cleanup;
231
0
  }
232
233
0
  dns_rdataset_init(&rdataset);
234
0
  result = dns_db_findrdataset(zonedb, node, version, dns_rdatatype_soa,
235
0
             dns_rdatatype_none, 0, &rdataset, NULL);
236
0
  if (result != ISC_R_SUCCESS) {
237
0
    goto soa_cleanup;
238
0
  }
239
0
  result = dns_rdataset_first(&rdataset);
240
0
  if (result != ISC_R_SUCCESS) {
241
0
    goto soa_cleanup;
242
0
  }
243
0
  dns_rdataset_current(&rdataset, &rdata);
244
0
  dns_rdata_toregion(&rdata, &r);
245
  /* WMM: This is changing the mctx from zone to notify. */
246
0
  isc_buffer_allocate(notify->mctx, &b, r.length);
247
0
  isc_buffer_putmem(b, r.base, r.length);
248
0
  isc_buffer_usedregion(b, &r);
249
0
  dns_rdata_fromregion(temprdata, rdata.rdclass, rdata.type, &r);
250
0
  dns_message_takebuffer(message, &b);
251
0
  result = dns_rdataset_next(&rdataset);
252
0
  dns_rdataset_disassociate(&rdataset);
253
0
  if (result != ISC_R_NOMORE) {
254
0
    goto soa_cleanup;
255
0
  }
256
0
  temprdatalist->rdclass = rdata.rdclass;
257
0
  temprdatalist->type = rdata.type;
258
0
  temprdatalist->ttl = rdataset.ttl;
259
0
  ISC_LIST_APPEND(temprdatalist->rdata, temprdata, link);
260
261
0
  dns_rdatalist_tordataset(temprdatalist, temprdataset);
262
263
0
  ISC_LIST_APPEND(tempname->list, temprdataset, link);
264
0
  dns_message_addname(message, tempname, DNS_SECTION_ANSWER);
265
0
  temprdatalist = NULL;
266
0
  temprdataset = NULL;
267
0
  temprdata = NULL;
268
0
  tempname = NULL;
269
270
0
soa_cleanup:
271
0
  if (node != NULL) {
272
0
    dns_db_detachnode(&node);
273
0
  }
274
0
  if (version != NULL) {
275
0
    dns_db_closeversion(zonedb, &version, false);
276
0
  }
277
0
  if (zonedb != NULL) {
278
0
    dns_db_detach(&zonedb);
279
0
  }
280
0
  if (tempname != NULL) {
281
0
    dns_message_puttempname(message, &tempname);
282
0
  }
283
0
  if (temprdata != NULL) {
284
0
    dns_message_puttemprdata(message, &temprdata);
285
0
  }
286
0
  if (temprdataset != NULL) {
287
0
    dns_message_puttemprdataset(message, &temprdataset);
288
0
  }
289
0
  if (temprdatalist != NULL) {
290
0
    dns_message_puttemprdatalist(message, &temprdatalist);
291
0
  }
292
293
0
done:
294
0
  *messagep = message;
295
0
  return ISC_R_SUCCESS;
296
0
}
297
298
static void
299
0
notify_send_toaddr(void *arg) {
300
0
  dns_notify_t *notify = (dns_notify_t *)arg;
301
0
  dns_notifyctx_t *notifyctx = NULL;
302
0
  isc_result_t result;
303
0
  dns_db_t *zonedb = NULL;
304
0
  dns_view_t *view = NULL;
305
0
  isc_loop_t *loop = NULL;
306
0
  dns_zonemgr_t *zmgr = NULL;
307
0
  dns_message_t *message = NULL;
308
0
  isc_netaddr_t dstip;
309
0
  dns_tsigkey_t *key = NULL;
310
0
  char addrbuf[ISC_SOCKADDR_FORMATSIZE];
311
0
  isc_sockaddr_t src;
312
0
  unsigned int options;
313
0
  bool have_notifysource = false;
314
0
  isc_tlsctx_cache_t *zmgr_tlsctx_cache = NULL;
315
316
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
317
318
0
  dns__zone_lock(notify->zone);
319
320
0
  notifyctx = dns__zone_getnotifyctx(notify->zone);
321
0
  zmgr = dns_zone_getmgr(notify->zone);
322
0
  view = dns_zone_getview(notify->zone);
323
0
  loop = dns_zone_getloop(notify->zone);
324
0
  result = dns_zone_getdb(notify->zone, &zonedb);
325
0
  isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
326
327
0
  if (!dns__zone_loaded(notify->zone) || notify->rlevent->canceled ||
328
0
      dns__zone_exiting(notify->zone) || zmgr == NULL || view == NULL ||
329
0
      view->requestmgr == NULL || loop == NULL || zonedb == NULL ||
330
0
      result != ISC_R_SUCCESS)
331
0
  {
332
0
    result = ISC_R_CANCELED;
333
0
    goto cleanup;
334
0
  }
335
336
  /*
337
   * The raw IPv4 address should also exist.  Don't send to the
338
   * mapped form.
339
   */
340
0
  if (isc_sockaddr_pf(&notify->dst) == PF_INET6 &&
341
0
      IN6_IS_ADDR_V4MAPPED(&notify->dst.type.sin6.sin6_addr))
342
0
  {
343
0
    notify_log(notify, ISC_LOG_DEBUG(3),
344
0
         "notify: ignoring IPv6 mapped IPV4 address: %s",
345
0
         addrbuf);
346
0
    result = ISC_R_CANCELED;
347
0
    goto cleanup;
348
0
  }
349
350
0
  result = notify_createmessage(notify, &message);
351
0
  if (result != ISC_R_SUCCESS) {
352
0
    goto cleanup;
353
0
  }
354
355
0
  if (notify->key != NULL) {
356
    /* Transfer ownership of key */
357
0
    key = notify->key;
358
0
    notify->key = NULL;
359
0
  } else {
360
0
    isc_netaddr_fromsockaddr(&dstip, &notify->dst);
361
0
    result = dns_view_getpeertsig(view, &dstip, &key);
362
0
    if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
363
0
      notify_log(notify, ISC_LOG_ERROR,
364
0
           "NOTIFY to %s not sent. "
365
0
           "Peer TSIG key lookup failure.",
366
0
           addrbuf);
367
0
      goto cleanup_message;
368
0
    }
369
0
  }
370
371
0
  if (key != NULL) {
372
0
    char namebuf[DNS_NAME_FORMATSIZE];
373
374
0
    dns_name_format(key->name, namebuf, sizeof(namebuf));
375
0
    notify_log(notify, ISC_LOG_INFO,
376
0
         "sending notify to %s : TSIG (%s)", addrbuf,
377
0
         namebuf);
378
0
  } else {
379
0
    notify_log(notify, ISC_LOG_INFO, "sending notify to %s",
380
0
         addrbuf);
381
0
  }
382
0
  options = 0;
383
0
  if (view->peers != NULL) {
384
0
    dns_peer_t *peer = NULL;
385
0
    bool usetcp = false;
386
0
    result = dns_peerlist_peerbyaddr(view->peers, &dstip, &peer);
387
0
    if (result == ISC_R_SUCCESS) {
388
0
      result = dns_peer_getnotifysource(peer, &src);
389
0
      if (result == ISC_R_SUCCESS) {
390
0
        have_notifysource = true;
391
0
      }
392
0
      result = dns_peer_getforcetcp(peer, &usetcp);
393
0
      if (result == ISC_R_SUCCESS && usetcp) {
394
0
        options |= DNS_FETCHOPT_TCP;
395
0
      }
396
0
    }
397
0
  }
398
0
  switch (isc_sockaddr_pf(&notify->dst)) {
399
0
  case PF_INET:
400
0
    if (!have_notifysource) {
401
0
      isc_sockaddr_t any;
402
0
      isc_sockaddr_any(&any);
403
404
0
      src = notify->src;
405
0
      if (isc_sockaddr_equal(&src, &any)) {
406
0
        src = notifyctx->notifysrc4;
407
0
      }
408
0
    }
409
0
    break;
410
0
  case PF_INET6:
411
0
    if (!have_notifysource) {
412
0
      isc_sockaddr_t any;
413
0
      isc_sockaddr_any6(&any);
414
415
0
      src = notify->src;
416
0
      if (isc_sockaddr_equal(&src, &any)) {
417
0
        src = notifyctx->notifysrc6;
418
0
      }
419
0
    }
420
0
    break;
421
0
  default:
422
0
    result = ISC_R_NOTIMPLEMENTED;
423
0
    goto cleanup_key;
424
0
  }
425
426
0
again:
427
0
  if ((notify->flags & DNS_NOTIFY_TCP) != 0) {
428
0
    options |= DNS_REQUESTOPT_TCP;
429
0
  }
430
431
0
  dns__zonemgr_tlsctx_attach(zmgr, &zmgr_tlsctx_cache);
432
433
0
  const unsigned int connect_timeout = isc_nm_getinitialtimeout() /
434
0
               MS_PER_SEC;
435
0
  result = dns_request_create(
436
0
    view->requestmgr, message, &src, &notify->dst,
437
0
    notify->transport, zmgr_tlsctx_cache, options, key,
438
0
    connect_timeout, TCP_REQUEST_TIMEOUT, UDP_REQUEST_TIMEOUT,
439
0
    UDP_REQUEST_RETRIES, loop, notify_done, notify,
440
0
    &notify->request);
441
442
0
  isc_tlsctx_cache_detach(&zmgr_tlsctx_cache);
443
444
0
  if (result == ISC_R_SUCCESS) {
445
0
    if (isc_sockaddr_pf(&notify->dst) == AF_INET) {
446
0
      dns__zone_stats_increment(
447
0
        notify->zone, dns_zonestatscounter_notifyoutv4);
448
0
    } else {
449
0
      dns__zone_stats_increment(
450
0
        notify->zone, dns_zonestatscounter_notifyoutv6);
451
0
    }
452
0
  } else if (result == ISC_R_SHUTTINGDOWN || result == ISC_R_CANCELED) {
453
0
    goto cleanup_key;
454
0
  } else if ((notify->flags & DNS_NOTIFY_TCP) == 0) {
455
0
    notify_log(notify, ISC_LOG_NOTICE,
456
0
         "notify to %s failed: %s: retrying over TCP",
457
0
         addrbuf, isc_result_totext(result));
458
0
    notify->flags |= DNS_NOTIFY_TCP;
459
0
    goto again;
460
0
  }
461
462
0
cleanup_key:
463
0
  if (key != NULL) {
464
0
    dns_tsigkey_detach(&key);
465
0
  }
466
0
cleanup_message:
467
0
  dns_message_detach(&message);
468
0
cleanup:
469
0
  dns__zone_unlock(notify->zone);
470
471
0
  if (zonedb != NULL) {
472
0
    dns_db_detach(&zonedb);
473
0
  }
474
475
0
  if (notify->rlevent != NULL) {
476
0
    isc_rlevent_free(&notify->rlevent);
477
0
  }
478
479
0
  if (result != ISC_R_SUCCESS) {
480
0
    isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
481
0
    notify_log(notify, ISC_LOG_WARNING, "notify to %s failed: %s",
482
0
         addrbuf, isc_result_totext(result));
483
0
    dns_notify_destroy(notify, false);
484
0
  }
485
0
}
486
487
static isc_result_t
488
0
notify_queue(dns_notify_t *notify, bool startup, bool dequeue) {
489
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
490
491
0
  isc_loop_t *loop = dns_zone_getloop(notify->zone);
492
0
  dns_zonemgr_t *zmgr = dns_zone_getmgr(notify->zone);
493
0
  isc_ratelimiter_t *notifyrl = NULL;
494
0
  isc_ratelimiter_t *startupnotifyrl = NULL;
495
496
0
  INSIST(loop != NULL);
497
0
  INSIST(zmgr != NULL);
498
499
0
  dns__zonemgr_getnotifyrl(zmgr, &notifyrl);
500
0
  dns__zonemgr_getstartupnotifyrl(zmgr, &startupnotifyrl);
501
502
0
  if (dequeue) {
503
0
    return isc_ratelimiter_dequeue(
504
0
      startup ? startupnotifyrl : notifyrl, &notify->rlevent);
505
0
  }
506
507
0
  return isc_ratelimiter_enqueue(startup ? startupnotifyrl : notifyrl,
508
0
               loop, notify_send_toaddr, notify,
509
0
               &notify->rlevent);
510
0
}
511
512
isc_result_t
513
0
dns_notify_dequeue(dns_notify_t *notify, bool startup) {
514
0
  return notify_queue(notify, startup, true);
515
0
}
516
517
isc_result_t
518
0
dns_notify_queue(dns_notify_t *notify, bool startup) {
519
0
  return notify_queue(notify, startup, false);
520
0
}
521
522
bool
523
dns_notify_isqueued(dns_notifyctx_t *nctx, unsigned int flags, dns_name_t *name,
524
        isc_sockaddr_t *addr, dns_tsigkey_t *key,
525
0
        dns_transport_t *transport) {
526
0
  dns_notify_t *notify = NULL;
527
0
  isc_result_t result;
528
529
0
  REQUIRE(nctx != NULL);
530
531
0
  ISC_LIST_FOREACH(nctx->notifies, n, link) {
532
0
    if (n->request != NULL) {
533
0
      continue;
534
0
    }
535
0
    if ((name != NULL && dns_name_dynamic(&n->ns) &&
536
0
         dns_name_equal(name, &n->ns)) ||
537
0
        (addr != NULL && isc_sockaddr_equal(addr, &n->dst) &&
538
0
         n->key == key && n->transport == transport))
539
0
    {
540
0
      notify = n;
541
0
      goto requeue;
542
0
    }
543
0
  }
544
0
  return false;
545
0
requeue:
546
  /*
547
   * If we are enqueued on the startup ratelimiter and this is
548
   * not a startup notify, re-enqueue on the normal notify
549
   * ratelimiter.
550
   */
551
0
  if (notify->rlevent != NULL && (flags & DNS_NOTIFY_STARTUP) == 0 &&
552
0
      (notify->flags & DNS_NOTIFY_STARTUP) != 0)
553
0
  {
554
0
    result = notify_queue(notify, true, true);
555
0
    if (result != ISC_R_SUCCESS) {
556
0
      return true;
557
0
    }
558
559
0
    notify->flags &= ~DNS_NOTIFY_STARTUP;
560
0
    result = notify_queue(notify, false, false);
561
0
    if (result != ISC_R_SUCCESS) {
562
0
      return false;
563
0
    }
564
0
  }
565
566
0
  return true;
567
0
}
568
569
static bool
570
0
notify_isself(dns_notify_t *notify, isc_sockaddr_t *dst) {
571
0
  dns_tsigkey_t *key = NULL;
572
0
  isc_sockaddr_t src;
573
0
  isc_sockaddr_t any;
574
0
  bool isself;
575
0
  isc_netaddr_t dstaddr;
576
0
  isc_result_t result;
577
0
  dns_notifyctx_t *notifyctx = NULL;
578
0
  dns_view_t *view = NULL;
579
0
  dns_isselffunc_t isselffunc;
580
0
  void *isselfarg = NULL;
581
582
0
  notifyctx = dns__zone_getnotifyctx(notify->zone);
583
0
  view = dns_zone_getview(notify->zone);
584
0
  dns__zone_getisself(notify->zone, &isselffunc, &isselfarg);
585
0
  if (view == NULL || isselffunc == NULL) {
586
0
    return false;
587
0
  }
588
589
0
  switch (isc_sockaddr_pf(dst)) {
590
0
  case PF_INET:
591
0
    src = notifyctx->notifysrc4;
592
0
    isc_sockaddr_any(&any);
593
0
    break;
594
0
  case PF_INET6:
595
0
    src = notifyctx->notifysrc6;
596
0
    isc_sockaddr_any6(&any);
597
0
    break;
598
0
  default:
599
0
    return false;
600
0
  }
601
602
  /*
603
   * When sending from any the kernel will assign a source address
604
   * that matches the destination address.
605
   */
606
0
  if (isc_sockaddr_eqaddr(&any, &src)) {
607
0
    src = *dst;
608
0
  }
609
610
0
  isc_netaddr_fromsockaddr(&dstaddr, dst);
611
0
  result = dns_view_getpeertsig(view, &dstaddr, &key);
612
0
  if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
613
0
    return false;
614
0
  }
615
0
  isself = (isselffunc)(view, key, &src, dst,
616
0
            dns_zone_getrdclass(notify->zone), isselfarg);
617
0
  if (key != NULL) {
618
0
    dns_tsigkey_detach(&key);
619
0
  }
620
0
  return isself;
621
0
}
622
623
static void
624
0
notify_send(dns_notify_t *notify) {
625
0
  isc_sockaddr_t dst;
626
0
  isc_result_t result;
627
0
  dns_notify_t *newnotify = NULL;
628
0
  dns_notifyctx_t *notifyctx = NULL;
629
0
  unsigned int flags;
630
0
  bool startup;
631
632
  /*
633
   * Zone lock held by caller.
634
   */
635
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
636
0
  REQUIRE(dns__zone_locked(notify->zone));
637
0
  if (dns__zone_exiting(notify->zone)) {
638
0
    return;
639
0
  }
640
0
  notifyctx = dns__zone_getnotifyctx(notify->zone);
641
642
0
  ISC_LIST_FOREACH(notify->find->list, ai, publink) {
643
0
    dst = ai->sockaddr;
644
0
    if (dns_notify_isqueued(notifyctx, notify->flags, NULL, &dst,
645
0
          NULL, NULL))
646
0
    {
647
0
      continue;
648
0
    }
649
0
    if (notify_isself(notify, &dst)) {
650
0
      continue;
651
0
    }
652
0
    newnotify = NULL;
653
0
    flags = notify->flags & DNS_NOTIFY_NOSOA;
654
0
    dns_notify_create(notify->mctx, flags, &newnotify);
655
0
    dns__zone_iattach_locked(notify->zone, &newnotify->zone);
656
0
    ISC_LIST_APPEND(notifyctx->notifies, newnotify, link);
657
0
    newnotify->dst = dst;
658
0
    if (isc_sockaddr_pf(&dst) == AF_INET6) {
659
0
      isc_sockaddr_any6(&newnotify->src);
660
0
    }
661
0
    startup = ((notify->flags & DNS_NOTIFY_STARTUP) != 0);
662
0
    result = dns_notify_queue(newnotify, startup);
663
0
    if (result != ISC_R_SUCCESS) {
664
0
      goto cleanup;
665
0
    }
666
0
    newnotify = NULL;
667
0
  }
668
669
0
cleanup:
670
0
  if (newnotify != NULL) {
671
0
    dns_notify_destroy(newnotify, true);
672
0
  }
673
0
}
674
675
/*
676
 * XXXAG should check for DNS_ZONEFLG_EXITING
677
 */
678
static void
679
0
process_notify_adb_event(void *arg) {
680
0
  dns_adbfind_t *find = (dns_adbfind_t *)arg;
681
0
  dns_notify_t *notify = (dns_notify_t *)find->cbarg;
682
0
  dns_adbstatus_t astat = find->status;
683
684
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
685
0
  REQUIRE(find == notify->find);
686
687
0
  switch (astat) {
688
0
  case DNS_ADB_MOREADDRESSES:
689
0
    dns_adb_destroyfind(&notify->find);
690
0
    dns_notify_find_address(notify);
691
0
    return;
692
693
0
  case DNS_ADB_NOMOREADDRESSES:
694
0
    dns__zone_lock(notify->zone);
695
0
    notify_send(notify);
696
0
    dns__zone_unlock(notify->zone);
697
0
    break;
698
699
0
  default:
700
0
    break;
701
0
  }
702
703
0
  dns_notify_destroy(notify, false);
704
0
}
705
706
void
707
0
dns_notify_find_address(dns_notify_t *notify) {
708
0
  isc_result_t result;
709
0
  unsigned int options;
710
0
  dns_adb_t *adb = NULL;
711
0
  dns_view_t *view = NULL;
712
0
  isc_loop_t *loop = NULL;
713
714
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
715
716
0
  options = DNS_ADBFIND_WANTEVENT;
717
0
  if (isc_net_probeipv4() != ISC_R_DISABLED) {
718
0
    options |= DNS_ADBFIND_INET;
719
0
  }
720
0
  if (isc_net_probeipv6() != ISC_R_DISABLED) {
721
0
    options |= DNS_ADBFIND_INET6;
722
0
  }
723
724
0
  loop = dns_zone_getloop(notify->zone);
725
0
  view = dns_zone_getview(notify->zone);
726
0
  dns_view_getadb(view, &adb);
727
0
  if (loop == NULL || view == NULL || adb == NULL) {
728
0
    goto destroy;
729
0
  }
730
731
0
  result = dns_adb_createfind(adb, loop, process_notify_adb_event, notify,
732
0
            &notify->ns, options, 0, view->dstport, 0,
733
0
            NULL, NULL, &notify->find);
734
0
  dns_adb_detach(&adb);
735
736
  /* Something failed? */
737
0
  if (result != ISC_R_SUCCESS) {
738
0
    goto destroy;
739
0
  }
740
741
  /* More addresses pending? */
742
0
  if ((notify->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
743
0
    return;
744
0
  }
745
746
  /* We have as many addresses as we can get. */
747
0
  dns__zone_lock(notify->zone);
748
0
  notify_send(notify);
749
0
  dns__zone_unlock(notify->zone);
750
0
destroy:
751
0
  dns_notify_destroy(notify, false);
752
0
}
753
754
void
755
0
dns_notify_cancel(dns_notifyctx_t *nctx) {
756
0
  ISC_LIST_FOREACH(nctx->notifies, notify, link) {
757
0
    INSIST(dns__zone_locked(notify->zone));
758
0
    if (notify->find != NULL) {
759
0
      dns_adb_cancelfind(notify->find);
760
0
    }
761
0
    if (notify->request != NULL) {
762
0
      dns_request_cancel(notify->request);
763
0
    }
764
0
  }
765
0
}