commit 66267fa01f36cd99f4b3ac53250a1b8cbd3e2568
parent 1bcf9fa40865b1840d67029d990dcbfe61a43a23
Author: Oliver Lowe <o@olowe.co>
Date: Tue, 16 Nov 2021 20:01:34 +1100
Support making DNS queries over TLS
Little gotcha is that TCP messages need to be prepended with
a 2-octet length field!
Diffstat:
3 files changed, 45 insertions(+), 8 deletions(-)
diff --git a/dns.go b/dns.go
@@ -1,6 +1,10 @@
package dns
import (
+ "crypto/tls"
+ "encoding/binary"
+ "fmt"
+ "io"
"net"
"golang.org/x/net/dns/dnsmessage"
@@ -21,22 +25,53 @@ func Exchange(msg dnsmessage.Message, addr string) (dnsmessage.Message, error) {
return send(msg, conn)
}
+// ExchangeTLS performs a synchronous DNS-over-TLS exchange with addr and returns its
+// reply to msg.
+func ExchangeTLS(msg dnsmessage.Message, addr string) (dnsmessage.Message, error) {
+ conn, err := tls.Dial("tcp", addr, nil)
+ if err != nil {
+ return dnsmessage.Message{}, err
+ }
+ defer conn.Close()
+ return send(msg, conn)
+}
+
func send(msg dnsmessage.Message, conn net.Conn) (dnsmessage.Message, error) {
packed, err := msg.Pack()
if err != nil {
return dnsmessage.Message{}, err
}
- if _, err := conn.Write(packed); err != nil {
- return dnsmessage.Message{}, err
+ if _, ok := conn.(net.PacketConn); ok {
+ if _, err = conn.Write(packed); err != nil {
+ return dnsmessage.Message{}, err
+ }
+ } else {
+ // DNS over TCP requires you to prepend the message with a
+ // 2-octet length field.
+ m := make([]byte, 2+len(packed))
+ binary.BigEndian.PutUint16(m, uint16(len(packed)))
+ copy(m[2:], packed)
+ if _, err = conn.Write(m); err != nil {
+ return dnsmessage.Message{}, err
+ }
}
buf := make([]byte, 1024)
n, err := conn.Read(buf)
- if err != nil {
+ if err != nil && err != io.EOF {
return dnsmessage.Message{}, err
}
+ if n == 0 {
+ return dnsmessage.Message{}, fmt.Errorf("empty response")
+ }
var rmsg dnsmessage.Message
- if err := rmsg.Unpack(buf[:n]); err != nil {
- return dnsmessage.Message{}, err
+ if _, ok := conn.(net.PacketConn); ok {
+ if err := rmsg.Unpack(buf[:n]); err != nil {
+ return dnsmessage.Message{}, fmt.Errorf("parse response: %v", err)
+ }
+ } else {
+ if err := rmsg.Unpack(buf[2:n]); err != nil {
+ return dnsmessage.Message{}, fmt.Errorf("parse response: %v", err)
+ }
}
return rmsg, nil
}
diff --git a/go.mod b/go.mod
@@ -3,7 +3,8 @@ module git.sr.ht/~otl/dns
go 1.17
require (
- golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect
- golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
- golang.org/x/text v0.3.3 // indirect
+ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa
+ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
)
+
+require golang.org/x/text v0.3.3 // indirect
diff --git a/go.sum b/go.sum
@@ -3,6 +3,7 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=