net/dns/resolver: add envknob to debug exit node DNS queries on on Windows

Add the envknob TS_DEBUG_EXIT_NODE_DNS_NET_PKG, which enables more
verbose debug logging when calling the handleExitNodeDNSQueryWithNetPkg
function. This function is currently only called on Windows and Android.

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: Ieb3ca7b98837d7dc69cd9ca47609c1c52e3afd7b
This commit is contained in:
Andrew Dunham 2023-02-03 11:25:31 -05:00
parent d2301db49c
commit 880a41bfcc
2 changed files with 35 additions and 5 deletions

View File

@ -23,6 +23,7 @@
"time"
dns "golang.org/x/net/dns/dnsmessage"
"tailscale.com/envknob"
"tailscale.com/net/dns/resolvconffile"
"tailscale.com/net/netaddr"
"tailscale.com/net/tsaddr"
@ -341,7 +342,7 @@ func (r *Resolver) HandleExitNodeDNSQuery(ctx context.Context, q []byte, from ne
default:
return nil, errors.New("unsupported exit node OS")
case "windows", "android":
return handleExitNodeDNSQueryWithNetPkg(ctx, nil, resp)
return handleExitNodeDNSQueryWithNetPkg(ctx, r.logf, nil, resp)
case "darwin":
// /etc/resolv.conf is a lie and only says one upstream DNS
// but for now that's probably good enough. Later we'll
@ -385,6 +386,8 @@ func (r *Resolver) HandleExitNodeDNSQuery(ctx context.Context, q []byte, from ne
}
}
var debugExitNodeDNSNetPkg = envknob.RegisterBool("TS_DEBUG_EXIT_NODE_DNS_NET_PKG")
// handleExitNodeDNSQueryWithNetPkg takes a DNS query message in q and
// return a reply (for the ExitDNS DoH service) using the net package's
// native APIs. This is only used on Windows for now.
@ -393,7 +396,8 @@ func (r *Resolver) HandleExitNodeDNSQuery(ctx context.Context, q []byte, from ne
//
// response contains the pre-serialized response, which notably
// includes the original question and its header.
func handleExitNodeDNSQueryWithNetPkg(ctx context.Context, resolver *net.Resolver, resp *response) (res []byte, err error) {
func handleExitNodeDNSQueryWithNetPkg(ctx context.Context, logf logger.Logf, resolver *net.Resolver, resp *response) (res []byte, err error) {
logf = logger.WithPrefix(logf, "exitNodeDNSQueryWithNetPkg: ")
if resp.Question.Class != dns.ClassINET {
return nil, errors.New("unsupported class")
}
@ -406,9 +410,16 @@ func handleExitNodeDNSQueryWithNetPkg(ctx context.Context, resolver *net.Resolve
handleError := func(err error) (res []byte, _ error) {
if isGoNoSuchHostError(err) {
if debugExitNodeDNSNetPkg() {
logf(`converting Go "no such host" error to a NXDOMAIN: %v`, err)
}
resp.Header.RCode = dns.RCodeNameError
return marshalResponse(resp)
}
if debugExitNodeDNSNetPkg() {
logf("returning error: %v", err)
}
// TODO: map other errors to RCodeServerFailure?
// Or I guess our caller should do that?
return nil, err
@ -422,6 +433,9 @@ func handleExitNodeDNSQueryWithNetPkg(ctx context.Context, resolver *net.Resolve
if resp.Question.Type == dns.TypeAAAA {
network = "ip6"
}
if debugExitNodeDNSNetPkg() {
logf("resolving %s %q", network, name)
}
ips, err := r.LookupIP(ctx, network, name)
if err != nil {
return handleError(err)
@ -432,6 +446,9 @@ func handleExitNodeDNSQueryWithNetPkg(ctx context.Context, resolver *net.Resolve
}
}
case dns.TypeTXT:
if debugExitNodeDNSNetPkg() {
logf("resolving TXT %q", name)
}
strs, err := r.LookupTXT(ctx, name)
if err != nil {
return handleError(err)
@ -443,6 +460,9 @@ func handleExitNodeDNSQueryWithNetPkg(ctx context.Context, resolver *net.Resolve
// TODO: is this RCodeFormatError?
return nil, errors.New("bogus PTR name")
}
if debugExitNodeDNSNetPkg() {
logf("resolving PTR %q", ipStr)
}
addrs, err := r.LookupAddr(ctx, ipStr)
if err != nil {
return handleError(err)
@ -451,12 +471,18 @@ func handleExitNodeDNSQueryWithNetPkg(ctx context.Context, resolver *net.Resolve
resp.Name, _ = dnsname.ToFQDN(addrs[0])
}
case dns.TypeCNAME:
if debugExitNodeDNSNetPkg() {
logf("resolving CNAME %q", name)
}
cname, err := r.LookupCNAME(ctx, name)
if err != nil {
return handleError(err)
}
resp.CNAME = cname
case dns.TypeSRV:
if debugExitNodeDNSNetPkg() {
logf("resolving SRV %q", name)
}
// Thanks, Go: "To accommodate services publishing SRV
// records under non-standard names, if both service
// and proto are empty strings, LookupSRV looks up
@ -467,6 +493,9 @@ func handleExitNodeDNSQueryWithNetPkg(ctx context.Context, resolver *net.Resolve
}
resp.SRVs = srvs
case dns.TypeNS:
if debugExitNodeDNSNetPkg() {
logf("resolving NS %q", name)
}
nss, err := r.LookupNS(ctx, name)
if err != nil {
return handleError(err)

View File

@ -10,6 +10,7 @@
"encoding/json"
"errors"
"fmt"
"log"
"math/rand"
"net"
"net/netip"
@ -1122,7 +1123,7 @@ func TestHandleExitNodeDNSQueryWithNetPkg(t *testing.T) {
}
t.Run("no_such_host", func(t *testing.T) {
res, err := handleExitNodeDNSQueryWithNetPkg(context.Background(), backResolver, &response{
res, err := handleExitNodeDNSQueryWithNetPkg(context.Background(), t.Logf, backResolver, &response{
Header: dnsmessage.Header{
ID: 123,
Response: true,
@ -1233,7 +1234,7 @@ func TestHandleExitNodeDNSQueryWithNetPkg(t *testing.T) {
for _, tt := range tests {
t.Run(fmt.Sprintf("%v_%v", tt.Type, strings.Trim(tt.Name, ".")), func(t *testing.T) {
got, err := handleExitNodeDNSQueryWithNetPkg(context.Background(), backResolver, &response{
got, err := handleExitNodeDNSQueryWithNetPkg(context.Background(), t.Logf, backResolver, &response{
Header: dnsmessage.Header{
ID: 123,
Response: true,
@ -1395,7 +1396,7 @@ func (a *wrapResolverConn) WriteTo(q []byte, _ net.Addr) (n int, err error) {
if resp == nil {
return 0, errors.New("bad query")
}
res, err := handleExitNodeDNSQueryWithNetPkg(context.Background(), a.r, resp)
res, err := handleExitNodeDNSQueryWithNetPkg(context.Background(), log.Printf, a.r, resp)
if err != nil {
return 0, err
}