Coverage Report

Created: 2026-01-10 06:28

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
  if (result == ISC_R_SUCCESS) {
468
0
    if (isc_sockaddr_pf(&notify->dst) == AF_INET) {
469
0
      dns__zone_stats_increment(
470
0
        notify->zone, dns_zonestatscounter_notifyoutv4);
471
0
    } else {
472
0
      dns__zone_stats_increment(
473
0
        notify->zone, dns_zonestatscounter_notifyoutv6);
474
0
    }
475
0
  } else if (result == ISC_R_SHUTTINGDOWN || result == ISC_R_CANCELED) {
476
0
    goto cleanup_key;
477
0
  } else if ((notify->flags & DNS_NOTIFY_TCP) == 0) {
478
0
    notify_log(notify, ISC_LOG_NOTICE,
479
0
         "notify(%s) to %s failed: %s: retrying over TCP",
480
0
         typebuf, addrbuf, isc_result_totext(result));
481
0
    notify->flags |= DNS_NOTIFY_TCP;
482
0
    goto again;
483
0
  }
484
485
0
cleanup_key:
486
0
  if (key != NULL) {
487
0
    dns_tsigkey_detach(&key);
488
0
  }
489
0
cleanup_message:
490
0
  dns_message_detach(&message);
491
0
cleanup:
492
0
  dns__zone_unlock(notify->zone);
493
494
0
  if (zonedb != NULL) {
495
0
    dns_db_detach(&zonedb);
496
0
  }
497
498
0
  if (notify->rlevent != NULL) {
499
0
    isc_rlevent_free(&notify->rlevent);
500
0
  }
501
502
0
  if (result != ISC_R_SUCCESS) {
503
0
    isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
504
0
    notify_log(notify, ISC_LOG_WARNING,
505
0
         "notify(%s) to %s failed: %s", typebuf, addrbuf,
506
0
         isc_result_totext(result));
507
0
    dns_notify_destroy(notify, false);
508
0
  }
509
0
}
510
511
static isc_result_t
512
0
notify_queue(dns_notify_t *notify, bool startup, bool dequeue) {
513
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
514
515
0
  isc_loop_t *loop = dns_zone_getloop(notify->zone);
516
0
  dns_zonemgr_t *zmgr = dns_zone_getmgr(notify->zone);
517
0
  isc_ratelimiter_t *notifyrl = NULL;
518
0
  isc_ratelimiter_t *startupnotifyrl = NULL;
519
520
0
  INSIST(loop != NULL);
521
0
  INSIST(zmgr != NULL);
522
523
0
  dns__zonemgr_getnotifyrl(zmgr, &notifyrl);
524
0
  dns__zonemgr_getstartupnotifyrl(zmgr, &startupnotifyrl);
525
526
0
  if (dequeue) {
527
0
    return isc_ratelimiter_dequeue(
528
0
      startup ? startupnotifyrl : notifyrl, &notify->rlevent);
529
0
  }
530
531
0
  return isc_ratelimiter_enqueue(startup ? startupnotifyrl : notifyrl,
532
0
               loop, notify_send_toaddr, notify,
533
0
               &notify->rlevent);
534
0
}
535
536
isc_result_t
537
0
dns_notify_queue(dns_notify_t *notify, bool startup) {
538
0
  return notify_queue(notify, startup, false);
539
0
}
540
541
bool
542
dns_notify_isqueued(dns_notifyctx_t *nctx, dns_rdatatype_t type, in_port_t port,
543
        unsigned int flags, dns_name_t *name, isc_sockaddr_t *addr,
544
0
        dns_tsigkey_t *key, dns_transport_t *transport) {
545
0
  dns_notify_t *notify = NULL;
546
0
  isc_result_t result;
547
548
0
  REQUIRE(nctx != NULL);
549
550
0
  ISC_LIST_FOREACH(nctx->notifies, n, link) {
551
0
    if (n->request != NULL) {
552
0
      continue;
553
0
    }
554
0
    if (n->type != type) {
555
0
      continue;
556
0
    }
557
0
    if ((name != NULL && dns_name_dynamic(&n->ns) &&
558
0
         dns_name_equal(name, &n->ns)) ||
559
0
        (addr != NULL && isc_sockaddr_equal(addr, &n->dst) &&
560
0
         n->port == port && n->key == key &&
561
0
         n->transport == transport))
562
0
    {
563
0
      notify = n;
564
0
      goto requeue;
565
0
    }
566
0
  }
567
0
  return false;
568
0
requeue:
569
  /*
570
   * If we are enqueued on the startup ratelimiter and this is
571
   * not a startup notify, re-enqueue on the normal notify
572
   * ratelimiter.
573
   */
574
0
  if (notify->rlevent != NULL && (flags & DNS_NOTIFY_STARTUP) == 0 &&
575
0
      (notify->flags & DNS_NOTIFY_STARTUP) != 0)
576
0
  {
577
0
    result = notify_queue(notify, true, true);
578
0
    if (result != ISC_R_SUCCESS) {
579
0
      return true;
580
0
    }
581
582
0
    notify->flags &= ~DNS_NOTIFY_STARTUP;
583
0
    result = notify_queue(notify, false, false);
584
0
    if (result != ISC_R_SUCCESS) {
585
0
      return false;
586
0
    }
587
0
  }
588
589
0
  return true;
590
0
}
591
592
static bool
593
0
notify_isself(dns_notify_t *notify, isc_sockaddr_t *dst) {
594
0
  dns_tsigkey_t *key = NULL;
595
0
  isc_sockaddr_t src;
596
0
  isc_sockaddr_t any;
597
0
  bool isself;
598
0
  isc_netaddr_t dstaddr;
599
0
  isc_result_t result;
600
0
  dns_notifyctx_t *notifyctx = NULL;
601
0
  dns_view_t *view = NULL;
602
0
  dns_isselffunc_t isselffunc;
603
0
  void *isselfarg = NULL;
604
605
0
  notifyctx = dns__zone_getnotifyctx(notify->zone, notify->type);
606
0
  view = dns_zone_getview(notify->zone);
607
0
  dns__zone_getisself(notify->zone, &isselffunc, &isselfarg);
608
0
  if (view == NULL || isselffunc == NULL) {
609
0
    return false;
610
0
  }
611
612
0
  switch (isc_sockaddr_pf(dst)) {
613
0
  case PF_INET:
614
0
    src = notifyctx->notifysrc4;
615
0
    isc_sockaddr_any(&any);
616
0
    break;
617
0
  case PF_INET6:
618
0
    src = notifyctx->notifysrc6;
619
0
    isc_sockaddr_any6(&any);
620
0
    break;
621
0
  default:
622
0
    return false;
623
0
  }
624
625
  /*
626
   * When sending from any the kernel will assign a source address
627
   * that matches the destination address.
628
   */
629
0
  if (isc_sockaddr_eqaddr(&any, &src)) {
630
0
    src = *dst;
631
0
  }
632
633
0
  isc_netaddr_fromsockaddr(&dstaddr, dst);
634
0
  result = dns_view_getpeertsig(view, &dstaddr, &key);
635
0
  if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
636
0
    return false;
637
0
  }
638
0
  isself = (isselffunc)(view, key, &src, dst,
639
0
            dns_zone_getrdclass(notify->zone), isselfarg);
640
0
  if (key != NULL) {
641
0
    dns_tsigkey_detach(&key);
642
0
  }
643
0
  return isself;
644
0
}
645
646
static void
647
0
notify_send(dns_notify_t *notify) {
648
0
  isc_sockaddr_t dst;
649
0
  isc_result_t result;
650
0
  dns_notify_t *newnotify = NULL;
651
0
  dns_notifyctx_t *notifyctx = NULL;
652
0
  unsigned int flags;
653
0
  bool startup;
654
655
  /*
656
   * Zone lock held by caller.
657
   */
658
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
659
0
  REQUIRE(dns__zone_locked(notify->zone));
660
0
  if (dns__zone_exiting(notify->zone)) {
661
0
    return;
662
0
  }
663
0
  notifyctx = dns__zone_getnotifyctx(notify->zone, notify->type);
664
665
0
  ISC_LIST_FOREACH(notify->find->list, ai, publink) {
666
0
    dst = ai->sockaddr;
667
0
    if (dns_notify_isqueued(notifyctx, notify->type, notify->port,
668
0
          notify->flags, NULL, &dst, NULL, NULL))
669
0
    {
670
0
      continue;
671
0
    }
672
0
    if (notify_isself(notify, &dst)) {
673
0
      continue;
674
0
    }
675
0
    newnotify = NULL;
676
0
    flags = notify->flags & DNS_NOTIFY_NOSOA;
677
0
    dns_notify_create(notify->mctx, notify->type, notify->port,
678
0
          flags, &newnotify);
679
0
    dns__zone_iattach_locked(notify->zone, &newnotify->zone);
680
0
    ISC_LIST_APPEND(notifyctx->notifies, newnotify, link);
681
0
    newnotify->dst = dst;
682
0
    if (isc_sockaddr_pf(&dst) == AF_INET6) {
683
0
      isc_sockaddr_any6(&newnotify->src);
684
0
    }
685
0
    startup = ((notify->flags & DNS_NOTIFY_STARTUP) != 0);
686
0
    CHECK(dns_notify_queue(newnotify, startup));
687
0
    newnotify = NULL;
688
0
  }
689
690
0
cleanup:
691
0
  if (newnotify != NULL) {
692
0
    dns_notify_destroy(newnotify, true);
693
0
  }
694
0
}
695
696
/*
697
 * XXXAG should check for DNS_ZONEFLG_EXITING
698
 */
699
static void
700
0
process_notify_adb_event(void *arg) {
701
0
  dns_adbfind_t *find = (dns_adbfind_t *)arg;
702
0
  dns_notify_t *notify = (dns_notify_t *)find->cbarg;
703
0
  dns_adbstatus_t astat = find->status;
704
705
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
706
0
  REQUIRE(find == notify->find);
707
708
0
  switch (astat) {
709
0
  case DNS_ADB_MOREADDRESSES:
710
0
    dns_adb_destroyfind(&notify->find);
711
0
    dns_notify_find_address(notify);
712
0
    return;
713
714
0
  case DNS_ADB_NOMOREADDRESSES:
715
0
    dns__zone_lock(notify->zone);
716
0
    notify_send(notify);
717
0
    dns__zone_unlock(notify->zone);
718
0
    break;
719
720
0
  default:
721
0
    break;
722
0
  }
723
724
0
  dns_notify_destroy(notify, false);
725
0
}
726
727
void
728
0
dns_notify_find_address(dns_notify_t *notify) {
729
0
  isc_result_t result;
730
0
  unsigned int options;
731
0
  dns_adb_t *adb = NULL;
732
0
  dns_view_t *view = NULL;
733
0
  isc_loop_t *loop = NULL;
734
735
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
736
737
0
  options = DNS_ADBFIND_WANTEVENT;
738
0
  if (isc_net_probeipv4() != ISC_R_DISABLED) {
739
0
    options |= DNS_ADBFIND_INET;
740
0
  }
741
0
  if (isc_net_probeipv6() != ISC_R_DISABLED) {
742
0
    options |= DNS_ADBFIND_INET6;
743
0
  }
744
745
0
  loop = dns_zone_getloop(notify->zone);
746
0
  view = dns_zone_getview(notify->zone);
747
0
  dns_view_getadb(view, &adb);
748
0
  if (loop == NULL || view == NULL || adb == NULL) {
749
0
    goto destroy;
750
0
  }
751
752
0
  result = dns_adb_createfind(adb, loop, process_notify_adb_event, notify,
753
0
            &notify->ns, options, 0, notify->port, 0,
754
0
            NULL, NULL, NULL, &notify->find);
755
0
  dns_adb_detach(&adb);
756
757
  /* Something failed? */
758
0
  if (result != ISC_R_SUCCESS) {
759
0
    goto destroy;
760
0
  }
761
762
  /* More addresses pending? */
763
0
  if ((notify->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
764
0
    return;
765
0
  }
766
767
  /* We have as many addresses as we can get. */
768
0
  dns__zone_lock(notify->zone);
769
0
  notify_send(notify);
770
0
  dns__zone_unlock(notify->zone);
771
0
destroy:
772
0
  dns_notify_destroy(notify, false);
773
0
}
774
775
void
776
0
dns_notify_cancel(dns_notifyctx_t *nctx) {
777
0
  ISC_LIST_FOREACH(nctx->notifies, notify, link) {
778
0
    INSIST(dns__zone_locked(notify->zone));
779
0
    if (notify->find != NULL) {
780
0
      dns_adb_cancelfind(notify->find);
781
0
    }
782
0
    if (notify->request != NULL) {
783
0
      dns_request_cancel(notify->request);
784
0
    }
785
0
  }
786
0
}