libsubconverter/utils/
network.rs

1//! Network utilities for IP address handling and validation
2
3use std::net::{IpAddr, ToSocketAddrs};
4
5/// Resolves a hostname to its IP address
6///
7/// # Arguments
8///
9/// * `host` - The hostname to resolve
10///
11/// # Returns
12///
13/// The IP address as a string, or an empty string if resolution fails
14pub fn hostname_to_ip_addr(host: &str) -> Option<String> {
15    // Try to resolve the hostname using the standard library
16    let sock_addr = format!("{}:0", host).to_socket_addrs();
17
18    match sock_addr {
19        Ok(mut addrs) => {
20            // Find the first IPv4 or IPv6 address
21            while let Some(addr) = addrs.next() {
22                match addr.ip() {
23                    IpAddr::V4(ipv4) => return Some(ipv4.to_string()),
24                    IpAddr::V6(ipv6) => return Some(ipv6.to_string()),
25                }
26            }
27            None
28        }
29        Err(_) => None,
30    }
31}
32
33/// Checks if a string is a valid IPv4 address
34///
35/// # Arguments
36///
37/// * `s` - The string to check
38///
39/// # Returns
40///
41/// True if the string is a valid IPv4 address, false otherwise
42pub fn is_ipv4(s: &str) -> bool {
43    let parts: Vec<&str> = s.split('.').collect();
44
45    if parts.len() != 4 {
46        return false;
47    }
48
49    for part in parts {
50        // Check if part is a valid number between 0-255
51        match part.parse::<u8>() {
52            Ok(_) => continue,
53            Err(_) => return false,
54        }
55    }
56
57    true
58}
59
60/// Checks if a string is a valid IPv6 address
61///
62/// # Arguments
63///
64/// * `s` - The string to check
65///
66/// # Returns
67///
68/// True if the string is a valid IPv6 address, false otherwise
69pub fn is_ipv6(s: &str) -> bool {
70    // Basic implementation - placeholder
71    // In a real implementation, we'd do proper IPv6 validation
72    s.contains(':')
73}
74
75/// Checks if a string is a valid URL
76pub fn is_link(link: &str) -> bool {
77    link.starts_with("http://")
78        || link.starts_with("https://")
79        || link.starts_with("data:")
80        || link.starts_with("content://")
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn test_is_ipv4_valid() {
89        assert!(is_ipv4("192.168.1.1"));
90        assert!(is_ipv4("127.0.0.1"));
91        assert!(is_ipv4("8.8.8.8"));
92        assert!(is_ipv4("255.255.255.255"));
93    }
94
95    #[test]
96    fn test_is_ipv4_invalid() {
97        assert!(!is_ipv4("192.168.1"));
98        assert!(!is_ipv4("192.168.1.256"));
99        assert!(!is_ipv4("192.168.1.a"));
100        assert!(!is_ipv4("192.168.1.1.1"));
101        assert!(!is_ipv4("2001:0db8:85a3:0000:0000:8a2e:0370:7334"));
102    }
103
104    #[test]
105    fn test_is_ipv6_valid() {
106        assert!(is_ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334"));
107        assert!(is_ipv6("::1"));
108        assert!(is_ipv6("2001:db8::"));
109    }
110
111    #[test]
112    fn test_is_ipv6_invalid() {
113        assert!(!is_ipv6("192.168.1.1"));
114        assert!(!is_ipv6("not an ip"));
115    }
116
117    #[test]
118    fn test_hostname_to_ip_addr() {
119        // This test might be flaky depending on network conditions
120        let ip = hostname_to_ip_addr("localhost");
121        assert!(ip.is_some());
122        assert!(ip.as_ref().unwrap() == "127.0.0.1" || ip.as_ref().unwrap() == "::1");
123    }
124}