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