Merge branch 'master' of github.com:jedisct1/rust-dnscrypt-server

* 'master' of github.com:jedisct1/rust-dnscrypt-server: (30 commits)
  Update Prometheus and friends
  Remove nightly feature from clap
  client_ttl_jitter -> client_ttl_holdon
  Use specific lengths for big arrays
  Update serde-big-array requirement from 0.2.0 to 0.3.0
  Update deps
  Add decreasing TTLs with jitter when a TTL becomes low
  Update precompiled binaries
  Bump
  Add my_ip feature
  dafuq
  Update deps
  Require tokio 0.2.17
  Update tokio dep due to a regression in the previous version
  Update precompiled binaries
  Bump
  Update deps to force a tokio update
  Revert "Disable parking_lot for tokio"
  Bump
  Disable parking_lot for tokio
  ...
pull/38/head
Frank Denis 4 years ago
commit 6a19db5edf

@ -1,6 +1,6 @@
[package] [package]
name = "encrypted-dns" name = "encrypted-dns"
version = "0.3.12" version = "0.3.17"
authors = ["Frank Denis <github@pureftpd.org>"] authors = ["Frank Denis <github@pureftpd.org>"]
edition = "2018" edition = "2018"
description = "A modern encrypted DNS server (DNSCrypt v2, Anonymized DNSCrypt, DoH)" description = "A modern encrypted DNS server (DNSCrypt v2, Anonymized DNSCrypt, DoH)"
@ -12,20 +12,20 @@ categories = ["asynchronous", "network-programming","command-line-utilities"]
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0.31"
byteorder = "1.3" byteorder = "1.3.4"
clap = { version="2.33", default-features = false, features=["wrap_help", "nightly"] } clap = { version = "2.33.1", default-features = false, features = ["wrap_help"] }
clockpro-cache = "0.1.8" clockpro-cache = "0.1.8"
coarsetime = "0.1.13" coarsetime = "0.1.13"
daemonize-simple = "0.1.4" daemonize-simple = "0.1.4"
derivative = "2.1.1" derivative = "2.1.1"
dnsstamps = "0.1.4" dnsstamps = "0.1.4"
env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] }
futures = { version = "0.3", features = ["async-await"] } futures = { version = "0.3.5", features = ["async-await"] }
hyper = { version = "0.13", default_features = false, optional = true } hyper = { version = "0.13.5", default_features = false, optional = true }
ipext = "0.1" ipext = "0.1.0"
jemallocator = "0.3" jemallocator = "0.3.2"
libsodium-sys-stable="1.19" libsodium-sys-stable= "1.19.5"
log = { version = "0.4.8", features = ["std", "release_max_level_debug"] } log = { version = "0.4.8", features = ["std", "release_max_level_debug"] }
socket2 = "0.3" socket2 = "0.3"
parking_lot = "0.10" parking_lot = "0.10"

@ -21,7 +21,7 @@ All of these can be served simultaneously, on the same port (usually port 443).
### Option 1: precompiled binary for Linux ### Option 1: precompiled binary for Linux
Download the Encrypted DNS Server Download the Encrypted DNS Server
[precompiled application for Linux (x86_64)](https://github.com/jedisct1/encrypted-dns-server/suites/433868705/artifacts/1517465). [precompiled application for Linux (x86_64)](https://github.com/jedisct1/encrypted-dns-server/suites/612418335/artifacts/4698672)
And make the application executable: And make the application executable:
@ -32,7 +32,7 @@ chmod +x encrypted-dns
Nothing else has to be installed. It doesn't require any external dependencies. Nothing else has to be installed. It doesn't require any external dependencies.
A [Debian package](https://github.com/jedisct1/encrypted-dns-server/suites/433868705/artifacts/1517464) A [Debian package](https://github.com/jedisct1/encrypted-dns-server/suites/612418335/artifacts/4698671)
for Linux/x86_64 is also available. for Linux/x86_64 is also available.
In this package, the example configuration file can be found in `/usr/share/doc/encrypted-dns/`. In this package, the example configuration file can be found in `/usr/share/doc/encrypted-dns/`.
@ -109,6 +109,10 @@ Putting it in a directory that is only readable by the super-user is not a bad i
Domains can be filtered directly by the proxy, see the `[filtering]` section of the configuration file. Domains can be filtered directly by the proxy, see the `[filtering]` section of the configuration file.
## Access control
Access control can be enabled in the `[access_control]` section and configured with the `query_meta` configuration value of `dnscrypt-proxy`.
## Prometheus metrics ## Prometheus metrics
Prometheus metrics can optionally be enabled in order to monitor performance, cache efficiency, and more. Prometheus metrics can optionally be enabled in order to monitor performance, cache efficiency, and more.

@ -83,6 +83,13 @@ cache_ttl_max = 86400
cache_ttl_error = 600 cache_ttl_error = 600
## DNS cache: to avoid bursts of traffic for popular queries when an
## RRSET expires, hold a TTL received from an upstream server for
## `client_ttl_holdon` seconds before decreasing it in client responses.
client_ttl_holdon = 60
## Run as a background process ## Run as a background process
daemonize = false daemonize = false
@ -114,6 +121,11 @@ daemonize = false
# chroot = "/var/empty" # chroot = "/var/empty"
## Queries sent to that name will return the client IP address.
## This can be very useful for debugging, or to check that relaying works.
my_ip = "my.ip"
#################################### ####################################
# DNSCrypt settings # # DNSCrypt settings #
@ -221,3 +233,24 @@ allow_non_reserved_ports = false
# Blacklisted upstream IP addresses # Blacklisted upstream IP addresses
blacklisted_ips = [ "93.184.216.34" ] blacklisted_ips = [ "93.184.216.34" ]
################################
# Access control #
################################
[access_control]
# Enable access control
enabled = false
# Only allow access to client queries including one of these random tokens
# Tokens can be configured in the `query_meta` section of `dnscrypt-proxy` as
# `query_meta = ["token:..."]` -- Replace ... with the token to use by the client.
# Example: `query_meta = ["token:Y2oHkDJNHz"]`
tokens = ["Y2oHkDJNHz", "G5zY3J5cHQtY", "C5zZWN1cmUuZG5z"]

@ -43,7 +43,7 @@ impl BlackList {
while line.ends_with('.') { while line.ends_with('.') {
line = &line[..line.len() - 1]; line = &line[..line.len() - 1];
} }
let qname = line.as_bytes().to_vec().to_ascii_lowercase(); let qname = line.as_bytes().to_ascii_lowercase();
if qname.is_empty() { if qname.is_empty() {
bail!("Unexpected blacklist rule at line {}", line_nb) bail!("Unexpected blacklist rule at line {}", line_nb)
} }
@ -53,7 +53,7 @@ impl BlackList {
} }
pub fn find(&self, qname: &[u8]) -> bool { pub fn find(&self, qname: &[u8]) -> bool {
let qname = qname.to_vec().to_ascii_lowercase(); let qname = qname.to_ascii_lowercase();
let mut qname = qname.as_slice(); let mut qname = qname.as_slice();
let map = &self.inner.map; let map = &self.inner.map;
let mut iterations = self.max_iterations; let mut iterations = self.max_iterations;

@ -9,6 +9,7 @@ use std::sync::Arc;
pub struct CachedResponse { pub struct CachedResponse {
response: Vec<u8>, response: Vec<u8>,
expiry: Instant, expiry: Instant,
original_ttl: u32,
} }
impl CachedResponse { impl CachedResponse {
@ -16,20 +17,37 @@ impl CachedResponse {
let ttl = dns::min_ttl(&response, cache.ttl_min, cache.ttl_max, cache.ttl_error) let ttl = dns::min_ttl(&response, cache.ttl_min, cache.ttl_max, cache.ttl_error)
.unwrap_or(cache.ttl_error); .unwrap_or(cache.ttl_error);
let expiry = Instant::recent() + Duration::from_secs(u64::from(ttl)); let expiry = Instant::recent() + Duration::from_secs(u64::from(ttl));
CachedResponse { response, expiry } CachedResponse {
response,
expiry,
original_ttl: ttl,
}
} }
#[inline]
pub fn set_tid(&mut self, tid: u16) { pub fn set_tid(&mut self, tid: u16) {
dns::set_tid(&mut self.response, tid) dns::set_tid(&mut self.response, tid)
} }
#[inline]
pub fn into_response(self) -> Vec<u8> { pub fn into_response(self) -> Vec<u8> {
self.response self.response
} }
#[inline]
pub fn has_expired(&self) -> bool { pub fn has_expired(&self) -> bool {
Instant::recent() > self.expiry Instant::recent() > self.expiry
} }
#[inline]
pub fn ttl(&self) -> u32 {
(self.expiry - Instant::recent()).as_secs() as _
}
#[inline]
pub fn original_ttl(&self) -> u32 {
self.original_ttl
}
} }
#[derive(Clone, Derivative)] #[derive(Clone, Derivative)]
@ -37,9 +55,9 @@ impl CachedResponse {
pub struct Cache { pub struct Cache {
#[derivative(Debug = "ignore")] #[derivative(Debug = "ignore")]
cache: Arc<Mutex<ClockProCache<u128, CachedResponse>>>, cache: Arc<Mutex<ClockProCache<u128, CachedResponse>>>,
ttl_min: u32, pub ttl_min: u32,
ttl_max: u32, pub ttl_max: u32,
ttl_error: u32, pub ttl_error: u32,
} }
impl Cache { impl Cache {

@ -2,13 +2,18 @@ use crate::crypto::*;
use crate::dnscrypt_certs::*; use crate::dnscrypt_certs::*;
use crate::errors::*; use crate::errors::*;
use std::fs::File; use std::fs;
use std::io::prelude::*;
use std::mem; use std::mem;
use std::net::{IpAddr, SocketAddr}; use std::net::{IpAddr, SocketAddr};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use tokio::prelude::*; use tokio::prelude::*;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AccessControlConfig {
pub enabled: bool,
pub tokens: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AnonymizedDNSConfig { pub struct AnonymizedDNSConfig {
pub enabled: bool, pub enabled: bool,
@ -76,9 +81,12 @@ pub struct Config {
pub daemonize: bool, pub daemonize: bool,
pub pid_file: Option<PathBuf>, pub pid_file: Option<PathBuf>,
pub log_file: Option<PathBuf>, pub log_file: Option<PathBuf>,
pub my_ip: Option<String>,
pub client_ttl_holdon: Option<u32>,
#[cfg(feature = "metrics")] #[cfg(feature = "metrics")]
pub metrics: Option<MetricsConfig>, pub metrics: Option<MetricsConfig>,
pub anonymized_dns: Option<AnonymizedDNSConfig>, pub anonymized_dns: Option<AnonymizedDNSConfig>,
pub access_control: Option<AccessControlConfig>,
} }
impl Config { impl Config {
@ -91,9 +99,7 @@ impl Config {
} }
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Config, Error> { pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Config, Error> {
let mut fd = File::open(path)?; let toml = fs::read_to_string(path)?;
let mut toml = String::new();
fd.read_to_string(&mut toml)?;
Config::from_string(&toml) Config::from_string(&toml)
} }
} }
@ -135,9 +141,7 @@ impl State {
} }
pub fn from_file<P: AsRef<Path>>(path: P, key_cache_capacity: usize) -> Result<Self, Error> { pub fn from_file<P: AsRef<Path>>(path: P, key_cache_capacity: usize) -> Result<Self, Error> {
let mut fp = File::open(path.as_ref())?; let state_bin = fs::read(path)?;
let mut state_bin = vec![];
fp.read_to_end(&mut state_bin)?;
let mut state: State = toml::from_slice(&state_bin)?; let mut state: State = toml::from_slice(&state_bin)?;
for params_set in &mut state.dnscrypt_encryption_params_set { for params_set in &mut state.dnscrypt_encryption_params_set {
params_set.add_key_cache(key_cache_capacity); params_set.add_key_cache(key_cache_capacity);

@ -23,7 +23,7 @@ impl Signature {
} }
} }
big_array! { BigArray; } big_array! { BigArray; crypto_sign_SECRETKEYBYTES as usize }
#[derive(Serialize, Deserialize, Derivative, Clone)] #[derive(Serialize, Deserialize, Derivative, Clone)]
#[derivative(Default)] #[derivative(Default)]

@ -2,6 +2,7 @@ use crate::dnscrypt_certs::*;
use crate::errors::*; use crate::errors::*;
use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use std::net::IpAddr;
use std::sync::Arc; use std::sync::Arc;
pub const DNS_MAX_HOSTNAME_SIZE: usize = 256; pub const DNS_MAX_HOSTNAME_SIZE: usize = 256;
@ -104,6 +105,12 @@ fn arcount_inc(packet: &mut [u8]) -> Result<(), Error> {
Ok(()) Ok(())
} }
#[inline]
fn arcount_clear(packet: &mut [u8]) -> Result<(), Error> {
BigEndian::write_u16(&mut packet[10..], 0);
Ok(())
}
#[inline] #[inline]
pub fn an_ns_ar_count_clear(packet: &mut [u8]) { pub fn an_ns_ar_count_clear(packet: &mut [u8]) {
packet[6..12].iter_mut().for_each(|x| *x = 0); packet[6..12].iter_mut().for_each(|x| *x = 0);
@ -312,13 +319,13 @@ fn traverse_rrs<F: FnMut(usize) -> Result<(), Error>>(
for _ in 0..rrcount { for _ in 0..rrcount {
offset = skip_name(packet, offset)?; offset = skip_name(packet, offset)?;
ensure!(packet_len - offset >= 10, "Short packet"); ensure!(packet_len - offset >= 10, "Short packet");
cb(offset)?;
let rdlen = BigEndian::read_u16(&packet[offset + 8..]) as usize; let rdlen = BigEndian::read_u16(&packet[offset + 8..]) as usize;
offset += 10;
ensure!( ensure!(
packet_len - offset >= rdlen, packet_len - offset >= 10 + rdlen,
"Record length would exceed packet length" "Record length would exceed packet length"
); );
cb(offset)?;
offset += 10;
offset += rdlen; offset += rdlen;
} }
Ok(offset) Ok(offset)
@ -334,13 +341,13 @@ fn traverse_rrs_mut<F: FnMut(&mut [u8], usize) -> Result<(), Error>>(
for _ in 0..rrcount { for _ in 0..rrcount {
offset = skip_name(packet, offset)?; offset = skip_name(packet, offset)?;
ensure!(packet_len - offset >= 10, "Short packet"); ensure!(packet_len - offset >= 10, "Short packet");
cb(packet, offset)?;
let rdlen = BigEndian::read_u16(&packet[offset + 8..]) as usize; let rdlen = BigEndian::read_u16(&packet[offset + 8..]) as usize;
offset += 10;
ensure!( ensure!(
packet_len - offset >= rdlen, packet_len - offset >= 10 + rdlen,
"Record length would exceed packet length" "Record length would exceed packet length"
); );
cb(packet, offset)?;
offset += 10;
offset += rdlen; offset += rdlen;
} }
Ok(offset) Ok(offset)
@ -374,6 +381,28 @@ pub fn min_ttl(packet: &[u8], min_ttl: u32, max_ttl: u32, failure_ttl: u32) -> R
Ok(found_min_ttl) 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<u8>, max_payload_size: u16) -> Result<(), Error> { fn add_edns_section(packet: &mut Vec<u8>, max_payload_size: u16) -> Result<(), Error> {
let opt_rr: [u8; 11] = [ let opt_rr: [u8; 11] = [
0, 0,
@ -502,6 +531,73 @@ pub fn qtype_qclass(packet: &[u8]) -> Result<(u16, u16), Error> {
Ok((qtype, qclass)) Ok((qtype, qclass))
} }
fn parse_txt_rrdata<F: FnMut(&str) -> 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<u8>) -> Result<Option<String>, 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<u8>) -> Result<Vec<u8>, Error> { pub fn serve_nxdomain_response(client_packet: Vec<u8>) -> Result<Vec<u8>, Error> {
ensure!(client_packet.len() >= DNS_HEADER_SIZE, "Short packet"); ensure!(client_packet.len() >= DNS_HEADER_SIZE, "Short packet");
ensure!(qdcount(&client_packet) == 1, "No question"); ensure!(qdcount(&client_packet) == 1, "No question");
@ -547,3 +643,37 @@ pub fn serve_blocked_response(client_packet: Vec<u8>) -> Result<Vec<u8>, Error>
packet.extend_from_slice(hinfo_rdata); packet.extend_from_slice(hinfo_rdata);
Ok(packet) Ok(packet)
} }
pub fn serve_ip_response(client_packet: Vec<u8>, ip: IpAddr, ttl: u32) -> Result<Vec<u8>, 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::<BigEndian>(0xc000 + DNS_HEADER_SIZE as u16)?;
match ip {
IpAddr::V4(ip) => {
packet.write_u16::<BigEndian>(DNS_TYPE_A)?;
packet.write_u16::<BigEndian>(DNS_CLASS_INET)?;
packet.write_u32::<BigEndian>(ttl)?;
packet.write_u16::<BigEndian>(4)?;
packet.extend_from_slice(&ip.octets());
}
IpAddr::V6(ip) => {
packet.write_u16::<BigEndian>(DNS_TYPE_AAAA)?;
packet.write_u16::<BigEndian>(DNS_CLASS_INET)?;
packet.write_u32::<BigEndian>(ttl)?;
packet.write_u16::<BigEndian>(16)?;
packet.extend_from_slice(&ip.octets());
}
};
Ok(packet)
}

@ -38,7 +38,7 @@ impl DNSCryptCertInner {
} }
} }
big_array! { BigArray; } big_array! { BigArray; 64 }
#[derive(Derivative, Serialize, Deserialize)] #[derive(Derivative, Serialize, Deserialize)]
#[derivative(Debug, Default, Clone)] #[derivative(Debug, Default, Clone)]

@ -48,6 +48,9 @@ pub struct Globals {
pub anonymized_dns_allowed_ports: Vec<u16>, pub anonymized_dns_allowed_ports: Vec<u16>,
pub anonymized_dns_allow_non_reserved_ports: bool, pub anonymized_dns_allow_non_reserved_ports: bool,
pub anonymized_dns_blacklisted_ips: Vec<IpAddr>, pub anonymized_dns_blacklisted_ips: Vec<IpAddr>,
pub access_control_tokens: Option<Vec<String>>,
pub client_ttl_holdon: u32,
pub my_ip: Option<Vec<u8>>,
#[cfg(feature = "metrics")] #[cfg(feature = "metrics")]
#[derivative(Debug = "ignore")] #[derivative(Debug = "ignore")]
pub varz: Varz, pub varz: Varz,

@ -218,7 +218,14 @@ async fn handle_client_query(
!dns::is_response(&packet), !dns::is_response(&packet),
"Question expected, but got a response instead" "Question expected, but got a response instead"
); );
let response = resolver::get_cached_response_or_resolve(&globals, &mut packet).await?; if let Some(tokens) = &globals.access_control_tokens {
match query_meta(&mut packet)? {
None => bail!("No access token"),
Some(token) => ensure!(tokens.contains(&token), "Access token not found"),
}
}
let response =
resolver::get_cached_response_or_resolve(&globals, &client_ctx, &mut packet).await?;
encrypt_and_respond_to_query( encrypt_and_respond_to_query(
globals, globals,
client_ctx, client_ctx,
@ -702,6 +709,13 @@ fn main() -> Result<(), Error> {
anonymized_dns.blacklisted_ips, anonymized_dns.blacklisted_ips,
), ),
}; };
let access_control_tokens = match config.access_control {
Some(access_control) if access_control.enabled && !access_control.tokens.is_empty() => {
info!("Access control enabled");
Some(access_control.tokens)
}
_ => None,
};
let runtime_handle = runtime.handle(); let runtime_handle = runtime.handle();
let globals = Arc::new(Globals { let globals = Arc::new(Globals {
runtime_handle: runtime_handle.clone(), runtime_handle: runtime_handle.clone(),
@ -739,6 +753,9 @@ fn main() -> Result<(), Error> {
anonymized_dns_allowed_ports, anonymized_dns_allowed_ports,
anonymized_dns_allow_non_reserved_ports, anonymized_dns_allow_non_reserved_ports,
anonymized_dns_blacklisted_ips, anonymized_dns_blacklisted_ips,
access_control_tokens,
my_ip: config.my_ip.map(|ip| ip.as_bytes().to_ascii_lowercase()),
client_ttl_holdon: config.client_ttl_holdon.unwrap_or(60),
#[cfg(feature = "metrics")] #[cfg(feature = "metrics")]
varz: Varz::default(), varz: Varz::default(),
}); });

@ -2,10 +2,12 @@ use crate::cache::*;
use crate::dns::{self, *}; use crate::dns::{self, *};
use crate::errors::*; use crate::errors::*;
use crate::globals::*; use crate::globals::*;
use crate::ClientCtx;
use byteorder::{BigEndian, ByteOrder}; use byteorder::{BigEndian, ByteOrder};
use rand::prelude::*; use rand::prelude::*;
use siphasher::sip128::Hasher128; use siphasher::sip128::Hasher128;
use std::cmp;
use std::hash::Hasher; use std::hash::Hasher;
use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6}; use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6};
use tokio::net::{TcpStream, UdpSocket}; use tokio::net::{TcpStream, UdpSocket};
@ -226,9 +228,20 @@ pub async fn resolve(
pub async fn get_cached_response_or_resolve( pub async fn get_cached_response_or_resolve(
globals: &Globals, globals: &Globals,
client_ctx: &ClientCtx,
mut packet: &mut Vec<u8>, mut packet: &mut Vec<u8>,
) -> Result<Vec<u8>, Error> { ) -> Result<Vec<u8>, Error> {
let packet_qname = dns::qname(&packet)?; let packet_qname = dns::qname(&packet)?;
if let Some(my_ip) = &globals.my_ip {
if &packet_qname.to_ascii_lowercase() == my_ip {
let client_ip = match client_ctx {
ClientCtx::Udp(u) => u.client_addr,
ClientCtx::Tcp(t) => t.client_connection.peer_addr()?,
}
.ip();
return serve_ip_response(packet.to_vec(), client_ip, 1);
}
}
if let Some(blacklist) = &globals.blacklist { if let Some(blacklist) = &globals.blacklist {
if blacklist.find(&packet_qname) { if blacklist.find(&packet_qname) {
#[cfg(feature = "metrics")] #[cfg(feature = "metrics")]
@ -276,7 +289,14 @@ pub async fn get_cached_response_or_resolve(
#[cfg(feature = "metrics")] #[cfg(feature = "metrics")]
globals.varz.client_queries_cached.inc(); globals.varz.client_queries_cached.inc();
cached_response.set_tid(original_tid); cached_response.set_tid(original_tid);
let original_ttl = cached_response.original_ttl();
let mut ttl = cached_response.ttl();
if ttl.saturating_add(globals.client_ttl_holdon) > original_ttl {
ttl = original_ttl;
}
ttl = cmp::max(1, ttl);
let mut response = cached_response.into_response(); let mut response = cached_response.into_response();
dns::set_ttl(&mut response, ttl)?;
dns::recase_qname(&mut response, &packet_qname)?; dns::recase_qname(&mut response, &packet_qname)?;
return Ok(response); return Ok(response);
} }

Loading…
Cancel
Save