Coverage Report

Created: 2025-12-12 06:31

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
  CHECK(notify_createmessage(notify, &message));
351
352
0
  if (notify->key != NULL) {
353
    /* Transfer ownership of key */
354
0
    key = notify->key;
355
0
    notify->key = NULL;
356
0
  } else {
357
0
    isc_netaddr_fromsockaddr(&dstip, &notify->dst);
358
0
    result = dns_view_getpeertsig(view, &dstip, &key);
359
0
    if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
360
0
      notify_log(notify, ISC_LOG_ERROR,
361
0
           "NOTIFY to %s not sent. "
362
0
           "Peer TSIG key lookup failure.",
363
0
           addrbuf);
364
0
      goto cleanup_message;
365
0
    }
366
0
  }
367
368
0
  if (key != NULL) {
369
0
    char namebuf[DNS_NAME_FORMATSIZE];
370
371
0
    dns_name_format(key->name, namebuf, sizeof(namebuf));
372
0
    notify_log(notify, ISC_LOG_INFO,
373
0
         "sending notify to %s : TSIG (%s)", addrbuf,
374
0
         namebuf);
375
0
  } else {
376
0
    notify_log(notify, ISC_LOG_INFO, "sending notify to %s",
377
0
         addrbuf);
378
0
  }
379
0
  options = 0;
380
0
  if (view->peers != NULL) {
381
0
    dns_peer_t *peer = NULL;
382
0
    bool usetcp = false;
383
0
    result = dns_peerlist_peerbyaddr(view->peers, &dstip, &peer);
384
0
    if (result == ISC_R_SUCCESS) {
385
0
      result = dns_peer_getnotifysource(peer, &src);
386
0
      if (result == ISC_R_SUCCESS) {
387
0
        have_notifysource = true;
388
0
      }
389
0
      result = dns_peer_getforcetcp(peer, &usetcp);
390
0
      if (result == ISC_R_SUCCESS && usetcp) {
391
0
        options |= DNS_FETCHOPT_TCP;
392
0
      }
393
0
    }
394
0
  }
395
0
  switch (isc_sockaddr_pf(&notify->dst)) {
396
0
  case PF_INET:
397
0
    if (!have_notifysource) {
398
0
      isc_sockaddr_t any;
399
0
      isc_sockaddr_any(&any);
400
401
0
      src = notify->src;
402
0
      if (isc_sockaddr_equal(&src, &any)) {
403
0
        src = notifyctx->notifysrc4;
404
0
      }
405
0
    }
406
0
    break;
407
0
  case PF_INET6:
408
0
    if (!have_notifysource) {
409
0
      isc_sockaddr_t any;
410
0
      isc_sockaddr_any6(&any);
411
412
0
      src = notify->src;
413
0
      if (isc_sockaddr_equal(&src, &any)) {
414
0
        src = notifyctx->notifysrc6;
415
0
      }
416
0
    }
417
0
    break;
418
0
  default:
419
0
    result = ISC_R_NOTIMPLEMENTED;
420
0
    goto cleanup_key;
421
0
  }
422
423
0
again:
424
0
  if ((notify->flags & DNS_NOTIFY_TCP) != 0) {
425
0
    options |= DNS_REQUESTOPT_TCP;
426
0
  }
427
428
0
  dns__zonemgr_tlsctx_attach(zmgr, &zmgr_tlsctx_cache);
429
430
0
  const unsigned int connect_timeout = isc_nm_getinitialtimeout() /
431
0
               MS_PER_SEC;
432
0
  result = dns_request_create(
433
0
    view->requestmgr, message, &src, &notify->dst,
434
0
    notify->transport, zmgr_tlsctx_cache, options, key,
435
0
    connect_timeout, TCP_REQUEST_TIMEOUT, UDP_REQUEST_TIMEOUT,
436
0
    UDP_REQUEST_RETRIES, loop, notify_done, notify,
437
0
    &notify->request);
438
439
0
  isc_tlsctx_cache_detach(&zmgr_tlsctx_cache);
440
441
0
  if (result == ISC_R_SUCCESS) {
442
0
    if (isc_sockaddr_pf(&notify->dst) == AF_INET) {
443
0
      dns__zone_stats_increment(
444
0
        notify->zone, dns_zonestatscounter_notifyoutv4);
445
0
    } else {
446
0
      dns__zone_stats_increment(
447
0
        notify->zone, dns_zonestatscounter_notifyoutv6);
448
0
    }
449
0
  } else if (result == ISC_R_SHUTTINGDOWN || result == ISC_R_CANCELED) {
450
0
    goto cleanup_key;
451
0
  } else if ((notify->flags & DNS_NOTIFY_TCP) == 0) {
452
0
    notify_log(notify, ISC_LOG_NOTICE,
453
0
         "notify to %s failed: %s: retrying over TCP",
454
0
         addrbuf, isc_result_totext(result));
455
0
    notify->flags |= DNS_NOTIFY_TCP;
456
0
    goto again;
457
0
  }
458
459
0
cleanup_key:
460
0
  if (key != NULL) {
461
0
    dns_tsigkey_detach(&key);
462
0
  }
463
0
cleanup_message:
464
0
  dns_message_detach(&message);
465
0
cleanup:
466
0
  dns__zone_unlock(notify->zone);
467
468
0
  if (zonedb != NULL) {
469
0
    dns_db_detach(&zonedb);
470
0
  }
471
472
0
  if (notify->rlevent != NULL) {
473
0
    isc_rlevent_free(&notify->rlevent);
474
0
  }
475
476
0
  if (result != ISC_R_SUCCESS) {
477
0
    isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
478
0
    notify_log(notify, ISC_LOG_WARNING, "notify to %s failed: %s",
479
0
         addrbuf, isc_result_totext(result));
480
0
    dns_notify_destroy(notify, false);
481
0
  }
482
0
}
483
484
static isc_result_t
485
0
notify_queue(dns_notify_t *notify, bool startup, bool dequeue) {
486
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
487
488
0
  isc_loop_t *loop = dns_zone_getloop(notify->zone);
489
0
  dns_zonemgr_t *zmgr = dns_zone_getmgr(notify->zone);
490
0
  isc_ratelimiter_t *notifyrl = NULL;
491
0
  isc_ratelimiter_t *startupnotifyrl = NULL;
492
493
0
  INSIST(loop != NULL);
494
0
  INSIST(zmgr != NULL);
495
496
0
  dns__zonemgr_getnotifyrl(zmgr, &notifyrl);
497
0
  dns__zonemgr_getstartupnotifyrl(zmgr, &startupnotifyrl);
498
499
0
  if (dequeue) {
500
0
    return isc_ratelimiter_dequeue(
501
0
      startup ? startupnotifyrl : notifyrl, &notify->rlevent);
502
0
  }
503
504
0
  return isc_ratelimiter_enqueue(startup ? startupnotifyrl : notifyrl,
505
0
               loop, notify_send_toaddr, notify,
506
0
               &notify->rlevent);
507
0
}
508
509
isc_result_t
510
0
dns_notify_queue(dns_notify_t *notify, bool startup) {
511
0
  return notify_queue(notify, startup, false);
512
0
}
513
514
bool
515
dns_notify_isqueued(dns_notifyctx_t *nctx, unsigned int flags, dns_name_t *name,
516
        isc_sockaddr_t *addr, dns_tsigkey_t *key,
517
0
        dns_transport_t *transport) {
518
0
  dns_notify_t *notify = NULL;
519
0
  isc_result_t result;
520
521
0
  REQUIRE(nctx != NULL);
522
523
0
  ISC_LIST_FOREACH(nctx->notifies, n, link) {
524
0
    if (n->request != NULL) {
525
0
      continue;
526
0
    }
527
0
    if ((name != NULL && dns_name_dynamic(&n->ns) &&
528
0
         dns_name_equal(name, &n->ns)) ||
529
0
        (addr != NULL && isc_sockaddr_equal(addr, &n->dst) &&
530
0
         n->key == key && n->transport == transport))
531
0
    {
532
0
      notify = n;
533
0
      goto requeue;
534
0
    }
535
0
  }
536
0
  return false;
537
0
requeue:
538
  /*
539
   * If we are enqueued on the startup ratelimiter and this is
540
   * not a startup notify, re-enqueue on the normal notify
541
   * ratelimiter.
542
   */
543
0
  if (notify->rlevent != NULL && (flags & DNS_NOTIFY_STARTUP) == 0 &&
544
0
      (notify->flags & DNS_NOTIFY_STARTUP) != 0)
545
0
  {
546
0
    result = notify_queue(notify, true, true);
547
0
    if (result != ISC_R_SUCCESS) {
548
0
      return true;
549
0
    }
550
551
0
    notify->flags &= ~DNS_NOTIFY_STARTUP;
552
0
    result = notify_queue(notify, false, false);
553
0
    if (result != ISC_R_SUCCESS) {
554
0
      return false;
555
0
    }
556
0
  }
557
558
0
  return true;
559
0
}
560
561
static bool
562
0
notify_isself(dns_notify_t *notify, isc_sockaddr_t *dst) {
563
0
  dns_tsigkey_t *key = NULL;
564
0
  isc_sockaddr_t src;
565
0
  isc_sockaddr_t any;
566
0
  bool isself;
567
0
  isc_netaddr_t dstaddr;
568
0
  isc_result_t result;
569
0
  dns_notifyctx_t *notifyctx = NULL;
570
0
  dns_view_t *view = NULL;
571
0
  dns_isselffunc_t isselffunc;
572
0
  void *isselfarg = NULL;
573
574
0
  notifyctx = dns__zone_getnotifyctx(notify->zone);
575
0
  view = dns_zone_getview(notify->zone);
576
0
  dns__zone_getisself(notify->zone, &isselffunc, &isselfarg);
577
0
  if (view == NULL || isselffunc == NULL) {
578
0
    return false;
579
0
  }
580
581
0
  switch (isc_sockaddr_pf(dst)) {
582
0
  case PF_INET:
583
0
    src = notifyctx->notifysrc4;
584
0
    isc_sockaddr_any(&any);
585
0
    break;
586
0
  case PF_INET6:
587
0
    src = notifyctx->notifysrc6;
588
0
    isc_sockaddr_any6(&any);
589
0
    break;
590
0
  default:
591
0
    return false;
592
0
  }
593
594
  /*
595
   * When sending from any the kernel will assign a source address
596
   * that matches the destination address.
597
   */
598
0
  if (isc_sockaddr_eqaddr(&any, &src)) {
599
0
    src = *dst;
600
0
  }
601
602
0
  isc_netaddr_fromsockaddr(&dstaddr, dst);
603
0
  result = dns_view_getpeertsig(view, &dstaddr, &key);
604
0
  if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
605
0
    return false;
606
0
  }
607
0
  isself = (isselffunc)(view, key, &src, dst,
608
0
            dns_zone_getrdclass(notify->zone), isselfarg);
609
0
  if (key != NULL) {
610
0
    dns_tsigkey_detach(&key);
611
0
  }
612
0
  return isself;
613
0
}
614
615
static void
616
0
notify_send(dns_notify_t *notify) {
617
0
  isc_sockaddr_t dst;
618
0
  isc_result_t result;
619
0
  dns_notify_t *newnotify = NULL;
620
0
  dns_notifyctx_t *notifyctx = NULL;
621
0
  unsigned int flags;
622
0
  bool startup;
623
624
  /*
625
   * Zone lock held by caller.
626
   */
627
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
628
0
  REQUIRE(dns__zone_locked(notify->zone));
629
0
  if (dns__zone_exiting(notify->zone)) {
630
0
    return;
631
0
  }
632
0
  notifyctx = dns__zone_getnotifyctx(notify->zone);
633
634
0
  ISC_LIST_FOREACH(notify->find->list, ai, publink) {
635
0
    dst = ai->sockaddr;
636
0
    if (dns_notify_isqueued(notifyctx, notify->flags, NULL, &dst,
637
0
          NULL, NULL))
638
0
    {
639
0
      continue;
640
0
    }
641
0
    if (notify_isself(notify, &dst)) {
642
0
      continue;
643
0
    }
644
0
    newnotify = NULL;
645
0
    flags = notify->flags & DNS_NOTIFY_NOSOA;
646
0
    dns_notify_create(notify->mctx, flags, &newnotify);
647
0
    dns__zone_iattach_locked(notify->zone, &newnotify->zone);
648
0
    ISC_LIST_APPEND(notifyctx->notifies, newnotify, link);
649
0
    newnotify->dst = dst;
650
0
    if (isc_sockaddr_pf(&dst) == AF_INET6) {
651
0
      isc_sockaddr_any6(&newnotify->src);
652
0
    }
653
0
    startup = ((notify->flags & DNS_NOTIFY_STARTUP) != 0);
654
0
    CHECK(dns_notify_queue(newnotify, startup));
655
0
    newnotify = NULL;
656
0
  }
657
658
0
cleanup:
659
0
  if (newnotify != NULL) {
660
0
    dns_notify_destroy(newnotify, true);
661
0
  }
662
0
}
663
664
/*
665
 * XXXAG should check for DNS_ZONEFLG_EXITING
666
 */
667
static void
668
0
process_notify_adb_event(void *arg) {
669
0
  dns_adbfind_t *find = (dns_adbfind_t *)arg;
670
0
  dns_notify_t *notify = (dns_notify_t *)find->cbarg;
671
0
  dns_adbstatus_t astat = find->status;
672
673
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
674
0
  REQUIRE(find == notify->find);
675
676
0
  switch (astat) {
677
0
  case DNS_ADB_MOREADDRESSES:
678
0
    dns_adb_destroyfind(&notify->find);
679
0
    dns_notify_find_address(notify);
680
0
    return;
681
682
0
  case DNS_ADB_NOMOREADDRESSES:
683
0
    dns__zone_lock(notify->zone);
684
0
    notify_send(notify);
685
0
    dns__zone_unlock(notify->zone);
686
0
    break;
687
688
0
  default:
689
0
    break;
690
0
  }
691
692
0
  dns_notify_destroy(notify, false);
693
0
}
694
695
void
696
0
dns_notify_find_address(dns_notify_t *notify) {
697
0
  isc_result_t result;
698
0
  unsigned int options;
699
0
  dns_adb_t *adb = NULL;
700
0
  dns_view_t *view = NULL;
701
0
  isc_loop_t *loop = NULL;
702
703
0
  REQUIRE(DNS_NOTIFY_VALID(notify));
704
705
0
  options = DNS_ADBFIND_WANTEVENT;
706
0
  if (isc_net_probeipv4() != ISC_R_DISABLED) {
707
0
    options |= DNS_ADBFIND_INET;
708
0
  }
709
0
  if (isc_net_probeipv6() != ISC_R_DISABLED) {
710
0
    options |= DNS_ADBFIND_INET6;
711
0
  }
712
713
0
  loop = dns_zone_getloop(notify->zone);
714
0
  view = dns_zone_getview(notify->zone);
715
0
  dns_view_getadb(view, &adb);
716
0
  if (loop == NULL || view == NULL || adb == NULL) {
717
0
    goto destroy;
718
0
  }
719
720
0
  result = dns_adb_createfind(adb, loop, process_notify_adb_event, notify,
721
0
            &notify->ns, options, 0, view->dstport, 0,
722
0
            NULL, NULL, NULL, &notify->find);
723
0
  dns_adb_detach(&adb);
724
725
  /* Something failed? */
726
0
  if (result != ISC_R_SUCCESS) {
727
0
    goto destroy;
728
0
  }
729
730
  /* More addresses pending? */
731
0
  if ((notify->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
732
0
    return;
733
0
  }
734
735
  /* We have as many addresses as we can get. */
736
0
  dns__zone_lock(notify->zone);
737
0
  notify_send(notify);
738
0
  dns__zone_unlock(notify->zone);
739
0
destroy:
740
0
  dns_notify_destroy(notify, false);
741
0
}
742
743
void
744
0
dns_notify_cancel(dns_notifyctx_t *nctx) {
745
0
  ISC_LIST_FOREACH(nctx->notifies, notify, link) {
746
0
    INSIST(dns__zone_locked(notify->zone));
747
0
    if (notify->find != NULL) {
748
0
      dns_adb_cancelfind(notify->find);
749
0
    }
750
0
    if (notify->request != NULL) {
751
0
      dns_request_cancel(notify->request);
752
0
    }
753
0
  }
754
0
}