xref: /openbsd-src/usr.sbin/nsd/xfrd-notify.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
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 15 /* seconds between retries sending NOTIFY */
21 
22 /* start sending notifies */
23 static void notify_enable(struct notify_zone_t* zone,
24 	struct xfrd_soa* new_soa);
25 /* setup the notify active state */
26 static void setup_notify_active(struct notify_zone_t* zone);
27 
28 /* returns if the notify send is done for the notify_current acl */
29 static int xfrd_handle_notify_reply(struct notify_zone_t* zone, buffer_type* packet);
30 
31 /* handle zone notify send */
32 static void xfrd_handle_notify_send(int fd, short event, void* arg);
33 
34 static void xfrd_notify_next(struct notify_zone_t* zone);
35 
36 static void xfrd_notify_send_udp(struct notify_zone_t* zone, buffer_type* packet);
37 
38 static void
39 notify_send_disable(struct notify_zone_t* zone)
40 {
41 	zone->notify_send_enable = 0;
42 	event_del(&zone->notify_send_handler);
43 	if(zone->notify_send_handler.ev_fd != -1) {
44 		close(zone->notify_send_handler.ev_fd);
45 	}
46 }
47 
48 void
49 notify_disable(struct notify_zone_t* zone)
50 {
51 	zone->notify_current = 0;
52 	/* if added, then remove */
53 	if(zone->notify_send_enable) {
54 		notify_send_disable(zone);
55 	}
56 
57 	if(xfrd->notify_udp_num == XFRD_MAX_UDP_NOTIFY) {
58 		/* find next waiting and needy zone */
59 		while(xfrd->notify_waiting_first) {
60 			/* snip off */
61 			struct notify_zone_t* wz = xfrd->notify_waiting_first;
62 			assert(wz->is_waiting);
63 			wz->is_waiting = 0;
64 			xfrd->notify_waiting_first = wz->waiting_next;
65 			if(wz->waiting_next)
66 				wz->waiting_next->waiting_prev = NULL;
67 			if(xfrd->notify_waiting_last == wz)
68 				xfrd->notify_waiting_last = NULL;
69 			/* see if this zone needs notify sending */
70 			if(wz->notify_current) {
71 				DEBUG(DEBUG_XFRD,1, (LOG_INFO,
72 					"xfrd: zone %s: notify off waiting list.",
73 					zone->apex_str)	);
74 				setup_notify_active(wz);
75 				return;
76 			}
77 		}
78 	}
79 	xfrd->notify_udp_num--;
80 }
81 
82 void
83 init_notify_send(rbtree_t* tree, region_type* region, zone_options_t* options)
84 {
85 	struct notify_zone_t* not = (struct notify_zone_t*)
86 		region_alloc(region, sizeof(struct notify_zone_t));
87 	memset(not, 0, sizeof(struct notify_zone_t));
88 	not->apex = options->node.key;
89 	not->apex_str = options->name;
90 	not->node.key = not->apex;
91 	not->options = options;
92 
93 	/* if master zone and have a SOA */
94 	not->current_soa = (struct xfrd_soa*)region_alloc(region,
95 		sizeof(struct xfrd_soa));
96 	memset(not->current_soa, 0, sizeof(struct xfrd_soa));
97 
98 	not->is_waiting = 0;
99 
100 	not->notify_send_enable = 0;
101 	tsig_create_record_custom(&not->notify_tsig, NULL, 0, 0, 4);
102 	not->notify_current = 0;
103 	rbtree_insert(tree, (rbnode_t*)not);
104 }
105 
106 void
107 xfrd_del_notify(xfrd_state_t* xfrd, const dname_type* dname)
108 {
109 	/* find it */
110 	struct notify_zone_t* not = (struct notify_zone_t*)rbtree_delete(
111 		xfrd->notify_zones, dname);
112 	if(!not)
113 		return;
114 
115 	/* waiting list */
116 	if(not->is_waiting) {
117 		if(not->waiting_prev)
118 			not->waiting_prev->waiting_next = not->waiting_next;
119 		else	xfrd->notify_waiting_first = not->waiting_next;
120 		if(not->waiting_next)
121 			not->waiting_next->waiting_prev = not->waiting_prev;
122 		else	xfrd->notify_waiting_last = not->waiting_prev;
123 		not->is_waiting = 0;
124 	}
125 
126 	/* event */
127 	if(not->notify_send_enable) {
128 		notify_disable(not);
129 	}
130 
131 	/* del tsig */
132 	tsig_delete_record(&not->notify_tsig, NULL);
133 
134 	/* free it */
135 	region_recycle(xfrd->region, not->current_soa, sizeof(xfrd_soa_t));
136 	/* the apex is recycled when the zone_options.node.key is removed */
137 	region_recycle(xfrd->region, not, sizeof(*not));
138 }
139 
140 static int
141 xfrd_handle_notify_reply(struct notify_zone_t* zone, buffer_type* packet)
142 {
143 	if((OPCODE(packet) != OPCODE_NOTIFY) ||
144 		(QR(packet) == 0)) {
145 		log_msg(LOG_ERR, "xfrd: zone %s: received bad notify reply opcode/flags",
146 			zone->apex_str);
147 		return 0;
148 	}
149 	/* we know it is OPCODE NOTIFY, QUERY_REPLY and for this zone */
150 	if(ID(packet) != zone->notify_query_id) {
151 		log_msg(LOG_ERR, "xfrd: zone %s: received notify-ack with bad ID",
152 			zone->apex_str);
153 		return 0;
154 	}
155 	/* could check tsig, but why. The reply does not cause processing. */
156 	if(RCODE(packet) != RCODE_OK) {
157 		log_msg(LOG_ERR, "xfrd: zone %s: received notify response error %s from %s",
158 			zone->apex_str, rcode2str(RCODE(packet)),
159 			zone->notify_current->ip_address_spec);
160 		if(RCODE(packet) == RCODE_IMPL)
161 			return 1; /* rfc1996: notimpl notify reply: consider retries done */
162 		return 0;
163 	}
164 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: host %s acknowledges notify",
165 		zone->apex_str, zone->notify_current->ip_address_spec));
166 	return 1;
167 }
168 
169 static void
170 xfrd_notify_next(struct notify_zone_t* zone)
171 {
172 	/* advance to next in acl */
173 	zone->notify_current = zone->notify_current->next;
174 	zone->notify_retry = 0;
175 	if(zone->notify_current == 0) {
176 		DEBUG(DEBUG_XFRD,1, (LOG_INFO,
177 			"xfrd: zone %s: no more notify-send acls. stop notify.",
178 			zone->apex_str));
179 		notify_disable(zone);
180 		return;
181 	}
182 }
183 
184 static void
185 xfrd_notify_send_udp(struct notify_zone_t* zone, buffer_type* packet)
186 {
187 	int fd;
188 	if(zone->notify_send_enable) {
189 		notify_send_disable(zone);
190 	}
191 	/* Set timeout for next reply */
192 	zone->notify_timeout.tv_sec = XFRD_NOTIFY_RETRY_TIMOUT;
193 	/* send NOTIFY to secondary. */
194 	xfrd_setup_packet(packet, TYPE_SOA, CLASS_IN, zone->apex,
195 		qid_generate());
196 	zone->notify_query_id = ID(packet);
197 	OPCODE_SET(packet, OPCODE_NOTIFY);
198 	AA_SET(packet);
199 	if(zone->current_soa->serial != 0) {
200 		/* add current SOA to answer section */
201 		ANCOUNT_SET(packet, 1);
202 		xfrd_write_soa_buffer(packet, zone->apex, zone->current_soa);
203 	}
204 	if(zone->notify_current->key_options) {
205 		xfrd_tsig_sign_request(packet, &zone->notify_tsig, zone->notify_current);
206 	}
207 	buffer_flip(packet);
208 	fd = xfrd_send_udp(zone->notify_current, packet,
209 		zone->options->pattern->outgoing_interface);
210 	if(fd == -1) {
211 		log_msg(LOG_ERR, "xfrd: zone %s: could not send notify #%d to %s",
212 			zone->apex_str, zone->notify_retry,
213 			zone->notify_current->ip_address_spec);
214 		event_set(&zone->notify_send_handler, -1, EV_TIMEOUT,
215 			xfrd_handle_notify_send, zone);
216 		if(event_base_set(xfrd->event_base, &zone->notify_send_handler) != 0)
217 			log_msg(LOG_ERR, "notify_send: event_base_set failed");
218 		if(evtimer_add(&zone->notify_send_handler, &zone->notify_timeout) != 0)
219 			log_msg(LOG_ERR, "notify_send: evtimer_add failed");
220 		zone->notify_send_enable = 1;
221 		return;
222 	}
223 	event_set(&zone->notify_send_handler, fd, EV_READ | EV_TIMEOUT,
224 		xfrd_handle_notify_send, zone);
225 	if(event_base_set(xfrd->event_base, &zone->notify_send_handler) != 0)
226 		log_msg(LOG_ERR, "notify_send: event_base_set failed");
227 	if(event_add(&zone->notify_send_handler, &zone->notify_timeout) != 0)
228 		log_msg(LOG_ERR, "notify_send: evtimer_add failed");
229 	zone->notify_send_enable = 1;
230 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: sent notify #%d to %s",
231 		zone->apex_str, zone->notify_retry,
232 		zone->notify_current->ip_address_spec));
233 }
234 
235 static void
236 xfrd_handle_notify_send(int fd, short event, void* arg)
237 {
238 	struct notify_zone_t* zone = (struct notify_zone_t*)arg;
239 	buffer_type* packet = xfrd_get_temp_buffer();
240 	assert(zone->notify_current);
241 	if(zone->is_waiting) {
242 		DEBUG(DEBUG_XFRD,1, (LOG_INFO,
243 			"xfrd: notify waiting, skipped, %s", zone->apex_str));
244 		return;
245 	}
246 	if((event & EV_READ)) {
247 		DEBUG(DEBUG_XFRD,1, (LOG_INFO,
248 			"xfrd: zone %s: read notify ACK", zone->apex_str));
249 		assert(fd != -1);
250 		if(xfrd_udp_read_packet(packet, fd)) {
251 			if(xfrd_handle_notify_reply(zone, packet))
252 				xfrd_notify_next(zone);
253 		}
254 	} else if((event & EV_TIMEOUT)) {
255 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: notify timeout",
256 			zone->apex_str));
257 		/* timeout, try again */
258 	}
259 	/* see if notify is still enabled */
260 	if(zone->notify_current) {
261 		zone->notify_retry++;
262 		if(zone->notify_retry > zone->options->pattern->notify_retry) {
263 			log_msg(LOG_ERR, "xfrd: zone %s: max notify send count reached, %s unreachable",
264 				zone->apex_str, zone->notify_current->ip_address_spec);
265 			xfrd_notify_next(zone);
266 		}
267 	}
268 	if(zone->notify_current) {
269 		/* try again */
270 		xfrd_notify_send_udp(zone, packet);
271 	}
272 }
273 
274 static void
275 setup_notify_active(struct notify_zone_t* zone)
276 {
277 	zone->notify_retry = 0;
278 	zone->notify_current = zone->options->pattern->notify;
279 	zone->notify_timeout.tv_sec = 0;
280 	zone->notify_timeout.tv_usec = 0;
281 
282 	if(zone->notify_send_enable)
283 		notify_send_disable(zone);
284 	event_set(&zone->notify_send_handler, -1, EV_TIMEOUT,
285 		xfrd_handle_notify_send, zone);
286 	if(event_base_set(xfrd->event_base, &zone->notify_send_handler) != 0)
287 		log_msg(LOG_ERR, "notifysend: event_base_set failed");
288 	if(evtimer_add(&zone->notify_send_handler, &zone->notify_timeout) != 0)
289 		log_msg(LOG_ERR, "notifysend: evtimer_add failed");
290 	zone->notify_send_enable = 1;
291 }
292 
293 static void
294 notify_enable(struct notify_zone_t* zone, struct xfrd_soa* new_soa)
295 {
296 	if(!zone->options->pattern->notify) {
297 		return; /* no notify acl, nothing to do */
298 	}
299 
300 	if(new_soa == NULL)
301 		memset(zone->current_soa, 0, sizeof(xfrd_soa_t));
302 	else
303 		memcpy(zone->current_soa, new_soa, sizeof(xfrd_soa_t));
304 	if(zone->is_waiting)
305 		return;
306 
307 	if(xfrd->notify_udp_num < XFRD_MAX_UDP_NOTIFY) {
308 		setup_notify_active(zone);
309 		xfrd->notify_udp_num++;
310 		return;
311 	}
312 	/* put it in waiting list */
313 	zone->notify_current = zone->options->pattern->notify;
314 	zone->is_waiting = 1;
315 	zone->waiting_next = NULL;
316 	zone->waiting_prev = xfrd->notify_waiting_last;
317 	if(xfrd->notify_waiting_last) {
318 		xfrd->notify_waiting_last->waiting_next = zone;
319 	} else {
320 		xfrd->notify_waiting_first = zone;
321 	}
322 	xfrd->notify_waiting_last = zone;
323 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: notify on waiting list.",
324 		zone->apex_str));
325 }
326 
327 void
328 xfrd_notify_start(struct notify_zone_t* zone, struct xfrd_state* xfrd)
329 {
330 	xfrd_zone_t* xz;
331 	if(zone->is_waiting || zone->notify_send_enable)
332 		return;
333 	xz = (xfrd_zone_t*)rbtree_search(xfrd->zones, zone->apex);
334 	if(xz && xz->soa_nsd_acquired)
335 		notify_enable(zone, &xz->soa_nsd);
336 	else	notify_enable(zone, NULL);
337 }
338 
339 void
340 xfrd_send_notify(rbtree_t* tree, const dname_type* apex, struct xfrd_soa* new_soa)
341 {
342 	/* lookup the zone */
343 	struct notify_zone_t* zone = (struct notify_zone_t*)
344 		rbtree_search(tree, apex);
345 	assert(zone);
346 	if(zone->notify_send_enable)
347 		notify_disable(zone);
348 
349 	notify_enable(zone, new_soa);
350 }
351 
352 void
353 notify_handle_master_zone_soainfo(rbtree_t* tree,
354 	const dname_type* apex, struct xfrd_soa* new_soa)
355 {
356 	/* lookup the zone */
357 	struct notify_zone_t* zone = (struct notify_zone_t*)
358 		rbtree_search(tree, apex);
359 	if(!zone) return; /* got SOAINFO but zone was deleted meanwhile */
360 
361 	/* check if SOA changed */
362 	if( (new_soa == NULL && zone->current_soa->serial == 0) ||
363 		(new_soa && new_soa->serial == zone->current_soa->serial))
364 		return;
365 	if(zone->notify_send_enable)
366 		notify_disable(zone);
367 	notify_enable(zone, new_soa);
368 }
369 
370 void
371 close_notify_fds(rbtree_t* tree)
372 {
373 	struct notify_zone_t* zone;
374 	RBTREE_FOR(zone, struct notify_zone_t*, tree)
375 	{
376 		if(zone->notify_send_enable)
377 			notify_send_disable(zone);
378 	}
379 }
380