perf(dot): implement connection pooling for improved performance

Implement TCP/TLS connection pooling for DoT resolver to match DoQ
performance. Previously, DoT created a new TCP/TLS connection for every
DNS query, incurring significant TLS handshake overhead. Now connections are
reused across queries, eliminating this overhead for subsequent requests.

The implementation follows the same pattern as DoQ, using parallel dialing
and connection pooling to achieve comparable performance characteristics.
This commit is contained in:
Cuong Manh Le
2026-01-08 20:00:15 +07:00
committed by Cuong Manh Le
parent 2e8a0f00a0
commit acbebcf7c2
6 changed files with 343 additions and 43 deletions
+20
View File
@@ -291,6 +291,9 @@ const hotCacheTTL = time.Second
// for a short period (currently 1 second), reducing unnecessary traffics
// sent to upstreams.
func (o *osResolver) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error) {
if err := validateMsg(msg); err != nil {
return nil, err
}
if len(msg.Question) == 0 {
return nil, errors.New("no question found")
}
@@ -509,6 +512,10 @@ type legacyResolver struct {
}
func (r *legacyResolver) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error) {
if err := validateMsg(msg); err != nil {
return nil, err
}
// See comment in (*dotResolver).resolve method.
dialer := newDialer(net.JoinHostPort(controldPublicDns, "53"))
dnsTyp := uint16(0)
@@ -534,6 +541,9 @@ func (r *legacyResolver) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, e
type dummyResolver struct{}
func (d dummyResolver) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error) {
if err := validateMsg(msg); err != nil {
return nil, err
}
ans := new(dns.Msg)
ans.SetReply(msg)
return ans, nil
@@ -769,3 +779,13 @@ func isLanAddr(addr netip.Addr) bool {
addr.IsLinkLocalUnicast() ||
tsaddr.CGNATRange().Contains(addr)
}
func validateMsg(msg *dns.Msg) error {
if msg == nil {
return errors.New("nil DNS message")
}
if len(msg.Question) == 0 {
return errors.New("no question found")
}
return nil
}