Skip to content

Commit 00399ec

Browse files
committed
proxy: set quic ops deadlines
1 parent 0f74749 commit 00399ec

1 file changed

Lines changed: 63 additions & 2 deletions

File tree

proxy/serverquic.go

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ const quicAddrValidatorCacheSize = 1000
4848
// TODO(ameshkov): make it configurable.
4949
const quicAddrValidatorCacheTTL = 30 * time.Minute
5050

51+
const (
52+
// DefaultDoQReadTimeout is the default timeout for DoQ reading operations.
53+
DefaultDoQReadTimeout = 2 * time.Second
54+
55+
// DefaultDoQWriteTimeout is the default timeout for DoQ writing operations.
56+
DefaultDoQWriteTimeout = 2 * time.Second
57+
)
58+
5159
const (
5260
// DoQCodeNoError is used when the connection or stream needs to be closed,
5361
// but there is no error to signal.
@@ -129,7 +137,7 @@ func (p *Proxy) quicPacketLoop(
129137
p.logger.InfoContext(ctx, "entering dns-over-quic listener loop", "addr", l.Addr())
130138

131139
for {
132-
conn, err := l.Accept(ctx)
140+
conn, err := p.acceptQUICConn(ctx, l)
133141
if err != nil {
134142
logQUICError(ctx, "accepting quic conn", err, p.logger)
135143

@@ -155,6 +163,17 @@ func (p *Proxy) quicPacketLoop(
155163
}
156164
}
157165

166+
// acceptQUICConn reads and accepts a single QUIC connection.
167+
func (p *Proxy) acceptQUICConn(
168+
ctx context.Context,
169+
l *quic.EarlyListener,
170+
) (conn *quic.Conn, err error) {
171+
acceptCtx, cancel := context.WithDeadline(ctx, time.Now().Add(DefaultDoQReadTimeout))
172+
defer cancel()
173+
174+
return l.Accept(acceptCtx)
175+
}
176+
158177
// logQUICError writes suitable log message for the given err.
159178
func logQUICError(ctx context.Context, prefix string, err error, l *slog.Logger) {
160179
if isQUICErrorForDebugLog(err) {
@@ -184,7 +203,7 @@ func (p *Proxy) handleQUICConnection(
184203
// design specifies that for each subsequent query on a QUIC connection
185204
// the client MUST select the next available client-initiated
186205
// bidirectional stream.
187-
stream, err := conn.AcceptStream(ctx)
206+
stream, err := acceptStream(ctx, conn)
188207
if err != nil {
189208
logQUICError(ctx, "accepting quic stream", err, p.logger)
190209

@@ -216,6 +235,48 @@ func (p *Proxy) handleQUICConnection(
216235
}
217236
}
218237

238+
// acceptStream accepts and starts processing a single QUIC stream. All
239+
// arguments must not be nil.
240+
//
241+
// NOTE: Any error returned from this method stops handling on conn.
242+
func acceptStream(parent context.Context, conn *quic.Conn) (stream *quic.Stream, err error) {
243+
// The stub to resolver DNS traffic follows a simple pattern in which the
244+
// client sends a query, and the server provides a response. This design
245+
// specifies that for each subsequent query on a QUIC connection the client
246+
// MUST select the next available client-initiated bidirectional stream.
247+
ctx, cancel := context.WithDeadline(parent, time.Now().Add(maxQUICIdleTimeout))
248+
defer cancel()
249+
250+
// For some reason AcceptStream below seems to get stuck even when ctx is
251+
// canceled. As a mitigation, check the context manually right before
252+
// feeding it into AcceptStream.
253+
//
254+
// TODO(a.garipov): Try to reproduce and report.
255+
select {
256+
case <-ctx.Done():
257+
return nil, fmt.Errorf("checking accept ctx: %w", ctx.Err())
258+
default:
259+
// Go on.
260+
}
261+
262+
stream, err = conn.AcceptStream(ctx)
263+
if err != nil {
264+
return nil, fmt.Errorf("accepting quic stream: %w", err)
265+
}
266+
267+
err = stream.SetReadDeadline(time.Now().Add(DefaultDoQReadTimeout))
268+
if err != nil {
269+
return nil, fmt.Errorf("setting read deadline: %w", err)
270+
}
271+
272+
err = stream.SetWriteDeadline(time.Now().Add(DefaultDoQWriteTimeout))
273+
if err != nil {
274+
return nil, fmt.Errorf("setting write deadline: %w", err)
275+
}
276+
277+
return stream, nil
278+
}
279+
219280
// handleQUICStream reads DNS queries from the stream, processes them,
220281
// and writes back the response.
221282
func (p *Proxy) handleQUICStream(ctx context.Context, stream *quic.Stream, conn *quic.Conn) {

0 commit comments

Comments
 (0)