Prefix DoQ packets with length as per rfc9250

This commit is contained in:
folbrich
2022-08-16 11:28:25 +02:00
parent dd823da5ad
commit 6bb93ef4f9
3 changed files with 40 additions and 12 deletions

View File

@@ -8,7 +8,7 @@ Features:
- Support for DNS-over-TLS (DoT, [RFC7858](https://tools.ietf.org/html/rfc7858)), client and server
- Support for DNS-over-HTTPS (DoH, [RFC8484](https://tools.ietf.org/html/rfc8484)), client and server with HTTP2
- Support for DNS-over-QUIC (doq-i02, [draft-ietf-dprive-dnsoquic-02](https://datatracker.ietf.org/doc/html/draft-ietf-dprive-dnsoquic)), client and server
- Support for DNS-over-QUIC (DoQ, [RFC9250](https://datatracker.ietf.org/doc/rfc9250/)), client and server
- Support for DNS-over-DTLS ([RFC8094](https://tools.ietf.org/html/rfc8094)), client and server
- DNS-over-HTTPS using a QUIC transport, client and server
- Custom CAs and mutual-TLS
@@ -81,7 +81,7 @@ RouteDNS supports building complex DNS processing pipelines. A typically configu
## QUIC support
Support for the QUIC protocol is still experimental. In the context of DNS, there are two implementations, DNS-over-QUIC ([draft-ietf-dprive-dnsoquic-02](https://datatracker.ietf.org/doc/html/draft-ietf-dprive-dnsoquic)) as well as DNS-over-HTTPS using QUIC. Both protocols are supported by RouteDNS, client and server implementations.
Support for the QUIC protocol is still experimental. In the context of DNS, there are two implementations, DNS-over-QUIC (DoQ, [RFC9250](https://datatracker.ietf.org/doc/rfc9250/)) as well as DNS-over-HTTPS using QUIC. Both protocols are supported by RouteDNS, client and server implementations.
## Use-cases / Examples

View File

@@ -2,7 +2,8 @@ package rdns
import (
"crypto/tls"
"io/ioutil"
"encoding/binary"
"io"
"net"
"sync"
"time"
@@ -123,12 +124,17 @@ func (d *DoQClient) Resolve(q *dns.Msg, ci ClientInfo) (*dns.Msg, error) {
defer func() { q.Id = id }()
// Encode the query
b, err := q.Pack()
p, err := q.Pack()
if err != nil {
d.metrics.err.Add("pack", 1)
return nil, err
}
// Add a length prefix
b := make([]byte, 2+len(p))
binary.BigEndian.PutUint16(b, uint16(len(p)))
copy(b[2:], p)
// Get a new stream in the connection
stream, err := d.connection.getStream()
if err != nil {
@@ -138,7 +144,7 @@ func (d *DoQClient) Resolve(q *dns.Msg, ci ClientInfo) (*dns.Msg, error) {
// Write the query into the stream and close is. Only one stream per query/response
_ = stream.SetWriteDeadline(time.Now().Add(time.Second))
if _, err = stream.Write(b); err != nil {
if _, err = stream.Write(p); err != nil {
d.metrics.err.Add("write", 1)
return nil, err
}
@@ -147,10 +153,18 @@ func (d *DoQClient) Resolve(q *dns.Msg, ci ClientInfo) (*dns.Msg, error) {
return nil, err
}
// Read the response
_ = stream.SetReadDeadline(time.Now().Add(time.Second))
b, err = ioutil.ReadAll(stream)
if err != nil {
// DoQ requires a length prefix, like TCP
var length uint16
if err := binary.Read(stream, binary.BigEndian, &length); err != nil {
d.metrics.err.Add("read", 1)
return nil, err
}
// Read the response
b = make([]byte, length)
if _, err = io.ReadFull(stream, b); err != nil {
d.metrics.err.Add("read", 1)
return nil, err
}

View File

@@ -3,8 +3,9 @@ package rdns
import (
"context"
"crypto/tls"
"encoding/binary"
"expvar"
"io/ioutil"
"io"
"net"
"time"
@@ -142,10 +143,18 @@ func (s DoQListener) handleStream(stream quic.Stream, log *logrus.Entry, ci Clie
defer stream.Close()
s.metrics.stream.Add(1)
// DoQ requires a length prefix, like TCP
var length uint16
if err := binary.Read(stream, binary.BigEndian, &length); err != nil {
s.metrics.err.Add("read", 1)
log.WithError(err).Error("failed to read query")
return
}
// Read the raw query
b := make([]byte, length)
_ = stream.SetReadDeadline(time.Now().Add(time.Second)) // TODO: configurable timeout
b, err := ioutil.ReadAll(stream)
if err != nil {
if _, err := io.ReadFull(stream, b); err != nil {
s.metrics.err.Add("read", 1)
log.WithError(err).Error("failed to read query")
return
@@ -182,13 +191,18 @@ func (s DoQListener) handleStream(stream quic.Stream, log *logrus.Entry, ci Clie
a.SetRcode(q, dns.RcodeServerFailure)
}
out, err := a.Pack()
p, err := a.Pack()
if err != nil {
log.WithError(err).Error("failed to encode response")
s.metrics.err.Add("encode", 1)
return
}
// Add a length prefix
out := make([]byte, 2+len(p))
binary.BigEndian.PutUint16(out, uint16(len(p)))
copy(out[2:], p)
// Send the response
_ = stream.SetWriteDeadline(time.Now().Add(time.Second)) // TODO: configurable timeout
if _, err = stream.Write(out); err != nil {