1use std::net::{IpAddr, Ipv4Addr, TcpListener, UdpSocket};
2use teaql_tool_core::{Result, TeaQLToolError};
3
4pub struct NetTool;
6
7impl NetTool {
8 pub fn new() -> Self {
9 Self
10 }
11
12 pub fn get_local_ipv4(&self) -> Result<String> {
14 let socket = UdpSocket::bind("0.0.0.0:0")
15 .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to bind socket: {}", e)))?;
16 socket.connect("8.8.8.8:80")
17 .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to connect socket: {}", e)))?;
18 let addr = socket.local_addr()
19 .map_err(|e| TeaQLToolError::ExecutionError(format!("Failed to get local address: {}", e)))?;
20 Ok(addr.ip().to_string())
21 }
22
23 pub fn is_usable_local_port(&self, port: u16) -> bool {
25 match TcpListener::bind(("127.0.0.1", port)) {
26 Ok(_) => true,
27 Err(_) => false,
28 }
29 }
30
31 pub fn is_inner_ip(&self, ip_str: impl AsRef<str>) -> bool {
33 let ip: IpAddr = match ip_str.as_ref().parse() {
34 Ok(ip) => ip,
35 Err(_) => return false,
36 };
37
38 match ip {
39 IpAddr::V4(ipv4) => {
40 let octets = ipv4.octets();
41 if octets[0] == 10 { return true; }
43 if octets[0] == 172 && octets[1] >= 16 && octets[1] <= 31 { return true; }
45 if octets[0] == 192 && octets[1] == 168 { return true; }
47 if octets[0] == 127 { return true; }
49 false
50 }
51 IpAddr::V6(ipv6) => {
52 let segments = ipv6.segments();
53 if (segments[0] & 0xfe00) == 0xfc00 { return true; }
55 if ipv6 == std::net::Ipv6Addr::LOCALHOST { return true; }
57 false
58 }
59 }
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn test_net_tool() {
69 let tool = NetTool::new();
70 assert!(tool.is_usable_local_port(0));
72 assert!(tool.is_inner_ip("192.168.1.10"));
73 assert!(tool.is_inner_ip("10.0.0.5"));
74 assert!(tool.is_inner_ip("127.0.0.1"));
75 assert!(!tool.is_inner_ip("8.8.8.8"));
76 }
77}