dns_stamp_parser/
encode.rs

1//! This module contains all encode functions for the crate.
2use crate::{
3    Addr, AnonymizedDnsCryptRelay, DnsCrypt, DnsOverHttps, DnsOverTls, DnsPlain, DnsStamp,
4    DnsStampType, EncodeError, EncodeResult, ObliviousDoHTarget, Props,
5};
6use base64::{prelude::BASE64_URL_SAFE_NO_PAD, Engine};
7use std::net::{IpAddr, SocketAddr};
8
9/// Encode a `crate::DnsStampType` into a `std::vec::Vec<u8>`.
10fn encode_type(buffer: &mut Vec<u8>, dns_stamp_type: DnsStampType) {
11    buffer.push(dns_stamp_type as u8);
12}
13
14/// Encode a `DnsStampType` into a `std::vec::Vec<u8>`.
15fn encode_props(buffer: &mut Vec<u8>, props: &Props) {
16    let bytes = props.bits().to_le_bytes();
17    buffer.extend(bytes.iter());
18}
19
20/// Encode a `u8` slice into a `std::vec::Vec<u8>`.
21fn encode_bytes(buffer: &mut Vec<u8>, bytes: impl AsRef<[u8]>) -> EncodeResult<()> {
22    let bytes = bytes.as_ref();
23    let len = bytes.len();
24    if len <= u8::MAX as usize {
25        buffer.push(len as u8);
26        buffer.extend(bytes);
27        Ok(())
28    } else {
29        Err(EncodeError::TooManyBytes)
30    }
31}
32
33/// Convert a `std::net::IpAddr` to a `String`.
34fn ip_addr_to_string(ip_addr: IpAddr) -> String {
35    let mut string = ip_addr.to_string();
36    if ip_addr.is_ipv6() {
37        string = format!("[{}]", string);
38    }
39    string
40}
41
42/// Convert a `std::net::SocketAddr` to a `String`.
43fn socket_addr_to_string(socket_addr: SocketAddr, default_port: u16) -> String {
44    match socket_addr {
45        SocketAddr::V6(socket_addr_v6) => {
46            if socket_addr_v6.scope_id() == 0 && socket_addr.port() == default_port {
47                ip_addr_to_string(socket_addr.ip())
48            } else {
49                socket_addr.to_string()
50            }
51        }
52        SocketAddr::V4(socket_addr_v4) => {
53            if socket_addr_v4.port() == default_port {
54                ip_addr_to_string(socket_addr.ip())
55            } else {
56                socket_addr_v4.to_string()
57            }
58        }
59    }
60}
61
62/// Encode a `std::net::SocketAddr` into a `std::vec::Vec<u8>`.
63/// If the `socket_addr` has the same `default_port`
64/// then encode only the `std::net::IpAddr`.
65fn encode_socket_addr(
66    buffer: &mut Vec<u8>,
67    socket_addr: SocketAddr,
68    default_port: u16,
69) -> EncodeResult<()> {
70    let string = socket_addr_to_string(socket_addr, default_port);
71    encode_bytes(buffer, &string)
72}
73
74/// Convert a `crate::Addr` to a `String`.
75fn addr_to_string(addr: Addr, default_port: u16) -> String {
76    match addr {
77        Addr::SocketAddr(socket_addr) => socket_addr_to_string(socket_addr, default_port),
78        Addr::Port(port) => format!(":{}", port),
79    }
80}
81
82/// Convert a `std::option::Option<crate::Addr>` to a `String`.
83fn option_addr_to_string(addr: Option<Addr>, default_port: u16) -> String {
84    match addr {
85        Some(addr) => addr_to_string(addr, default_port),
86        None => "".to_string(),
87    }
88}
89
90/// Encode a `crate::Addr` into a `std::vec::Vec<u8>`.
91/// If the `addr` is `None` then encode only the `default_port`.
92fn encode_option_addr(
93    buffer: &mut Vec<u8>,
94    addr: Option<Addr>,
95    default_port: u16,
96) -> EncodeResult<()> {
97    let string = option_addr_to_string(addr, default_port);
98    encode_bytes(buffer, string)
99}
100
101/// Encode a `std::net::IpAddr` into a `std::vec::Vec<u8>`.
102fn encode_ip_addr(buffer: &mut Vec<u8>, ip_addr: IpAddr) -> EncodeResult<()> {
103    let string = ip_addr_to_string(ip_addr);
104    encode_bytes(buffer, &string)
105}
106
107/// Encode a `[u8;32]` into a `std::vec::Vec<u8>`.
108fn encode_pk(buffer: &mut Vec<u8>, pk: &[u8; 32]) -> EncodeResult<()> {
109    encode_bytes(buffer, &pk[..])
110}
111
112/// Encode a `std::vec::Vec<u8>` into a `std::vec::Vec<u8>`.
113/// See [`VLP()`].
114///
115/// [`VLP()`]:  https://dnscrypt.info/stamps-specifications#common-definitions
116fn encode_vlp<T: AsRef<[u8]>>(buffer: &mut Vec<u8>, vlp: &[T]) -> EncodeResult<()> {
117    if vlp.is_empty() {
118        encode_bytes(buffer, [])
119    } else {
120        let len = vlp.len();
121        if let Some(array) = vlp.get(..(len - 1)) {
122            for bytes in array {
123                let bytes = bytes.as_ref();
124                let len = bytes.len();
125                if len <= 0x80 {
126                    buffer.push((len ^ 0x80) as u8);
127                    buffer.extend(bytes);
128                } else {
129                    return Err(EncodeError::TooManyBytes);
130                }
131            }
132        }
133        if let Some(bytes) = vlp.get(len - 1) {
134            encode_bytes(buffer, bytes)
135        } else {
136            Err(EncodeError::EmptyArray)
137        }
138    }
139}
140
141/// Encode a `std::vec::Vec<[u8;32]>` into a `std::vec::Vec<u8>`.
142fn encode_hashi(buffer: &mut Vec<u8>, hashi: &[[u8; 32]]) -> EncodeResult<()> {
143    encode_vlp(buffer, hashi)
144}
145
146/// Encode a `std::vec::Vec<std::net::IpAddr>` into a `std::vec::Vec<u8>`.
147fn encode_bootstrap_ipi(buffer: &mut Vec<u8>, bootstrap_ipi: &[IpAddr]) -> EncodeResult<()> {
148    encode_vlp(
149        buffer,
150        &bootstrap_ipi
151            .iter()
152            .cloned()
153            .map(ip_addr_to_string)
154            .collect::<Vec<_>>(),
155    )
156}
157
158/// Encode `[u8]` slice with Base64 and prepand `"sdns://"`.
159fn encode_base64(buffer: &[u8]) -> String {
160    format!("sdns://{}", BASE64_URL_SAFE_NO_PAD.encode(buffer))
161}
162
163/// Encode a `crate::DnsPlain` into a `std::vec::Vec<u8>` as `crate::DnsStampType::Plain`.
164fn encode_dns_plain(buffer: &mut Vec<u8>, dns_plain: &DnsPlain) -> EncodeResult<()> {
165    encode_type(buffer, DnsStampType::Plain);
166    encode_props(buffer, &dns_plain.props);
167    encode_ip_addr(buffer, dns_plain.addr)?;
168    Ok(())
169}
170
171/// Encode a `crate::DnsCrypt` into a `std::vec::Vec<u8>` as `crate::DnsStampType::DnsCrypt`.
172fn encode_dns_crypt(buffer: &mut Vec<u8>, dns_crypt: &DnsCrypt) -> EncodeResult<()> {
173    encode_type(buffer, DnsStampType::DnsCrypt);
174    encode_props(buffer, &dns_crypt.props);
175    encode_socket_addr(buffer, dns_crypt.addr, 443)?;
176    encode_pk(buffer, &dns_crypt.pk)?;
177    encode_bytes(buffer, &dns_crypt.provider_name)?;
178    Ok(())
179}
180
181/// Encode a `crate::DnsCrypt` into a `std::vec::Vec<u8>`.
182fn encode_dns_over_https_data(
183    buffer: &mut Vec<u8>,
184    dns_over_https: &DnsOverHttps,
185) -> EncodeResult<()> {
186    encode_props(buffer, &dns_over_https.props);
187    encode_option_addr(buffer, dns_over_https.addr, 443)?;
188    encode_hashi(buffer, &dns_over_https.hashi)?;
189    encode_bytes(buffer, &dns_over_https.hostname)?;
190    encode_bytes(buffer, &dns_over_https.path)?;
191    if !dns_over_https.bootstrap_ipi.is_empty() {
192        encode_bootstrap_ipi(buffer, &dns_over_https.bootstrap_ipi)?;
193    }
194    Ok(())
195}
196
197/// Encode a `crate::DnsOverHttps` into a `std::vec::Vec<u8>`
198/// as `crate::DnsStampType::DnsOverHttps`.
199fn encode_dns_over_https(buffer: &mut Vec<u8>, dns_over_https: &DnsOverHttps) -> EncodeResult<()> {
200    encode_type(buffer, DnsStampType::DnsOverHttps);
201    encode_dns_over_https_data(buffer, dns_over_https)
202}
203
204/// Encode a `crate::DnsOverHttps` into a `std::vec::Vec<u8>`
205/// as `crate::DnsStampType::ObliviousDoHRelay`.
206fn encode_oblivious_doh_relay(
207    buffer: &mut Vec<u8>,
208    dns_over_https: &DnsOverHttps,
209) -> EncodeResult<()> {
210    encode_type(buffer, DnsStampType::ObliviousDoHRelay);
211    encode_dns_over_https_data(buffer, dns_over_https)
212}
213
214/// Encode a `crate::DnsOverTls` into a `std::vec::Vec<u8>`.
215fn encode_dns_over_tls_data(buffer: &mut Vec<u8>, dns_over_tls: &DnsOverTls) -> EncodeResult<()> {
216    encode_props(buffer, &dns_over_tls.props);
217    encode_option_addr(buffer, dns_over_tls.addr, 443)?;
218    encode_hashi(buffer, &dns_over_tls.hashi)?;
219    encode_bytes(buffer, &dns_over_tls.hostname)?;
220    if !dns_over_tls.bootstrap_ipi.is_empty() {
221        encode_bootstrap_ipi(buffer, &dns_over_tls.bootstrap_ipi)?;
222    }
223    Ok(())
224}
225
226/// Encode a `crate::DnsOverTls` into a `std::vec::Vec<u8>` as `crate::DnsStampType::DnsOverTls`.
227fn encode_dns_over_tls(buffer: &mut Vec<u8>, dns_over_tls: &DnsOverTls) -> EncodeResult<()> {
228    encode_type(buffer, DnsStampType::DnsOverTls);
229    encode_dns_over_tls_data(buffer, dns_over_tls)?;
230    Ok(())
231}
232
233/// Encode a `crate::DnsOverTls` into a `std::vec::Vec<u8>` as `crate::DnsStampType::DnsOverQuic`.
234fn encode_dns_over_quic(buffer: &mut Vec<u8>, dns_over_tls: &DnsOverTls) -> EncodeResult<()> {
235    encode_type(buffer, DnsStampType::DnsOverQuic);
236    encode_dns_over_tls_data(buffer, dns_over_tls)?;
237    Ok(())
238}
239
240/// Encode a `crate::ObliviousDoHTarget` into a `std::vec::Vec<u8>`
241/// as `crate::DnsStampType::ObliviousDoHTarget`.
242fn encode_oblivious_doh_target(
243    buffer: &mut Vec<u8>,
244    oblivious_doh_target: &ObliviousDoHTarget,
245) -> EncodeResult<()> {
246    encode_type(buffer, DnsStampType::ObliviousDoHTarget);
247    encode_props(buffer, &oblivious_doh_target.props);
248    encode_bytes(buffer, &oblivious_doh_target.hostname)?;
249    encode_bytes(buffer, &oblivious_doh_target.path)?;
250    Ok(())
251}
252
253/// Encode a `crate::AnonymizedDnsCryptRelay` into a `std::vec::Vec<u8>`
254/// as `crate::DnsStampType::AnonymizedDnsCryptRelay`.
255fn encode_anonymized_dns_crypt_relay(
256    buffer: &mut Vec<u8>,
257    anonymized_dns_crypt_relay: &AnonymizedDnsCryptRelay,
258) -> EncodeResult<()> {
259    encode_type(buffer, DnsStampType::AnonymizedDnsCryptRelay);
260    encode_socket_addr(buffer, anonymized_dns_crypt_relay.addr, 443)?;
261    Ok(())
262}
263
264impl DnsStamp {
265    /// Encode a `crate::DnsStamp` to a `std::string::String`.
266    pub fn encode(&self) -> EncodeResult<String> {
267        let mut buffer = Vec::new();
268        match self {
269            DnsStamp::DnsPlain(dns_plain) => encode_dns_plain(&mut buffer, dns_plain)?,
270            DnsStamp::DnsCrypt(dns_crypt) => encode_dns_crypt(&mut buffer, dns_crypt)?,
271            DnsStamp::DnsOverHttps(dns_over_https) => {
272                encode_dns_over_https(&mut buffer, dns_over_https)?
273            }
274            DnsStamp::DnsOverTls(dns_over_tls) => encode_dns_over_tls(&mut buffer, dns_over_tls)?,
275            DnsStamp::DnsOverQuic(dns_over_quic) => {
276                encode_dns_over_quic(&mut buffer, dns_over_quic)?
277            }
278            DnsStamp::ObliviousDoHTarget(oblivious_doh_target) => {
279                encode_oblivious_doh_target(&mut buffer, oblivious_doh_target)?
280            }
281            DnsStamp::AnonymizedDnsCryptRelay(anonymized_dns_crypt_relay) => {
282                encode_anonymized_dns_crypt_relay(&mut buffer, anonymized_dns_crypt_relay)?;
283            }
284            DnsStamp::ObliviousDoHRelay(oblivious_doh_relay) => {
285                encode_oblivious_doh_relay(&mut buffer, oblivious_doh_relay)?;
286            }
287        }
288
289        Ok(encode_base64(&buffer))
290    }
291}