Merge 84df34f86a
into 5d4b4ffc3c
This commit is contained in:
commit
405a5a1512
|
@ -548,6 +548,8 @@ func getLocalBackend(ctx context.Context, logf logger.Logf, logID logid.PublicID
|
|||
}
|
||||
}
|
||||
if socksListener != nil || httpProxyListener != nil {
|
||||
dialer.UserDialCustomResolver = dns.Quad100Resolver(ctx, sys.DNSManager.Get())
|
||||
|
||||
var addrs []string
|
||||
if httpProxyListener != nil {
|
||||
hs := &http.Server{Handler: httpProxyHandler(dialer.UserDial)}
|
||||
|
|
|
@ -93,6 +93,12 @@ func (m *Manager) Set(cfg Config) error {
|
|||
if err := m.resolver.SetConfig(rcfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
m.logf("err: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.os.SetDNS(ocfg); err != nil {
|
||||
health.SetDNSOSHealth(err)
|
||||
return err
|
||||
|
@ -249,7 +255,7 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig
|
|||
// builds.
|
||||
} else {
|
||||
health.SetDNSOSHealth(err)
|
||||
return resolver.Config{}, OSConfig{}, err
|
||||
return rcfg, OSConfig{}, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Quad100conn struct {
|
||||
Ctx context.Context
|
||||
DnsManager *Manager
|
||||
|
||||
rbuf bytes.Buffer
|
||||
}
|
||||
|
||||
var (
|
||||
_ net.Conn = (*Quad100conn)(nil)
|
||||
_ net.PacketConn = (*Quad100conn)(nil) // be a PacketConn to change net.Resolver semantics
|
||||
)
|
||||
|
||||
func (*Quad100conn) Close() error { return nil }
|
||||
func (*Quad100conn) LocalAddr() net.Addr { return todoAddr{} }
|
||||
func (*Quad100conn) RemoteAddr() net.Addr { return todoAddr{} }
|
||||
func (*Quad100conn) SetDeadline(t time.Time) error { return nil }
|
||||
func (*Quad100conn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (*Quad100conn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
|
||||
func (c *Quad100conn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
return c.Write(p)
|
||||
}
|
||||
|
||||
func (c *Quad100conn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
n, err = c.Read(p)
|
||||
return n, todoAddr{}, err
|
||||
}
|
||||
|
||||
func (c *Quad100conn) Read(p []byte) (n int, err error) {
|
||||
return c.rbuf.Read(p)
|
||||
}
|
||||
|
||||
func (c *Quad100conn) Write(packet []byte) (n int, err error) {
|
||||
pkt, err := c.DnsManager.Query(c.Ctx, packet, "tcp", netip.AddrPort{})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
c.rbuf.Write(pkt)
|
||||
return len(packet), nil
|
||||
}
|
||||
|
||||
type todoAddr struct{}
|
||||
|
||||
func (todoAddr) Network() string { return "unused" }
|
||||
func (todoAddr) String() string { return "unused-todoAddr" }
|
||||
|
||||
// Quad100Resolver sets up a DNS resolver that uses the Quad100Conn to send DNS
|
||||
// queries to MagicDNS.
|
||||
func Quad100Resolver(ctx context.Context, mgr *Manager) func(host string) (netip.Addr, error) {
|
||||
return func(host string) (netip.Addr, error) {
|
||||
var r net.Resolver
|
||||
r.PreferGo = true
|
||||
r.Dial = func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return &Quad100conn{
|
||||
Ctx: ctx,
|
||||
DnsManager: mgr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
ips, err := r.LookupIP(ctx, "ip4", host)
|
||||
if err != nil {
|
||||
mgr.logf("dns lookup err: %v", err)
|
||||
return netip.Addr{}, err
|
||||
}
|
||||
if len(ips) == 0 {
|
||||
return netip.Addr{}, fmt.Errorf("DNS lookup returned no results for %q", host)
|
||||
}
|
||||
ip, _ := netip.AddrFromSlice(ips[0])
|
||||
return ip, nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
dns "golang.org/x/net/dns/dnsmessage"
|
||||
"tailscale.com/net/tsdial"
|
||||
"tailscale.com/util/dnsname"
|
||||
)
|
||||
|
||||
func TestQuad100Conn(t *testing.T) {
|
||||
f := fakeOSConfigurator{
|
||||
SplitDNS: true,
|
||||
BaseConfig: OSConfig{
|
||||
Nameservers: mustIPs("8.8.8.8"),
|
||||
SearchDomains: fqdns("coffee.shop"),
|
||||
},
|
||||
}
|
||||
m := NewManager(t.Logf, &f, nil, new(tsdial.Dialer), nil, nil)
|
||||
m.resolver.TestOnlySetHook(f.SetResolver)
|
||||
m.Set(Config{
|
||||
Hosts: hosts(
|
||||
"dave.ts.net.", "1.2.3.4",
|
||||
"matt.ts.net.", "2.3.4.5"),
|
||||
Routes: upstreams("ts.net", ""),
|
||||
SearchDomains: fqdns("tailscale.com", "universe.tf"),
|
||||
})
|
||||
defer m.Down()
|
||||
|
||||
q100 := &Quad100conn{
|
||||
Ctx: context.Background(),
|
||||
DnsManager: m,
|
||||
}
|
||||
defer q100.Close()
|
||||
|
||||
var b []byte
|
||||
domain := dnsname.FQDN("matt.ts.net.")
|
||||
|
||||
// Send a query
|
||||
b = mkDNSRequest(domain, dns.TypeA, addEDNS)
|
||||
_, err := q100.Write(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
resp := make([]byte, 100)
|
||||
if _, err := q100.Read(resp); err != nil {
|
||||
t.Fatalf("reading data: %v", err)
|
||||
}
|
||||
|
||||
var parser dns.Parser
|
||||
if _, err := parser.Start(resp); err != nil {
|
||||
t.Errorf("parser.Start() failed: %v", err)
|
||||
}
|
||||
_, err = parser.Question()
|
||||
if err != nil {
|
||||
t.Errorf("parser.Question(): %v", err)
|
||||
}
|
||||
if err := parser.SkipAllQuestions(); err != nil {
|
||||
t.Errorf("parser.SkipAllQuestions(): %v", err)
|
||||
}
|
||||
ah, err := parser.AnswerHeader()
|
||||
if err != nil {
|
||||
t.Errorf("parser.AnswerHeader(): %v", err)
|
||||
}
|
||||
if ah.Type != dns.TypeA {
|
||||
t.Errorf("unexpected answer type: got %v, want %v", ah.Type, dns.TypeA)
|
||||
}
|
||||
res, err := parser.AResource()
|
||||
if err != nil {
|
||||
t.Errorf("parser.AResource(): %v", err)
|
||||
}
|
||||
if net.IP(res.A[:]).String() != "2.3.4.5" {
|
||||
t.Fatalf("dns query did not return expected result")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestQuad100Resolver(t *testing.T) {
|
||||
f := fakeOSConfigurator{
|
||||
SplitDNS: true,
|
||||
BaseConfig: OSConfig{
|
||||
Nameservers: mustIPs("8.8.8.8"),
|
||||
SearchDomains: fqdns("coffee.shop"),
|
||||
},
|
||||
}
|
||||
m := NewManager(t.Logf, &f, nil, new(tsdial.Dialer), nil, nil)
|
||||
m.resolver.TestOnlySetHook(f.SetResolver)
|
||||
m.Set(Config{
|
||||
Hosts: hosts(
|
||||
"dave.ts.net.", "1.2.3.4",
|
||||
"matt.ts.net.", "2.3.4.5"),
|
||||
Routes: upstreams("ts.net", ""),
|
||||
SearchDomains: fqdns("tailscale.com", "universe.tf"),
|
||||
})
|
||||
defer m.Down()
|
||||
|
||||
resolver := Quad100Resolver(context.Background(), m)
|
||||
|
||||
ip, err := resolver("matt.ts.net")
|
||||
if err != nil {
|
||||
t.Errorf("could not resolve host: %v", err)
|
||||
}
|
||||
|
||||
if ip.String() != "2.3.4.5" {
|
||||
t.Fatalf("dns query did not return expected result")
|
||||
}
|
||||
|
||||
}
|
|
@ -720,6 +720,8 @@ func (f *forwarder) sendTCP(ctx context.Context, fq *forwardQuery, rr resolverAn
|
|||
ctx, cancel := context.WithTimeout(ctx, tcpQueryTimeout)
|
||||
defer cancel()
|
||||
|
||||
// Keeping this as SystemDial per discussion in https://github.com/tailscale/tailscale/pull/10380
|
||||
// This would mean SplitDNS via upstreams only reachable via UserDial would not work currently.
|
||||
conn, err := f.dialer.SystemDial(ctx, tcpFam, ipp.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -43,6 +43,10 @@ type Dialer struct {
|
|||
// If nil, it's not used.
|
||||
NetstackDialTCP func(context.Context, netip.AddrPort) (net.Conn, error)
|
||||
|
||||
// UserDialCustomResolver if non-nil is invoked by UserDial to resolve a destination address.
|
||||
// It is invoked after the in-memory tailnet machine map.
|
||||
UserDialCustomResolver func(string) (netip.Addr, error)
|
||||
|
||||
peerClientOnce sync.Once
|
||||
peerClient *http.Client
|
||||
|
||||
|
@ -255,16 +259,25 @@ func (d *Dialer) userDialResolve(ctx context.Context, network, addr string) (net
|
|||
return ipp, err
|
||||
}
|
||||
|
||||
// Otherwise, hit the network.
|
||||
|
||||
// TODO(bradfitz): wire up net/dnscache too.
|
||||
|
||||
// Try tsdns resolver next to resolve SplitDNS
|
||||
host, port, err := splitHostPort(addr)
|
||||
if err != nil {
|
||||
// addr is malformed.
|
||||
return netip.AddrPort{}, err
|
||||
}
|
||||
|
||||
if d.UserDialCustomResolver != nil {
|
||||
ip, err := d.UserDialCustomResolver(host)
|
||||
if err == nil {
|
||||
ipp := netip.AddrPortFrom(ip, port)
|
||||
return ipp, err
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, hit the network.
|
||||
|
||||
var r net.Resolver
|
||||
if exitDNSDoH != "" && runtime.GOOS != "windows" { // Windows: https://github.com/golang/go/issues/33097
|
||||
r.PreferGo = true
|
||||
|
|
Loading…
Reference in New Issue