int event_refresh(conf_t *conf, zone_t *zone) { assert(zone); /* Ignore if not slave zone. */ if (!zone_is_slave(conf, zone)) { return KNOT_EOK; } if (zone_contents_is_empty(zone->contents)) { /* No contents, schedule retransfer now. */ zone_events_schedule(zone, ZONE_EVENT_XFER, ZONE_EVENT_NOW); return KNOT_EOK; } int ret = zone_master_try(conf, zone, try_refresh, NULL, "refresh"); const knot_rdataset_t *soa = zone_soa(zone); if (ret != KNOT_EOK) { log_zone_error(zone->name, "refresh, failed (%s)", knot_strerror(ret)); /* Schedule next retry. */ zone_events_schedule(zone, ZONE_EVENT_REFRESH, knot_soa_retry(soa)); start_expire_timer(conf, zone, soa); } else { /* SOA query answered, reschedule refresh timer. */ zone_events_schedule(zone, ZONE_EVENT_REFRESH, knot_soa_refresh(soa)); } return KNOT_EOK; }
int event_xfer(conf_t *conf, zone_t *zone) { assert(zone); /* Ignore if not slave zone. */ if (!zone_is_slave(conf, zone)) { return KNOT_EOK; } struct transfer_data data = { 0 }; const char *err_str = ""; /* Determine transfer type. */ bool is_bootstrap = zone_contents_is_empty(zone->contents); if (is_bootstrap || zone->flags & ZONE_FORCE_AXFR) { data.pkt_type = KNOT_QUERY_AXFR; err_str = "AXFR, incoming"; } else { data.pkt_type = KNOT_QUERY_IXFR; err_str = "IXFR, incoming"; } /* Execute zone transfer. */ int ret = zone_master_try(conf, zone, try_xfer, &data, err_str); zone_clear_preferred_master(zone); if (ret != KNOT_EOK) { log_zone_error(zone->name, "%s, failed (%s)", err_str, knot_strerror(ret)); if (is_bootstrap) { zone->bootstrap_retry = bootstrap_next(zone->bootstrap_retry); zone_events_schedule(zone, ZONE_EVENT_XFER, zone->bootstrap_retry); } else { const knot_rdataset_t *soa = zone_soa(zone); zone_events_schedule(zone, ZONE_EVENT_XFER, knot_soa_retry(soa)); start_expire_timer(conf, zone, soa); } return KNOT_EOK; } assert(!zone_contents_is_empty(zone->contents)); const knot_rdataset_t *soa = zone_soa(zone); /* Rechedule events. */ zone_events_schedule(zone, ZONE_EVENT_REFRESH, knot_soa_refresh(soa)); zone_events_schedule(zone, ZONE_EVENT_NOTIFY, ZONE_EVENT_NOW); zone_events_cancel(zone, ZONE_EVENT_EXPIRE); conf_val_t val = conf_zone_get(conf, C_ZONEFILE_SYNC, zone->name); int64_t sync_timeout = conf_int(&val); if (sync_timeout == 0) { zone_events_schedule(zone, ZONE_EVENT_FLUSH, ZONE_EVENT_NOW); } else if (sync_timeout > 0 && !zone_events_is_scheduled(zone, ZONE_EVENT_FLUSH)) { zone_events_schedule(zone, ZONE_EVENT_FLUSH, sync_timeout); } /* Transfer cleanup. */ zone->bootstrap_retry = ZONE_EVENT_NOW; zone->flags &= ~ZONE_FORCE_AXFR; /* Trim extra heap. */ if (!is_bootstrap) { mem_trim(); } return KNOT_EOK; }
int event_load(conf_t *conf, zone_t *zone) { assert(zone); /* Take zone file mtime and load it. */ char *filename = conf_zonefile(conf, zone->name); time_t mtime = zonefile_mtime(filename); free(filename); uint32_t dnssec_refresh = time(NULL); zone_contents_t *contents = NULL; int ret = zone_load_contents(conf, zone->name, &contents); if (ret != KNOT_EOK) { goto fail; } /* Store zonefile serial and apply changes from the journal. */ zone->zonefile_serial = zone_contents_serial(contents); ret = zone_load_journal(conf, zone, contents); if (ret != KNOT_EOK) { goto fail; } /* Post load actions - calculate delta, sign with DNSSEC... */ /*! \todo issue #242 dnssec signing should occur in the special event */ ret = zone_load_post(conf, zone, contents, &dnssec_refresh); if (ret != KNOT_EOK) { if (ret == KNOT_ESPACE) { log_zone_error(zone->name, "journal size is too small " "to fit the changes"); } else { log_zone_error(zone->name, "failed to store changes into " "journal (%s)", knot_strerror(ret)); } goto fail; } /* Check zone contents consistency. */ ret = zone_load_check(conf, contents); if (ret != KNOT_EOK) { goto fail; } /* Everything went alright, switch the contents. */ zone->zonefile_mtime = mtime; zone_contents_t *old = zone_switch_contents(zone, contents); zone->flags &= ~ZONE_EXPIRED; uint32_t old_serial = zone_contents_serial(old); if (old != NULL) { synchronize_rcu(); zone_contents_deep_free(&old); } /* Schedule notify and refresh after load. */ if (zone_is_slave(conf, zone)) { zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW); } if (!zone_contents_is_empty(contents)) { zone_events_schedule(zone, ZONE_EVENT_NOTIFY, ZONE_EVENT_NOW); zone->bootstrap_retry = ZONE_EVENT_NOW; } /* Schedule zone resign. */ conf_val_t val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name); if (conf_bool(&val)) { schedule_dnssec(zone, dnssec_refresh); } /* Periodic execution. */ val = conf_zone_get(conf, C_ZONEFILE_SYNC, zone->name); int64_t sync_timeout = conf_int(&val); if (sync_timeout >= 0) { zone_events_schedule(zone, ZONE_EVENT_FLUSH, sync_timeout); } uint32_t current_serial = zone_contents_serial(zone->contents); log_zone_info(zone->name, "loaded, serial %u -> %u", old_serial, current_serial); return KNOT_EOK; fail: zone_contents_deep_free(&contents); /* Try to bootstrap the zone if local error. */ if (zone_is_slave(conf, zone) && !zone_events_is_scheduled(zone, ZONE_EVENT_XFER)) { zone_events_schedule(zone, ZONE_EVENT_XFER, ZONE_EVENT_NOW); } return ret; }
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)); }
/* * Reads the specified zone into the memory * nsd_options can be NULL if no config file is passed. */ unsigned int zonec_read(const char* name, const char* zonefile, zone_type* zone) { const dname_type *dname; totalrrs = 0; startzonec = time(NULL); parser->errors = 0; dname = dname_parse(parser->rr_region, name); if (!dname) { zc_error("incorrect zone name '%s'", name); return 1; } #ifndef ROOT_SERVER /* Is it a root zone? Are we a root server then? Idiot proof. */ if (dname->label_count == 1) { zc_error("not configured as a root server"); return 1; } #endif /* Open the zone file */ if (!zone_open(zonefile, 3600, CLASS_IN, dname)) { zc_error("cannot open '%s': %s", zonefile, strerror(errno)); return 1; } parser->current_zone = zone; /* Parse and process all RRs. */ yyparse(); /* remove origin if it was unused */ if(parser->origin != error_domain) domain_table_deldomain(parser->db, parser->origin); /* rr_region has been emptied by now */ dname = dname_parse(parser->rr_region, name); /* check if zone file contained a correct SOA record */ if (!parser->current_zone) { zc_error("zone configured as '%s' has no content.", name); } else if(!parser->current_zone->soa_rrset || parser->current_zone->soa_rrset->rr_count == 0) { zc_error("zone configured as '%s' has no SOA record.", name); } else if(dname_compare(domain_dname( parser->current_zone->soa_rrset->rrs[0].owner), dname) != 0) { zc_error("zone configured as '%s', but SOA has owner '%s'.", name, domain_to_string( parser->current_zone->soa_rrset->rrs[0].owner)); } region_free_all(parser->rr_region); parser_flush(); fclose(yyin); if(!zone_is_slave(zone->opts)) check_dname(zone); parser->filename = NULL; return parser->errors; }
int process_rr(void) { zone_type *zone = parser->current_zone; rr_type *rr = &parser->current_rr; rrset_type *rrset; size_t max_rdlength; int i; rrtype_descriptor_type *descriptor = rrtype_descriptor_by_type(rr->type); /* We only support IN class */ if (rr->klass != CLASS_IN) { if(zone_is_slave(zone->opts)) zc_warning_prev_line("only class IN is supported"); else zc_error_prev_line("only class IN is supported"); return 0; } /* Make sure the maximum RDLENGTH does not exceed 65535 bytes. */ max_rdlength = rdata_maximum_wireformat_size( descriptor, rr->rdata_count, rr->rdatas); if (max_rdlength > MAX_RDLENGTH) { zc_error_prev_line("maximum rdata length exceeds %d octets", MAX_RDLENGTH); return 0; } /* we have the zone already */ assert(zone); if (rr->type == TYPE_SOA) { if (rr->owner != zone->apex) { zc_error_prev_line( "SOA record with invalid domain name"); return 0; } if(has_soa(rr->owner)) { if(zone_is_slave(zone->opts)) zc_warning_prev_line("this SOA record was already encountered"); else zc_error_prev_line("this SOA record was already encountered"); return 0; } rr->owner->is_apex = 1; } if (!domain_is_subdomain(rr->owner, zone->apex)) { if(zone_is_slave(zone->opts)) zc_warning_prev_line("out of zone data"); else zc_error_prev_line("out of zone data"); return 0; } /* Do we have this type of rrset already? */ rrset = domain_find_rrset(rr->owner, zone, rr->type); if (!rrset) { rrset = (rrset_type *) region_alloc(parser->region, sizeof(rrset_type)); rrset->zone = zone; rrset->rr_count = 1; rrset->rrs = (rr_type *) region_alloc(parser->region, sizeof(rr_type)); rrset->rrs[0] = *rr; /* Add it */ domain_add_rrset(rr->owner, rrset); } else { rr_type* o; if (rr->type != TYPE_RRSIG && rrset->rrs[0].ttl != rr->ttl) { zc_warning_prev_line( "%s TTL %u does not match the TTL %u of the %s RRset", domain_to_string(rr->owner), (unsigned)rr->ttl, (unsigned)rrset->rrs[0].ttl, rrtype_to_string(rr->type)); } /* Search for possible duplicates... */ for (i = 0; i < rrset->rr_count; i++) { if (!zrdatacmp(rr->type, rr, &rrset->rrs[i])) { break; } } /* Discard the duplicates... */ if (i < rrset->rr_count) { return 0; } if(rrset->rr_count == 65535) { zc_error_prev_line("too many RRs for domain RRset"); return 0; } /* Add it... */ o = rrset->rrs; rrset->rrs = (rr_type *) region_alloc_array(parser->region, (rrset->rr_count + 1), sizeof(rr_type)); memcpy(rrset->rrs, o, (rrset->rr_count) * sizeof(rr_type)); region_recycle(parser->region, o, (rrset->rr_count) * sizeof(rr_type)); rrset->rrs[rrset->rr_count] = *rr; ++rrset->rr_count; } if(rr->type == TYPE_DNAME && rrset->rr_count > 1) { if(zone_is_slave(zone->opts)) zc_warning_prev_line("multiple DNAMEs at the same name"); else zc_error_prev_line("multiple DNAMEs at the same name"); } if(rr->type == TYPE_CNAME && rrset->rr_count > 1) { if(zone_is_slave(zone->opts)) zc_warning_prev_line("multiple CNAMEs at the same name"); else zc_error_prev_line("multiple CNAMEs at the same name"); } if((rr->type == TYPE_DNAME && domain_find_rrset(rr->owner, zone, TYPE_CNAME)) ||(rr->type == TYPE_CNAME && domain_find_rrset(rr->owner, zone, TYPE_DNAME))) { if(zone_is_slave(zone->opts)) zc_warning_prev_line("DNAME and CNAME at the same name"); else zc_error_prev_line("DNAME and CNAME at the same name"); } if(domain_find_rrset(rr->owner, zone, TYPE_CNAME) && domain_find_non_cname_rrset(rr->owner, zone)) { if(zone_is_slave(zone->opts)) zc_warning_prev_line("CNAME and other data at the same name"); else zc_error_prev_line("CNAME and other data at the same name"); } /* Check we have SOA */ if(rr->owner == zone->apex) apex_rrset_checks(parser->db, rrset, rr->owner); if(parser->line % ZONEC_PCT_COUNT == 0 && time(NULL) > startzonec + ZONEC_PCT_TIME) { struct stat buf; startzonec = time(NULL); buf.st_size = 0; fstat(fileno(yyin), &buf); if(buf.st_size == 0) buf.st_size = 1; VERBOSITY(1, (LOG_INFO, "parse %s %d %%", parser->current_zone->opts->name, (int)((uint64_t)ftell(yyin)*(uint64_t)100/(uint64_t)buf.st_size))); } ++totalrrs; return 1; }