Skip to content

Commit

Permalink
udp
Browse files Browse the repository at this point in the history
  • Loading branch information
ibigbug committed Sep 30, 2024
1 parent 05d6677 commit 2f1ffac
Show file tree
Hide file tree
Showing 12 changed files with 280 additions and 128 deletions.
45 changes: 36 additions & 9 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions clash_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ hyper-util = "0.1"
http = { version = "1" }
http-body-util = "0.1.2"
socket2 = { version = "0.5", features = ["all"] }
unix-udp-sock = { git = "https://github.com/Watfaq/unix-udp-sock.git", rev = "cd3e4eca43e6f3be82a2703c3d711b7e18fbfd18"}
tokio-tungstenite = "0.24.0"

# TLS
Expand Down
93 changes: 2 additions & 91 deletions clash_lib/src/proxy/datagram.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
use crate::{
app::dns::ThreadSafeDNSResolver,
common::errors::new_io_error,
proxy::{socks::Socks5UDPCodec, InboundDatagram},
app::dns::ThreadSafeDNSResolver, common::errors::new_io_error,
session::SocksAddr,
};
use bytes::Bytes;
use futures::{ready, Sink, SinkExt, Stream, StreamExt};
use futures::{ready, Sink, Stream};
use std::{
fmt::{Debug, Display, Formatter},
io,
net::SocketAddr,
pin::Pin,
task::{Context, Poll},
};
use tokio::{io::ReadBuf, net::UdpSocket};
use tokio_util::udp::UdpFramed;

#[derive(Clone)]
pub struct UdpPacket {
Expand Down Expand Up @@ -64,90 +59,6 @@ impl UdpPacket {
}
}

pub struct InboundUdp<I> {
inner: I,
}

impl<I> InboundUdp<I>
where
I: Stream + Unpin,
I: Sink<((Bytes, SocksAddr), SocketAddr)>,
{
pub fn new(inner: I) -> Self {
Self { inner }
}
}

impl Debug for InboundUdp<UdpFramed<Socks5UDPCodec>> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InboundUdp").finish()
}
}

impl Stream for InboundUdp<UdpFramed<Socks5UDPCodec>> {
type Item = UdpPacket;

fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
let pin = self.get_mut();

match pin.inner.poll_next_unpin(cx) {
Poll::Ready(item) => match item {
None => Poll::Ready(None),
Some(item) => match item {
Ok(((dst, pkt), src)) => Poll::Ready(Some(UdpPacket {
data: pkt.to_vec(),
src_addr: SocksAddr::Ip(src),
dst_addr: dst,
})),
Err(_) => Poll::Ready(None),
},
},
Poll::Pending => Poll::Pending,
}
}
}

impl Sink<UdpPacket> for InboundUdp<UdpFramed<Socks5UDPCodec>> {
type Error = std::io::Error;

fn poll_ready(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
let pin = self.get_mut();
pin.inner.poll_ready_unpin(cx)
}

fn start_send(self: Pin<&mut Self>, item: UdpPacket) -> Result<(), Self::Error> {
let pin = self.get_mut();
pin.inner.start_send_unpin((
(item.data.into(), item.src_addr),
item.dst_addr.must_into_socket_addr(),
))
}

fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
let pin = self.get_mut();
pin.inner.poll_flush_unpin(cx)
}

fn poll_close(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
let pin = self.get_mut();
pin.inner.poll_close_unpin(cx)
}
}

impl InboundDatagram<UdpPacket> for InboundUdp<UdpFramed<Socks5UDPCodec>> {}

#[must_use = "sinks do nothing unless polled"]
// TODO: maybe we should use abstract datagram IO interface instead of the
// Stream + Sink trait
Expand Down
4 changes: 4 additions & 0 deletions clash_lib/src/proxy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ pub trait InboundDatagram<Item>:
Stream<Item = Item> + Sink<Item, Error = io::Error> + Send + Sync + Unpin + Debug
{
}
impl<T, U> InboundDatagram<U> for T where
T: Stream<Item = U> + Sink<U, Error = io::Error> + Send + Sync + Unpin + Debug
{
}
pub type AnyInboundDatagram =
Box<dyn InboundDatagram<UdpPacket, Error = io::Error, Item = UdpPacket>>;

Expand Down
97 changes: 94 additions & 3 deletions clash_lib/src/proxy/socks/inbound/datagram.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
use crate::session::SocksAddr;
use crate::{proxy::datagram::UdpPacket, session::SocksAddr};
use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::io;
use tokio_util::codec::{Decoder, Encoder};
use futures::{Sink, SinkExt, Stream, StreamExt};
use std::{
io,
net::SocketAddr,
pin::Pin,
task::{Context, Poll},
};
use tokio_util::{
codec::{Decoder, Encoder},
udp::UdpFramed,
};

// +----+------+------+----------+----------+----------+
// |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
Expand Down Expand Up @@ -65,3 +74,85 @@ impl Decoder for Socks5UDPCodec {
Ok(Some((addr, packet)))
}
}

pub struct InboundUdp<I> {
inner: I,
}

impl<I> InboundUdp<I>
where
I: Stream + Unpin,
I: Sink<((Bytes, SocksAddr), SocketAddr)>,
{
pub fn new(inner: I) -> Self {
Self { inner }
}
}

impl std::fmt::Debug for InboundUdp<UdpFramed<Socks5UDPCodec>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InboundUdp").finish()
}
}

impl Stream for InboundUdp<UdpFramed<Socks5UDPCodec>> {
type Item = UdpPacket;

fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
let pin = self.get_mut();

match pin.inner.poll_next_unpin(cx) {
Poll::Ready(item) => match item {
None => Poll::Ready(None),
Some(item) => match item {
Ok(((dst, pkt), src)) => Poll::Ready(Some(UdpPacket {
data: pkt.to_vec(),
src_addr: SocksAddr::Ip(src),
dst_addr: dst,
})),
Err(_) => Poll::Ready(None),
},
},
Poll::Pending => Poll::Pending,
}
}
}

impl Sink<UdpPacket> for InboundUdp<UdpFramed<Socks5UDPCodec>> {
type Error = std::io::Error;

fn poll_ready(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
let pin = self.get_mut();
pin.inner.poll_ready_unpin(cx)
}

fn start_send(self: Pin<&mut Self>, item: UdpPacket) -> Result<(), Self::Error> {
let pin = self.get_mut();
pin.inner.start_send_unpin((
(item.data.into(), item.src_addr),
item.dst_addr.must_into_socket_addr(),
))
}

fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
let pin = self.get_mut();
pin.inner.poll_flush_unpin(cx)
}

fn poll_close(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
let pin = self.get_mut();
pin.inner.poll_close_unpin(cx)
}
}
2 changes: 1 addition & 1 deletion clash_lib/src/proxy/socks/inbound/stream.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{
common::{auth::ThreadSafeAuthenticator, errors::new_io_error},
proxy::{
datagram::InboundUdp,
socks::{
inbound::datagram::InboundUdp,
socks5::{auth_methods, response_code, socks_command},
Socks5UDPCodec, SOCKS5_VERSION,
},
Expand Down
6 changes: 3 additions & 3 deletions clash_lib/src/proxy/tproxy/iptables.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ iptables -t mangle -N CLASH-TPROXY-INPUT
for i in $LOCAL_BY_PASS; do
iptables -t mangle -A CLASH-TPROXY-INPUT -d $i -j RETURN
done
iptables -t mangle -A CLASH-TPROXY-LOCAL -m mark --mark 0x3332/0x3332 -j RETURN
iptables -t mangle -A CLASH-TPROXY-LOCAL -m owner --uid-owner root -j RETURN
iptables -t mangle -A CLASH-TPROXY-INPUT -p tcp -j TPROXY \
--tproxy-mark 0x3333/0x3333 --on-port 8900 --on-ip 127.0.0.1

Expand All @@ -27,10 +27,10 @@ for i in $LOCAL_BY_PASS; do
iptables -t mangle -A CLASH-TPROXY-LOCAL -d $i -j RETURN
done
iptables -t mangle -A CLASH-TPROXY-LOCAL -p tcp -m conntrack --ctdir REPLY -j RETURN
iptables -t mangle -A CLASH-TPROXY-LOCAL -m mark --mark 0x3332/0x3332 -j RETURN
iptables -t mangle -A CLASH-TPROXY-LOCAL -m owner --uid-owner root -j RETURN
# https://github.com/shadowsocks/shadowsocks-rust/blob/6e6e6948d7fc426c99cc03ef91abae989b6482b4/configs/iptables_tproxy.sh#L187
iptables -t mangle -A CLASH-TPROXY-LOCAL -p tcp -j MARK --set-xmark 0x3333/0xffffffff
iptables -t mangle -A OUTPUT -j CLASH-TPROXY-LOCAL
#iptables -t mangle -A OUTPUT -j CLASH-TPROXY-LOCAL

# for routed traffic
iptables -t mangle -A PREROUTING -p tcp -j CLASH-TPROXY-INPUT
Expand Down
Loading

0 comments on commit 2f1ffac

Please sign in to comment.