handleQUICStream reads DNS queries from the stream, processes them, and writes back the response.
(ctx context.Context, stream *quic.Stream, conn *quic.Conn)
| 310 | // handleQUICStream reads DNS queries from the stream, processes them, |
| 311 | // and writes back the response. |
| 312 | func (p *Proxy) handleQUICStream(ctx context.Context, stream *quic.Stream, conn *quic.Conn) { |
| 313 | bufPtr := p.bytesPool.Get() |
| 314 | defer p.bytesPool.Put(bufPtr) |
| 315 | |
| 316 | ctx, cancel := p.reqCtx.New(ctx) |
| 317 | defer cancel() |
| 318 | |
| 319 | // One query - one stream. |
| 320 | // The client MUST select the next available client-initiated bidirectional |
| 321 | // stream for each subsequent query on a QUIC connection. |
| 322 | |
| 323 | // err is not checked here because STREAM FIN sent by the client is |
| 324 | // indicated as error here. Instead, we should check the number of bytes |
| 325 | // received. |
| 326 | buf := *bufPtr |
| 327 | n, err := readAll(stream, buf) |
| 328 | |
| 329 | // Note that io.EOF does not really mean that there's any error, this is |
| 330 | // just a signal that there will be no data to read anymore from this |
| 331 | // stream. |
| 332 | if (err != nil && err != io.EOF) || n < minDNSPacketSize { |
| 333 | logShortQUICRead(ctx, err, p.logger) |
| 334 | |
| 335 | return |
| 336 | } |
| 337 | |
| 338 | // In theory, we should use ALPN to get the DoQ version properly. However, |
| 339 | // since there are not too many versions now, we only check how the DNS |
| 340 | // query is encoded. If it's sent with a 2-byte prefix, we consider this a |
| 341 | // DoQ v1. Otherwise, a draft version. |
| 342 | doqVersion := DoQv1 |
| 343 | req := &dns.Msg{} |
| 344 | |
| 345 | // Note that we support both the old drafts and the new RFC. In the old |
| 346 | // draft DNS messages were not prefixed with the message length. |
| 347 | packetLen := binary.BigEndian.Uint16(buf[:2]) |
| 348 | if packetLen == uint16(n-2) { |
| 349 | err = req.Unpack(buf[2:n]) |
| 350 | } else { |
| 351 | err = req.Unpack(buf[:n]) |
| 352 | doqVersion = DoQv1Draft |
| 353 | } |
| 354 | |
| 355 | if err != nil { |
| 356 | p.logger.ErrorContext(ctx, "unpacking quic packet", slogutil.KeyError, err) |
| 357 | closeQUICConn(conn, DoQCodeProtocolError, p.logger) |
| 358 | |
| 359 | return |
| 360 | } |
| 361 | |
| 362 | if !validQUICMsg(req, p.logger) { |
| 363 | // If a peer encounters such an error condition, it is considered a |
| 364 | // fatal error. It SHOULD forcibly abort the connection using QUIC's |
| 365 | // CONNECTION_CLOSE mechanism and SHOULD use the DoQ error code |
| 366 | // DOQ_PROTOCOL_ERROR. |
| 367 | closeQUICConn(conn, DoQCodeProtocolError, p.logger) |
| 368 | |
| 369 | return |
no test coverage detected