Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement grpc for trojan and vmess #203

Merged
merged 22 commits into from
Dec 6, 2023
Merged
7 changes: 4 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions clash/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,15 @@ fn main() {
}
}
}
clash::start(clash::Options {
match clash::start(clash::Options {
config: clash::Config::File(file),
cwd: cli.directory.map(|x| x.to_string_lossy().to_string()),
rt: Some(TokioRuntime::MultiThread),
log_file: None,
})
.unwrap();
}) {
Ok(_) => {}
Err(_) => {
exit(1);
}
}
}
36 changes: 33 additions & 3 deletions clash/tests/data/config/rules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ dns:
- 114.114.114.114 # default value
- 1.1.1.1 # default value
- tls://1.1.1.1:853 # DNS over TLS
- https://1.1.1.1/dns-query # DNS over HTTPS
# - dhcp://en0 # dns from dhcp

allow-lan: true
Expand Down Expand Up @@ -167,6 +166,20 @@ proxies:
h2-opts:
path: /ray

- name: grpc-vmess
type: vmess
server: 10.0.0.13
port: 19443
uuid: b831381d-6324-4d53-ad4f-8cda48b30811
alterId: 0
cipher: auto
udp: true
skip-cert-verify: true
tls: true
network: grpc
grpc-opts:
grpc-service-name: abc

- name: vmess-altid
type: vmess
server: tw-1.ac.laowanxiang.com
Expand All @@ -187,6 +200,7 @@ proxies:
cipher: aes-256-gcm
password: "password"
udp: true

- name: "trojan"
type: trojan
server: 10.0.0.13
Expand All @@ -199,6 +213,20 @@ proxies:
- http/1.1
skip-cert-verify: true

- name: "trojan-grpc"
type: trojan
server: 10.0.0.13
port: 19443
password: password1
udp: true
# sni: example.com # aka server name
alpn:
- h2
skip-cert-verify: true
network: grpc
grpc-opts:
grpc-service-name: def

proxy-providers:
file-provider:
type: file
Expand All @@ -217,11 +245,13 @@ rule-providers:
behavior: domain

rules:
- DOMAIN,ipinfo.io,relay
- DOMAIN,google.com,grpc-vmess
- DOMAIN-KEYWORD,httpbin,trojan-grpc
- DOMAIN,ipinfo.io,trojan-grpc
# - RULE-SET,file-provider,trojan
- GEOIP,CN,relay
- DOMAIN-SUFFIX,facebook.com,REJECT
- DOMAIN-KEYWORD,google,select
- DOMAIN-KEYWORD,google,grpc-vmess
- DOMAIN,google.com,select
- SRC-IP-CIDR,192.168.1.1/24,DIRECT
- GEOIP,CN,DIRECT
Expand Down
6 changes: 3 additions & 3 deletions clash_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ bench = ["criterion"]

[dependencies]
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7", features = ["net", "codec", "io"] }
tokio-util = { version = "0.7", features = ["net", "codec", "io", "compat"] }
tokio-rustls = "0.24"
thiserror = "1.0"
async-trait = "0.1"
Expand All @@ -26,9 +26,9 @@ byteorder = "1.5"
state = "0.6"
lru_time_cache = "0.11"
hyper = { version = "0.14", features = ["http1","http2","client", "server", "tcp"] }
http = { version = "0.2" }
http = { version = "0.2.11" }
httparse = "1.8.0"
h2 = "0.3"
h2 = "0.3.22"
prost = "0.12"
tower = { version = "0.4", features = ["util"] }
libc = "0.2"
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 @@ -6,6 +6,7 @@ use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{Mutex, RwLock};
use tracing::debug;
use tracing::error;

use tracing::info;
Expand Down Expand Up @@ -65,6 +66,7 @@ impl OutboundManager {
let mut selector_control = HashMap::new();
let proxy_manager = ProxyManager::new(dns_resolver.clone());

debug!("initializing proxy providers");
Self::load_proxy_providers(
cwd,
proxy_providers,
Expand All @@ -74,6 +76,7 @@ impl OutboundManager {
)
.await?;

debug!("initializing handlers");
Self::load_handlers(
outbounds,
outbound_groups,
Expand Down Expand Up @@ -628,6 +631,7 @@ impl OutboundManager {
error!("failed to initialize proxy provider {}: {}", p.name(), err);
}
}
info!("initialized provider {}", p.name());
}

Ok(())
Expand Down
16 changes: 9 additions & 7 deletions clash_lib/src/app/remote_content_manager/healthcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ impl HealthCheck {
let lazy = self.lazy;
let proxies = self.inner.read().await.proxies.clone();

let url = self.url.clone();
tokio::spawn(async move {
proxy_manager.check(&proxies, &url, None).await;
});
{
let url = self.url.clone();
let proxies = proxies.clone();
tokio::spawn(async move {
proxy_manager.check(&proxies, &url, None).await;
});
}

let inner = self.inner.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 {
Expand All @@ -65,8 +67,8 @@ impl HealthCheck {
_ = ticker.tick() => {
pm_debug!("healthcheck ticking: {}, lazy: {}", url, lazy);
let now = tokio::time::Instant::now();
let r = inner.read().await;
if !lazy || now.duration_since(r.last_check).as_secs() >= interval {
let last_check = inner.read().await.last_check;
if !lazy || now.duration_since(last_check).as_secs() >= interval {
proxy_manager.check(&proxies, &url, None).await;
let mut w = inner.write().await;
w.last_check = now;
Expand Down
1 change: 1 addition & 0 deletions clash_lib/src/config/internal/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ pub struct OutboundVmess {
pub network: Option<String>,
pub ws_opts: Option<WsOpt>,
pub h2_opts: Option<H2Opt>,
pub grpc_opts: Option<GrpcOpt>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
Expand Down
19 changes: 14 additions & 5 deletions clash_lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ use common::http::new_http_client;
use common::mmdb;
use config::def::LogLevel;
use proxy::tun::get_tun_runner;

use state::InitCell;
use std::io;
use std::path::PathBuf;
use tokio::task::JoinHandle;
use tracing::error;
use tracing::info;

use std::sync::Arc;
use thiserror::Error;
use tokio::sync::{broadcast, mpsc, Mutex};
use tokio::task::JoinHandle;
use tracing::debug;
use tracing::error;
use tracing::info;

mod app;
mod common;
Expand Down Expand Up @@ -161,9 +162,12 @@ async fn start_async(opts: Options) -> Result<(), Error> {
let mut tasks = Vec::<Runner>::new();
let mut runners = Vec::new();

debug!("initializing dns resolver");
let system_resolver =
Arc::new(SystemResolver::new().map_err(|x| Error::DNSError(x.to_string()))?);
let client = new_http_client(system_resolver).map_err(|x| Error::DNSError(x.to_string()))?;

debug!("initializing mmdb");
let mmdb = Arc::new(
mmdb::MMDB::new(
cwd.join(&config.general.mmdb),
Expand All @@ -173,13 +177,15 @@ async fn start_async(opts: Options) -> Result<(), Error> {
.await?,
);

debug!("initializing cache store");
let cache_store = profile::ThreadSafeCacheFile::new(
cwd.join("cache.db").as_path().to_str().unwrap(),
config.profile.store_selected,
);

let dns_resolver = dns::Resolver::new(&config.dns, cache_store.clone(), mmdb.clone()).await;

debug!("initializing outbound manager");
let outbound_manager = Arc::new(
OutboundManager::new(
config
Expand Down Expand Up @@ -207,6 +213,7 @@ async fn start_async(opts: Options) -> Result<(), Error> {
.await?,
);

debug!("initializing router");
let router = Arc::new(
Router::new(
config.rules,
Expand All @@ -230,6 +237,7 @@ async fn start_async(opts: Options) -> Result<(), Error> {

let authenticator = Arc::new(auth::PlainAuthenticator::new(config.users));

debug!("initializing inbound manager");
let inbound_manager = Arc::new(Mutex::new(InboundManager::new(
config.general.inbound,
dispatcher.clone(),
Expand All @@ -244,6 +252,7 @@ async fn start_async(opts: Options) -> Result<(), Error> {
runners.push(tun_runner);
}

debug!("initializing dns listener");
let dns_listener_handle = dns::get_dns_listener(config.dns, dns_resolver.clone())
.await
.map(|l| tokio::spawn(l));
Expand Down Expand Up @@ -272,7 +281,7 @@ async fn start_async(opts: Options) -> Result<(), Error> {
}

runners.push(Box::pin(async move {
info!("receive shutdown signal");
info!("receiving shutdown signal");
shutdown_rx.recv().await;
Ok(())
}));
Expand Down
7 changes: 6 additions & 1 deletion clash_lib/src/proxy/converters/trojan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ impl TryFrom<&OutboundTrojan> for AnyOutboundHandler {
.ok_or(Error::InvalidConfig(
"grpc_opts is required for grpc".to_owned(),
)),
_ => return Err(Error::InvalidConfig(format!("unsupported network: {}", x))),
_ => {
return Err(Error::InvalidConfig(format!(
"unsupported trojan network: {}",
x
)))
}
})
.transpose()?,
});
Expand Down
20 changes: 18 additions & 2 deletions clash_lib/src/proxy/converters/vmess.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use tracing::warn;
use crate::{
config::internal::proxy::OutboundVmess,
proxy::{
options::{Http2Option, WsOption},
options::{GrpcOption, Http2Option, WsOption},
transport::TLSOptions,
vmess::{Handler, HandlerOptions, VmessTransport},
AnyOutboundHandler, CommonOption,
Expand Down Expand Up @@ -75,6 +75,22 @@ impl TryFrom<&OutboundVmess> for AnyOutboundHandler {
.ok_or(Error::InvalidConfig(
"h2_opts is required for h2".to_owned(),
)),
"grpc" => s
.grpc_opts
.as_ref()
.map(|x| {
VmessTransport::Grpc(GrpcOption {
service_name: x
.grpc_service_name
.as_ref()
.to_owned()
.unwrap_or(&"GunService".to_owned())
.to_owned(),
})
})
.ok_or(Error::InvalidConfig(
"grpc_opts is required for grpc".to_owned(),
)),
_ => {
return Err(Error::InvalidConfig(format!("unsupported network: {}", x)));
}
Expand Down Expand Up @@ -106,7 +122,7 @@ impl TryFrom<&OutboundVmess> for AnyOutboundHandler {
.map(|x| match x.as_str() {
"ws" => Ok(vec!["http/1.1".to_owned()]),
"http" => Ok(vec![]),
"h2" => Ok(vec!["h2".to_owned()]),
"h2" | "grpc" => Ok(vec!["h2".to_owned()]),
_ => Err(Error::InvalidConfig(format!("unsupported network: {}", x))),
})
.transpose()?,
Expand Down
Loading
Loading