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.