lanutils.lanutils

  1import ipaddress
  2import socket
  3from concurrent.futures import ThreadPoolExecutor
  4
  5import icmplib
  6import ifaddr
  7
  8
  9def get_my_ip(
 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_my_ip()[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    )
 97
 98
 99def whats_my_ip_cli():
100    print(get_my_ip())
101
102
103def enumerate_devices_cli():
104    print(*enumerate_devices(), sep="\n")
def get_my_ip( adapters_to_find: list[str] = ['Ethernet', 'Wi-Fi', 'wlo1']) -> list[tuple[str, int, str]]:
10def get_my_ip(
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_my_ip()[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.

def whats_my_ip_cli():
100def whats_my_ip_cli():
101    print(get_my_ip())
def enumerate_devices_cli():
104def enumerate_devices_cli():
105    print(*enumerate_devices(), sep="\n")