playit_agent_core/network/
lan_address.rs

1use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
2
3use byteorder::{BigEndian, ByteOrder};
4use tokio::net::{TcpSocket, TcpStream, UdpSocket};
5
6use crate::utils::shuffle::shuffle;
7
8pub struct LanAddress;
9
10impl LanAddress {
11    pub async fn tcp_socket(special_lan_ip: bool, peer: SocketAddr, host: SocketAddr) -> std::io::Result<TcpStream> {
12        let is_loopback = host.ip().is_loopback();
13        if is_loopback && special_lan_ip {
14            let local_ip = map_to_local_ip4(peer.ip());
15            let socket = TcpSocket::new_v4()?;
16
17            match socket.bind(SocketAddrV4::new(local_ip, 0).into()) {
18                Err(e) => {
19                    tracing::warn!("Failed to bind connection to special local address to support IP based banning: {:?}", e);
20                }
21                Ok(_) => {
22                    match socket.connect(host).await {
23                        Err(e) => {
24                            tracing::warn!("Failed to establish connection using special lan {} for flow {:?} {:?}", local_ip, (peer, host), e);
25                        }
26                        v => return v,
27                    };
28                }
29            }
30        }
31
32        tracing::warn!(is_loopback, host_ip = %host.ip(), special_lan_ip, "not using special lan address");
33        match TcpStream::connect(host).await {
34            Err(e) => {
35                tracing::error!("Failed to establish connection for flow {:?} {:?}. Is your server running?", (peer, host), e);
36                Err(e)
37            }
38            v => v,
39        }
40    }
41
42    pub async fn udp_socket(special_lan_ip: bool, peer: SocketAddr, tunnel_id: u64) -> std::io::Result<UdpSocket> {
43        let ip_shuffle = shuffle_ip_to_u32(peer.ip());
44
45        /* try to have the same client bind to the same local resource */
46        let rand_id = shuffle(peer.port() as u32)
47            ^ shuffle((tunnel_id >> 32) as u32)
48            ^ shuffle(tunnel_id as u32)
49            ^ ip_shuffle;
50
51        let local_port = (2048u32 + rand_id % (u16::MAX as u32 - 2048u32)) as u16;
52
53        if special_lan_ip {
54            let local_ip = Ipv4Addr::from(as_local_masked(ip_shuffle));
55
56            match UdpSocket::bind(SocketAddrV4::new(local_ip, local_port)).await {
57                Ok(v) => Ok(v),
58                Err(bad_port_error) => match UdpSocket::bind(SocketAddrV4::new(local_ip, 0)).await {
59                    Ok(v) => {
60                        tracing::warn!("Failed to bind UDP port to {} to have connections survive agent restart: {:?}", local_port, bad_port_error);
61                        Ok(v)
62                    }
63                    Err(bad_local_ip_err) => {
64                        let v = UdpSocket::bind(SocketAddrV4::new(0.into(), 0)).await?;
65                        tracing::warn!("Failed to bind UDP to special local address, in-game ip banning will not work: {:?}", bad_local_ip_err);
66                        Ok(v)
67                    }
68                }
69            }
70        } else {
71            match UdpSocket::bind(SocketAddrV4::new(0.into(), local_port)).await {
72                Ok(v) => Ok(v),
73                Err(bad_port_error) => {
74                    let v = UdpSocket::bind(SocketAddrV4::new(0.into(), 0)).await?;
75                    tracing::warn!("Failed to bind UDP to special port: {:?}", bad_port_error);
76                    Ok(v)
77                }
78            }
79        }
80    }
81}
82
83fn as_local_masked(mut ip: u32) -> u32 {
84    ip = shuffle(ip) & 0x00FFFFFFu32;
85    if ip == 0 {
86        ip = 1;
87    }
88    ip | 0x7F000000u32
89}
90
91fn map_to_local_ip4(ip: IpAddr) -> Ipv4Addr {
92    Ipv4Addr::from(as_local_masked(shuffle_ip_to_u32(ip)))
93}
94
95fn shuffle_ip_to_u32(ip: IpAddr) -> u32 {
96    match ip {
97        IpAddr::V4(ip) => u32::from(ip),
98        IpAddr::V6(ip) => {
99            let bytes = ip.octets();
100
101            shuffle(BigEndian::read_u32(&bytes[..4]))
102                ^ shuffle(BigEndian::read_u32(&bytes[4..8]))
103                ^ shuffle(BigEndian::read_u32(&bytes[4..8]))
104                ^ shuffle(BigEndian::read_u32(&bytes[4..8]))
105        }
106    }
107}