Skip to content

Commit

Permalink
dns server support user provided cert
Browse files Browse the repository at this point in the history
  • Loading branch information
ibigbug committed Sep 28, 2024
1 parent ace28d8 commit e4cfe27
Show file tree
Hide file tree
Showing 15 changed files with 410 additions and 109 deletions.
File renamed without changes.
File renamed without changes.
19 changes: 13 additions & 6 deletions clash/tests/data/config/rules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ socks-port: 8889
mixed-port: 8899

tun:
enable: true
enable: false
device-id: "dev://utun1989"
route-all: false
gateway: "198.19.0.1/32"
Expand All @@ -18,11 +18,18 @@ ipv6: true
dns:
enable: true
ipv6: true
listen: 127.0.0.1:53553
# udp: 127.0.0.1:53553
# tcp: 127.0.0.1:53553
# dot: 127.0.0.1:53554
# doh: 127.0.0.1:53555
listen:
udp: 127.0.0.1:53553
tcp: 127.0.0.1:53553
dot:
addr: 127.0.0.1:53554
hostname: dns.example.com
ca-cert: dns.crt
ca-key: dns.key
doh:
addr: 127.0.0.1:53555
ca-cert: dns.crt
ca-key: dns.key

# ipv6: false # when the false, response to AAAA questions will be empty

Expand Down
118 changes: 73 additions & 45 deletions clash_lib/src/app/dns/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,17 @@ use std::{

use ipnet::AddrParseError;
use regex::Regex;
use rustls::pki_types::{CertificateDer, PrivateKeyDer};

use serde::Deserialize;
use url::Url;

use crate::{
common::{trie, utils},
common::trie,
config::def::{DNSListen, DNSMode},
Error,
};

use super::{
dns_client::DNSNetMode,
dummy_keys::{TEST_CERT, TEST_KEY},
};
use super::dns_client::DNSNetMode;

#[derive(Clone, Debug)]
pub struct NameServer {
Expand Down Expand Up @@ -47,23 +45,32 @@ pub struct FallbackFilter {
pub domain: Vec<String>,
}

#[derive(Debug)]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct DoHConfig {
pub certificate_and_key: (Vec<CertificateDer<'static>>, PrivateKeyDer<'static>),
pub dns_hostname: Option<String>,
pub addr: SocketAddr,
pub ca_cert: DnsServerCert,
pub ca_key: DnsServerKey,
pub hostname: Option<String>,
}

#[derive(Debug)]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct DoTConfig {
pub certificate_and_key: (Vec<CertificateDer<'static>>, PrivateKeyDer<'static>),
pub addr: SocketAddr,
pub ca_cert: DnsServerCert,
pub ca_key: DnsServerKey,
}

pub type DnsServerKey = Option<String>;
pub type DnsServerCert = Option<String>;

#[derive(Debug, Default)]
pub struct DNSListenAddr {
pub udp: Option<SocketAddr>,
pub tcp: Option<SocketAddr>,
pub doh: Option<(SocketAddr, DoHConfig)>,
pub dot: Option<(SocketAddr, DoTConfig)>,
pub doh: Option<DoHConfig>,
pub dot: Option<DoTConfig>,
}

#[derive(Default)]
Expand Down Expand Up @@ -270,48 +277,69 @@ impl TryFrom<&crate::config::def::Config> for Config {
})
}
DNSListen::Multiple(map) => {
use std::path::Path;
let mut udp = None;
let mut tcp = None;
let mut doh = None;
let mut dot = None;

for (k, v) in map {
let addr = v.parse::<SocketAddr>().map_err(|_| {
Error::InvalidConfig(format!(
"invalid DNS listen address: {} -> {}",
k, v
))
})?;
match k.as_str() {
"udp" => udp = Some(addr),
"tcp" => tcp = Some(addr),
"udp" => {
let addr = v
.as_str()
.ok_or(Error::InvalidConfig(format!(
"invalid udp dns listen address - must \
be string: {:?}",
v
)))?
.parse::<SocketAddr>()
.map_err(|_| {
Error::InvalidConfig(format!(
"invalid dns listen address: {:?}",
v
))
})?;
udp = Some(addr)
}
"tcp" => {
let addr = v
.as_str()
.ok_or(Error::InvalidConfig(format!(
"invalid tcp dns listen address - must \
be string: {:?}",
v
)))?
.parse::<SocketAddr>()
.map_err(|_| {
Error::InvalidConfig(format!(
"invalid dns listen address: {:?}",
v
))
})?;
tcp = Some(addr)
}
"doh" => {
let certs =
utils::load_cert_chain(Path::new(TEST_CERT))
.unwrap();
let priv_key =
utils::load_priv_key(Path::new(TEST_KEY))
.unwrap();
let c = DoHConfig {
certificate_and_key: (certs, priv_key),
dns_hostname: Some(
"dns.example.com".to_owned(),
),
};
doh = Some((addr, c))
let c =
DoHConfig::deserialize(v).map_err(|x| {
Error::InvalidConfig(format!(
"invalid doh dns listen config: \
{:?}",
x
))
})?;

doh = Some(c)
}
"dot" => {
let certs =
utils::load_cert_chain(Path::new(TEST_CERT))
.unwrap();
let priv_key =
utils::load_priv_key(Path::new(TEST_KEY))
.unwrap();
let c = DoTConfig {
certificate_and_key: (certs, priv_key),
};
dot = Some((addr, c))
let c =
DoTConfig::deserialize(v).map_err(|x| {
Error::InvalidConfig(format!(
"invalid dot dns listen config: \
{:?}",
x
))
})?;
dot = Some(c)
}
_ => {
return Err(Error::InvalidConfig(format!(
Expand Down
2 changes: 1 addition & 1 deletion clash_lib/src/app/dns/dns_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ async fn dns_stream_builder(
let mut tls_config = ClientConfig::builder()
.with_root_certificates(GLOBAL_ROOT_STORE.clone())
.with_no_client_auth();
tls_config.alpn_protocols = vec!["dot".into()];
tls_config.alpn_protocols = vec!["dot".into(), "h2".into()];

let fut = new_tcp_stream(
*addr,
Expand Down
2 changes: 1 addition & 1 deletion clash_lib/src/app/dns/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub async fn make_clients(
let mut rv = Vec::new();

for s in servers {
debug!("building nameserver: {:?}", s);
debug!("building nameserver: {}", s);

let (host, port) = if s.net == DNSNetMode::Dhcp {
(s.address.as_str(), "0")
Expand Down
1 change: 0 additions & 1 deletion clash_lib/src/app/dns/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use mockall::automock;
mod config;
mod dhcp;
mod dns_client;
mod dummy_keys;
mod fakeip;
mod filters;
mod helper;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Test certificate and key
//! host: dns.example.com
//! TODO(#51): use real certificate and key
pub static TEST_CERT: &str = include_str!("test/test.cert");

Expand Down
Loading

0 comments on commit e4cfe27

Please sign in to comment.