mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-07-04 01:07:49 +02:00
fix(dot): validate connections before reuse to prevent io.EOF errors
Add connection health check in getConn to validate TLS connections before reusing them from the pool. This prevents io.EOF errors when reusing connections that were closed by the server (e.g., due to idle timeout).
This commit is contained in:
committed by
Cuong Manh Le
parent
209c9211b9
commit
da3ea05763
@@ -48,7 +48,7 @@ type dotConnPool struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type dotConn struct {
|
type dotConn struct {
|
||||||
conn net.Conn
|
conn *tls.Conn
|
||||||
lastUsed time.Time
|
lastUsed time.Time
|
||||||
refCount int
|
refCount int
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
@@ -105,13 +105,6 @@ func (p *dotConnPool) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, erro
|
|||||||
return nil, wrapCertificateVerificationError(err)
|
return nil, wrapCertificateVerificationError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set deadline
|
|
||||||
deadline, ok := ctx.Deadline()
|
|
||||||
if !ok {
|
|
||||||
deadline = time.Now().Add(5 * time.Second)
|
|
||||||
}
|
|
||||||
_ = conn.SetDeadline(deadline)
|
|
||||||
|
|
||||||
client := dns.Client{Net: "tcp-tls"}
|
client := dns.Client{Net: "tcp-tls"}
|
||||||
answer, _, err := client.ExchangeWithConnContext(ctx, msg, &dns.Conn{Conn: conn})
|
answer, _, err := client.ExchangeWithConnContext(ctx, msg, &dns.Conn{Conn: conn})
|
||||||
isGood := err == nil
|
isGood := err == nil
|
||||||
@@ -136,7 +129,7 @@ func (p *dotConnPool) getConn(ctx context.Context) (net.Conn, string, error) {
|
|||||||
// Try to reuse an existing connection
|
// Try to reuse an existing connection
|
||||||
for addr, dotConn := range p.conns {
|
for addr, dotConn := range p.conns {
|
||||||
dotConn.mu.Lock()
|
dotConn.mu.Lock()
|
||||||
if dotConn.refCount == 0 && dotConn.conn != nil {
|
if dotConn.refCount == 0 && dotConn.conn != nil && isAlive(dotConn.conn) {
|
||||||
dotConn.refCount++
|
dotConn.refCount++
|
||||||
dotConn.lastUsed = time.Now()
|
dotConn.lastUsed = time.Now()
|
||||||
conn := dotConn.conn
|
conn := dotConn.conn
|
||||||
@@ -193,7 +186,7 @@ func (p *dotConnPool) putConn(addr string, conn net.Conn, isGood bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// dialConn creates a new TCP/TLS connection.
|
// dialConn creates a new TCP/TLS connection.
|
||||||
func (p *dotConnPool) dialConn(ctx context.Context) (string, net.Conn, error) {
|
func (p *dotConnPool) dialConn(ctx context.Context) (string, *tls.Conn, error) {
|
||||||
logger := ProxyLogger.Load()
|
logger := ProxyLogger.Load()
|
||||||
var endpoint string
|
var endpoint string
|
||||||
|
|
||||||
@@ -215,7 +208,7 @@ func (p *dotConnPool) dialConn(ctx context.Context) (string, net.Conn, error) {
|
|||||||
// Try bootstrap IPs in parallel
|
// Try bootstrap IPs in parallel
|
||||||
if len(p.addrs) > 0 {
|
if len(p.addrs) > 0 {
|
||||||
type result struct {
|
type result struct {
|
||||||
conn net.Conn
|
conn *tls.Conn
|
||||||
addr string
|
addr string
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
@@ -307,3 +300,28 @@ func (p *dotConnPool) CloseIdleConnections() {
|
|||||||
delete(p.conns, addr)
|
delete(p.conns, addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isAlive(c *tls.Conn) bool {
|
||||||
|
// Set a very short deadline for the read
|
||||||
|
c.SetReadDeadline(time.Now().Add(1 * time.Millisecond))
|
||||||
|
|
||||||
|
// Try to read 1 byte without consuming it (using a small buffer)
|
||||||
|
one := make([]byte, 1)
|
||||||
|
_, err := c.Read(one)
|
||||||
|
|
||||||
|
// Reset the deadline for future operations
|
||||||
|
c.SetReadDeadline(time.Time{})
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
return false // Connection is definitely closed
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get a timeout, it means no data is waiting,
|
||||||
|
// but the connection is likely still "up."
|
||||||
|
var netErr net.Error
|
||||||
|
if errors.As(err, &netErr) && netErr.Timeout() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user