c1b06a2d09
This diff has been extracted and adapted from 8848c8c516
The reason to prefer composition over embedding is that we want the
build to break if we add new methods to interfaces we define. If the build
does not break, we may forget about wrapping methods we should
actually be wrapping. I noticed this issue inside netxlite when I was working
on websteps-illustrated and I added support for NS and PTR queries.
See https://github.com/ooni/probe/issues/2096
While there, perform comprehensive netxlite code review
and apply minor changes and improve the docs.
76 lines
1.8 KiB
Go
76 lines
1.8 KiB
Go
package netxlite
|
|
|
|
//
|
|
// DNS-over-UDP transport
|
|
//
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
|
)
|
|
|
|
// DNSOverUDPTransport is a DNS-over-UDP DNSTransport.
|
|
type DNSOverUDPTransport struct {
|
|
dialer model.Dialer
|
|
address string
|
|
}
|
|
|
|
// NewDNSOverUDPTransport creates a DNSOverUDPTransport instance.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// - dialer is any type that implements the Dialer interface;
|
|
//
|
|
// - address is the endpoint address (e.g., 8.8.8.8:53).
|
|
func NewDNSOverUDPTransport(dialer model.Dialer, address string) *DNSOverUDPTransport {
|
|
return &DNSOverUDPTransport{dialer: dialer, address: address}
|
|
}
|
|
|
|
// RoundTrip sends a query and receives a reply.
|
|
func (t *DNSOverUDPTransport) RoundTrip(ctx context.Context, query []byte) ([]byte, error) {
|
|
conn, err := t.dialer.DialContext(ctx, "udp", t.address)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer conn.Close()
|
|
// Use five seconds timeout like Bionic does. See
|
|
// https://labs.ripe.net/Members/baptiste_jonglez_1/persistent-dns-connections-for-reliability-and-performance
|
|
if err = conn.SetDeadline(time.Now().Add(5 * time.Second)); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err = conn.Write(query); err != nil {
|
|
return nil, err
|
|
}
|
|
reply := make([]byte, 1<<17)
|
|
var n int
|
|
n, err = conn.Read(reply)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return reply[:n], nil
|
|
}
|
|
|
|
// RequiresPadding returns false for UDP according to RFC8467.
|
|
func (t *DNSOverUDPTransport) RequiresPadding() bool {
|
|
return false
|
|
}
|
|
|
|
// Network returns the transport network, i.e., "udp".
|
|
func (t *DNSOverUDPTransport) Network() string {
|
|
return "udp"
|
|
}
|
|
|
|
// Address returns the upstream server address.
|
|
func (t *DNSOverUDPTransport) Address() string {
|
|
return t.address
|
|
}
|
|
|
|
// CloseIdleConnections closes idle connections, if any.
|
|
func (t *DNSOverUDPTransport) CloseIdleConnections() {
|
|
// nothing to do
|
|
}
|
|
|
|
var _ model.DNSTransport = &DNSOverUDPTransport{}
|