Commit Diff
Diff:
55b1070657e65c8c1c8f66deeecba8941b1ed4e7
ac6d3a7a0e566907f51b9d7f0734c96d1e150bb3
Commit:
ac6d3a7a0e566907f51b9d7f0734c96d1e150bb3
Tree:
263e8f17686a5140ad589bf31b36338f240e6a8f
Author:
pjp <pjp@delphinusdns.org>
Committer:
pjp <pjp@delphinusdns.org>
Date:
Sat Nov 2 17:24:27 2019 UTC
Message:
Make replicant work on a timer basis (the SOA values). It still needs some more work for changing on notifies.
blob - f28baa2d7a250f52cde48b7d39aec27a014d48c7
blob + 492532feacd44cc35103e66ca8f5ea1c46f20e9b
--- db.c
+++ db.c
@@ -27,7 +27,7 @@
*/
/*
- * $Id: db.c,v 1.13 2019/06/06 14:56:08 pjp Exp $
+ * $Id: db.c,v 1.14 2019/11/02 17:24:27 pjp Exp $
*/
#include <sys/types.h>
@@ -329,7 +329,7 @@ add_rr(struct rbtree *rbt, char *name, int len, u_int1
struct rrset *
find_rr(struct rbtree *rbt, u_int16_t rrtype)
{
- struct rrset *rp, *rp0;
+ struct rrset *rp = NULL, *rp0 = NULL;
#ifdef __linux__
TAILQ_FOREACH(rp, &rbt->rrset_head, entries) {
blob - c663fe105156272dc55d81487d090d928f186172
blob + 57e8b71dd63ba1a821660db306b2006491b0d521
--- ddd-db.h
+++ ddd-db.h
@@ -27,7 +27,7 @@
*/
/*
- * $Id: ddd-db.h,v 1.23 2019/11/01 19:46:56 pjp Exp $
+ * $Id: ddd-db.h,v 1.24 2019/11/02 17:24:27 pjp Exp $
*/
#ifndef _DB_H
@@ -36,6 +36,8 @@
#include <sys/types.h>
#include <limits.h>
+#include <openssl/hmac.h>
+
#define CONFFILE "/etc/delphinusdns/delphinusdns.conf"
#define DEFAULT_SOCKET 64
@@ -408,8 +410,13 @@ struct rzone {
struct sockaddr_storage storage;
char *tsigkey;
char *filename;
- struct soa *soa;
+ struct soa soa;
} *rz, *rz0;
+struct raxfr_logic {
+ int rrtype;
+ int dnssec;
+ int (*raxfr)(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+};
#endif /* _DB_H */
blob - b55c27a833d75613bb57fac75dbf22ce8087aaa9
blob + f10ba51f7561dee658638b0d3781bef62a579f84
--- dddctl/Makefile.openbsd
+++ dddctl/Makefile.openbsd
@@ -6,7 +6,7 @@ SRCS=dddctl.c parse.y base64.c dnssec.c util.c ent.c d
CFLAGS= -Wall -g -I${.CURDIR}/..
LDFLAGS= -Wall -g
-LDADD= -lcrypto -lssl
+LDADD= -lcrypto -lssl -lutil
OBJDIR=.
BINDIR=/usr/local/bin
blob - a8bc84aaff0387f9da3759f664561a11e7ee0ce3
blob + c7be9e01bc4a919d6ccbc93a16fe5557673cf9be
--- dddctl.c
+++ dddctl.c
@@ -27,7 +27,7 @@
*/
/*
- * $Id: dddctl.c,v 1.80 2019/11/01 19:46:56 pjp Exp $
+ * $Id: dddctl.c,v 1.81 2019/11/02 17:24:27 pjp Exp $
*/
#include <sys/param.h>
@@ -205,7 +205,6 @@ void debug_bindump(const char *, int);
int command_socket(char *);
int connect_server(char *, int, u_int32_t);
int lookup_name(FILE *, int, char *, u_int16_t, struct soa *, u_int32_t, char *, u_int16_t);
-int lookup_axfr(FILE *, int, char *, struct soa *, u_int32_t, char *, char *);
int count_db(ddDB *);
void update_soa_serial(ddDB *, char *, time_t);
int notglue(ddDB *, struct rbtree *, char *);
@@ -241,6 +240,7 @@ struct _mycmdtab {
{ NULL, NULL }
};
+
#define KEYTYPE_NONE 0
#define KEYTYPE_KSK 1
#define KEYTYPE_ZSK 2
@@ -364,15 +364,12 @@ extern int raxfr_tsig(FILE *, u_char *, u_char *, u_ch
extern int memcasecmp(u_char *, u_char *, int);
extern int tsig_pseudoheader(char *, uint16_t, time_t, HMAC_CTX *);
+extern int lookup_axfr(FILE *, int, char *, struct soa *, u_int32_t, char *, char *);
extern int dnssec;
-static struct raxfr_logic {
- int rrtype;
- int dnssec;
- int (*raxfr)(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
-} supported[] = {
+static struct raxfr_logic supported[] = {
{ DNS_TYPE_A, 0, raxfr_a },
{ DNS_TYPE_NS, 0, raxfr_ns },
{ DNS_TYPE_MX, 0, raxfr_mx },
@@ -6580,404 +6577,6 @@ connect_server(char *nameserver, int port, u_int32_t f
return (so);
}
-
-
-int
-lookup_axfr(FILE *f, int so, char *zonename, struct soa *mysoa, u_int32_t format, char *tsigkey, char *tsigpass)
-{
- char query[512];
- char pseudo_packet[512];
- char shabuf[32];
- char *reply;
- struct timeval tv, savetv;
- struct question *q;
- struct whole_header {
- u_int16_t len;
- struct dns_header dh;
- } *wh, *rwh;
- struct raxfr_logic *sr;
-
- u_char *p, *name, *keyname;
-
- u_char *end, *estart;
- int len, totallen, zonelen, rrlen, rrtype;
- int soacount = 0;
- int elen = 0;
- int segmentcount = 0;
- int count = 0;
- u_int32_t *ttl;
- u_int16_t *class, *type, rdlen, *plen;
- u_int16_t tcplen;
-
- HMAC_CTX *ctx;
- time_t now = 0;
- socklen_t sizetv;
-
- if (!(format & TCP_FORMAT))
- return -1;
-
- memset(&query, 0, sizeof(query));
-
- wh = (struct whole_header *)&query[0];
-
- wh->dh.id = htons(arc4random() & 0xffff);
- wh->dh.query = 0;
- wh->dh.question = htons(1);
- wh->dh.answer = 0;
- wh->dh.nsrr = 0;
- wh->dh.additional = htons(0);
-
-
- SET_DNS_QUERY(&wh->dh);
- SET_DNS_RECURSION(&wh->dh);
- HTONS(wh->dh.query);
-
- totallen = sizeof(struct whole_header);
-
- name = dns_label(zonename, &len);
- if (name == NULL) {
- return -1;
- }
-
- zonelen = len;
-
- p = (char *)&wh[1];
-
- memcpy(p, name, len);
- totallen += len;
-
- type = (u_int16_t *)&query[totallen];
- *type = htons(DNS_TYPE_AXFR);
- totallen += sizeof(u_int16_t);
-
- class = (u_int16_t *)&query[totallen];
- *class = htons(DNS_CLASS_IN);
- totallen += sizeof(u_int16_t);
-
- /* we have a key, attach a TSIG payload */
- if (tsigkey) {
-
- if ((len = mybase64_decode(tsigpass, (u_char *)&pseudo_packet, sizeof(pseudo_packet))) < 0) {
- fprintf(stderr, "bad base64 password\n");
- return -1;
- }
-
- ctx = HMAC_CTX_new();
- HMAC_Init_ex(ctx, pseudo_packet, len, EVP_sha256(), NULL);
- HMAC_Update(ctx, &query[2], totallen - 2);
-
- now = time(NULL);
- if (tsig_pseudoheader(tsigkey, 300, now, ctx) < 0) {
- fprintf(stderr, "tsig_pseudoheader failed\n");
- return -1;
- }
-
- HMAC_Final(ctx, shabuf, &len);
-
- if (len != 32) {
- fprintf(stderr, "not expected len != 32\n");
- return -1;
- }
-
-#if defined __linux__ || defined __FreeBSD__
- HMAC_CTX_free(ctx);
-#else
- HMAC_cleanup(ctx);
-#endif
-
- keyname = dns_label(tsigkey, &len);
- if (keyname == NULL) {
- return -1;
- }
-
- memcpy(&query[totallen], keyname, len);
- totallen += len;
-
- type = (u_int16_t *)&query[totallen];
- *type = htons(DNS_TYPE_TSIG);
- totallen += 2;
-
- class = (u_int16_t *)&query[totallen];
- *class = htons(DNS_CLASS_ANY);
- totallen += 2;
-
- ttl = (u_int32_t *)&query[totallen];
- *ttl = htonl(0);
- totallen += 4;
-
- keyname = dns_label("hmac-sha256", &len);
- if (keyname == NULL) {
- return -1;
- }
-
- /* rdlen */
- type = (u_int16_t *)&query[totallen];
- *type = htons(len + 2 + 4 + 2 + 2 + 32 + 2 + 2 + 2);
- totallen += 2;
-
- /* algorithm name */
- memcpy(&query[totallen], keyname, len);
- totallen += len;
-
- /* time 1 */
- type = (u_int16_t *)&query[totallen];
- *type = htons((now >> 32) & 0xffff);
- totallen += 2;
-
- /* time 2 */
- ttl = (u_int32_t *)&query[totallen];
- *ttl = htonl((now & 0xffffffff));
- totallen += 4;
-
- /* fudge */
- type = (u_int16_t *)&query[totallen];
- *type = htons(300);
- totallen += 2;
-
- /* hmac size */
- type = (u_int16_t *)&query[totallen];
- *type = htons(sizeof(shabuf));
- totallen += 2;
-
- /* hmac */
- memcpy(&query[totallen], shabuf, sizeof(shabuf));
- totallen += sizeof(shabuf);
-
- /* original id */
- type = (u_int16_t *)&query[totallen];
- *type = wh->dh.id;
- totallen += 2;
-
- /* error */
- type = (u_int16_t *)&query[totallen];
- *type = 0;
- totallen += 2;
-
- /* other len */
- type = (u_int16_t *)&query[totallen];
- *type = 0;
- totallen += 2;
-
- wh->dh.additional = htons(1);
- }
-
-
- wh->len = htons(totallen - 2);
-
- if (send(so, query, totallen, 0) < 0) {
- perror("send");
- return -1;
- }
-
- /* catch reply */
-
- reply = calloc(1, 0xffff + 2);
- if (reply == NULL) {
- perror("calloc");
- return -1;
- }
-
- if (tsigkey) {
- uint16_t maclen;
-
- if ((len = mybase64_decode(tsigpass, (u_char *)&pseudo_packet, sizeof(pseudo_packet))) < 0) {
- fprintf(stderr, "bad base64 password\n");
- return -1;
- }
-
- ctx = HMAC_CTX_new();
- HMAC_Init_ex(ctx, pseudo_packet, len, EVP_sha256(), NULL);
- maclen = htons(32);
- HMAC_Update(ctx, (char *)&maclen, 2);
- HMAC_Update(ctx, shabuf, sizeof(shabuf));
- } else
- ctx = NULL;
-
- q = build_question((char *)&wh->dh, wh->len, wh->dh.additional, (tsigkey == NULL) ? NULL : shabuf);
- if (q == NULL) {
- fprintf(stderr, "failed to build_question\n");
- return -1;
- }
-
- for (;;) {
- if (getsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &savetv, &sizetv) < 0) {
- perror("getsockopt");
- }
-
- tv.tv_sec = 1;
- tv.tv_usec = 0;
-
- if (setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
- perror("setsockopt");
- }
- len = recv(so, reply, 2, MSG_PEEK | MSG_WAITALL);
- if (len <= 0)
- break;
-
- plen = (u_int16_t *)reply;
- tcplen = ntohs(*plen) + 2;
-
- if (setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &savetv, sizeof(savetv)) < 0) {
- perror("setsockopt");
- }
-
- len = recv(so, reply, tcplen, MSG_WAITALL);
- if (len < 0) {
- perror("recv");
- return -1;
- }
- rwh = (struct whole_header *)&reply[0];
- bytes_received += ntohs(rwh->len);
-
- end = &reply[len];
- len = rwh->len;
-
- if (rwh->dh.id != wh->dh.id) {
- fprintf(stderr, "DNS ID mismatch\n");
- return -1;
- }
-
- if (!(htons(rwh->dh.query) & DNS_REPLY)) {
- fprintf(stderr, "NOT a DNS reply\n");
- return -1;
- }
-
- if (ntohs(rwh->dh.answer) < 1) {
- fprintf(stderr, "NO ANSWER provided\n");
- return -1;
- }
-
- segmentcount = ntohs(rwh->dh.answer);
- if (tsigkey) {
- segmentcount += ntohs(rwh->dh.additional);
- additionalcount += ntohs(rwh->dh.additional);
- rwh->dh.additional = 0;
- }
- answers += segmentcount;
-
-
- if (memcmp(q->hdr->name, name, q->hdr->namelen) != 0) {
- fprintf(stderr, "question name not for what we asked\n");
- return -1;
- }
-
- if (q->hdr->qclass != htons(DNS_CLASS_IN) || q->hdr->qtype != htons(DNS_TYPE_AXFR)) {
- fprintf(stderr, "wrong class or type\n");
- return -1;
- }
-
- p = (char *)&rwh[1];
- p += q->hdr->namelen;
- p += sizeof(u_int16_t); /* type */
- p += sizeof(u_int16_t); /* class */
- /* end of question */
-
- estart = (u_char *)&rwh->dh;
-
- if (tsigkey) {
- HMAC_Update(ctx, estart, (p - estart));
- }
-
- if (segment == 0 && (format & ZONE_FORMAT) && f != NULL)
- fprintf(f, "zone \"%s\" {\n", zonename);
-
- segment++;
-
- for (count = 0; count < segmentcount; count++) {
- char mac[32];
- elen = 0;
-
- if ((rrlen = raxfr_peek(f, p, estart, end, &rrtype, soacount, &rdlen, format, ctx)) < 0) {
- fprintf(stderr, "not a SOA reply, or ERROR\n");
- return -1;
- }
-
- if (tsigkey && (rrtype == DNS_TYPE_TSIG)) {
- uint16_t maclen;
-
- /* do tsig checks here */
- if ((len = raxfr_tsig(f,p,estart,end,mysoa,rdlen,ctx, (char *)&mac)) < 0) {
- fprintf(stderr, "error with TSIG record\n");
- return -1;
- }
-
- p = (estart + len);
-
- if ((len = mybase64_decode(tsigpass, (u_char *)&pseudo_packet, sizeof(pseudo_packet))) < 0) {
- fprintf(stderr, "bad base64 password\n");
- return -1;
- }
-
- HMAC_CTX_reset(ctx);
- HMAC_Init_ex(ctx, pseudo_packet, len, EVP_sha256(), NULL);
- maclen = htons(32);
- HMAC_Update(ctx, (char *)&maclen, 2);
- HMAC_Update(ctx, mac, 32);
-
- if (soacount > 1)
- goto out;
- } else
- p = (estart + rrlen);
-
- if (rrtype == DNS_TYPE_SOA) {
- if ((len = raxfr_soa(f, p, estart, end, mysoa, soacount, format, rdlen, ctx)) < 0) {
- fprintf(stderr, "raxfr_soa failed\n");
- return -1;
- }
- p = (estart + len);
- soacount++;
-
- /*
- * the envelopes are done because we have
- * two SOA's, continue here to catch the
- * TSIG.
- */
- if (soacount > 1)
- continue;
- } else {
- for (sr = supported; sr->rrtype != 0; sr++) {
- if (rrtype == sr->rrtype) {
- if ((len = (*sr->raxfr)(f, p, estart, end, mysoa, rdlen, ctx)) < 0) {
- fprintf(stderr, "error with rrtype %d\n", sr->rrtype);
- return -1;
- }
- p = (estart + len);
- break;
- }
- }
-
- if (sr->rrtype == 0) {
- if (rrtype != DNS_TYPE_TSIG) {
- fprintf(stderr, "unsupported RRTYPE %d\n", rrtype);
- return -1;
- }
- }
- }
- }
- }
-
- if ((len = recv(so, reply, 0xffff, 0)) > 0) {
- fprintf(stderr, ";; WARN: received %d more bytes.\n", len);
- }
-
-out:
-
- if (tsigkey) {
- HMAC_CTX_free(ctx);
- }
-
- if (f != NULL) {
- if ((format & ZONE_FORMAT))
- fprintf(f, "}\n");
- }
-
- free_question(q);
-
- return 0;
-
-}
-
-
int
lookup_name(FILE *f, int so, char *zonename, u_int16_t myrrtype, struct soa *mysoa, u_int32_t format, char *nameserver, u_int16_t port)
blob - 4c500818b15512785e3f3cd19c1237bac33898ed
blob + f746a2db0d5466a89c92130d70d36cd4a3e3690a
--- delphinusdnsd.c
+++ delphinusdnsd.c
@@ -27,7 +27,7 @@
*/
/*
- * $Id: delphinusdnsd.c,v 1.76 2019/11/01 19:52:40 pjp Exp $
+ * $Id: delphinusdnsd.c,v 1.77 2019/11/02 17:24:27 pjp Exp $
*/
@@ -97,7 +97,7 @@
extern void add_rrlimit(int, u_int16_t *, int, char *);
extern void axfrloop(int *, int, char **, ddDB *, struct imsgbuf *);
-extern void replicantloop(ddDB *, struct imsgbuf *);
+extern void replicantloop(ddDB *, struct imsgbuf *, struct imsgbuf *);
extern struct question *build_fake_question(char *, int, u_int16_t, char *, int);
extern int check_ent(char *, int);
extern int check_rrlimit(int, u_int16_t *, int, char *);
@@ -389,7 +389,14 @@ main(int argc, char *argv[], char *environ[])
if (! debug)
daemon(0,0);
else {
+ int status;
/*
+ * clean up any zombies left behind, this is only in debug mode
+ */
+
+ while (waitpid(-1, &status, WNOHANG) > 0);
+
+ /*
* even if in debug mode we want to have our own parent group
* for reasons in that regress needs it when killing debug
* mode delphinusdnsd
@@ -998,6 +1005,12 @@ main(int argc, char *argv[], char *environ[])
}
#if __OpenBSD__
+ if (unveil("/replicant", "rwc") < 0) {
+ perror("unveil");
+ slave_shutdown();
+ exit(1);
+ }
+
if (pledge("stdio inet proc id sendfd recvfd unveil cpath wpath rpath", NULL) < 0) {
perror("pledge");
slave_shutdown();
@@ -1023,7 +1036,7 @@ main(int argc, char *argv[], char *environ[])
close(cfg->my_imsg[MY_IMSG_RAXFR].imsg_fds[1]);
imsg_init(parent_ibuf[MY_IMSG_RAXFR], cfg->my_imsg[MY_IMSG_RAXFR].imsg_fds[0]);
- replicantloop(db, parent_ibuf[MY_IMSG_RAXFR]);
+ replicantloop(db, parent_ibuf[MY_IMSG_RAXFR], child_ibuf[MY_IMSG_MASTER]);
/* NOTREACHED */
exit(1);
@@ -1430,7 +1443,7 @@ get_ns(ddDB *db, struct rbtree *rbt, int *delegation)
len = rbt->zonelen;
while (*p && len > 0) {
- rbt0 = Lookup_zone(db, p, len, DNS_TYPE_NS, 0);
+ rbt0 = Lookup_zone(db, p, len, htons(DNS_TYPE_NS), 0);
if (rbt0 == NULL) {
p += (*p + 1);
len -= (*p + 1);
@@ -3620,7 +3633,7 @@ drop_privs(char *chrootpath, struct passwd *pw)
return -1;
}
- if (unveil("/", "r") < 0) {
+ if (unveil("/", "rwc") < 0) {
dolog(LOG_INFO, "unveil: %s\n", strerror(errno));
return -1;
}
blob - 820eae0ff9348dd08a83fd32148839f70ff89674
blob + 7356fc95dc198c622629a62baff2b7efb367d3df
--- examples/example9.conf
+++ examples/example9.conf
@@ -2,7 +2,7 @@ version "9";
options "cool stuff" {
versionstring "DELPHINUSDNSD - http://delphinusdns.centroid.eu";
interface "lo0";
- interface "em0";
+ interface "re0";
;bind 127.0.0.1;
;bind 192.168.34.4;
@@ -16,10 +16,12 @@ options "cool stuff" {
;dnssec;
}
-rzone "centroid.eu." {
- tsigkey "YmFzZTY0Cg==";
+tsig-auth pass "YmFzZTY0Cg==";
+
+rzone "internal.centroid.eu." {
+ tsigkey "pass";
masterport 10053;
- master 127.0.0.1;
- zonename "centroid.eu.";
- filename "/etc/delphinusdns/centroid.eu.repl";
+ master 192.168.177.2;
+ zonename "internal.centroid.eu.";
+ filename "/etc/delphinusdns/replicant/internal.centroid.eu.repl";
}
blob - 4e85ddd67b5ef3eba7b13ea8204d232363d4ac07
blob + c8e0e92ff7726227e78b3bb32695880653de6a03
--- raxfr.c
+++ raxfr.c
@@ -26,11 +26,14 @@
*
*/
/*
- * $Id: raxfr.c,v 1.16 2019/11/01 19:46:57 pjp Exp $
+ * $Id: raxfr.c,v 1.17 2019/11/02 17:24:27 pjp Exp $
*/
#include <sys/types.h>
+#include <sys/select.h>
#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -38,6 +41,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
@@ -58,7 +62,11 @@
#else /* not linux */
#include <sys/queue.h>
#include <sys/tree.h>
+#ifdef __FreeBSD__
+#include "imsg.h"
+#else
#include <imsg.h>
+#endif /* __FreeBSD__ */
#endif /* __linux__ */
#include <openssl/bn.h>
@@ -67,8 +75,24 @@
#include "ddd-dns.h"
#include "ddd-db.h"
+
+#define MY_SOCK_TIMEOUT -10
+
SLIST_HEAD(rzones ,rzone) rzones;
+LIST_HEAD(, myschedule) myschedules = LIST_HEAD_INITIALIZER(myschedules);
+struct myschedule {
+ char zonename[DNS_MAXNAME + 1];
+ time_t when;
+ int action;
+#define SCHEDULE_ACTION_REBOOT 0x1
+#define SCHEDULE_ACTION_REFRESH 0x2
+#define SCHEDULE_ACTION_RETRY 0x3
+ LIST_ENTRY(myschedule) myschedule_entry;
+} *sp0, *sp1, *spn;
+
+
+
int raxfr_a(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
int raxfr_aaaa(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
int raxfr_cname(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
@@ -90,9 +114,14 @@ u_int16_t raxfr_skip(FILE *, u_char *, u_char *);
int raxfr_soa(FILE *, u_char *, u_char *, u_char *, struct soa *, int, u_int32_t, u_int16_t, HMAC_CTX *);
int raxfr_peek(FILE *, u_char *, u_char *, u_char *, int *, int, u_int16_t *, u_int32_t, HMAC_CTX *);
int raxfr_tsig(FILE *f, u_char *p, u_char *estart, u_char *end, struct soa *mysoa, u_int16_t rdlen, HMAC_CTX *ctx, char *);
-void replicantloop(ddDB *, struct imsgbuf *);
+void replicantloop(ddDB *, struct imsgbuf *, struct imsgbuf *);
+static void schedule_refresh(char *, time_t);
+static void schedule_retry(char *, time_t);
+static void schedule_reboot(char *, time_t);
+static void schedule_delete(struct myschedule *);
+int64_t get_remote_soa(struct rzone *rzone);
+int do_raxfr(FILE *f, int64_t serial, struct rzone *rzone);
-
extern int memcasecmp(u_char *, u_char *, int);
extern char * dns_label(char *, int *);
extern char *get_dns_type(int, int);
@@ -104,7 +133,14 @@ extern char *base32hex_encode(u_char *, int);
extern u_int64_t timethuman(time_t);
extern char * expand_compression(u_char *, u_char *, u_char *, u_char *, int *, int);
extern void dolog(int, char *, ...);
+extern struct rbtree * Lookup_zone(ddDB *, char *, u_int16_t, u_int16_t, int);
+extern struct rbtree * find_rrset(ddDB *db, char *name, int len);
+extern struct rrset * find_rr(struct rbtree *rbt, u_int16_t rrtype);
+extern struct question *build_question(char *, int, int, char *);
+extern int lookup_axfr(FILE *, int, char *, struct soa *, u_int32_t, char *, char *);
+extern int find_tsig_key(char *, int, char *, int);
+
/* The following alias helps with bounds checking all input, needed! */
#define BOUNDS_CHECK(cur, begin, rdlen, end) do { \
@@ -115,6 +151,25 @@ extern void dolog(int, char *, ...);
return -1; \
} while (0)
+static struct raxfr_logic supported[] = {
+ { DNS_TYPE_A, 0, raxfr_a },
+ { DNS_TYPE_NS, 0, raxfr_ns },
+ { DNS_TYPE_MX, 0, raxfr_mx },
+ { DNS_TYPE_PTR, 0, raxfr_ptr },
+ { DNS_TYPE_AAAA, 0, raxfr_aaaa },
+ { DNS_TYPE_CNAME, 0, raxfr_cname },
+ { DNS_TYPE_TXT, 0, raxfr_txt },
+ { DNS_TYPE_DNSKEY, 1, raxfr_dnskey },
+ { DNS_TYPE_RRSIG, 1, raxfr_rrsig },
+ { DNS_TYPE_NSEC3PARAM, 1, raxfr_nsec3param },
+ { DNS_TYPE_NSEC3, 1, raxfr_nsec3 },
+ { DNS_TYPE_DS, 1, raxfr_ds },
+ { DNS_TYPE_SSHFP, 0, raxfr_sshfp },
+ { DNS_TYPE_TLSA, 0, raxfr_tlsa },
+ { DNS_TYPE_SRV, 0, raxfr_srv },
+ { DNS_TYPE_NAPTR, 0, raxfr_naptr },
+ { 0, 0, NULL }
+};
int
@@ -1192,13 +1247,23 @@ out:
void
-replicantloop(ddDB *db, struct imsgbuf *ibuf)
+replicantloop(ddDB *db, struct imsgbuf *ibuf, struct imsgbuf *master_ibuf)
{
- struct rzone *lrz;
- time_t scheduled_reboot = (time_t)(1572628314 + (31 * 24 * 3600));
- time_t now;
- int sleepint = 10;
+ struct rzone *lrz, *lrz0;
+ char buf[PATH_MAX];
+ char *p, *q;
+ time_t now, lastnow;
+ int apexlen, sel, endspurt = 0;
+ int idata;
+ int64_t serial;
+ char *apex;
+ struct rbtree *rbt;
+ struct rrset *rrset;
+ struct rr *rrp;
+ struct timeval tv;
+ FILE *f = NULL;
+
#if __OpenBSD__
if (pledge("stdio wpath rpath cpath inet", NULL) < 0) {
perror("pledge");
@@ -1206,20 +1271,692 @@ replicantloop(ddDB *db, struct imsgbuf *ibuf)
}
#endif
- SLIST_FOREACH(lrz, &rzones, rzone_entry) {
+ lastnow = time(NULL);
+
+ SLIST_FOREACH_SAFE(lrz, &rzones, rzone_entry, lrz0) {
if (lrz->zonename == NULL)
continue;
dolog(LOG_INFO, "adding SOA values to zone %s\n", lrz->zonename);
+ apex = dns_label(lrz->zonename, &apexlen);
+ if (apex == NULL) {
+ dolog(LOG_INFO, "dns_label failed\n");
+ continue;
+ }
+
+ rbt = find_rrset(db, apex, apexlen);
+ if (rbt == NULL) {
+ dolog(LOG_INFO, "%s has no apex, removing zone from replicant engine\n", lrz->zonename);
+ SLIST_REMOVE(&rzones, lrz, rzone, rzone_entry);
+ continue;
+ }
+
+ rrset = find_rr(rbt, DNS_TYPE_SOA);
+ if (rrset == NULL) {
+ dolog(LOG_INFO, "%s has no SOA, removing zone from replicant engine\n", lrz->zonename);
+ SLIST_REMOVE(&rzones, lrz, rzone, rzone_entry);
+ continue;
+ }
+ rrp = TAILQ_FIRST(&rrset->rr_head);
+ if (rrp == NULL) {
+ dolog(LOG_INFO, "SOA record corrupted for zone %s, removing zone from replicant engine\n", lrz->zonename);
+ SLIST_REMOVE(&rzones, lrz, rzone, rzone_entry);
+ continue;
+ }
+
+ lrz->soa.serial = ((struct soa *)rrp->rdata)->serial;
+ lrz->soa.refresh = ((struct soa *)rrp->rdata)->refresh;
+ lrz->soa.retry = ((struct soa *)rrp->rdata)->retry;
+ lrz->soa.expire = ((struct soa *)rrp->rdata)->expire;
+
+ dolog(LOG_INFO, "%s -> %u, %u, %u, %u\n", lrz->zonename,
+ lrz->soa.serial, lrz->soa.refresh, lrz->soa.retry,
+ lrz->soa.expire);
+
+ now = time(NULL);
+ schedule_refresh(lrz->zonename, now + lrz->soa.refresh);
+ free(rbt);
+ free(apex);
}
for (;;) {
+ if (endspurt) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 5000;
+ } else {
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ }
+
+ sel = select(0, NULL, NULL, NULL, &tv);
+ if (sel == -1) {
+ dolog(LOG_INFO, "select error: %s\n", strerror(errno));
+ continue;
+ }
+
now = time(NULL);
- if (now >= scheduled_reboot) {
- dolog(LOG_INFO, "pretending to send a scheduled reboot\n");
- }
+ /* some time safety */
+ if (now < lastnow) {
+ /* we had time go backwards, this is bad */
+ dolog(LOG_ERR, "time went backwards! rescheduling all schedules on refresh timeouts...\n");
- sleep (sleepint);
+ /* blow away all schedules and redo them */
+ while (!LIST_EMPTY(&myschedules)) {
+ sp0 = LIST_FIRST(&myschedules);
+ LIST_REMOVE(sp0, myschedule_entry);
+ free(sp0);
+ }
+
+ SLIST_FOREACH(lrz, &rzones, rzone_entry) {
+ if (lrz->zonename == NULL)
+ continue;
+ schedule_refresh(lrz->zonename, now + lrz->soa.refresh);
+ }
+
+ lastnow = now;
+ continue;
+ }
+
+ lastnow = now;
+
+ LIST_FOREACH_SAFE(sp0, &myschedules, myschedule_entry, sp1) {
+ if (sp0->when <= now) {
+ /* we hit a timeout on refresh */
+ if (sp0->action == SCHEDULE_ACTION_REFRESH) {
+ SLIST_FOREACH(lrz, &rzones, rzone_entry) {
+ if (lrz->zonename == NULL)
+ continue;
+
+ if (strcmp(sp0->zonename, lrz->zonename) == 0)
+ break;
+ }
+
+ if (lrz != NULL) {
+ dolog(LOG_DEBUG, "zone %s is being refreshed now\n", sp0->zonename);
+ /* must delete before adding any more */
+ schedule_delete(sp0);
+ if ((serial = get_remote_soa(lrz)) == MY_SOCK_TIMEOUT) {
+ /* we didn't get a reply and our socket timed out */
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ /* schedule a retry and go on */
+ } else if (serial > lrz->soa.serial) {
+ /* initiate AXFR and update zone */
+ dolog(LOG_INFO, "new higher serial detected (%ld vs. %ld)\n", serial, lrz->soa.serial);
+
+ p = strrchr(lrz->filename, '/');
+ if (p == NULL) {
+ dolog(LOG_INFO, "can't determine temporary filename from %s\n", lrz->filename);
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ goto out;
+ }
+
+ p++;
+ q = p;
+ if (*p == '\0') {
+ dolog(LOG_INFO, "can't determine temporary filename from %s (2)\n", lrz->filename);
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ goto out;
+ }
+
+ snprintf(buf, sizeof(buf), "%s.XXXXXXXXXXXXXX", p);
+ if ((p = mktemp(buf)) == NULL) {
+ dolog(LOG_INFO, "can't determine temporary filename from %s (3)\n", lrz->filename);
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ goto out;
+ }
+
+ f = fopen(p, "w");
+ if (f == NULL) {
+ dolog(LOG_INFO, "can't create temporary filename for zone %s\n", lrz->zonename);
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ goto out;
+ }
+
+ fprintf(f, "; This is a REPLICANT file for zone %s gotten on %lu\n\n", lrz->zonename, now);
+
+ if (do_raxfr(f, serial, lrz) < 0) {
+ dolog(LOG_INFO, "do_raxfr failed\n");
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ goto out;
+ }
+
+ fclose(f);
+
+ unlink(q);
+ if (link(p, q) < 0) {
+ dolog(LOG_ERR, "can't link %s to %s\n", p, q);
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ goto out;
+ }
+
+ unlink(p);
+
+ /* schedule reboot */
+ schedule_reboot(lrz->zonename, now + 100);
+ /*
+ * we've scheduled a reboot and there may be more
+ * AXFR's to do we only have a window of 100 seconds
+ * so we select for 5000 microseconds only, so that
+ * other tasks can still complete.
+ */
+ endspurt = 1;
+ }
+ }
+ } else if (sp0->action == SCHEDULE_ACTION_RETRY) {
+ /* we hit a timeout on retry */
+
+ SLIST_FOREACH(lrz, &rzones, rzone_entry) {
+ if (lrz->zonename == NULL)
+ continue;
+
+ if (strcmp(sp0->zonename, lrz->zonename) == 0)
+ break;
+ }
+
+ if (lrz != NULL) {
+ dolog(LOG_INFO, "zone %s is being retried now\n", sp0->zonename);
+ schedule_delete(sp0);
+ if ((serial = get_remote_soa(lrz)) == MY_SOCK_TIMEOUT) {
+ /* we didn't get a reply and our socket timed out */
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ /* schedule a retry and go on */
+ } else if (serial > lrz->soa.serial) {
+ /* initiate AXFR and update zone */
+
+ dolog(LOG_INFO, "new higher serial detected (%ld vs. %ld)\n", serial, lrz->soa.serial);
+
+ p = strrchr(lrz->filename, '/');
+ if (p == NULL) {
+ dolog(LOG_INFO, "can't determine temporary filename from %s\n", lrz->filename);
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ goto out;
+ }
+
+ p++;
+ q = p;
+ if (*p == '\0') {
+ dolog(LOG_INFO, "can't determine temporary filename from %s (2)\n", lrz->filename);
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ goto out;
+ }
+
+ snprintf(buf, sizeof(buf), "%s.XXXXXXXXXXXXXX", p);
+ if ((p = mktemp(buf)) == NULL) {
+ dolog(LOG_INFO, "can't determine temporary filename from %s (3)\n", lrz->filename);
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ goto out;
+ }
+
+ f = fopen(p, "w");
+ if (f == NULL) {
+ dolog(LOG_INFO, "can't create temporary filename for zone %s\n", lrz->zonename);
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ goto out;
+ }
+
+ fprintf(f, "; This is a REPLICANT file for zone %s gotten on %lu\n\n", lrz->zonename, now);
+
+ if (do_raxfr(f, serial, lrz) < 0) {
+ dolog(LOG_INFO, "do_raxfr failed\n");
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ goto out;
+ }
+
+ fclose(f);
+
+ unlink(q);
+ if (link(p, q) < 0) {
+ dolog(LOG_ERR, "can't link %s to %s\n", p, q);
+ schedule_retry(lrz->zonename, now + lrz->soa.retry);
+ goto out;
+ }
+
+ unlink(p);
+ /* schedule reboot */
+ schedule_reboot(lrz->zonename, now + 100);
+ /*
+ * we've scheduled a reboot and there may be more
+ * AXFR's to do we only have a window of 100 seconds
+ * so we select for 5000 microseconds only, so that
+ * other tasks can still complete.
+ */
+
+ endspurt = 1;
+ }
+ }
+
+ } else if (sp0->action == SCHEDULE_ACTION_REBOOT) {
+ /* we hit a scheduling on rebooting, nothing can save you now! */
+ dolog(LOG_INFO, "I'm supposed to reboot now, REBOOT\n");
+
+ idata = 1;
+ imsg_compose(master_ibuf, IMSG_RELOAD_MESSAGE,
+ 0, 0, -1, &idata, sizeof(idata));
+ msgbuf_write(&master_ibuf->w);
+ exit(0);
+ }
+
+ } /* when below now */
+out:
+ continue;
+ } /* LIST_FOREACH schedules */
+ } /* for (;;) */
+
+ /* NOTREACHED */
+}
+
+static void
+schedule_refresh(char *zonename, time_t seconds)
+{
+ sp0 = calloc(1, sizeof(struct myschedule));
+ if (sp0 == NULL)
+ return;
+
+ strlcpy(sp0->zonename, zonename, sizeof(sp0->zonename));
+ sp0->when = seconds;
+ sp0->action = SCHEDULE_ACTION_REFRESH;
+
+ LIST_INSERT_HEAD(&myschedules, sp0, myschedule_entry);
+}
+
+static void
+schedule_retry(char *zonename, time_t seconds)
+{
+ sp0 = calloc(1, sizeof(struct myschedule));
+ if (sp0 == NULL)
+ return;
+
+ strlcpy(sp0->zonename, zonename, sizeof(sp0->zonename));
+ sp0->when = seconds;
+ sp0->action = SCHEDULE_ACTION_RETRY;
+
+ LIST_INSERT_HEAD(&myschedules, sp0, myschedule_entry);
+
+}
+
+static void
+schedule_reboot(char *zonename, time_t seconds)
+{
+ sp0 = calloc(1, sizeof(struct myschedule));
+ if (sp0 == NULL)
+ return;
+
+ strlcpy(sp0->zonename, zonename, sizeof(sp0->zonename));
+ sp0->when = seconds;
+ sp0->action = SCHEDULE_ACTION_REBOOT;
+
+ LIST_INSERT_HEAD(&myschedules, sp0, myschedule_entry);
+
+ dolog(LOG_INFO, "scheduling reboot at %lu\n", seconds);
+}
+
+static void
+schedule_delete(struct myschedule *sched)
+{
+ LIST_REMOVE(sched, myschedule_entry);
+ free(sched);
+}
+
+/*
+ * get the remote serial from the SOA, via TCP
+ */
+
+int64_t
+get_remote_soa(struct rzone *rzone)
+{
+ int so;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr *sa;
+ struct soa mysoa;
+ socklen_t slen = sizeof(struct sockaddr_in);
+
+ int len, i, answers;
+ int numansw, numaddi, numauth;
+ int rrtype, soacount = 0;
+ u_int16_t rdlen;
+ char query[512];
+ char *reply;
+ struct raxfr_logic *sr;
+ struct question *q;
+ struct dns_optrr *optrr;
+ struct whole_header {
+ struct dns_header dh;
+ } *wh, *rwh;
+
+ u_char *p, *name;
+
+ u_char *end, *estart;
+ int totallen, zonelen, rrlen;
+ int replysize = 0;
+ u_int16_t *class, *type, *tcpsize;
+ u_int16_t *plen;
+ u_int16_t tcplen;
+
+ FILE *f = NULL;
+ int format = 0;
+
+
+ if ((so = socket(rzone->storage.ss_family, SOCK_STREAM, 0)) < 0) {
+ dolog(LOG_INFO, "get_remote_soa: %s\n", strerror(errno));
+ return MY_SOCK_TIMEOUT;
}
+
+ if (rzone->storage.ss_family == AF_INET6) {
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(rzone->masterport);
+ memcpy(&sin6.sin6_addr, (void *)&((struct sockaddr_in6 *)(&rzone->storage))->sin6_addr, sizeof(struct in6_addr));
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sa = (struct sockaddr *)&sin6;
+ slen = sizeof(struct sockaddr_in6);
+ } else {
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(rzone->masterport);
+ sin.sin_addr.s_addr = ((struct sockaddr_in *)(&rzone->storage))->sin_addr.s_addr;
+ sa = (struct sockaddr *)&sin;
+ }
+
+ if (connect(so, sa, slen) < 0) {
+ dolog(LOG_INFO, "connect to master %s port %u: %s\n", rzone->master, rzone->masterport, strerror(errno));
+ close(so);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+
+
+ replysize = 0xffff;
+ memset(&query, 0, sizeof(query));
+
+ tcpsize = (u_int16_t *)&query[0];
+ wh = (struct whole_header *)&query[2];
+
+ wh->dh.id = htons(arc4random() & 0xffff);
+ wh->dh.query = 0;
+ wh->dh.question = htons(1);
+ wh->dh.answer = 0;
+ wh->dh.nsrr = 0;
+ wh->dh.additional = htons(1);;
+
+ SET_DNS_QUERY(&wh->dh);
+ SET_DNS_RECURSION(&wh->dh);
+
+
+ HTONS(wh->dh.query);
+
+ totallen = sizeof(struct whole_header) + 2;
+
+ name = dns_label(rzone->zonename, &len);
+ if (name == NULL) {
+ close(so);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+ zonelen = len;
+
+ p = (char *)&wh[1];
+
+ memcpy(p, name, len);
+ totallen += len;
+
+ type = (u_int16_t *)&query[totallen];
+ *type = htons(DNS_TYPE_SOA);
+ totallen += sizeof(u_int16_t);
+
+ class = (u_int16_t *)&query[totallen];
+ *class = htons(DNS_CLASS_IN);
+ totallen += sizeof(u_int16_t);
+
+ /* attach EDNS0 */
+
+ optrr = (struct dns_optrr *)&query[totallen];
+
+ optrr->name[0] = 0;
+ optrr->type = htons(DNS_TYPE_OPT);
+ optrr->class = htons(replysize);
+ optrr->ttl = htonl(0); /* EDNS version 0 */
+#if 0
+ if ((format & DNSSEC_FORMAT))
+ SET_DNS_ERCODE_DNSSECOK(optrr);
+#endif
+ HTONL(optrr->ttl);
+ optrr->rdlen = 0;
+ optrr->rdata[0] = 0;
+
+ totallen += (sizeof(struct dns_optrr));
+ *tcpsize = htons(totallen - 2);
+
+ if (send(so, query, totallen, 0) < 0) {
+ close(so);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+ /* catch reply */
+
+ reply = calloc(1, replysize + 2);
+ if (reply == NULL) {
+ dolog(LOG_INFO, "calloc: %s\n", strerror(errno));
+ close(so);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+ if ((len = recv(so, reply, 2, MSG_PEEK | MSG_WAITALL)) < 0) {
+ dolog(LOG_INFO, "recv: %s\n", strerror(errno));
+ close(so);
+ free(reply);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+ plen = (u_int16_t *)reply;
+ tcplen = ntohs(*plen);
+
+ if ((len = recv(so, reply, tcplen + 2, MSG_WAITALL)) < 0) {
+ dolog(LOG_INFO, "recv: %s\n", strerror(errno));
+ close(so);
+ free(reply);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+ rwh = (struct whole_header *)&reply[2];
+
+ end = &reply[len];
+
+ if (rwh->dh.id != wh->dh.id) {
+ dolog(LOG_INFO, "DNS ID mismatch\n");
+ close(so);
+ free(reply);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+ if (!(htons(rwh->dh.query) & DNS_REPLY)) {
+ dolog(LOG_INFO, "NOT a DNS reply\n");
+ close(so);
+ free(reply);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+ numansw = ntohs(rwh->dh.answer);
+ numauth = ntohs(rwh->dh.nsrr);
+ numaddi = ntohs(rwh->dh.additional);
+ answers = numansw + numauth + numaddi;
+
+ if (answers < 1) {
+ dolog(LOG_INFO, "NO ANSWER provided\n");
+ close(so);
+ free(reply);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+ q = build_question((char *)&wh->dh, len, wh->dh.additional, NULL);
+ if (q == NULL) {
+ dolog(LOG_INFO, "failed to build_question\n");
+ close(so);
+ free(reply);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+ if (memcmp(q->hdr->name, name, q->hdr->namelen) != 0) {
+ dolog(LOG_INFO, "question name not for what we asked\n");
+ close(so);
+ free(reply);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+ if (q->hdr->qclass != *class || q->hdr->qtype != *type) {
+ dolog(LOG_INFO, "wrong class or type\n");
+ close(so);
+ free(reply);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+ p = (u_char *)&rwh[1];
+
+ p += q->hdr->namelen;
+ p += sizeof(u_int16_t); /* type */
+ p += sizeof(u_int16_t); /* class */
+
+ /* end of question */
+
+
+ estart = (u_char *)&rwh->dh;
+
+ for (i = answers; i > 0; i--) {
+ if ((rrlen = raxfr_peek(f, p, estart, end, &rrtype, 0, &rdlen, format, NULL)) < 0) {
+ dolog(LOG_INFO, "not a SOA reply, or ERROR\n");
+ close(so);
+ free(reply);
+ return(MY_SOCK_TIMEOUT);
+ }
+
+ p = (estart + rrlen);
+
+ if (rrtype == DNS_TYPE_SOA) {
+ if ((len = raxfr_soa(f, p, estart, end, &mysoa, soacount, format, rdlen, NULL)) < 0) {
+ dolog(LOG_INFO, "raxfr_soa failed\n");
+ close(so);
+ free(reply);
+ return(MY_SOCK_TIMEOUT);
+ }
+ p = (estart + len);
+ soacount++;
+ } else {
+ for (sr = supported; sr->rrtype != 0; sr++) {
+ if (rrtype == sr->rrtype) {
+ if ((len = (*sr->raxfr)(f, p, estart, end, &mysoa, rdlen, NULL)) < 0) {
+ dolog(LOG_INFO, "error with rrtype %d\n", sr->rrtype);
+ close(so);
+ free(reply);
+ return(MY_SOCK_TIMEOUT);
+ }
+ p = (estart + len);
+ break;
+ }
+ }
+
+ if (sr->rrtype == 0) {
+ if (rrtype != 41) {
+ dolog(LOG_INFO, "unsupported RRTYPE %u\n", rrtype);
+ close(so);
+ free(reply);
+ return(MY_SOCK_TIMEOUT);
+ }
+ }
+ } /* rrtype == DNS_TYPE_SOA */
+
+
+ } /* for () */
+
+ free(reply);
+
+ close(so);
+ return ((int64_t)ntohl(mysoa.serial));
+}
+
+int
+do_raxfr(FILE *f, int64_t serial, struct rzone *rzone)
+{
+ int so;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr *sa;
+ socklen_t slen = sizeof(struct sockaddr_in);
+
+ int window = 32768;
+ char tsigpass[512];
+ char humanpass[1024];
+ char *keyname;
+ int tsigpasslen, keynamelen;
+ int format = (TCP_FORMAT | ZONE_FORMAT);
+ int len;
+
+ struct soa mysoa;
+
+
+ if ((so = socket(rzone->storage.ss_family, SOCK_STREAM, 0)) < 0) {
+ dolog(LOG_INFO, "get_remote_soa: %s\n", strerror(errno));
+ return -1;
+ }
+
+#ifndef __linux__
+ /* biggen the window */
+
+ while (setsockopt(so, SOL_SOCKET, SO_RCVBUF, &window, sizeof(window)) != -1)
+ window <<= 1;
+#endif
+
+ if (rzone->storage.ss_family == AF_INET6) {
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(rzone->masterport);
+ memcpy(&sin6.sin6_addr, (void *)&((struct sockaddr_in6 *)(&rzone->storage))->sin6_addr, sizeof(struct in6_addr));
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sa = (struct sockaddr *)&sin6;
+ slen = sizeof(struct sockaddr_in6);
+ } else {
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(rzone->masterport);
+ sin.sin_addr.s_addr = ((struct sockaddr_in *)(&rzone->storage))->sin_addr.s_addr;
+ sa = (struct sockaddr *)&sin;
+ }
+
+ if (connect(so, sa, slen) < 0) {
+ dolog(LOG_INFO, "connect to master %s port %u: %s\n", rzone->master, rzone->masterport, strerror(errno));
+ close(so);
+ return -1;
+ }
+
+ keyname = dns_label(rzone->tsigkey, &keynamelen);
+ if (keyname == NULL) {
+ dolog(LOG_ERR, "dns_label failed\n");
+ close(so);
+ return -1;
+ }
+
+ if ((tsigpasslen = find_tsig_key(keyname, keynamelen, (char *)&tsigpass, sizeof(tsigpass))) < 0) {
+ dolog(LOG_ERR, "do not have a record of TSIG key %s\n", rzone->tsigkey);
+ close(so);
+ return -1;
+ }
+
+ free(keyname);
+
+ if ((len = mybase64_encode(tsigpass, tsigpasslen, humanpass, sizeof(humanpass))) < 0) {
+ dolog(LOG_ERR, "base64_encode() failed\n");
+ close(so);
+ return -1;
+ }
+
+ humanpass[len] = '\0';
+
+ if (lookup_axfr(f, so, rzone->zonename, &mysoa, format, rzone->tsigkey, humanpass) < 0) {
+ dolog(LOG_ERR, "lookup_axfr() failed\n");
+ close(so);
+ return -1;
+ }
+
+ close(so);
+ return (0);
}
blob - 5b9d7e89e558ef0d651bde547f03c23ff8d35b3a
blob + 9beba068cc6785e9935c2be307e4287f2e97a5c5
--- util.c
+++ util.c
@@ -27,7 +27,7 @@
*/
/*
- * $Id: util.c,v 1.42 2019/11/01 19:46:57 pjp Exp $
+ * $Id: util.c,v 1.43 2019/11/02 17:24:27 pjp Exp $
*/
#include <sys/types.h>
@@ -103,7 +103,13 @@ int tsig_pseudoheader(char *, uint16_t, time_t, HMAC_C
char * bin2hex(char *, int);
u_int64_t timethuman(time_t);
char * bitmap2human(char *, int);
+int lookup_axfr(FILE *, int, char *, struct soa *, u_int32_t, char *, char *);
+static int segment;
+static int bytes_received, answers;
+static int additionalcount = 0;
+
+
/* externs */
extern int debug;
@@ -119,8 +125,30 @@ extern int add_rr(struct rbtree *rbt, char *name, int
extern int display_rr(struct rrset *rrset);
extern int check_ent(char *, int);
extern int find_tsig_key(char *, int, char *, int);
+extern int mybase64_decode(char const *, u_char *, size_t);
+extern int raxfr_a(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_tlsa(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_srv(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_naptr(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_aaaa(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_cname(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_ns(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_ptr(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_mx(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_txt(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_dnskey(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_rrsig(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_nsec3param(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_nsec3(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_ds(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern int raxfr_sshfp(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *);
+extern u_int16_t raxfr_skip(FILE *, u_char *, u_char *);
+extern int raxfr_soa(FILE *, u_char *, u_char *, u_char *, struct soa *, int, u_int32_t, u_int16_t, HMAC_CTX *);
+extern int raxfr_peek(FILE *, u_char *, u_char *, u_char *, int *, int, u_int16_t *, u_int32_t, HMAC_CTX *);
+extern int raxfr_tsig(FILE *, u_char *, u_char *, u_char *, struct soa *, u_int16_t, HMAC_CTX *, char *);
+
/* internals */
struct typetable {
char *type;
@@ -171,6 +199,28 @@ static struct rrtab myrrtab[] = {
{ "txt", DNS_TYPE_TXT, DNS_TYPE_TXT },
};
+
+
+static struct raxfr_logic supported[] = {
+ { DNS_TYPE_A, 0, raxfr_a },
+ { DNS_TYPE_NS, 0, raxfr_ns },
+ { DNS_TYPE_MX, 0, raxfr_mx },
+ { DNS_TYPE_PTR, 0, raxfr_ptr },
+ { DNS_TYPE_AAAA, 0, raxfr_aaaa },
+ { DNS_TYPE_CNAME, 0, raxfr_cname },
+ { DNS_TYPE_TXT, 0, raxfr_txt },
+ { DNS_TYPE_DNSKEY, 1, raxfr_dnskey },
+ { DNS_TYPE_RRSIG, 1, raxfr_rrsig },
+ { DNS_TYPE_NSEC3PARAM, 1, raxfr_nsec3param },
+ { DNS_TYPE_NSEC3, 1, raxfr_nsec3 },
+ { DNS_TYPE_DS, 1, raxfr_ds },
+ { DNS_TYPE_SSHFP, 0, raxfr_sshfp },
+ { DNS_TYPE_TLSA, 0, raxfr_tlsa },
+ { DNS_TYPE_SRV, 0, raxfr_srv },
+ { DNS_TYPE_NAPTR, 0, raxfr_naptr },
+ { 0, 0, NULL }
+};
+
/*
* LABEL_COUNT - count the labels and return that number
*/
@@ -365,7 +415,7 @@ lookup_zone(ddDB *db, struct question *question, int *
}
/*
- * Lookup_zone: wrapper for lookup_zone() et al.
+ * Lookup_zone: wrapper for lookup_zone() et al. type must be htons()'ed!
*/
struct rbtree *
@@ -1592,3 +1642,401 @@ bitmap2human(char *bitmap, int len)
return ((char *)&human);
}
+
+
+int
+lookup_axfr(FILE *f, int so, char *zonename, struct soa *mysoa, u_int32_t format, char *tsigkey, char *tsigpass)
+{
+ char query[512];
+ char pseudo_packet[512];
+ char shabuf[32];
+ char *reply;
+ struct timeval tv, savetv;
+ struct question *q;
+ struct whole_header {
+ u_int16_t len;
+ struct dns_header dh;
+ } *wh, *rwh;
+ struct raxfr_logic *sr;
+
+ u_char *p, *name, *keyname;
+
+ u_char *end, *estart;
+ int len, totallen, zonelen, rrlen, rrtype;
+ int soacount = 0;
+ int elen = 0;
+ int segmentcount = 0;
+ int count = 0;
+ u_int32_t *ttl;
+ u_int16_t *class, *type, rdlen, *plen;
+ u_int16_t tcplen;
+
+ HMAC_CTX *ctx;
+ time_t now = 0;
+ socklen_t sizetv;
+
+ if (!(format & TCP_FORMAT))
+ return -1;
+
+ memset(&query, 0, sizeof(query));
+
+ wh = (struct whole_header *)&query[0];
+
+ wh->dh.id = htons(arc4random() & 0xffff);
+ wh->dh.query = 0;
+ wh->dh.question = htons(1);
+ wh->dh.answer = 0;
+ wh->dh.nsrr = 0;
+ wh->dh.additional = htons(0);
+
+
+ SET_DNS_QUERY(&wh->dh);
+ SET_DNS_RECURSION(&wh->dh);
+ HTONS(wh->dh.query);
+
+ totallen = sizeof(struct whole_header);
+
+ name = dns_label(zonename, &len);
+ if (name == NULL) {
+ return -1;
+ }
+
+ zonelen = len;
+
+ p = (char *)&wh[1];
+
+ memcpy(p, name, len);
+ totallen += len;
+
+ type = (u_int16_t *)&query[totallen];
+ *type = htons(DNS_TYPE_AXFR);
+ totallen += sizeof(u_int16_t);
+
+ class = (u_int16_t *)&query[totallen];
+ *class = htons(DNS_CLASS_IN);
+ totallen += sizeof(u_int16_t);
+
+ /* we have a key, attach a TSIG payload */
+ if (tsigkey) {
+
+ if ((len = mybase64_decode(tsigpass, (u_char *)&pseudo_packet, sizeof(pseudo_packet))) < 0) {
+ fprintf(stderr, "bad base64 password\n");
+ return -1;
+ }
+
+ ctx = HMAC_CTX_new();
+ HMAC_Init_ex(ctx, pseudo_packet, len, EVP_sha256(), NULL);
+ HMAC_Update(ctx, &query[2], totallen - 2);
+
+ now = time(NULL);
+ if (tsig_pseudoheader(tsigkey, 300, now, ctx) < 0) {
+ fprintf(stderr, "tsig_pseudoheader failed\n");
+ return -1;
+ }
+
+ HMAC_Final(ctx, shabuf, &len);
+
+ if (len != 32) {
+ fprintf(stderr, "not expected len != 32\n");
+ return -1;
+ }
+
+#if defined __linux__ || defined __FreeBSD__
+ HMAC_CTX_free(ctx);
+#else
+ HMAC_cleanup(ctx);
+#endif
+
+ keyname = dns_label(tsigkey, &len);
+ if (keyname == NULL) {
+ return -1;
+ }
+
+ memcpy(&query[totallen], keyname, len);
+ totallen += len;
+
+ type = (u_int16_t *)&query[totallen];
+ *type = htons(DNS_TYPE_TSIG);
+ totallen += 2;
+
+ class = (u_int16_t *)&query[totallen];
+ *class = htons(DNS_CLASS_ANY);
+ totallen += 2;
+
+ ttl = (u_int32_t *)&query[totallen];
+ *ttl = htonl(0);
+ totallen += 4;
+
+ keyname = dns_label("hmac-sha256", &len);
+ if (keyname == NULL) {
+ return -1;
+ }
+
+ /* rdlen */
+ type = (u_int16_t *)&query[totallen];
+ *type = htons(len + 2 + 4 + 2 + 2 + 32 + 2 + 2 + 2);
+ totallen += 2;
+
+ /* algorithm name */
+ memcpy(&query[totallen], keyname, len);
+ totallen += len;
+
+ /* time 1 */
+ type = (u_int16_t *)&query[totallen];
+ *type = htons((now >> 32) & 0xffff);
+ totallen += 2;
+
+ /* time 2 */
+ ttl = (u_int32_t *)&query[totallen];
+ *ttl = htonl((now & 0xffffffff));
+ totallen += 4;
+
+ /* fudge */
+ type = (u_int16_t *)&query[totallen];
+ *type = htons(300);
+ totallen += 2;
+
+ /* hmac size */
+ type = (u_int16_t *)&query[totallen];
+ *type = htons(sizeof(shabuf));
+ totallen += 2;
+
+ /* hmac */
+ memcpy(&query[totallen], shabuf, sizeof(shabuf));
+ totallen += sizeof(shabuf);
+
+ /* original id */
+ type = (u_int16_t *)&query[totallen];
+ *type = wh->dh.id;
+ totallen += 2;
+
+ /* error */
+ type = (u_int16_t *)&query[totallen];
+ *type = 0;
+ totallen += 2;
+
+ /* other len */
+ type = (u_int16_t *)&query[totallen];
+ *type = 0;
+ totallen += 2;
+
+ wh->dh.additional = htons(1);
+ }
+
+
+ wh->len = htons(totallen - 2);
+
+ if (send(so, query, totallen, 0) < 0) {
+ perror("send");
+ return -1;
+ }
+
+ /* catch reply */
+
+ reply = calloc(1, 0xffff + 2);
+ if (reply == NULL) {
+ perror("calloc");
+ return -1;
+ }
+
+ if (tsigkey) {
+ uint16_t maclen;
+
+ if ((len = mybase64_decode(tsigpass, (u_char *)&pseudo_packet, sizeof(pseudo_packet))) < 0) {
+ fprintf(stderr, "bad base64 password\n");
+ return -1;
+ }
+
+ ctx = HMAC_CTX_new();
+ HMAC_Init_ex(ctx, pseudo_packet, len, EVP_sha256(), NULL);
+ maclen = htons(32);
+ HMAC_Update(ctx, (char *)&maclen, 2);
+ HMAC_Update(ctx, shabuf, sizeof(shabuf));
+ } else
+ ctx = NULL;
+
+ q = build_question((char *)&wh->dh, wh->len, wh->dh.additional, (tsigkey == NULL) ? NULL : shabuf);
+ if (q == NULL) {
+ fprintf(stderr, "failed to build_question\n");
+ return -1;
+ }
+
+ for (;;) {
+ if (getsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &savetv, &sizetv) < 0) {
+ perror("getsockopt");
+ }
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ if (setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+ perror("setsockopt");
+ }
+ len = recv(so, reply, 2, MSG_PEEK | MSG_WAITALL);
+ if (len <= 0)
+ break;
+
+ plen = (u_int16_t *)reply;
+ tcplen = ntohs(*plen) + 2;
+
+ if (setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &savetv, sizeof(savetv)) < 0) {
+ perror("setsockopt");
+ }
+
+ len = recv(so, reply, tcplen, MSG_WAITALL);
+ if (len < 0) {
+ perror("recv");
+ return -1;
+ }
+ rwh = (struct whole_header *)&reply[0];
+ bytes_received += ntohs(rwh->len);
+
+ end = &reply[len];
+ len = rwh->len;
+
+ if (rwh->dh.id != wh->dh.id) {
+ fprintf(stderr, "DNS ID mismatch\n");
+ return -1;
+ }
+
+ if (!(htons(rwh->dh.query) & DNS_REPLY)) {
+ fprintf(stderr, "NOT a DNS reply\n");
+ return -1;
+ }
+
+ if (ntohs(rwh->dh.answer) < 1) {
+ fprintf(stderr, "NO ANSWER provided\n");
+ return -1;
+ }
+
+ segmentcount = ntohs(rwh->dh.answer);
+ if (tsigkey) {
+ segmentcount += ntohs(rwh->dh.additional);
+ additionalcount += ntohs(rwh->dh.additional);
+ rwh->dh.additional = 0;
+ }
+ answers += segmentcount;
+
+
+ if (memcmp(q->hdr->name, name, q->hdr->namelen) != 0) {
+ fprintf(stderr, "question name not for what we asked\n");
+ return -1;
+ }
+
+ if (q->hdr->qclass != htons(DNS_CLASS_IN) || q->hdr->qtype != htons(DNS_TYPE_AXFR)) {
+ fprintf(stderr, "wrong class or type\n");
+ return -1;
+ }
+
+ p = (char *)&rwh[1];
+ p += q->hdr->namelen;
+ p += sizeof(u_int16_t); /* type */
+ p += sizeof(u_int16_t); /* class */
+ /* end of question */
+
+ estart = (u_char *)&rwh->dh;
+
+ if (tsigkey) {
+ HMAC_Update(ctx, estart, (p - estart));
+ }
+
+ if (segment == 0 && (format & ZONE_FORMAT) && f != NULL)
+ fprintf(f, "zone \"%s\" {\n", zonename);
+
+ segment++;
+
+ for (count = 0; count < segmentcount; count++) {
+ char mac[32];
+ elen = 0;
+
+ if ((rrlen = raxfr_peek(f, p, estart, end, &rrtype, soacount, &rdlen, format, ctx)) < 0) {
+ fprintf(stderr, "not a SOA reply, or ERROR\n");
+ return -1;
+ }
+
+ if (tsigkey && (rrtype == DNS_TYPE_TSIG)) {
+ uint16_t maclen;
+
+ /* do tsig checks here */
+ if ((len = raxfr_tsig(f,p,estart,end,mysoa,rdlen,ctx, (char *)&mac)) < 0) {
+ fprintf(stderr, "error with TSIG record\n");
+ return -1;
+ }
+
+ p = (estart + len);
+
+ if ((len = mybase64_decode(tsigpass, (u_char *)&pseudo_packet, sizeof(pseudo_packet))) < 0) {
+ fprintf(stderr, "bad base64 password\n");
+ return -1;
+ }
+
+ HMAC_CTX_reset(ctx);
+ HMAC_Init_ex(ctx, pseudo_packet, len, EVP_sha256(), NULL);
+ maclen = htons(32);
+ HMAC_Update(ctx, (char *)&maclen, 2);
+ HMAC_Update(ctx, mac, 32);
+
+ if (soacount > 1)
+ goto out;
+ } else
+ p = (estart + rrlen);
+
+ if (rrtype == DNS_TYPE_SOA) {
+ if ((len = raxfr_soa(f, p, estart, end, mysoa, soacount, format, rdlen, ctx)) < 0) {
+ fprintf(stderr, "raxfr_soa failed\n");
+ return -1;
+ }
+ p = (estart + len);
+ soacount++;
+
+ /*
+ * the envelopes are done because we have
+ * two SOA's, continue here to catch the
+ * TSIG.
+ */
+ if (soacount > 1)
+ continue;
+ } else {
+ for (sr = supported; sr->rrtype != 0; sr++) {
+ if (rrtype == sr->rrtype) {
+ if ((len = (*sr->raxfr)(f, p, estart, end, mysoa, rdlen, ctx)) < 0) {
+ fprintf(stderr, "error with rrtype %d\n", sr->rrtype);
+ return -1;
+ }
+ p = (estart + len);
+ break;
+ }
+ }
+
+ if (sr->rrtype == 0) {
+ if (rrtype != DNS_TYPE_TSIG) {
+ fprintf(stderr, "unsupported RRTYPE %d\n", rrtype);
+ return -1;
+ }
+ }
+ }
+ }
+ }
+
+ if ((len = recv(so, reply, 0xffff, 0)) > 0) {
+ fprintf(stderr, ";; WARN: received %d more bytes.\n", len);
+ }
+
+out:
+
+ if (tsigkey) {
+ HMAC_CTX_free(ctx);
+ }
+
+ if (f != NULL) {
+ if ((format & ZONE_FORMAT))
+ fprintf(f, "}\n");
+ }
+
+ free_question(q);
+
+ return 0;
+
+}
+
+
repomaster@centroid.eu