/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, ¬ify->mctx); |
71 | 0 | isc_sockaddr_any(¬ify->src); |
72 | 0 | isc_sockaddr_any(¬ify->dst); |
73 | 0 | dns_name_init(¬ify->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(¬ify->zone); |
100 | 0 | } else { |
101 | 0 | dns_zone_idetach(¬ify->zone); |
102 | 0 | } |
103 | 0 | } |
104 | 0 | if (notify->find != NULL) { |
105 | 0 | dns_adb_destroyfind(¬ify->find); |
106 | 0 | } |
107 | 0 | if (notify->request != NULL) { |
108 | 0 | dns_request_destroy(¬ify->request); |
109 | 0 | } |
110 | 0 | if (dns_name_dynamic(¬ify->ns)) { |
111 | 0 | dns_name_free(¬ify->ns, notify->mctx); |
112 | 0 | } |
113 | 0 | if (notify->key != NULL) { |
114 | 0 | dns_tsigkey_detach(¬ify->key); |
115 | 0 | } |
116 | 0 | if (notify->transport != NULL) { |
117 | 0 | dns_transport_detach(¬ify->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(¬ify->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(¬ify->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(¬ify->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(¬ify->dst) == PF_INET6 && |
367 | 0 | IN6_IS_ADDR_V4MAPPED(¬ify->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, ¬ify->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(¬ify->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, ¬ify->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 | ¬ify->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(¬ify->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(¬ify->rlevent); |
500 | 0 | } |
501 | |
|
502 | 0 | if (result != ISC_R_SUCCESS) { |
503 | 0 | isc_sockaddr_format(¬ify->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, ¬ifyrl); |
524 | 0 | dns__zonemgr_getstartupnotifyrl(zmgr, &startupnotifyrl); |
525 | |
|
526 | 0 | if (dequeue) { |
527 | 0 | return isc_ratelimiter_dequeue( |
528 | 0 | startup ? startupnotifyrl : notifyrl, ¬ify->rlevent); |
529 | 0 | } |
530 | | |
531 | 0 | return isc_ratelimiter_enqueue(startup ? startupnotifyrl : notifyrl, |
532 | 0 | loop, notify_send_toaddr, notify, |
533 | 0 | ¬ify->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(¬ify->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 | ¬ify->ns, options, 0, notify->port, 0, |
754 | 0 | NULL, NULL, NULL, ¬ify->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 | } |