mirror of
https://github.com/folbricht/routedns.git
synced 2025-12-19 08:29:50 -06:00
* Add metric tracking * Clean up metrics structures. * Add available route metric to router.
84 lines
2.2 KiB
Go
84 lines
2.2 KiB
Go
package rdns
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"net"
|
|
|
|
"github.com/miekg/dns"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// DoTClient is a DNS-over-TLS resolver.
|
|
type DoTClient struct {
|
|
id string
|
|
endpoint string
|
|
pipeline *Pipeline
|
|
// Pipeline also provides operation metrics.
|
|
}
|
|
|
|
// DoTClientOptions contains options used by the DNS-over-TLS resolver.
|
|
type DoTClientOptions struct {
|
|
// Bootstrap address - IP to use for the serivce instead of looking up
|
|
// the service's hostname with potentially plain DNS.
|
|
BootstrapAddr string
|
|
|
|
// Local IP to use for outbound connections. If nil, a local address is chosen.
|
|
LocalAddr net.IP
|
|
|
|
TLSConfig *tls.Config
|
|
}
|
|
|
|
var _ Resolver = &DoTClient{}
|
|
|
|
// NewDoTClient instantiates a new DNS-over-TLS resolver.
|
|
func NewDoTClient(id, endpoint string, opt DoTClientOptions) (*DoTClient, error) {
|
|
if err := validEndpoint(endpoint); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Use a custom dialer if a local address was provided
|
|
var dialer *net.Dialer
|
|
if opt.LocalAddr != nil {
|
|
dialer = &net.Dialer{LocalAddr: &net.TCPAddr{IP: opt.LocalAddr}}
|
|
}
|
|
client := &dns.Client{
|
|
Net: "tcp-tls",
|
|
TLSConfig: opt.TLSConfig,
|
|
Dialer: dialer,
|
|
}
|
|
// If a bootstrap address was provided, we need to use the IP for the connection but the
|
|
// hostname in the TLS handshake. The DNS library doesn't support custom dialers, so
|
|
// instead set the ServerName in the TLS config to the name in the endpoint config, and
|
|
// replace the name in the endpoint with the bootstrap IP.
|
|
if opt.BootstrapAddr != "" {
|
|
host, port, err := net.SplitHostPort(endpoint)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to parse dot endpoint '%s'", endpoint)
|
|
}
|
|
client.TLSConfig.ServerName = host
|
|
endpoint = net.JoinHostPort(opt.BootstrapAddr, port)
|
|
}
|
|
return &DoTClient{
|
|
id: id,
|
|
endpoint: endpoint,
|
|
pipeline: NewPipeline(id, endpoint, client),
|
|
}, nil
|
|
}
|
|
|
|
// Resolve a DNS query.
|
|
func (d *DoTClient) Resolve(q *dns.Msg, ci ClientInfo) (*dns.Msg, error) {
|
|
logger(d.id, q, ci).WithFields(logrus.Fields{
|
|
"resolver": d.endpoint,
|
|
"protocol": "dot",
|
|
}).Debug("querying upstream resolver")
|
|
|
|
// Add padding to the query before sending over TLS
|
|
padQuery(q)
|
|
return d.pipeline.Resolve(q)
|
|
}
|
|
|
|
func (d *DoTClient) String() string {
|
|
return d.id
|
|
}
|