From 9429e7488e421830a78d941fd4bbf93131457120 Mon Sep 17 00:00:00 2001 From: Tamas Nepusz Date: Thu, 11 Jul 2024 15:12:47 +0200 Subject: [PATCH] fix: work around a bug in netifaces that crashes on Android --- src/flockwave/networking/interfaces.py | 30 +++++++++++++++++++++----- src/flockwave/networking/scanner.py | 5 +++-- src/flockwave/networking/sockets.py | 6 ++++-- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/flockwave/networking/interfaces.py b/src/flockwave/networking/interfaces.py index b9573c8..ab75fe4 100644 --- a/src/flockwave/networking/interfaces.py +++ b/src/flockwave/networking/interfaces.py @@ -1,5 +1,7 @@ """Generic networking-related utility functions.""" +import sys + from ipaddress import ( ip_address, ip_network, @@ -7,7 +9,7 @@ IPv6Address, IPv6Network, ) -from netifaces import AF_INET, AF_INET6, AF_LINK, ifaddresses, interfaces +from netifaces import AF_INET, AF_INET6, AF_LINK, ifaddresses, interfaces as _interfaces from typing import Optional, Sequence, Union from .addressing import canonicalize_mac_address @@ -19,9 +21,23 @@ "get_all_ipv4_addresses", "get_broadcast_address_of_network_interface", "get_link_layer_address_mapping", + "list_network_interfaces", "resolve_network_interface_or_address", ) +_is_android = hasattr(sys, "getandroidapilevel") + + +if _is_android: + + def list_network_interfaces() -> Sequence[str]: + """Lists all the network interfaces of the system.""" + # rmnet and r_rmnet interfaces on Android seem to cause a crash with + # netifaces.ifaddresses() so we pretend they do not exist + return [iface for iface in _interfaces() if "rmnet" not in iface] +else: + list_network_interfaces = _interfaces + def find_interfaces_with_address( address: str, @@ -44,7 +60,7 @@ def find_interfaces_with_address( family = AF_INET candidates: list[tuple[str, Union[IPv4Network, IPv6Network]]] = [] - for interface in interfaces(): + for interface in list_network_interfaces(): specs = ifaddresses(interface).get(family) or [] ip_addresses_in_network = ( (spec.get("addr"), spec.get("netmask")) for spec in specs @@ -79,7 +95,7 @@ def find_interfaces_in_network( family = AF_INET candidates: list[tuple[str, str, Optional[str]]] = [] - for interface in interfaces(): + for interface in list_network_interfaces(): specs = ifaddresses(interface).get(family) or [] ip_addresses_in_network = ( (spec.get("addr") or "", spec.get("netmask")) @@ -130,7 +146,7 @@ def get_address_of_network_interface(value: str, family: int = AF_INET) -> str: def get_all_ipv4_addresses() -> Sequence[str]: """Returns all IPv4 addresses of the current machine.""" result = [] - for iface in interfaces(): + for iface in list_network_interfaces(): addresses = ifaddresses(iface) if AF_INET in addresses: result.append(addresses[AF_INET][0]["addr"]) @@ -169,7 +185,7 @@ def get_link_layer_address_mapping() -> dict[str, str]: We assume that one interface may have only one link-layer address. """ result = {} - for iface in interfaces(): + for iface in list_network_interfaces(): addresses = ifaddresses(iface) if AF_LINK in addresses: result[iface] = canonicalize_mac_address(addresses[AF_LINK][0]["addr"]) @@ -190,6 +206,10 @@ def resolve_network_interface_or_address(value: str) -> str: Returns: the IPv4 address of the interface. + + Raises: + ValueError: if the value seems to be a network interface name but it has + no IPv4 address """ try: return str(ip_address(value)) diff --git a/src/flockwave/networking/scanner.py b/src/flockwave/networking/scanner.py index 0c2333f..d9450fd 100644 --- a/src/flockwave/networking/scanner.py +++ b/src/flockwave/networking/scanner.py @@ -3,7 +3,7 @@ from enum import Enum from functools import partial from ipaddress import ip_address -from netifaces import AF_INET, AF_LINK, ifaddresses, interfaces +from netifaces import AF_INET, AF_LINK, ifaddresses from typing import ( Callable, Iterable, @@ -12,6 +12,7 @@ Sequence, ) +from .interfaces import list_network_interfaces from .utils import aclosing from .wired import is_carrier_detected, is_maybe_wired_or_wireless from .wireless import get_connected_access_point_name, is_likely_wireless @@ -166,7 +167,7 @@ def _find_relevant_interfaces( its IPv4 addresses (or an empty list if the interface has no IPv4 address) """ - for interface in interfaces(): + for interface in list_network_interfaces(): addresses = ifaddresses(interface) ipv4_addresses = addresses.get(AF_INET) if not ipv4_addresses: diff --git a/src/flockwave/networking/sockets.py b/src/flockwave/networking/sockets.py index 5ec27e6..b40e7e0 100644 --- a/src/flockwave/networking/sockets.py +++ b/src/flockwave/networking/sockets.py @@ -4,9 +4,11 @@ from contextlib import closing from errno import EADDRINUSE from ipaddress import ip_address, ip_network -from netifaces import AF_INET, gateways, ifaddresses, interfaces +from netifaces import AF_INET, gateways, ifaddresses from typing import Any, Optional +from .interfaces import list_network_interfaces + __all__ = ( "can_bind_to_tcp_address", "can_bind_to_udp_address", @@ -187,7 +189,7 @@ def get_socket_address( remote_host = None if remote_host: - for interface in interfaces(): + for interface in list_network_interfaces(): # We are currently interested only in IPv4 addresses specs = ifaddresses(interface).get(AF_INET) if not specs: