Coverage Report

Created: 2026-02-26 06:45

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