summaryrefslogtreecommitdiffstats
path: root/main/gdnsd
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2012-06-01 08:28:07 +0300
committerTimo Teräs <timo.teras@iki.fi>2012-06-01 08:28:07 +0300
commitb7a63d57d6f6bfa7d6348e4442521503cc5740a5 (patch)
tree46882444dddb48af00228a6b2f16a953f66bda94 /main/gdnsd
parent79a450ab3534176607ac4067f556b5e46dc4d86d (diff)
downloadaports-b7a63d57d6f6bfa7d6348e4442521503cc5740a5.tar.bz2
aports-b7a63d57d6f6bfa7d6348e4442521503cc5740a5.tar.xz
main/gdnsd: add preliminary support for djbdns config files
Diffstat (limited to 'main/gdnsd')
-rw-r--r--main/gdnsd/0001-preliminary-djbdns-support.patch576
-rw-r--r--main/gdnsd/APKBUILD4
2 files changed, 579 insertions, 1 deletions
diff --git a/main/gdnsd/0001-preliminary-djbdns-support.patch b/main/gdnsd/0001-preliminary-djbdns-support.patch
new file mode 100644
index 000000000..238436857
--- /dev/null
+++ b/main/gdnsd/0001-preliminary-djbdns-support.patch
@@ -0,0 +1,576 @@
+From d72aa39cb88178792c1e058b0b757b7cd9466e82 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
+Date: Tue, 8 May 2012 16:57:06 +0300
+Subject: [PATCH] preliminary djbdns support
+
+---
+ docs/TODO | 2 +-
+ gdnsd/Makefile.am | 2 +-
+ gdnsd/conf.c | 6 +-
+ gdnsd/conf.h | 1 +
+ gdnsd/ltree.c | 10 +-
+ gdnsd/zscan-djb.c | 442 +++++++++++++++++++++++++++++++++++++++++++++++++++++
+ gdnsd/zscan.h | 3 +
+ 7 files changed, 460 insertions(+), 6 deletions(-)
+ create mode 100644 gdnsd/zscan-djb.c
+
+diff --git a/docs/TODO b/docs/TODO
+index 2f84a9f..2a7fd9c 100644
+--- a/docs/TODO
++++ b/docs/TODO
+@@ -58,7 +58,7 @@ Realistically, AXFR is pretty far down the priority list. I think a higher-prio
+
+ 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? in both cases, they could periodically update using the same switching mechanism as stat() watcher above...).
++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 (a SQL connection? in both cases, they could periodically update using the same switching mechanism as stat() watcher above...).
+
+ Stuff from conversations w/ Paul Dekkers: --------
+
+diff --git a/gdnsd/Makefile.am b/gdnsd/Makefile.am
+index feaed57..266b344 100644
+--- a/gdnsd/Makefile.am
++++ b/gdnsd/Makefile.am
+@@ -12,7 +12,7 @@ AM_CPPFLAGS = -I$(srcdir)/libgdnsd -I$(builddir)/libgdnsd -DVARDIR=\"$(localstat
+
+ # How to build gdnsd
+ sbin_PROGRAMS = gdnsd
+-gdnsd_SOURCES = main.c conf.c $(ZSCAN_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.h pkterr.h gdnsd.h
++gdnsd_SOURCES = main.c conf.c $(ZSCAN_C) ltarena.c ltree.c zscan-djb.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.h pkterr.h gdnsd.h
+ gdnsd_LDADD = libgdnsd/libgdnsd.la $(CAPLIBS)
+
+ zscan.c: zscan.rl
+diff --git a/gdnsd/conf.c b/gdnsd/conf.c
+index b79cbb2..a3357d4 100644
+--- a/gdnsd/conf.c
++++ b/gdnsd/conf.c
+@@ -55,6 +55,7 @@ global_config_t gconfig = {
+ .pidfile = def_pidfile,
+ .username = def_username,
+ .chroot_path = def_chroot_path,
++ .djbdns_path = NULL,
+ .include_optional_ns = false,
+ .realtime_stats = false,
+ .lock_mem = false,
+@@ -577,7 +578,7 @@ void conf_load(const char* cfg_file) {
+
+ const vscf_data_t* options = vscf_hash_get_data_byconstkey(cfg_root, "options", true);
+
+- const char* zdopt = NULL;
++ const char* zdopt = NULL, * djbopt = NULL;
+ char* zones_dir = NULL;
+ const vscf_data_t* listen_opt = NULL;
+ const vscf_data_t* http_listen_opt = NULL;
+@@ -628,6 +629,7 @@ void conf_load(const char* cfg_file) {
+ CFG_OPT_STR(options, pidfile);
+ CFG_OPT_STR(options, username);
+ CFG_OPT_STR(options, chroot_path);
++ CFG_OPT_STR_NOCOPY(options, djbdns_path, djbopt);
+ CFG_OPT_STR_NOCOPY(options, zones_dir, zdopt);
+ listen_opt = vscf_hash_get_data_byconstkey(options, "listen", true);
+ http_listen_opt = vscf_hash_get_data_byconstkey(options, "http_listen", true);
+@@ -637,6 +639,8 @@ void conf_load(const char* cfg_file) {
+
+ // Potentially a subdirectory of cfg_dir
+ zones_dir = make_zones_dir(gdnsd_get_cfdir(), zdopt);
++ if (djbopt)
++ gconfig.djbdns_path = make_zones_dir(gdnsd_get_cfdir(), djbopt);
+
+ // Set up the http listener data
+ process_http_listen(http_listen_opt, def_http_port);
+diff --git a/gdnsd/conf.h b/gdnsd/conf.h
+index 07a39d4..4374aa6 100644
+--- a/gdnsd/conf.h
++++ b/gdnsd/conf.h
+@@ -48,6 +48,7 @@ typedef struct {
+ const char* pidfile;
+ const char* username;
+ const char* chroot_path;
++ const char* djbdns_path;
+ bool include_optional_ns;
+ bool realtime_stats;
+ bool lock_mem;
+diff --git a/gdnsd/ltree.c b/gdnsd/ltree.c
+index 8d3debf..ef5d46c 100644
+--- a/gdnsd/ltree.c
++++ b/gdnsd/ltree.c
+@@ -1126,9 +1126,13 @@ void ltree_load_zones(void) {
+ ltree_find_or_add_dname(zone->dname, true);
+ }
+
+- for(unsigned i = 0; i < gconfig.num_zones; i++) {
+- const zoneinfo_t* zone = &gconfig.zones[i];
+- scan_zone(zone);
++ if (!gconfig.djbdns_path) {
++ for(unsigned i = 0; i < gconfig.num_zones; i++) {
++ const zoneinfo_t* zone = &gconfig.zones[i];
++ scan_zone(zone);
++ }
++ } else {
++ read_djb_directory(gconfig.djbdns_path);
+ }
+
+ // Close the ltarena to further allocations. Mostly
+diff --git a/gdnsd/zscan-djb.c b/gdnsd/zscan-djb.c
+new file mode 100644
+index 0000000..fb18133
+--- /dev/null
++++ b/gdnsd/zscan-djb.c
+@@ -0,0 +1,442 @@
++/* Copyright © 2012 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/>.
++ *
++ */
++
++#include "zscan.h"
++
++#include <string.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <dirent.h>
++
++#include "conf.h"
++#include "ltree.h"
++#include "ltarena.h"
++#include "gdnsd-misc.h"
++
++#define TTL_NS 259200
++#define TTL_POSITIVE 86400
++#define TTL_NEGATIVE 2560
++
++#define parse_warn(_fmt...) \
++ log_warn("Zonefile parse error: " _fmt)
++
++#define parse_error(_fmt...) \
++ log_fatal("Zonefile parse error: " _fmt)
++
++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;
++
++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};
++
++F_NONNULL
++static uint8_t *parse_dname(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 *expand_dname(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(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(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(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(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;
++}
++
++#define TTDCHECK(fno) if (field[fno].len) { skipped++; continue; }
++#define LOCCHECK(fno) if (field[fno].len) { skipped++; continue; }
++
++unsigned read_djb_file(const char *filename, soa_info_t *soas, unsigned mtime) {
++ field_t field[15];
++ unsigned skipped = 0;
++
++ dmn_assert(filename);
++
++ log_debug("Scanning djbzone file '%s'", filename);
++
++ FILE *f = fopen(filename, "rt");
++ if(f == NULL)
++ log_fatal("Cannot open zone file '%s' for reading: %s", filename, logf_errno());
++
++ size_t allocated = 0;
++ ssize_t len;
++ char *line = NULL;
++ uint8_t dname[256], dname2[256], **texts = NULL;
++ unsigned i, ttl;
++
++ while ((len = getline(&line, &allocated, f)) != -1) {
++ if (len == 0 || line[0] == '#' || line[0] == '-')
++ continue;
++ if (line[len-1] == '\n') {
++ line[len-1] = 0;
++ len--;
++ }
++ if (len == 0)
++ continue;
++
++ /* Skip location records */
++ if (line[0] == '%')
++ continue;
++
++ char *c;
++ for (i = 0, c = line + 1; i < sizeof(field)/sizeof(field[0]); i++) {
++ field[i].ptr = c ?: "";
++ 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;
++ }
++ }
++ }
++
++ parse_dname(dname, &field[0]);
++
++ for (i = 0; i < gconfig.num_zones; i++) {
++ if (dname_isinzone(gconfig.zones[i].dname, dname))
++ break;
++ }
++ if (i >= gconfig.num_zones)
++ continue;
++
++ if (mtime > soas[i].mtime)
++ soas[i].mtime = mtime;
++
++ switch (line[0]) {
++ case 'Z': /* SOA */
++ TTDCHECK(9);
++ LOCCHECK(10);
++ if (dname_cmp(gconfig.zones[i].dname, dname) == 0) {
++ parse_dname(soas[i].ns, &field[1]);
++ parse_dname(soas[i].email, &field[2]);
++ soas[i].ttl = parse_ttl(&field[8], TTL_NEGATIVE);
++ soas[i].serial = parse_int(&field[3]);
++ soas[i].refresh = parse_int(&field[4]) ?: 16384;
++ soas[i].retry = parse_int(&field[5]) ?: 2048;
++ soas[i].expire = parse_int(&field[6]) ?: 1048576;
++ soas[i].cache = parse_int(&field[7]) ?: 2560;
++ } else {
++ uint8_t email[256];
++ ltree_add_rec_soa(dname,
++ parse_dname(dname2, &field[1]),
++ parse_dname(email, &field[2]),
++ parse_ttl(&field[8], TTL_NEGATIVE),
++ parse_int(&field[3]) ?: mtime, /* serial */
++ parse_int(&field[4]) ?: 16384, /* refresh */
++ parse_int(&field[5]) ?: 2048, /* retry */
++ parse_int(&field[6]) ?: 1048576, /* expire */
++ parse_int(&field[7]) ?: 2560 /* cache */);
++ }
++ break;
++ case '.': /* NS + SOA (+ A) */
++ case '&': /* NS (+ A) */
++ TTDCHECK(4);
++ LOCCHECK(5);
++ expand_dname(dname2, &field[2], dname_ns, dname);
++ ttl = parse_ttl(&field[3], TTL_NS);
++ ltree_add_rec_ns(dname, dname2, ttl);
++ if (field[1].len) {
++ ltree_add_rec_a(dname2,
++ parse_ipv4(&field[1]),
++ ttl, 0, NULL);
++ }
++ break;
++ case '@': /* MX (+ A) */
++ TTDCHECK(5);
++ LOCCHECK(6);
++ expand_dname(dname2, &field[2], dname_mx, dname);
++ ttl = parse_ttl(&field[4], TTL_POSITIVE);
++ ltree_add_rec_mx(dname, dname2, ttl, parse_int(&field[3]));
++ if (field[1].len) {
++ ltree_add_rec_a(dname2,
++ parse_ipv4(&field[1]),
++ ttl, 0, NULL);
++ }
++ break;
++ case '+': /* A */
++ case '=': /* A + PTR */
++ TTDCHECK(3);
++ ttl = parse_ttl(&field[2], TTL_POSITIVE);
++ if (field[4].len == 2 && memcmp(field[4].ptr, "~~", 2) == 0) {
++ ltree_add_rec_dynaddr(dname, (const uint8_t *) field[1].ptr,
++ ttl, 0, 0);
++ } else {
++ LOCCHECK(4);
++ ltree_add_rec_a(dname, parse_ipv4(&field[1]), ttl, 0, NULL);
++#if 0
++ /* FIXME: autogen PTR record */
++ if (line[0] == '=') {
++ ltree_add_rec_ptr();
++ }
++#endif
++ }
++ break;
++ case 'C': /* CNAME */
++ TTDCHECK(3);
++ ttl = parse_ttl(&field[2], TTL_POSITIVE);
++ if (field[4].len == 2 && memcmp(field[4].ptr, "~~", 2) == 0) {
++ ltree_add_rec_dyncname(dname, (const uint8_t *) field[1].ptr,
++ dname_root, ttl);
++ } else {
++ LOCCHECK(4);
++ ltree_add_rec_cname(dname, parse_dname(dname2, &field[1]), ttl);
++ }
++ 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("Text chunk too long (>255 unescaped)");
++ if(bytes > 65500)
++ parse_error("Text chunk too long (>65500 unescaped)");
++
++ texts = realloc(texts, sizeof(uint8_t *) * (chunks + 1));
++ for (i = 0; i < chunks; i++) {
++ int s = (bytes > 255 ? 255 : bytes);
++ texts[i] = lta_malloc_1(s + 1);
++ texts[i][0] = s;
++ memcpy(&texts[i][1], src, s);
++
++ bytes -= s;
++ src += s;
++ }
++ texts[i] = NULL;
++ ltree_add_rec_txt(dname, chunks, texts,
++ parse_ttl(&field[2], TTL_POSITIVE));
++ break;
++ case 'S': /* SRV (+ A) */
++ TTDCHECK(7);
++ LOCCHECK(8);
++ expand_dname(dname2, &field[2], dname_srv, dname);
++ ttl = parse_ttl(&field[6], TTL_POSITIVE);
++ ltree_add_rec_srv(dname, dname2,
++ ttl,
++ parse_int(&field[4]),
++ parse_int(&field[5]),
++ parse_int(&field[3]));
++ if (field[1].len) {
++ ltree_add_rec_a(dname2,
++ parse_ipv4(&field[1]),
++ ttl, 0, NULL);
++ }
++ 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("NAPTR label cannot exceed 255 chars");
++
++ texts = realloc(texts, sizeof(uint8_t *) * (3 + 1));
++ for (i = 0; i < 3; i++) {
++ texts[i] = lta_malloc_1(field[3+i].len + 1);
++ texts[i][0] = field[3+i].len;
++ memcpy(&texts[i][1], field[3+i].ptr, field[3+i].len);
++ }
++ texts[i] = NULL;
++ ltree_add_rec_naptr(dname,
++ parse_dname(dname2, &field[6]),
++ parse_ttl(&field[7], TTL_POSITIVE),
++ parse_int(&field[1]),
++ parse_int(&field[2]),
++ 3, texts);
++ break;
++#if 0
++ case '3': /* AAAA */
++ case '6': /* AAAA + PTR */
++ case '^': /* PTR */
++ case ':': /* raw */
++#endif
++ default:
++ log_warn("Unsupported djbdns record type '%c'", line[0]);
++ }
++ }
++ free(texts);
++ free(line);
++ fclose(f);
++
++ return skipped;
++}
++
++void read_djb_directory(const char *path) {
++ struct dirent *e;
++ DIR *dir;
++ unsigned i, skipped = 0;
++ soa_info_t *soas;
++
++ soas = calloc(gconfig.num_zones, sizeof(soa_info_t));
++
++ dir = opendir(path);
++ if (dir == NULL)
++ log_fatal("Cannot open djbzone directory '%s': %s", path, logf_errno());
++ while ((e = readdir(dir)) != NULL) {
++ if (e->d_name[0] == '.')
++ continue;
++ struct stat st;
++ char *fn = gdnsd_make_abs_fn(gconfig.djbdns_path, e->d_name);
++ if (stat(fn, &st))
++ log_fatal("Cannot stat djbzone file '%s': %s", e->d_name, logf_errno());
++ if((st.st_mode & S_IFMT) == S_IFREG)
++ skipped += read_djb_file(fn, soas, st.st_mtime);
++ free(fn);
++ }
++ closedir(dir);
++
++ for (i = 0; i < gconfig.num_zones; i++) {
++ if (soas[i].mtime == 0)
++ continue;
++
++ ltree_add_rec_soa(gconfig.zones[i].dname,
++ soas[i].ns, soas[i].email,
++ soas[i].ttl,
++ soas[i].serial ? : soas[i].mtime,
++ soas[i].refresh,
++ soas[i].retry,
++ soas[i].expire,
++ soas[i].cache);
++ }
++
++ free(soas);
++
++ if (skipped)
++ log_warn("Skipped: %d records with TTD or location", skipped);
++}
+diff --git a/gdnsd/zscan.h b/gdnsd/zscan.h
+index faaba1f..d246617 100644
+--- a/gdnsd/zscan.h
++++ b/gdnsd/zscan.h
+@@ -36,4 +36,7 @@ typedef struct {
+ F_NONNULL
+ void scan_zone(const zoneinfo_t* zone);
+
++F_NONNULL
++void read_djb_directory(const char* path);
++
+ #endif // _GDNSD_ZSCAN_H
+--
+1.7.10.2
+
diff --git a/main/gdnsd/APKBUILD b/main/gdnsd/APKBUILD
index ef6935d70..16e3b77c3 100644
--- a/main/gdnsd/APKBUILD
+++ b/main/gdnsd/APKBUILD
@@ -2,7 +2,7 @@
# Maintainer: Natanael Copa <ncopa@alpinelinux.org>
pkgname=gdnsd
pkgver=1.6.7
-pkgrel=1
+pkgrel=2
pkgdesc="Geographic Authoritative DNS server"
url="http://code.google.com/p/gdnsd/"
arch="all"
@@ -15,6 +15,7 @@ subpackages="$pkgname-dev $pkgname-doc"
source="http://gdnsd.googlecode.com/files/gdnsd-$pkgver.tar.xz
geoip-autodc.patch
geoip-speedup.patch
+ 0001-preliminary-djbdns-support.patch
gdnsd.initd"
_builddir="$srcdir"/gdnsd-$pkgver
@@ -50,4 +51,5 @@ package() {
md5sums="86b95b6533d2f73174f21feb34a5cf3f gdnsd-1.6.7.tar.xz
7955bf52d394862512ae6c000c8b6242 geoip-autodc.patch
7fb697653b8295322e53492f9051e831 geoip-speedup.patch
+4c8ff14e377fb8f1854b68b7bf9be282 0001-preliminary-djbdns-support.patch
00f2838e0908effaaa2f6e6a1699f25b gdnsd.initd"