Skip to content

Commit

Permalink
rebase (#217)
Browse files Browse the repository at this point in the history
Signed-off-by: Yuwei Ba <[email protected]>
Co-authored-by: Yuwei Ba <[email protected]>
  • Loading branch information
eauxxs and ibigbug authored Oct 16, 2024
1 parent d07f7ab commit 6f15468
Show file tree
Hide file tree
Showing 14 changed files with 1,563 additions and 7,281 deletions.
7,280 changes: 0 additions & 7,280 deletions Cargo.lock

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions clash/tests/data/config/hysteria2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
port: 8891
socks-port: 8889
mixed-port: 8888


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

proxies:
- name: "local"
type: hysteria2
server: 127.0.0.1
port: 10086
password: "passwd"
sni: example.com
skip-cert-verify: true
obfs: salamander
obfs-password: "obfs"

rules:
- MATCH, local
7 changes: 6 additions & 1 deletion clash_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = { workspace = true }
[features]
default = []
shadowsocks = ["dep:shadowsocks"]
tuic = ["dep:tuic", "dep:tuic-quinn", "dep:quinn", "dep:register-count"]
tuic = ["dep:tuic", "dep:tuic-quinn", "dep:register-count"]
tracing = []
bench = ["dep:criterion"]
onion = ["dep:arti-client", "dep:tor-rtcompat", "arti-client/onion-service-client"]
Expand Down Expand Up @@ -131,6 +131,11 @@ tracing-timing = { version = "0.6" }
criterion = { version = "0.5", features = ["html_reports", "async_tokio"], optional = true }

memory-stats = "1.0.0"
h3 = "0.0.6"
h3-quinn = "0.0.7"
quinn-proto = "0.11.8"
blake2 = "0.10.6"
digest = "0.10.7"

[dev-dependencies]
tempfile = "3.13"
Expand Down
3 changes: 3 additions & 0 deletions clash_lib/src/app/outbound/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ impl OutboundManager {
Arc::new(h) as _
});
}
OutboundProxyProtocol::Hysteria2(h) => {
handlers.insert(h.name.clone(), h.clone().try_into()?);
}

OutboundProxyProtocol::Wireguard(wg) => {
warn!("wireguard is experimental");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ impl ProxySetProvider {
let h: vmess::Handler = vm.try_into()?;
Ok(Arc::new(h) as _)
}
OutboundProxyProtocol::Hysteria2(h) => h.try_into(),
OutboundProxyProtocol::Wireguard(wg) => {
let h: wg::Handler = wg.try_into()?;
Ok(Arc::new(h) as _)
Expand Down
35 changes: 35 additions & 0 deletions clash_lib/src/config/internal/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ pub enum OutboundProxyProtocol {
#[cfg(feature = "tuic")]
#[serde(rename = "tuic")]
Tuic(OutboundTuic),
#[serde(rename = "hysteria2")]
Hysteria2(OutboundHysteria2),
}

impl OutboundProxyProtocol {
Expand All @@ -87,6 +89,7 @@ impl OutboundProxyProtocol {
OutboundProxyProtocol::Tor(tor) => &tor.name,
#[cfg(feature = "tuic")]
OutboundProxyProtocol::Tuic(tuic) => &tuic.common_opts.name,
OutboundProxyProtocol::Hysteria2(hysteria2) => &hysteria2.name,
}
}
}
Expand Down Expand Up @@ -122,6 +125,7 @@ impl Display for OutboundProxyProtocol {
OutboundProxyProtocol::Tor(_) => write!(f, "Tor"),
#[cfg(feature = "tuic")]
OutboundProxyProtocol::Tuic(_) => write!(f, "Tuic"),
OutboundProxyProtocol::Hysteria2(_) => write!(f, "Hysteria2"),
}
}
}
Expand Down Expand Up @@ -475,3 +479,34 @@ impl TryFrom<HashMap<String, Value>> for OutboundProxyProviderDef {
.map_err(map_serde_error(name))
}
}
#[derive(Clone, serde::Serialize, serde::Deserialize, Debug, Default)]
#[serde(rename_all = "kebab-case")]
pub struct OutboundHysteria2 {
pub name: String,
pub server: String,
pub port: u16,
/// port hopping
pub ports: Option<String>,
pub password: String,
pub obfs: Option<Hysteria2Obfs>,
pub obfs_password: Option<String>,
pub alpn: Option<Vec<String>>,
/// set burtal congestion control, need compare with tx which is received by
/// auth request
pub up: Option<u64>,
/// receive_bps: send by auth request
pub down: Option<u64>,
pub sni: Option<String>,
pub skip_cert_verify: bool,
pub ca: Option<String>,
pub ca_str: Option<String>,
pub fingerprint: Option<String>,
/// bbr congestion control window
pub cwnd: Option<u64>,
}

#[derive(Clone, serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum Hysteria2Obfs {
Salamander,
}
142 changes: 142 additions & 0 deletions clash_lib/src/proxy/converters/hysteria2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use std::{
num::{NonZeroU16, ParseIntError},
ops::RangeInclusive,
sync::Arc,
};

use rand::Rng;

use crate::{
config::internal::proxy::OutboundHysteria2,
proxy::{
hysteria2::{Handler, HystOption},
AnyOutboundHandler,
},
session::SocksAddr,
};
#[derive(Clone)]
pub struct PortGenrateor {
// must have a default port
pub default: u16,
ports: Vec<u16>,
range: Vec<RangeInclusive<u16>>,
}

impl PortGenrateor {
pub fn new(port: u16) -> Self {
PortGenrateor {
default: port,
ports: vec![],
range: vec![],
}
}

pub fn add_single(&mut self, port: u16) {
self.ports.push(port);
}

fn add_range(&mut self, start: u16, end: u16) {
self.range.push(start..=end);
}

pub fn get(&self) -> u16 {
let mut rng = rand::thread_rng();
let len =
1 + self.ports.len() + self.range.iter().map(|r| r.len()).sum::<usize>();
let idx = rng.gen_range(0..len);
match idx {
0 => self.default,
idx if idx <= self.ports.len() => self.ports[idx - 1],
idx => {
let mut x = self.range.iter().cloned().flatten();
x.nth(idx - 1 - self.ports.len()).unwrap()
}
}
}

pub fn parse_ports_str(self, ports: &str) -> Result<Self, ParseIntError> {
if ports.is_empty() {
return Ok(self);
}
ports
.split(',')
.map(|s| s.trim())
.try_fold(self, |mut acc, ports| {
let x: Result<_, ParseIntError> = ports
.parse::<u16>()
.map(|p| acc.add_single(p))
.or_else(|e| {
let mut iter = ports.split('-');
let start = iter.next().ok_or(e.clone())?;
let end = iter.next().ok_or(e)?;
let start = start.parse::<NonZeroU16>()?;
let end = end.parse::<NonZeroU16>()?;
acc.add_range(start.get(), end.get());
Ok(())
})
.map(|_| acc);
x
})
}
}

impl TryFrom<OutboundHysteria2> for AnyOutboundHandler {
type Error = crate::Error;

fn try_from(value: OutboundHysteria2) -> Result<Self, Self::Error> {
let addr = SocksAddr::try_from((value.server, value.port))?;
let obfs_passwd = match value.obfs {
Some(_) => value
.obfs_password
.ok_or(crate::Error::InvalidConfig(
"hysteria2 found obfs enable, but obfs password is none"
.to_owned(),
))?
.into(),
None => None,
};

let ports_gen = if let Some(ports) = value.ports {
Some(
PortGenrateor::new(value.port)
.parse_ports_str(&ports)
.map_err(|e| {
crate::Error::InvalidConfig(format!(
"hysteria2 parse ports error: {:?}, ports: {:?}",
e, ports
))
})?,
)
} else {
None
};
let opts = HystOption {
name: value.name,
sni: value.sni.or(addr.domain().map(|s| s.to_owned())),
addr,
alpn: value.alpn.unwrap_or_default(),
ca: value.ca.map(|s| s.into()),
fingerprint: value.fingerprint,
skip_cert_verify: value.skip_cert_verify,
passwd: value.password,
ports: ports_gen,
salamander: obfs_passwd,
up_down: value.up.zip(value.down),
ca_str: value.ca_str,
cwnd: value.cwnd,
};

let c = Handler::new(opts).unwrap();
Ok(Arc::new(c))
}
}

#[test]
fn test_port_gen() {
let p = PortGenrateor::new(1000).parse_ports_str("").unwrap();
let p = p.parse_ports_str("1001,1002,1003, 5000-5001").unwrap();

for _ in 0..100 {
println!("{}", p.get());
}
}
1 change: 1 addition & 0 deletions clash_lib/src/proxy/converters/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod hysteria2;
#[cfg(feature = "shadowsocks")]
pub mod shadowsocks;
pub mod socks5;
Expand Down
Loading

0 comments on commit 6f15468

Please sign in to comment.