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, ¬->notify_send_handler); 102 tsig_create_record_custom(¬->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