Skip to content

Commit

Permalink
add wireguard outbound support (#214)
Browse files Browse the repository at this point in the history
* WIP

* WIP

* up

* TCP working

* stack

* speed

* add udp

* up

* fix build

* todo DNS

* wg remote dns

* add allowed_ips check

* readme

* add wg dns timeout
  • Loading branch information
ibigbug authored Dec 25, 2023
1 parent d0228b7 commit 29d357f
Show file tree
Hide file tree
Showing 37 changed files with 1,914 additions and 42 deletions.
121 changes: 121 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ A custom protocol, rule based network proxy software.
- 🌈 Flexible traffic routing rules based off source/destination IP/Domain/GeoIP etc.
- 📦 Local anti spoofing DNS with support of UDP/TCP/DoH/DoT remote.
- 🛡 Run as a HTTP/Socks5 proxy, or utun device as a home network gateway.
- ⚙️ Shadowsocks/Trojan/Vmess outbound support with different underlying trasports.
- ⚙️ Shadowsocks/Trojan/Vmess/Wireguard(userspace) outbound support with different underlying trasports.
- 🌍 Dynamic remote rule/proxy loader.
- 🎵 Tracing with Jaeger

Expand Down
61 changes: 61 additions & 0 deletions clash/tests/data/config/wg.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
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

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

proxies:
- name: "wg"
type: wireguard
server: 10.0.0.17
port: 51820
private-key: 2AS8PSccSenWrws5ExglmpwjVBub9Oy9X3zOlk6heHU=
ip: 192.168.2.2
public-key: MAZPwQBniuXmQf5w8BwM3owlO7Kw07rzyZUXxOvsF3w=
allowed-ips: ['0.0.0.0/0']
remote-dns-resolve: true
dns:
- 8.8.8.8
udp: true


rules:
- MATCH, wg
...
2 changes: 2 additions & 0 deletions clash_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ tun = { git = "https://github.com/Watfaq/rust-tun.git", rev = "8f7568190f1200d3e
netstack-lwip = { git = "https://github.com/Watfaq/netstack-lwip.git", rev = "2817bf82740e04bbee6b7bf1165f55657a6ed163" }

boringtun = { version = "0.6.0" }
smoltcp = { version = "0.10", default-features = false, features = ["std", "log", "medium-ip", "proto-ipv4", "proto-ipv6", "socket-udp", "socket-tcp"] }


serde = { version = "1.0", features=["derive"] }
serde_yaml = "0.9"
Expand Down
2 changes: 1 addition & 1 deletion clash_lib/src/app/dispatcher/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ impl Dispatcher {

// populate fake ip for route matching
let sess = if resolver.fake_ip_enabled() {
trace!("fake ip enabled");
trace!("looking up fake ip for {sess}");
match sess.destination {
crate::session::SocksAddr::Ip(addr) => {
let ip = addr.ip();
Expand Down
6 changes: 6 additions & 0 deletions clash_lib/src/app/outbound/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::time::Duration;
use tokio::sync::{Mutex, RwLock};
use tracing::debug;
use tracing::error;
use tracing::warn;

use tracing::info;

Expand Down Expand Up @@ -205,6 +206,11 @@ impl OutboundManager {
handlers.insert(v.name.clone(), v.try_into()?);
}

OutboundProxyProtocol::Wireguard(wg) => {
warn!("wireguard is experimental");
handlers.insert(wg.name.clone(), wg.try_into()?);
}

p => {
unimplemented!("proto {} not supported yet", p);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ impl ProxySetProvider {
OutboundProxyProtocol::Socks5(_) => todo!("socks5 not supported yet"),
OutboundProxyProtocol::Trojan(tr) => tr.try_into(),
OutboundProxyProtocol::Vmess(vm) => vm.try_into(),
OutboundProxyProtocol::Wireguard(wg) => wg.try_into(),
})
.collect::<Result<Vec<_>, _>>();
Ok(proxies?)
Expand Down
6 changes: 5 additions & 1 deletion clash_lib/src/app/router/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use std::sync::Arc;
use std::time::Duration;

use http::Uri;
use tracing::{error, info};
use tracing::{debug, error, info};

use super::dns::ThreadSafeDNSResolver;
use super::remote_content_manager::providers::rule_provider::{
Expand Down Expand Up @@ -78,6 +78,10 @@ impl Router {

for r in self.rules.iter() {
if sess.destination.is_domain() && r.should_resolve_ip() && !sess_resolved {
debug!(
"rule {r} local resolving domain {}",
sess.destination.domain().unwrap()
);
if let Ok(ip) = self
.dns_resolver
.resolve(sess.destination.domain().unwrap(), false)
Expand Down
6 changes: 6 additions & 0 deletions clash_lib/src/app/router/rules/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ pub struct Domain {
pub target: String,
}

impl std::fmt::Display for Domain {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} domain {}", self.target, self.domain)
}
}

impl RuleMatcher for Domain {
fn apply(&self, sess: &session::Session) -> bool {
match &sess.destination {
Expand Down
8 changes: 8 additions & 0 deletions clash_lib/src/app/router/rules/domain_keyword.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::fmt::Display;

use crate::session;

use super::RuleMatcher;
Expand All @@ -8,6 +10,12 @@ pub struct DomainKeyword {
pub target: String,
}

impl Display for DomainKeyword {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} keyword {}", self.target, self.keyword)
}
}

impl RuleMatcher for DomainKeyword {
fn apply(&self, sess: &session::Session) -> bool {
match &sess.destination {
Expand Down
6 changes: 6 additions & 0 deletions clash_lib/src/app/router/rules/domain_suffix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ pub struct DomainSuffix {
pub target: String,
}

impl std::fmt::Display for DomainSuffix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} suffix {}", self.target, self.suffix)
}
}

impl RuleMatcher for DomainSuffix {
fn apply(&self, sess: &Session) -> bool {
match &sess.destination {
Expand Down
6 changes: 6 additions & 0 deletions clash_lib/src/app/router/rules/final_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ pub struct Final {
pub target: String,
}

impl std::fmt::Display for Final {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} final", self.target)
}
}

impl RuleMatcher for Final {
fn apply(&self, _sess: &Session) -> bool {
true
Expand Down
6 changes: 6 additions & 0 deletions clash_lib/src/app/router/rules/geoip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ pub struct GeoIP {
pub mmdb: Arc<mmdb::MMDB>,
}

impl std::fmt::Display for GeoIP {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} geoip {}", self.target, self.country_code)
}
}

impl RuleMatcher for GeoIP {
fn apply(&self, sess: &Session) -> bool {
match sess.destination {
Expand Down
Loading

0 comments on commit 29d357f

Please sign in to comment.