Skip to content

Connection leak in DNSCaching dual-stack happy-eyeballs dialing #758

@queelius

Description

@queelius

Bug Description

When DNSCaching is enabled and a hostname resolves to both IPv4 and IPv6 addresses, the DialContext closure dials both concurrently (happy-eyeballs style) and uses the first to succeed. However, when both dials succeed, the losing connection is silently discarded without being closed:

for i := 0; i < cap(ch); i++ {
    if r := <-ch; conn == nil {
        conn, err = r.conn, r.err
    }
    // BUG: if conn != nil and r.conn != nil, r.conn is leaked
}

The cancel() call on line 389 attempts to cancel the other dial, but there's a race: if both TCP connections have already been established before either goroutine calls cancel(), the second connection will be leaked.

Impact

On dual-stack hosts (very common), every DNS-cached request leaks one TCP connection. Under high load, this exhausts file descriptors and can cause too many open files errors.

Fix

Close the losing connection when the winning connection has already been selected:

for i := 0; i < cap(ch); i++ {
    r := <-ch
    if conn == nil {
        conn, err = r.conn, r.err
    } else if r.conn != nil {
        r.conn.Close()
    }
}

PR forthcoming.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions