lanutils.lanutils
1import ipaddress 2import socket 3from concurrent.futures import ThreadPoolExecutor 4 5import icmplib 6import ifaddr 7 8 9def get_myip( 10 adapters_to_find: list[str] = ["Ethernet", "Wi-Fi", "wlo1"] 11) -> list[tuple[str, int, str]]: 12 """Returns this machine's active local network ipv4 addresses 13 for adapters listed in adapters_to_find. 14 15 :param adapters_to_find: List of adapter names to look for 16 active ip addresses. If None or an empty list, return 17 any adapters with active addresses. 18 19 Returns a list of tuples. Each tuple contains the ip address, 20 network prefix, and name for the adapter.""" 21 myips = [] 22 for adapter in ifaddr.get_adapters(): 23 for ip in adapter.ips: 24 # ipaddress module throws exception if it doesn't think the ip address is valid 25 try: 26 if ( 27 ip.is_IPv4 28 and not ip.ip.startswith("169.254.") # Indicates an inactive ip 29 and ( 30 (adapters_to_find and ip.nice_name in adapters_to_find) 31 or not adapters_to_find 32 ) 33 ): 34 myips.append((ip.ip, ip.network_prefix, ip.nice_name)) 35 except Exception as e: 36 pass 37 return myips 38 39 40def ip_is_alive(ip: str, timeout: float = 0.1) -> bool: 41 """Checks if the host at ip is alive. 42 43 :param timeout: How long in seconds 44 to wait before declaring host dead.""" 45 return icmplib.ping(ip, count=1, timeout=timeout, privileged=False).is_alive 46 47 48def enumerate_devices(timeout: float = 0.1) -> list[str]: 49 """Scan the local network this device is on for devices 50 and return a list of ip addresses, including this device. 51 52 :param timeout: How long, in seconds, to wait before 53 declaring an ip address inactive.""" 54 myip = get_myip()[0] 55 network = ipaddress.ip_network(f"{myip[0]}/{myip[1]}", strict=False) 56 # Skip network and broadcast ip addresses 57 hosts = list(network.hosts())[1:-1] 58 with ThreadPoolExecutor() as executor: 59 threads = [executor.submit(ip_is_alive, str(ip), timeout) for ip in hosts] 60 return [str(ip) for ip, thread in zip(hosts, threads) if thread.result()] 61 62 63def port_is_open(ip: str, port: int) -> bool: 64 """Returns whether a port is open on the given ip address.""" 65 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 66 sock.settimeout(0.1) 67 try: 68 sock.connect((ip, port)) 69 sock.close() 70 return True 71 except Exception as e: 72 return False 73 74 75def scan_ports(ip: str, port_range: tuple[int, int] = (0, 65535)) -> list[int]: 76 """Scan given ip address for open ports. 77 78 :param port_range: Range of port numbers to scan, inclusive.""" 79 ports = list(range(port_range[0], port_range[1] + 1)) 80 with ThreadPoolExecutor() as executor: 81 threads = [executor.submit(port_is_open, ip, port) for port in ports] 82 return [port for port, thread in zip(ports, threads) if thread.result()] 83 84 85def get_available_port(ip: str, port_range: tuple[int, int] = (0, 65535)) -> int: 86 """Get the first unused port. 87 88 :param ip: The ip address to scan. 89 90 :param port_range: The port range to scan, bounds inclusive.""" 91 for port in range(port_range[0], port_range[1] + 1): 92 if not port_is_open(ip, port): 93 return port 94 raise RuntimeError( 95 f"Could not find an available port within the range {port_range}" 96 )
def
get_myip( adapters_to_find: list[str] = ['Ethernet', 'Wi-Fi', 'wlo1']) -> list[tuple[str, int, str]]:
10def get_myip( 11 adapters_to_find: list[str] = ["Ethernet", "Wi-Fi", "wlo1"] 12) -> list[tuple[str, int, str]]: 13 """Returns this machine's active local network ipv4 addresses 14 for adapters listed in adapters_to_find. 15 16 :param adapters_to_find: List of adapter names to look for 17 active ip addresses. If None or an empty list, return 18 any adapters with active addresses. 19 20 Returns a list of tuples. Each tuple contains the ip address, 21 network prefix, and name for the adapter.""" 22 myips = [] 23 for adapter in ifaddr.get_adapters(): 24 for ip in adapter.ips: 25 # ipaddress module throws exception if it doesn't think the ip address is valid 26 try: 27 if ( 28 ip.is_IPv4 29 and not ip.ip.startswith("169.254.") # Indicates an inactive ip 30 and ( 31 (adapters_to_find and ip.nice_name in adapters_to_find) 32 or not adapters_to_find 33 ) 34 ): 35 myips.append((ip.ip, ip.network_prefix, ip.nice_name)) 36 except Exception as e: 37 pass 38 return myips
Returns this machine's active local network ipv4 addresses for adapters listed in adapters_to_find.
Parameters
- adapters_to_find: List of adapter names to look for active ip addresses. If None or an empty list, return any adapters with active addresses.
Returns a list of tuples. Each tuple contains the ip address, network prefix, and name for the adapter.
def
ip_is_alive(ip: str, timeout: float = 0.1) -> bool:
41def ip_is_alive(ip: str, timeout: float = 0.1) -> bool: 42 """Checks if the host at ip is alive. 43 44 :param timeout: How long in seconds 45 to wait before declaring host dead.""" 46 return icmplib.ping(ip, count=1, timeout=timeout, privileged=False).is_alive
Checks if the host at ip is alive.
Parameters
- timeout: How long in seconds to wait before declaring host dead.
def
enumerate_devices(timeout: float = 0.1) -> list[str]:
49def enumerate_devices(timeout: float = 0.1) -> list[str]: 50 """Scan the local network this device is on for devices 51 and return a list of ip addresses, including this device. 52 53 :param timeout: How long, in seconds, to wait before 54 declaring an ip address inactive.""" 55 myip = get_myip()[0] 56 network = ipaddress.ip_network(f"{myip[0]}/{myip[1]}", strict=False) 57 # Skip network and broadcast ip addresses 58 hosts = list(network.hosts())[1:-1] 59 with ThreadPoolExecutor() as executor: 60 threads = [executor.submit(ip_is_alive, str(ip), timeout) for ip in hosts] 61 return [str(ip) for ip, thread in zip(hosts, threads) if thread.result()]
Scan the local network this device is on for devices and return a list of ip addresses, including this device.
Parameters
- timeout: How long, in seconds, to wait before declaring an ip address inactive.
def
port_is_open(ip: str, port: int) -> bool:
64def port_is_open(ip: str, port: int) -> bool: 65 """Returns whether a port is open on the given ip address.""" 66 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 67 sock.settimeout(0.1) 68 try: 69 sock.connect((ip, port)) 70 sock.close() 71 return True 72 except Exception as e: 73 return False
Returns whether a port is open on the given ip address.
def
scan_ports(ip: str, port_range: tuple[int, int] = (0, 65535)) -> list[int]:
76def scan_ports(ip: str, port_range: tuple[int, int] = (0, 65535)) -> list[int]: 77 """Scan given ip address for open ports. 78 79 :param port_range: Range of port numbers to scan, inclusive.""" 80 ports = list(range(port_range[0], port_range[1] + 1)) 81 with ThreadPoolExecutor() as executor: 82 threads = [executor.submit(port_is_open, ip, port) for port in ports] 83 return [port for port, thread in zip(ports, threads) if thread.result()]
Scan given ip address for open ports.
Parameters
- port_range: Range of port numbers to scan, inclusive.
def
get_available_port(ip: str, port_range: tuple[int, int] = (0, 65535)) -> int:
86def get_available_port(ip: str, port_range: tuple[int, int] = (0, 65535)) -> int: 87 """Get the first unused port. 88 89 :param ip: The ip address to scan. 90 91 :param port_range: The port range to scan, bounds inclusive.""" 92 for port in range(port_range[0], port_range[1] + 1): 93 if not port_is_open(ip, port): 94 return port 95 raise RuntimeError( 96 f"Could not find an available port within the range {port_range}" 97 )
Get the first unused port.
Parameters
ip: The ip address to scan.
port_range: The port range to scan, bounds inclusive.