net/dns, net/dns/resolver, wgengine: refactor DNS request path (#4364)

* net/dns, net/dns/resolver, wgengine: refactor DNS request path

Previously, method calls into the DNS manager/resolver types handled DNS
requests rather than DNS packets. This is fine for UDP as one packet
corresponds to one request or response, however will not suit an
implementation that supports DNS over TCP.

To support PRs implementing this in the future, wgengine delegates
all handling/construction of packets to the magic DNS endpoint, to
the DNS types themselves. Handling IP packets at this level enables
future support for both UDP and TCP.

Signed-off-by: Tom DNetto <tom@tailscale.com>
This commit is contained in:
Tom
2022-04-08 12:17:31 -07:00
committed by GitHub
parent 3b3d1b9350
commit 24bdcbe5c7
4 changed files with 97 additions and 57 deletions

View File

@@ -487,23 +487,22 @@ func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper)
// handleDNS is an outbound pre-filter resolving Tailscale domains.
func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.Wrapper) filter.Response {
if p.Dst.Port() == magicDNSPort && p.IPProto == ipproto.UDP {
switch p.Dst.IP() {
case magicDNSIP, magicDNSIPv6:
err := e.dns.EnqueueRequest(append([]byte(nil), p.Payload()...), p.Src)
if err != nil {
e.logf("dns: enqueue: %v", err)
}
return filter.Drop
switch p.Dst.IP() {
case magicDNSIP, magicDNSIPv6:
err := e.dns.EnqueuePacket(append([]byte(nil), p.Payload()...), p.IPProto, p.Src, p.Dst)
if err != nil {
e.logf("dns: enqueue: %v", err)
}
return filter.Drop
default:
return filter.Accept
}
return filter.Accept
}
// pollResolver reads responses from the DNS resolver and injects them inbound.
// pollResolver reads packets from the DNS resolver and injects them inbound.
func (e *userspaceEngine) pollResolver() {
for {
bs, to, err := e.dns.NextResponse()
bs, err := e.dns.NextPacket()
if err == resolver.ErrClosed {
return
}
@@ -512,39 +511,9 @@ func (e *userspaceEngine) pollResolver() {
continue
}
var buf []byte
const offset = tstun.PacketStartOffset
switch {
case to.IP().Is4():
h := packet.UDP4Header{
IP4Header: packet.IP4Header{
Src: magicDNSIP,
Dst: to.IP(),
},
SrcPort: magicDNSPort,
DstPort: to.Port(),
}
hlen := h.Len()
// TODO(dmytro): avoid this allocation without importing tstun quirks into dns.
buf = make([]byte, offset+hlen+len(bs))
copy(buf[offset+hlen:], bs)
h.Marshal(buf[offset:])
case to.IP().Is6():
h := packet.UDP6Header{
IP6Header: packet.IP6Header{
Src: magicDNSIPv6,
Dst: to.IP(),
},
SrcPort: magicDNSPort,
DstPort: to.Port(),
}
hlen := h.Len()
// TODO(dmytro): avoid this allocation without importing tstun quirks into dns.
buf = make([]byte, offset+hlen+len(bs))
copy(buf[offset+hlen:], bs)
h.Marshal(buf[offset:])
}
e.tundev.InjectInboundDirect(buf, offset)
// The leading empty space required by the semantics of
// InjectInboundDirect is allocated in NextPacket().
e.tundev.InjectInboundDirect(bs, tstun.PacketStartOffset)
}
}