use std::net::{IpAddr, Ipv4Addr, TcpListener, UdpSocket};
use teaql_tool_core::{Result, TeaQLToolError};
pub struct NetTool;
impl NetTool {
pub fn new() -> Self {
Self
}
pub fn get_local_ipv4(&self) -> Result<String> {
let socket = UdpSocket::bind("0.0.0.0:0")
.map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to bind socket: {}", e)))?;
socket.connect("8.8.8.8:80")
.map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to connect socket: {}", e)))?;
let addr = socket.local_addr()
.map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to get local address: {}", e)))?;
Ok(addr.ip().to_string())
}
pub fn is_usable_local_port(&self, port: u16) -> bool {
match TcpListener::bind(("127.0.0.1", port)) {
Ok(_) => true,
Err(_) => false,
}
}
pub fn is_inner_ip(&self, ip_str: impl AsRef<str>) -> bool {
let ip: IpAddr = match ip_str.as_ref().parse() {
Ok(ip) => ip,
Err(_) => return false,
};
match ip {
IpAddr::V4(ipv4) => {
let octets = ipv4.octets();
if octets[0] == 10 { return true; }
if octets[0] == 172 && octets[1] >= 16 && octets[1] <= 31 { return true; }
if octets[0] == 192 && octets[1] == 168 { return true; }
if octets[0] == 127 { return true; }
false
}
IpAddr::V6(ipv6) => {
let segments = ipv6.segments();
if (segments[0] & 0xfe00) == 0xfc00 { return true; }
if ipv6 == std::net::Ipv6Addr::LOCALHOST { return true; }
false
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_net_tool() {
let tool = NetTool::new();
assert!(tool.is_usable_local_port(0));
assert!(tool.is_inner_ip("192.168.1.10"));
assert!(tool.is_inner_ip("10.0.0.5"));
assert!(tool.is_inner_ip("127.0.0.1"));
assert!(!tool.is_inner_ip("8.8.8.8"));
}
}