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