hickory_proto/quic/
quic_stream.rs

1// Copyright 2015-2022 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use bytes::{Bytes, BytesMut};
9use quinn::{RecvStream, SendStream, VarInt};
10use tracing::debug;
11
12use crate::{
13    error::{ProtoError, ProtoErrorKind},
14    op::Message,
15    xfer::DnsResponse,
16};
17
18/// ```text
19/// 5.1. Connection Establishment
20///
21/// DoQ connections are established as described in the QUIC transport specification [RFC9000]. During connection establishment,
22/// DoQ support is indicated by selecting the ALPN token "doq" in the crypto handshake.
23/// ```
24pub(crate) const DOQ_ALPN: &[u8] = b"doq";
25
26/// [DoQ Error Codes](https://www.ietf.org/archive/id/draft-ietf-dprive-dnsoquic-10.html#name-doq-error-codes), draft-ietf-dprive-dnsoquic, Feb. 28, 2022
27/// ```text
28///  5.3. DoQ Error Codes
29///
30/// The following error codes are defined for use when abruptly terminating streams, aborting reading of streams, or immediately closing connections:
31///
32/// DOQ_NO_ERROR (0x0):
33///     No error. This is used when the connection or stream needs to be closed, but there is no error to signal.
34///
35/// DOQ_INTERNAL_ERROR (0x1):
36///     The DoQ implementation encountered an internal error and is incapable of pursuing the transaction or the connection.
37///
38/// DOQ_PROTOCOL_ERROR (0x2):
39///     The DoQ implementation encountered an protocol error and is forcibly aborting the connection.
40///
41/// DOQ_REQUEST_CANCELLED (0x3):
42///     A DoQ client uses this to signal that it wants to cancel an outstanding transaction.
43///
44/// DOQ_EXCESSIVE_LOAD (0x4):
45///     A DoQ implementation uses this to signal when closing a connection due to excessive load.
46///
47/// DOQ_ERROR_RESERVED (0xd098ea5e):
48///     Alternative error code used for tests.
49/// ```
50#[derive(Clone, Copy)]
51pub enum DoqErrorCode {
52    /// No error. This is used when the connection or stream needs to be closed, but there is no error to signal.
53    NoError,
54    /// The DoQ implementation encountered an internal error and is incapable of pursuing the transaction or the connection.
55    InternalError,
56    /// The DoQ implementation encountered an protocol error and is forcibly aborting the connection.
57    ProtocolError,
58    /// A DoQ client uses this to signal that it wants to cancel an outstanding transaction.
59    RequestCancelled,
60    /// A DoQ implementation uses this to signal when closing a connection due to excessive load.
61    ExcessiveLoad,
62    /// Alternative error code used for tests.
63    ErrorReserved,
64    /// Unknown Error code
65    Unknown(u32),
66}
67
68// not using repr(u32) above because of the Unknown
69const NO_ERROR: u32 = 0x0;
70const INTERNAL_ERROR: u32 = 0x1;
71const PROTOCOL_ERROR: u32 = 0x2;
72const REQUEST_CANCELLED: u32 = 0x3;
73const EXCESSIVE_LOAD: u32 = 0x4;
74const ERROR_RESERVED: u32 = 0xd098ea5e;
75
76impl From<DoqErrorCode> for VarInt {
77    fn from(doq_error: DoqErrorCode) -> Self {
78        use DoqErrorCode::*;
79
80        match doq_error {
81            NoError => Self::from_u32(NO_ERROR),
82            InternalError => Self::from_u32(INTERNAL_ERROR),
83            ProtocolError => Self::from_u32(PROTOCOL_ERROR),
84            RequestCancelled => Self::from_u32(REQUEST_CANCELLED),
85            ExcessiveLoad => Self::from_u32(EXCESSIVE_LOAD),
86            ErrorReserved => Self::from_u32(ERROR_RESERVED),
87            Unknown(code) => Self::from_u32(code),
88        }
89    }
90}
91
92impl From<VarInt> for DoqErrorCode {
93    fn from(doq_error: VarInt) -> Self {
94        let code: u32 = if let Ok(code) = doq_error.into_inner().try_into() {
95            code
96        } else {
97            return Self::ProtocolError;
98        };
99
100        match code {
101            NO_ERROR => Self::NoError,
102            INTERNAL_ERROR => Self::InternalError,
103            PROTOCOL_ERROR => Self::ProtocolError,
104            REQUEST_CANCELLED => Self::RequestCancelled,
105            EXCESSIVE_LOAD => Self::ExcessiveLoad,
106            ERROR_RESERVED => Self::ErrorReserved,
107            _ => Self::Unknown(code),
108        }
109    }
110}
111
112/// A single bi-directional stream
113pub struct QuicStream {
114    send_stream: SendStream,
115    receive_stream: RecvStream,
116}
117
118impl QuicStream {
119    pub(crate) fn new(send_stream: SendStream, receive_stream: RecvStream) -> Self {
120        Self {
121            send_stream,
122            receive_stream,
123        }
124    }
125
126    /// Send the DNS message to the other side
127    pub async fn send(&mut self, mut message: Message) -> Result<(), ProtoError> {
128        // RFC: When sending queries over a QUIC connection, the DNS Message ID MUST be set to zero. The stream mapping for DoQ allows for
129        // unambiguous correlation of queries and responses and so the Message ID field is not required.
130        message.set_id(0);
131
132        let bytes = Bytes::from(message.to_vec()?);
133
134        self.send_bytes(bytes).await
135    }
136
137    /// Send pre-encoded bytes, warning, QUIC requires the message id to be 0.
138    pub async fn send_bytes(&mut self, bytes: Bytes) -> Result<(), ProtoError> {
139        // In order that multiple responses can be parsed, a 2-octet length field is used in exactly the same way as the 2-octet length
140        // field defined for DNS over TCP [RFC1035]. The practical result of this is that the content of each QUIC stream is exactly
141        // the same as the content of a TCP connection that would manage exactly one query.All DNS messages (queries and responses)
142        // sent over DoQ connections MUST be encoded as a 2-octet length field followed by the message content as specified in [RFC1035].
143        let bytes_len = u16::try_from(bytes.len())
144            .map_err(|_e| ProtoErrorKind::MaxBufferSizeExceeded(bytes.len()))?;
145        let len = bytes_len.to_be_bytes().to_vec();
146        let len = Bytes::from(len);
147
148        debug!("received packet len: {} bytes: {:x?}", bytes_len, bytes);
149        self.send_stream.write_all_chunks(&mut [len, bytes]).await?;
150        Ok(())
151    }
152
153    /// finishes the send stream, i.e. there will be no more data sent to the remote
154    pub async fn finish(&mut self) -> Result<(), ProtoError> {
155        self.send_stream.finish().await?;
156        Ok(())
157    }
158
159    /// Receive a single packet
160    pub async fn receive(&mut self) -> Result<DnsResponse, ProtoError> {
161        let bytes = self.receive_bytes().await?;
162        let message = Message::from_vec(&bytes)?;
163
164        // assert that the message id is 0, this is a bad dns-over-quic packet if not
165        if message.id() != 0 {
166            self.reset(DoqErrorCode::ProtocolError)
167                .map_err(|_| debug!("stream already closed"))
168                .ok();
169            return Err(ProtoErrorKind::QuicMessageIdNot0(message.id()).into());
170        }
171
172        Ok(DnsResponse::new(message, bytes.to_vec()))
173    }
174
175    // TODO: we should change the protocol handlers to work with Messages since some require things like 0 for the Message ID.
176    /// Receive a single packet as raw bytes
177    pub async fn receive_bytes(&mut self) -> Result<BytesMut, ProtoError> {
178        // following above, the data should be first the length, followed by the message(s)
179        let mut len = [0u8; 2];
180        self.receive_stream.read_exact(&mut len).await?;
181        let len = u16::from_be_bytes(len) as usize;
182
183        // RFC: DoQ Queries and Responses are sent on QUIC streams, which in theory can carry up to 2^62 bytes.
184        //  However, DNS messages are restricted in practice to a maximum size of 65535 bytes. This maximum size
185        //  is enforced by the use of a two-octet message length field in DNS over TCP [RFC1035] and DNS over TLS [RFC7858],
186        //  and by the definition of the "application/dns-message" for DNS over HTTP [RFC8484]. DoQ enforces the same restriction.
187        let mut bytes = BytesMut::with_capacity(len);
188        bytes.resize(len, 0);
189        if let Err(e) = self.receive_stream.read_exact(&mut bytes[..len]).await {
190            debug!("received bad packet len: {} bytes: {:?}", len, bytes);
191
192            self.reset(DoqErrorCode::ProtocolError)
193                .map_err(|_| debug!("stream already closed"))
194                .ok();
195            return Err(e.into());
196        }
197
198        debug!("received packet len: {} bytes: {:x?}", len, bytes);
199        Ok(bytes)
200    }
201
202    /// Reset the sending stream due to some error
203    pub fn reset(&mut self, code: DoqErrorCode) -> Result<(), ProtoError> {
204        self.send_stream
205            .reset(code.into())
206            .map_err(|_| ProtoError::from(ProtoErrorKind::QuinnUnknownStreamError))
207    }
208
209    /// Stop the receiving stream due to some error
210    pub fn stop(&mut self, code: DoqErrorCode) -> Result<(), ProtoError> {
211        self.receive_stream
212            .stop(code.into())
213            .map_err(|_| ProtoError::from(ProtoErrorKind::QuinnUnknownStreamError))
214    }
215}