mirror of
https://gitlab.alpinelinux.org/alpine/aports.git
synced 2026-05-05 12:26:52 +02:00
testing/gdnsd: experimental version 1.9.0
* update to upstream 1.9.0 * updated to use userspace-rcu * cherry-pick cname reply fix * refreshed djbdns support patch (from upstream dev branch) * updated init.d script
This commit is contained in:
parent
bbc06ec1ab
commit
3c24eab6e2
265
testing/gdnsd/0001-Fix-auth-section-of-ANY-query-on-CNAME.patch
Normal file
265
testing/gdnsd/0001-Fix-auth-section-of-ANY-query-on-CNAME.patch
Normal file
@ -0,0 +1,265 @@
|
||||
From 22b0dcf8a19aaeb1e6f32ad9f0aad95ab26b8a61 Mon Sep 17 00:00:00 2001
|
||||
From: Brandon Black <blblack@gmail.com>
|
||||
Date: Thu, 11 Jul 2013 14:37:57 -0500
|
||||
Subject: [PATCH] Fix auth section of ANY-query on CNAME
|
||||
|
||||
Queries with QTYPE=ANY for a name which has a CNAME RR
|
||||
should be treated as if QTYPE=CNAME. Prior to this
|
||||
fix, they were being treated more like QTYPE=A. Given
|
||||
it's QTYPE=ANY and the effects seem to be limited to
|
||||
the auth section, I doubt this is a production concern
|
||||
for anyone, but it's good to be correct.
|
||||
|
||||
Fixes Issue #51 (thanks Timo!)
|
||||
---
|
||||
gdnsd/dnspacket.c | 11 ++-
|
||||
t/012cname/001cname.t | 157 +++++++++++++++++++++++++++++++++++++++++++
|
||||
t/012cname/gdnsd.conf | 11 +++
|
||||
t/012cname/zones/example.com | 24 +++++++
|
||||
4 files changed, 201 insertions(+), 2 deletions(-)
|
||||
create mode 100644 t/012cname/001cname.t
|
||||
create mode 100644 t/012cname/gdnsd.conf
|
||||
create mode 100644 t/012cname/zones/example.com
|
||||
|
||||
diff --git a/gdnsd/dnspacket.c b/gdnsd/dnspacket.c
|
||||
index 3c26d83..db7e26a 100644
|
||||
--- a/gdnsd/dnspacket.c
|
||||
+++ b/gdnsd/dnspacket.c
|
||||
@@ -1190,6 +1190,9 @@ static unsigned int encode_rrs_any(dnspacket_context_t* c, unsigned int offset,
|
||||
case DNS_TYPE_SOA:
|
||||
offset = encode_rr_soa(c, offset, (const void*)rrset, true);
|
||||
break;
|
||||
+ case DNS_TYPE_CNAME:
|
||||
+ offset = encode_rr_cname(c, offset, (const void*)rrset, true);
|
||||
+ break;
|
||||
case DNS_TYPE_NS:
|
||||
offset = encode_rrs_ns(c, offset, (const void*)rrset, true);
|
||||
break;
|
||||
@@ -1659,8 +1662,12 @@ static unsigned int answer_from_db(dnspacket_context_t* c, const uint8_t* qname,
|
||||
// for the normal response handling code below. The explicit check of the first
|
||||
// rrsets entry works because if CNAME exists at all, by definition it is the only
|
||||
// type of rrset at this node.
|
||||
- while(resdom && resdom->rrsets
|
||||
- && resdom->rrsets->gen.type == DNS_TYPE_CNAME && c->qtype != DNS_TYPE_CNAME) {
|
||||
+ while(resdom
|
||||
+ && resdom->rrsets
|
||||
+ && resdom->rrsets->gen.type == DNS_TYPE_CNAME
|
||||
+ && c->qtype != DNS_TYPE_CNAME
|
||||
+ && c->qtype != DNS_TYPE_ANY) {
|
||||
+
|
||||
dmn_assert(status == DNAME_AUTH);
|
||||
|
||||
res_hdr->flags1 |= 4; // AA bit
|
||||
diff --git a/t/012cname/001cname.t b/t/012cname/001cname.t
|
||||
new file mode 100644
|
||||
index 0000000..6e96335
|
||||
--- /dev/null
|
||||
+++ b/t/012cname/001cname.t
|
||||
@@ -0,0 +1,157 @@
|
||||
+
|
||||
+# CNAME test, with include_optional_ns to get the auth section right...
|
||||
+# this is basically going through A, CNAME, and ANY queries against
|
||||
+# five different classes of CNAME targets (local nonexistent,
|
||||
+# local existent, delegation, delegation glue record, and external).
|
||||
+# CNAME and ANY responses should be identical (this was the bug that
|
||||
+# triggered writing these testcases - ANY was being treated more like A).
|
||||
+
|
||||
+use _GDT ();
|
||||
+use FindBin ();
|
||||
+use File::Spec ();
|
||||
+use Test::More tests => 17;
|
||||
+
|
||||
+my $standard_soa = 'example.com 21600 SOA ns1.example.com hmaster.example.net 1 7200 1800 259200 900';
|
||||
+
|
||||
+my $pid = _GDT->test_spawn_daemon(File::Spec->catfile($FindBin::Bin, 'gdnsd.conf'));
|
||||
+
|
||||
+_GDT->test_dns(
|
||||
+ qname => 'cn-nx.example.com', qtype => 'A',
|
||||
+ header => { rcode => 'NXDOMAIN' },
|
||||
+ answer => 'cn-nx.example.com 21600 CNAME nx.example.com',
|
||||
+ auth => $standard_soa,
|
||||
+ stats => [qw/udp_reqs nxdomain/],
|
||||
+);
|
||||
+
|
||||
+foreach my $qt (qw/CNAME ANY/) {
|
||||
+ _GDT->test_dns(
|
||||
+ qname => 'cn-nx.example.com', qtype => $qt,
|
||||
+ answer => 'cn-nx.example.com 21600 CNAME nx.example.com',
|
||||
+ auth => [
|
||||
+ 'example.com 21600 NS ns1.example.com',
|
||||
+ 'example.com 21600 NS ns2.example.com',
|
||||
+ ],
|
||||
+ addtl => [
|
||||
+ 'ns1.example.com 21600 A 192.0.2.1',
|
||||
+ 'ns2.example.com 21600 A 192.0.2.2',
|
||||
+ ],
|
||||
+ );
|
||||
+}
|
||||
+
|
||||
+_GDT->test_dns(
|
||||
+ qname => 'cn-local.example.com', qtype => 'A',
|
||||
+ answer => [
|
||||
+ 'cn-local.example.com 21600 CNAME ns1.example.com',
|
||||
+ 'ns1.example.com 21600 A 192.0.2.1',
|
||||
+ ],
|
||||
+ auth => [
|
||||
+ 'example.com 21600 NS ns1.example.com',
|
||||
+ 'example.com 21600 NS ns2.example.com',
|
||||
+ ],
|
||||
+ addtl => [
|
||||
+ 'ns2.example.com 21600 A 192.0.2.2',
|
||||
+ ],
|
||||
+);
|
||||
+
|
||||
+foreach my $qt (qw/CNAME ANY/) {
|
||||
+ _GDT->test_dns(
|
||||
+ qname => 'cn-local.example.com', qtype => $qt,
|
||||
+ answer => [
|
||||
+ 'cn-local.example.com 21600 CNAME ns1.example.com'
|
||||
+ ],
|
||||
+ auth => [
|
||||
+ 'example.com 21600 NS ns1.example.com',
|
||||
+ 'example.com 21600 NS ns2.example.com',
|
||||
+ ],
|
||||
+ addtl => [
|
||||
+ 'ns1.example.com 21600 A 192.0.2.1',
|
||||
+ 'ns2.example.com 21600 A 192.0.2.2',
|
||||
+ ],
|
||||
+ );
|
||||
+}
|
||||
+
|
||||
+_GDT->test_dns(
|
||||
+ qname => 'cn-deleg.example.com', qtype => 'A',
|
||||
+ answer => [
|
||||
+ 'cn-deleg.example.com 21600 CNAME foo.subz.example.com',
|
||||
+ ],
|
||||
+ auth => [
|
||||
+ 'subz.example.com 21600 NS ns1.subz.example.com',
|
||||
+ 'subz.example.com 21600 NS ns2.subz.example.com',
|
||||
+ ],
|
||||
+ addtl => [
|
||||
+ 'ns1.subz.example.com 21600 A 192.0.2.10',
|
||||
+ 'ns2.subz.example.com 21600 A 192.0.2.20',
|
||||
+ ],
|
||||
+);
|
||||
+
|
||||
+foreach my $qt (qw/CNAME ANY/) {
|
||||
+ _GDT->test_dns(
|
||||
+ qname => 'cn-deleg.example.com', qtype => $qt,
|
||||
+ answer => [
|
||||
+ 'cn-deleg.example.com 21600 CNAME foo.subz.example.com',
|
||||
+ ],
|
||||
+ auth => [
|
||||
+ 'example.com 21600 NS ns1.example.com',
|
||||
+ 'example.com 21600 NS ns2.example.com',
|
||||
+ ],
|
||||
+ addtl => [
|
||||
+ 'ns1.example.com 21600 A 192.0.2.1',
|
||||
+ 'ns2.example.com 21600 A 192.0.2.2',
|
||||
+ ],
|
||||
+ );
|
||||
+}
|
||||
+
|
||||
+_GDT->test_dns(
|
||||
+ qname => 'cn-deleg-glue.example.com', qtype => 'A',
|
||||
+ answer => [
|
||||
+ 'cn-deleg-glue.example.com 21600 CNAME ns1.subz.example.com',
|
||||
+ ],
|
||||
+ auth => [
|
||||
+ 'subz.example.com 21600 NS ns1.subz.example.com',
|
||||
+ 'subz.example.com 21600 NS ns2.subz.example.com',
|
||||
+ ],
|
||||
+ addtl => [
|
||||
+ 'ns1.subz.example.com 21600 A 192.0.2.10',
|
||||
+ 'ns2.subz.example.com 21600 A 192.0.2.20',
|
||||
+ ],
|
||||
+);
|
||||
+
|
||||
+foreach my $qt (qw/CNAME ANY/) {
|
||||
+ _GDT->test_dns(
|
||||
+ qname => 'cn-deleg-glue.example.com', qtype => $qt,
|
||||
+ answer => [
|
||||
+ 'cn-deleg-glue.example.com 21600 CNAME ns1.subz.example.com',
|
||||
+ ],
|
||||
+ auth => [
|
||||
+ 'example.com 21600 NS ns1.example.com',
|
||||
+ 'example.com 21600 NS ns2.example.com',
|
||||
+ ],
|
||||
+ addtl => [
|
||||
+ 'ns1.example.com 21600 A 192.0.2.1',
|
||||
+ 'ns2.example.com 21600 A 192.0.2.2',
|
||||
+ ],
|
||||
+ );
|
||||
+}
|
||||
+
|
||||
+_GDT->test_dns(
|
||||
+ qname => 'cn-ext.example.com', qtype => 'A',
|
||||
+ answer => 'cn-ext.example.com 21600 CNAME www.example.net',
|
||||
+);
|
||||
+
|
||||
+foreach my $qt (qw/CNAME ANY/) {
|
||||
+ _GDT->test_dns(
|
||||
+ qname => 'cn-ext.example.com', qtype => $qt,
|
||||
+ answer => 'cn-ext.example.com 21600 CNAME www.example.net',
|
||||
+ auth => [
|
||||
+ 'example.com 21600 NS ns1.example.com',
|
||||
+ 'example.com 21600 NS ns2.example.com',
|
||||
+ ],
|
||||
+ addtl => [
|
||||
+ 'ns1.example.com 21600 A 192.0.2.1',
|
||||
+ 'ns2.example.com 21600 A 192.0.2.2',
|
||||
+ ],
|
||||
+ );
|
||||
+}
|
||||
+
|
||||
+_GDT->test_kill_daemon($pid);
|
||||
diff --git a/t/012cname/gdnsd.conf b/t/012cname/gdnsd.conf
|
||||
new file mode 100644
|
||||
index 0000000..2bc6c92
|
||||
--- /dev/null
|
||||
+++ b/t/012cname/gdnsd.conf
|
||||
@@ -0,0 +1,11 @@
|
||||
+options => {
|
||||
+ listen => @dns_lspec@
|
||||
+ http_listen => @http_lspec@
|
||||
+ dns_port => @dns_port@
|
||||
+ http_port => @http_port@
|
||||
+ zones_default_ttl = 21600
|
||||
+ realtime_stats = true
|
||||
+ max_response = 62464
|
||||
+ chaos_response = "some random string"
|
||||
+ include_optional_ns = true
|
||||
+}
|
||||
diff --git a/t/012cname/zones/example.com b/t/012cname/zones/example.com
|
||||
new file mode 100644
|
||||
index 0000000..94a452f
|
||||
--- /dev/null
|
||||
+++ b/t/012cname/zones/example.com
|
||||
@@ -0,0 +1,24 @@
|
||||
+
|
||||
+@ SOA ns1 hmaster.example.net. (
|
||||
+ 1 ; serial
|
||||
+ 7200 ; refresh
|
||||
+ 1800 ; retry
|
||||
+ 259200 ; expire
|
||||
+ 900 ; ncache
|
||||
+)
|
||||
+
|
||||
+@ NS ns1
|
||||
+@ NS ns2
|
||||
+ns1 A 192.0.2.1
|
||||
+ns2 A 192.0.2.2
|
||||
+
|
||||
+subz NS ns1.subz
|
||||
+subz NS ns2.subz
|
||||
+ns1.subz A 192.0.2.10
|
||||
+ns2.subz A 192.0.2.20
|
||||
+
|
||||
+cn-nx CNAME nx
|
||||
+cn-local CNAME ns1
|
||||
+cn-deleg CNAME foo.subz
|
||||
+cn-deleg-glue CNAME ns1.subz
|
||||
+cn-ext CNAME www.example.net.
|
||||
--
|
||||
1.8.3.2
|
||||
|
||||
42
testing/gdnsd/0001-Fix-ztree_txn_-API-to-work.patch
Normal file
42
testing/gdnsd/0001-Fix-ztree_txn_-API-to-work.patch
Normal file
@ -0,0 +1,42 @@
|
||||
From e4d663a531205cdb281318bc912d76f2be22b328 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:47:22 +0300
|
||||
Subject: [PATCH 1/2] Fix ztree_txn_* API to work
|
||||
|
||||
ztree_clone() sets ztclone->zones to valid pointer even where
|
||||
it was NULL in the original ztree. This happens since malloc(0)
|
||||
returns always valid pointer. This confuses _ztree_update() and
|
||||
various other places as in several cases ->zones is tested
|
||||
instead of ->zones_len.
|
||||
|
||||
Fix ztree_clone() to keep ->zones as NULL if the original had
|
||||
it as NULL.
|
||||
---
|
||||
gdnsd/ztree.c | 11 ++++++++---
|
||||
1 file changed, 8 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/gdnsd/ztree.c b/gdnsd/ztree.c
|
||||
index 57ff6d6..3924504 100644
|
||||
--- a/gdnsd/ztree.c
|
||||
+++ b/gdnsd/ztree.c
|
||||
@@ -526,9 +526,14 @@ static ztree_t* ztree_clone(const ztree_t* original) {
|
||||
|
||||
ztree_t* ztclone = malloc(sizeof(ztree_t));
|
||||
ztclone->label = original->label;
|
||||
- ztclone->zones = malloc(original->zones_len * sizeof(zone_t*));
|
||||
- memcpy(ztclone->zones, original->zones, original->zones_len * sizeof(zone_t*));
|
||||
- ztclone->zones_len = original->zones_len;
|
||||
+ if (original->zones) {
|
||||
+ ztclone->zones = malloc(original->zones_len * sizeof(zone_t*));
|
||||
+ memcpy(ztclone->zones, original->zones, original->zones_len * sizeof(zone_t*));
|
||||
+ ztclone->zones_len = original->zones_len;
|
||||
+ } else {
|
||||
+ ztclone->zones = NULL;
|
||||
+ ztclone->zones_len = 0;
|
||||
+ }
|
||||
ztchildren_t* old_ztc = original->children;
|
||||
if(old_ztc) {
|
||||
ztchildren_t* new_ztc = ztclone->children = calloc(1, sizeof(ztchildren_t));
|
||||
--
|
||||
1.8.3.2
|
||||
|
||||
823
testing/gdnsd/0002-Impelement-loading-of-DJBDNS-zone-files.patch
Normal file
823
testing/gdnsd/0002-Impelement-loading-of-DJBDNS-zone-files.patch
Normal file
@ -0,0 +1,823 @@
|
||||
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 statio.c monio.c conf.h dnsio_tcp.h dnsio_udp.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 statio.c monio.c conf.h dnsio_tcp.h dnsio_udp.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
|
||||
|
||||
68
testing/gdnsd/APKBUILD
Normal file
68
testing/gdnsd/APKBUILD
Normal file
@ -0,0 +1,68 @@
|
||||
# Contributor: Natanael Copa <ncopa@alpinelinux.org>
|
||||
# Maintainer: Timo Teräs <timo.teras@iki.fi>
|
||||
pkgname=gdnsd
|
||||
pkgver=1.9.0
|
||||
pkgrel=0
|
||||
pkgdesc="Geographic Authoritative DNS server"
|
||||
url="https://github.com/blblack/gdnsd/"
|
||||
arch="all"
|
||||
license="GPL-3"
|
||||
depends=""
|
||||
depends_dev=""
|
||||
makedepends="userspace-rcu-dev libev-dev libcap-dev autoconf automake libtool"
|
||||
install="$pkgname.pre-install"
|
||||
subpackages="$pkgname-dev $pkgname-doc"
|
||||
source="https://github.com/blblack/gdnsd/archive/v$pkgver.tar.gz
|
||||
0001-Fix-auth-section-of-ANY-query-on-CNAME.patch
|
||||
0001-Fix-ztree_txn_-API-to-work.patch
|
||||
0002-Impelement-loading-of-DJBDNS-zone-files.patch
|
||||
gdnsd.initd"
|
||||
|
||||
_builddir="$srcdir"/gdnsd-$pkgver
|
||||
prepare() {
|
||||
local i
|
||||
cd "$_builddir"
|
||||
for i in $source; do
|
||||
case $i in
|
||||
*.patch) msg $i; patch -p1 -i "$srcdir"/$i || return 1;;
|
||||
esac
|
||||
done
|
||||
./autogen.sh || return 1
|
||||
}
|
||||
|
||||
build() {
|
||||
cd "$_builddir"
|
||||
./configure --prefix=/usr \
|
||||
--sysconfdir=/etc \
|
||||
--localstatedir=/var \
|
||||
--enable-fast-install \
|
||||
|| return 1
|
||||
make || return 1
|
||||
install -Dm755 "$srcdir"/gdnsd.initd \
|
||||
"$pkgdir"/etc/init.d/gdnsd || return 1
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$_builddir"
|
||||
make DESTDIR="$pkgdir" install || return 1
|
||||
rm -f "$pkgdir"/usr/lib/*.la \
|
||||
"$pkgdir"/usr/lib/gdnsd/*.la
|
||||
install -Dm755 "$srcdir"/gdnsd.initd \
|
||||
"$pkgdir"/etc/init.d/gdnsd || return 1
|
||||
}
|
||||
|
||||
md5sums="17b5450d6b78f73bb3f47f7b2d1e5f0f v1.9.0.tar.gz
|
||||
c6229e37f4d3f9c2bec7f8e56ef93b0c 0001-Fix-auth-section-of-ANY-query-on-CNAME.patch
|
||||
64b0232acbd664db83ff2ac800cb5459 0001-Fix-ztree_txn_-API-to-work.patch
|
||||
bc54485f31d09b0c83eb78dee4cd7446 0002-Impelement-loading-of-DJBDNS-zone-files.patch
|
||||
85f07d47b324a8913cb87a45067d4f44 gdnsd.initd"
|
||||
sha256sums="955970ddd07c9926450a07877f106124a57dd56913f40e8fe2262287a2377db0 v1.9.0.tar.gz
|
||||
459b8fab55c701ffa196e6838056322c60129ab3646eefac4dd4900df8300e2a 0001-Fix-auth-section-of-ANY-query-on-CNAME.patch
|
||||
f42d30f3aa88d5fedcf1642de33132beafade609c041ed9f5943ba3da9d7c2fb 0001-Fix-ztree_txn_-API-to-work.patch
|
||||
f78f0071812ef675e7b57cfa8d0edfcd00be84f6e320afedcf9352bc3a08a197 0002-Impelement-loading-of-DJBDNS-zone-files.patch
|
||||
dd1ee7fa4063455f127c444b467625fd12cc51349858757614607cf367804a74 gdnsd.initd"
|
||||
sha512sums="666cb34241fa3dd345d6dbde6d9166c691e8418eaf913dd2b1a7d1c3aa6b1a6f8d7d74b6f6e7804a989951b45d10e69f34bce647737b8460c5b0fc18e25150aa v1.9.0.tar.gz
|
||||
703ef89071a21db03f14efa1fc0d269fbd1d50cc6861cd9912cad1076d0cfe91e7cfc4760a6355cca91df1d58387431c3f53ffa8b19342bde54bc850ff80b278 0001-Fix-auth-section-of-ANY-query-on-CNAME.patch
|
||||
c8a1a23a623dcacf3cd799929a974edbe29e0ab36ef85ada4047dd04726c96ff040eff733509f523b9982aa8956408b9a654f8c6c6c26e454773b3b81d4f49cb 0001-Fix-ztree_txn_-API-to-work.patch
|
||||
c32e7bfd09006344cc53b32fe8597c5d9744d2d66723e0eea6ba4a5ec4db39c5700c844d880bacd41862126a21cdaaca276b8510a512e7f1b5ccdcfdb5ab1f1b 0002-Impelement-loading-of-DJBDNS-zone-files.patch
|
||||
0e8263182bbfa52aa8176443373d5de8b05dfb3f6d4f3268e0deecaca24832125bb3fa59309a4880bd7ae76ffb612ecb61f6cf00732f0237a4de21fadae219b5 gdnsd.initd"
|
||||
53
testing/gdnsd/gdnsd.initd
Executable file
53
testing/gdnsd/gdnsd.initd
Executable file
@ -0,0 +1,53 @@
|
||||
#!/sbin/runscript
|
||||
|
||||
name="gdnsd daemon"
|
||||
extra_commands="configtest"
|
||||
extra_started_commands="reload fastrestart"
|
||||
description="Geographic Authoritative DNS server"
|
||||
description_configtest="Run syntax tests for configuration files only."
|
||||
description_reload="Signal running gdnsd to reload configuration files"
|
||||
description_fastrestart="Optimized restart sequence (minimizes down time)"
|
||||
|
||||
if [ -z "${GDNSD_CONFFILE}" ]; then
|
||||
if [ "${SVCNAME}" = "gdnsd" ]; then
|
||||
GDNSD_ROOT=system
|
||||
else
|
||||
GDNSD_ROOT=/var/gdnsd/${SVCNAME}
|
||||
fi
|
||||
fi
|
||||
|
||||
depend() {
|
||||
need net
|
||||
use logger
|
||||
after firewall
|
||||
provide auth-dns
|
||||
}
|
||||
|
||||
act() {
|
||||
ebegin "$1 ${SVCNAME}"
|
||||
/usr/sbin/gdnsd -d ${GDNSD_ROOT} $2
|
||||
eend $?
|
||||
}
|
||||
|
||||
configtest() {
|
||||
act "Checking configuration" checkconf
|
||||
}
|
||||
|
||||
start() {
|
||||
act "Starting" start
|
||||
}
|
||||
|
||||
stop () {
|
||||
if [ "${RC_CMD}" = "restart" ]; then
|
||||
configtest || return 1
|
||||
fi
|
||||
act "Stopping" stop
|
||||
}
|
||||
|
||||
fastrestart() {
|
||||
act "Restarting" restart
|
||||
}
|
||||
|
||||
reload() {
|
||||
act "Reloading" reload
|
||||
}
|
||||
5
testing/gdnsd/gdnsd.pre-install
Normal file
5
testing/gdnsd/gdnsd.pre-install
Normal file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
addgroup gdnsd 2>/dev/null
|
||||
adduser -S -H -h /dev/null -s /bin/false -D -G gdnsd gdnsd 2>/dev/null
|
||||
exit 0
|
||||
Loading…
x
Reference in New Issue
Block a user