backup.c
Go to the documentation of this file.
/*
* Copyright (c) 2006-2010 NLNet Labs. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "config.h"
#include "adapter/adapi.h"
#include "adapter/adutil.h"
#include "duration.h"
#include "file.h"
#include "log.h"
#include "status.h"
#include "util.h"
#include "signer/backup.h"
#include "signer/zone.h"
#include <ldns/ldns.h>
static const char* backup_str = "backup";
char*
backup_read_token(FILE* in)
{
static char buf[4000];
buf[sizeof(buf)-1]=0;
while (1) {
if (fscanf(in, "%3990s", buf) != 1) {
return 0;
}
if (buf[0] != '#') {
return buf;
}
if (!fgets(buf, sizeof(buf), in)) {
return 0;
}
}
return 0;
}
int
backup_read_check_str(FILE* in, const char* str)
{
char *p = backup_read_token(in);
if (!p) {
ods_log_debug("[%s] cannot read check string \'%s\'", backup_str, str);
return 0;
}
if (ods_strcmp(p, str) != 0) {
if (!strcmp(p, "rfc5011") && !strcmp(str, "keytag")) {
return 1;
}
if (!strcmp(p, "jitter") && !strcmp(str, "keyset")) {
return fseek(in, -7, SEEK_CUR) == 0;
}
ods_log_debug("[%s] \'%s\' does not match \'%s\'", backup_str, p, str);
return 0;
}
return 1;
}
int
backup_read_str(FILE* in, const char** str)
{
char *p = backup_read_token(in);
if (!p) {
ods_log_debug("[%s] cannot read string", backup_str);
return 0;
}
*str = strdup(p);
return 1;
}
int
backup_read_time_t(FILE* in, time_t* v)
{
char* p = backup_read_token(in);
if (!p) {
ods_log_debug("[%s] cannot read time", backup_str);
return 0;
}
*v=atol(p);
return 1;
}
int
backup_read_duration(FILE* in, duration_type** v)
{
char* p = backup_read_token(in);
if (!p) {
ods_log_debug("[%s] cannot read duration", backup_str);
return 0;
}
if (!strcmp(p, "jitter")) {
return fseek(in, -7, SEEK_CUR) == 0;
}
*v=duration_create_from_string((const char*) p);
return 1;
}
int
backup_read_rr_type(FILE* in, ldns_rr_type* v)
{
char* p = backup_read_token(in);
if (!p) {
ods_log_debug("[%s] cannot read rr type", backup_str);
return 0;
}
*v=(ldns_rr_type) atoi(p);
return 1;
}
int
backup_read_int(FILE* in, int* v)
{
char* p = backup_read_token(in);
if (!p) {
ods_log_debug("[%s] cannot read integer", backup_str);
return 0;
}
*v=atoi(p);
return 1;
}
int
backup_read_uint8_t(FILE* in, uint8_t* v)
{
char* p = backup_read_token(in);
if (!p) {
ods_log_debug("[%s] cannot read uint8_t", backup_str);
return 0;
}
*v= (uint8_t)atoi(p);
return 1;
}
int
backup_read_uint32_t(FILE* in, uint32_t* v)
{
char* p = backup_read_token(in);
if (!p) {
ods_log_debug("[%s] cannot read uint32_t", backup_str);
return 0;
}
*v= (uint32_t)atol(p);
return 1;
}
static ldns_rr*
backup_read_rr(FILE* in, zone_type* zone, char* line, ldns_rdf** orig,
ldns_rdf** prev, ldns_status* status, unsigned int* l)
{
ldns_rr* rr = NULL;
int len = 0;
backup_read_line:
len = adutil_readline_frm_file(in, line, l, 1);
if (len >= 0) {
switch (line[0]) {
case ';':
/* done */
*status = LDNS_STATUS_OK;
return NULL;
break;
case '\n':
case '\0':
goto backup_read_line; /* perhaps next line is rr */
break;
/* let's hope its a RR */
default:
*status = ldns_rr_new_frm_str(&rr, line, zone->default_ttl,
*orig, prev);
if (*status == LDNS_STATUS_OK) {
return rr;
} else if (*status == LDNS_STATUS_SYNTAX_EMPTY) {
if (rr) {
ldns_rr_free(rr);
rr = NULL;
}
*status = LDNS_STATUS_OK;
goto backup_read_line; /* perhaps next line is rr */
break;
} else {
ods_log_error("[%s] error parsing RR #%i (%s): %s",
backup_str, l&&*l?*l:0,
ldns_get_errorstr_by_id(*status), line);
if (rr) {
ldns_rr_free(rr);
rr = NULL;
}
return NULL;
}
break;
}
}
/* -1, EOF */
*status = LDNS_STATUS_OK;
return NULL;
}
static char*
replace_space_with_nul(char* str)
{
int i = 0;
if (!str) {
return NULL;
}
i = strlen(str);
while (i>0) {
--i;
if (str[i] == ' ') {
str[i] = '\0';
}
}
return strdup(str);
}
ods_status
backup_read_namedb(FILE* in, void* zone)
{
zone_type* z = (zone_type*) zone;
denial_type* denial = NULL;
rrset_type* rrset = NULL;
ods_status result = ODS_STATUS_OK;
ldns_rr_type type_covered;
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];
char* str = NULL;
char* locator = NULL;
uint32_t flags = 0;
unsigned int l = 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 */
ods_log_debug("[%s] read RRs %s", backup_str, z->name);
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_namedb_done;
}
/* add to the database */
result = adapi_add_rr(z, rr, 1);
if (result == ODS_STATUS_UNCHANGED) {
ods_log_debug("[%s] skipping RR #%i (duplicate): %s",
backup_str, l, line);
ldns_rr_free(rr);
rr = NULL;
result = ODS_STATUS_OK;
continue;
} else if (result != ODS_STATUS_OK) {
ods_log_error("[%s] error adding RR #%i: %s",
backup_str, l, line);
ldns_rr_free(rr);
rr = NULL;
goto backup_namedb_done;
}
}
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;
goto backup_namedb_done;
}
namedb_diff(z->db, 0, 0);
/* read NSEC(3)s */
ods_log_debug("[%s] read NSEC(3)s %s", backup_str, z->name);
l = 0;
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 NSEC(3) #%i (%s): %s",
backup_str, l, ldns_get_errorstr_by_id(status), line);
result = ODS_STATUS_ERR;
goto backup_namedb_done;
}
if (ldns_rr_get_type(rr) != LDNS_RR_TYPE_NSEC &&
ldns_rr_get_type(rr) != LDNS_RR_TYPE_NSEC3) {
ods_log_error("[%s] error NSEC(3) #%i is not NSEC(3): %s",
backup_str, l, line);
ldns_rr_free(rr);
rr = NULL;
result = ODS_STATUS_ERR;
goto backup_namedb_done;
}
/* add to the denial chain */
denial = namedb_lookup_denial(z->db, ldns_rr_owner(rr));
if (!denial) {
ods_log_error("[%s] error adding NSEC(3) #%i: %s",
backup_str, l, line);
ldns_rr_free(rr);
rr = NULL;
result = ODS_STATUS_ERR;
goto backup_namedb_done;
}
denial_add_rr(denial, rr);
}
if (result == ODS_STATUS_OK && status != LDNS_STATUS_OK) {
ods_log_error("[%s] error reading NSEC(3) #%i (%s): %s",
backup_str, l, ldns_get_errorstr_by_id(status), line);
result = ODS_STATUS_ERR;
goto backup_namedb_done;
}
/* read RRSIGs */
ods_log_debug("[%s] read RRSIGs %s", backup_str, z->name);
l = 0;
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 RRSIG #%i (%s): %s",
backup_str, l, ldns_get_errorstr_by_id(status), line);
result = ODS_STATUS_ERR;
goto backup_namedb_done;
}
if (ldns_rr_get_type(rr) != LDNS_RR_TYPE_RRSIG) {
ods_log_error("[%s] error RRSIG #%i is not RRSIG: %s",
backup_str, l, line);
ldns_rr_free(rr);
rr = NULL;
result = ODS_STATUS_ERR;
goto backup_namedb_done;
}
/* read locator and flags */
str = strstr(line, "flags");
if (str) {
flags = (uint32_t) atoi(str+6);
}
str = strstr(line, "locator");
if (str) {
locator = replace_space_with_nul(str+8);
}
/* add signatures */
type_covered = ldns_rdf2rr_type(ldns_rr_rrsig_typecovered(rr));
if (type_covered == LDNS_RR_TYPE_NSEC ||
type_covered == LDNS_RR_TYPE_NSEC3) {
denial = namedb_lookup_denial(z->db, ldns_rr_owner(rr));
if (!denial) {
ods_log_error("[%s] error restoring RRSIG #%i (%s): %s",
backup_str, l, ldns_get_errorstr_by_id(status), line);
ldns_rr_free(rr);
rr = NULL;
result = ODS_STATUS_ERR;
goto backup_namedb_done;
}
rrset = denial->rrset;
} else {
rrset = zone_lookup_rrset(z, ldns_rr_owner(rr), type_covered);
}
if (!rrset) {
ods_log_error("[%s] error restoring RRSIG #%i (%s): %s",
backup_str, l, ldns_get_errorstr_by_id(status), line);
ldns_rr_free(rr);
rr = NULL;
result = ODS_STATUS_ERR;
goto backup_namedb_done;
}
rrset_add_rrsig(rrset, rr, locator, flags);
locator = NULL; /* Locator is owned by rrset now */
rrset->needs_signing = 0;
}
if (result == ODS_STATUS_OK && status != LDNS_STATUS_OK) {
ods_log_error("[%s] error reading RRSIG #%i (%s): %s",
backup_str, l, ldns_get_errorstr_by_id(status), line);
result = ODS_STATUS_ERR;
}
backup_namedb_done:
if (orig) {
ldns_rdf_deep_free(orig);
orig = NULL;
}
if (prev) {
ldns_rdf_deep_free(prev);
prev = NULL;
}
free(locator); /* if everything went well this is NULL. otherwise
clean up. */
return result;
}
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];
char *str;
uint32_t serial = 0;
unsigned l = 0;
unsigned first_soa = 0;
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) {
str = ldns_rr2str(rr);
ods_log_debug("[%s] ixfr first SOA: %s", backup_str,
str);
LDNS_FREE(str);
/* 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 = 1;
continue;
}
ods_log_assert(first_soa);
if (!del_mode) {
if (z->db->outserial == serial) {
/* final SOA */
str = ldns_rr2str(rr);
ods_log_debug("[%s] ixfr final SOA: %s", backup_str,
str);
LDNS_FREE(str);
ldns_rr_free(rr);
rr = NULL;
result = ODS_STATUS_OK;
first_soa = 2;
continue;
} else {
str = ldns_rr2str(rr);
ods_log_debug("[%s] new part SOA: %s", backup_str,
str);
LDNS_FREE(str);
pthread_mutex_lock(&z->ixfr->ixfr_lock);
ixfr_purge(z->ixfr, z->name);
pthread_mutex_unlock(&z->ixfr->ixfr_lock);
}
} else {
str = ldns_rr2str(rr);
ods_log_debug("[%s] second part SOA: %s", backup_str,
str);
LDNS_FREE(str);
}
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);
if (z->db->is_initialized) {
str = ldns_rr2str(rr);
pthread_mutex_lock(&z->ixfr->ixfr_lock);
if (del_mode) {
ods_log_deeebug("[%s] -IXFR: %s", backup_str, str);
ixfr_del_rr(z->ixfr, rr);
} else {
ods_log_deeebug("[%s] +IXFR: %s", backup_str, str);
ixfr_add_rr(z->ixfr, rr);
}
pthread_mutex_unlock(&z->ixfr->ixfr_lock);
LDNS_FREE(str);
}
ldns_rr_free(rr);
}
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;
}
adapi_add_rr
ods_status adapi_add_rr(zone_type *zone, ldns_rr *rr, int backup)
Definition adapi.c:359
adapi_get_origin
ldns_rdf * adapi_get_origin(zone_type *zone)
Definition adapi.c:78
adapi.h
adutil_readline_frm_file
int adutil_readline_frm_file(FILE *fd, char *line, unsigned int *l, int keep_comments)
Definition adutil.c:47
adutil.h
SE_ADFILE_MAXLINE
#define SE_ADFILE_MAXLINE
Definition adutil.h:40
backup_read_namedb
ods_status backup_read_namedb(FILE *in, void *zone)
Definition backup.c:304
backup_read_uint32_t
int backup_read_uint32_t(FILE *in, uint32_t *v)
Definition backup.c:209
backup_read_int
int backup_read_int(FILE *in, int *v)
Definition backup.c:175
backup_read_ixfr
ods_status backup_read_ixfr(FILE *in, void *zone)
Definition backup.c:499
backup_read_rr_type
int backup_read_rr_type(FILE *in, ldns_rr_type *v)
Definition backup.c:158
backup_read_time_t
int backup_read_time_t(FILE *in, time_t *v)
Definition backup.c:121
backup_read_uint8_t
int backup_read_uint8_t(FILE *in, uint8_t *v)
Definition backup.c:192
backup_read_check_str
int backup_read_check_str(FILE *in, const char *str)
Definition backup.c:77
backup_read_duration
int backup_read_duration(FILE *in, duration_type **v)
Definition backup.c:138
backup_read_str
int backup_read_str(FILE *in, const char **str)
Definition backup.c:104
backup_read_token
char * backup_read_token(FILE *in)
Definition backup.c:53
backup.h
denial_add_rr
void denial_add_rr(denial_type *denial, ldns_rr *rr)
Definition denial.c:259
ixfr_purge
void ixfr_purge(ixfr_type *ixfr, char const *zonename)
Definition ixfr.c:237
ixfr_del_rr
void ixfr_del_rr(ixfr_type *ixfr, ldns_rr *rr)
Definition ixfr.c:131
ixfr_add_rr
void ixfr_add_rr(ixfr_type *ixfr, ldns_rr *rr)
Definition ixfr.c:106
namedb_diff
void namedb_diff(namedb_type *db, unsigned is_ixfr, unsigned more_coming)
Definition namedb.c:811
namedb_lookup_denial
denial_type * namedb_lookup_denial(namedb_type *db, ldns_rdf *dname)
Definition namedb.c:424
rrset_add_rrsig
void rrset_add_rrsig(rrset_type *rrset, ldns_rr *rr, const char *locator, uint32_t flags)
Definition rrset.c:435
denial_struct
Definition denial.h:50
denial_struct::rrset
rrset_type * rrset
Definition denial.h:55
ixfr_struct::ixfr_lock
pthread_mutex_t ixfr_lock
Definition ixfr.h:64
namedb_struct::is_initialized
unsigned is_initialized
Definition namedb.h:57
namedb_struct::outserial
uint32_t outserial
Definition namedb.h:55
rrset_struct
Definition rrset.h:59
rrset_struct::needs_signing
unsigned needs_signing
Definition rrset.h:67
zone_struct
Definition zone.h:60
zone_struct::default_ttl
uint32_t default_ttl
Definition zone.h:63
zone_struct::db
namedb_type * db
Definition zone.h:79
zone_struct::ixfr
ixfr_type * ixfr
Definition zone.h:80
zone_struct::name
const char * name
Definition zone.h:69
zone_lookup_rrset
rrset_type * zone_lookup_rrset(zone_type *zone, ldns_rdf *owner, ldns_rr_type type)
Definition zone.c:510
zone.h