- Re-cloned zonemaster-ldns with --recurse-submodules so the bundled ldns C library source (including Changelog and configure.ac) is present - Added autoconf, automake, libtool to Dockerfile.backend ldns-build stage so libtoolize + autoreconf can generate ldns/configure during make Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
246 lines
5.6 KiB
C
246 lines
5.6 KiB
C
/* zinfo.c - zone information */
|
|
#include "config.h"
|
|
#include "zinfo.h"
|
|
#include "zones.h"
|
|
|
|
static int cmp_version(const void* a, const void* b)
|
|
{
|
|
struct zversion_t* x = (struct zversion_t*)a;
|
|
struct zversion_t* y = (struct zversion_t*)b;
|
|
if(x->serial < y->serial)
|
|
return -1;
|
|
if(x->serial > y->serial)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int cmp_domain(const void* a, const void* b)
|
|
{
|
|
struct zdomain_t* x = (struct zdomain_t*)a;
|
|
struct zdomain_t* y = (struct zdomain_t*)b;
|
|
return ldns_rdf_compare(x->name, y->name);
|
|
}
|
|
|
|
struct zinfo_t* zinfo_create(void)
|
|
{
|
|
struct zinfo_t* zinfo = (struct zinfo_t*)calloc(1, sizeof(*zinfo));
|
|
zinfo->is_present = 0;
|
|
ldns_rbtree_init(&zinfo->vs, cmp_version);
|
|
ldns_rbtree_init(&zinfo->zone, cmp_domain);
|
|
return zinfo;
|
|
}
|
|
|
|
static void del_vs(ldns_rbnode_t* node, void* arg)
|
|
{
|
|
(void)arg;
|
|
zversion_delete((struct zversion_t*)node);
|
|
}
|
|
|
|
static void del_domain(ldns_rbnode_t* node, void* arg)
|
|
{
|
|
(void)arg;
|
|
zdomain_delete((struct zdomain_t*)node);
|
|
}
|
|
|
|
void zinfo_delete(struct zinfo_t* zinfo)
|
|
{
|
|
if(!zinfo) return;
|
|
free(zinfo->dir);
|
|
ldns_rr_list_deep_free(zinfo->last_soa);
|
|
ldns_traverse_postorder(&zinfo->vs, del_vs, NULL);
|
|
ldns_traverse_postorder(&zinfo->zone, del_domain, NULL);
|
|
free(zinfo);
|
|
}
|
|
|
|
/** Get a pointer to a static buffer with filename */
|
|
const char* zinfo_index_name(struct zone_entry_t* entry)
|
|
{
|
|
static char buf[1024];
|
|
snprintf(buf, sizeof(buf), "%s/zone.%s.index",
|
|
entry->zinfo->dir, entry->zstr);
|
|
return buf;
|
|
}
|
|
/** Get a pointer to a static buffer with filename */
|
|
const char* zinfo_ixfr_name(struct zone_entry_t* entry, uint32_t soa)
|
|
{
|
|
static char buf[1024];
|
|
snprintf(buf, sizeof(buf), "%s/zone.%s.ixfr.%u",
|
|
entry->zinfo->dir, entry->zstr, soa);
|
|
return buf;
|
|
}
|
|
/** Get a pointer to a static buffer with filename */
|
|
const char* zinfo_full_name(struct zone_entry_t* entry, uint32_t soa)
|
|
{
|
|
static char buf[1024];
|
|
snprintf(buf, sizeof(buf), "%s/zone.%s.full.%u",
|
|
entry->zinfo->dir, entry->zstr, soa);
|
|
return buf;
|
|
}
|
|
/** see if file exists */
|
|
static int file_exists(const char* path)
|
|
{
|
|
struct stat buf;
|
|
if(stat(path, &buf) < 0) {
|
|
if(errno == ENOENT)
|
|
return 0;
|
|
printf("stat(%s): %s\n", path, strerror(errno));
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int zinfo_read(struct zone_entry_t* entry)
|
|
{
|
|
const char* iname = zinfo_index_name(entry);
|
|
FILE* index = fopen(iname, "ra");
|
|
uint32_t serial = 0;
|
|
uint32_t last_full = 0;
|
|
int have_last_full;
|
|
if(!index) {
|
|
if(errno == ENOENT) {
|
|
printf("zone %s is empty\n", entry->zstr);
|
|
return 1;
|
|
}
|
|
perror(iname);
|
|
return 0;
|
|
}
|
|
if(fscanf(index, " %u", &serial) != 1) {
|
|
fclose(index);
|
|
printf("error reading %s\n", iname);
|
|
return 0;
|
|
}
|
|
fclose(index);
|
|
|
|
/* read versions */
|
|
while(file_exists(zinfo_ixfr_name(entry, serial))) {
|
|
struct zversion_t* v = zversion_read(entry, serial);
|
|
if(!v) return 0;
|
|
if(file_exists(zinfo_full_name(entry, serial))) {
|
|
have_last_full = 1;
|
|
last_full = serial;
|
|
}
|
|
serial = v->next_serial;
|
|
}
|
|
if(file_exists(zinfo_full_name(entry, serial))) {
|
|
have_last_full = 1;
|
|
last_full = serial;
|
|
}
|
|
|
|
/* read full zone */
|
|
if(!have_last_full) {
|
|
printf("No full zone file available for zone %s\n",
|
|
entry->zstr);
|
|
return 0;
|
|
}
|
|
entry->zinfo->last_serial = serial;
|
|
entry->zinfo->is_present = 1;
|
|
if(!zfull_read(entry, last_full))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int zinfo_get_zone_diff(struct zone_entry_t* entry, uint32_t serial_from,
|
|
uint32_t serial_to, ldns_rr_list** rr_remove, ldns_rr_list** rr_add,
|
|
ldns_rr** soa_from, ldns_rr** soa_to)
|
|
{
|
|
/* DIFFs stored in memory and served from memory without copy */
|
|
/* TODO */
|
|
}
|
|
|
|
void zinfo_get_zone_full(struct zone_entry_t* entry, uint32_t serial,
|
|
ldns_zone** z)
|
|
{
|
|
/* full zone kept in memory, served from memory after a fork
|
|
* (and close of other sockets after fork) */
|
|
/* TODO */
|
|
}
|
|
|
|
void zversion_delete(struct zversion_t* v)
|
|
{
|
|
if(!v) return;
|
|
ldns_rr_list_deep_free(v->ixfr);
|
|
free(v);
|
|
}
|
|
|
|
struct zversion_t* zversion_read(struct zone_entry_t* entry, uint32_t serial)
|
|
{
|
|
const char* fn = zinfo_ixfr_name(entry, serial);
|
|
struct zversion_t* v;
|
|
FILE* in = fopen(fn, "ra");
|
|
ldns_status status;
|
|
ldns_rr* rr = 0;
|
|
uint32_t dttl = 3600;
|
|
ldns_rdf* origin = 0, *prev = 0;
|
|
int line_nr = 1;
|
|
if(!in) {
|
|
perror(fn);
|
|
return NULL;
|
|
}
|
|
v = (struct zversion_t*)calloc(1, sizeof(*v));
|
|
if(!v) {
|
|
fclose(in);
|
|
printf("out of memory\n");
|
|
return NULL;
|
|
}
|
|
v->serial = serial;
|
|
v->ixfr = ldns_rr_list_new();
|
|
while(!feof(in)) {
|
|
status = ldns_rr_new_frm_fp_l(&rr, in, &dttl, &origin,
|
|
&prev, &line_nr);
|
|
if(status == LDNS_STATUS_SYNTAX_TTL ||
|
|
status == LDNS_STATUS_SYNTAX_ORIGIN ||
|
|
status == LDNS_STATUS_SYNTAX_EMPTY)
|
|
continue;
|
|
if(status != LDNS_STATUS_OK) {
|
|
printf("error %s:%d: %s\n", fn, line_nr,
|
|
ldns_get_errorstr_by_id(status));
|
|
fclose(in);
|
|
ldns_rdf_deep_free(origin);
|
|
ldns_rdf_deep_free(prev);
|
|
ldns_rr_list_deep_free(v->ixfr);
|
|
free(v);
|
|
return NULL;
|
|
}
|
|
ldns_rr_list_push_rr(v->ixfr, rr);
|
|
}
|
|
ldns_rdf_deep_free(origin);
|
|
ldns_rdf_deep_free(prev);
|
|
fclose(in);
|
|
if(ldns_rr_list_rr_count(v->ixfr) < 1 ||
|
|
ldns_rr_get_type(ldns_rr_list_rr(v->ixfr, 0))
|
|
!= LDNS_RR_TYPE_SOA) {
|
|
printf("invalid IXFR format in %s\n", fn);
|
|
ldns_rr_list_deep_free(v->ixfr);
|
|
free(v);
|
|
return NULL;
|
|
}
|
|
v->next_serial = ldns_rdf2native_int32(ldns_rr_rdf(
|
|
ldns_rr_list_rr(v->ixfr, 0), 2));
|
|
return v;
|
|
}
|
|
|
|
static void del_rrset(ldns_rbnode_t* node, void* arg)
|
|
{
|
|
(void)arg;
|
|
zrrset_delete((struct zrrset_t*)node);
|
|
}
|
|
|
|
void zdomain_delete(struct zdomain_t* d)
|
|
{
|
|
if(!d) return;
|
|
ldns_traverse_postorder(&d->rrsets, del_rrset, NULL);
|
|
ldns_rdf_deep_free(d->name);
|
|
free(d);
|
|
}
|
|
|
|
int zfull_read(struct zone_entry_t* entry, uint32_t serial)
|
|
{
|
|
}
|
|
|
|
void zrrset_delete(struct zrrset_t* r)
|
|
{
|
|
if(!r) return;
|
|
ldns_rr_list_deep_free(r->list);
|
|
free(r);
|
|
}
|