Skip to content

Commit

Permalink
let's not rely on the chwd
Browse files Browse the repository at this point in the history
  • Loading branch information
ibigbug committed Sep 26, 2023
1 parent fd42e8e commit 20fcb8a
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 173 deletions.
179 changes: 123 additions & 56 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 2 additions & 8 deletions clash/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
extern crate clash_lib as clash;

use clap::Parser;
use std::env;
use std::path::PathBuf;

#[derive(Parser)]
Expand All @@ -26,13 +25,8 @@ struct Cli {
fn main() {
let cli = Cli::parse();
clash::start(clash::Options {
config: clash::Config::File(
cli.directory
.unwrap_or(env::current_dir().expect("cwd error"))
.to_string_lossy()
.to_string(),
cli.config.to_string_lossy().to_string(),
),
config: clash::Config::File("".to_string(), cli.config.to_string_lossy().to_string()),
cwd: cli.directory.map(|x| x.to_string_lossy().to_string()),
})
.unwrap();
}
49 changes: 2 additions & 47 deletions clash/tests/data/config/simple.yaml
Original file line number Diff line number Diff line change
@@ -1,47 +1,2 @@
---
port: 8888
socks-port: 8889
mixed-port: 8899


dns:
enable: true
listen: 127.0.0.1:53533
# ipv6: false # when the false, response to AAAA questions will be empty

# These nameservers are used to resolve the DNS nameserver hostnames below.
# Specify IP addresses only
default-nameserver:
- 114.114.114.114
- 8.8.8.8
enhanced-mode: fake-ip # or fake-ip
fake-ip-range: 198.18.0.1/16 # Fake IP addresses pool CIDR
# use-hosts: true # lookup hosts and return IP record

# Hostnames in this list will not be resolved with fake IPs
# i.e. questions to these domain names will always be answered with their
# real IP addresses
# fake-ip-filter:
# - '*.lan'
# - localhost.ptlogin2.qq.com

# Supports UDP, TCP, DoT, DoH. You can specify the port to connect to.
# All DNS questions are sent directly to the nameserver, without proxies
# involved. Clash answers the DNS question with the first result gathered.
nameserver:
- 114.114.114.114 # default value
- 8.8.8.8 # default value
- tls://dns.google:853 # DNS over TLS
- https://1.1.1.1/dns-query # DNS over HTTPS
- dhcp://en0 # dns from dhcp

allow-lan: true
mode: rule
log-level: debug
external-controller: 127.0.0.1:6170
experimental:
ignore-resolve-fail: true

rules:
- MATCH, DIRECT
...
port: 8080
external-controller: 127.0.0.1:9090
3 changes: 1 addition & 2 deletions clash_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ tun = { git = "https://github.com/Watfaq/rust-tun.git", rev = "5c0702b", featur
netstack-lwip = { git = "https://github.com/Watfaq/netstack-lwip.git", rev = "8c8c0b0" }
boringtun = { version = "0.6.0", features = ["device"] }



serde = { version = "1.0", features=["derive"] }
serde_yaml = "0.9"
erased-serde = "0.3.30"
Expand Down Expand Up @@ -92,6 +90,7 @@ tokio-tungstenite = "0.20.0"

tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-oslog = "0.1"


shadowsocks = { git = "https://github.com/Watfaq/shadowsocks-rust.git", optional = true, features=["aead-cipher-2022"], rev = "2021741" }
Expand Down
44 changes: 27 additions & 17 deletions clash_lib/src/app/dns/system.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,49 @@
use async_trait::async_trait;
use rand::seq::IteratorRandom;
use trust_dns_resolver::{Name, TokioAsyncResolver};

use super::{ClashResolver, ResolverKind};

pub struct SystemResolver {
resolver: TokioAsyncResolver,
}
pub struct SystemResolver;

/// SystemResolver is a resolver that uses libc getaddrinfo to resolve hostnames.
impl SystemResolver {
pub fn new() -> anyhow::Result<Self> {
let resolver = TokioAsyncResolver::tokio_from_system_conf()?;
Ok(Self { resolver })
Ok(Self)
}
}

#[async_trait]
impl ClashResolver for SystemResolver {
async fn resolve(&self, host: &str, _: bool) -> anyhow::Result<Option<std::net::IpAddr>> {
if let Ok(ip) = host.parse::<std::net::IpAddr>() {
return Ok(Some(ip));
}

let host = Name::from_str_relaxed(host)?;
let response = self.resolver.lookup_ip(host).await?;
Ok(response.iter().choose(&mut rand::thread_rng()))
let response = tokio::net::lookup_host(format!("{}:0", host))
.await?
.collect::<Vec<_>>();
Ok(response
.iter()
.map(|x| x.ip())
.choose(&mut rand::thread_rng()))
}

async fn resolve_v4(&self, host: &str, _: bool) -> anyhow::Result<Option<std::net::Ipv4Addr>> {
let host = Name::from_str_relaxed(host)?;
let response = self.resolver.lookup_ip(host).await?;
let response = tokio::net::lookup_host(format!("{}:0", host))
.await?
.collect::<Vec<_>>();
Ok(response
.iter()
.map(|x| x.ip())
.filter_map(|ip| match ip {
std::net::IpAddr::V4(ip) => Some(ip),
_ => None,
})
.choose(&mut rand::thread_rng()))
}
async fn resolve_v6(&self, host: &str, _: bool) -> anyhow::Result<Option<std::net::Ipv6Addr>> {
let host = Name::from_str_relaxed(host)?;
let response = self.resolver.lookup_ip(host).await?;
let response = tokio::net::lookup_host(format!("{}:0", host))
.await?
.collect::<Vec<_>>();
Ok(response
.iter()
.map(|x| x.ip())
.filter_map(|ip| match ip {
std::net::IpAddr::V6(ip) => Some(ip),
_ => None,
Expand Down Expand Up @@ -83,6 +84,8 @@ impl ClashResolver for SystemResolver {
mod tests {
use trust_dns_resolver::TokioAsyncResolver;

use crate::app::dns::{ClashResolver, SystemResolver};

#[tokio::test]
async fn test_system_resolver_with_bad_labels() {
let resolver = TokioAsyncResolver::tokio_from_system_conf().unwrap();
Expand All @@ -93,4 +96,11 @@ mod tests {
"proto error: Label contains invalid characters: Err(Errors { invalid_mapping, disallowed_by_std3_ascii_rules })"
);
}

#[tokio::test]
async fn test_system_resolver_default_config() {
let resolver = SystemResolver::new().unwrap();
let response = resolver.resolve("www.google.com", false).await.unwrap();
assert!(response.is_some());
}
}
8 changes: 8 additions & 0 deletions clash_lib/src/app/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use serde::Serialize;
use tokio::sync::broadcast::Sender;

use tracing::debug;
use tracing_oslog::OsLogger;
use tracing_subscriber::filter::Directive;
use tracing_subscriber::prelude::*;
use tracing_subscriber::Layer;
Expand Down Expand Up @@ -91,7 +92,14 @@ pub fn setup_logging(level: LogLevel, collector: EventCollector) -> anyhow::Resu
None
};

let ios_os_log = if cfg!(target_os = "ios") {
Some(OsLogger::new("com.watfaq.clash", "default"))
} else {
None
};

let subscriber = tracing_subscriber::registry()
.with(ios_os_log)
.with(jaeger)
.with(filter)
.with(collector)
Expand Down
4 changes: 4 additions & 0 deletions clash_lib/src/app/outbound/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,15 @@ impl OutboundManager {
proxy_names: Vec<String>,
dns_resolver: ThreadSafeDNSResolver,
cache_store: ThreadSafeCacheFile,
cwd: String,
) -> Result<Self, Error> {
let mut handlers = HashMap::new();
let mut provider_registry = HashMap::new();
let mut selector_control = HashMap::new();
let proxy_manager = ProxyManager::new(dns_resolver.clone());

Self::load_proxy_providers(
cwd,
proxy_providers,
proxy_manager.clone(),
dns_resolver.clone(),
Expand Down Expand Up @@ -553,6 +555,7 @@ impl OutboundManager {
}

async fn load_proxy_providers(
cwd: String,
proxy_providers: HashMap<String, OutboundProxyProviderDef>,
proxy_manager: ProxyManager,
resolver: ThreadSafeDNSResolver,
Expand All @@ -566,6 +569,7 @@ impl OutboundManager {
.parse::<Uri>()
.expect(format!("invalid provider url: {}", http.url).as_str()),
http.path,
Some(cwd.clone()),
resolver.clone(),
);
let hc = HealthCheck::new(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ impl Vehicle {
pub fn new<T: Into<Uri>, P: AsRef<Path>>(
url: T,
path: P,
cwd: Option<P>,
dns_resolver: ThreadSafeDNSResolver,
) -> Self {
let client = new_http_client(dns_resolver).expect("failed to create http client");
Self {
url: url.into(),
path: path.as_ref().to_path_buf(),
path: match cwd {
Some(cwd) => cwd.as_ref().join(path),
None => path.as_ref().to_path_buf(),
},
http_client: client,
}
}
Expand Down Expand Up @@ -74,6 +78,7 @@ mod tests {
let v = super::Vehicle::new(
u,
"/tmp/test_http_vehicle",
None,
r.clone() as ThreadSafeDNSResolver,
);

Expand Down
4 changes: 4 additions & 0 deletions clash_lib/src/app/router/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ impl Router {
rule_providers: HashMap<String, RuleProviderDef>,
dns_resolver: ThreadSafeDNSResolver,
mmdb: Arc<MMDB>,
cwd: String,
) -> Self {
let mut rule_provider_registry = HashMap::new();

Expand All @@ -52,6 +53,7 @@ impl Router {
&mut rule_provider_registry,
dns_resolver.clone(),
mmdb.clone(),
cwd,
)
.await
.ok();
Expand Down Expand Up @@ -106,6 +108,7 @@ impl Router {
rule_provider_registry: &mut HashMap<String, ThreadSafeRuleProvider>,
resolver: ThreadSafeDNSResolver,
mmdb: Arc<MMDB>,
cwd: String,
) -> Result<(), Error> {
for (name, provider) in rule_providers.into_iter() {
match provider {
Expand All @@ -115,6 +118,7 @@ impl Router {
.parse::<Uri>()
.expect(format!("invalid provider url: {}", http.url).as_str()),
http.path,
Some(cwd.clone()),
resolver.clone(),
);

Expand Down
35 changes: 21 additions & 14 deletions clash_lib/src/common/mmdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@ impl MMDB {
path: P,
download_url: Option<String>,
http_client: HttpClient,
) -> anyhow::Result<MMDB> {
) -> Result<MMDB, Error> {
debug!("mmdb path: {}", path.as_ref().to_string_lossy());

let cwd = std::env::current_dir().unwrap();
let mmdb_file = Path::new(&cwd).join(&path);
let mmdb_file = path.as_ref().to_path_buf();

if !mmdb_file.exists() {
if let Some(url) = download_url.as_ref() {
info!("downloading mmdb from {}", url);
Self::download(url, &mmdb_file, &http_client).await?;
Self::download(url, &mmdb_file, &http_client)
.await
.map_err(|x| Error::InvalidConfig(format!("mmdb download failed: {}", x)))?;
} else {
return Err(Error::InvalidConfig(format!(
"mmdb `{}/{}` not found and mmdb_download_url is not set",
cwd.to_string_lossy(),
"mmdb `{}` not found and mmdb_download_url is not set",
path.as_ref().to_string_lossy()
))
.into());
Expand All @@ -48,8 +48,7 @@ impl MMDB {
maxminddb::MaxMindDBError::InvalidDatabaseError(_)
| maxminddb::MaxMindDBError::IoError(_) => {
warn!(
"invalid mmdb `{}/{}`: {}, trying to download again",
cwd.to_string_lossy(),
"invalid mmdb `{}`: {}, trying to download again",
path.as_ref().to_string_lossy(),
e.to_string()
);
Expand All @@ -58,22 +57,30 @@ impl MMDB {
fs::remove_file(&mmdb_file)?;
if let Some(url) = download_url.as_ref() {
info!("downloading mmdb from {}", url);
Self::download(url, &mmdb_file, &http_client).await?;
Self::download(url, &mmdb_file, &http_client)
.await
.map_err(|x| {
Error::InvalidConfig(format!("mmdb download failed: {}", x))
})?;
Ok(MMDB {
reader: maxminddb::Reader::open_readfile(&path)?,
reader: maxminddb::Reader::open_readfile(&path).map_err(|x| {
Error::InvalidConfig(format!(
"cant open mmdb `{}`: {}",
path.as_ref().to_string_lossy(),
x.to_string()
))
})?,
})
} else {
return Err(Error::InvalidConfig(format!(
"mmdb `{}/{}` not found and mmdb_download_url is not set",
cwd.to_string_lossy(),
"mmdb `{}` not found and mmdb_download_url is not set",
path.as_ref().to_string_lossy()
))
.into());
}
}
_ => Err(Error::InvalidConfig(format!(
"cant open mmdb `{}/{}`: {}",
cwd.to_string_lossy(),
"cant open mmdb `{}`: {}",
path.as_ref().to_string_lossy(),
e.to_string()
))
Expand Down
Loading

0 comments on commit 20fcb8a

Please sign in to comment.