quinn-rs / quinn

Async-friendly QUIC implementation in Rust
Apache License 2.0
3.57k stars 364 forks source link

How can I use socks5 udp socket as AsyncUdpSocket #1770

Closed 0xffffharry closed 3 months ago

0xffffharry commented 3 months ago

I want to make quic connection under the socks5 proxy. But how can I do?

Ralith commented 3 months ago

I'm not familiar with the details of SOCKS5. What part are you having trouble with?

0xffffharry commented 3 months ago

for example:

I need to add header before send and parse header after recv. But I can't save poll state to anywhere. Maybe we should add a new field in args. like this:

pub trait AsyncUdpSocket: Send + Debug + 'static {
    // Required methods
    fn poll_send(
        &self,
        state: &UdpState,
        cx: &mut Context<'_>,
        transmits: &[Transmit],
        temp_bufs: &mut [bytes::BytesMut], // add temp buffer here or other field
    ) -> Poll<Result<usize, Error>>;

    fn poll_recv(
        &self,
        cx: &mut Context<'_>,
        bufs: &mut [IoSliceMut<'_>],
        temp_bufs: &mut [bytes::BytesMut], // add temp buffer here or other field
        meta: &mut [RecvMeta]
    ) -> Poll<Result<usize>>;

    fn local_addr(&self) -> Result<SocketAddr>;

    // Provided method
    fn may_fragment(&self) -> bool { ... }
}
use std::net::SocketAddr;

#[derive(Debug)]
struct Socks5UdpSocket {
    _tcp_stream: tokio::net::TcpStream,
    inner: tokio::net::UdpSocket  // connected
}

// +----+------+------+----------+----------+----------+
// |RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
// +----+------+------+----------+----------+----------+
// | 2  |  1   |  1   | Variable |    2     | Variable |
// +----+------+------+----------+----------+----------+
struct UdpPacketRef<'a> {
    rsv: u16, // 0
    frag: u8, // 0
    address: SocketAddr,
    data: &'a [u8]
}

impl quinn::AsyncUdpSocket for Socks5UdpSocket {
    fn poll_send(
        &self,
        state: &quinn::udp::UdpState,
        cx: &mut std::task::Context<'_>,
        transmits: &[quinn::Transmit]
    ) -> std::task::Poll<Result<usize, std::io::Error>> {
        // how to do
    }

    fn poll_recv(
        &self,
        cx: &mut std::task::Context<'_>,
        bufs: &mut [std::io::IoSliceMut<'_>],
        meta: &mut [quinn::RecvMeta]
    ) -> std::task::Poll<Result<usize>> {
        // how to do
    }

    fn local_addr(&self) -> Result<SocketAddr> {
        self.inner.local_addr()
    }

    fn may_fragment(&self) -> bool {
        false
    }
}
djc commented 3 months ago

Why do you need to persist poll state/why can't you have that live in whatever type you have implementing AsyncUdpSocket?

0xffffharry commented 3 months ago

Why do you need to persist poll state/why can't you have that live in whatever type you have implementing AsyncUdpSocket?

I reviewed the code, and this seems feasible. Would it be better to make the Self of poll_recv and poll_send mutable?

Ralith commented 3 months ago

No, because we have concrete plans to use UDP sockets from multiple threads; see e.g. #1729. Use the stack, interior mutability, or thread-local state according to your requirements.

0xffffharry commented 3 months ago

Thank, I found a way to implement socks5 AsyncUdpSocket.