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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#[cfg(feature = "std")]
use std::str::{from_utf8_unchecked, FromStr};

#[cfg(feature = "std")]
use std::net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr};

#[cfg(feature = "std")]
#[inline]
pub fn is_local_ip(addr: IpAddr) -> bool {
    match addr {
        IpAddr::V4(addr) => is_local_ipv4(addr),
        IpAddr::V6(addr) => is_local_ipv6(addr),
    }
}

#[cfg(feature = "std")]
#[inline]
pub fn is_local_ipv4(addr: Ipv4Addr) -> bool {
    addr.is_private()
        || addr.is_loopback()
        || addr.is_link_local()
        || addr.is_broadcast()
        || addr.is_documentation()
        || addr.is_unspecified()
}

#[cfg(feature = "std")]
#[inline]
pub fn is_local_ipv6(addr: Ipv6Addr) -> bool {
    let segments = addr.segments();

    let first = segments[0];

    if (first & 0xff00) == 0xff00 {
        // is_multicast
        first & 0x000f != 14 // 14 is `std::net::Ipv6MulticastScope::Global`
    } else {
        if segments.starts_with(&[0, 0, 0, 0, 0, 0, 0]) {
            match segments[7] {
                0 | 1 => {
                    // is_loopback
                    // is_unspecified
                    return true;
                }
                _ => (),
            }
        }

        match first & 0xffc0 {
            0xfe80 | 0xfec0 => {
                // is_unicast_link_local
                // is_unicast_site_local
                return true;
            }
            _ => (),
        }

        if first & 0xfe00 == 0xfc00 || (first == 0x2001) && (segments[1] == 0xdb8) {
            // is_unique_local
            // is_documentation
            return true;
        }

        match addr.to_ipv4() {
            Some(addr) => is_local_ipv4(addr),
            None => false,
        }
    }
}

#[inline]
pub fn is_local_domain<S: AsRef<str>>(s: S) -> bool {
    let bytes = s.as_ref().as_bytes();

    debug_assert!(!bytes.is_empty());

    let length_dec = bytes.len() - 1;

    let bytes = if bytes[length_dec] == b'.' {
        &bytes[..length_dec]
    } else {
        bytes
    };

    bytes.eq_ignore_ascii_case(b"localhost")
}

#[inline]
pub fn is_at_least_two_labels_domain<S: AsRef<str>>(s: S) -> bool {
    let s = s.as_ref();

    debug_assert!(!s.is_empty());

    s
        .bytes()
        .take(s.len() - 1) // to avoid "."-ended domain
        .any(|e| e == b'.')
}

#[cfg(feature = "std")]
#[inline]
pub fn parse_ipv4_allow_an_ended_dot<S: AsRef<str>>(s: S) -> Result<Ipv4Addr, AddrParseError> {
    let s = s.as_ref();
    let bytes = s.as_bytes();

    debug_assert!(!bytes.is_empty());

    let length = bytes.len();

    let s = if length > 0 && bytes[length - 1] == b'.' {
        unsafe { from_utf8_unchecked(&bytes[..(length - 1)]) }
    } else {
        s
    };

    Ipv4Addr::from_str(s)
}