Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
ibigbug committed Dec 17, 2023
1 parent 73cdd52 commit 0ab9cc6
Show file tree
Hide file tree
Showing 16 changed files with 719 additions and 229 deletions.
59 changes: 59 additions & 0 deletions clash/tests/data/config/wg.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
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

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


rules:
- MATCH, wg
...
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 @@ -205,6 +205,10 @@ impl OutboundManager {
handlers.insert(v.name.clone(), v.try_into()?);
}

OutboundProxyProtocol::Wireguard(wg) => {
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
19 changes: 19 additions & 0 deletions clash_lib/src/config/internal/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ pub enum OutboundProxyProtocol {
Trojan(OutboundTrojan),
#[serde(rename = "vmess")]
Vmess(OutboundVmess),
#[serde(rename = "wireguard")]
Wireguard(OutboundWireguard),
}

impl OutboundProxyProtocol {
Expand All @@ -64,6 +66,7 @@ impl OutboundProxyProtocol {
OutboundProxyProtocol::Socks5(socks5) => &socks5.name,
OutboundProxyProtocol::Trojan(trojan) => &trojan.name,
OutboundProxyProtocol::Vmess(vmess) => &vmess.name,
OutboundProxyProtocol::Wireguard(wireguard) => &wireguard.name,
}
}
}
Expand All @@ -86,6 +89,7 @@ impl Display for OutboundProxyProtocol {
OutboundProxyProtocol::Reject => write!(f, "{}", PROXY_REJECT),
OutboundProxyProtocol::Trojan(_) => write!(f, "{}", "Trojan"),
OutboundProxyProtocol::Vmess(_) => write!(f, "{}", "Vmess"),
OutboundProxyProtocol::Wireguard(_) => write!(f, "{}", "Wireguard"),
}
}
}
Expand Down Expand Up @@ -173,6 +177,21 @@ pub struct OutboundVmess {
pub grpc_opts: Option<GrpcOpt>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Default, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct OutboundWireguard {
pub name: String,
pub server: String,
pub port: u16,
pub private_key: String,
pub public_key: String,
pub preshared_key: Option<String>,
pub mtu: Option<u16>,
pub udp: Option<bool>,
pub ip: String,
pub ipv6: Option<String>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(tag = "type")]
pub enum OutboundGroupProtocol {
Expand Down
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 shadowsocks;
pub mod trojan;
pub mod vmess;
pub mod wireguard;
49 changes: 49 additions & 0 deletions clash_lib/src/proxy/converters/wireguard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use crate::{
config::internal::proxy::OutboundWireguard,
proxy::{
wg::{Handler, Opts},
AnyOutboundHandler,
},
Error,
};

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

fn try_from(value: OutboundWireguard) -> Result<Self, Self::Error> {
(&value).try_into()
}
}

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

fn try_from(s: &OutboundWireguard) -> Result<Self, Self::Error> {
let h = Handler::new(Opts {
name: s.name.to_owned(),
common_opts: Default::default(),
server: s.server.to_owned(),
port: s.port,
ip: s
.ip
.parse()
.map_err(|x| Error::InvalidConfig(format!("invalid ip address: {}", x)))?,
ipv6: s
.ipv6
.as_ref()
.map(|x| {
x.parse()
.map_err(|x| Error::InvalidConfig(format!("invalid ipv6 address: {}", x)))
})
.transpose()?,
private_key: s.private_key.to_owned(),
public_key: s.public_key.to_owned(),
preshared_key: s.preshared_key.as_ref().map(|x| x.to_owned()),
remote_dns_resolve: false,
dns: None,
mtu: s.mtu,
udp: s.udp.unwrap_or_default(),
});
Ok(h)
}
}
118 changes: 109 additions & 9 deletions clash_lib/src/proxy/wg/device.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,114 @@
use std::{
collections::VecDeque,
sync::{Arc, Mutex},
};
use bytes::{Bytes, BytesMut};
use smoltcp::phy::Device;
use tokio::sync::mpsc::{Receiver, Sender};
use tracing::error;

use bytes::Bytes;

use super::events::BusSender;
use super::{events::PortProtocol, ports::PortPool};

pub struct VirtualIpDevice {
mtu: usize,
bus_sender: BusSender,
queue: Arc<Mutex<VecDeque<Bytes>>>,

tcp_port_pool: PortPool,

packet_sender: Sender<Bytes>,
packet_receiver: Receiver<(PortProtocol, Bytes)>,
}

impl VirtualIpDevice {
pub fn new(
packet_sender: Sender<Bytes>,
packet_receiver: Receiver<(PortProtocol, Bytes)>,
mtu: usize,
) -> Self {
Self {
mtu,
tcp_port_pool: PortPool::new(),
packet_sender,
packet_receiver,
}
}

pub async fn get_ephemeral_tcp_port(&self) -> u16 {
self.tcp_port_pool.next().await.unwrap()
}
pub async fn release_ephemeral_tcp_port(&self, port: u16) {
self.tcp_port_pool.release(port).await;
}
}

impl Device for VirtualIpDevice {
type RxToken<'a> = RxToken;
type TxToken<'a> = TxToken;

fn receive(
&mut self,
timestamp: smoltcp::time::Instant,
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let next = self.packet_receiver.try_recv().ok();

match next {
Some((proto, data)) => {
let rx_token = RxToken {
buffer: {
let mut buffer = BytesMut::new();
buffer.extend_from_slice(&data);
buffer
},
};
let tx_token = TxToken {
sender: self.packet_sender.clone(),
};
Some((rx_token, tx_token))
}
None => None,
}
}

fn transmit(&mut self, timestamp: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
Some(TxToken {
sender: self.packet_sender.clone(),
})
}

fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities {
let mut caps = smoltcp::phy::DeviceCapabilities::default();
caps.medium = smoltcp::phy::Medium::Ip;
caps.max_transmission_unit = self.mtu;
caps
}
}

pub struct RxToken {
buffer: BytesMut,
}

impl smoltcp::phy::RxToken for RxToken {
fn consume<R, F>(mut self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
f(&mut self.buffer)
}
}

pub struct TxToken {
sender: Sender<Bytes>,
}

impl smoltcp::phy::TxToken for TxToken {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
let mut buffer = Vec::new();
buffer.resize(len, 0);
let result = f(&mut buffer);
match self.sender.try_send(buffer.into()) {
Ok(_) => {}
Err(err) => {
error!("failed to send packet: {}", err);
}
}
result
}
}
Loading

0 comments on commit 0ab9cc6

Please sign in to comment.