summaryrefslogtreecommitdiffhomepage
path: root/tunnel-obfuscation/src/udp2tcp.rs
blob: 6435629936876c639ae1ac880ea5fa2c5d66bab8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use crate::Obfuscator;
use async_trait::async_trait;
use std::net::SocketAddr;
use udp_over_tcp::{
    TcpOptions,
    udp2tcp::{self, Udp2Tcp as Udp2TcpImpl},
};

#[derive(Debug, Clone)]
pub struct Settings {
    pub peer: SocketAddr,
    #[cfg(target_os = "linux")]
    pub fwmark: Option<u32>,
}

type Result<T> = std::result::Result<T, Error>;

#[derive(thiserror::Error, Debug)]
pub enum Error {
    /// Failed to create obfuscator
    #[error("Failed to create obfuscator")]
    CreateObfuscator(#[source] udp2tcp::Error),

    /// Failed to determine UDP socket details
    #[error("Failed to determine UDP socket details")]
    GetUdpSocketDetails(#[source] std::io::Error),

    /// Failed to run obfuscator
    #[error("Failed to run obfuscator")]
    RunObfuscator(#[source] udp2tcp::Error),
}

pub struct Udp2Tcp {
    local_addr: SocketAddr,
    instance: Udp2TcpImpl,
}

impl Udp2Tcp {
    pub(crate) async fn new(settings: &Settings) -> Result<Self> {
        let listen_addr = if settings.peer.is_ipv4() {
            SocketAddr::new("127.0.0.1".parse().unwrap(), 0)
        } else {
            SocketAddr::new("::1".parse().unwrap(), 0)
        };

        let instance = Udp2TcpImpl::new(
            listen_addr,
            settings.peer,
            TcpOptions {
                #[cfg(target_os = "linux")]
                fwmark: settings.fwmark,
                // Disables the Nagle algorithm on the TCP socket. Improves performance
                nodelay: true,
                ..TcpOptions::default()
            },
        )
        .await
        .map_err(Error::CreateObfuscator)?;
        let local_addr = instance
            .local_udp_addr()
            .map_err(Error::GetUdpSocketDetails)?;

        Ok(Self {
            local_addr,
            instance,
        })
    }
}

#[async_trait]
impl Obfuscator for Udp2Tcp {
    fn endpoint(&self) -> SocketAddr {
        self.local_addr
    }

    async fn run(self: Box<Self>) -> crate::Result<()> {
        self.instance
            .run()
            .await
            .map_err(Error::RunObfuscator)
            .map_err(crate::Error::RunUdp2TcpObfuscator)
    }

    #[cfg(target_os = "android")]
    fn remote_socket_fd(&self) -> std::os::unix::io::RawFd {
        self.instance.remote_tcp_fd()
    }

    fn packet_overhead(&self) -> u16 {
        let max_tcp_header_len = 60; // https://datatracker.ietf.org/doc/html/rfc9293#section-3.1-6.22.1
        let udp_header_len = 8; // https://datatracker.ietf.org/doc/html/rfc768

        // TODO: Make `HEADER_LEN` constant public in udp-over-tcp lib and use it instead
        let udp_over_tcp_header_len = size_of::<u16>();

        let overhead = max_tcp_header_len - udp_header_len + udp_over_tcp_header_len;

        u16::try_from(overhead).expect("packet overhead is less than u16::MAX")
    }
}