/** * Write zone to output adapter. * */ ods_status tools_output(zone_type* zone, engine_type* engine) { ods_status status = ODS_STATUS_OK; ods_log_assert(engine); ods_log_assert(engine->config); ods_log_assert(zone); ods_log_assert(zone->db); ods_log_assert(zone->name); ods_log_assert(zone->signconf); ods_log_assert(zone->adoutbound); /* prepare */ if (zone->stats) { pthread_mutex_lock(&zone->stats->stats_lock); if (zone->stats->sort_done == 0 && (zone->stats->sig_count <= zone->stats->sig_soa_count)) { ods_log_verbose("[%s] skip write zone %s serial %u (zone not " "changed)", tools_str, zone->name?zone->name:"(null)", zone->db->intserial); stats_clear(zone->stats); pthread_mutex_unlock(&zone->stats->stats_lock); zone->db->intserial = zone->db->outserial; return ODS_STATUS_OK; } pthread_mutex_unlock(&zone->stats->stats_lock); } /* Output Adapter */ status = adapter_write((void*)zone); if (status != ODS_STATUS_OK) { ods_log_error("[%s] unable to write zone %s: adapter failed (%s)", tools_str, zone->name, ods_status2str(status)); return status; } zone->db->outserial = zone->db->intserial; zone->db->is_initialized = 1; zone->db->have_serial = 1; pthread_mutex_lock(&zone->ixfr->ixfr_lock); ixfr_purge(zone->ixfr, zone->name); pthread_mutex_unlock(&zone->ixfr->ixfr_lock); /* kick the nameserver */ if (zone->notify_ns) { int pid_status; pid_t pid, wpid; ods_log_verbose("[%s] notify nameserver: %s", tools_str, zone->notify_ns); /** fork */ switch ((pid = fork())) { case -1: /* error */ ods_log_error("[%s] notify nameserver failed: unable to fork " "(%s)", tools_str, strerror(errno)); return ODS_STATUS_FORK_ERR; case 0: /* child */ /** close fds */ ods_closeall(0); /** execv */ execvp(zone->notify_ns, zone->notify_args); /** error */ ods_log_error("[%s] notify nameserver failed: execv() failed " "(%s)", tools_str, strerror(errno)); exit(1); break; default: /* parent */ ods_log_debug("[%s] notify nameserver process forked", tools_str); /** wait for completion */ while((wpid = waitpid(pid, &pid_status, 0)) <= 0) { if (errno != EINTR) { break; } } if (wpid == -1) { ods_log_error("[%s] notify nameserver failed: waitpid() " "failed (%s)", tools_str, strerror(errno)); } else if (!WIFEXITED(pid_status)) { ods_log_error("[%s] notify nameserver failed: notify " "command did not terminate normally", tools_str); } else { ods_log_verbose("[%s] notify nameserver ok", tools_str); } break; } } /* log stats */ if (zone->stats) { pthread_mutex_lock(&zone->stats->stats_lock); zone->stats->end_time = time(NULL); ods_log_debug("[%s] log stats for zone %s serial %u", tools_str, zone->name?zone->name:"(null)", (unsigned) zone->db->outserial); stats_log(zone->stats, zone->name, zone->db->outserial, zone->signconf->nsec_type); stats_clear(zone->stats); pthread_mutex_unlock(&zone->stats->stats_lock); } if (engine->dnshandler) { ods_log_debug("[%s] forward a notify", tools_str); dnshandler_fwd_notify(engine->dnshandler, (uint8_t*) ODS_SE_NOTIFY_CMD, strlen(ODS_SE_NOTIFY_CMD)); } return status; }
/** * Read ixfr journal from file. * * */ ods_status backup_read_ixfr(FILE* in, void* zone) { zone_type* z = (zone_type*) zone; ods_status result = ODS_STATUS_OK; ldns_rr* rr = NULL; ldns_rdf* prev = NULL; ldns_rdf* orig = NULL; ldns_rdf* dname = NULL; ldns_status status = LDNS_STATUS_OK; char line[SE_ADFILE_MAXLINE]; uint32_t serial = 0; unsigned l = 0; unsigned first_soa = 1; /* expect soa first */ unsigned del_mode = 0; ods_log_assert(in); ods_log_assert(z); /* $ORIGIN <zone name> */ dname = adapi_get_origin(z); if (!dname) { ods_log_error("[%s] error getting default value for $ORIGIN", backup_str); return ODS_STATUS_ERR; } orig = ldns_rdf_clone(dname); if (!orig) { ods_log_error("[%s] error setting default value for $ORIGIN", backup_str); return ODS_STATUS_ERR; } /* read RRs */ while ((rr = backup_read_rr(in, z, line, &orig, &prev, &status, &l)) != NULL) { /* check status */ if (status != LDNS_STATUS_OK) { ods_log_error("[%s] error reading RR #%i (%s): %s", backup_str, l, ldns_get_errorstr_by_id(status), line); result = ODS_STATUS_ERR; goto backup_ixfr_done; } if (first_soa == 2) { ods_log_error("[%s] bad ixfr journal: trailing RRs after final " "SOA", backup_str); ldns_rr_free(rr); rr = NULL; result = ODS_STATUS_ERR; goto backup_ixfr_done; } if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) { serial = ldns_rdf2native_int32( ldns_rr_rdf(rr, SE_SOA_RDATA_SERIAL)); if (first_soa) { ods_log_debug("[%s] ixfr first SOA: %s", backup_str, ldns_rr2str(rr)); /* first SOA */ ldns_rr_free(rr); rr = NULL; if (z->db->outserial != serial) { ods_log_error("[%s] bad ixfr journal: first SOA wrong " "serial (was %u, expected %u)", backup_str, serial, z->db->outserial); result = ODS_STATUS_ERR; goto backup_ixfr_done; } first_soa = 0; continue; } ods_log_assert(!first_soa); if (!del_mode) { if (z->db->outserial == serial) { /* final SOA */ ods_log_debug("[%s] ixfr final SOA: %s", backup_str, ldns_rr2str(rr)); ldns_rr_free(rr); rr = NULL; result = ODS_STATUS_OK; first_soa = 2; continue; } else { ods_log_debug("[%s] new part SOA: %s", backup_str, ldns_rr2str(rr)); lock_basic_lock(&z->ixfr->ixfr_lock); ixfr_purge(z->ixfr); lock_basic_unlock(&z->ixfr->ixfr_lock); } } else { ods_log_debug("[%s] second part SOA: %s", backup_str, ldns_rr2str(rr)); } del_mode = !del_mode; } /* ixfr add or del rr */ if (first_soa) { ods_log_error("[%s] bad ixfr journal: first RR not SOA", backup_str); ldns_rr_free(rr); rr = NULL; result = ODS_STATUS_ERR; goto backup_ixfr_done; } ods_log_assert(!first_soa); lock_basic_lock(&z->ixfr->ixfr_lock); if (del_mode) { ods_log_debug("[%s] -IXFR: %s", backup_str, ldns_rr2str(rr)); ixfr_del_rr(z->ixfr, rr); } else { ods_log_debug("[%s] +IXFR: %s", backup_str, ldns_rr2str(rr)); ixfr_add_rr(z->ixfr, rr); } lock_basic_unlock(&z->ixfr->ixfr_lock); } if (result == ODS_STATUS_OK && status != LDNS_STATUS_OK) { ods_log_error("[%s] error reading RR #%i (%s): %s", backup_str, l, ldns_get_errorstr_by_id(status), line); result = ODS_STATUS_ERR; } backup_ixfr_done: if (orig) { ldns_rdf_deep_free(orig); orig = NULL; } if (prev) { ldns_rdf_deep_free(prev); prev = NULL; } return result; }
/** * Recover zone from backup. * */ ods_status zone_recover2(zone_type* zone) { char* filename = NULL; FILE* fd = NULL; const char* token = NULL; time_t when = 0; task_type* task = NULL; ods_status status = ODS_STATUS_OK; /* zone part */ int klass = 0; uint32_t inbound = 0, internal = 0, outbound = 0; /* signconf part */ time_t lastmod = 0; /* nsec3params part */ const char* salt = NULL; ods_log_assert(zone); ods_log_assert(zone->name); ods_log_assert(zone->signconf); ods_log_assert(zone->db); filename = ods_build_path(zone->name, ".backup2", 0, 1); if (!filename) { return ODS_STATUS_MALLOC_ERR; } fd = ods_fopen(filename, NULL, "r"); if (fd) { /* start recovery */ if (!backup_read_check_str(fd, ODS_SE_FILE_MAGIC_V3)) { ods_log_error("[%s] corrupted backup file zone %s: read magic " "error", zone_str, zone->name); goto recover_error2; } if (!backup_read_check_str(fd, ";;Time:") | !backup_read_time_t(fd, &when)) { ods_log_error("[%s] corrupted backup file zone %s: read time " "error", zone_str, zone->name); goto recover_error2; } /* zone stuff */ if (!backup_read_check_str(fd, ";;Zone:") | !backup_read_check_str(fd, "name") | !backup_read_check_str(fd, zone->name)) { ods_log_error("[%s] corrupted backup file zone %s: read name " "error", zone_str, zone->name); goto recover_error2; } if (!backup_read_check_str(fd, "class") | !backup_read_int(fd, &klass)) { ods_log_error("[%s] corrupted backup file zone %s: read class " "error", zone_str, zone->name); goto recover_error2; } if (!backup_read_check_str(fd, "inbound") | !backup_read_uint32_t(fd, &inbound) | !backup_read_check_str(fd, "internal") | !backup_read_uint32_t(fd, &internal) | !backup_read_check_str(fd, "outbound") | !backup_read_uint32_t(fd, &outbound)) { ods_log_error("[%s] corrupted backup file zone %s: read serial " "error", zone_str, zone->name); goto recover_error2; } zone->klass = (ldns_rr_class) klass; zone->db->inbserial = inbound; zone->db->intserial = internal; zone->db->outserial = outbound; /* signconf part */ if (!backup_read_check_str(fd, ";;Signconf:") | !backup_read_check_str(fd, "lastmod") | !backup_read_time_t(fd, &lastmod) | !backup_read_check_str(fd, "maxzonettl") | !backup_read_check_str(fd, "0") | !backup_read_check_str(fd, "resign") | !backup_read_duration(fd, &zone->signconf->sig_resign_interval) | !backup_read_check_str(fd, "refresh") | !backup_read_duration(fd, &zone->signconf->sig_refresh_interval) | !backup_read_check_str(fd, "valid") | !backup_read_duration(fd, &zone->signconf->sig_validity_default) | !backup_read_check_str(fd, "denial") | !backup_read_duration(fd,&zone->signconf->sig_validity_denial) | !backup_read_check_str(fd, "jitter") | !backup_read_duration(fd, &zone->signconf->sig_jitter) | !backup_read_check_str(fd, "offset") | !backup_read_duration(fd, &zone->signconf->sig_inception_offset) | !backup_read_check_str(fd, "nsec") | !backup_read_rr_type(fd, &zone->signconf->nsec_type) | !backup_read_check_str(fd, "dnskeyttl") | !backup_read_duration(fd, &zone->signconf->dnskey_ttl) | !backup_read_check_str(fd, "soattl") | !backup_read_duration(fd, &zone->signconf->soa_ttl) | !backup_read_check_str(fd, "soamin") | !backup_read_duration(fd, &zone->signconf->soa_min) | !backup_read_check_str(fd, "serial") | !backup_read_str(fd, &zone->signconf->soa_serial)) { ods_log_error("[%s] corrupted backup file zone %s: read signconf " "error", zone_str, zone->name); goto recover_error2; } /* nsec3params part */ if (zone->signconf->nsec_type == LDNS_RR_TYPE_NSEC3) { if (!backup_read_check_str(fd, ";;Nsec3parameters:") | !backup_read_check_str(fd, "salt") | !backup_read_str(fd, &salt) | !backup_read_check_str(fd, "algorithm") | !backup_read_uint32_t(fd, &zone->signconf->nsec3_algo) | !backup_read_check_str(fd, "optout") | !backup_read_int(fd, &zone->signconf->nsec3_optout) | !backup_read_check_str(fd, "iterations") | !backup_read_uint32_t(fd, &zone->signconf->nsec3_iterations)) { ods_log_error("[%s] corrupted backup file zone %s: read " "nsec3parameters error", zone_str, zone->name); goto recover_error2; } zone->signconf->nsec3_salt = allocator_strdup( zone->signconf->allocator, salt); free((void*) salt); salt = NULL; zone->signconf->nsec3params = nsec3params_create( (void*) zone->signconf, (uint8_t) zone->signconf->nsec3_algo, (uint8_t) zone->signconf->nsec3_optout, (uint16_t) zone->signconf->nsec3_iterations, zone->signconf->nsec3_salt); if (!zone->signconf->nsec3params) { ods_log_error("[%s] corrupted backup file zone %s: unable to " "create nsec3param", zone_str, zone->name); goto recover_error2; } } zone->signconf->last_modified = lastmod; zone->default_ttl = (uint32_t) duration2time(zone->signconf->soa_min); /* keys part */ zone->signconf->keys = keylist_create((void*) zone->signconf); while (backup_read_str(fd, &token)) { if (ods_strcmp(token, ";;Key:") == 0) { if (!key_recover2(fd, zone->signconf->keys)) { ods_log_error("[%s] corrupted backup file zone %s: read " "key error", zone_str, zone->name); goto recover_error2; } } else if (ods_strcmp(token, ";;") == 0) { /* keylist done */ free((void*) token); token = NULL; break; } else { /* keylist corrupted */ goto recover_error2; } free((void*) token); token = NULL; } /* publish dnskeys */ status = zone_publish_dnskeys(zone); if (status != ODS_STATUS_OK) { ods_log_error("[%s] corrupted backup file zone %s: unable to " "publish dnskeys (%s)", zone_str, zone->name, ods_status2str(status)); goto recover_error2; } /* publish nsec3param */ status = zone_publish_nsec3param(zone); if (status != ODS_STATUS_OK) { ods_log_error("[%s] corrupted backup file zone %s: unable to " "publish nsec3param (%s)", zone_str, zone->name, ods_status2str(status)); goto recover_error2; } /* publish other records */ status = backup_read_namedb(fd, zone); if (status != ODS_STATUS_OK) { ods_log_error("[%s] corrupted backup file zone %s: unable to " "read resource records (%s)", zone_str, zone->name, ods_status2str(status)); goto recover_error2; } /* task */ task = task_create(TASK_SIGN, when, (void*) zone); if (!task) { ods_log_error("[%s] failed to restore zone %s: unable to " "create task", zone_str, zone->name); goto recover_error2; } zone->task = (void*) task; free((void*)filename); ods_fclose(fd); /* journal */ zone->db->is_initialized = 1; filename = ods_build_path(zone->name, ".ixfr", 0, 1); if (filename) { fd = ods_fopen(filename, NULL, "r"); } if (fd) { status = backup_read_ixfr(fd, zone); if (status != ODS_STATUS_OK) { ods_log_warning("[%s] corrupted journal file zone %s, " "skipping (%s)", zone_str, zone->name, ods_status2str(status)); ixfr_cleanup(zone->ixfr); zone->ixfr = ixfr_create((void*)zone); } } lock_basic_lock(&zone->ixfr->ixfr_lock); ixfr_purge(zone->ixfr); lock_basic_unlock(&zone->ixfr->ixfr_lock); /* all ok */ free((void*)filename); ods_fclose(fd); if (zone->stats) { lock_basic_lock(&zone->stats->stats_lock); stats_clear(zone->stats); lock_basic_unlock(&zone->stats->stats_lock); } return ODS_STATUS_OK; } return ODS_STATUS_UNCHANGED; recover_error2: free((void*)filename); ods_fclose(fd); /* signconf cleanup */ free((void*)salt); salt = NULL; signconf_cleanup(zone->signconf); zone->signconf = signconf_create(); ods_log_assert(zone->signconf); /* namedb cleanup */ namedb_cleanup(zone->db); zone->db = namedb_create((void*)zone); ods_log_assert(zone->db); /* stats reset */ if (zone->stats) { lock_basic_lock(&zone->stats->stats_lock); stats_clear(zone->stats); lock_basic_unlock(&zone->stats->stats_lock); } return ODS_STATUS_ERR; }