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(¬->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(¬->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