diff --git a/CHANGES b/CHANGES
index 98eca3480b..8ca7f9e5fd 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+6380. [func] Queries and responses now emit distinct dnstap entries
+ for DoT and DoH. [GL #4523]
+
6379. [bug] A QP iterator bug could result in DNSSEC validation
failing because the wrong NSEC was returned. [GL #4659]
diff --git a/bin/tools/dnstap-read.c b/bin/tools/dnstap-read.c
index 1df44dad6f..cbc82c33c5 100644
--- a/bin/tools/dnstap-read.c
+++ b/bin/tools/dnstap-read.c
@@ -269,7 +269,8 @@ print_yaml(dns_dtdata_t *dt) {
}
}
- printf(" socket_protocol: %s\n", dt->tcp ? "TCP" : "UDP");
+ printf(" socket_protocol: %s\n",
+ dt->transport == DNS_TRANSPORT_UDP ? "UDP" : "TCP");
if (m->has_query_address) {
ProtobufCBinaryData *ip = &m->query_address;
diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst
index 2f71c587d2..0fd1bb9137 100644
--- a/doc/notes/notes-current.rst
+++ b/doc/notes/notes-current.rst
@@ -26,6 +26,9 @@ New Features
- A new DNSSEC tool :iscman:`dnssec-ksr` is added to create Key Signing Request
(KSR) and Signed Key Response (SKR) files. :gl:`#1128`
+- Queries and responses now emit distinct dnstap entries for DoT and DoH.
+ :any:`dnstap-read` understands DoH and DoT entries. :gl:`#4523`
+
Removed Features
~~~~~~~~~~~~~~~~
diff --git a/lib/dns/dnstap.c b/lib/dns/dnstap.c
index 887c017996..bc0be5fe6a 100644
--- a/lib/dns/dnstap.c
+++ b/lib/dns/dnstap.c
@@ -643,7 +643,7 @@ cpbuf(isc_buffer_t *buf, ProtobufCBinaryData *p, protobuf_c_boolean *has) {
}
static void
-setaddr(dns_dtmsg_t *dm, isc_sockaddr_t *sa, bool tcp,
+setaddr(dns_dtmsg_t *dm, isc_sockaddr_t *sa, dns_transport_type_t transport,
ProtobufCBinaryData *addr, protobuf_c_boolean *has_addr, uint32_t *port,
protobuf_c_boolean *has_port) {
int family = isc_sockaddr_pf(sa);
@@ -664,10 +664,22 @@ setaddr(dns_dtmsg_t *dm, isc_sockaddr_t *sa, bool tcp,
*port = ntohs(sa->type.sin.sin_port);
}
- if (tcp) {
+ switch (transport) {
+ case DNS_TRANSPORT_TCP:
dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__TCP;
- } else {
+ break;
+ case DNS_TRANSPORT_UDP:
dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__UDP;
+ break;
+ case DNS_TRANSPORT_TLS:
+ dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__DOT;
+ break;
+ case DNS_TRANSPORT_HTTP:
+ dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__DOH;
+ break;
+ case DNS_TRANSPORT_NONE:
+ case DNS_TRANSPORT_COUNT:
+ UNREACHABLE();
}
dm->m.has_socket_protocol = 1;
@@ -732,8 +744,9 @@ unlock_and_return:
void
dns_dt_send(dns_view_t *view, dns_dtmsgtype_t msgtype, isc_sockaddr_t *qaddr,
- isc_sockaddr_t *raddr, bool tcp, isc_region_t *zone,
- isc_time_t *qtime, isc_time_t *rtime, isc_buffer_t *buf) {
+ isc_sockaddr_t *raddr, dns_transport_type_t transport,
+ isc_region_t *zone, isc_time_t *qtime, isc_time_t *rtime,
+ isc_buffer_t *buf) {
isc_time_t now, *t;
dns_dtmsg_t dm;
@@ -833,12 +846,12 @@ dns_dt_send(dns_view_t *view, dns_dtmsgtype_t msgtype, isc_sockaddr_t *qaddr,
}
if (qaddr != NULL) {
- setaddr(&dm, qaddr, tcp, &dm.m.query_address,
+ setaddr(&dm, qaddr, transport, &dm.m.query_address,
&dm.m.has_query_address, &dm.m.query_port,
&dm.m.has_query_port);
}
if (raddr != NULL) {
- setaddr(&dm, raddr, tcp, &dm.m.response_address,
+ setaddr(&dm, raddr, transport, &dm.m.response_address,
&dm.m.has_response_address, &dm.m.response_port,
&dm.m.has_response_port);
}
@@ -1162,11 +1175,27 @@ dns_dt_parse(isc_mem_t *mctx, isc_region_t *src, dns_dtdata_t **destp) {
protobuf_c_enum_descriptor_get_value(
&dnstap__socket_protocol__descriptor,
m->socket_protocol);
- if (type != NULL && type->value == DNSTAP__SOCKET_PROTOCOL__TCP)
- {
- d->tcp = true;
+
+ if (type != NULL) {
+ switch (type->value) {
+ case DNSTAP__SOCKET_PROTOCOL__DNSCryptUDP:
+ case DNSTAP__SOCKET_PROTOCOL__DOQ:
+ case DNSTAP__SOCKET_PROTOCOL__UDP:
+ d->transport = DNS_TRANSPORT_UDP;
+ break;
+ case DNSTAP__SOCKET_PROTOCOL__DNSCryptTCP:
+ case DNSTAP__SOCKET_PROTOCOL__TCP:
+ d->transport = DNS_TRANSPORT_TCP;
+ break;
+ case DNSTAP__SOCKET_PROTOCOL__DOT:
+ d->transport = DNS_TRANSPORT_TLS;
+ break;
+ case DNSTAP__SOCKET_PROTOCOL__DOH:
+ d->transport = DNS_TRANSPORT_HTTP;
+ break;
+ }
} else {
- d->tcp = false;
+ d->transport = DNS_TRANSPORT_UDP;
}
}
@@ -1292,10 +1321,24 @@ dns_dt_datatotext(dns_dtdata_t *d, isc_buffer_t **dest) {
CHECK(putstr(dest, " "));
/* Protocol */
- if (d->tcp) {
- CHECK(putstr(dest, "TCP "));
- } else {
+ switch (d->transport) {
+ case DNS_TRANSPORT_NONE:
+ CHECK(putstr(dest, "NUL "));
+ break;
+ case DNS_TRANSPORT_UDP:
CHECK(putstr(dest, "UDP "));
+ break;
+ case DNS_TRANSPORT_TCP:
+ CHECK(putstr(dest, "TCP "));
+ break;
+ case DNS_TRANSPORT_TLS:
+ CHECK(putstr(dest, "DOT "));
+ break;
+ case DNS_TRANSPORT_HTTP:
+ CHECK(putstr(dest, "DOH "));
+ break;
+ case DNS_TRANSPORT_COUNT:
+ UNREACHABLE();
}
/* Message size */
diff --git a/lib/dns/dnstap.proto b/lib/dns/dnstap.proto
index 9d0ac4167a..b589dc84d5 100644
--- a/lib/dns/dnstap.proto
+++ b/lib/dns/dnstap.proto
@@ -25,6 +25,7 @@
//
// .
+syntax = "proto2";
package dnstap;
// "Dnstap": this is the top-level dnstap type, which is a "union" type that
@@ -66,11 +67,60 @@ enum SocketFamily {
INET6 = 2; // IPv6 (RFC 2460)
}
-// SocketProtocol: the transport protocol of a socket. This specifies how to
-// interpret "transport port" fields.
+// SocketProtocol: the protocol used to transport a DNS message.
enum SocketProtocol {
- UDP = 1; // User Datagram Protocol (RFC 768)
- TCP = 2; // Transmission Control Protocol (RFC 793)
+ UDP = 1; // DNS over UDP transport (RFC 1035 section 4.2.1)
+ TCP = 2; // DNS over TCP transport (RFC 1035 section 4.2.2)
+ DOT = 3; // DNS over TLS (RFC 7858)
+ DOH = 4; // DNS over HTTPS (RFC 8484)
+ DNSCryptUDP = 5; // DNSCrypt over UDP (https://dnscrypt.info/protocol)
+ DNSCryptTCP = 6; // DNSCrypt over TCP (https://dnscrypt.info/protocol)
+ DOQ = 7; // DNS over QUIC (RFC 9250)
+}
+
+// Policy: information about any name server operator policy
+// applied to the processing of a DNS message.
+message Policy {
+
+ // Match: what aspect of the message or message exchange
+ // triggered the application of the Policy.
+ enum Match {
+ QNAME = 1; // Name in question section of query
+ CLIENT_IP = 2; // Client IP address
+ RESPONSE_IP = 3; // Address in A/AAAA RRSet
+ NS_NAME = 4; // Authoritative name server, by name
+ NS_IP = 5; // Authoritative name server, by IP address
+ }
+
+ // The Action taken to implement the Policy.
+ enum Action {
+ NXDOMAIN = 1; // Respond with NXDOMAIN
+ NODATA = 2; // Respond with empty answer section
+ PASS = 3; // Do not alter the response (passthrough)
+ DROP = 4; // Do not respond.
+ TRUNCATE = 5; // Truncate UDP response, forcing TCP retry
+ LOCAL_DATA = 6; // Respond with local data from policy
+ }
+
+ // type: the type of policy applied, e.g. "RPZ" for a
+ // policy from a Response Policy Zone.
+ optional string type = 1;
+
+ // rule: the rule matched by the message.
+ //
+ // In a RPZ context, this is the owner name of the rule in
+ // the Reponse Policy Zone in wire format.
+ optional bytes rule = 2;
+
+ // action: the policy action taken in response to the
+ // rule match.
+ optional Action action = 3;
+
+ // match: the feature of the message exchange which matched the rule.
+ optional Match match = 4;
+
+ // The matched value. Format depends on the matched feature .
+ optional bytes value = 5;
}
// Message: a wire-format (RFC 1035 section 4) DNS message and associated
@@ -177,15 +227,15 @@ message Message {
// tool from a DNS server, from the perspective of the tool.
TOOL_RESPONSE = 12;
- // UPDATE_QUERY is a DNS update query message received from a resolver
+ // UPDATE_QUERY is a Dynamic DNS Update request (RFC 2136) received
// by an authoritative name server, from the perspective of the
// authoritative name server.
- UPDATE_QUERY = 13;
+ UPDATE_QUERY = 13;
- // UPDATE_RESPONSE is a DNS update response message sent from an
- // authoritative name server to a resolver, from the perspective of the
+ // UPDATE_RESPONSE is a Dynamic DNS Update response (RFC 2136) sent
+ // from an authoritative name server, from the perspective of the
// authoritative name server.
- UPDATE_RESPONSE = 14;
+ UPDATE_RESPONSE = 14;
}
// One of the Type values described above.
@@ -243,6 +293,9 @@ message Message {
// The responder's original wire-format DNS response message, verbatim.
optional bytes response_message = 14;
+
+ // Operator policy applied to the processing of this message, if any.
+ optional Policy policy = 15;
}
// All fields except for 'type' in the Message schema are optional.
diff --git a/lib/dns/include/dns/dnstap.h b/lib/dns/include/dns/dnstap.h
index 0d937c4e00..aa80cbba78 100644
--- a/lib/dns/include/dns/dnstap.h
+++ b/lib/dns/include/dns/dnstap.h
@@ -39,6 +39,7 @@ struct fstrm_iothr_options;
#include
#include
#include
+#include
#include
/*%
@@ -93,9 +94,9 @@ struct dns_dtdata {
void *frame;
- bool query;
- bool tcp;
- dns_dtmsgtype_t type;
+ bool query;
+ dns_dtmsgtype_t type;
+ dns_transport_type_t transport;
isc_time_t qtime;
isc_time_t rtime;
@@ -262,8 +263,9 @@ dns_dt_getstats(dns_dtenv_t *env, isc_stats_t **statsp);
void
dns_dt_send(dns_view_t *view, dns_dtmsgtype_t msgtype, isc_sockaddr_t *qaddr,
- isc_sockaddr_t *dstaddr, bool tcp, isc_region_t *zone,
- isc_time_t *qtime, isc_time_t *rtime, isc_buffer_t *buf);
+ isc_sockaddr_t *dstaddr, dns_transport_type_t transport,
+ isc_region_t *zone, isc_time_t *qtime, isc_time_t *rtime,
+ isc_buffer_t *buf);
/*%<
* Sends a dnstap message to the log, if 'msgtype' is one of the message
* types represented in 'view->dttypes'.
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
index 6fe8ba448b..fd5cc04be1 100644
--- a/lib/dns/resolver.c
+++ b/lib/dns/resolver.c
@@ -2319,6 +2319,7 @@ resquery_send(resquery_t *query) {
#ifdef HAVE_DNSTAP
isc_sockaddr_t localaddr, *la = NULL;
unsigned char zone[DNS_NAME_MAXWIRE];
+ dns_transport_type_t transport_type;
dns_dtmsgtype_t dtmsgtype;
isc_region_t zr;
isc_buffer_t zb;
@@ -2717,8 +2718,17 @@ resquery_send(resquery_t *query) {
la = &localaddr;
}
+ if (query->addrinfo->transport != NULL) {
+ transport_type =
+ dns_transport_get_type(query->addrinfo->transport);
+ } else if ((query->options & DNS_FETCHOPT_TCP) != 0) {
+ transport_type = DNS_TRANSPORT_TCP;
+ } else {
+ transport_type = DNS_TRANSPORT_UDP;
+ }
+
dns_dt_send(fctx->res->view, dtmsgtype, la, &query->addrinfo->sockaddr,
- tcp, &zr, &query->start, NULL, &buffer);
+ transport_type, &zr, &query->start, NULL, &buffer);
#endif /* HAVE_DNSTAP */
return (ISC_R_SUCCESS);
@@ -9556,6 +9566,7 @@ rctx_logpacket(respctx_t *rctx) {
isc_result_t result;
isc_sockaddr_t localaddr, *la = NULL;
unsigned char zone[DNS_NAME_MAXWIRE];
+ dns_transport_type_t transport_type;
dns_dtmsgtype_t dtmsgtype;
dns_compress_t cctx;
isc_region_t zr;
@@ -9594,9 +9605,17 @@ rctx_logpacket(respctx_t *rctx) {
la = &localaddr;
}
+ if (rctx->query->addrinfo->transport != NULL) {
+ transport_type = dns_transport_get_type(
+ rctx->query->addrinfo->transport);
+ } else if ((rctx->query->options & DNS_FETCHOPT_TCP) != 0) {
+ transport_type = DNS_TRANSPORT_TCP;
+ } else {
+ transport_type = DNS_TRANSPORT_UDP;
+ }
+
dns_dt_send(fctx->res->view, dtmsgtype, la,
- &rctx->query->addrinfo->sockaddr,
- ((rctx->query->options & DNS_FETCHOPT_TCP) != 0), &zr,
+ &rctx->query->addrinfo->sockaddr, transport_type, &zr,
&rctx->query->start, NULL, &rctx->buffer);
#endif /* HAVE_DNSTAP */
}
diff --git a/lib/ns/client.c b/lib/ns/client.c
index 34ab13caa3..f6ccdd0c5b 100644
--- a/lib/ns/client.c
+++ b/lib/ns/client.c
@@ -123,6 +123,53 @@ static void
compute_cookie(ns_client_t *client, uint32_t when, const unsigned char *secret,
isc_buffer_t *buf);
+#ifdef HAVE_DNSTAP
+static dns_transport_type_t
+ns_client_transport_type(const ns_client_t *client) {
+ /*
+ * Early escape hatch for libtest/ns.c
+ *
+ * When DoQ support this had to be removed to get correct DoQ entries.
+ */
+ if (!TCP_CLIENT(client)) {
+ return DNS_TRANSPORT_UDP;
+ }
+
+ INSIST(client->handle != NULL);
+
+ switch (isc_nm_socket_type(client->handle)) {
+ case isc_nm_udpsocket:
+ case isc_nm_udplistener:
+ case isc_nm_proxyudpsocket:
+ case isc_nm_proxyudplistener:
+ return DNS_TRANSPORT_UDP;
+ case isc_nm_tlssocket:
+ case isc_nm_tlslistener:
+ return DNS_TRANSPORT_TLS;
+ case isc_nm_httpsocket:
+ case isc_nm_httplistener:
+ return DNS_TRANSPORT_HTTP;
+ case isc_nm_streamdnslistener:
+ case isc_nm_streamdnssocket:
+ case isc_nm_proxystreamlistener:
+ case isc_nm_proxystreamsocket:
+ /* If it isn't DoT, it is DNS-over-TCP */
+ if (isc_nm_has_encryption(client->handle)) {
+ return DNS_TRANSPORT_TLS;
+ }
+ FALLTHROUGH;
+ case isc_nm_tcpsocket:
+ case isc_nm_tcplistener:
+ return DNS_TRANSPORT_TCP;
+ case isc_nm_maxsocket:
+ case isc_nm_nonesocket:
+ UNREACHABLE();
+ }
+
+ return DNS_TRANSPORT_UDP;
+}
+#endif /* HAVE_DNSTAP */
+
void
ns_client_recursing(ns_client_t *client) {
REQUIRE(NS_CLIENT_VALID(client));
@@ -396,6 +443,10 @@ ns_client_sendraw(ns_client_t *client, dns_message_t *message) {
isc_buffer_t buffer;
isc_region_t r;
isc_region_t *mr = NULL;
+#ifdef HAVE_DNSTAP
+ dns_transport_type_t transport_type;
+ dns_dtmsgtype_t dtmsgtype;
+#endif
REQUIRE(NS_CLIENT_VALID(client));
@@ -427,8 +478,8 @@ ns_client_sendraw(ns_client_t *client, dns_message_t *message) {
#ifdef HAVE_DNSTAP
if (client->view != NULL) {
- bool tcp = TCP_CLIENT(client);
- dns_dtmsgtype_t dtmsgtype;
+ transport_type = ns_client_transport_type(client);
+
if (client->message->opcode == dns_opcode_update) {
dtmsgtype = DNS_DTTYPE_UR;
} else if ((client->message->flags & DNS_MESSAGEFLAG_RD) != 0) {
@@ -437,7 +488,7 @@ ns_client_sendraw(ns_client_t *client, dns_message_t *message) {
dtmsgtype = DNS_DTTYPE_AR;
}
dns_dt_send(client->view, dtmsgtype, &client->peeraddr,
- &client->destsockaddr, tcp, NULL,
+ &client->destsockaddr, transport_type, NULL,
&client->requesttime, NULL, &buffer);
}
#endif
@@ -470,6 +521,7 @@ ns_client_send(ns_client_t *client) {
dns_aclenv_t *env = NULL;
#ifdef HAVE_DNSTAP
unsigned char zone[DNS_NAME_MAXWIRE];
+ dns_transport_type_t transport_type;
dns_dtmsgtype_t dtmsgtype;
isc_region_t zr;
#endif /* HAVE_DNSTAP */
@@ -637,6 +689,8 @@ renderend:
} else {
dtmsgtype = DNS_DTTYPE_AR;
}
+
+ transport_type = ns_client_transport_type(client);
#endif /* HAVE_DNSTAP */
if (cleanup_cctx) {
@@ -650,7 +704,7 @@ renderend:
#ifdef HAVE_DNSTAP
if (client->view != NULL) {
dns_dt_send(client->view, dtmsgtype, &client->peeraddr,
- &client->destsockaddr, true, &zr,
+ &client->destsockaddr, transport_type, &zr,
&client->requesttime, NULL, &buffer);
}
#endif /* HAVE_DNSTAP */
@@ -679,7 +733,7 @@ renderend:
*/
if (client->view != NULL) {
dns_dt_send(client->view, dtmsgtype, &client->peeraddr,
- &client->destsockaddr, false, &zr,
+ &client->destsockaddr, transport_type, &zr,
&client->requesttime, NULL, &buffer);
}
#endif /* HAVE_DNSTAP */
@@ -1652,6 +1706,7 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
size_t reqsize;
dns_aclenv_t *env = NULL;
#ifdef HAVE_DNSTAP
+ dns_transport_type_t transport_type;
dns_dtmsgtype_t dtmsgtype;
#endif /* ifdef HAVE_DNSTAP */
static const char *ra_reasons[] = {
@@ -2210,6 +2265,10 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
}
}
+#ifdef HAVE_DNSTAP
+ transport_type = ns_client_transport_type(client);
+#endif /* HAVE_DNSTAP */
+
/*
* Dispatch the request.
*/
@@ -2224,7 +2283,7 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
}
dns_dt_send(client->view, dtmsgtype, &client->peeraddr,
- &client->destsockaddr, TCP_CLIENT(client), NULL,
+ &client->destsockaddr, transport_type, NULL,
&client->requesttime, NULL, buffer);
#endif /* HAVE_DNSTAP */
@@ -2234,7 +2293,7 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
CTRACE("update");
#ifdef HAVE_DNSTAP
dns_dt_send(client->view, DNS_DTTYPE_UQ, &client->peeraddr,
- &client->destsockaddr, TCP_CLIENT(client), NULL,
+ &client->destsockaddr, transport_type, NULL,
&client->requesttime, NULL, buffer);
#endif /* HAVE_DNSTAP */
ns_client_settimeout(client, 60);
diff --git a/tests/dns/dnstap_test.c b/tests/dns/dnstap_test.c
index 8c82461b28..6d37979c3d 100644
--- a/tests/dns/dnstap_test.c
+++ b/tests/dns/dnstap_test.c
@@ -230,14 +230,20 @@ ISC_RUN_TEST_IMPL(dns_dt_send) {
break;
}
- dns_dt_send(view, dt, q, r, false, &zr, &p, &f, m);
- dns_dt_send(view, dt, q, r, false, &zr, NULL, &f, m);
- dns_dt_send(view, dt, q, r, false, &zr, &p, NULL, m);
- dns_dt_send(view, dt, q, r, false, &zr, NULL, NULL, m);
- dns_dt_send(view, dt, q, r, true, &zr, &p, &f, m);
- dns_dt_send(view, dt, q, r, true, &zr, NULL, &f, m);
- dns_dt_send(view, dt, q, r, true, &zr, &p, NULL, m);
- dns_dt_send(view, dt, q, r, true, &zr, NULL, NULL, m);
+ dns_dt_send(view, dt, q, r, DNS_TRANSPORT_UDP, &zr, &p, &f, m);
+ dns_dt_send(view, dt, q, r, DNS_TRANSPORT_UDP, &zr, NULL, &f,
+ m);
+ dns_dt_send(view, dt, q, r, DNS_TRANSPORT_UDP, &zr, &p, NULL,
+ m);
+ dns_dt_send(view, dt, q, r, DNS_TRANSPORT_UDP, &zr, NULL, NULL,
+ m);
+ dns_dt_send(view, dt, q, r, DNS_TRANSPORT_TCP, &zr, &p, &f, m);
+ dns_dt_send(view, dt, q, r, DNS_TRANSPORT_TCP, &zr, NULL, &f,
+ m);
+ dns_dt_send(view, dt, q, r, DNS_TRANSPORT_TCP, &zr, &p, NULL,
+ m);
+ dns_dt_send(view, dt, q, r, DNS_TRANSPORT_TCP, &zr, NULL, NULL,
+ m);
}
dns_dt_detach(&view->dtenv);