Home | History | Annotate | Line # | Download | only in dist
      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
     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
     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
     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
     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(&not->notify_tsig, NULL, 0, 0, 4);
    116 	not->notify_current = 0;
    117 	rbtree_insert(tree, (rbnode_type*)not);
    118 }
    119 
    120 void
    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(&not->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
    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
    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
    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
    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
    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
    275 xfrd_notify_send_udp(struct notify_zone* zone, int index)
    276 {
    277 	int apex_compress = 0;
    278 	buffer_type* packet = xfrd_get_temp_buffer();
    279 	if(!zone->pkts[index].dest) return 0;
    280 	/* send NOTIFY to secondary. */
    281 	xfrd_setup_packet(packet, TYPE_SOA, CLASS_IN, zone->apex,
    282 		qid_generate(), &apex_compress);
    283 	zone->pkts[index].notify_query_id = ID(packet);
    284 	OPCODE_SET(packet, OPCODE_NOTIFY);
    285 	AA_SET(packet);
    286 	if(zone->current_soa->serial != 0) {
    287 		/* add current SOA to answer section */
    288 		ANCOUNT_SET(packet, 1);
    289 		xfrd_write_soa_buffer(packet, zone->apex, zone->current_soa,
    290 			apex_compress);
    291 	}
    292 	if(zone->pkts[index].dest->key_options) {
    293 		xfrd_tsig_sign_request(packet, &zone->notify_tsig, zone->pkts[index].dest);
    294 	}
    295 	buffer_flip(packet);
    296 
    297 	if((zone->pkts[index].dest->is_ipv6
    298 		&& zone->notify_send6_handler.ev_fd == -1) ||
    299 		(!zone->pkts[index].dest->is_ipv6
    300 		&& zone->notify_send_handler.ev_fd == -1)) {
    301 		/* open fd */
    302 		int fd = xfrd_send_udp(zone->pkts[index].dest, packet,
    303 			zone->options->pattern->outgoing_interface);
    304 		if(fd == -1) {
    305 			log_msg(LOG_ERR, "xfrd: zone %s: could not send notify #%d to %s",
    306 				zone->apex_str, zone->pkts[index].notify_retry,
    307 				zone->pkts[index].dest->ip_address_spec);
    308 			return 0;
    309 		}
    310 		if(zone->pkts[index].dest->is_ipv6)
    311 			zone->notify_send6_handler.ev_fd = fd;
    312 		else	zone->notify_send_handler.ev_fd = fd;
    313 	} else {
    314 		/* send on existing fd */
    315 #ifdef INET6
    316         	struct sockaddr_storage to;
    317 #else
    318         	struct sockaddr_in to;
    319 #endif /* INET6 */
    320 		int fd;
    321 		socklen_t to_len = xfrd_acl_sockaddr_to(
    322 			zone->pkts[index].dest, &to);
    323 		if(zone->pkts[index].dest->is_ipv6
    324 		&& zone->notify_send6_handler.ev_fd != -1)
    325 			fd = zone->notify_send6_handler.ev_fd;
    326 		else if (zone->notify_send_handler.ev_fd != -1)
    327 			fd = zone->notify_send_handler.ev_fd;
    328 		else {
    329 			log_msg(LOG_ERR, "xfrd notify: sendto %s failed %s",
    330 				zone->pkts[index].dest->ip_address_spec,
    331 				"invalid file descriptor");
    332 			return 0;
    333 		}
    334 		if(sendto(fd,
    335 			buffer_current(packet), buffer_remaining(packet), 0,
    336 			(struct sockaddr*)&to, to_len) == -1) {
    337 			log_msg(LOG_ERR, "xfrd notify: sendto %s failed %s",
    338 				zone->pkts[index].dest->ip_address_spec,
    339 				strerror(errno));
    340 			return 0;
    341 		}
    342 	}
    343 	zone->pkts[index].send_time = time(NULL);
    344 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: sent notify #%d to %s",
    345 		zone->apex_str, zone->pkts[index].notify_retry,
    346 		zone->pkts[index].dest->ip_address_spec));
    347 	return 1;
    348 }
    349 
    350 static void
    351 notify_timeout_check(struct notify_zone* zone)
    352 {
    353 	time_t now = time(NULL);
    354 	int i;
    355 	for(i=0; i<NOTIFY_CONCURRENT_MAX; i++) {
    356 		if(!zone->pkts[i].dest)
    357 			continue;
    358 		if(now >= zone->pkts[i].send_time + XFRD_NOTIFY_RETRY_TIMOUT) {
    359 			notify_pkt_retry(zone, i);
    360 		}
    361 	}
    362 }
    363 
    364 static void
    365 notify_start_pkts(struct notify_zone* zone)
    366 {
    367 	int i;
    368 	if(!zone->notify_current) return; /* no more acl to send to */
    369 	for(i=0; i<NOTIFY_CONCURRENT_MAX; i++) {
    370 		/* while loop, in case the retries all fail, and we can
    371 		 * start another on this slot, or run out of notify acls */
    372 		while(zone->pkts[i].dest==NULL && zone->notify_current) {
    373 			zone->pkts[i].dest = zone->notify_current;
    374 			zone->notify_current = zone->notify_current->next;
    375 			zone->pkts[i].notify_retry = 0;
    376 			zone->pkts[i].notify_query_id = 0;
    377 			zone->pkts[i].send_time = 0;
    378 			zone->notify_pkt_count++;
    379 			if(!xfrd_notify_send_udp(zone, i)) {
    380 				notify_pkt_retry(zone, i);
    381 			}
    382 		}
    383 	}
    384 }
    385 
    386 static void
    387 notify_setup_event(struct notify_zone* zone)
    388 {
    389 	if(zone->notify_send_handler.ev_fd != -1) {
    390 		int fd = zone->notify_send_handler.ev_fd;
    391 		if(zone->notify_send_enable) {
    392 			event_del(&zone->notify_send_handler);
    393 		}
    394 		zone->notify_timeout.tv_sec = XFRD_NOTIFY_RETRY_TIMOUT;
    395 		memset(&zone->notify_send_handler, 0,
    396 			sizeof(zone->notify_send_handler));
    397 		event_set(&zone->notify_send_handler, fd, EV_READ | EV_TIMEOUT,
    398 			xfrd_handle_notify_send, zone);
    399 		if(event_base_set(xfrd->event_base, &zone->notify_send_handler) != 0)
    400 			log_msg(LOG_ERR, "notify_send: event_base_set failed");
    401 		if(event_add(&zone->notify_send_handler, &zone->notify_timeout) != 0)
    402 			log_msg(LOG_ERR, "notify_send: event_add failed");
    403 		zone->notify_send_enable = 1;
    404 	}
    405 	if(zone->notify_send6_handler.ev_fd != -1) {
    406 		int fd = zone->notify_send6_handler.ev_fd;
    407 		if(zone->notify_send6_enable) {
    408 			event_del(&zone->notify_send6_handler);
    409 		}
    410 		zone->notify_timeout.tv_sec = XFRD_NOTIFY_RETRY_TIMOUT;
    411 		memset(&zone->notify_send6_handler, 0,
    412 			sizeof(zone->notify_send6_handler));
    413 		event_set(&zone->notify_send6_handler, fd, EV_READ | EV_TIMEOUT,
    414 			xfrd_handle_notify_send, zone);
    415 		if(event_base_set(xfrd->event_base, &zone->notify_send6_handler) != 0)
    416 			log_msg(LOG_ERR, "notify_send: event_base_set failed");
    417 		if(event_add(&zone->notify_send6_handler, &zone->notify_timeout) != 0)
    418 			log_msg(LOG_ERR, "notify_send: event_add failed");
    419 		zone->notify_send6_enable = 1;
    420 	}
    421 }
    422 
    423 static void
    424 xfrd_handle_notify_send(int fd, short event, void* arg)
    425 {
    426 	struct notify_zone* zone = (struct notify_zone*)arg;
    427 	buffer_type* packet = xfrd_get_temp_buffer();
    428 	if(zone->is_waiting) {
    429 		DEBUG(DEBUG_XFRD,1, (LOG_INFO,
    430 			"xfrd: notify waiting, skipped, %s", zone->apex_str));
    431 		return;
    432 	}
    433 	if((event & EV_READ)) {
    434 		struct sockaddr_storage src;
    435 		socklen_t srclen = (socklen_t)sizeof(src);
    436 		DEBUG(DEBUG_XFRD,1, (LOG_INFO,
    437 			"xfrd: zone %s: read notify ACK", zone->apex_str));
    438 		assert(fd != -1);
    439 		if(xfrd_udp_read_packet(packet, fd, (struct sockaddr*)&src,
    440 			&srclen)) {
    441 			/* find entry, send retry or make entry NULL */
    442 			xfrd_handle_notify_reply(zone, packet,
    443 				(struct sockaddr*)&src, srclen);
    444 		}
    445 	}
    446 	if((event & EV_TIMEOUT)) {
    447 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: notify timeout",
    448 			zone->apex_str));
    449 		/* timeout, try again */
    450 	}
    451 
    452 	/* see which pkts have timeouted, retry or NULL them */
    453 	notify_timeout_check(zone);
    454 
    455 	/* start new packets if we have empty space */
    456 	notify_start_pkts(zone);
    457 
    458 	/* see if we are done */
    459 	if(!zone->notify_current && !zone->notify_pkt_count) {
    460 		/* we are done */
    461 		DEBUG(DEBUG_XFRD,1, (LOG_INFO,
    462 			"xfrd: zone %s: no more notify-send acls. stop notify.",
    463 			zone->apex_str));
    464 		notify_disable(zone);
    465 		return;
    466 	}
    467 
    468 	notify_setup_event(zone);
    469 }
    470 
    471 static void
    472 setup_notify_active(struct notify_zone* zone)
    473 {
    474 	zone->notify_pkt_count = 0;
    475 	memset(zone->pkts, 0, sizeof(zone->pkts));
    476 	zone->notify_current = zone->options->pattern->notify;
    477 	zone->notify_timeout.tv_sec = 0;
    478 	zone->notify_timeout.tv_usec = 0;
    479 
    480 	if(zone->notify_send_enable)
    481 		notify_send_disable(zone);
    482 	memset(&zone->notify_send_handler, 0,
    483 		sizeof(zone->notify_send_handler));
    484 	event_set(&zone->notify_send_handler, -1, EV_TIMEOUT,
    485 		xfrd_handle_notify_send, zone);
    486 	if(event_base_set(xfrd->event_base, &zone->notify_send_handler) != 0)
    487 		log_msg(LOG_ERR, "notifysend: event_base_set failed");
    488 	if(evtimer_add(&zone->notify_send_handler, &zone->notify_timeout) != 0)
    489 		log_msg(LOG_ERR, "notifysend: evtimer_add failed");
    490 	zone->notify_send_enable = 1;
    491 }
    492 
    493 static void
    494 notify_enable(struct notify_zone* zone, struct xfrd_soa* new_soa)
    495 {
    496 	if(!zone->options->pattern->notify) {
    497 		return; /* no notify acl, nothing to do */
    498 	}
    499 
    500 	if(new_soa == NULL)
    501 		memset(zone->current_soa, 0, sizeof(xfrd_soa_type));
    502 	else
    503 		memcpy(zone->current_soa, new_soa, sizeof(xfrd_soa_type));
    504 	if(zone->is_waiting)
    505 		return;
    506 
    507 	if(xfrd->notify_udp_num < XFRD_MAX_UDP_NOTIFY) {
    508 		setup_notify_active(zone);
    509 		xfrd->notify_udp_num++;
    510 		return;
    511 	}
    512 	/* put it in waiting list */
    513 	zone->notify_current = zone->options->pattern->notify;
    514 	zone->is_waiting = 1;
    515 	zone->waiting_next = NULL;
    516 	zone->waiting_prev = xfrd->notify_waiting_last;
    517 	if(xfrd->notify_waiting_last) {
    518 		xfrd->notify_waiting_last->waiting_next = zone;
    519 	} else {
    520 		xfrd->notify_waiting_first = zone;
    521 	}
    522 	xfrd->notify_waiting_last = zone;
    523 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: notify on waiting list.",
    524 		zone->apex_str));
    525 }
    526 
    527 void
    528 xfrd_notify_start(struct notify_zone* zone, struct xfrd_state* xfrd)
    529 {
    530 	xfrd_zone_type* xz;
    531 	if(zone->is_waiting || zone->notify_send_enable ||
    532 		zone->notify_send6_enable)
    533 		return;
    534 	xz = (xfrd_zone_type*)rbtree_search(xfrd->zones, zone->apex);
    535 	if(xz && xz->soa_nsd_acquired)
    536 		notify_enable(zone, &xz->soa_nsd);
    537 	else	notify_enable(zone, NULL);
    538 }
    539 
    540 void
    541 xfrd_send_notify(rbtree_type* tree, const dname_type* apex, struct xfrd_soa* new_soa)
    542 {
    543 	/* lookup the zone */
    544 	struct notify_zone* zone = (struct notify_zone*)
    545 		rbtree_search(tree, apex);
    546 	assert(zone);
    547 	if(zone->notify_send_enable || zone->notify_send6_enable)
    548 		notify_disable(zone);
    549 
    550 	notify_enable(zone, new_soa);
    551 }
    552 
    553 void
    554 notify_handle_master_zone_soainfo(rbtree_type* tree,
    555 	const dname_type* apex, struct xfrd_soa* new_soa)
    556 {
    557 	/* lookup the zone */
    558 	struct notify_zone* zone = (struct notify_zone*)
    559 		rbtree_search(tree, apex);
    560 	if(!zone) return; /* got SOAINFO but zone was deleted meanwhile */
    561 
    562 	/* check if SOA changed */
    563 	if( (new_soa == NULL && zone->current_soa->serial == 0) ||
    564 		(new_soa && new_soa->serial == zone->current_soa->serial))
    565 		return;
    566 	if(zone->notify_send_enable || zone->notify_send6_enable)
    567 		notify_disable(zone);
    568 	notify_enable(zone, new_soa);
    569 }
    570 
    571 void
    572 close_notify_fds(rbtree_type* tree)
    573 {
    574 	struct notify_zone* zone;
    575 	RBTREE_FOR(zone, struct notify_zone*, tree)
    576 	{
    577 		if(zone->notify_send_enable || zone->notify_send6_enable)
    578 			notify_send_disable(zone);
    579 	}
    580 }
    581