From 4dd786dd85e6993352333086e1df4030e013011c Mon Sep 17 00:00:00 2001 From: dev0 Date: Tue, 5 Sep 2023 00:31:22 +1000 Subject: [PATCH] fix dns lock --- Cargo.lock | 15 +- clash/tests/data/config/rules.yaml | 6 +- clash/tests/data/config/ss-loop.yaml | 740 ++++++++++++++++++ clash/tests/data/config/ss.yaml | 10 +- clash_lib/Cargo.toml | 12 +- clash_lib/src/app/api/handlers/config.rs | 2 +- clash_lib/src/app/api/handlers/dns.rs | 2 +- clash_lib/src/app/api/handlers/provider.rs | 15 +- clash_lib/src/app/api/mod.rs | 3 +- clash_lib/src/app/dispatcher.rs | 3 +- clash_lib/src/app/dns/dhcp.rs | 66 +- clash_lib/src/app/dns/dns_client.rs | 23 +- clash_lib/src/app/dns/mod.rs | 7 +- clash_lib/src/app/dns/resolver.rs | 7 +- clash_lib/src/app/dns/server/mod.rs | 13 +- clash_lib/src/app/mod.rs | 2 - clash_lib/src/app/outbound/manager.rs | 23 +- .../src/app/proxy_manager/healthcheck.rs | 29 +- .../src/app/proxy_manager/http_client.rs | 2 +- clash_lib/src/app/proxy_manager/mod.rs | 16 +- .../providers/{fether.rs => fetcher.rs} | 23 +- .../proxy_manager/providers/http_vehicle.rs | 4 +- .../src/app/proxy_manager/providers/mod.rs | 4 +- .../proxy_manager/providers/plain_provider.rs | 11 +- .../proxy_manager/providers/proxy_provider.rs | 5 +- .../providers/proxy_set_provider.rs | 16 +- clash_lib/src/app/router/mod.rs | 3 +- clash_lib/src/common/http.rs | 2 +- clash_lib/src/config/internal/proxy.rs | 1 + clash_lib/src/lib.rs | 2 +- clash_lib/src/proxy/datagram.rs | 2 +- clash_lib/src/proxy/direct/mod.rs | 3 +- clash_lib/src/proxy/fallback/mod.rs | 2 +- clash_lib/src/proxy/loadbalance/mod.rs | 3 +- clash_lib/src/proxy/mocks.rs | 2 +- clash_lib/src/proxy/mod.rs | 2 +- clash_lib/src/proxy/reject/mod.rs | 2 +- clash_lib/src/proxy/relay/mod.rs | 3 +- clash_lib/src/proxy/selector/mod.rs | 9 +- clash_lib/src/proxy/shadowsocks/datagram.rs | 2 +- clash_lib/src/proxy/shadowsocks/mod.rs | 2 +- clash_lib/src/proxy/tun/inbound.rs | 2 +- clash_lib/src/proxy/urltest/mod.rs | 2 +- clash_lib/src/proxy/utils/provider_helper.rs | 4 +- clash_lib/src/proxy/utils/socket_helpers.rs | 3 +- clash_lib/src/proxy/vmess/mod.rs | 2 +- 46 files changed, 932 insertions(+), 180 deletions(-) create mode 100644 clash/tests/data/config/ss-loop.yaml rename clash_lib/src/app/proxy_manager/providers/{fether.rs => fetcher.rs} (95%) diff --git a/Cargo.lock b/Cargo.lock index 6853629f8..894922e99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3288,8 +3288,7 @@ dependencies = [ [[package]] name = "trust-dns-client" version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1bcca49cb7115ce70857db94ebff3f2903b50e3e5c20b1def5cf9b1273455f" +source = "git+https://github.com/Watfaq/trust-dns.git?rev=ca798f2#ca798f2b931b9c85cb28bdaad7a922f3049c9394" dependencies = [ "cfg-if", "data-encoding", @@ -3356,8 +3355,7 @@ dependencies = [ [[package]] name = "trust-dns-proto" version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc775440033cb114085f6f2437682b194fa7546466024b1037e82a48a052a69" +source = "git+https://github.com/Watfaq/trust-dns.git?rev=ca798f2#ca798f2b931b9c85cb28bdaad7a922f3049c9394" dependencies = [ "async-trait", "bytes", @@ -3376,7 +3374,6 @@ dependencies = [ "ring", "rustls", "rustls-pemfile", - "rustls-webpki", "serde", "smallvec", "thiserror", @@ -3385,7 +3382,6 @@ dependencies = [ "tokio-rustls", "tracing", "url", - "webpki-roots", ] [[package]] @@ -3411,8 +3407,7 @@ dependencies = [ [[package]] name = "trust-dns-resolver" version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff7aed33ef3e8bf2c9966fccdfed93f93d46f432282ea875cd66faabc6ef2f" +source = "git+https://github.com/Watfaq/trust-dns.git?rev=ca798f2#ca798f2b931b9c85cb28bdaad7a922f3049c9394" dependencies = [ "cfg-if", "futures-util", @@ -3430,14 +3425,12 @@ dependencies = [ "tokio-rustls", "tracing", "trust-dns-proto 0.23.0", - "webpki-roots", ] [[package]] name = "trust-dns-server" version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f2863cefc06d1d5605ea937bfd8939e23687bb44dd5d136217ad9378582f9cc" +source = "git+https://github.com/Watfaq/trust-dns.git?rev=ca798f2#ca798f2b931b9c85cb28bdaad7a922f3049c9394" dependencies = [ "async-trait", "bytes", diff --git a/clash/tests/data/config/rules.yaml b/clash/tests/data/config/rules.yaml index d468a3841..c6f2b71d2 100644 --- a/clash/tests/data/config/rules.yaml +++ b/clash/tests/data/config/rules.yaml @@ -40,7 +40,7 @@ dns: - 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 + - https://dns.google/dns-query # DNS over HTTPS # - dhcp://en0 # dns from dhcp allow-lan: true @@ -150,14 +150,14 @@ proxies: proxy-providers: file-provider: type: file - path: ./ss.yaml + path: ./ss-loop.yaml health-check: enable: true url: http://www.gstatic.com/generate_204 interval: 300 remote-provider: type: http - url: https://xample.com/proxies.yaml + url: https://example.com/clash/proxies interval: 300 path: ./proxies.yaml health-check: diff --git a/clash/tests/data/config/ss-loop.yaml b/clash/tests/data/config/ss-loop.yaml new file mode 100644 index 000000000..fe25bc795 --- /dev/null +++ b/clash/tests/data/config/ss-loop.yaml @@ -0,0 +1,740 @@ +allow-lan: true +dns: + default-nameserver: + - 114.114.114.114 + - 8.8.8.8 + enable: true + enhanced-mode: fake-ip + fake-ip-range: 198.18.0.1/16 + listen: 127.0.0.1:53533 + nameserver: + - 114.114.114.114 + - 8.8.8.8 + - tls://dns.google:853 + - https://1.1.1.1/dns-query + - dhcp://en0 +experimental: + ignore-resolve-fail: true +external-controller: 127.0.0.1:6170 +log-level: debug +mixed-port: 8899 +mode: rule +port: 8888 +proxies: +- cipher: aes-256-gcm + name: ss-01 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-02 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-0 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-1 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-2 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-3 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-4 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-5 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-6 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-7 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-8 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-9 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-10 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-11 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-12 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-13 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-14 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-15 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-16 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-17 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-18 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-19 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-20 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-21 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-22 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-23 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-24 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-25 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-26 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-27 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-28 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-29 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-30 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-31 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-32 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-33 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-34 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-35 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-36 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-37 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-38 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-39 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-40 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-41 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-42 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-43 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-44 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-45 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-46 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-47 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-48 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-49 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-50 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-51 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-52 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-53 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-54 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-55 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-56 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-57 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-58 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-59 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-60 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-61 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-62 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-63 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-64 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-65 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-66 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-67 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-68 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-69 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-70 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-71 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-72 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-73 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-74 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-75 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-76 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-77 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-78 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-79 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-80 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-81 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-82 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-83 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-84 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-85 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-86 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-87 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-88 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-89 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-90 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-91 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-92 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-93 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-94 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-95 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-96 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-97 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-98 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +- cipher: aes-256-gcm + name: ss-99 + password: password + port: 8388 + server: 10.0.0.13 + type: ss + udp: true +rules: +- MATCH, ss +socks-port: 8889 diff --git a/clash/tests/data/config/ss.yaml b/clash/tests/data/config/ss.yaml index cbd527571..b93a31855 100644 --- a/clash/tests/data/config/ss.yaml +++ b/clash/tests/data/config/ss.yaml @@ -43,7 +43,15 @@ experimental: ignore-resolve-fail: true proxies: - - name: "ss" + - name: "ss-01" + type: ss + server: 10.0.0.13 + port: 8388 + cipher: aes-256-gcm + password: "password" + udp: true + + - name: "ss-02" type: ss server: 10.0.0.13 port: 8388 diff --git a/clash_lib/Cargo.toml b/clash_lib/Cargo.toml index e47038170..63d6bea05 100644 --- a/clash_lib/Cargo.toml +++ b/clash_lib/Cargo.toml @@ -57,10 +57,14 @@ serde = { version = "1.0", features=["derive"] } serde_yaml = "0.9" erased-serde = "0.3.30" -trust-dns-client = "0.23" -trust-dns-resolver = "0.23" -trust-dns-server = { version = "0.23", features = ["dns-over-rustls", "dns-over-https-rustls"] } -trust-dns-proto = { version = "0.23", features = ["dns-over-rustls", "dns-over-https-rustls"]} +trust-dns-client = { git = "https://github.com/Watfaq/trust-dns.git", rev = "ca798f2" } +trust-dns-resolver = { git = "https://github.com/Watfaq/trust-dns.git", rev = "ca798f2" } +trust-dns-server = { git = "https://github.com/Watfaq/trust-dns.git", rev = "ca798f2", features = ["dns-over-rustls", "dns-over-https-rustls"] } +trust-dns-proto = { git = "https://github.com/Watfaq/trust-dns.git", rev = "ca798f2", features = ["dns-over-rustls", "dns-over-https-rustls"] } + +# trust-dns-resolver = "0.23" +# trust-dns-server = { version = "0.23", features = ["dns-over-rustls", "dns-over-https-rustls"] } +# trust-dns-proto = { version = "0.23", features = ["dns-over-rustls", "dns-over-https-rustls"]} # DoH rustls = { version = "0.21", features=["dangerous_configuration"] } rustls-pemfile = "1.0.3" diff --git a/clash_lib/src/app/api/handlers/config.rs b/clash_lib/src/app/api/handlers/config.rs index 43ee00f2b..82169ff4a 100644 --- a/clash_lib/src/app/api/handlers/config.rs +++ b/clash_lib/src/app/api/handlers/config.rs @@ -10,8 +10,8 @@ use crate::{ app::{ api::AppState, dispatcher, + dns::ThreadSafeDNSResolver, inbound::manager::{Ports, ThreadSafeInboundManager}, - ThreadSafeDNSResolver, }, config::{def, internal::config::BindAddress}, GlobalState, diff --git a/clash_lib/src/app/api/handlers/dns.rs b/clash_lib/src/app/api/handlers/dns.rs index a65731101..25733a352 100644 --- a/clash_lib/src/app/api/handlers/dns.rs +++ b/clash_lib/src/app/api/handlers/dns.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use axum::{response::IntoResponse, routing::get, Router}; use http::StatusCode; -use crate::app::{api::AppState, ThreadSafeDNSResolver}; +use crate::app::{api::AppState, dns::ThreadSafeDNSResolver}; #[derive(Clone)] struct DNSState { diff --git a/clash_lib/src/app/api/handlers/provider.rs b/clash_lib/src/app/api/handlers/provider.rs index bfce2e0fd..b425a565c 100644 --- a/clash_lib/src/app/api/handlers/provider.rs +++ b/clash_lib/src/app/api/handlers/provider.rs @@ -57,7 +57,7 @@ async fn get_providers(State(state): State) -> impl IntoResponse let mut providers = HashMap::new(); for (name, p) in outbound_manager.get_proxy_providers() { - let p = p.lock().await; + let p = p.read().await; let proxies = p.proxies().await; let proxies = futures::future::join_all(proxies.iter().map(|x| outbound_manager.get_proxy(x))); @@ -92,14 +92,14 @@ async fn find_proxy_provider_by_name( async fn get_provider( Extension(provider): Extension, ) -> impl IntoResponse { - let provider = provider.lock().await; + let provider = provider.read().await; axum::response::Json(provider.as_map().await) } async fn update_provider( Extension(provider): Extension, ) -> impl IntoResponse { - let provider = provider.lock().await; + let provider = provider.read().await; match provider.update().await { Ok(_) => (StatusCode::ACCEPTED, "provider update started").into_response(), Err(err) => ( @@ -117,9 +117,10 @@ async fn update_provider( async fn provider_healthcheck( Extension(provider): Extension, ) -> impl IntoResponse { - let provider = provider.lock().await; + let provider = provider.read().await; provider.healthcheck().await; - (StatusCode::ACCEPTED, "provider healthcheck started") + + (StatusCode::ACCEPTED, "provider healthcheck done") } async fn find_provider_proxy_by_name( @@ -128,7 +129,7 @@ async fn find_provider_proxy_by_name( mut req: Request, next: Next, ) -> Response { - let proxy = provider.lock().await.proxies().await; + let proxy = provider.read().await.proxies().await; let proxy = proxy .iter() .find(|x| Some(&x.name().to_string()) == params.get("proxy_name")); @@ -142,7 +143,7 @@ async fn find_provider_proxy_by_name( format!( "proxy {} not found in provider {}", params.get("proxy_name").unwrap(), - provider.lock().await.name() + provider.read().await.name() ), ) .into_response() diff --git a/clash_lib/src/app/api/mod.rs b/clash_lib/src/app/api/mod.rs index 3c545e409..490b61721 100644 --- a/clash_lib/src/app/api/mod.rs +++ b/clash_lib/src/app/api/mod.rs @@ -11,10 +11,11 @@ use tracing::info; use crate::{config::internal::config::Controller, GlobalState, Runner}; +use super::dns::ThreadSafeDNSResolver; use super::logging::LogEvent; use super::{ dispatcher, inbound::manager::ThreadSafeInboundManager, - outbound::manager::ThreadSafeOutboundManager, router::ThreadSafeRouter, ThreadSafeDNSResolver, + outbound::manager::ThreadSafeOutboundManager, router::ThreadSafeRouter, }; mod handlers; diff --git a/clash_lib/src/app/dispatcher.rs b/clash_lib/src/app/dispatcher.rs index 1601c9471..c409f2cee 100644 --- a/clash_lib/src/app/dispatcher.rs +++ b/clash_lib/src/app/dispatcher.rs @@ -1,6 +1,5 @@ use crate::app::outbound::manager::ThreadSafeOutboundManager; use crate::app::router::ThreadSafeRouter; -use crate::app::ThreadSafeDNSResolver; use crate::config::def::RunMode; use crate::config::internal::proxy::PROXY_DIRECT; use crate::config::internal::proxy::PROXY_GLOBAL; @@ -18,6 +17,8 @@ use tokio::task::JoinHandle; use tracing::{debug, error, info, warn}; use tracing::{event, instrument}; +use super::dns::ThreadSafeDNSResolver; + pub struct Dispatcher { outbound_manager: ThreadSafeOutboundManager, router: ThreadSafeRouter, diff --git a/clash_lib/src/app/dns/dhcp.rs b/clash_lib/src/app/dns/dhcp.rs index ce9939a47..2a50cb7b3 100644 --- a/clash_lib/src/app/dns/dhcp.rs +++ b/clash_lib/src/app/dns/dhcp.rs @@ -14,6 +14,7 @@ use std::sync::Arc; use std::time::{Duration, Instant}; use std::{env, io}; use tokio::net::UdpSocket; +use tokio::sync::Mutex; use tokio::task::yield_now; use tracing::{debug, warn}; @@ -25,39 +26,37 @@ const IFACE_TTL: Duration = Duration::from_secs(20); const DHCP_TTL: Duration = Duration::from_secs(3600); const DHCP_TIMEOUT: Duration = Duration::from_secs(60); -pub struct DhcpClient { - iface: String, - - iface_addr: ipnet::IpNet, - +struct Inner { clients: Vec, iface_expires_at: std::time::Instant, dns_expires_at: std::time::Instant, + iface_addr: ipnet::IpNet, +} + +pub struct DhcpClient { + iface: String, + + inner: Mutex, } impl Debug for DhcpClient { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("DhcpClient") .field("iface", &self.iface) - .field("iface_addr", &self.iface_addr) - .field("iface_expires_at", &self.iface_expires_at) - .field("clients", &self.clients) - .field("dns_expires_at", &self.dns_expires_at) .finish() } } #[async_trait] impl Client for DhcpClient { - async fn exchange(&mut self, msg: &Message) -> anyhow::Result { + async fn exchange(&self, msg: &Message) -> anyhow::Result { let clients = self.resolve().await?; let mut dbg_str = vec![]; - for c in clients { - let l = c.lock().await; - dbg_str.push(format!("{:?}", l)); + for c in &clients { + dbg_str.push(format!("{:?}", c)); } debug!("using clients: {:?}", dbg_str); - tokio::time::timeout(DHCP_TIMEOUT, Resolver::batch_exchange(clients, msg)).await? + tokio::time::timeout(DHCP_TIMEOUT, Resolver::batch_exchange(&clients, msg)).await? } } @@ -65,18 +64,22 @@ impl DhcpClient { pub async fn new(iface: &str) -> Self { Self { iface: iface.to_owned(), - iface_addr: ipnet::IpNet::default(), - clients: vec![], - iface_expires_at: Instant::now(), - dns_expires_at: Instant::now(), + inner: Mutex::new(Inner { + clients: vec![], + iface_expires_at: Instant::now(), + dns_expires_at: Instant::now(), + iface_addr: ipnet::IpNet::default(), + }), } } - async fn resolve(&mut self) -> io::Result<&Vec> { - let expired = self.update_if_lease_expired()?; + async fn resolve(&self) -> io::Result> { + let expired = self.update_if_lease_expired().await?; if expired { let dns = probe_dns_server(&self.iface).await?; - self.clients = make_clients( + let mut inner = self.inner.lock().await; + + inner.clients = make_clients( dns.into_iter() .map(|s| NameServer { net: DNSNetMode::UDP, @@ -89,21 +92,22 @@ impl DhcpClient { .await; } - Ok(&self.clients) + Ok(self.inner.lock().await.clients.clone()) } /// Check if interface updated or DHCP changed /// and update if necessary - fn update_if_lease_expired(&mut self) -> io::Result { - if self.clients.is_empty() { + async fn update_if_lease_expired(&self) -> io::Result { + let mut inner = self.inner.lock().await; + if inner.clients.is_empty() { return Ok(true); } - if Instant::now() < self.iface_expires_at { + if Instant::now() < inner.iface_expires_at { return Ok(false); } - self.iface_expires_at = Instant::now().add(IFACE_TTL); + inner.iface_expires_at = Instant::now().add(IFACE_TTL); let iface = network_interface::NetworkInterface::show() .map_err(|x| io::Error::new(io::ErrorKind::Other, format!("list ifaces: {:?}", x)))? @@ -124,9 +128,9 @@ impl DhcpClient { match addr { Addr::V4(v4) => { - if Instant::now() < self.dns_expires_at - && self.iface_addr.addr() == v4.ip - && self.iface_addr.netmask() + if Instant::now() < inner.dns_expires_at + && inner.iface_addr.addr() == v4.ip + && inner.iface_addr.netmask() == v4.netmask.ok_or(io::Error::new( io::ErrorKind::Other, format!("no netmask on iface: {}", self.iface), @@ -134,8 +138,8 @@ impl DhcpClient { { Ok(false) } else { - self.dns_expires_at = Instant::now().add(DHCP_TTL); - self.iface_addr = ipnet::IpNet::new( + inner.dns_expires_at = Instant::now().add(DHCP_TTL); + inner.iface_addr = ipnet::IpNet::new( v4.ip.into(), u32::from( v4.netmask diff --git a/clash_lib/src/app/dns/dns_client.rs b/clash_lib/src/app/dns/dns_client.rs index fabd45c3b..987f16f1d 100644 --- a/clash_lib/src/app/dns/dns_client.rs +++ b/clash_lib/src/app/dns/dns_client.rs @@ -4,7 +4,6 @@ use std::str::FromStr; use std::{net, sync::Arc, time::Duration}; use async_trait::async_trait; -use futures::lock::Mutex; use rustls::{ClientConfig, OwnedTrustAnchor, RootCertStore}; use trust_dns_client::{ client, proto::iocompat::AsyncIoTokioAsStd, tcp::TcpClientStream, udp::UdpClientStream, @@ -88,7 +87,7 @@ impl DnsClient { pub async fn new(opts: Opts) -> anyhow::Result { // TODO: use proxy to connect? match &opts.net { - DNSNetMode::DHCP => Ok(Arc::new(Mutex::new(DhcpClient::new(&opts.host).await))), + DNSNetMode::DHCP => Ok(Arc::new(DhcpClient::new(&opts.host).await)), other => { let ip = if let Some(r) = opts.r { @@ -134,14 +133,14 @@ impl DnsClient { .map_err(|x| Error::DNSError(x.to_string()))?; tokio::spawn(bg); - Ok(Arc::new(Mutex::new(Self { + Ok(Arc::new(Self { c: client, host: opts.host, port: opts.port, net: opts.net, iface: opts.iface, - }))) + })) } DNSNetMode::TCP => { let (stream, sender) = TcpClientStream::>::with_bind_addr_and_timeout( @@ -160,13 +159,14 @@ impl DnsClient { .await .map_err(|x| Error::DNSError(x.to_string()))?; tokio::spawn(bg); - Ok(Arc::new(Mutex::new(Self { + Ok(Arc::new(Self { c: client, + host: opts.host, port: opts.port, net: opts.net, iface: opts.iface, - }))) + })) } DNSNetMode::DoT => { let mut root_store = RootCertStore::empty(); @@ -210,14 +210,14 @@ impl DnsClient { .map_err(|x| Error::DNSError(x.to_string()))?; tokio::spawn(bg); - Ok(Arc::new(Mutex::new(Self { + Ok(Arc::new(Self { c: client, host: opts.host, port: opts.port, net: opts.net, iface: opts.iface, - }))) + })) } DNSNetMode::DoH => { let mut root_store = RootCertStore::empty(); @@ -262,13 +262,14 @@ impl DnsClient { .map_err(|x| Error::DNSError(x.to_string()))?; tokio::spawn(bg); - Ok(Arc::new(Mutex::new(Self { + Ok(Arc::new(Self { c: client, + host: opts.host, port: opts.port, net: opts.net, iface: opts.iface, - }))) + })) } _ => unreachable!("."), } @@ -290,7 +291,7 @@ impl Debug for DnsClient { #[async_trait] impl Client for DnsClient { - async fn exchange(&mut self, msg: &Message) -> anyhow::Result { + async fn exchange(&self, msg: &Message) -> anyhow::Result { let mut req = DnsRequest::new(msg.clone(), DnsRequestOptions::default()); req.set_id(rand::random::()); self.c diff --git a/clash_lib/src/app/dns/mod.rs b/clash_lib/src/app/dns/mod.rs index 8d5767ff7..374ad0fa7 100644 --- a/clash_lib/src/app/dns/mod.rs +++ b/clash_lib/src/app/dns/mod.rs @@ -48,17 +48,18 @@ macro_rules! dns_warn { #[async_trait] pub trait Client: Sync + Send + Debug { - // TODO: make this non mutable - async fn exchange(&mut self, msg: &op::Message) -> anyhow::Result; + async fn exchange(&self, msg: &op::Message) -> anyhow::Result; } -type ThreadSafeDNSClient = Arc>; +type ThreadSafeDNSClient = Arc; pub enum ResolverKind { Clash, System, } +pub type ThreadSafeDNSResolver = Arc; + /// A implementation of "anti-poisoning" Resolver /// it can hold multiple clients in different protocols /// each client can also hold a "default_resolver" diff --git a/clash_lib/src/app/dns/resolver.rs b/clash_lib/src/app/dns/resolver.rs index d229f412e..eaade84b8 100644 --- a/clash_lib/src/app/dns/resolver.rs +++ b/clash_lib/src/app/dns/resolver.rs @@ -10,7 +10,6 @@ use tracing::{debug, warn}; use trust_dns_proto::{op, rr}; -use crate::app::ThreadSafeDNSResolver; use crate::config::def::DNSMode; use crate::dns::helper::make_clients; use crate::dns::ThreadSafeDNSClient; @@ -23,7 +22,7 @@ use super::{ filters::{DomainFilter, FallbackDomainFilter, FallbackIPFilter, GeoIPFilter, IPNetFilter}, Config, }; -use super::{ClashResolver, ResolverKind}; +use super::{ClashResolver, ResolverKind, ThreadSafeDNSResolver}; static TTL: Duration = Duration::from_secs(60); @@ -180,9 +179,7 @@ impl Resolver { for c in clients { queries.push( async move { - c.lock() - .await - .exchange(message) + c.exchange(message) .inspect_err(|x| warn!("DNS resolve error: {}", x.to_string())) .await } diff --git a/clash_lib/src/app/dns/server/mod.rs b/clash_lib/src/app/dns/server/mod.rs index e61fe3e0c..85d3b518f 100644 --- a/clash_lib/src/app/dns/server/mod.rs +++ b/clash_lib/src/app/dns/server/mod.rs @@ -1,18 +1,15 @@ -use std::{net::IpAddr, sync::Arc, time::Duration}; +use std::{net::IpAddr, time::Duration}; use async_trait::async_trait; use thiserror::Error; -use tokio::{ - net::{TcpListener, UdpSocket}, - sync::RwLock, -}; +use tokio::net::{TcpListener, UdpSocket}; use tracing::{debug, info, warn}; use trust_dns_proto::{ op::{Header, MessageType, OpCode, ResponseCode}, rr::{ rdata::{A, AAAA}, - RData, Record, RecordType, + RData, Record, }, }; use trust_dns_server::{ @@ -21,9 +18,9 @@ use trust_dns_server::{ ServerFuture, }; -use crate::{app::ThreadSafeDNSResolver, Runner}; +use crate::Runner; -use super::Config; +use super::{Config, ThreadSafeDNSResolver}; struct DnsListener { server: ServerFuture, diff --git a/clash_lib/src/app/mod.rs b/clash_lib/src/app/mod.rs index 317c04676..b24e9854b 100644 --- a/clash_lib/src/app/mod.rs +++ b/clash_lib/src/app/mod.rs @@ -9,5 +9,3 @@ pub mod outbound; pub mod profile; pub mod proxy_manager; pub mod router; - -pub type ThreadSafeDNSResolver = Arc; diff --git a/clash_lib/src/app/outbound/manager.rs b/clash_lib/src/app/outbound/manager.rs index 0ab308767..1f510b648 100644 --- a/clash_lib/src/app/outbound/manager.rs +++ b/clash_lib/src/app/outbound/manager.rs @@ -7,6 +7,7 @@ use std::time::Duration; use tokio::sync::{Mutex, RwLock}; use tracing::info; +use crate::app::dns::ThreadSafeDNSResolver; use crate::app::proxy_manager::healthcheck::HealthCheck; use crate::app::proxy_manager::providers::file_vehicle; use crate::app::proxy_manager::providers::http_vehicle; @@ -25,7 +26,6 @@ use crate::proxy::selector::ThreadSafeSelectorControl; use crate::proxy::urltest; use crate::proxy::{reject, relay}; use crate::{ - app::ThreadSafeDNSResolver, config::internal::proxy::{OutboundGroupProtocol, OutboundProxyProtocol}, proxy::{direct, AnyOutboundHandler}, Error, @@ -235,7 +235,7 @@ impl OutboundManager { ) .map_err(|e| Error::InvalidConfig(format!("invalid hc config {}", e)))?; - let pd = Arc::new(Mutex::new( + let pd = Arc::new(RwLock::new( PlainProvider::new(name.to_owned(), proxies, hc).map_err(|x| { Error::InvalidConfig(format!("invalid provider config: {}", x)) })?, @@ -514,7 +514,7 @@ impl OutboundManager { proxy_manager.clone(), ) .unwrap(); - let pd = Arc::new(Mutex::new( + let pd = Arc::new(RwLock::new( PlainProvider::new(PROXY_GLOBAL.to_owned(), g, hc).unwrap(), )); let h = selector::Handler::new( @@ -531,11 +531,6 @@ impl OutboundManager { handlers.insert(PROXY_GLOBAL.to_owned(), Arc::new(h.clone())); selector_control.insert(PROXY_GLOBAL.to_owned(), Arc::new(Mutex::new(h))); - for provider in proxy_providers { - info!("initializing provider {}", provider.lock().await.name()); - provider.lock().await.initialize().await?; - } - Ok(()) } @@ -559,7 +554,7 @@ impl OutboundManager { vec![], http.health_check.url, http.health_check.interval, - true, + http.health_check.lazy.unwrap_or_default(), proxy_manager.clone(), ) .map_err(|e| Error::InvalidConfig(format!("invalid hc config {}", e)))?; @@ -571,7 +566,7 @@ impl OutboundManager { ) .map_err(|x| Error::InvalidConfig(format!("invalid provider config: {}", x)))?; - provider_registry.insert(name, Arc::new(Mutex::new(provider))); + provider_registry.insert(name, Arc::new(RwLock::new(provider))); } OutboundProxyProvider::File(file) => { let vehicle = file_vehicle::Vehicle::new(&file.path); @@ -579,7 +574,7 @@ impl OutboundManager { vec![], file.health_check.url, file.health_check.interval, - true, + file.health_check.lazy.unwrap_or_default(), proxy_manager.clone(), ) .map_err(|e| Error::InvalidConfig(format!("invalid hc config {}", e)))?; @@ -592,14 +587,14 @@ impl OutboundManager { ) .map_err(|x| Error::InvalidConfig(format!("invalid provider config: {}", x)))?; - provider_registry.insert(name, Arc::new(Mutex::new(provider))); + provider_registry.insert(name, Arc::new(RwLock::new(provider))); } } } for p in provider_registry.values() { - info!("initializing provider {}", p.lock().await.name()); - p.lock().await.initialize().await?; + info!("initializing provider {}", p.read().await.name()); + p.write().await.initialize().await?; } Ok(()) } diff --git a/clash_lib/src/app/proxy_manager/healthcheck.rs b/clash_lib/src/app/proxy_manager/healthcheck.rs index a08134c01..22d9e4ec7 100644 --- a/clash_lib/src/app/proxy_manager/healthcheck.rs +++ b/clash_lib/src/app/proxy_manager/healthcheck.rs @@ -18,7 +18,7 @@ pub struct HealthCheck { interval: u64, lazy: bool, proxy_manager: ThreadSafeProxyManager, - inner: Arc>, + inner: Arc>, } impl HealthCheck { @@ -34,7 +34,7 @@ impl HealthCheck { interval, lazy, proxy_manager, - inner: Arc::new(tokio::sync::Mutex::new(HealCheckInner { + inner: Arc::new(tokio::sync::RwLock::new(HealCheckInner { last_check: tokio::time::Instant::now(), proxies, task_handle: None, @@ -47,15 +47,15 @@ impl HealthCheck { let proxy_manager = self.proxy_manager.clone(); let interval = self.interval; let lazy = self.lazy; - let proxies = self.inner.lock().await.proxies.clone(); + let proxies = self.inner.read().await.proxies.clone(); let url = self.url.clone(); - let handle = tokio::spawn(async move { + tokio::spawn(async move { proxy_manager.check(&proxies, &url, None).await; }); let inner = self.inner.clone(); - let proxies = self.inner.lock().await.proxies.clone(); + let proxies = self.inner.read().await.proxies.clone(); let proxy_manager = self.proxy_manager.clone(); let url = self.url.clone(); let task_handle = tokio::spawn(async move { @@ -63,34 +63,31 @@ impl HealthCheck { loop { tokio::select! { _ = ticker.tick() => { - pm_debug!("healthcheck ticking: {}", url); + pm_debug!("healthcheck ticking: {}, lazy: {}", url, lazy); let now = tokio::time::Instant::now(); - if !lazy || now.duration_since(inner.lock().await.last_check).as_secs() >= interval { + if !lazy || now.duration_since(inner.read().await.last_check).as_secs() >= interval { proxy_manager.check(&proxies, &url, None).await; - inner.lock().await.last_check = now; + inner.write().await.last_check = now; } }, } } }); - self.inner.lock().await.task_handle = Some(Arc::new(tokio::spawn(async move { - futures::future::join_all(vec![task_handle, handle]).await; - }))); + self.inner.write().await.task_handle = Some(Arc::new(task_handle)); } pub async fn touch(&self) { - self.inner.lock().await.last_check = tokio::time::Instant::now(); + self.inner.write().await.last_check = tokio::time::Instant::now(); } pub async fn check(&self) { - self.proxy_manager - .check(&self.inner.lock().await.proxies, &self.url, None) - .await; + let proxies = self.inner.read().await.proxies.clone(); + self.proxy_manager.check(&proxies, &self.url, None).await; } pub async fn update(&self, proxies: Vec) { - self.inner.lock().await.proxies = proxies; + self.inner.write().await.proxies = proxies; } pub fn auto(&self) -> bool { diff --git a/clash_lib/src/app/proxy_manager/http_client.rs b/clash_lib/src/app/proxy_manager/http_client.rs index f568fa77e..a0c3427fa 100644 --- a/clash_lib/src/app/proxy_manager/http_client.rs +++ b/clash_lib/src/app/proxy_manager/http_client.rs @@ -9,7 +9,7 @@ use http::Uri; use tower::Service; use crate::{ - app::ThreadSafeDNSResolver, + app::dns::ThreadSafeDNSResolver, proxy::{AnyOutboundHandler, AnyStream}, session::Session, }; diff --git a/clash_lib/src/app/proxy_manager/mod.rs b/clash_lib/src/app/proxy_manager/mod.rs index 4f0cd9240..8604a257d 100644 --- a/clash_lib/src/app/proxy_manager/mod.rs +++ b/clash_lib/src/app/proxy_manager/mod.rs @@ -10,6 +10,7 @@ use std::{ use boring::ssl::{SslConnector, SslMethod}; use chrono::{DateTime, Utc}; +use futures::StreamExt; use http::Request; use hyper_boring::HttpsConnector; use serde::Serialize; @@ -23,7 +24,7 @@ use crate::{ use self::http_client::LocalConnector; -use super::ThreadSafeDNSResolver; +use super::dns::ThreadSafeDNSResolver; pub mod healthcheck; mod http_client; @@ -80,20 +81,24 @@ impl ProxyManager { url: &str, timeout: Option, ) { - let mut futures = vec![]; + let mut futs = vec![]; for proxy in proxies { let proxy = proxy.clone(); let url = url.to_owned(); let timeout = timeout.clone(); let manager = self.clone(); - futures.push(async move { + futs.push(tokio::spawn(async move { manager .url_test(proxy, url.as_str(), timeout) .await .map_err(|e| warn!("healthcheck failed: {}", e)) - }); + })); } - futures::future::join_all(futures).await; + + futures::stream::iter(futs) + .buffer_unordered(10) + .collect::>() + .await; } pub async fn alive(&self, name: &str) -> bool { @@ -213,6 +218,7 @@ impl ProxyManager { delay: result.as_ref().map(|x| x.0).unwrap_or(0), mean_delay: result.as_ref().map(|x| x.1).unwrap_or(0), }; + let mut state = self.proxy_state.write().await; let state = state.entry(name.to_owned()).or_default(); diff --git a/clash_lib/src/app/proxy_manager/providers/fether.rs b/clash_lib/src/app/proxy_manager/providers/fetcher.rs similarity index 95% rename from clash_lib/src/app/proxy_manager/providers/fether.rs rename to clash_lib/src/app/proxy_manager/providers/fetcher.rs index af5e4f3fc..71c3c0e08 100644 --- a/clash_lib/src/app/proxy_manager/providers/fether.rs +++ b/clash_lib/src/app/proxy_manager/providers/fetcher.rs @@ -6,7 +6,10 @@ use std::{ }; use chrono::{DateTime, Utc}; -use tokio::{sync::Mutex, time::Instant}; +use tokio::{ + sync::{Mutex, RwLock}, + time::Instant, +}; use tracing::info; use crate::common::utils; @@ -24,17 +27,11 @@ pub struct Fetcher { vehicle: ThreadSafeProviderVehicle, thread_handle: Option>, ticker: Option, - inner: std::sync::Arc>, + inner: std::sync::Arc>, parser: Arc>, pub on_update: Arc>>, } -/*impl Drop for Fetcher { - fn drop(&mut self) { - self.destroy(); - } -}*/ - impl Fetcher where T: Send + Sync + 'static, @@ -60,7 +57,7 @@ where interval, )), }, - inner: Arc::new(tokio::sync::Mutex::new(Inner { + inner: Arc::new(tokio::sync::RwLock::new(Inner { updated_at: SystemTime::UNIX_EPOCH, hash: [0; 16], })), @@ -77,7 +74,7 @@ where } pub async fn updated_at(&self) -> DateTime { - self.inner.lock().await.updated_at.into() + self.inner.read().await.updated_at.into() } pub async fn initial(&mut self) -> anyhow::Result { @@ -86,7 +83,7 @@ where let vehicle_path = self.vehicle.path().to_owned(); - let mut inner = self.inner.lock().await; + let mut inner = self.inner.write().await; let content = match metadata(&vehicle_path) { Ok(meta) => { @@ -146,11 +143,11 @@ where } async fn update_inner( - inner: Arc>, + inner: Arc>, vehicle: ThreadSafeProviderVehicle, parser: Arc>, ) -> anyhow::Result<(T, bool)> { - let mut this = inner.lock().await; + let mut this = inner.write().await; let content = vehicle.read().await?; let proxies = (parser.lock().await)(&content)?; diff --git a/clash_lib/src/app/proxy_manager/providers/http_vehicle.rs b/clash_lib/src/app/proxy_manager/providers/http_vehicle.rs index c892bc73b..718d86517 100644 --- a/clash_lib/src/app/proxy_manager/providers/http_vehicle.rs +++ b/clash_lib/src/app/proxy_manager/providers/http_vehicle.rs @@ -1,5 +1,5 @@ use super::{ProviderVehicle, ProviderVehicleType}; -use crate::app::ThreadSafeDNSResolver; +use crate::app::dns::ThreadSafeDNSResolver; use crate::common::errors::map_io_error; use crate::common::http::{new_http_client, HttpClient}; @@ -63,7 +63,7 @@ mod tests { use http::Uri; - use crate::app::{dns::Resolver, ThreadSafeDNSResolver}; + use crate::app::dns::{Resolver, ThreadSafeDNSResolver}; #[tokio::test] async fn test_http_vehicle() { diff --git a/clash_lib/src/app/proxy_manager/providers/mod.rs b/clash_lib/src/app/proxy_manager/providers/mod.rs index 844619076..b62003116 100644 --- a/clash_lib/src/app/proxy_manager/providers/mod.rs +++ b/clash_lib/src/app/proxy_manager/providers/mod.rs @@ -5,7 +5,7 @@ use std::fmt::{Display, Formatter}; use std::io; use std::sync::Arc; -pub mod fether; +pub mod fetcher; pub mod file_vehicle; pub mod http_vehicle; pub mod plain_provider; @@ -16,8 +16,6 @@ pub mod rule_provider; #[cfg(test)] use mockall::automock; -use crate::app::outbound::manager::ThreadSafeOutboundManager; - #[derive(PartialEq, Clone, Copy, Debug)] pub enum ProviderVehicleType { File, diff --git a/clash_lib/src/app/proxy_manager/providers/plain_provider.rs b/clash_lib/src/app/proxy_manager/providers/plain_provider.rs index 411594341..6288872dc 100644 --- a/clash_lib/src/app/proxy_manager/providers/plain_provider.rs +++ b/clash_lib/src/app/proxy_manager/providers/plain_provider.rs @@ -10,7 +10,7 @@ use crate::{app::proxy_manager::healthcheck::HealthCheck, proxy::AnyOutboundHand use super::{proxy_provider::ProxyProvider, Provider, ProviderType, ProviderVehicleType}; struct Inner { - hc: HealthCheck, + hc: Arc, } pub struct PlainProvider { @@ -23,15 +23,20 @@ impl PlainProvider { pub fn new( name: String, proxies: Vec, - mut hc: HealthCheck, + hc: HealthCheck, ) -> anyhow::Result { + let hc = Arc::new(hc); + if proxies.is_empty() { return Err(Error::InvalidConfig(format!("{}: proxies is empty", name)).into()); } if hc.auto() { debug!("kicking off healthcheck: {}", name); - hc.kick_off(); + let hc = hc.clone(); + tokio::spawn(async move { + hc.kick_off().await; + }); } Ok(Self { diff --git a/clash_lib/src/app/proxy_manager/providers/proxy_provider.rs b/clash_lib/src/app/proxy_manager/providers/proxy_provider.rs index cda5a13bb..0e5fb45ba 100644 --- a/clash_lib/src/app/proxy_manager/providers/proxy_provider.rs +++ b/clash_lib/src/app/proxy_manager/providers/proxy_provider.rs @@ -1,17 +1,18 @@ use std::sync::Arc; use async_trait::async_trait; -use tokio::sync::Mutex; +use tokio::sync::RwLock; use crate::proxy::AnyOutboundHandler; use super::Provider; -pub type ThreadSafeProxyProvider = Arc>; +pub type ThreadSafeProxyProvider = Arc>; #[async_trait] pub trait ProxyProvider: Provider { async fn proxies(&self) -> Vec; async fn touch(&self); + /// this is a blocking call, you may want to spawn a new task to run this async fn healthcheck(&self); } diff --git a/clash_lib/src/app/proxy_manager/providers/proxy_set_provider.rs b/clash_lib/src/app/proxy_manager/providers/proxy_set_provider.rs index c8a770dd7..c35f59ade 100644 --- a/clash_lib/src/app/proxy_manager/providers/proxy_set_provider.rs +++ b/clash_lib/src/app/proxy_manager/providers/proxy_set_provider.rs @@ -8,7 +8,7 @@ use serde_yaml::Value; use tracing::debug; use super::{ - fether::Fetcher, proxy_provider::ProxyProvider, Provider, ProviderType, ProviderVehicleType, + fetcher::Fetcher, proxy_provider::ProxyProvider, Provider, ProviderType, ProviderVehicleType, ThreadSafeProviderVehicle, }; use crate::{ @@ -35,7 +35,7 @@ pub struct ProxySetProvider { Box) + Send + Sync + 'static>, Box anyhow::Result> + Send + Sync + 'static>, >, - inner: std::sync::Arc>, + inner: std::sync::Arc>, } impl ProxySetProvider { @@ -55,7 +55,7 @@ impl ProxySetProvider { }); } - let inner = Arc::new(tokio::sync::Mutex::new(Inner { + let inner = Arc::new(tokio::sync::RwLock::new(Inner { proxies: vec![], hc: hc.clone(), })); @@ -67,9 +67,11 @@ impl ProxySetProvider { let hc = hc.clone(); let inner = inner_clone.clone(); tokio::spawn(async move { - let mut inner = inner.lock().await; + let mut inner = inner.write().await; inner.proxies = input.clone(); hc.update(input).await; + // check once after update + hc.check().await; }); }); @@ -160,7 +162,7 @@ impl Provider for ProxySetProvider { impl ProxyProvider for ProxySetProvider { async fn proxies(&self) -> Vec { self.inner - .lock() + .read() .await .proxies .iter() @@ -169,11 +171,11 @@ impl ProxyProvider for ProxySetProvider { } async fn touch(&self) { - self.inner.lock().await.hc.touch().await; + self.inner.read().await.hc.touch().await; } async fn healthcheck(&self) { - self.inner.lock().await.hc.check().await; + self.inner.read().await.hc.check().await; } } diff --git a/clash_lib/src/app/router/mod.rs b/clash_lib/src/app/router/mod.rs index 55cb04ba3..4ffaaf821 100644 --- a/clash_lib/src/app/router/mod.rs +++ b/clash_lib/src/app/router/mod.rs @@ -4,7 +4,6 @@ use crate::app::router::rules::domain_suffix::DomainSuffix; use crate::app::router::rules::ipcidr::IPCIDR; use crate::app::router::rules::ruleset::RuleSet; use crate::app::router::rules::RuleMatcher; -use crate::app::ThreadSafeDNSResolver; use crate::common::http::new_http_client; use crate::config::internal::rule::RuleType; @@ -15,6 +14,8 @@ use std::sync::Arc; use tracing::info; +use super::dns::ThreadSafeDNSResolver; + mod mmdb; mod rules; diff --git a/clash_lib/src/common/http.rs b/clash_lib/src/common/http.rs index ceddc1bb7..159a0f11f 100644 --- a/clash_lib/src/common/http.rs +++ b/clash_lib/src/common/http.rs @@ -11,7 +11,7 @@ use hyper_boring::HttpsConnector; use tower::Service; use crate::{ - app::ThreadSafeDNSResolver, + app::dns::ThreadSafeDNSResolver, proxy::{utils::new_tcp_stream, AnyStream}, }; diff --git a/clash_lib/src/config/internal/proxy.rs b/clash_lib/src/config/internal/proxy.rs index a46b021a2..64e1557a7 100644 --- a/clash_lib/src/config/internal/proxy.rs +++ b/clash_lib/src/config/internal/proxy.rs @@ -324,6 +324,7 @@ pub struct HealthCheck { pub enable: bool, pub url: String, pub interval: u64, + pub lazy: Option, } impl TryFrom> for OutboundProxyProvider { diff --git a/clash_lib/src/lib.rs b/clash_lib/src/lib.rs index 06ede2ab8..ecd5777a3 100644 --- a/clash_lib/src/lib.rs +++ b/clash_lib/src/lib.rs @@ -3,10 +3,10 @@ extern crate anyhow; extern crate core; use crate::app::dispatcher::Dispatcher; +use crate::app::dns; use crate::app::inbound::manager::InboundManager; use crate::app::outbound::manager::OutboundManager; use crate::app::router::Router; -use crate::app::{dns, ThreadSafeDNSResolver}; use crate::config::def; use crate::config::internal::proxy::OutboundProxy; use crate::config::internal::InternalConfig; diff --git a/clash_lib/src/proxy/datagram.rs b/clash_lib/src/proxy/datagram.rs index f50a4c4de..dbe4df93b 100644 --- a/clash_lib/src/proxy/datagram.rs +++ b/clash_lib/src/proxy/datagram.rs @@ -1,7 +1,7 @@ +use crate::app::dns::ThreadSafeDNSResolver; use crate::proxy::socks::Socks5UDPCodec; use crate::proxy::{AnyOutboundDatagram, InboundDatagram}; use crate::session::SocksAddr; -use crate::ThreadSafeDNSResolver; use bytes::Bytes; use futures::{ready, Sink, SinkExt, Stream, StreamExt}; use std::fmt::{Debug, Display, Formatter}; diff --git a/clash_lib/src/proxy/direct/mod.rs b/clash_lib/src/proxy/direct/mod.rs index 7ea9efcc2..a33883803 100644 --- a/clash_lib/src/proxy/direct/mod.rs +++ b/clash_lib/src/proxy/direct/mod.rs @@ -1,9 +1,10 @@ +use crate::app::dns::ThreadSafeDNSResolver; use crate::config::internal::proxy::PROXY_DIRECT; use crate::proxy::datagram::OutboundDatagramImpl; use crate::proxy::utils::{new_tcp_stream, new_udp_socket}; use crate::proxy::{AnyOutboundDatagram, AnyOutboundHandler, AnyStream, OutboundHandler}; use crate::session::{Session, SocksAddr}; -use crate::ThreadSafeDNSResolver; + use async_trait::async_trait; use serde::Serialize; use std::sync::Arc; diff --git a/clash_lib/src/proxy/fallback/mod.rs b/clash_lib/src/proxy/fallback/mod.rs index 9e63f739a..6925ac5ff 100644 --- a/clash_lib/src/proxy/fallback/mod.rs +++ b/clash_lib/src/proxy/fallback/mod.rs @@ -5,10 +5,10 @@ use tracing::debug; use crate::{ app::{ + dns::ThreadSafeDNSResolver, proxy_manager::{ providers::proxy_provider::ThreadSafeProxyProvider, ThreadSafeProxyManager, }, - ThreadSafeDNSResolver, }, session::{Session, SocksAddr}, }; diff --git a/clash_lib/src/proxy/loadbalance/mod.rs b/clash_lib/src/proxy/loadbalance/mod.rs index 6ec0a9fc6..fc9c3f79b 100644 --- a/clash_lib/src/proxy/loadbalance/mod.rs +++ b/clash_lib/src/proxy/loadbalance/mod.rs @@ -8,7 +8,8 @@ use tracing::debug; use crate::{ app::{ - proxy_manager::providers::proxy_provider::ThreadSafeProxyProvider, ThreadSafeDNSResolver, + dns::ThreadSafeDNSResolver, + proxy_manager::providers::proxy_provider::ThreadSafeProxyProvider, }, config::internal::proxy::LoadBalanceStrategy, session::{Session, SocksAddr}, diff --git a/clash_lib/src/proxy/mocks.rs b/clash_lib/src/proxy/mocks.rs index c1c73e09e..9b1606e75 100644 --- a/clash_lib/src/proxy/mocks.rs +++ b/clash_lib/src/proxy/mocks.rs @@ -5,10 +5,10 @@ use mockall::mock; use crate::{ app::{ + dns::ThreadSafeDNSResolver, proxy_manager::providers::{ proxy_provider::ProxyProvider, Provider, ProviderType, ProviderVehicleType, }, - ThreadSafeDNSResolver, }, session::{Session, SocksAddr}, }; diff --git a/clash_lib/src/proxy/mod.rs b/clash_lib/src/proxy/mod.rs index d33d75334..05830f7c4 100644 --- a/clash_lib/src/proxy/mod.rs +++ b/clash_lib/src/proxy/mod.rs @@ -1,7 +1,7 @@ +use crate::app::dns::ThreadSafeDNSResolver; use crate::proxy::datagram::UdpPacket; use crate::proxy::utils::Interface; use crate::session::{Session, SocksAddr}; -use crate::ThreadSafeDNSResolver; use async_trait::async_trait; use erased_serde::Serialize as ESerialize; use futures::{Sink, Stream}; diff --git a/clash_lib/src/proxy/reject/mod.rs b/clash_lib/src/proxy/reject/mod.rs index 051e411c9..25a000d7f 100644 --- a/clash_lib/src/proxy/reject/mod.rs +++ b/clash_lib/src/proxy/reject/mod.rs @@ -1,7 +1,7 @@ +use crate::app::dns::ThreadSafeDNSResolver; use crate::config::internal::proxy::PROXY_REJECT; use crate::proxy::{AnyOutboundDatagram, AnyOutboundHandler, AnyStream, OutboundHandler}; use crate::session::{Session, SocksAddr}; -use crate::ThreadSafeDNSResolver; use async_trait::async_trait; use serde::Serialize; use std::io; diff --git a/clash_lib/src/proxy/relay/mod.rs b/clash_lib/src/proxy/relay/mod.rs index dcb100a9e..e3f31d123 100644 --- a/clash_lib/src/proxy/relay/mod.rs +++ b/clash_lib/src/proxy/relay/mod.rs @@ -6,7 +6,8 @@ use futures::stream::{self, StreamExt}; use crate::{ app::{ - proxy_manager::providers::proxy_provider::ThreadSafeProxyProvider, ThreadSafeDNSResolver, + dns::ThreadSafeDNSResolver, + proxy_manager::providers::proxy_provider::ThreadSafeProxyProvider, }, common::errors::new_io_error, proxy::utils::new_tcp_stream, diff --git a/clash_lib/src/proxy/selector/mod.rs b/clash_lib/src/proxy/selector/mod.rs index e84b1b4e1..df55cb9bb 100644 --- a/clash_lib/src/proxy/selector/mod.rs +++ b/clash_lib/src/proxy/selector/mod.rs @@ -7,7 +7,8 @@ use tracing::debug; use crate::{ app::{ - proxy_manager::providers::proxy_provider::ThreadSafeProxyProvider, ThreadSafeDNSResolver, + dns::ThreadSafeDNSResolver, + proxy_manager::providers::proxy_provider::ThreadSafeProxyProvider, }, p_debug, session::{Session, SocksAddr}, @@ -51,7 +52,7 @@ impl Handler { let current = providers .first() .unwrap() - .lock() + .read() .await .proxies() .await @@ -170,7 +171,7 @@ impl OutboundHandler for Handler { mod tests { use std::sync::Arc; - use tokio::sync::Mutex; + use tokio::sync::{Mutex, RwLock}; use crate::proxy::{ mocks::{MockDummyOutboundHandler, MockDummyProxyProvider}, @@ -198,7 +199,7 @@ mod tests { udp: false, common_option: super::CommonOption::default(), }, - vec![Arc::new(Mutex::new(mock_provider))], + vec![Arc::new(RwLock::new(mock_provider))], ) .await; diff --git a/clash_lib/src/proxy/shadowsocks/datagram.rs b/clash_lib/src/proxy/shadowsocks/datagram.rs index 52a473bc7..7be13c20a 100644 --- a/clash_lib/src/proxy/shadowsocks/datagram.rs +++ b/clash_lib/src/proxy/shadowsocks/datagram.rs @@ -10,7 +10,7 @@ use tokio::io::ReadBuf; use tracing::{debug, instrument}; use crate::{ - app::ThreadSafeDNSResolver, + app::dns::ThreadSafeDNSResolver, proxy::{datagram::UdpPacket, AnyOutboundDatagram}, session::SocksAddr, }; diff --git a/clash_lib/src/proxy/shadowsocks/mod.rs b/clash_lib/src/proxy/shadowsocks/mod.rs index fb410d391..8539ef29f 100644 --- a/clash_lib/src/proxy/shadowsocks/mod.rs +++ b/clash_lib/src/proxy/shadowsocks/mod.rs @@ -11,7 +11,7 @@ use shadowsocks::{ }; use crate::{ - app::ThreadSafeDNSResolver, + app::dns::ThreadSafeDNSResolver, proxy::{CommonOption, OutboundHandler}, session::{Session, SocksAddr}, Error, diff --git a/clash_lib/src/proxy/tun/inbound.rs b/clash_lib/src/proxy/tun/inbound.rs index 19dd98c93..d9662c54c 100644 --- a/clash_lib/src/proxy/tun/inbound.rs +++ b/clash_lib/src/proxy/tun/inbound.rs @@ -7,7 +7,7 @@ use tun::TunPacket; use url::Url; use crate::{ - app::{dispatcher::Dispatcher, ThreadSafeDNSResolver}, + app::{dispatcher::Dispatcher, dns::ThreadSafeDNSResolver}, config::internal::config::TunConfig, proxy::datagram::UdpPacket, session::{Network, Session, SocksAddr}, diff --git a/clash_lib/src/proxy/urltest/mod.rs b/clash_lib/src/proxy/urltest/mod.rs index 40370d94d..8c3e98cae 100644 --- a/clash_lib/src/proxy/urltest/mod.rs +++ b/clash_lib/src/proxy/urltest/mod.rs @@ -6,10 +6,10 @@ use tracing::debug; use crate::{ app::{ + dns::ThreadSafeDNSResolver, proxy_manager::{ providers::proxy_provider::ThreadSafeProxyProvider, ThreadSafeProxyManager, }, - ThreadSafeDNSResolver, }, p_debug, session::{Session, SocksAddr}, diff --git a/clash_lib/src/proxy/utils/provider_helper.rs b/clash_lib/src/proxy/utils/provider_helper.rs index 834107cb3..ca23612f3 100644 --- a/clash_lib/src/proxy/utils/provider_helper.rs +++ b/clash_lib/src/proxy/utils/provider_helper.rs @@ -10,11 +10,11 @@ pub async fn get_proxies_from_providers( let mut proxies = vec![]; for provider in providers { if touch { - provider.lock().await.touch().await; + provider.read().await.touch().await; } let mut proxies_from_provider = provider - .lock() + .read() .await .proxies() .await diff --git a/clash_lib/src/proxy/utils/socket_helpers.rs b/clash_lib/src/proxy/utils/socket_helpers.rs index ed9c27665..a12d7dcba 100644 --- a/clash_lib/src/proxy/utils/socket_helpers.rs +++ b/clash_lib/src/proxy/utils/socket_helpers.rs @@ -10,9 +10,8 @@ use tokio::{ time::timeout, }; -use crate::{app::ThreadSafeDNSResolver, proxy::AnyStream}; - use super::Interface; +use crate::{app::dns::ThreadSafeDNSResolver, proxy::AnyStream}; pub async fn apply_tcp_options(s: TcpStream) -> std::io::Result { let s = socket2::Socket::from(s.into_std()?); diff --git a/clash_lib/src/proxy/vmess/mod.rs b/clash_lib/src/proxy/vmess/mod.rs index b36ae164b..170621b57 100644 --- a/clash_lib/src/proxy/vmess/mod.rs +++ b/clash_lib/src/proxy/vmess/mod.rs @@ -6,7 +6,7 @@ use futures::TryFutureExt; mod vmess_impl; use crate::{ - app::ThreadSafeDNSResolver, + app::dns::ThreadSafeDNSResolver, common::errors::{map_io_error, new_io_error}, session::{Session, SocksAddr}, };