/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, ¬ify->mctx); |
54 | 0 | isc_sockaddr_any(¬ify->src); |
55 | 0 | isc_sockaddr_any(¬ify->dst); |
56 | 0 | dns_name_init(¬ify->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(¬ify->zone); |
83 | 0 | } else { |
84 | 0 | dns_zone_idetach(¬ify->zone); |
85 | 0 | } |
86 | 0 | } |
87 | 0 | if (notify->find != NULL) { |
88 | 0 | dns_adb_destroyfind(¬ify->find); |
89 | 0 | } |
90 | 0 | if (notify->request != NULL) { |
91 | 0 | dns_request_destroy(¬ify->request); |
92 | 0 | } |
93 | 0 | if (dns_name_dynamic(¬ify->ns)) { |
94 | 0 | dns_name_free(¬ify->ns, notify->mctx); |
95 | 0 | } |
96 | 0 | if (notify->key != NULL) { |
97 | 0 | dns_tsigkey_detach(¬ify->key); |
98 | 0 | } |
99 | 0 | if (notify->transport != NULL) { |
100 | 0 | dns_transport_detach(¬ify->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(¬ify->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(¬ify->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(¬ify->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(¬ify->dst) == PF_INET6 && |
341 | 0 | IN6_IS_ADDR_V4MAPPED(¬ify->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, ¬ify->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(¬ify->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, ¬ify->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 | ¬ify->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(¬ify->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(¬ify->rlevent); |
477 | 0 | } |
478 | |
|
479 | 0 | if (result != ISC_R_SUCCESS) { |
480 | 0 | isc_sockaddr_format(¬ify->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, ¬ifyrl); |
500 | 0 | dns__zonemgr_getstartupnotifyrl(zmgr, &startupnotifyrl); |
501 | |
|
502 | 0 | if (dequeue) { |
503 | 0 | return isc_ratelimiter_dequeue( |
504 | 0 | startup ? startupnotifyrl : notifyrl, ¬ify->rlevent); |
505 | 0 | } |
506 | | |
507 | 0 | return isc_ratelimiter_enqueue(startup ? startupnotifyrl : notifyrl, |
508 | 0 | loop, notify_send_toaddr, notify, |
509 | 0 | ¬ify->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(¬ify->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 | ¬ify->ns, options, 0, view->dstport, 0, |
733 | 0 | NULL, NULL, ¬ify->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 | } |