mirror of
https://github.com/HorlogeSkynet/archey4
synced 2024-12-28 16:00:13 +01:00
121b1cc796
Co-authored-by: Michael Bromilow <12384431+ingrinder@users.noreply.github.com> Co-authored-by: Samuel FORESTIER <samuel+dev@forestier.app>
98 lines
3.5 KiB
Python
98 lines
3.5 KiB
Python
"""Local IP addresses detection class"""
|
|
|
|
import ipaddress
|
|
from itertools import islice
|
|
from typing import Iterator
|
|
|
|
try:
|
|
import netifaces
|
|
except ImportError:
|
|
netifaces = None
|
|
|
|
from archey.entry import Entry
|
|
|
|
|
|
class LanIP(Entry):
|
|
"""Relies on the `netifaces` module to detect LAN IP addresses"""
|
|
|
|
_ICON = "\U000f0a60" # md_ip_network
|
|
_PRETTY_NAME = "LAN IP"
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
if not netifaces:
|
|
self._logger.warning(
|
|
"`netifaces` Python module couldn't be found. "
|
|
"Please either install it or explicitly disable `LAN_IP` entry in configuration."
|
|
)
|
|
return
|
|
|
|
# IPv4 will be enabled by default.
|
|
addr_families = [netifaces.AF_INET]
|
|
if self.options.get("ipv6_support", True):
|
|
addr_families.append(netifaces.AF_INET6)
|
|
|
|
max_count = self.options.get("max_count", 2)
|
|
# Consistency with other entries' configuration: Infinite count if false.
|
|
if max_count is False:
|
|
max_count = None
|
|
|
|
# Global IP addresses (in RFC1918 terms) will be hidden by default.
|
|
show_global = bool(self.options.get("show_global"))
|
|
|
|
# Link-local IP addresses (in RFC3927 terms) will be shown by default.
|
|
show_link_local = bool(self.options.get("show_link_local", True))
|
|
|
|
self.value = list(
|
|
islice(
|
|
self._lan_ip_addresses_generator(addr_families, show_global, show_link_local),
|
|
max_count,
|
|
)
|
|
)
|
|
|
|
@staticmethod
|
|
def _lan_ip_addresses_generator(
|
|
addr_families: list, show_global: bool, show_link_local: bool
|
|
) -> Iterator[str]:
|
|
"""Generator yielding local IP address according to passed address families"""
|
|
# Loop through all available network interfaces.
|
|
for if_name in netifaces.interfaces():
|
|
# Fetch associated addresses elements.
|
|
if_addrs = netifaces.ifaddresses(if_name)
|
|
|
|
for addr_family in addr_families:
|
|
for if_addr in if_addrs.get(addr_family, []):
|
|
# IPv6 addresses may contain '%' token separator.
|
|
ip_addr = ipaddress.ip_address(if_addr["addr"].split("%")[0])
|
|
|
|
# Filter out loopback and public/link-local IP addresses (if enabled).
|
|
if (
|
|
not ip_addr.is_loopback
|
|
and (not ip_addr.is_global or show_global)
|
|
and (not ip_addr.is_link_local or show_link_local)
|
|
):
|
|
# Finally, yield the address compressed representation.
|
|
yield ip_addr.compressed
|
|
|
|
def output(self, output) -> None:
|
|
"""Adds the entry to `output` after pretty-formatting the IP address list."""
|
|
# If we found IP addresses, join them together nicely.
|
|
# If not, fall back on default strings according to `netifaces` availability.
|
|
if self.value:
|
|
if not self.options.get("one_line", True):
|
|
# One-line output has been disabled, add one IP address per item.
|
|
for ip_address in self.value:
|
|
output.append(self.name, ip_address)
|
|
|
|
return
|
|
|
|
text_output = ", ".join(self.value)
|
|
|
|
elif netifaces:
|
|
text_output = self._default_strings.get("no_address")
|
|
else:
|
|
text_output = self._default_strings.get("not_detected")
|
|
|
|
output.append(self.name, text_output)
|