xref: /openbsd/usr.sbin/nsd/xfrd-notify.c (revision 7b21d3dd)
1 /*
2  * xfrd-notify.c - notify sending routines
3  *
4  * Copyright (c) 2001-2011, 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 
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 /* stop sending notifies */
26 static void notify_disable(struct notify_zone_t* zone);
27 /* setup the notify active state */
28 static void setup_notify_active(struct notify_zone_t* zone);
29 
30 /* returns if the notify send is done for the notify_current acl */
31 static int xfrd_handle_notify_reply(struct notify_zone_t* zone, buffer_type* packet);
32 
33 /* handle zone notify send */
34 static void xfrd_handle_notify_send(netio_type *netio,
35         netio_handler_type *handler, netio_event_types_type event_types);
36 
37 static void xfrd_notify_next(struct notify_zone_t* zone);
38 
39 static void xfrd_notify_send_udp(struct notify_zone_t* zone, buffer_type* packet);
40 
41 static void
42 notify_disable(struct notify_zone_t* zone)
43 {
44 	zone->notify_current = 0;
45 	zone->notify_send_handler.timeout = NULL;
46 	if(zone->notify_send_handler.fd != -1) {
47 		close(zone->notify_send_handler.fd);
48 		zone->notify_send_handler.fd = -1;
49 	}
50 
51 	if(xfrd->notify_udp_num == XFRD_MAX_UDP_NOTIFY) {
52 		/* find next waiting and needy zone */
53 		while(xfrd->notify_waiting_first) {
54 			/* snip off */
55 			struct notify_zone_t* wz = xfrd->notify_waiting_first;
56 			assert(wz->is_waiting);
57 			wz->is_waiting = 0;
58 			xfrd->notify_waiting_first = wz->waiting_next;
59 			if(xfrd->notify_waiting_last == wz)
60 				xfrd->notify_waiting_last = NULL;
61 			/* see if this zone needs notify sending */
62 			if(wz->notify_current) {
63 				DEBUG(DEBUG_XFRD,1, (LOG_INFO,
64 					"xfrd: zone %s: notify off waiting list.",
65 					zone->apex_str)	);
66 				setup_notify_active(wz);
67 				return;
68 			}
69 		}
70 	}
71 	xfrd->notify_udp_num--;
72 }
73 
74 void
75 init_notify_send(rbtree_t* tree, netio_type* netio, region_type* region,
76 	const dname_type* apex, zone_options_t* options, zone_type* dbzone)
77 {
78 	struct notify_zone_t* not = (struct notify_zone_t*)
79 		region_alloc(region, sizeof(struct notify_zone_t));
80 	memset(not, 0, sizeof(struct notify_zone_t));
81 	not->apex = apex;
82 	not->apex_str = options->name;
83 	not->node.key = not->apex;
84 	not->options = options;
85 
86 	/* if master zone and have a SOA */
87 	not->current_soa = (struct xfrd_soa*)region_alloc(region,
88 		sizeof(struct xfrd_soa));
89 	memset(not->current_soa, 0, sizeof(struct xfrd_soa));
90 	if(dbzone && dbzone->soa_rrset && dbzone->soa_rrset->rrs) {
91 		xfrd_copy_soa(not->current_soa,	dbzone->soa_rrset->rrs);
92 	}
93 
94 	not->is_waiting = 0;
95 	not->notify_send_handler.fd = -1;
96 	not->notify_send_handler.timeout = 0;
97 	not->notify_send_handler.user_data = not;
98 	not->notify_send_handler.event_types =
99 		NETIO_EVENT_READ|NETIO_EVENT_TIMEOUT;
100 	not->notify_send_handler.event_handler = xfrd_handle_notify_send;
101 	netio_add_handler(netio, &not->notify_send_handler);
102 	tsig_create_record_custom(&not->notify_tsig, region, 0, 0, 4);
103 	not->notify_current = 0;
104 	rbtree_insert(tree, (rbnode_t*)not);
105 }
106 
107 static int
108 xfrd_handle_notify_reply(struct notify_zone_t* zone, buffer_type* packet)
109 {
110 	if((OPCODE(packet) != OPCODE_NOTIFY) ||
111 		(QR(packet) == 0)) {
112 		log_msg(LOG_ERR, "xfrd: zone %s: received bad notify reply opcode/flags",
113 			zone->apex_str);
114 		return 0;
115 	}
116 	/* we know it is OPCODE NOTIFY, QUERY_REPLY and for this zone */
117 	if(ID(packet) != zone->notify_query_id) {
118 		log_msg(LOG_ERR, "xfrd: zone %s: received notify-ack with bad ID",
119 			zone->apex_str);
120 		return 0;
121 	}
122 	/* could check tsig, but why. The reply does not cause processing. */
123 	if(RCODE(packet) != RCODE_OK) {
124 		log_msg(LOG_ERR, "xfrd: zone %s: received notify response error %s from %s",
125 			zone->apex_str, rcode2str(RCODE(packet)),
126 			zone->notify_current->ip_address_spec);
127 		if(RCODE(packet) == RCODE_IMPL)
128 			return 1; /* rfc1996: notimpl notify reply: consider retries done */
129 		return 0;
130 	}
131 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: host %s acknowledges notify",
132 		zone->apex_str, zone->notify_current->ip_address_spec));
133 	return 1;
134 }
135 
136 static void
137 xfrd_notify_next(struct notify_zone_t* zone)
138 {
139 	/* advance to next in acl */
140 	zone->notify_current = zone->notify_current->next;
141 	zone->notify_retry = 0;
142 	if(zone->notify_current == 0) {
143 		DEBUG(DEBUG_XFRD,1, (LOG_INFO,
144 			"xfrd: zone %s: no more notify-send acls. stop notify.",
145 			zone->apex_str));
146 		notify_disable(zone);
147 		return;
148 	}
149 }
150 
151 static void
152 xfrd_notify_send_udp(struct notify_zone_t* zone, buffer_type* packet)
153 {
154 	if(zone->notify_send_handler.fd != -1)
155 		close(zone->notify_send_handler.fd);
156 	zone->notify_send_handler.fd = -1;
157 	/* Set timeout for next reply */
158 	zone->notify_timeout.tv_sec = xfrd_time() + XFRD_NOTIFY_RETRY_TIMOUT;
159 	/* send NOTIFY to secondary. */
160 	xfrd_setup_packet(packet, TYPE_SOA, CLASS_IN, zone->apex);
161 	zone->notify_query_id = ID(packet);
162 	OPCODE_SET(packet, OPCODE_NOTIFY);
163 	AA_SET(packet);
164 	if(zone->current_soa->serial != 0) {
165 		/* add current SOA to answer section */
166 		ANCOUNT_SET(packet, 1);
167 		xfrd_write_soa_buffer(packet, zone->apex, zone->current_soa);
168 	}
169 	if(zone->notify_current->key_options) {
170 		xfrd_tsig_sign_request(packet, &zone->notify_tsig, zone->notify_current);
171 	}
172 	buffer_flip(packet);
173 	zone->notify_send_handler.fd = xfrd_send_udp(zone->notify_current,
174 		packet, zone->options->outgoing_interface);
175 	if(zone->notify_send_handler.fd == -1) {
176 		log_msg(LOG_ERR, "xfrd: zone %s: could not send notify #%d to %s",
177 			zone->apex_str, zone->notify_retry,
178 			zone->notify_current->ip_address_spec);
179 		return;
180 	}
181 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: sent notify #%d to %s",
182 		zone->apex_str, zone->notify_retry,
183 		zone->notify_current->ip_address_spec));
184 }
185 
186 static void
187 xfrd_handle_notify_send(netio_type* ATTR_UNUSED(netio),
188 	netio_handler_type *handler, netio_event_types_type event_types)
189 {
190 	struct notify_zone_t* zone = (struct notify_zone_t*)handler->user_data;
191 	buffer_type* packet = xfrd_get_temp_buffer();
192 	assert(zone->notify_current);
193 	if(zone->is_waiting) {
194 		DEBUG(DEBUG_XFRD,1, (LOG_INFO,
195 			"xfrd: notify waiting, skipped, %s", zone->apex_str));
196 		assert(zone->notify_send_handler.fd == -1);
197 		return;
198 	}
199 	if(event_types & NETIO_EVENT_READ) {
200 		DEBUG(DEBUG_XFRD,1, (LOG_INFO,
201 			"xfrd: zone %s: read notify ACK", zone->apex_str));
202 		assert(handler->fd != -1);
203 		if(xfrd_udp_read_packet(packet, zone->notify_send_handler.fd)) {
204 			if(xfrd_handle_notify_reply(zone, packet))
205 				xfrd_notify_next(zone);
206 		}
207 	} else if(event_types & NETIO_EVENT_TIMEOUT) {
208 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: notify timeout",
209 			zone->apex_str));
210 		/* timeout, try again */
211 	}
212 	/* see if notify is still enabled */
213 	if(zone->notify_current) {
214 		zone->notify_retry++;
215 		if(zone->notify_retry > zone->options->notify_retry) {
216 			log_msg(LOG_ERR, "xfrd: zone %s: max notify send count reached, %s unreachable",
217 				zone->apex_str, zone->notify_current->ip_address_spec);
218 			xfrd_notify_next(zone);
219 		}
220 	}
221 	if(zone->notify_current) {
222 		/* try again */
223 		xfrd_notify_send_udp(zone, packet);
224 	}
225 }
226 
227 static void
228 setup_notify_active(struct notify_zone_t* zone)
229 {
230 	zone->notify_retry = 0;
231 	zone->notify_current = zone->options->notify;
232 	zone->notify_send_handler.timeout = &zone->notify_timeout;
233 	zone->notify_timeout.tv_sec = xfrd_time();
234 	zone->notify_timeout.tv_nsec = 0;
235 }
236 
237 static void
238 notify_enable(struct notify_zone_t* zone, struct xfrd_soa* new_soa)
239 {
240 	if(!zone->options->notify) {
241 		return; /* no notify acl, nothing to do */
242 	}
243 
244 	if(new_soa == NULL)
245 		memset(zone->current_soa, 0, sizeof(xfrd_soa_t));
246 	else
247 		memcpy(zone->current_soa, new_soa, sizeof(xfrd_soa_t));
248 	if(zone->is_waiting)
249 		return;
250 
251 	if(xfrd->notify_udp_num < XFRD_MAX_UDP_NOTIFY) {
252 		setup_notify_active(zone);
253 		xfrd->notify_udp_num++;
254 		return;
255 	}
256 	/* put it in waiting list */
257 	zone->notify_current = zone->options->notify;
258 	zone->is_waiting = 1;
259 	zone->waiting_next = NULL;
260 	if(xfrd->notify_waiting_last) {
261 		xfrd->notify_waiting_last->waiting_next = zone;
262 	} else {
263 		xfrd->notify_waiting_first = zone;
264 	}
265 	xfrd->notify_waiting_last = zone;
266 	zone->notify_send_handler.timeout = NULL;
267 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: notify on waiting list.",
268 		zone->apex_str));
269 }
270 
271 void
272 xfrd_send_notify(rbtree_t* tree, const dname_type* apex, struct xfrd_soa* new_soa)
273 {
274 	/* lookup the zone */
275 	struct notify_zone_t* zone = (struct notify_zone_t*)
276 		rbtree_search(tree, apex);
277 	assert(zone);
278 
279 	notify_enable(zone, new_soa);
280 }
281 
282 void
283 notify_handle_master_zone_soainfo(rbtree_t* tree,
284 	const dname_type* apex, struct xfrd_soa* new_soa)
285 {
286 	/* lookup the zone */
287 	struct notify_zone_t* zone = (struct notify_zone_t*)
288 		rbtree_search(tree, apex);
289 	assert(zone);
290 
291 	/* check if SOA changed */
292 	if( (new_soa == NULL && zone->current_soa->serial == 0) ||
293 		(new_soa && new_soa->serial == zone->current_soa->serial))
294 		return;
295 
296 	notify_enable(zone, new_soa);
297 }
298 
299 void close_notify_fds(rbtree_t* tree)
300 {
301 	struct notify_zone_t* zone;
302 	RBTREE_FOR(zone, struct notify_zone_t*, tree)
303 	{
304 		if(zone->notify_send_handler.fd != -1) {
305 			close(zone->notify_send_handler.fd);
306 			zone->notify_send_handler.fd = -1;
307 		}
308 	}
309 }
310