Skip to content

Non-IP DNS queries time out when using DNS proxy with default nonIPQuery setting #3525

@darren

Description

@darren

In the proxy/dns outbound, the nonIPQuery configuration was introduced in PR #2954 to control the behavior for DNS queries other than A or AAAA records (e.g., MX, SOA, TXT).

However, there are some questions in the current implementation. When nonIPQuery is set to "skip" or is left empty, non-IP queries are incorrectly processed, leading to client-side timeouts.

v2ray-core/proxy/dns/dns.go

Lines 205 to 218 in 8eef1bb

if !h.isOwnLink(ctx) {
isIPQuery, domain, id, qType := parseIPQuery(b.Bytes())
if isIPQuery || h.nonIPQuery != "drop" {
if domain, err := strmatcher.ToDomain(domain); err == nil {
go h.handleIPQuery(id, qType, domain, writer)
} else {
h.handleDNSError(id, dnsmessage.RCodeFormatError, writer)
}
} else {
h.handleDNSError(id, dnsmessage.RCodeNotImplemented, writer)
}
} else if err := connWriter.WriteMessage(b); err != nil {
return err
}

  1. For a non-IP query (e.g., an MX record lookup), parseIPQuery returns isIPQuery = false. Crucially, it also returns an empty string for the domain variable in this case.
  2. The condition isIPQuery || h.nonIPQuery != "drop" evaluates to true when nonIPQuery is not "drop".
  3. The code then calls strmatcher.ToDomain(""), which successfully returns an empty string and a nil error.
  4. This causes the code to enter the if block and call go h.handleIPQuery(...) with an empty domain multiple times
  5. Inside handleIPQuery, the call to h.ipv4Lookup.LookupIPv4("") (or ipv6Lookup) immediately returns an "empty domain name" error from the core DNS module. handleIPQuery then logs ip query and executes a return statement without sending any response back to the client.
  6. Because no DNS response (neither an answer nor an error code) is ever sent, the original DNS client's request will eventually time out.

#2954 (comment) The intended behavior for "skip" is to forward the original DNS query packet to the upstream server . This should be handled by connWriter.WriteMessage(b), but this code path is never reached for external non-IP queries.

@rp-hello can you shed some light on this change of code path?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions