1 /*
2 * xfrd-notify.c - notify sending routines
3 *
4 * Copyright (c) 2006, NLnet Labs. All rights reserved.
5 *
6 * See LICENSE for the license.
7 *
8 */
9
10 #include "config.h"
11 #include <assert.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include "xfrd-notify.h"
16 #include "xfrd.h"
17 #include "xfrd-tcp.h"
18 #include "packet.h"
19
20 #define XFRD_NOTIFY_RETRY_TIMOUT 3 /* seconds between retries sending NOTIFY */
21
22 /* start sending notifies */
23 static void notify_enable(struct notify_zone* zone,
24 struct xfrd_soa* new_soa);
25 /* setup the notify active state */
26 static void setup_notify_active(struct notify_zone* zone);
27
28 /* handle zone notify send */
29 static void xfrd_handle_notify_send(int fd, short event, void* arg);
30
31 static int xfrd_notify_send_udp(struct notify_zone* zone, int index);
32
33 static void
notify_send_disable(struct notify_zone * zone)34 notify_send_disable(struct notify_zone* zone)
35 {
36 zone->notify_send_enable = 0;
37 event_del(&zone->notify_send_handler);
38 if(zone->notify_send_handler.ev_fd != -1) {
39 close(zone->notify_send_handler.ev_fd);
40 zone->notify_send_handler.ev_fd = -1;
41 }
42 }
43
44 static void
notify_send6_disable(struct notify_zone * zone)45 notify_send6_disable(struct notify_zone* zone)
46 {
47 zone->notify_send6_enable = 0;
48 event_del(&zone->notify_send6_handler);
49 if(zone->notify_send6_handler.ev_fd != -1) {
50 close(zone->notify_send6_handler.ev_fd);
51 zone->notify_send6_handler.ev_fd = -1;
52 }
53 }
54
55 void
notify_disable(struct notify_zone * zone)56 notify_disable(struct notify_zone* zone)
57 {
58 zone->notify_current = 0;
59 /* if added, then remove */
60 if(zone->notify_send_enable) {
61 notify_send_disable(zone);
62 }
63 if(zone->notify_send6_enable) {
64 notify_send6_disable(zone);
65 }
66
67 if(xfrd->notify_udp_num == XFRD_MAX_UDP_NOTIFY) {
68 /* find next waiting and needy zone */
69 while(xfrd->notify_waiting_first) {
70 /* snip off */
71 struct notify_zone* wz = xfrd->notify_waiting_first;
72 assert(wz->is_waiting);
73 wz->is_waiting = 0;
74 xfrd->notify_waiting_first = wz->waiting_next;
75 if(wz->waiting_next)
76 wz->waiting_next->waiting_prev = NULL;
77 if(xfrd->notify_waiting_last == wz)
78 xfrd->notify_waiting_last = NULL;
79 /* see if this zone needs notify sending */
80 if(wz->notify_current) {
81 DEBUG(DEBUG_XFRD,1, (LOG_INFO,
82 "xfrd: zone %s: notify off waiting list.",
83 zone->apex_str) );
84 setup_notify_active(wz);
85 return;
86 }
87 }
88 }
89 xfrd->notify_udp_num--;
90 }
91
92 void
init_notify_send(rbtree_type * tree,region_type * region,struct zone_options * options)93 init_notify_send(rbtree_type* tree, region_type* region,
94 struct zone_options* options)
95 {
96 struct notify_zone* not = (struct notify_zone*)
97 region_alloc(region, sizeof(struct notify_zone));
98 memset(not, 0, sizeof(struct notify_zone));
99 not->apex = options->node.key;
100 not->apex_str = options->name;
101 not->node.key = not->apex;
102 not->options = options;
103
104 /* if master zone and have a SOA */
105 not->current_soa = (struct xfrd_soa*)region_alloc(region,
106 sizeof(struct xfrd_soa));
107 memset(not->current_soa, 0, sizeof(struct xfrd_soa));
108
109 not->notify_send_handler.ev_fd = -1;
110 not->notify_send6_handler.ev_fd = -1;
111 not->is_waiting = 0;
112
113 not->notify_send_enable = 0;
114 not->notify_send6_enable = 0;
115 tsig_create_record_custom(¬->notify_tsig, NULL, 0, 0, 4);
116 not->notify_current = 0;
117 rbtree_insert(tree, (rbnode_type*)not);
118 }
119
120 void
xfrd_del_notify(xfrd_state_type * xfrd,const dname_type * dname)121 xfrd_del_notify(xfrd_state_type* xfrd, const dname_type* dname)
122 {
123 /* find it */
124 struct notify_zone* not = (struct notify_zone*)rbtree_delete(
125 xfrd->notify_zones, dname);
126 if(!not)
127 return;
128
129 /* waiting list */
130 if(not->is_waiting) {
131 if(not->waiting_prev)
132 not->waiting_prev->waiting_next = not->waiting_next;
133 else xfrd->notify_waiting_first = not->waiting_next;
134 if(not->waiting_next)
135 not->waiting_next->waiting_prev = not->waiting_prev;
136 else xfrd->notify_waiting_last = not->waiting_prev;
137 not->is_waiting = 0;
138 }
139
140 /* event */
141 if(not->notify_send_enable || not->notify_send6_enable) {
142 notify_disable(not);
143 }
144
145 /* del tsig */
146 tsig_delete_record(¬->notify_tsig, NULL);
147
148 /* free it */
149 region_recycle(xfrd->region, not->current_soa, sizeof(xfrd_soa_type));
150 /* the apex is recycled when the zone_options.node.key is removed */
151 region_recycle(xfrd->region, not, sizeof(*not));
152 }
153
154 static int
reply_pkt_is_ack(struct notify_zone * zone,buffer_type * packet,int index)155 reply_pkt_is_ack(struct notify_zone* zone, buffer_type* packet, int index)
156 {
157 if((OPCODE(packet) != OPCODE_NOTIFY) ||
158 (QR(packet) == 0)) {
159 log_msg(LOG_ERR, "xfrd: zone %s: received bad notify reply opcode/flags from %s",
160 zone->apex_str, zone->pkts[index].dest->ip_address_spec);
161
162 return 0;
163 }
164 /* we know it is OPCODE NOTIFY, QUERY_REPLY and for this zone */
165 if(ID(packet) != zone->pkts[index].notify_query_id) {
166 log_msg(LOG_ERR, "xfrd: zone %s: received notify-ack with bad ID from %s",
167 zone->apex_str, zone->pkts[index].dest->ip_address_spec);
168 return 0;
169 }
170 /* could check tsig, but why. The reply does not cause processing. */
171 if(RCODE(packet) != RCODE_OK) {
172 log_msg(LOG_ERR, "xfrd: zone %s: received notify response error %s from %s",
173 zone->apex_str, rcode2str(RCODE(packet)),
174 zone->pkts[index].dest->ip_address_spec);
175 if(RCODE(packet) == RCODE_IMPL)
176 return 1; /* rfc1996: notimpl notify reply: consider retries done */
177 return 0;
178 }
179 DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: host %s acknowledges notify",
180 zone->apex_str, zone->pkts[index].dest->ip_address_spec));
181 return 1;
182 }
183
184 /* compare sockaddr and acl_option addr and port numbers */
185 static int
cmp_addr_equal(struct sockaddr * a,socklen_t a_len,struct acl_options * dest)186 cmp_addr_equal(struct sockaddr* a, socklen_t a_len, struct acl_options* dest)
187 {
188 if(dest) {
189 unsigned int destport = ((dest->port == 0)?
190 (unsigned)atoi(TCP_PORT):dest->port);
191 #ifdef INET6
192 struct sockaddr_storage* a1 = (struct sockaddr_storage*)a;
193 if(a1->ss_family == AF_INET6 && dest->is_ipv6) {
194 struct sockaddr_in6* a2 = (struct sockaddr_in6*)a;
195 if(a_len < sizeof(struct sockaddr_in6))
196 return 0; /* too small */
197 if(ntohs(a2->sin6_port) != destport)
198 return 0; /* different port number */
199 if(memcmp(&a2->sin6_addr, &dest->addr.addr6,
200 sizeof(struct in6_addr)) != 0)
201 return 0; /* different address */
202 return 1;
203 }
204 if(a1->ss_family == AF_INET6 || dest->is_ipv6)
205 return 0; /* different address family */
206 else {
207 #endif /* INET6 */
208 struct sockaddr_in* a3 = (struct sockaddr_in*)a;
209 if(a_len < sizeof(struct sockaddr_in))
210 return 0; /* too small */
211 if(ntohs(a3->sin_port) != destport)
212 return 0; /* different port number */
213 if(memcmp(&a3->sin_addr, &dest->addr.addr,
214 sizeof(struct in_addr)) != 0)
215 return 0; /* different address */
216 return 1;
217 #ifdef INET6
218 }
219 #endif
220 }
221 return 0;
222 }
223
224 static void
notify_pkt_done(struct notify_zone * zone,int index)225 notify_pkt_done(struct notify_zone* zone, int index)
226 {
227 zone->pkts[index].dest = NULL;
228 zone->pkts[index].notify_retry = 0;
229 zone->pkts[index].send_time = 0;
230 zone->pkts[index].notify_query_id = 0;
231 zone->notify_pkt_count--;
232 }
233
234 static void
notify_pkt_retry(struct notify_zone * zone,int index)235 notify_pkt_retry(struct notify_zone* zone, int index)
236 {
237 if(++zone->pkts[index].notify_retry >=
238 zone->options->pattern->notify_retry) {
239 log_msg(LOG_ERR, "xfrd: zone %s: max notify send count reached, %s unreachable",
240 zone->apex_str,
241 zone->pkts[index].dest->ip_address_spec);
242 notify_pkt_done(zone, index);
243 return;
244 }
245 if(!xfrd_notify_send_udp(zone, index)) {
246 notify_pkt_retry(zone, index);
247 }
248 }
249
250 static void
xfrd_handle_notify_reply(struct notify_zone * zone,buffer_type * packet,struct sockaddr * src,socklen_t srclen)251 xfrd_handle_notify_reply(struct notify_zone* zone, buffer_type* packet,
252 struct sockaddr* src, socklen_t srclen)
253 {
254 int i;
255 for(i=0; i<NOTIFY_CONCURRENT_MAX; i++) {
256 /* is this entry in use */
257 if(!zone->pkts[i].dest)
258 continue;
259 /* based on destination */
260 if(!cmp_addr_equal(src, srclen, zone->pkts[i].dest))
261 continue;
262 if(reply_pkt_is_ack(zone, packet, i)) {
263 /* is done */
264 notify_pkt_done(zone, i);
265 return;
266 } else {
267 /* retry */
268 notify_pkt_retry(zone, i);
269 return;
270 }
271 }
272 }
273
274 static int
xfrd_notify_send_udp(struct notify_zone * zone,int index)275 xfrd_notify_send_udp(struct notify_zone* zone, int index)
276 {
277 buffer_type* packet = xfrd_get_temp_buffer();
278 if(!zone->pkts[index].dest) return 0;
279 /* send NOTIFY to secondary. */
280 xfrd_setup_packet(packet, TYPE_SOA, CLASS_IN, zone->apex,
281 qid_generate());
282 zone->pkts[index].notify_query_id = ID(packet);
283 OPCODE_SET(packet, OPCODE_NOTIFY);
284 AA_SET(packet);
285 if(zone->current_soa->serial != 0) {
286 /* add current SOA to answer section */
287 ANCOUNT_SET(packet, 1);
288 xfrd_write_soa_buffer(packet, zone->apex, zone->current_soa);
289 }
290 if(zone->pkts[index].dest->key_options) {
291 xfrd_tsig_sign_request(packet, &zone->notify_tsig, zone->pkts[index].dest);
292 }
293 buffer_flip(packet);
294
295 if((zone->pkts[index].dest->is_ipv6
296 && zone->notify_send6_handler.ev_fd == -1) ||
297 (!zone->pkts[index].dest->is_ipv6
298 && zone->notify_send_handler.ev_fd == -1)) {
299 /* open fd */
300 int fd = xfrd_send_udp(zone->pkts[index].dest, packet,
301 zone->options->pattern->outgoing_interface);
302 if(fd == -1) {
303 log_msg(LOG_ERR, "xfrd: zone %s: could not send notify #%d to %s",
304 zone->apex_str, zone->pkts[index].notify_retry,
305 zone->pkts[index].dest->ip_address_spec);
306 return 0;
307 }
308 if(zone->pkts[index].dest->is_ipv6)
309 zone->notify_send6_handler.ev_fd = fd;
310 else zone->notify_send_handler.ev_fd = fd;
311 } else {
312 /* send on existing fd */
313 #ifdef INET6
314 struct sockaddr_storage to;
315 #else
316 struct sockaddr_in to;
317 #endif /* INET6 */
318 int fd;
319 socklen_t to_len = xfrd_acl_sockaddr_to(
320 zone->pkts[index].dest, &to);
321 if(zone->pkts[index].dest->is_ipv6)
322 fd = zone->notify_send6_handler.ev_fd;
323 else fd = zone->notify_send_handler.ev_fd;
324 if(sendto(fd,
325 buffer_current(packet), buffer_remaining(packet), 0,
326 (struct sockaddr*)&to, to_len) == -1) {
327 log_msg(LOG_ERR, "xfrd notify: sendto %s failed %s",
328 zone->pkts[index].dest->ip_address_spec,
329 strerror(errno));
330 return 0;
331 }
332 }
333 zone->pkts[index].send_time = time(NULL);
334 DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: sent notify #%d to %s",
335 zone->apex_str, zone->pkts[index].notify_retry,
336 zone->pkts[index].dest->ip_address_spec));
337 return 1;
338 }
339
340 static void
notify_timeout_check(struct notify_zone * zone)341 notify_timeout_check(struct notify_zone* zone)
342 {
343 time_t now = time(NULL);
344 int i;
345 for(i=0; i<NOTIFY_CONCURRENT_MAX; i++) {
346 if(!zone->pkts[i].dest)
347 continue;
348 if(now >= zone->pkts[i].send_time + XFRD_NOTIFY_RETRY_TIMOUT) {
349 notify_pkt_retry(zone, i);
350 }
351 }
352 }
353
354 static void
notify_start_pkts(struct notify_zone * zone)355 notify_start_pkts(struct notify_zone* zone)
356 {
357 int i;
358 if(!zone->notify_current) return; /* no more acl to send to */
359 for(i=0; i<NOTIFY_CONCURRENT_MAX; i++) {
360 /* while loop, in case the retries all fail, and we can
361 * start another on this slot, or run out of notify acls */
362 while(zone->pkts[i].dest==NULL && zone->notify_current) {
363 zone->pkts[i].dest = zone->notify_current;
364 zone->notify_current = zone->notify_current->next;
365 zone->pkts[i].notify_retry = 0;
366 zone->pkts[i].notify_query_id = 0;
367 zone->pkts[i].send_time = 0;
368 zone->notify_pkt_count++;
369 if(!xfrd_notify_send_udp(zone, i)) {
370 notify_pkt_retry(zone, i);
371 }
372 }
373 }
374 }
375
376 static void
notify_setup_event(struct notify_zone * zone)377 notify_setup_event(struct notify_zone* zone)
378 {
379 if(zone->notify_send_handler.ev_fd != -1) {
380 int fd = zone->notify_send_handler.ev_fd;
381 if(zone->notify_send_enable) {
382 event_del(&zone->notify_send_handler);
383 }
384 zone->notify_timeout.tv_sec = XFRD_NOTIFY_RETRY_TIMOUT;
385 memset(&zone->notify_send_handler, 0,
386 sizeof(zone->notify_send_handler));
387 event_set(&zone->notify_send_handler, fd, EV_READ | EV_TIMEOUT,
388 xfrd_handle_notify_send, zone);
389 if(event_base_set(xfrd->event_base, &zone->notify_send_handler) != 0)
390 log_msg(LOG_ERR, "notify_send: event_base_set failed");
391 if(event_add(&zone->notify_send_handler, &zone->notify_timeout) != 0)
392 log_msg(LOG_ERR, "notify_send: event_add failed");
393 zone->notify_send_enable = 1;
394 }
395 if(zone->notify_send6_handler.ev_fd != -1) {
396 int fd = zone->notify_send6_handler.ev_fd;
397 if(zone->notify_send6_enable) {
398 event_del(&zone->notify_send6_handler);
399 }
400 zone->notify_timeout.tv_sec = XFRD_NOTIFY_RETRY_TIMOUT;
401 memset(&zone->notify_send6_handler, 0,
402 sizeof(zone->notify_send6_handler));
403 event_set(&zone->notify_send6_handler, fd, EV_READ | EV_TIMEOUT,
404 xfrd_handle_notify_send, zone);
405 if(event_base_set(xfrd->event_base, &zone->notify_send6_handler) != 0)
406 log_msg(LOG_ERR, "notify_send: event_base_set failed");
407 if(event_add(&zone->notify_send6_handler, &zone->notify_timeout) != 0)
408 log_msg(LOG_ERR, "notify_send: event_add failed");
409 zone->notify_send6_enable = 1;
410 }
411 }
412
413 static void
xfrd_handle_notify_send(int fd,short event,void * arg)414 xfrd_handle_notify_send(int fd, short event, void* arg)
415 {
416 struct notify_zone* zone = (struct notify_zone*)arg;
417 buffer_type* packet = xfrd_get_temp_buffer();
418 if(zone->is_waiting) {
419 DEBUG(DEBUG_XFRD,1, (LOG_INFO,
420 "xfrd: notify waiting, skipped, %s", zone->apex_str));
421 return;
422 }
423 if((event & EV_READ)) {
424 struct sockaddr_storage src;
425 socklen_t srclen = (socklen_t)sizeof(src);
426 DEBUG(DEBUG_XFRD,1, (LOG_INFO,
427 "xfrd: zone %s: read notify ACK", zone->apex_str));
428 assert(fd != -1);
429 if(xfrd_udp_read_packet(packet, fd, (struct sockaddr*)&src,
430 &srclen)) {
431 /* find entry, send retry or make entry NULL */
432 xfrd_handle_notify_reply(zone, packet,
433 (struct sockaddr*)&src, srclen);
434 }
435 }
436 if((event & EV_TIMEOUT)) {
437 DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: notify timeout",
438 zone->apex_str));
439 /* timeout, try again */
440 }
441
442 /* see which pkts have timeouted, retry or NULL them */
443 notify_timeout_check(zone);
444
445 /* start new packets if we have empty space */
446 notify_start_pkts(zone);
447
448 /* see if we are done */
449 if(!zone->notify_current && !zone->notify_pkt_count) {
450 /* we are done */
451 DEBUG(DEBUG_XFRD,1, (LOG_INFO,
452 "xfrd: zone %s: no more notify-send acls. stop notify.",
453 zone->apex_str));
454 notify_disable(zone);
455 return;
456 }
457
458 notify_setup_event(zone);
459 }
460
461 static void
setup_notify_active(struct notify_zone * zone)462 setup_notify_active(struct notify_zone* zone)
463 {
464 zone->notify_pkt_count = 0;
465 memset(zone->pkts, 0, sizeof(zone->pkts));
466 zone->notify_current = zone->options->pattern->notify;
467 zone->notify_timeout.tv_sec = 0;
468 zone->notify_timeout.tv_usec = 0;
469
470 if(zone->notify_send_enable)
471 notify_send_disable(zone);
472 memset(&zone->notify_send_handler, 0,
473 sizeof(zone->notify_send_handler));
474 event_set(&zone->notify_send_handler, -1, EV_TIMEOUT,
475 xfrd_handle_notify_send, zone);
476 if(event_base_set(xfrd->event_base, &zone->notify_send_handler) != 0)
477 log_msg(LOG_ERR, "notifysend: event_base_set failed");
478 if(evtimer_add(&zone->notify_send_handler, &zone->notify_timeout) != 0)
479 log_msg(LOG_ERR, "notifysend: evtimer_add failed");
480 zone->notify_send_enable = 1;
481 }
482
483 static void
notify_enable(struct notify_zone * zone,struct xfrd_soa * new_soa)484 notify_enable(struct notify_zone* zone, struct xfrd_soa* new_soa)
485 {
486 if(!zone->options->pattern->notify) {
487 return; /* no notify acl, nothing to do */
488 }
489
490 if(new_soa == NULL)
491 memset(zone->current_soa, 0, sizeof(xfrd_soa_type));
492 else
493 memcpy(zone->current_soa, new_soa, sizeof(xfrd_soa_type));
494 if(zone->is_waiting)
495 return;
496
497 if(xfrd->notify_udp_num < XFRD_MAX_UDP_NOTIFY) {
498 setup_notify_active(zone);
499 xfrd->notify_udp_num++;
500 return;
501 }
502 /* put it in waiting list */
503 zone->notify_current = zone->options->pattern->notify;
504 zone->is_waiting = 1;
505 zone->waiting_next = NULL;
506 zone->waiting_prev = xfrd->notify_waiting_last;
507 if(xfrd->notify_waiting_last) {
508 xfrd->notify_waiting_last->waiting_next = zone;
509 } else {
510 xfrd->notify_waiting_first = zone;
511 }
512 xfrd->notify_waiting_last = zone;
513 DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: notify on waiting list.",
514 zone->apex_str));
515 }
516
517 void
xfrd_notify_start(struct notify_zone * zone,struct xfrd_state * xfrd)518 xfrd_notify_start(struct notify_zone* zone, struct xfrd_state* xfrd)
519 {
520 xfrd_zone_type* xz;
521 if(zone->is_waiting || zone->notify_send_enable ||
522 zone->notify_send6_enable)
523 return;
524 xz = (xfrd_zone_type*)rbtree_search(xfrd->zones, zone->apex);
525 if(xz && xz->soa_nsd_acquired)
526 notify_enable(zone, &xz->soa_nsd);
527 else notify_enable(zone, NULL);
528 }
529
530 void
xfrd_send_notify(rbtree_type * tree,const dname_type * apex,struct xfrd_soa * new_soa)531 xfrd_send_notify(rbtree_type* tree, const dname_type* apex, struct xfrd_soa* new_soa)
532 {
533 /* lookup the zone */
534 struct notify_zone* zone = (struct notify_zone*)
535 rbtree_search(tree, apex);
536 assert(zone);
537 if(zone->notify_send_enable || zone->notify_send6_enable)
538 notify_disable(zone);
539
540 notify_enable(zone, new_soa);
541 }
542
543 void
notify_handle_master_zone_soainfo(rbtree_type * tree,const dname_type * apex,struct xfrd_soa * new_soa)544 notify_handle_master_zone_soainfo(rbtree_type* tree,
545 const dname_type* apex, struct xfrd_soa* new_soa)
546 {
547 /* lookup the zone */
548 struct notify_zone* zone = (struct notify_zone*)
549 rbtree_search(tree, apex);
550 if(!zone) return; /* got SOAINFO but zone was deleted meanwhile */
551
552 /* check if SOA changed */
553 if( (new_soa == NULL && zone->current_soa->serial == 0) ||
554 (new_soa && new_soa->serial == zone->current_soa->serial))
555 return;
556 if(zone->notify_send_enable || zone->notify_send6_enable)
557 notify_disable(zone);
558 notify_enable(zone, new_soa);
559 }
560
561 void
close_notify_fds(rbtree_type * tree)562 close_notify_fds(rbtree_type* tree)
563 {
564 struct notify_zone* zone;
565 RBTREE_FOR(zone, struct notify_zone*, tree)
566 {
567 if(zone->notify_send_enable || zone->notify_send6_enable)
568 notify_send_disable(zone);
569 }
570 }
571