void xfrd_handle_passed_packet(buffer_type* packet, int acl_num) { uint8_t qnamebuf[MAXDOMAINLEN]; uint16_t qtype, qclass; const dname_type* dname; region_type* tempregion = region_create(xalloc, free); xfrd_zone_t* zone; buffer_skip(packet, QHEADERSZ); if(!packet_read_query_section(packet, qnamebuf, &qtype, &qclass)) { region_destroy(tempregion); return; /* drop bad packet */ } dname = dname_make(tempregion, qnamebuf, 1); DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: got passed packet for %s, acl " "%d", dname_to_string(dname,0), acl_num)); /* find the zone */ zone = (xfrd_zone_t*)rbtree_search(xfrd->zones, dname); if(!zone) { log_msg(LOG_INFO, "xfrd: incoming packet for unknown zone %s", dname_to_string(dname,0)); region_destroy(tempregion); return; /* drop packet for unknown zone */ } region_destroy(tempregion); /* handle */ if(OPCODE(packet) == OPCODE_NOTIFY) { xfrd_soa_t soa; int have_soa = 0; int next; /* get serial from a SOA */ if(ANCOUNT(packet) == 1 && packet_skip_dname(packet) && xfrd_parse_soa_info(packet, &soa)) { have_soa = 1; } if(xfrd_handle_incoming_notify(zone, have_soa?&soa:NULL)) { if(zone->zone_handler.fd == -1 && zone->tcp_conn == -1 && !zone->tcp_waiting && !zone->udp_waiting) { xfrd_set_refresh_now(zone); } } next = find_same_master_notify(zone, acl_num); if(next != -1) { zone->next_master = next; DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: notify set next master to query %d", next)); } } else { /* TODO handle incoming IXFR udp reply via port 53 */ } }
void xfrd_read_state(struct xfrd_state* xfrd) { const char* statefile = xfrd->nsd->options->xfrdfile; FILE *in; uint32_t filetime = 0; uint32_t numzones, i; region_type *tempregion; tempregion = region_create(xalloc, free); if(!tempregion) return; in = fopen(statefile, "r"); if(!in) { if(errno != ENOENT) { log_msg(LOG_ERR, "xfrd: Could not open file %s for reading: %s", statefile, strerror(errno)); } else { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: no file %s. refreshing all zones.", statefile)); } region_destroy(tempregion); return; } if(!xfrd_read_check_str(in, XFRD_FILE_MAGIC) || !xfrd_read_check_str(in, "filetime:") || !xfrd_read_i32(in, &filetime) || (time_t)filetime > xfrd_time()+15 || !xfrd_read_check_str(in, "numzones:") || !xfrd_read_i32(in, &numzones)) { log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%lld)", statefile, (int)filetime, (long long)xfrd_time()); fclose(in); region_destroy(tempregion); return; } for(i=0; i<numzones; i++) { char *p; xfrd_zone_t* zone; const dname_type* dname; uint32_t state, masnum, nextmas, round_num, timeout; xfrd_soa_t soa_nsd_read, soa_disk_read, soa_notified_read; time_t soa_nsd_acquired_read, soa_disk_acquired_read, soa_notified_acquired_read; xfrd_soa_t incoming_soa; time_t incoming_acquired; memset(&soa_nsd_read, 0, sizeof(soa_nsd_read)); memset(&soa_disk_read, 0, sizeof(soa_disk_read)); memset(&soa_notified_read, 0, sizeof(soa_notified_read)); if(!xfrd_read_check_str(in, "zone:") || !xfrd_read_check_str(in, "name:") || !(p=xfrd_read_token(in)) || !(dname = dname_parse(tempregion, p)) || !xfrd_read_check_str(in, "state:") || !xfrd_read_i32(in, &state) || (state>2) || !xfrd_read_check_str(in, "master:") || !xfrd_read_i32(in, &masnum) || !xfrd_read_check_str(in, "next_master:") || !xfrd_read_i32(in, &nextmas) || !xfrd_read_check_str(in, "round_num:") || !xfrd_read_i32(in, &round_num) || !xfrd_read_check_str(in, "next_timeout:") || !xfrd_read_i32(in, &timeout) || !xfrd_read_state_soa(in, "soa_nsd_acquired:", "soa_nsd:", &soa_nsd_read, &soa_nsd_acquired_read) || !xfrd_read_state_soa(in, "soa_disk_acquired:", "soa_disk:", &soa_disk_read, &soa_disk_acquired_read) || !xfrd_read_state_soa(in, "soa_notify_acquired:", "soa_notify:", &soa_notified_read, &soa_notified_acquired_read)) { log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%lld)", statefile, (int)filetime, (long long)xfrd_time()); fclose(in); region_destroy(tempregion); return; } zone = (xfrd_zone_t*)rbtree_search(xfrd->zones, dname); if(!zone) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: state file has info for not configured zone %s", p)); continue; } if(soa_nsd_acquired_read>xfrd_time()+15 || soa_disk_acquired_read>xfrd_time()+15 || soa_notified_acquired_read>xfrd_time()+15) { log_msg(LOG_ERR, "xfrd: statefile %s contains" " times in the future for zone %s. Ignoring.", statefile, zone->apex_str); continue; } zone->state = state; zone->master_num = masnum; zone->next_master = nextmas; zone->round_num = round_num; zone->timeout.tv_sec = timeout; zone->timeout.tv_usec = 0; /* read the zone OK, now set the master properly */ zone->master = acl_find_num(zone->zone_options->pattern-> request_xfr, zone->master_num); if(!zone->master) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: masters changed for zone %s", zone->apex_str)); zone->master = zone->zone_options->pattern->request_xfr; zone->master_num = 0; zone->round_num = 0; } /* * There is no timeout, * or there is a notification, * or there is a soa && current time is past refresh point */ if(timeout == 0 || soa_notified_acquired_read != 0 || (soa_disk_acquired_read != 0 && (uint32_t)xfrd_time() - soa_disk_acquired_read > ntohl(soa_disk_read.refresh))) { zone->state = xfrd_zone_refreshing; xfrd_set_refresh_now(zone); } /* There is a soa && current time is past expiry point */ if(soa_disk_acquired_read!=0 && (uint32_t)xfrd_time() - soa_disk_acquired_read > ntohl(soa_disk_read.expire)) { zone->state = xfrd_zone_expired; xfrd_set_refresh_now(zone); } /* handle as an incoming SOA. */ incoming_soa = zone->soa_nsd; incoming_acquired = zone->soa_nsd_acquired; zone->soa_nsd = soa_nsd_read; zone->soa_disk = soa_disk_read; zone->soa_notified = soa_notified_read; zone->soa_nsd_acquired = soa_nsd_acquired_read; /* we had better use what we got from starting NSD, not * what we store in this file, because the actual zone * contents trumps the contents of this cache */ /* zone->soa_disk_acquired = soa_disk_acquired_read; */ zone->soa_notified_acquired = soa_notified_acquired_read; xfrd_handle_incoming_soa(zone, &incoming_soa, incoming_acquired); } if(!xfrd_read_check_str(in, XFRD_FILE_MAGIC)) { log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%lld)", statefile, (int)filetime, (long long)xfrd_time()); region_destroy(tempregion); fclose(in); return; } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: read %d zones from state file", (int)numzones)); fclose(in); region_destroy(tempregion); }
void xfrd_handle_incoming_soa(xfrd_zone_t* zone, xfrd_soa_t* soa, time_t acquired) { if(soa == NULL) { /* nsd no longer has a zone in memory */ zone->soa_nsd_acquired = 0; xfrd_set_zone_state(zone, xfrd_zone_refreshing); xfrd_set_refresh_now(zone); return; } if(zone->soa_nsd_acquired && soa->serial == zone->soa_nsd.serial) return; if(zone->soa_disk_acquired && soa->serial == zone->soa_disk.serial) { /* soa in disk has been loaded in memory */ log_msg(LOG_INFO, "Zone %s serial %u is updated to %u.", zone->apex_str, ntohl(zone->soa_nsd.serial), ntohl(soa->serial)); zone->soa_nsd = zone->soa_disk; zone->soa_nsd_acquired = zone->soa_disk_acquired; if((uint32_t)xfrd_time() - zone->soa_disk_acquired < ntohl(zone->soa_disk.refresh)) { /* zone ok, wait for refresh time */ xfrd_set_zone_state(zone, xfrd_zone_ok); zone->round_num = -1; xfrd_set_timer_refresh(zone); } else if((uint32_t)xfrd_time() - zone->soa_disk_acquired < ntohl(zone->soa_disk.expire)) { /* zone refreshing */ xfrd_set_zone_state(zone, xfrd_zone_refreshing); xfrd_set_refresh_now(zone); } if((uint32_t)xfrd_time() - zone->soa_disk_acquired >= ntohl(zone->soa_disk.expire)) { /* zone expired */ xfrd_set_zone_state(zone, xfrd_zone_expired); xfrd_set_refresh_now(zone); } if(zone->soa_notified_acquired != 0 && (zone->soa_notified.serial == 0 || compare_serial(ntohl(zone->soa_disk.serial), ntohl(zone->soa_notified.serial)) >= 0)) { /* read was in response to this notification */ zone->soa_notified_acquired = 0; } if(zone->soa_notified_acquired && zone->state == xfrd_zone_ok) { /* refresh because of notification */ xfrd_set_zone_state(zone, xfrd_zone_refreshing); xfrd_set_refresh_now(zone); } xfrd_send_notify(xfrd->notify_zones, zone->apex, &zone->soa_nsd); return; } /* user must have manually provided zone data */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s serial %u from unknown source. refreshing", zone->apex_str, ntohl(soa->serial))); zone->soa_nsd = *soa; zone->soa_disk = *soa; zone->soa_nsd_acquired = acquired; zone->soa_disk_acquired = acquired; if(zone->soa_notified_acquired != 0 && (zone->soa_notified.serial == 0 || compare_serial(ntohl(zone->soa_disk.serial), ntohl(zone->soa_notified.serial)) >= 0)) { /* user provided in response to this notification */ zone->soa_notified_acquired = 0; } xfrd_set_zone_state(zone, xfrd_zone_refreshing); xfrd_set_refresh_now(zone); xfrd_send_notify(xfrd->notify_zones, zone->apex, &zone->soa_nsd); }
static void xfrd_handle_zone(netio_type* ATTR_UNUSED(netio), netio_handler_type *handler, netio_event_types_type event_types) { xfrd_zone_t* zone = (xfrd_zone_t*)handler->user_data; if(zone->tcp_conn != -1) { /* busy in tcp transaction */ if(xfrd_tcp_is_reading(xfrd->tcp_set, zone->tcp_conn) && event_types & NETIO_EVENT_READ) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s event tcp read", zone->apex_str)); xfrd_set_timer(zone, xfrd_time() + xfrd->tcp_set->tcp_timeout); xfrd_tcp_read(xfrd->tcp_set, zone); return; } else if(!xfrd_tcp_is_reading(xfrd->tcp_set, zone->tcp_conn) && event_types & NETIO_EVENT_WRITE) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s event tcp write", zone->apex_str)); xfrd_set_timer(zone, xfrd_time() + xfrd->tcp_set->tcp_timeout); xfrd_tcp_write(xfrd->tcp_set, zone); return; } else if(event_types & NETIO_EVENT_TIMEOUT) { /* tcp connection timed out. Stop it. */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s event tcp timeout", zone->apex_str)); xfrd_tcp_release(xfrd->tcp_set, zone); /* continue to retry; as if a timeout happened */ event_types = NETIO_EVENT_TIMEOUT; } } if(event_types & NETIO_EVENT_READ) { /* busy in udp transaction */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s event udp read", zone->apex_str)); xfrd_set_refresh_now(zone); xfrd_udp_read(zone); return; } /* timeout */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s timeout", zone->apex_str)); if(handler->fd != -1) { assert(zone->tcp_conn == -1); xfrd_udp_release(zone); } if(zone->tcp_waiting) { DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s skips retry, TCP connections full", zone->apex_str)); xfrd_unset_timer(zone); return; } if(zone->udp_waiting) { DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s skips retry, UDP connections full", zone->apex_str)); xfrd_unset_timer(zone); return; } if(zone->soa_disk_acquired) { if (zone->state != xfrd_zone_expired && (uint32_t)xfrd_time() >= zone->soa_disk_acquired + ntohl(zone->soa_disk.expire)) { /* zone expired */ log_msg(LOG_ERR, "xfrd: zone %s has expired", zone->apex_str); xfrd_set_zone_state(zone, xfrd_zone_expired); } else if(zone->state == xfrd_zone_ok && (uint32_t)xfrd_time() >= zone->soa_disk_acquired + ntohl(zone->soa_disk.refresh)) { /* zone goes to refreshing state. */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s is refreshing", zone->apex_str)); xfrd_set_zone_state(zone, xfrd_zone_refreshing); } } /* make a new request */ xfrd_make_request(zone); }
static void xfrd_init_zones() { zone_type *dbzone; zone_options_t *zone_opt; xfrd_zone_t *xzone; const dname_type* dname; assert(xfrd->zones == 0); assert(xfrd->nsd->db != 0); xfrd->zones = rbtree_create(xfrd->region, (int (*)(const void *, const void *)) dname_compare); xfrd->notify_zones = rbtree_create(xfrd->region, (int (*)(const void *, const void *)) dname_compare); RBTREE_FOR(zone_opt, zone_options_t*, xfrd->nsd->options->zone_options) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Zone %s\n", zone_opt->name)); dname = dname_parse(xfrd->region, zone_opt->name); if(!dname) { log_msg(LOG_ERR, "xfrd: Could not parse zone name %s.", zone_opt->name); continue; } dbzone = domain_find_zone(domain_table_find(xfrd->nsd->db->domains, dname)); if(dbzone && dname_compare(dname, domain_dname(dbzone->apex)) != 0) dbzone = 0; /* we found a parent zone */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: adding %s zone %s\n", dbzone?"filled":"empty", zone_opt->name)); init_notify_send(xfrd->notify_zones, xfrd->netio, xfrd->region, dname, zone_opt, dbzone); if(!zone_is_slave(zone_opt)) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s, master zone has no outgoing xfr requests", zone_opt->name)); continue; } xzone = (xfrd_zone_t*)region_alloc(xfrd->region, sizeof(xfrd_zone_t)); memset(xzone, 0, sizeof(xfrd_zone_t)); xzone->apex = dname; xzone->apex_str = zone_opt->name; xzone->state = xfrd_zone_refreshing; xzone->dirty = 0; xzone->zone_options = zone_opt; /* first retry will use first master */ xzone->master = 0; xzone->master_num = 0; xzone->next_master = 0; xzone->fresh_xfr_timeout = XFRD_TRANSFER_TIMEOUT_START; xzone->soa_nsd_acquired = 0; xzone->soa_disk_acquired = 0; xzone->soa_notified_acquired = 0; /* [0]=1, [1]=0; "." domain name */ xzone->soa_nsd.prim_ns[0] = 1; xzone->soa_nsd.email[0] = 1; xzone->soa_disk.prim_ns[0]=1; xzone->soa_disk.email[0]=1; xzone->soa_notified.prim_ns[0]=1; xzone->soa_notified.email[0]=1; xzone->zone_handler.fd = -1; xzone->zone_handler.timeout = 0; xzone->zone_handler.user_data = xzone; xzone->zone_handler.event_types = NETIO_EVENT_READ|NETIO_EVENT_TIMEOUT; xzone->zone_handler.event_handler = xfrd_handle_zone; netio_add_handler(xfrd->netio, &xzone->zone_handler); xzone->tcp_conn = -1; xzone->tcp_waiting = 0; xzone->udp_waiting = 0; tsig_create_record_custom(&xzone->tsig, xfrd->region, 0, 0, 4); if(dbzone && dbzone->soa_rrset && dbzone->soa_rrset->rrs) { xzone->soa_nsd_acquired = xfrd_time(); xzone->soa_disk_acquired = xfrd_time(); /* we only use the first SOA in the rrset */ xfrd_copy_soa(&xzone->soa_nsd, dbzone->soa_rrset->rrs); xfrd_copy_soa(&xzone->soa_disk, dbzone->soa_rrset->rrs); } /* set refreshing anyway, we have data but it may be old */ xfrd_set_refresh_now(xzone); xzone->node.key = dname; rbtree_insert(xfrd->zones, (rbnode_t*)xzone); } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: started server %d secondary zones", (int)xfrd->zones->count)); }