mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-11 21:27:31 +00:00
net/dns/resolver, ipn/ipnlocal: wire up peerapi DoH server to DNS forwarder
Updates #1713 Change-Id: Ia4ed9d8c9cef0e70aa6d30f2852eaab80f5f695a Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:

committed by
Brad Fitzpatrick

parent
9bb91cb977
commit
25525b7754
@@ -546,8 +546,26 @@ type forwardQuery struct {
|
||||
// ...
|
||||
}
|
||||
|
||||
// forward forwards the query to all upstream nameservers and returns the first response.
|
||||
// forward forwards the query to all upstream nameservers and waits for
|
||||
// the first response.
|
||||
//
|
||||
// It either sends to f.responses and returns nil, or returns a
|
||||
// non-nil error (without sending to the channel).
|
||||
func (f *forwarder) forward(query packet) error {
|
||||
ctx, cancel := context.WithTimeout(f.ctx, responseTimeout)
|
||||
defer cancel()
|
||||
return f.forwardWithDestChan(ctx, query, f.responses)
|
||||
}
|
||||
|
||||
// forward forwards the query to all upstream nameservers and waits
|
||||
// for the first response.
|
||||
//
|
||||
// It either sends to responseChan and returns nil, or returns a
|
||||
// non-nil error (without sending to the channel).
|
||||
//
|
||||
// If backupResolvers are specified, they're used in the case that no
|
||||
// upstreams are available.
|
||||
func (f *forwarder) forwardWithDestChan(ctx context.Context, query packet, responseChan chan<- packet, backupResolvers ...resolverAndDelay) error {
|
||||
domain, err := nameFromQuery(query.bs)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -564,6 +582,9 @@ func (f *forwarder) forward(query packet) error {
|
||||
clampEDNSSize(query.bs, maxResponseBytes)
|
||||
|
||||
resolvers := f.resolvers(domain)
|
||||
if len(resolvers) == 0 {
|
||||
resolvers = backupResolvers
|
||||
}
|
||||
if len(resolvers) == 0 {
|
||||
return errNoUpstreams
|
||||
}
|
||||
@@ -575,9 +596,6 @@ func (f *forwarder) forward(query packet) error {
|
||||
}
|
||||
defer fq.closeOnCtxDone.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(f.ctx, responseTimeout)
|
||||
defer cancel()
|
||||
|
||||
resc := make(chan []byte, 1)
|
||||
var (
|
||||
mu sync.Mutex
|
||||
@@ -616,7 +634,7 @@ func (f *forwarder) forward(query packet) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case f.responses <- packet{v, query.addr}:
|
||||
case responseChan <- packet{v, query.addr}:
|
||||
return nil
|
||||
}
|
||||
case <-ctx.Done():
|
||||
|
@@ -8,6 +8,7 @@ package resolver
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -298,6 +299,58 @@ func (r *Resolver) NextResponse() (packet []byte, to netaddr.IPPort, err error)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleExitNodeDNSQuery handles a DNS query that arrived from a peer
|
||||
// via the peerapi's DoH server. This is only used when the local
|
||||
// node is being an exit node.
|
||||
func (r *Resolver) HandleExitNodeDNSQuery(ctx context.Context, q []byte, from netaddr.IPPort) (res []byte, err error) {
|
||||
ch := make(chan packet, 1)
|
||||
|
||||
err = r.forwarder.forwardWithDestChan(ctx, packet{q, from}, ch)
|
||||
if err == errNoUpstreams {
|
||||
// Handle to the system resolver.
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
// Assume for now that we don't have an upstream because
|
||||
// they're using systemd-resolved and we're in Split DNS mode
|
||||
// where we don't know the base config.
|
||||
//
|
||||
// TODO(bradfitz): this is a lazy assumption. Do better, and
|
||||
// maybe move the HandleExitNodeDNSQuery method to the dns.Manager
|
||||
// instead? But this works for now.
|
||||
err = r.forwarder.forwardWithDestChan(ctx, packet{q, from}, ch, resolverAndDelay{
|
||||
name: dnstype.Resolver{
|
||||
Addr: "127.0.0.1:53",
|
||||
},
|
||||
})
|
||||
default:
|
||||
// TODO(bradfitz): if we're on an exit node
|
||||
// on, say, Windows, we need to parse the DNS
|
||||
// packet in q and call OS-native APIs for
|
||||
// each question. But we'll want to strip out
|
||||
// questions for MagicDNS names probably, so
|
||||
// they don't loop back into
|
||||
// 100.100.100.100. We don't want to resolve
|
||||
// MagicDNS names across Tailnets once we
|
||||
// permit sharing exit nodes.
|
||||
//
|
||||
// For now, just return an error.
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
select {
|
||||
case p, ok := <-ch:
|
||||
if ok {
|
||||
return p.bs, nil
|
||||
}
|
||||
panic("unexpected close chan")
|
||||
default:
|
||||
panic("unexpected unreadable chan")
|
||||
}
|
||||
}
|
||||
|
||||
// resolveLocal returns an IP for the given domain, if domain is in
|
||||
// the local hosts map and has an IP corresponding to the requested
|
||||
// typ (A, AAAA, ALL).
|
||||
|
Reference in New Issue
Block a user