-# $OpenBSD: Makefile,v 1.7 2019/12/13 14:38:25 otto Exp $
+# $OpenBSD: Makefile,v 1.8 2021/01/24 18:29:15 florian Exp $
PROG= unwind
SRCS= control.c resolver.c frontend.c log.c unwind.c parse.y printconf.c
+SRCS+= dns64_synth.c
MAN= unwind.8 unwind.conf.5
.include "${.CURDIR}/libunbound/Makefile.inc"
--- /dev/null
+/* $OpenBSD: dns64_synth.c,v 1.1 2021/01/24 18:29:15 florian Exp $ */
+
+/*
+ * dns64/dns64.h - DNS64 module
+ *
+ * Copyright (c) 2009, Viagénie. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2021 Florian Obser <florian@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <net/route.h>
+
+#include <string.h>
+
+#include "libunbound/config.h"
+#include "libunbound/util/regional.h"
+#include "libunbound/util/data/msgencode.h"
+#include "libunbound/util/data/msgparse.h"
+#include "libunbound/util/data/msgreply.h"
+
+#include "log.h"
+#include "unwind.h"
+#include "frontend.h"
+#include "dns64_synth.h"
+
+extern struct dns64_prefix *dns64_prefixes;
+extern int dns64_prefix_count;
+
+static void
+synthesize_aaaa(const struct in6_addr *in6, int prefixlen, const uint8_t *a,
+ size_t a_len, uint8_t *aaaa, size_t aaaa_len)
+{
+ size_t i, pos;
+ memcpy(aaaa, in6->s6_addr, sizeof(in6->s6_addr));
+ for (i = 0, pos = prefixlen / 8; i < a_len && pos < aaaa_len;
+ i++, pos++) {
+ if (pos == 8)
+ aaaa[pos++] = 0;
+ aaaa[pos] = a[i];
+ }
+}
+
+/*
+ * Copied from unbound/dns64/dns64.c and slightly extended to work with more
+ * than one IPv6 prefix.
+ */
+
+void
+dns64_synth_aaaa_data(const struct ub_packed_rrset_key* fk, const struct
+ packed_rrset_data* fd, struct ub_packed_rrset_key *dk, struct
+ packed_rrset_data **dd_out, struct regional *region)
+{
+ struct packed_rrset_data *dd;
+ size_t i, pos;
+ int j;
+
+ /*
+ * Create synthesized AAAA RR set data. We need to allocated extra
+ * memory for the RRs themselves. Each RR has a length, TTL, pointer to
+ * wireformat data, 2 bytes of data length, and 16 bytes of IPv6
+ * address.
+ */
+ if(fd->count > RR_COUNT_MAX) {
+ *dd_out = NULL;
+ return; /* integer overflow protection in alloc */
+ }
+ if (!(dd = *dd_out = regional_alloc(region, sizeof(struct
+ packed_rrset_data) + fd->count * dns64_prefix_count *
+ (sizeof(size_t) + sizeof(time_t) + sizeof(uint8_t*) + 2 + 16)))) {
+ log_warnx("out of memory");
+ return;
+ }
+
+ /* Copy attributes from A RR set. */
+ dd->ttl = fd->ttl;
+ dd->count = fd->count * dns64_prefix_count;
+ dd->rrsig_count = 0;
+ dd->trust = fd->trust;
+ dd->security = fd->security;
+
+ /* Synthesize AAAA records. Adjust pointers in structure. */
+ dd->rr_len = (size_t*)((uint8_t*)dd + sizeof(struct packed_rrset_data));
+ dd->rr_data = (uint8_t**)&dd->rr_len[dd->count];
+ dd->rr_ttl = (time_t*)&dd->rr_data[dd->count];
+ for(i = 0, pos = 0; i < fd->count; ++i) {
+ if (fd->rr_len[i] != 6 || fd->rr_data[i][0] != 0 ||
+ fd->rr_data[i][1] != 4) {
+ *dd_out = NULL;
+ return;
+ }
+ for (j = 0; j < dns64_prefix_count; j++, pos++) {
+ dd->rr_len[pos] = 18;
+ dd->rr_ttl[pos] = fd->rr_ttl[i];
+ dd->rr_data[pos] = (uint8_t*)&dd->rr_ttl[dd->count] +
+ 18 * pos;
+ dd->rr_data[pos][0] = 0;
+ dd->rr_data[pos][1] = 16;
+ synthesize_aaaa(&dns64_prefixes[j].in6,
+ dns64_prefixes[j].prefixlen, &fd->rr_data[i][2],
+ fd->rr_len[i]-2, &dd->rr_data[pos][2],
+ dd->rr_len[pos]-2);
+ }
+ }
+
+ /*
+ * Create synthesized AAAA RR set key. This is mostly just bookkeeping,
+ * nothing interesting here.
+ */
+ if(!dk) {
+ log_warnx("no key");
+ *dd_out = NULL;
+ return;
+ }
+
+ dk->rk.dname = (uint8_t*)regional_alloc_init(region, fk->rk.dname,
+ fk->rk.dname_len);
+
+ if(!dk->rk.dname) {
+ log_warnx("out of memory");
+ *dd_out = NULL;
+ return;
+ }
+
+ dk->rk.type = htons(LDNS_RR_TYPE_AAAA);
+ memset(&dk->entry, 0, sizeof(dk->entry));
+ dk->entry.key = dk;
+ dk->entry.hash = rrset_key_hash(&dk->rk);
+ dk->entry.data = dd;
+}
--- /dev/null
+/* $OpenBSD: dns64_synth.h,v 1.1 2021/01/24 18:29:15 florian Exp $ */
+
+/*
+ * dns64/dns64.h - DNS64 module
+ *
+ * Copyright (c) 2009, Viagénie. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DNS64_SYNTH_H
+#define DNS64_SYNTH_H
+
+void
+dns64_synth_aaaa_data(const struct ub_packed_rrset_key *, const struct
+ packed_rrset_data *, struct ub_packed_rrset_key *, struct
+ packed_rrset_data **, struct regional *);
+
+#endif /* DNS64_SYNTH_H */
-/* $OpenBSD: frontend.c,v 1.64 2021/01/19 16:52:40 florian Exp $ */
+/* $OpenBSD: frontend.c,v 1.65 2021/01/24 18:29:15 florian Exp $ */
/*
* Copyright (c) 2018 Florian Obser <florian@openbsd.org>
#include "unwind.h"
#include "frontend.h"
#include "control.h"
+#include "dns64_synth.h"
#define ROUTE_SOCKET_BUF_SIZE 16384
uint64_t imsg_id;
int fd;
int tcp;
+ int dns64_synthesize;
};
TAILQ_HEAD(, pending_query) pending_queries;
void tcp_timeout(int, short, void *);
int check_query(sldns_buffer*);
void noerror_answer(struct pending_query *);
+void synthesize_dns64_answer(struct pending_query *);
+void resend_dns64_query(struct pending_query *);
void chaos_answer(struct pending_query *);
void error_answer(struct pending_query *, int rcode);
void send_answer(struct pending_query *);
RB_PROTOTYPE(bl_tree, bl_node, entry, bl_cmp)
RB_GENERATE(bl_tree, bl_node, entry, bl_cmp)
+struct dns64_prefix *dns64_prefixes;
+int dns64_prefix_count;
+
void
frontend_sig_handler(int sig, short event, void *bula)
{
void
frontend_dispatch_resolver(int fd, short event, void *bula)
{
+ static struct dns64_prefix *new_dns64_prefixes = NULL;
+ static int new_dns64_prefix_count = 0;
+ static int new_dns64_prefix_pos = 0;
struct pending_query *pq;
struct imsgev *iev = bula;
struct imsgbuf *ibuf = &iev->ibuf;
if (sldns_buffer_position(pq->abuf) ==
sldns_buffer_capacity(pq->abuf)) {
sldns_buffer_flip(pq->abuf);
- noerror_answer(pq);
- send_answer(pq);
+ if (pq->dns64_synthesize) {
+ synthesize_dns64_answer(pq);
+ send_answer(pq);
+ } else {
+ noerror_answer(pq);
+ if (pq->dns64_synthesize)
+ /* we did not find a answer */
+ resend_dns64_query(pq);
+ else
+ send_answer(pq);
+ }
}
break;
}
if (ta_fd != -1)
write_trust_anchors(&trust_anchors, ta_fd);
break;
+ case IMSG_NEW_DNS64_PREFIXES_START:
+ if (IMSG_DATA_SIZE(imsg) !=
+ sizeof(new_dns64_prefix_count))
+ fatalx("%s: IMSG_NEW_DNS64_PREFIXES_START "
+ "wrong length: %lu", __func__,
+ IMSG_DATA_SIZE(imsg));
+ memcpy(&new_dns64_prefix_count, imsg.data,
+ sizeof(new_dns64_prefix_count));
+ free(new_dns64_prefixes);
+ new_dns64_prefixes = NULL;
+ if (new_dns64_prefix_count > 0)
+ new_dns64_prefixes =
+ calloc(new_dns64_prefix_count,
+ sizeof(struct dns64_prefix));
+ new_dns64_prefix_pos = 0;
+ break;
+ case IMSG_NEW_DNS64_PREFIX: {
+ if (IMSG_DATA_SIZE(imsg) != sizeof(struct dns64_prefix))
+ fatalx("%s: IMSG_NEW_DNS64_PREFIX wrong "
+ "length: %lu", __func__,
+ IMSG_DATA_SIZE(imsg));
+ if (new_dns64_prefixes == NULL)
+ break;
+ if (new_dns64_prefix_pos >= new_dns64_prefix_count)
+ fatalx("%s: IMSG_NEW_DNS64_PREFIX: too many "
+ "prefixes", __func__);
+ memcpy(&new_dns64_prefixes[new_dns64_prefix_pos++],
+ imsg.data, sizeof(struct dns64_prefix));
+ break;
+ }
+ case IMSG_NEW_DNS64_PREFIXES_DONE:
+ free(dns64_prefixes);
+ dns64_prefixes = new_dns64_prefixes;
+ dns64_prefix_count = new_dns64_prefix_count;
+ new_dns64_prefixes = NULL;
+ break;
default:
log_debug("%s: error handling imsg %d", __func__,
imsg.hdr.type);
void
noerror_answer(struct pending_query *pq)
{
- struct query_info skip, qinfo;
- struct reply_info *rinfo = NULL;
- struct alloc_cache alloc;
- struct edns_data edns;
+ struct query_info skip, qinfo;
+ struct reply_info *rinfo = NULL;
+ struct alloc_cache alloc;
+ struct edns_data edns;
+ struct ub_packed_rrset_key *an_rrset = NULL;
+ struct packed_rrset_data *an_rrset_data = NULL;
alloc_init(&alloc, NULL, 0);
memset(&qinfo, 0, sizeof(qinfo));
if (reply_info_parse(pq->abuf, &alloc, &qinfo, &rinfo, pq->region,
&edns) != 0)
goto srvfail;
+
+ if ((an_rrset = reply_find_answer_rrset(&qinfo, rinfo)) != NULL)
+ an_rrset_data = (struct packed_rrset_data*)an_rrset->entry.data;
+
/* reply_info_parse() allocates memory */
query_info_clear(&qinfo);
+ /* XXX check that there a no AAAA records in answer section? */
+ if ((an_rrset_data == NULL || an_rrset_data->count == 0) &&
+ !pq->dns64_synthesize && pq->qinfo.qtype == LDNS_RR_TYPE_AAAA &&
+ pq->qinfo.qclass == LDNS_RR_CLASS_IN && dns64_prefix_count > 0) {
+ pq->dns64_synthesize = 1;
+ return;
+ }
+
sldns_buffer_clear(pq->abuf);
if (reply_info_encode(&pq->qinfo, rinfo, pq->qmsg->id, rinfo->flags,
pq->abuf, 0, pq->region, pq->tcp ? UINT16_MAX : pq->edns.udp_size,
error_answer(pq, LDNS_RCODE_SERVFAIL);
}
+void
+synthesize_dns64_answer(struct pending_query *pq)
+{
+ struct query_info skip, qinfo;
+ struct reply_info *rinfo = NULL, *synth_rinfo = NULL;
+ struct alloc_cache alloc;
+ struct edns_data edns;
+ size_t i;
+
+ pq->dns64_synthesize = 0;
+
+ alloc_init(&alloc, NULL, 0);
+ memset(&qinfo, 0, sizeof(qinfo));
+ /* read past query section, no memory is allocated */
+ if (!query_info_parse(&skip, pq->abuf))
+ goto srvfail;
+
+ if (reply_info_parse(pq->abuf, &alloc, &qinfo, &rinfo, pq->region,
+ &edns) != 0)
+ goto srvfail;
+
+ /* reply_info_parse() allocates memory */
+ query_info_clear(&qinfo);
+
+ synth_rinfo = construct_reply_info_base(pq->region, rinfo->flags,
+ rinfo->qdcount, rinfo->ttl, rinfo->prefetch_ttl,
+ rinfo->serve_expired_ttl, rinfo->an_numrrsets,
+ rinfo->ns_numrrsets, rinfo->ar_numrrsets, rinfo->rrset_count,
+ rinfo->security);
+
+ if (!synth_rinfo)
+ goto srvfail;
+
+ if(!reply_info_alloc_rrset_keys(synth_rinfo, NULL, pq->region))
+ goto srvfail;
+
+ for (i = 0; i < synth_rinfo->rrset_count; i++) {
+ struct ub_packed_rrset_key *src_rrset_key, *dst_rrset_key;
+ struct packed_rrset_data *src_rrset_data;
+ struct packed_rrset_data *dst_rrset_data;
+
+ src_rrset_key = rinfo->rrsets[i];
+ src_rrset_data =
+ (struct packed_rrset_data *)src_rrset_key->entry.data;
+ dst_rrset_key = synth_rinfo->rrsets[i];
+
+ dst_rrset_key->id = src_rrset_key->id;
+ dst_rrset_key->rk = src_rrset_key->rk;
+
+ if (i < rinfo->an_numrrsets && src_rrset_key->rk.type ==
+ htons(LDNS_RR_TYPE_A)) {
+ dns64_synth_aaaa_data(src_rrset_key, src_rrset_data,
+ dst_rrset_key, &dst_rrset_data, pq->region);
+ if (dst_rrset_data == NULL)
+ goto srvfail;
+ } else {
+ dst_rrset_key->entry.hash = src_rrset_key->entry.hash;
+ dst_rrset_key->rk.dname = regional_alloc_init(
+ pq->region, src_rrset_key->rk.dname,
+ src_rrset_key->rk.dname_len);
+ if (dst_rrset_key->rk.dname == NULL)
+ goto srvfail;
+
+ dst_rrset_data = regional_alloc_init(pq->region,
+ src_rrset_data,
+ packed_rrset_sizeof(src_rrset_data));
+ if (dst_rrset_data == NULL)
+ goto srvfail;
+ }
+
+ packed_rrset_ptr_fixup(dst_rrset_data);
+ dst_rrset_key->entry.data = dst_rrset_data;
+ }
+
+ if (!sldns_buffer_set_capacity(pq->abuf, pq->tcp ? UINT16_MAX :
+ pq->edns.udp_size))
+ goto srvfail;
+
+ sldns_buffer_clear(pq->abuf);
+
+ if (reply_info_encode(&pq->qinfo, synth_rinfo, pq->qmsg->id,
+ synth_rinfo->flags, pq->abuf, 0, pq->region,
+ pq->tcp ? UINT16_MAX : pq->edns.udp_size,
+ pq->edns.bits & EDNS_DO, MINIMIZE_ANSWER) == 0)
+ goto srvfail;
+
+ reply_info_parsedelete(rinfo, &alloc);
+ alloc_clear(&alloc);
+ return;
+
+ srvfail:
+ reply_info_parsedelete(rinfo, &alloc);
+ alloc_clear(&alloc);
+ error_answer(pq, LDNS_RCODE_SERVFAIL);
+}
+
+void
+resend_dns64_query(struct pending_query *opq) {
+ struct pending_query *pq;
+ struct query_imsg query_imsg;
+ int rcode;
+ char dname[LDNS_MAX_DOMAINLEN + 1];
+
+ if ((pq = calloc(1, sizeof(*pq))) == NULL) {
+ log_warn(NULL);
+ return;
+ }
+
+ do {
+ arc4random_buf(&pq->imsg_id, sizeof(pq->imsg_id));
+ } while(find_pending_query(pq->imsg_id) != NULL);
+
+ TAILQ_INSERT_TAIL(&pending_queries, pq, entry);
+
+ pq->from = opq->from;
+ pq->fd = opq->fd;
+ opq->fd = -1;
+ pq->tcp = opq->tcp;
+ pq->qbuf = sldns_buffer_new(sldns_buffer_capacity(opq->qbuf));
+ pq->abuf = sldns_buffer_new(sldns_buffer_capacity(opq->abuf));
+ pq->region = regional_create();
+ pq->qmsg = regional_alloc(pq->region, sizeof(*pq->qmsg));
+
+ if (!pq->qbuf || !pq->abuf || !pq->region || !pq->qmsg) {
+ log_warnx("out of memory");
+ free_pending_query(pq);
+ free_pending_query(opq);
+ return;
+ }
+
+ sldns_buffer_rewind(opq->qbuf);
+ sldns_buffer_write(pq->qbuf, sldns_buffer_current(opq->qbuf),
+ sldns_buffer_remaining(opq->qbuf));
+ sldns_buffer_flip(pq->qbuf);
+ memset(pq->qmsg, 0, sizeof(*pq->qmsg));
+
+ if (pq->tcp) {
+ struct timeval timeout = {TCP_TIMEOUT, 0};
+
+ event_set(&pq->ev, pq->fd, EV_READ | EV_PERSIST, tcp_request,
+ pq);
+ event_set(&pq->resp_ev, pq->fd, EV_WRITE | EV_PERSIST,
+ tcp_response, pq);
+ evtimer_set(&pq->tmo_ev, tcp_timeout, pq);
+ evtimer_add(&pq->tmo_ev, &timeout);
+ }
+
+ if (!query_info_parse(&pq->qinfo, pq->qbuf)) {
+ log_warnx("query_info_parse failed");
+ goto drop;
+ }
+
+ sldns_buffer_rewind(pq->qbuf);
+
+ if (parse_packet(pq->qbuf, pq->qmsg, pq->region) !=
+ LDNS_RCODE_NOERROR) {
+ log_warnx("parse_packet failed");
+ goto drop;
+ }
+
+ rcode = parse_extract_edns(pq->qmsg, &pq->edns, pq->region);
+ if (rcode != LDNS_RCODE_NOERROR) {
+ error_answer(pq, rcode);
+ goto send_answer;
+ }
+
+ dname_str(pq->qinfo.qname, dname);
+ strlcpy(query_imsg.qname, dname, sizeof(query_imsg.qname));
+ query_imsg.id = pq->imsg_id;
+ query_imsg.t = LDNS_RR_TYPE_A;
+ query_imsg.c = pq->qinfo.qclass;
+
+ pq->dns64_synthesize = 1;
+
+ if (frontend_imsg_compose_resolver(IMSG_QUERY, 0, &query_imsg,
+ sizeof(query_imsg)) == -1) {
+ error_answer(pq, LDNS_RCODE_SERVFAIL);
+ goto send_answer;
+ }
+
+ free_pending_query(opq);
+ return;
+
+ send_answer:
+ free_pending_query(opq);
+ send_answer(pq);
+ return;
+
+ drop:
+ free_pending_query(opq);
+ free_pending_query(pq);
+}
+
void
chaos_answer(struct pending_query *pq)
{
-/* $OpenBSD: frontend.h,v 1.7 2021/01/19 16:52:12 florian Exp $ */
+/* $OpenBSD: frontend.h,v 1.8 2021/01/24 18:29:15 florian Exp $ */
/*
* Copyright (c) 2018 Florian Obser <florian@openbsd.org>
struct sockaddr_rtdns rtdns;
};
+struct dns64_prefix {
+ struct in6_addr in6;
+ int prefixlen;
+ int flags;
+};
+
void frontend(int, int);
void frontend_dispatch_main(int, short, void *);
void frontend_dispatch_resolver(int, short, void *);
-/* $OpenBSD: resolver.c,v 1.133 2021/01/23 16:28:12 florian Exp $ */
+/* $OpenBSD: resolver.c,v 1.134 2021/01/24 18:29:15 florian Exp $ */
/*
* Copyright (c) 2018 Florian Obser <florian@openbsd.org>
#define BOGUS 1
#define SECURE 2
+#define WKA1_FOUND 1
+#define WKA2_FOUND 2
+
struct uw_resolver {
struct event check_ev;
struct event free_ev;
struct uw_conf *);
const char *query_imsg2str(struct query_imsg *);
char *gen_resolv_conf(void);
+void check_dns64(void);
+void check_dns64_done(struct asr_result *, void *);
+int dns64_prefixlen(const struct in6_addr *,
+ const uint8_t *);
+void add_dns64_prefix(const struct in6_addr *, int,
+ struct dns64_prefix *, int, int);
struct uw_conf *resolver_conf;
static struct imsgev *iev_frontend;
struct key_cache *unified_key_cache;
struct val_neg_cache *unified_neg_cache;
+int dns64_present;
+
static const char * const as112_zones[] = {
/* RFC1918 */
"10.in-addr.arpa. transparent",
}
if (sec == SECURE) {
- if (prev_state != VALIDATING)
- new_resolver(checked_resolver->type, VALIDATING);
- if (!(evtimer_pending(&trust_anchor_timer, NULL)))
- evtimer_add(&trust_anchor_timer, &tv);
+ if (dns64_present && (res->type == UW_RES_DHCP ||
+ res->type == UW_RES_ODOT_DHCP)) {
+ /* do not upgrade to validating, DNS64 breaks DNSSEC */
+ if (prev_state != RESOLVING)
+ new_resolver(checked_resolver->type,
+ RESOLVING);
+ } else {
+ if (prev_state != VALIDATING)
+ new_resolver(checked_resolver->type,
+ VALIDATING);
+ if (!(evtimer_pending(&trust_anchor_timer, NULL)))
+ evtimer_add(&trust_anchor_timer, &tv);
+ }
} else if (rcode == LDNS_RCODE_NOERROR &&
LDNS_RCODE_WIRE((uint8_t*)answer_packet) == LDNS_RCODE_NOERROR) {
if (why_bogus) {
new_resolver(UW_RES_ASR, UNKNOWN);
new_resolver(UW_RES_DHCP, UNKNOWN);
new_resolver(UW_RES_ODOT_DHCP, UNKNOWN);
+ check_dns64();
} else {
while ((tmp = TAILQ_FIRST(&new_forwarder_list)) != NULL) {
TAILQ_REMOVE(&new_forwarder_list, tmp, entry);
}
return resolv_conf;
}
+
+void
+check_dns64(void)
+{
+ struct asr_query *aq = NULL;
+ char *resolv_conf;
+ void *asr_ctx;
+
+ if (TAILQ_EMPTY(&autoconf_forwarder_list))
+ return;
+
+ if ((resolv_conf = gen_resolv_conf()) == NULL) {
+ log_warnx("could not create asr context");
+ return;
+ }
+
+ if ((asr_ctx = asr_resolver_from_string(resolv_conf)) != NULL) {
+ if ((aq = res_query_async("ipv4only.arpa", LDNS_RR_CLASS_IN,
+ LDNS_RR_TYPE_AAAA, asr_ctx)) == NULL) {
+ log_warn("%s: res_query_async", __func__);
+ asr_resolver_free(asr_ctx);
+ }
+ if (event_asr_run(aq, check_dns64_done, asr_ctx) == NULL) {
+ log_warn("%s: event_asr_run", __func__);
+ free(aq);
+ asr_resolver_free(asr_ctx);
+ }
+ } else
+ log_warnx("%s: could not create asr context", __func__);
+
+ free(resolv_conf);
+}
+
+void
+check_dns64_done(struct asr_result *ar, void *arg)
+{
+ /* RFC 7050: ipv4only.arpa resolves to 192.0.0.170 and 192.9.0.171 */
+ const uint8_t wka1[] = {192, 0, 0, 170};
+ const uint8_t wka2[] = {192, 0, 0, 171};
+ struct query_info skip, qinfo;
+ struct reply_info *rinfo = NULL;
+ struct regional *region = NULL;
+ struct sldns_buffer *buf = NULL;
+ struct ub_packed_rrset_key *an_rrset = NULL;
+ struct packed_rrset_data *an_rrset_data;
+ struct alloc_cache alloc;
+ struct edns_data edns;
+ struct dns64_prefix *prefixes = NULL;
+ size_t i;
+ int preflen, count = 0;
+ void *asr_ctx = arg;
+
+ memset(&qinfo, 0, sizeof(qinfo));
+ alloc_init(&alloc, NULL, 0);
+
+ if (ar->ar_datalen < LDNS_HEADER_SIZE) {
+ log_warnx("%s: bad packet: too short: %d", __func__,
+ ar->ar_datalen);
+ goto out;
+ }
+
+ if (ar->ar_datalen > UINT16_MAX) {
+ log_warnx("%s: bad packet: too large: %d", __func__,
+ ar->ar_datalen);
+ goto out;
+ }
+
+ if (ar->ar_rcode == LDNS_RCODE_NXDOMAIN) {
+ /* XXX this means that the dhcp resolver is broken */
+ log_debug("%s: NXDOMAIN", __func__);
+ goto out;
+ }
+
+ if ((buf = sldns_buffer_new(ar->ar_datalen)) == NULL)
+ goto out;
+
+ if ((region = regional_create()) == NULL)
+ goto out;
+
+ sldns_buffer_write(buf, ar->ar_data, ar->ar_datalen);
+ sldns_buffer_flip(buf);
+
+ /* read past query section, no memory is allocated */
+ if (!query_info_parse(&skip, buf))
+ goto out;
+
+ if (reply_info_parse(buf, &alloc, &qinfo, &rinfo, region, &edns) != 0)
+ goto out;
+
+ if ((an_rrset = reply_find_answer_rrset(&qinfo, rinfo)) == NULL)
+ goto out;
+
+ an_rrset_data = (struct packed_rrset_data*)an_rrset->entry.data;
+
+ prefixes = calloc(an_rrset_data->count, sizeof(struct dns64_prefix));
+ if (prefixes == NULL)
+ goto out;
+
+ for (i = 0; i < an_rrset_data->count; i++) {
+ struct in6_addr in6;
+
+ /* check for AAAA record */
+ if (an_rrset_data->rr_len[i] != 18) /* 2 + 128/8 */
+ continue;
+ if (an_rrset_data->rr_data[i][0] != 0 &&
+ an_rrset_data->rr_data[i][1] != 16)
+ continue;
+
+ memcpy(&in6, &an_rrset_data->rr_data[i][2],
+ sizeof(in6));
+ if ((preflen = dns64_prefixlen(&in6, wka1)) != -1)
+ add_dns64_prefix(&in6, preflen, prefixes,
+ an_rrset_data->count, WKA1_FOUND);
+ if ((preflen = dns64_prefixlen(&in6, wka2)) != -1)
+ add_dns64_prefix(&in6, preflen, prefixes,
+ an_rrset_data->count, WKA2_FOUND);
+ }
+
+ for (i = 0; i < an_rrset_data->count && prefixes[i].flags != 0; i++)
+ if ((prefixes[i].flags & (WKA1_FOUND | WKA2_FOUND)) ==
+ (WKA1_FOUND | WKA2_FOUND))
+ count++;
+
+ dns64_present = count > 0;
+
+ if (dns64_present) {
+ /* downgrade DHCP resolvers, DNS64 breaks DNSSEC */
+ if (resolvers[UW_RES_DHCP] != NULL &&
+ resolvers[UW_RES_DHCP]->state == VALIDATING)
+ new_resolver(UW_RES_DHCP, RESOLVING);
+ if (resolvers[UW_RES_ODOT_DHCP] != NULL &&
+ resolvers[UW_RES_ODOT_DHCP]->state == VALIDATING)
+ new_resolver(UW_RES_ODOT_DHCP, RESOLVING);
+ }
+
+ resolver_imsg_compose_frontend(IMSG_NEW_DNS64_PREFIXES_START, 0,
+ &count, sizeof(count));
+ for (i = 0; i < an_rrset_data->count && prefixes[i].flags != 0; i++) {
+ if ((prefixes[i].flags & (WKA1_FOUND | WKA2_FOUND)) ==
+ (WKA1_FOUND | WKA2_FOUND)) {
+ resolver_imsg_compose_frontend(IMSG_NEW_DNS64_PREFIX,
+ 0, &prefixes[i], sizeof(struct dns64_prefix));
+ }
+ }
+ resolver_imsg_compose_frontend(IMSG_NEW_DNS64_PREFIXES_DONE, 0, NULL,
+ 0);
+ out:
+ free(prefixes);
+ query_info_clear(&qinfo);
+ reply_info_parsedelete(rinfo, &alloc);
+ alloc_clear(&alloc);
+ regional_destroy(region);
+ sldns_buffer_free(buf);
+ free(ar->ar_data);
+ asr_resolver_free(asr_ctx);
+}
+
+int
+dns64_prefixlen(const struct in6_addr *in6, const uint8_t *wka)
+{
+ /* RFC 6052, 2.2 */
+ static const int possible_prefixes[] = {32, 40, 48, 56, 64, 96};
+ size_t i, j;
+ int found, pos;
+
+ for (i = 0; i < nitems(possible_prefixes); i++) {
+ pos = possible_prefixes[i] / 8;
+ found = 1;
+ for (j = 0; j < 4 && found; j++, pos++) {
+ if (pos == 8) {
+ if (in6->s6_addr[pos] != 0)
+ found = 0;
+ pos++;
+ }
+ if (in6->s6_addr[pos] != wka[j])
+ found = 0;
+ }
+ if (found)
+ return possible_prefixes[i];
+ }
+ return -1;
+}
+
+void
+add_dns64_prefix(const struct in6_addr *in6, int prefixlen,
+ struct dns64_prefix *prefixes, int prefixes_size, int flag)
+{
+ struct in6_addr tmp;
+ int i;
+
+ tmp = *in6;
+
+ for(i = prefixlen / 8; i < 16; i++)
+ tmp.s6_addr[i] = 0;
+
+ for (i = 0; i < prefixes_size; i++) {
+ if (prefixes[i].flags == 0) {
+ prefixes[i].in6 = tmp;
+ prefixes[i].prefixlen = prefixlen;
+ prefixes[i].flags |= flag;
+ break;
+ } else if (prefixes[i].prefixlen == prefixlen &&
+ memcmp(&prefixes[i].in6, &tmp, sizeof(tmp)) == 0) {
+ prefixes[i].flags |= flag;
+ break;
+ }
+ }
+}
-/* $OpenBSD: unwind.h,v 1.51 2021/01/19 16:50:23 florian Exp $ */
+/* $OpenBSD: unwind.h,v 1.52 2021/01/24 18:29:15 florian Exp $ */
/*
* Copyright (c) 2018 Florian Obser <florian@openbsd.org>
IMSG_NETWORK_CHANGED,
IMSG_BLFD,
IMSG_REPLACE_DNS,
+ IMSG_NEW_DNS64_PREFIXES_START,
+ IMSG_NEW_DNS64_PREFIX,
+ IMSG_NEW_DNS64_PREFIXES_DONE,
};
struct uw_forwarder {