tina-core 0.0.2

Tina platform
Documentation
//! IP 工具
use once_cell::sync::Lazy;
use std::net::UdpSocket;
use tracing::warn;

/// IP 工具
pub struct IpUtil;

impl IpUtil {
    /// 获取IP地址
    #[cfg(feature = "server-http")]
    pub fn get_ip_addr<Req: crate::tina::server::http::request_ext::RequestExt>(req: &Req) -> std::borrow::Cow<str> {
        req.get_remote_ip_address()
    }
    /// 判断是否是内部 IP
    pub fn internal_ip(ip: &str) -> bool {
        if ip == "127.0.0.1" {
            return true;
        }
        let addr = Self::text_to_numeric_format_v_4(ip);
        internal_ip(addr.as_slice())
    }
    /// 将IPv4地址转换成字节
    pub fn text_to_numeric_format_v_4(text: &str) -> Vec<u8> {
        if text.is_empty() {
            return vec![];
        }

        let mut bytes: [u8; 4] = [0, 0, 0, 0];
        let elements = text.split('.').collect::<Vec<&str>>();
        let mut l: u64;
        match elements.len() {
            1 => {
                l = match elements[0].parse::<u64>() {
                    Ok(v) => v,
                    Err(_) => {
                        warn!("invalid ip v4: {}", text);
                        return vec![];
                    }
                };
                if l > 4294967295 {
                    return vec![];
                }
                bytes[0] = (l >> 24 & 0xFF) as u32 as u8;
                bytes[1] = ((l & 0xFFFFFF) >> 16 & 0xFF) as u32 as u8;
                bytes[2] = ((l & 0xFFFF) >> 8 & 0xFF) as u32 as u8;
                bytes[3] = (l & 0xFF) as u32 as u8;
            }
            2 => {
                l = match elements[0].parse::<u64>() {
                    Ok(v) => v,
                    Err(_) => {
                        warn!("invalid ip v4: {}", text);
                        return vec![];
                    }
                };
                if l > 255 {
                    return vec![];
                }
                bytes[0] = (l & 0xFF) as u32 as u8;
                l = match elements[1].parse::<u64>() {
                    Ok(v) => v,
                    Err(_) => {
                        warn!("invalid ip v4: {}", text);
                        return vec![];
                    }
                };
                if l > 16777215 {
                    return vec![];
                }
                bytes[1] = (l >> 16 & 0xFF) as u32 as u8;
                bytes[2] = ((l & 0xFFFF) >> 8 & 0xFF) as u32 as u8;
                bytes[3] = (l & 0xFF) as u32 as u8;
            }
            3 => {
                for i in 0..2 {
                    l = match elements[i].parse::<u64>() {
                        Ok(v) => v,
                        Err(_) => {
                            warn!("invalid ip v4: {}", text);
                            return vec![];
                        }
                    };
                    if l > 255 {
                        return vec![];
                    }
                    bytes[i] = (l & 0xFF) as u32 as u8;
                }
                l = match elements[2].parse::<u64>() {
                    Ok(v) => v,
                    Err(_) => {
                        warn!("invalid ip v4: {}", text);
                        return vec![];
                    }
                };
                if l > 65535 {
                    return vec![];
                }
                bytes[2] = (l >> 8 & 0xFF) as u32 as u8;
                bytes[3] = (l & 0xFF) as u32 as u8;
            }
            4 => {
                for i in 0..4 {
                    l = match elements[i].parse::<u64>() {
                        Ok(v) => v,
                        Err(_) => {
                            warn!("invalid ip v4: {}", text);
                            return vec![];
                        }
                    };
                    if l > 255 {
                        return vec![];
                    }
                    bytes[i] = (l & 0xFF) as u32 as u8;
                }
            }
            _ => return vec![],
        }
        Vec::from(bytes)
    }
    /// 获取本机IP
    pub fn get_host_ip() -> Option<&'static str> {
        static HOST_IP: Lazy<Option<String>> = Lazy::new(|| {
            let socket = match UdpSocket::bind("0.0.0.0:0") {
                Ok(s) => s,
                Err(_) => return None,
            };

            match socket.connect("8.8.8.8:80") {
                Ok(()) => (),
                Err(_) => return None,
            };

            match socket.local_addr() {
                Ok(addr) => Some(addr.ip().to_string()),
                Err(_) => None,
            }
        });
        HOST_IP.as_ref().map(|s| s.as_str())
    }
}

fn internal_ip(addr: &[u8]) -> bool {
    if addr.len() < 2 {
        return true;
    }
    let b0 = addr[0];
    let b1 = addr[1];
    // 10.x.x.x/8
    const SECTION_1: u8 = 0x0A;
    // 172.16.x.x/12
    const SECTION_2: u8 = 0xAC;
    const SECTION_3: u8 = 0x10;
    const SECTION_4: u8 = 0x1F;
    // 192.168.x.x/16
    const SECTION_5: u8 = 0xC0;
    const SECTION_6: u8 = 0xA8;
    match b0 {
        SECTION_1 => true,
        SECTION_2 => {
            if (SECTION_3..=SECTION_4).contains(&b1) {
                return true;
            }
            false
        }
        SECTION_5 => matches!(b1, SECTION_6),
        _ => false,
    }
}