mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-09-17 01:45:27 +02:00
doq: configure QUIC keep-alive and retry on idle timeout
Pass a quic.Config with KeepAlivePeriod (15s) to DoQ dial calls instead of nil, so pooled connections send periodic QUIC PINGs to stay alive and detect dead paths proactively. Also add IdleTimeoutError to the DoQ retry conditions alongside io.EOF, so stale pooled connections trigger a transparent retry instead of propagating as a query failure.
This commit is contained in:
@@ -46,6 +46,7 @@ type doqConnPool struct {
|
|||||||
addrs []string
|
addrs []string
|
||||||
port string
|
port string
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
|
quicConfig *quic.Config
|
||||||
conns chan *doqConn
|
conns chan *doqConn
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,11 +66,16 @@ func newDOQConnPool(uc *UpstreamConfig, addrs []string) *doqConnPool {
|
|||||||
ServerName: uc.Domain,
|
ServerName: uc.Domain,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quicConfig := &quic.Config{
|
||||||
|
KeepAlivePeriod: 15 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
pool := &doqConnPool{
|
pool := &doqConnPool{
|
||||||
uc: uc,
|
uc: uc,
|
||||||
addrs: addrs,
|
addrs: addrs,
|
||||||
port: port,
|
port: port,
|
||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
|
quicConfig: quicConfig,
|
||||||
conns: make(chan *doqConn, doqPoolSize),
|
conns: make(chan *doqConn, doqPoolSize),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,12 +91,17 @@ func newDOQConnPool(uc *UpstreamConfig, addrs []string) *doqConnPool {
|
|||||||
|
|
||||||
// Resolve performs a DNS query using a pooled QUIC connection.
|
// Resolve performs a DNS query using a pooled QUIC connection.
|
||||||
func (p *doqConnPool) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error) {
|
func (p *doqConnPool) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error) {
|
||||||
// Retry logic for io.EOF errors (as per original implementation)
|
// Retry logic for transient errors: io.EOF (connection reset) and
|
||||||
|
// IdleTimeoutError (stale pooled connection timed out).
|
||||||
for range 5 {
|
for range 5 {
|
||||||
answer, err := p.doResolve(ctx, msg)
|
answer, err := p.doResolve(ctx, msg)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
var idleErr *quic.IdleTimeoutError
|
||||||
|
if errors.As(err, &idleErr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, wrapCertificateVerificationError(err)
|
return nil, wrapCertificateVerificationError(err)
|
||||||
}
|
}
|
||||||
@@ -226,7 +237,7 @@ func (p *doqConnPool) dialConn(ctx context.Context) (string, *quic.Conn, error)
|
|||||||
udpConn.Close()
|
udpConn.Close()
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
conn, err := quic.DialEarly(ctx, udpConn, remoteAddr, p.tlsConfig, nil)
|
conn, err := quic.DialEarly(ctx, udpConn, remoteAddr, p.tlsConfig, p.quicConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
udpConn.Close()
|
udpConn.Close()
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
@@ -241,7 +252,7 @@ func (p *doqConnPool) dialConn(ctx context.Context) (string, *quic.Conn, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pd := &quicParallelDialer{}
|
pd := &quicParallelDialer{}
|
||||||
conn, err := pd.Dial(ctx, dialAddrs, p.tlsConfig, nil)
|
conn, err := pd.Dial(ctx, dialAddrs, p.tlsConfig, p.quicConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user