use std::net::IpAddr; use std::sync::Arc; use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use crate::dnscrypt_certs::*; use crate::errors::*; pub const DNS_MAX_HOSTNAME_SIZE: usize = 256; pub const DNS_HEADER_SIZE: usize = 12; pub const DNS_OFFSET_FLAGS: usize = 2; pub const DNS_MAX_PACKET_SIZE: usize = 0x1600; const DNS_MAX_INDIRECTIONS: usize = 16; const DNS_FLAGS_TC: u16 = 1u16 << 9; const DNS_FLAGS_QR: u16 = 1u16 << 15; const DNS_FLAGS_RA: u16 = 1u16 << 7; const DNS_FLAGS_RD: u16 = 1u16 << 8; const DNS_FLAGS_CD: u16 = 1u16 << 4; const DNS_OFFSET_QUESTION: usize = DNS_HEADER_SIZE; pub const DNS_TYPE_A: u16 = 1; pub const DNS_TYPE_AAAA: u16 = 28; pub const DNS_TYPE_OPT: u16 = 41; pub const DNS_TYPE_TXT: u16 = 16; pub const DNS_TYPE_HINFO: u16 = 13; pub const DNS_CLASS_INET: u16 = 1; pub const DNS_RCODE_SERVFAIL: u8 = 2; pub const DNS_RCODE_NXDOMAIN: u8 = 3; pub const DNS_RCODE_REFUSED: u8 = 5; #[inline] pub fn rcode(packet: &[u8]) -> u8 { packet[3] & 0x0f } #[inline] pub fn set_rcode(packet: &mut [u8], rcode: u8) { packet[3] = (packet[3] & !0x0f) | rcode; } #[inline] pub fn rcode_servfail(packet: &[u8]) -> bool { rcode(packet) == DNS_RCODE_SERVFAIL } #[inline] pub fn set_rcode_servfail(packet: &mut [u8]) { set_rcode(packet, DNS_RCODE_SERVFAIL) } #[inline] pub fn rcode_refused(packet: &[u8]) -> bool { rcode(packet) == DNS_RCODE_REFUSED } #[inline] pub fn set_rcode_refused(packet: &mut [u8]) { set_rcode(packet, DNS_RCODE_REFUSED) } #[inline] pub fn rcode_nxdomain(packet: &[u8]) -> bool { rcode(packet) == DNS_RCODE_NXDOMAIN } #[inline] pub fn set_rcode_nxdomain(packet: &mut [u8]) { set_rcode(packet, DNS_RCODE_NXDOMAIN) } #[inline] pub fn qdcount(packet: &[u8]) -> u16 { BigEndian::read_u16(&packet[4..]) } #[inline] pub fn ancount(packet: &[u8]) -> u16 { BigEndian::read_u16(&packet[6..]) } fn ancount_inc(packet: &mut [u8]) -> Result<(), Error> { let mut ancount = ancount(packet); ensure!(ancount < 0xffff, "Too many answer records"); ancount += 1; BigEndian::write_u16(&mut packet[6..], ancount); Ok(()) } #[inline] fn nscount(packet: &[u8]) -> u16 { BigEndian::read_u16(&packet[8..]) } #[inline] pub fn arcount(packet: &[u8]) -> u16 { BigEndian::read_u16(&packet[10..]) } fn arcount_inc(packet: &mut [u8]) -> Result<(), Error> { let mut arcount = arcount(packet); ensure!(arcount < 0xffff, "Too many additional records"); arcount += 1; BigEndian::write_u16(&mut packet[10..], arcount); Ok(()) } #[inline] fn arcount_clear(packet: &mut [u8]) -> Result<(), Error> { BigEndian::write_u16(&mut packet[10..], 0); Ok(()) } #[inline] pub fn an_ns_ar_count_clear(packet: &mut [u8]) { packet[6..12].iter_mut().for_each(|x| *x = 0); } #[inline] pub fn tid(packet: &[u8]) -> u16 { BigEndian::read_u16(&packet[0..]) } #[inline] pub fn set_tid(packet: &mut [u8], tid: u16) { BigEndian::write_u16(&mut packet[0..], tid); } #[inline] pub fn set_flags(packet: &mut [u8], flags: u16) { BigEndian::write_u16(&mut packet[DNS_OFFSET_FLAGS..], flags); } #[inline] pub fn authoritative_response(packet: &mut [u8]) { let current_rd_cd_flags = BigEndian::read_u16(&packet[DNS_OFFSET_FLAGS..]) & (DNS_FLAGS_CD | DNS_FLAGS_RD); set_flags(packet, current_rd_cd_flags | DNS_FLAGS_QR | DNS_FLAGS_RA); } #[inline] pub fn truncate(packet: &mut [u8]) { let current_flags = BigEndian::read_u16(&packet[DNS_OFFSET_FLAGS..]); BigEndian::write_u16( &mut packet[DNS_OFFSET_FLAGS..], current_flags | DNS_FLAGS_TC | DNS_FLAGS_QR | DNS_FLAGS_RA, ); } #[inline] pub fn is_response(packet: &[u8]) -> bool { BigEndian::read_u16(&packet[DNS_OFFSET_FLAGS..]) & DNS_FLAGS_QR == DNS_FLAGS_QR } #[inline] pub fn is_truncated(packet: &[u8]) -> bool { BigEndian::read_u16(&packet[DNS_OFFSET_FLAGS..]) & DNS_FLAGS_TC == DNS_FLAGS_TC } pub fn qname(packet: &[u8]) -> Result, Error> { debug_assert!(std::usize::MAX > 0xffff); debug_assert!(DNS_MAX_HOSTNAME_SIZE > 0xff); let packet_len = packet.len(); ensure!(packet_len > DNS_OFFSET_QUESTION, "Short packet"); ensure!(qdcount(packet) == 1, "Unexpected query count"); let mut offset = DNS_HEADER_SIZE; let mut qname = Vec::with_capacity(DNS_MAX_HOSTNAME_SIZE); loop { ensure!(offset < packet_len, "Short packet"); match packet[offset] as usize { label_len if label_len & 0xc0 == 0xc0 => bail!("Indirections"), 0 => { if qname.is_empty() { qname.push(b'.') } break; } label_len => { ensure!(label_len < 0x40, "Long label"); ensure!(packet_len - offset > 1, "Short packet"); offset += 1; ensure!(packet_len - offset > label_len, "Short packet"); if !qname.is_empty() { qname.push(b'.') } ensure!( qname.len() < DNS_MAX_HOSTNAME_SIZE - label_len, "Name too long" ); qname.extend_from_slice(&packet[offset..offset + label_len]); offset += label_len; } } } Ok(qname) } pub fn normalize_qname(packet: &mut [u8]) -> Result<(), Error> { debug_assert!(std::usize::MAX > 0xffff); debug_assert!(DNS_MAX_HOSTNAME_SIZE > 0xff); let packet_len = packet.len(); ensure!(packet_len > DNS_OFFSET_QUESTION, "Short packet"); ensure!(qdcount(packet) == 1, "Unexpected query count"); let mut offset = DNS_HEADER_SIZE; loop { ensure!(offset < packet_len, "Short packet"); match packet[offset] as usize { label_len if label_len & 0xc0 == 0xc0 => bail!("Indirections"), 0 => { break; } label_len => { ensure!(label_len < 0x40, "Long label"); ensure!(packet_len - offset > 1, "Short packet"); offset += 1; ensure!(packet_len - offset > label_len, "Short packet"); ensure!( offset - DNS_HEADER_SIZE < DNS_MAX_HOSTNAME_SIZE - label_len, "Name too long" ); packet[offset..offset + label_len] .iter_mut() .for_each(|x| *x = x.to_ascii_lowercase()); offset += label_len; } } } Ok(()) } pub fn qname_tld(qname: &[u8]) -> &[u8] { qname.rsplit(|c| *c == b'.').next().unwrap_or_default() } pub fn recase_qname(packet: &mut [u8], qname: &[u8]) -> Result<(), Error> { debug_assert!(std::usize::MAX > 0xffff); let packet_len = packet.len(); ensure!(packet_len > DNS_OFFSET_QUESTION, "Short packet"); ensure!(qdcount(packet) == 1, "Unexpected query count"); let qname_len = qname.len(); let mut offset = DNS_HEADER_SIZE; let mut qname_offset = 0; loop { ensure!(offset < packet_len, "Short packet"); match packet[offset] as usize { label_len if label_len & 0xc0 == 0xc0 => bail!("Indirections"), 0 => { ensure!( (qname_len == 1 && qname[0] == b'.') || qname_offset == qname_len, "Unterminated reference qname" ); break; } label_len => { ensure!(label_len < 0x40, "Long label"); ensure!(packet_len - offset > 1, "Short packet"); ensure!(qname_offset < qname_len, "Short reference qname"); offset += 1; if qname_offset != 0 { ensure!(qname[qname_offset] == b'.', "Non-matching reference qname"); qname_offset += 1; } ensure!(packet_len - offset > label_len, "Short packet"); ensure!( qname_len - qname_offset >= label_len, "Short reference qname" ); packet[offset..offset + label_len] .iter_mut() .zip(&qname[qname_offset..qname_offset + label_len]) .for_each(|(a, b)| { debug_assert!(a.eq_ignore_ascii_case(b)); *a = *b }); offset += label_len; qname_offset += label_len; } } } Ok(()) } fn skip_name(packet: &[u8], offset: usize) -> Result { let packet_len = packet.len(); ensure!(offset < packet_len - 1, "Short packet"); let mut qname_len: usize = 0; let mut offset = offset; loop { let label_len = match packet[offset] as usize { label_len if label_len & 0xc0 == 0xc0 => { ensure!(packet_len - offset >= 2, "Incomplete offset"); offset += 2; break; } label_len => label_len, }; ensure!(label_len < 0x40, "Long label"); ensure!( packet_len - offset - 1 > label_len, "Malformed packet with an out-of-bounds name" ); qname_len += label_len + 1; ensure!(qname_len <= DNS_MAX_HOSTNAME_SIZE, "Name too long"); offset += label_len + 1; if label_len == 0 { break; } } Ok(offset) } fn traverse_rrs Result<(), Error>>( packet: &[u8], mut offset: usize, rrcount: usize, mut cb: F, ) -> Result { let packet_len = packet.len(); for _ in 0..rrcount { offset = skip_name(packet, offset)?; ensure!(packet_len - offset >= 10, "Short packet"); let rdlen = BigEndian::read_u16(&packet[offset + 8..]) as usize; ensure!( packet_len - offset >= 10 + rdlen, "Record length would exceed packet length" ); cb(offset)?; offset += 10; offset += rdlen; } Ok(offset) } fn traverse_rrs_mut Result<(), Error>>( packet: &mut [u8], mut offset: usize, rrcount: usize, mut cb: F, ) -> Result { let packet_len = packet.len(); for _ in 0..rrcount { offset = skip_name(packet, offset)?; ensure!(packet_len - offset >= 10, "Short packet"); let rdlen = BigEndian::read_u16(&packet[offset + 8..]) as usize; ensure!( packet_len - offset >= 10 + rdlen, "Record length would exceed packet length" ); cb(packet, offset)?; offset += 10; offset += rdlen; } Ok(offset) } pub fn min_ttl(packet: &[u8], min_ttl: u32, max_ttl: u32, failure_ttl: u32) -> Result { let packet_len = packet.len(); ensure!(packet_len > DNS_OFFSET_QUESTION, "Short packet"); ensure!(packet_len <= DNS_MAX_PACKET_SIZE, "Large packet"); ensure!(qdcount(packet) == 1, "No question"); let mut offset = skip_name(packet, DNS_OFFSET_QUESTION)?; assert!(offset > DNS_OFFSET_QUESTION); ensure!(packet_len - offset > 4, "Short packet"); offset += 4; let (ancount, nscount, arcount) = (ancount(packet), nscount(packet), arcount(packet)); let rrcount = ancount as usize + nscount as usize + arcount as usize; let mut found_min_ttl = if rrcount > 0 { max_ttl } else { failure_ttl }; offset = traverse_rrs(packet, offset, rrcount, |offset| { let qtype = BigEndian::read_u16(&packet[offset..]); let ttl = BigEndian::read_u32(&packet[offset + 4..]); if qtype != DNS_TYPE_OPT && ttl < found_min_ttl { found_min_ttl = ttl; } Ok(()) })?; if found_min_ttl < min_ttl { found_min_ttl = min_ttl; } ensure!(packet_len == offset, "Garbage after packet"); Ok(found_min_ttl) } pub fn set_ttl(packet: &mut [u8], ttl: u32) -> Result<(), Error> { let packet_len = packet.len(); ensure!(packet_len > DNS_OFFSET_QUESTION, "Short packet"); ensure!(packet_len <= DNS_MAX_PACKET_SIZE, "Large packet"); ensure!(qdcount(packet) == 1, "No question"); let mut offset = skip_name(packet, DNS_OFFSET_QUESTION)?; assert!(offset > DNS_OFFSET_QUESTION); ensure!(packet_len - offset > 4, "Short packet"); offset += 4; let (ancount, nscount, arcount) = (ancount(packet), nscount(packet), arcount(packet)); let rrcount = ancount as usize + nscount as usize + arcount as usize; offset = traverse_rrs_mut(packet, offset, rrcount, |packet, offset| { let qtype = BigEndian::read_u16(&packet[offset..]); if qtype != DNS_TYPE_OPT { BigEndian::write_u32(&mut packet[offset + 4..], ttl) } Ok(()) })?; ensure!(packet_len == offset, "Garbage after packet"); Ok(()) } fn add_edns_section(packet: &mut Vec, max_payload_size: u16) -> Result<(), Error> { let opt_rr: [u8; 11] = [ 0, (DNS_TYPE_OPT >> 8) as u8, DNS_TYPE_OPT as u8, (max_payload_size >> 8) as u8, max_payload_size as u8, 0, 0, 0, 0, 0, 0, ]; ensure!( DNS_MAX_PACKET_SIZE - packet.len() >= opt_rr.len(), "Packet would be too large to add a new record" ); arcount_inc(packet)?; packet.extend(opt_rr); Ok(()) } pub fn set_edns_max_payload_size(packet: &mut Vec, max_payload_size: u16) -> Result<(), Error> { let packet_len = packet.len(); ensure!(packet_len > DNS_OFFSET_QUESTION, "Short packet"); ensure!(packet_len <= DNS_MAX_PACKET_SIZE, "Large packet"); ensure!(qdcount(packet) == 1, "No question"); let mut offset = skip_name(packet, DNS_OFFSET_QUESTION)?; assert!(offset > DNS_OFFSET_QUESTION); ensure!(packet_len - offset >= 4, "Short packet"); offset += 4; let (ancount, nscount, arcount) = (ancount(packet), nscount(packet), arcount(packet)); offset = traverse_rrs( packet, offset, ancount as usize + nscount as usize, |_offset| Ok(()), )?; let mut edns_payload_set = false; traverse_rrs_mut(packet, offset, arcount as _, |packet, offset| { let qtype = BigEndian::read_u16(&packet[offset..]); if qtype == DNS_TYPE_OPT { ensure!(!edns_payload_set, "Duplicate OPT RR found"); BigEndian::write_u16(&mut packet[offset + 2..], max_payload_size); edns_payload_set = true; } Ok(()) })?; if edns_payload_set { return Ok(()); } add_edns_section(packet, max_payload_size)?; Ok(()) } pub fn serve_certificates<'t>( client_packet: &[u8], expected_qname: &str, dnscrypt_encryption_params_set: impl IntoIterator>, ) -> Result>, Error> { ensure!(client_packet.len() >= DNS_HEADER_SIZE, "Short packet"); ensure!(qdcount(client_packet) == 1, "No question"); ensure!( !is_response(client_packet), "Question expected, but got a response instead" ); let offset = skip_name(client_packet, DNS_HEADER_SIZE)?; ensure!(client_packet.len() - offset >= 4, "Short packet"); let qtype = BigEndian::read_u16(&client_packet[offset..]); let qclass = BigEndian::read_u16(&client_packet[offset + 2..]); if qtype != DNS_TYPE_TXT || qclass != DNS_CLASS_INET { return Ok(None); } let qname_v = qname(client_packet)?; let qname = std::str::from_utf8(&qname_v)?; if !qname.eq_ignore_ascii_case(expected_qname) { return Ok(None); } let mut packet = client_packet[..offset + 4].to_vec(); an_ns_ar_count_clear(&mut packet); authoritative_response(&mut packet); let dnscrypt_encryption_params = dnscrypt_encryption_params_set .into_iter() .max_by_key(|x| x.dnscrypt_cert().ts_end()) .ok_or_else(|| anyhow!("No certificates"))?; let cert_bin = dnscrypt_encryption_params.dnscrypt_cert().as_bytes(); ensure!(cert_bin.len() <= 0xff, "Certificate too long"); ancount_inc(&mut packet)?; packet.write_u16::(0xc000 + DNS_HEADER_SIZE as u16)?; packet.write_u16::(DNS_TYPE_TXT)?; packet.write_u16::(DNS_CLASS_INET)?; packet.write_u32::(DNSCRYPT_CERTS_RENEWAL)?; packet.write_u16::(1 + cert_bin.len() as u16)?; packet.write_u8(cert_bin.len() as u8)?; packet.extend_from_slice(cert_bin); ensure!(packet.len() < DNS_MAX_PACKET_SIZE, "Packet too large"); Ok(Some(packet)) } pub fn serve_truncated_response(client_packet: Vec) -> Result, Error> { ensure!(client_packet.len() >= DNS_HEADER_SIZE, "Short packet"); ensure!(qdcount(&client_packet) == 1, "No question"); ensure!( !is_response(&client_packet), "Question expected, but got a response instead" ); let offset = skip_name(&client_packet, DNS_HEADER_SIZE)?; let mut packet = client_packet; ensure!(packet.len() - offset >= 4, "Short packet"); packet.truncate(offset + 4); an_ns_ar_count_clear(&mut packet); authoritative_response(&mut packet); truncate(&mut packet); Ok(packet) } pub fn qtype_qclass(packet: &[u8]) -> Result<(u16, u16), Error> { ensure!(packet.len() >= DNS_HEADER_SIZE, "Short packet"); ensure!(qdcount(packet) == 1, "No question"); let offset = skip_name(packet, DNS_HEADER_SIZE)?; ensure!(packet.len() - offset >= 4, "Short packet"); let qtype = BigEndian::read_u16(&packet[offset..]); let qclass = BigEndian::read_u16(&packet[offset + 2..]); Ok((qtype, qclass)) } fn parse_txt_rrdata Result<(), Error>>( rrdata: &[u8], mut cb: F, ) -> Result<(), Error> { let rrdata_len = rrdata.len(); let mut offset = 0; while offset < rrdata_len { let part_len = rrdata[offset] as usize; if part_len == 0 { break; } ensure!(rrdata_len - offset > part_len, "Short TXT RR data"); offset += 1; let part_bin = &rrdata[offset..offset + part_len]; let part = std::str::from_utf8(part_bin)?; cb(part)?; offset += part_len; } Ok(()) } pub fn query_meta(packet: &mut Vec) -> Result, Error> { let packet_len = packet.len(); ensure!(packet_len > DNS_OFFSET_QUESTION, "Short packet"); ensure!(packet_len <= DNS_MAX_PACKET_SIZE, "Large packet"); ensure!(qdcount(packet) == 1, "No question"); let mut offset = skip_name(packet, DNS_OFFSET_QUESTION)?; assert!(offset > DNS_OFFSET_QUESTION); ensure!(packet_len - offset >= 4, "Short packet"); offset += 4; let (ancount, nscount, arcount) = (ancount(packet), nscount(packet), arcount(packet)); offset = traverse_rrs( packet, offset, ancount as usize + nscount as usize, |_offset| Ok(()), )?; let mut token = None; traverse_rrs(packet, offset, arcount as _, |mut offset| { let qtype = BigEndian::read_u16(&packet[offset..]); let qclass = BigEndian::read_u16(&packet[offset + 2..]); if qtype != DNS_TYPE_TXT || qclass != DNS_CLASS_INET { return Ok(()); } let len = BigEndian::read_u16(&packet[offset + 8..]) as usize; offset += 10; ensure!(packet_len - offset >= len, "Short packet"); let rrdata = &packet[offset..offset + len]; parse_txt_rrdata(rrdata, |txt| { if txt.len() < 7 || !txt.starts_with("token:") { return Ok(()); } ensure!(token.is_none(), "Duplicate token"); let found_token = &txt[6..]; let found_token = found_token.to_owned(); token = Some(found_token); Ok(()) })?; Ok(()) })?; if token.is_some() { arcount_clear(packet)?; packet.truncate(offset); } Ok(token) } pub fn serve_nxdomain_response(client_packet: Vec) -> Result, Error> { ensure!(client_packet.len() >= DNS_HEADER_SIZE, "Short packet"); ensure!(qdcount(&client_packet) == 1, "No question"); ensure!( !is_response(&client_packet), "Question expected, but got a response instead" ); let offset = skip_name(&client_packet, DNS_HEADER_SIZE)?; let mut packet = client_packet; ensure!(packet.len() - offset >= 4, "Short packet"); packet.truncate(offset + 4); an_ns_ar_count_clear(&mut packet); authoritative_response(&mut packet); set_rcode_nxdomain(&mut packet); Ok(packet) } pub fn serve_blocked_response(client_packet: Vec) -> Result, Error> { ensure!(client_packet.len() >= DNS_HEADER_SIZE, "Short packet"); ensure!(qdcount(&client_packet) == 1, "No question"); ensure!( !is_response(&client_packet), "Question expected, but got a response instead" ); let offset = skip_name(&client_packet, DNS_HEADER_SIZE)?; let mut packet = client_packet; ensure!(packet.len() - offset >= 4, "Short packet"); packet.truncate(offset + 4); an_ns_ar_count_clear(&mut packet); authoritative_response(&mut packet); let hinfo_cpu = b"Query blocked"; let hinfo_rdata = b"by the DNS server"; let rdata_len = 1 + hinfo_cpu.len() + 1 + hinfo_rdata.len(); ancount_inc(&mut packet)?; packet.write_u16::(0xc000 + DNS_HEADER_SIZE as u16)?; packet.write_u16::(DNS_TYPE_HINFO)?; packet.write_u16::(DNS_CLASS_INET)?; packet.write_u32::(60)?; packet.write_u16::(rdata_len as _)?; packet.push(hinfo_cpu.len() as u8); packet.extend_from_slice(hinfo_cpu); packet.push(hinfo_rdata.len() as u8); packet.extend_from_slice(hinfo_rdata); Ok(packet) } pub fn serve_ip_response(client_packet: Vec, ip: IpAddr, ttl: u32) -> Result, Error> { ensure!(client_packet.len() >= DNS_HEADER_SIZE, "Short packet"); ensure!(qdcount(&client_packet) == 1, "No question"); ensure!( !is_response(&client_packet), "Question expected, but got a response instead" ); let offset = skip_name(&client_packet, DNS_HEADER_SIZE)?; let mut packet = client_packet; ensure!(packet.len() - offset >= 4, "Short packet"); packet.truncate(offset + 4); an_ns_ar_count_clear(&mut packet); authoritative_response(&mut packet); ancount_inc(&mut packet)?; packet.write_u16::(0xc000 + DNS_HEADER_SIZE as u16)?; match ip { IpAddr::V4(ip) => { packet.write_u16::(DNS_TYPE_A)?; packet.write_u16::(DNS_CLASS_INET)?; packet.write_u32::(ttl)?; packet.write_u16::(4)?; packet.extend_from_slice(&ip.octets()); } IpAddr::V6(ip) => { packet.write_u16::(DNS_TYPE_AAAA)?; packet.write_u16::(DNS_CLASS_INET)?; packet.write_u32::(ttl)?; packet.write_u16::(16)?; packet.extend_from_slice(&ip.octets()); } }; Ok(packet) }