Skip to main content

playit_api_client/
ip_resource.rs

1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
2use byteorder::{BigEndian, ByteOrder};
3
4#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
5pub struct IpResource {
6    pub ip_num: u64,
7    pub region: PlayitRegion,
8}
9
10impl IpResource {
11    pub fn from_ip(ip: IpAddr) -> Self {
12        let (region, ip_num) = PlayitRegion::from_ip(ip);
13
14        let is_region_ip = ip_num >= 64 && ip_num < 128;
15        let region = match (is_region_ip, region) {
16            (true, Some(region)) => region,
17            _ => PlayitRegion::Anycast,
18        };
19
20        IpResource { ip_num, region }
21    }
22
23    pub fn to_tunnel_ip(&self) -> Ipv6Addr {
24        self.region.tunnel_address(self.ip_num)
25    }
26}
27
28#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
29#[repr(u16)]
30pub enum PlayitRegion {
31    Anycast = 0,
32    Global = 1,
33    NorthAmerica = 2,
34    Europe = 3,
35    Asia = 4,
36    India = 5,
37    SouthAmerica = 6,
38}
39
40impl PlayitRegion {
41    pub fn from_ip(ip: IpAddr) -> (Option<Self>, u64) {
42        match ip {
43            IpAddr::V4(ip) => Self::from_ip4(ip),
44            IpAddr::V6(ip) => Self::from_ip6(ip),
45        }
46    }
47
48    pub fn from_ip4(ip: Ipv4Addr) -> (Option<Self>, u64) {
49        let octs = ip.octets();
50
51        let net = match (octs[0], octs[1], octs[2]) {
52            (0, 0, 0) => Some(PlayitRegion::Anycast),
53            (209, 25, 140) => Some(PlayitRegion::NorthAmerica),
54            (209, 25, 141) => Some(PlayitRegion::Europe),
55            (209, 25, 142) => Some(PlayitRegion::Asia),
56            (209, 25, 143) => Some(PlayitRegion::India),
57            (23, 133, 216) => Some(PlayitRegion::SouthAmerica),
58            (198, 22, 204) => Some(PlayitRegion::SouthAmerica),
59            (147, 185, 221) => Some(PlayitRegion::Global),
60            _ => None,
61        };
62
63        (net, octs[3] as _)
64    }
65
66    pub fn from_ip6(ip: Ipv6Addr) -> (Option<Self>, u64) {
67        let parts = ip.octets();
68
69        let region_number = BigEndian::read_u16(&parts[6..8]);
70        let ip_number = BigEndian::read_u64(&parts[8..]);
71
72        let region = match region_number {
73            0 => Some(PlayitRegion::Anycast),
74            1 => Some(PlayitRegion::Global),
75            2 => Some(PlayitRegion::NorthAmerica),
76            3 => Some(PlayitRegion::Europe),
77            4 => Some(PlayitRegion::Asia),
78            5 => Some(PlayitRegion::India),
79            6 => Some(PlayitRegion::SouthAmerica),
80            _ => None,
81        };
82
83        (
84            region,
85            ip_number
86        )
87    }
88
89    pub fn tunnel_address(&self, ip_number: u64) -> Ipv6Addr {
90        let mut octs = [0u8; 16];
91        octs[0] = 0x26;
92        octs[1] = 0x02;
93        octs[2] = 0xfb;
94        octs[3] = 0xaf;
95        BigEndian::write_u16(&mut octs[6..8], *self as u16);
96        BigEndian::write_u64(&mut octs[8..], ip_number);
97        octs.into()
98    }
99}