playit_agent_core/network/
lan_address.rs1use 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 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}