aboutsummaryrefslogtreecommitdiffstats
path: root/main/gdnsd/0002-Impelement-loading-of-DJBDNS-zone-files.patch
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2017-10-27 06:12:15 +0000
committerTimo Teräs <timo.teras@iki.fi>2017-10-27 06:12:15 +0000
commiteb26ddb58a52b62f0ca9024cf617cc21bbfc4961 (patch)
tree2bfe3978cf92826d966ba909b03df4f19aaa416e /main/gdnsd/0002-Impelement-loading-of-DJBDNS-zone-files.patch
parenta274400e95b3ef7c072496be2eb36c553af52b4c (diff)
downloadaports-eb26ddb58a52b62f0ca9024cf617cc21bbfc4961.tar.bz2
aports-eb26ddb58a52b62f0ca9024cf617cc21bbfc4961.tar.xz
main/gdnsd: remove unused patches
Diffstat (limited to 'main/gdnsd/0002-Impelement-loading-of-DJBDNS-zone-files.patch')
-rw-r--r--main/gdnsd/0002-Impelement-loading-of-DJBDNS-zone-files.patch823
1 files changed, 0 insertions, 823 deletions
diff --git a/main/gdnsd/0002-Impelement-loading-of-DJBDNS-zone-files.patch b/main/gdnsd/0002-Impelement-loading-of-DJBDNS-zone-files.patch
deleted file mode 100644
index 8003a1b384..0000000000
--- a/main/gdnsd/0002-Impelement-loading-of-DJBDNS-zone-files.patch
+++ /dev/null
@@ -1,823 +0,0 @@
-From 53c6e973765c7cd096f982a304bc87fc1ca114ae Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
-Date: Thu, 11 Jul 2013 18:51:43 +0300
-Subject: [PATCH 2/2] Impelement loading of DJBDNS zone files
-
----
- docs/TODO | 12 +-
- gdnsd/Makefile.am | 2 +-
- gdnsd/main.c | 1 +
- gdnsd/zscan_djb.c | 577 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- gdnsd/zscan_djb.h | 38 ++++
- gdnsd/zsrc_djb.c | 91 ++++++---
- gdnsd/zsrc_djb.h | 2 +
- 7 files changed, 694 insertions(+), 29 deletions(-)
- create mode 100644 gdnsd/zscan_djb.c
- create mode 100644 gdnsd/zscan_djb.h
-
-diff --git a/docs/TODO b/docs/TODO
-index 1e67141..831231b 100644
---- a/docs/TODO
-+++ b/docs/TODO
-@@ -161,12 +161,12 @@ Other zonefile formats:
- -------------------------
- Load other zonefile (or zone data in general) formats? The BIND syntax
- sucks, but I'm keeping it as the default, it's too widespread not to.
--However, the zonefile scanner is mostly cleanly separated from the rest
--of the code, and it wouldn't be that hard to add support for more
--formats (djbdns? a SQL connection?). Update: the core code is
--basically ready for this. I even created a mostly-empty zsrc_djb.c
--file since that's likely the first/easiest target. Just needs
--implementation.
-+
-+Additionally, the djbdns style zone files are also supported.
-+
-+As the zonefile scanner is mostly cleanly separated from the rest of
-+the code, additional zonefile formats (e.g. SQL backend) should not
-+be too hard to implement.
-
- Stuff from conversations w/ Paul Dekkers: --------
-
-diff --git a/gdnsd/Makefile.am b/gdnsd/Makefile.am
-index a57a3d4..a42309d 100644
---- a/gdnsd/Makefile.am
-+++ b/gdnsd/Makefile.am
-@@ -4,7 +4,7 @@ AM_CPPFLAGS = -I$(srcdir)/libgdnsd -I$(builddir)/libgdnsd
-
- # How to build gdnsd
- sbin_PROGRAMS = gdnsd
--gdnsd_SOURCES = main.c conf.c zsrc_djb.c zsrc_djb.h zsrc_rfc1035.c zsrc_rfc1035.h ztree.c ztree.h zscan_rfc1035.c ltarena.c ltree.c dnspacket.c dnsio_udp.c dnsio_tcp.c dnsio.c statio.c monio.c conf.h dnsio_tcp.h dnsio_udp.h dnsio.h dnspacket.h dnswire.h ltarena.h ltree.h statio.h monio.h zscan_rfc1035.h
-+gdnsd_SOURCES = main.c conf.c zsrc_djb.c zsrc_djb.h zscan_djb.c zsrc_rfc1035.c zsrc_rfc1035.h ztree.c ztree.h zscan_rfc1035.c ltarena.c ltree.c dnspacket.c dnsio_udp.c dnsio_tcp.c dnsio.c statio.c monio.c conf.h dnsio_tcp.h dnsio_udp.h dnsio.h dnspacket.h dnswire.h ltarena.h ltree.h statio.h monio.h zscan_rfc1035.h
- gdnsd_LDADD = libgdnsd/libgdnsd.la $(LIBGDNSD_LIBS) $(CAPLIBS)
-
- zscan_rfc1035.c: zscan_rfc1035.rl
-diff --git a/gdnsd/main.c b/gdnsd/main.c
-index b2b5d22..759ce1e 100644
---- a/gdnsd/main.c
-+++ b/gdnsd/main.c
-@@ -89,6 +89,7 @@ static void hup_signal(struct ev_loop* loop V_UNUSED, struct ev_signal *w V_UNUS
-
- log_debug("Received SIGHUP");
- // these functions should log_info() that they're taking SIGHUP actions, as appropriate
-+ zsrc_djb_sighup();
- zsrc_rfc1035_sighup();
- }
-
-diff --git a/gdnsd/zscan_djb.c b/gdnsd/zscan_djb.c
-new file mode 100644
-index 0000000..34c41e8
---- /dev/null
-+++ b/gdnsd/zscan_djb.c
-@@ -0,0 +1,577 @@
-+/* Copyright © 2012-2013 Timo Teräs <timo.teras@iki.fi>
-+ *
-+ * This file is part of gdnsd.
-+ *
-+ * gdnsd is free software: you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation, either version 3 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * gdnsd is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with gdnsd. If not, see <http://www.gnu.org/licenses/>.
-+ *
-+ */
-+
-+#define _GNU_SOURCE
-+#include <stdio.h>
-+#include <string.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
-+#include <dirent.h>
-+#include <setjmp.h>
-+
-+#include "conf.h"
-+#include "ztree.h"
-+#include "zscan_djb.h"
-+#include "gdnsd/log.h"
-+#include "gdnsd/misc.h"
-+
-+#define TTL_NS 259200
-+#define TTL_POSITIVE 86400
-+#define TTL_NEGATIVE 2560
-+
-+#define parse_abort() \
-+ siglongjmp(z->jbuf, 1)
-+
-+#define parse_warn(_fmt, ...) \
-+ log_warn("djb: %s: parse error at line %u: " _fmt,z->fn,z->lcount,__VA_ARGS__);\
-+
-+#define parse_error_noargs(_fmt) \
-+ do {\
-+ log_err("djb: %s: parse error at line %u: " _fmt,z->fn,z->lcount);\
-+ parse_abort();\
-+ } while(0)
-+
-+#define parse_error(_fmt, ...) \
-+ do {\
-+ log_err("djb: %s: parse error at line %u: " _fmt,z->fn,z->lcount,__VA_ARGS__);\
-+ parse_abort();\
-+ } while(0)
-+
-+typedef struct {
-+ char* ptr;
-+ unsigned len;
-+} field_t;
-+
-+typedef struct {
-+ uint8_t ns[256];
-+ uint8_t email[256];
-+ unsigned ttl;
-+ unsigned serial;
-+ unsigned refresh;
-+ unsigned retry;
-+ unsigned expire;
-+ unsigned cache;
-+
-+ unsigned mtime;
-+} soa_info_t;
-+
-+typedef struct {
-+ /* variables preserved across files */
-+ uint64_t mtime;
-+ zscan_djb_zonedata_t* zonedata;
-+ const char* path;
-+ uint8_t** texts;
-+ char* line;
-+ size_t allocated;
-+ int num_texts;
-+ int skipped;
-+
-+ /* file specific data */
-+ int lcount;
-+ char* full_fn;
-+ const char* fn;
-+ FILE* file;
-+
-+ sigjmp_buf jbuf;
-+} zscan_t;
-+
-+static const uint8_t dname_root[] = {1,0};
-+static const uint8_t dname_ns[] = {4,2,'n','s',255};
-+static const uint8_t dname_mx[] = {4,2,'m','x',255};
-+static const uint8_t dname_srv[] = {5,3,'s','r','v',255};
-+
-+void zscan_djbzone_add(zscan_djb_zonedata_t** zd, zone_t *zone) {
-+ zscan_djb_zonedata_t* nzd = malloc(sizeof(zscan_djb_zonedata_t));
-+ nzd->zone = zone;
-+ nzd->marked = 0;
-+ nzd->next = *zd;
-+ *zd = nzd;
-+}
-+
-+zscan_djb_zonedata_t* zscan_djbzone_get(zscan_djb_zonedata_t* zd, const uint8_t* dname, int exact) {
-+ zscan_djb_zonedata_t* best = NULL;
-+
-+ for (; zd; zd = zd->next) {
-+ if (exact) {
-+ if (dname_cmp(zd->zone->dname, dname) == 0)
-+ return zd;
-+ } else {
-+ if (!dname_isinzone(zd->zone->dname, dname))
-+ continue;
-+ if (best == NULL || zd->zone->dname[0] > best->zone->dname[0]) {
-+ best = zd;
-+ if (best->zone->dname[0] == dname[0])
-+ return best;
-+ }
-+ }
-+ }
-+ return best;
-+}
-+
-+void zscan_djbzone_free(zscan_djb_zonedata_t** zd) {
-+ zscan_djb_zonedata_t* cur = *zd;
-+ zscan_djb_zonedata_t* next;
-+
-+ while (cur) {
-+ next = cur->next;
-+ free(cur);
-+ cur = next;
-+ }
-+ *zd = NULL;
-+}
-+
-+
-+F_NONNULL
-+static uint8_t *parse_dname(zscan_t *z, uint8_t *dname, field_t *f) {
-+ dname_status_t status = dname_from_string(dname, (const uint8_t*) f->ptr, f->len);
-+
-+ switch(status) {
-+ case DNAME_INVALID:
-+ parse_error("'%.*s' is not a domain name", f->len, f->ptr);
-+ break;
-+ case DNAME_VALID:
-+ break;
-+ case DNAME_PARTIAL:
-+ if(dname_cat(dname, dname_root) == DNAME_INVALID)
-+ parse_error("'%.*s' is not a valid name", f->len, f->ptr);
-+ break;
-+ }
-+ return dname;
-+}
-+
-+F_NONNULL
-+static uint8_t *make_dname_relative(uint8_t* dname, const uint8_t* parent_dname) {
-+ *dname -= *parent_dname - 1;
-+ dname[*dname] = 0;
-+ dmn_assert(dname_status(dname) == DNAME_VALID);
-+ return dname;
-+}
-+
-+F_NONNULL
-+static uint8_t *expand_dname(zscan_t *z, uint8_t *dname, field_t *f, const uint8_t *subzone, const uint8_t *zone) {
-+ /* fully qualified name in the primary field? */
-+ if (strchr(f->ptr, '.') != NULL)
-+ return parse_dname(z, dname, f);
-+
-+ /* construct dname of form <fieldname>.<subzone>.<zone>
-+ * e.g. ns1.ns.example.com */
-+ dname_from_string(dname, (const uint8_t*) f->ptr, f->len);
-+ dname_cat(dname, subzone);
-+ switch (dname_cat(dname, zone)) {
-+ case DNAME_VALID:
-+ break;
-+ case DNAME_PARTIAL:
-+ if(dname_cat(dname, dname_root) != DNAME_INVALID)
-+ break;
-+ /* fallthrough */
-+ case DNAME_INVALID:
-+ parse_error("unable to expand '%.*s' as to valid domain name", f->len, f->ptr);
-+ break;
-+ }
-+
-+ return dname;
-+}
-+
-+F_NONNULL
-+static uint32_t parse_ipv4(zscan_t *z, field_t *f) {
-+ struct in_addr addr;
-+
-+ if(inet_pton(AF_INET, f->ptr, &addr) <= 0)
-+ parse_error("IPv4 address '%s' invalid", f->ptr);
-+
-+ return addr.s_addr;
-+}
-+
-+F_NONNULL
-+static unsigned parse_ttl(zscan_t *z, field_t *f, unsigned defttl) {
-+ char *end;
-+ if (f->len == 0)
-+ return defttl;
-+ unsigned ttl = strtol(f->ptr, &end, 10);
-+ if (end != f->ptr + f->len)
-+ parse_error("Invalid TTL '%.*s'", f->len, f->ptr);
-+ return ttl;
-+}
-+
-+F_NONNULL
-+static unsigned parse_int(zscan_t *z, field_t *f) {
-+ char *end;
-+ unsigned ttl = strtol(f->ptr, &end, 10);
-+ if (end != f->ptr + f->len)
-+ parse_error("Invalid integer value '%.*s'", f->len, f->ptr);
-+ return ttl;
-+}
-+
-+F_NONNULL
-+static void parse_txt(field_t *f) {
-+ char ch;
-+ unsigned int i;
-+ unsigned int j;
-+
-+ j = 0;
-+ i = 0;
-+ while (i < f->len) {
-+ ch = f->ptr[i++];
-+ if (ch == '\\') {
-+ if (i >= f->len) break;
-+ ch = f->ptr[i++];
-+ if ((ch >= '0') && (ch <= '7')) {
-+ ch -= '0';
-+ if ((i < f->len) && (f->ptr[i] >= '0') && (f->ptr[i] <= '7')) {
-+ ch <<= 3;
-+ ch += f->ptr[i++] - '0';
-+ if ((i < f->len) && (f->ptr[i] >= '0') && (f->ptr[i] <= '7')) {
-+ ch <<= 3;
-+ ch += f->ptr[i++] - '0';
-+ }
-+ }
-+ }
-+ }
-+ f->ptr[j++] = ch;
-+ }
-+ f->len = j;
-+ f->ptr[j] = 0;
-+}
-+
-+static void create_zones(zscan_t *z, char record_type, field_t *field) {
-+ uint8_t dname[256];
-+
-+ if (record_type != 'Z')
-+ return;
-+
-+ parse_dname(z, dname, &field[0]);
-+ if (zscan_djbzone_get(z->zonedata, dname, 1))
-+ return;
-+
-+ char* src = gdnsd_str_combine("djb:", z->fn, NULL);
-+ zscan_djbzone_add(&z->zonedata, zone_new(logf_dname(dname), src));
-+ dmn_fmtbuf_reset();
-+ free(src);
-+}
-+
-+#define TTDCHECK(fno) if (field[fno].len) { z->skipped++; return; }
-+#define LOCCHECK(fno) if (field[fno].len) { z->skipped++; return; }
-+
-+static void load_zones(zscan_t *z, char record_type, field_t *field) {
-+ uint8_t dname[256], dname2[256], email[256];
-+ unsigned i, ttl;
-+
-+ parse_dname(z, dname, &field[0]);
-+ zscan_djb_zonedata_t* zd = zscan_djbzone_get(z->zonedata, dname, 0);
-+ if (!zd)
-+ return;
-+
-+ //log_info("djb: processing '%s'", logf_dname(dname));
-+
-+ zone_t* zone = zd->zone;
-+ make_dname_relative(dname, zone->dname);
-+
-+ //log_info("djb: record %c name '%s' in zone '%s'", record_type, logf_dname(dname), logf_dname(zone->dname));
-+
-+ switch (record_type) {
-+ case 'Z': /* SOA */
-+ TTDCHECK(9);
-+ LOCCHECK(10);
-+ zone->serial = parse_int(z, &field[3]);
-+ zone->mtime = z->mtime;
-+ if (ltree_add_rec_soa(zone, dname,
-+ parse_dname(z, dname2, &field[1]),
-+ parse_dname(z, email, &field[2]),
-+ parse_ttl(z, &field[8], TTL_NEGATIVE),
-+ zone->serial ?: z->mtime, /* serial */
-+ parse_int(z, &field[4]) ?: 16384, /* refresh */
-+ parse_int(z, &field[5]) ?: 2048, /* retry */
-+ parse_int(z, &field[6]) ?: 1048576, /* expire */
-+ parse_int(z, &field[7]) ?: 2560 /* cache */))
-+ parse_abort();
-+ break;
-+ case '.': /* NS + SOA (+ A) */
-+ case '&': /* NS (+ A) */
-+ TTDCHECK(4);
-+ LOCCHECK(5);
-+ expand_dname(z, dname2, &field[2], dname_ns, dname);
-+ ttl = parse_ttl(z, &field[3], TTL_NS);
-+ if (ltree_add_rec_ns(zone, dname, dname2, ttl))
-+ parse_abort();
-+ if (field[1].len) {
-+ zd = zscan_djbzone_get(z->zonedata, dname2, 0);
-+ if (zd) {
-+ make_dname_relative(dname2, zd->zone->dname);
-+ log_info("djb: NS+A name '%s' in zone '%s'", logf_dname(dname2), logf_dname(zd->zone->dname));
-+ if (ltree_add_rec_a(zone, dname2, parse_ipv4(z, &field[1]), ttl, 0, NULL))
-+ parse_abort();
-+ }
-+ }
-+ break;
-+ case '@': /* MX (+ A) */
-+ TTDCHECK(5);
-+ LOCCHECK(6);
-+ expand_dname(z, dname2, &field[2], dname_mx, dname);
-+ ttl = parse_ttl(z, &field[4], TTL_POSITIVE);
-+ if (ltree_add_rec_mx(zone, dname, dname2, ttl, parse_int(z, &field[3])))
-+ parse_abort();
-+ if (field[1].len) {
-+ zd = zscan_djbzone_get(z->zonedata, dname2, 0);
-+ if (zd) {
-+ make_dname_relative(dname2, zd->zone->dname);
-+ log_info("djb: MX+A name '%s' in zone '%s'", logf_dname(dname2), logf_dname(zd->zone->dname));
-+ if (ltree_add_rec_a(zone, dname2, parse_ipv4(z, &field[1]), ttl, 0, NULL))
-+ parse_abort();
-+ }
-+ }
-+ break;
-+ case '+': /* A */
-+ case '=': /* A + PTR */
-+ TTDCHECK(3);
-+ ttl = parse_ttl(z, &field[2], TTL_POSITIVE);
-+ if (field[4].len == 2 && memcmp(field[4].ptr, "~~", 2) == 0) {
-+ /* FIXME: check ooz is right */
-+ if (ltree_add_rec_dynaddr(zone, dname, (const uint8_t *) field[1].ptr, ttl, 0, 0, 0))
-+ parse_abort();
-+ } else {
-+ LOCCHECK(4);
-+ if (ltree_add_rec_a(zone, dname, parse_ipv4(z, &field[1]), ttl, 0, NULL))
-+ parse_abort();
-+#if 0
-+ /* FIXME: autogen PTR record */
-+ if (line[0] == '=') {
-+ ltree_add_rec_ptr();
-+ }
-+#endif
-+ }
-+ break;
-+ case 'C': /* CNAME */
-+ TTDCHECK(3);
-+ ttl = parse_ttl(z, &field[2], TTL_POSITIVE);
-+ if (field[4].len == 2 && memcmp(field[4].ptr, "~~", 2) == 0) {
-+ if (ltree_add_rec_dyncname(zone, dname, (const uint8_t *) field[1].ptr, dname_root, ttl))
-+ parse_abort();
-+ } else {
-+ LOCCHECK(4);
-+ if (ltree_add_rec_cname(zone, dname, parse_dname(z, dname2, &field[1]), ttl))
-+ parse_abort();
-+ }
-+ break;
-+ case '\'': /* TXT */
-+ TTDCHECK(3);
-+ LOCCHECK(4);
-+
-+ parse_txt(&field[1]);
-+
-+ unsigned bytes = field[1].len;
-+ const char* src = field[1].ptr;
-+ unsigned chunks = (bytes + 254) / 255;
-+
-+ if(bytes > 255 && gconfig.disable_text_autosplit)
-+ parse_error_noargs("Text chunk too long (>255 unescaped)");
-+ if(bytes > 65500)
-+ parse_error_noargs("Text chunk too long (>65500 unescaped)");
-+
-+ z->texts = realloc(z->texts, sizeof(uint8_t *) * (chunks + 1));
-+ for (i = 0; i < chunks; i++) {
-+ int s = (bytes > 255 ? 255 : bytes);
-+ z->texts[i] = malloc(s + 1);
-+ z->texts[i][0] = s;
-+ memcpy(&z->texts[i][1], src, s);
-+ bytes -= s;
-+ src += s;
-+ }
-+ z->texts[i] = NULL;
-+ if (ltree_add_rec_txt(zone, dname, chunks, z->texts, parse_ttl(z,&field[2], TTL_POSITIVE))) {
-+ for (i = 0; i < chunks; i++)
-+ free(z->texts[i]);
-+ parse_abort();
-+ }
-+ break;
-+ case 'S': /* SRV (+ A) */
-+ TTDCHECK(7);
-+ LOCCHECK(8);
-+ expand_dname(z, dname2, &field[2], dname_srv, dname);
-+ ttl = parse_ttl(z, &field[6], TTL_POSITIVE);
-+ if (ltree_add_rec_srv(zone, dname, dname2, ttl, parse_int(z, &field[4]), parse_int(z, &field[5]), parse_int(z, &field[3])))
-+ parse_abort();
-+ if (field[1].len) {
-+ zd = zscan_djbzone_get(z->zonedata, dname2, 0);
-+ if (zd) {
-+ make_dname_relative(dname2, zd->zone->dname);
-+ log_info("djb: SRV+A name '%s' in zone '%s'", logf_dname(dname2), logf_dname(zd->zone->dname));
-+ if (ltree_add_rec_a(zone, dname2, parse_ipv4(z, &field[1]), ttl, 0, NULL))
-+ parse_abort();
-+ }
-+ }
-+ break;
-+ case 'N': /* NAPTR */
-+ TTDCHECK(8);
-+ LOCCHECK(9);
-+ parse_txt(&field[3]);
-+ parse_txt(&field[4]);
-+ parse_txt(&field[5]);
-+ if (field[3].len > 255 || field[4].len > 255 || field[5].len > 255)
-+ parse_error_noargs("NAPTR label cannot exceed 255 chars");
-+
-+ z->texts = realloc(z->texts, 4 * sizeof(uint8_t *));
-+ for (i = 0; i < 3; i++) {
-+ z->texts[i] = malloc(field[3+i].len + 1);
-+ z->texts[i][0] = field[3+i].len;
-+ memcpy(&z->texts[i][1], field[3+i].ptr, field[3+i].len);
-+ }
-+ z->texts[i] = NULL;
-+ if (ltree_add_rec_naptr(zone, dname, parse_dname(z, dname2, &field[6]), parse_ttl(z, &field[7], TTL_POSITIVE), parse_int(z, &field[1]), parse_int(z, &field[2]), 3, z->texts)) {
-+ for (i = 0; i < 3; i++)
-+ free(z->texts[i]);
-+ parse_abort();
-+ }
-+ break;
-+#if 0
-+ case '3': /* AAAA */
-+ case '6': /* AAAA + PTR */
-+ case '^': /* PTR */
-+ case ':': /* raw */
-+#endif
-+ default:
-+ parse_warn("Unsupported djb record type '%c'", record_type);
-+ }
-+}
-+
-+typedef void (*djb_recordcb_t)(zscan_t *z, char record_type, field_t *fields);
-+
-+static void zscan_foreach_file_record(zscan_t *z, djb_recordcb_t cb) {
-+ field_t field[15];
-+ ssize_t len;
-+ size_t i;
-+ char *c;
-+
-+ z->lcount = 0;
-+ log_debug("Scanning djbzone file '%s'", z->fn);
-+
-+ z->file = fopen(z->full_fn, "rt");
-+ if(z->file == NULL)
-+ parse_error("Cannot open zone file '%s' for reading: %s", z->full_fn, logf_errno());
-+
-+ while ((len = getline(&z->line, &z->allocated, z->file)) != -1) {
-+ z->lcount++;
-+
-+ /* Skip empty lines and comments */
-+ if (len == 0 || z->line[0] == '#' || z->line[0] == '-')
-+ continue;
-+ if (z->line[len-1] == '\n') {
-+ z->line[len-1] = 0;
-+ len--;
-+ }
-+ /* Skip empty lines and location records */
-+ if (len == 0 || z->line[0] == '%')
-+ continue;
-+
-+ for (i = 0, c = z->line + 1; i < sizeof(field)/sizeof(field[0]); i++) {
-+ field[i].ptr = c ?: (char*) "";
-+ field[i].len = 0;
-+ if (c) {
-+ char *n = strchr(c, ':');
-+ if (n) {
-+ field[i].len = n - c;
-+ *n = 0;
-+ c = n + 1;
-+ } else {
-+ field[i].len = strlen(c);
-+ c = NULL;
-+ }
-+ }
-+ }
-+
-+ cb(z, z->line[0], field);
-+ }
-+}
-+
-+static bool zscan_foreach_record(zscan_t *z, djb_recordcb_t cb) {
-+ DIR *dir;
-+ struct dirent *e;
-+ bool failed = false;
-+
-+ dir = opendir(z->path);
-+ if (dir == NULL) {
-+ log_err("djb: cannot open directory '%s': %s", z->path, logf_errno());
-+ return true;
-+ }
-+
-+ while ((e = readdir(dir)) != NULL) {
-+ if (e->d_name[0] == '.')
-+ continue;
-+
-+ struct stat st;
-+ z->full_fn = gdnsd_str_combine(z->path, e->d_name, &z->fn);
-+ if (stat(z->full_fn, &st)) {
-+ log_err("djb: cannot stat file '%s': %s", z->fn, logf_errno());
-+ parse_abort();
-+ }
-+ if((st.st_mode & S_IFMT) != S_IFREG) {
-+ free(z->full_fn);
-+ z->fn = z->full_fn = NULL;
-+ continue;
-+ }
-+ uint64_t emtime = get_extended_mtime(&st);
-+ if (emtime > z->mtime)
-+ z->mtime = emtime;
-+ failed = true;
-+ if(!sigsetjmp(z->jbuf, 0)) {
-+ zscan_foreach_file_record(z, cb);
-+ failed = false;
-+ }
-+ if (z->file) {
-+ fclose(z->file);
-+ z->file = NULL;
-+ }
-+ free(z->full_fn);
-+ z->fn = z->full_fn = NULL;
-+
-+ if (failed)
-+ break;
-+ }
-+ closedir(dir);
-+
-+ return failed;
-+}
-+
-+F_WUNUSED F_NONNULL
-+bool zscan_djb(const char* djb_path, zscan_djb_zonedata_t** zonedata)
-+{
-+ dmn_assert(djb_path);
-+
-+ zscan_t _z, *z = &_z;
-+ memset(z, 0, sizeof(*z));
-+ z->path = djb_path;
-+
-+ if (zscan_foreach_record(z, create_zones) || zscan_foreach_record(z, load_zones))
-+ goto error;
-+
-+ for (zscan_djb_zonedata_t *zd = z->zonedata; zd; zd = zd->next)
-+ if (zone_finalize(zd->zone))
-+ goto error;
-+
-+ if (z->skipped)
-+ log_warn("djb: skipped %d records with TTD or location", z->skipped);
-+
-+ *zonedata = z->zonedata;
-+ return false;
-+
-+error:
-+ zscan_djbzone_free(&z->zonedata);
-+ return true;
-+}
-diff --git a/gdnsd/zscan_djb.h b/gdnsd/zscan_djb.h
-new file mode 100644
-index 0000000..5c47deb
---- /dev/null
-+++ b/gdnsd/zscan_djb.h
-@@ -0,0 +1,38 @@
-+/* Copyright © 2013 Timo Teräs <timo.teras@iki.fi>
-+ *
-+ * This file is part of gdnsd.
-+ *
-+ * gdnsd is free software: you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation, either version 3 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * gdnsd is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with gdnsd. If not, see <http://www.gnu.org/licenses/>.
-+ *
-+ */
-+
-+#ifndef GDNSD_ZSCAN_DJB_H
-+#define GDNSD_ZSCAN_DJB_H
-+
-+#include "config.h"
-+
-+typedef struct _zscan_djb_zonedata {
-+ zone_t* zone;
-+ int marked;
-+ struct _zscan_djb_zonedata* next;
-+} zscan_djb_zonedata_t;
-+
-+void zscan_djbzone_add(zscan_djb_zonedata_t**, zone_t *zone);
-+zscan_djb_zonedata_t* zscan_djbzone_get(zscan_djb_zonedata_t*, const uint8_t*, int);
-+void zscan_djbzone_free(zscan_djb_zonedata_t**);
-+
-+F_WUNUSED F_NONNULL
-+bool zscan_djb(const char* djb_path, zscan_djb_zonedata_t** zonedata);
-+
-+#endif // GDNSD_ZSCAN_DJB_H
-diff --git a/gdnsd/zsrc_djb.c b/gdnsd/zsrc_djb.c
-index 688d467..9d77a0a 100644
---- a/gdnsd/zsrc_djb.c
-+++ b/gdnsd/zsrc_djb.c
-@@ -18,6 +18,7 @@
- */
-
- #include "zsrc_djb.h"
-+#include "zscan_djb.h"
-
- #include <string.h>
- #include <stdlib.h>
-@@ -27,36 +28,82 @@
-
- #include "conf.h"
- #include "ltree.h"
--#include "ltarena.h"
--#include "ztree.h"
--#include "gdnsd/misc.h"
- #include "gdnsd/log.h"
-+#include "gdnsd/paths.h"
-+
-+static struct ev_loop* zones_loop = NULL;
-+static ev_async* sighup_waker = NULL;
-+static char* djb_dir = NULL;
-+static zscan_djb_zonedata_t* active_zonedata = NULL;
-
- static void unload_zones(void) {
-- // for every zone_t created and sent to ztree earlier
-- // during zsrc_djb_load_zones:
-- // zlist_update(z, NULL); // removes from runtime lookup
-- // zone_delete(z); // destroys actual data inside
-- // free other associated local data, if any
-+ ztree_txn_start();
-+ for (zscan_djb_zonedata_t* cur = active_zonedata; cur; cur = cur->next)
-+ ztree_txn_update(cur->zone, NULL);
-+ ztree_txn_end();
-+
-+ zscan_djbzone_free(&active_zonedata);
-+}
-+
-+static void zsrc_djb_sync_zones(void) {
-+ zscan_djb_zonedata_t* zonedata;
-+ int num_zones = 0;
-+
-+ if (zscan_djb(djb_dir, &zonedata))
-+ return;
-+
-+ ztree_txn_start();
-+ for (zscan_djb_zonedata_t* cur = zonedata; cur; cur = cur->next) {
-+ zscan_djb_zonedata_t* old = zscan_djbzone_get(active_zonedata, cur->zone->dname, 1);
-+ if (old) {
-+ old->marked = 1;
-+ ztree_txn_update(old->zone, cur->zone);
-+ //ztree_update(old->zone, cur->zone);
-+ } else {
-+ ztree_txn_update(NULL, cur->zone);
-+ //ztree_update(NULL, cur->zone);
-+ }
-+ num_zones++;
-+ }
-+ for (zscan_djb_zonedata_t* cur = active_zonedata; cur; cur = cur->next) {
-+ if (!cur->marked)
-+ ztree_txn_update(cur->zone, NULL);
-+ //ztree_update(cur->zone, NULL);
-+ }
-+ ztree_txn_end();
-+
-+ log_info("zsrc_djb: loaded %d zones...", num_zones);
-+
-+ zscan_djbzone_free(&active_zonedata);
-+ active_zonedata = zonedata;
- }
-
- void zsrc_djb_load_zones(void) {
-- // scan input file(s):
-- // create zone_t object for each local zone using
-- // ztree.h:zone_new("example.com", "djb:datafile")
-- // set zone_t->mtime from filesystem mtime.
-- // add records to the zone_t via ltree_add_rec_*.
-- // call zone_finalize(z) to do post-processing
-- // call zlist_update(NULL, z); for each zone created,
-- // which makes it available for runtime lookup
-- // keep track of the zone_t's you created, you're
-- // responsible for destroying them later.
-+ djb_dir = gdnsd_resolve_path_cfg("djbdns/", NULL);
-+ zsrc_djb_sync_zones();
- if(atexit(unload_zones))
- log_fatal("zsrc_djb: atexit(unload_zones) failed: %s", logf_errno());
- }
-
--void zsrc_djb_runtime_init(struct ev_loop* loop V_UNUSED) {
-- // for runtime reloading based on FS updates,
-- // can just no-op for now and load on startup only, above.
-- return;
-+// called within our thread/loop to take sighup action
-+F_NONNULL
-+static void sighup_cb(struct ev_loop* loop, ev_async* w V_UNUSED, int revents V_UNUSED) {
-+ dmn_assert(loop); dmn_assert(w);
-+ log_info("zsrc_djb: received SIGHUP notification, scanning for changes...");
-+ zsrc_djb_sync_zones();
-+}
-+
-+// called from main thread to feed ev_async
-+void zsrc_djb_sighup(void) {
-+ dmn_assert(zones_loop); dmn_assert(sighup_waker);
-+ ev_async_send(zones_loop, sighup_waker);
-+}
-+
-+void zsrc_djb_runtime_init(struct ev_loop* loop) {
-+ dmn_assert(loop);
-+
-+ zones_loop = loop;
-+ sighup_waker = malloc(sizeof(ev_async));
-+ ev_async_init(sighup_waker, sighup_cb);
-+ ev_async_start(loop, sighup_waker);
- }
-diff --git a/gdnsd/zsrc_djb.h b/gdnsd/zsrc_djb.h
-index dc60ae1..dc4c96d 100644
---- a/gdnsd/zsrc_djb.h
-+++ b/gdnsd/zsrc_djb.h
-@@ -28,4 +28,6 @@ void zsrc_djb_load_zones(void);
- F_NONNULL
- void zsrc_djb_runtime_init(struct ev_loop* loop);
-
-+void zsrc_djb_sighup(void);
-+
- #endif // GDNSD_ZSRC_DJB_H
---
-1.8.3.2
-