from __future__ import annotations import logging import os import shutil import subprocess from typing import Any logger = logging.getLogger('intercept.dependencies') # Additional paths to search for tools (e.g., /usr/sbin on Debian) EXTRA_TOOL_PATHS = ['/usr/sbin', '/sbin'] def check_tool(name: str) -> bool: """Check if a tool is installed.""" return get_tool_path(name) is not None def get_tool_path(name: str) -> str | None: """Get the full path to a tool, checking standard PATH and extra locations.""" # First check standard PATH path = shutil.which(name) if path: return path # Check additional paths (e.g., /usr/sbin for aircrack-ng on Debian) for extra_path in EXTRA_TOOL_PATHS: full_path = os.path.join(extra_path, name) if os.path.isfile(full_path) and os.access(full_path, os.X_OK): return full_path return None def _get_soapy_env() -> dict[str, str]: """Get environment variables needed for SoapySDR on macOS. On macOS with Homebrew, SoapySDR modules are installed in paths that require SOAPY_SDR_ROOT or DYLD_LIBRARY_PATH to be set. This fixes detection issues where modules like SoapyHackRF are installed but not found by SoapySDRUtil. See: https://github.com/smittix/intercept/issues/77 """ import platform env = os.environ.copy() if platform.system() == 'Darwin': # Homebrew paths for Apple Silicon and Intel Macs homebrew_paths = ['/opt/homebrew', '/usr/local'] lib_paths = [] module_paths = [] for base in homebrew_paths: lib_path = f'{base}/lib' if os.path.isdir(lib_path): lib_paths.append(lib_path) # SoapySDR modules are in lib/SoapySDR/modules soapy_mod_base = f'{base}/lib/SoapySDR' if os.path.isdir(soapy_mod_base): module_paths.append(soapy_mod_base) if lib_paths: current_dyld = env.get('DYLD_LIBRARY_PATH', '') env['DYLD_LIBRARY_PATH'] = ':'.join(lib_paths + ([current_dyld] if current_dyld else [])) # Set SOAPY_SDR_ROOT if we found Homebrew installation for base in homebrew_paths: if os.path.isdir(f'{base}/lib/SoapySDR'): env['SOAPY_SDR_ROOT'] = base break return env def check_soapy_factory(factory_name: str) -> bool: """Check if a SoapySDR factory/module is available using SoapySDRUtil.""" try: # Run SoapySDRUtil --info and look for the factory in 'Available factories' # Use macOS-aware environment to find Homebrew-installed modules env = _get_soapy_env() result = subprocess.run(['SoapySDRUtil', '--info'], capture_output=True, text=True, env=env) if result.returncode != 0: return False # Parse output for available factories # Format usually: "Available factories... hackrf, lime, rtlsdr" for line in result.stdout.splitlines(): if "Available factories" in line: factories = line.split("...")[-1].strip().split(",") factories = [f.strip() for f in factories] if factory_name in factories: return True return False except Exception as e: logger.debug(f"Failed to check SoapySDR factory {factory_name}: {e}") return False # Comprehensive tool dependency definitions TOOL_DEPENDENCIES = { 'pager': { 'name': 'Pager Decoding', 'tools': { 'rtl_fm': { 'required': True, 'description': 'RTL-SDR FM demodulator', 'install': { 'apt': 'sudo apt install rtl-sdr', 'brew': 'brew install librtlsdr', 'manual': 'https://osmocom.org/projects/rtl-sdr/wiki' } }, 'multimon-ng': { 'required': True, 'description': 'Digital transmission decoder', 'install': { 'apt': 'sudo apt install multimon-ng', 'brew': 'brew install multimon-ng', 'manual': 'https://github.com/EliasOenal/multimon-ng' } }, 'rtl_test': { 'required': False, 'description': 'RTL-SDR device detection', 'install': { 'apt': 'sudo apt install rtl-sdr', 'brew': 'brew install librtlsdr', 'manual': 'https://osmocom.org/projects/rtl-sdr/wiki' } } } }, 'sensor': { 'name': '433MHz Sensors', 'tools': { 'rtl_433': { 'required': True, 'description': 'ISM band decoder for sensors, weather stations, TPMS', 'install': { 'apt': 'sudo apt install rtl-433', 'brew': 'brew install rtl_433', 'manual': 'https://github.com/merbanan/rtl_433' } } } }, 'wifi': { 'name': 'WiFi Reconnaissance', 'tools': { 'airmon-ng': { 'required': True, 'description': 'Monitor mode controller', 'install': { 'apt': 'sudo apt install aircrack-ng', 'brew': 'Not available on macOS', 'manual': 'https://aircrack-ng.org' } }, 'airodump-ng': { 'required': True, 'description': 'WiFi network scanner', 'install': { 'apt': 'sudo apt install aircrack-ng', 'brew': 'Not available on macOS', 'manual': 'https://aircrack-ng.org' } }, 'aireplay-ng': { 'required': False, 'description': 'Deauthentication / packet injection', 'install': { 'apt': 'sudo apt install aircrack-ng', 'brew': 'Not available on macOS', 'manual': 'https://aircrack-ng.org' } }, 'aircrack-ng': { 'required': False, 'description': 'Handshake verification', 'install': { 'apt': 'sudo apt install aircrack-ng', 'brew': 'brew install aircrack-ng', 'manual': 'https://aircrack-ng.org' } }, 'hcxdumptool': { 'required': False, 'description': 'PMKID capture tool', 'install': { 'apt': 'sudo apt install hcxdumptool', 'brew': 'brew install hcxtools', 'manual': 'https://github.com/ZerBea/hcxdumptool' } }, 'hcxpcapngtool': { 'required': False, 'description': 'PMKID hash extractor', 'install': { 'apt': 'sudo apt install hcxtools', 'brew': 'brew install hcxtools', 'manual': 'https://github.com/ZerBea/hcxtools' } } } }, 'bluetooth': { 'name': 'Bluetooth Scanning', 'tools': { 'hcitool': { 'required': False, 'description': 'Bluetooth HCI tool (legacy)', 'install': { 'apt': 'sudo apt install bluez', 'brew': 'Not available on macOS (use native)', 'manual': 'http://www.bluez.org' } }, 'bluetoothctl': { 'required': True, 'description': 'Modern Bluetooth controller', 'install': { 'apt': 'sudo apt install bluez', 'brew': 'Not available on macOS (use native)', 'manual': 'http://www.bluez.org' } }, 'hciconfig': { 'required': False, 'description': 'Bluetooth adapter configuration', 'install': { 'apt': 'sudo apt install bluez', 'brew': 'Not available on macOS', 'manual': 'http://www.bluez.org' } } } }, 'aircraft': { 'name': 'Aircraft Tracking (ADS-B)', 'tools': { 'dump1090': { 'required': False, 'description': 'Mode S / ADS-B decoder (preferred)', 'install': { 'apt': 'sudo apt install dump1090-mutability (or build dump1090-fa from source)', 'brew': 'brew install dump1090-mutability', 'manual': 'https://github.com/flightaware/dump1090' }, 'alternatives': ['dump1090-mutability', 'dump1090-fa'] }, 'rtl_adsb': { 'required': False, 'description': 'Simple ADS-B decoder', 'install': { 'apt': 'sudo apt install rtl-sdr', 'brew': 'brew install librtlsdr', 'manual': 'https://osmocom.org/projects/rtl-sdr/wiki' } } } }, 'acars': { 'name': 'Aircraft Messaging (ACARS)', 'tools': { 'acarsdec': { 'required': True, 'description': 'ACARS VHF decoder', 'install': { 'apt': 'Run ./setup.sh (builds from source)', 'brew': 'Run ./setup.sh (builds from source)', 'manual': 'https://github.com/TLeconte/acarsdec' } } } }, 'ais': { 'name': 'Vessel Tracking (AIS)', 'tools': { 'AIS-catcher': { 'required': True, 'description': 'AIS receiver and decoder', 'install': { 'apt': 'Download .deb from https://github.com/jvde-github/AIS-catcher/releases', 'brew': 'brew install aiscatcher', 'manual': 'https://github.com/jvde-github/AIS-catcher/releases' } } } }, 'aprs': { 'name': 'APRS Tracking', 'tools': { 'direwolf': { 'required': False, 'description': 'APRS/packet radio decoder (preferred)', 'install': { 'apt': 'sudo apt install direwolf', 'brew': 'brew install direwolf', 'manual': 'https://github.com/wb2osz/direwolf' } }, 'multimon-ng': { 'required': False, 'description': 'Alternative AFSK1200 decoder', 'install': { 'apt': 'sudo apt install multimon-ng', 'brew': 'brew install multimon-ng', 'manual': 'https://github.com/EliasOenal/multimon-ng' } } } }, 'satellite': { 'name': 'Satellite Tracking', 'tools': { 'skyfield': { 'required': True, 'description': 'Python orbital mechanics library', 'install': { 'pip': 'pip install skyfield', 'manual': 'https://rhodesmill.org/skyfield/' }, 'python_module': True } } }, 'sdr_hardware': { 'name': 'SDR Hardware Support', 'tools': { 'SoapySDRUtil': { 'required': False, 'description': 'Universal SDR abstraction (required for LimeSDR, HackRF)', 'install': { 'apt': 'sudo apt install soapysdr-tools', 'brew': 'brew install soapysdr', 'manual': 'https://github.com/pothosware/SoapySDR' } }, 'rx_fm': { 'required': False, 'description': 'SoapySDR FM receiver (for non-RTL hardware)', 'install': { 'manual': 'Part of SoapySDR utilities or build from source' } }, 'LimeUtil': { 'required': False, 'description': 'LimeSDR native utilities', 'install': { 'apt': 'sudo apt install limesuite', 'brew': 'brew install limesuite', 'manual': 'https://github.com/myriadrf/LimeSuite' } }, 'SoapyLMS7': { 'required': False, 'description': 'SoapySDR plugin for LimeSDR', 'soapy_factory': 'lime', 'install': { 'apt': 'sudo apt install soapysdr-module-lms7', 'brew': 'brew install soapylms7', 'manual': 'https://github.com/myriadrf/LimeSuite' } }, 'hackrf_info': { 'required': False, 'description': 'HackRF native utilities', 'install': { 'apt': 'sudo apt install hackrf', 'brew': 'brew install hackrf', 'manual': 'https://github.com/greatscottgadgets/hackrf' } }, 'SoapyHackRF': { 'required': False, 'description': 'SoapySDR plugin for HackRF', 'soapy_factory': 'hackrf', 'install': { 'apt': 'sudo apt install soapysdr-module-hackrf', 'brew': 'brew install soapyhackrf', 'manual': 'https://github.com/pothosware/SoapyHackRF' } }, 'readsb': { 'required': False, 'description': 'ADS-B decoder with SoapySDR support', 'install': { 'apt': 'Build from source with SoapySDR support', 'brew': 'Build from source with SoapySDR support', 'manual': 'https://github.com/wiedehopf/readsb' } } } }, 'tscm': { 'name': 'TSCM Counter-Surveillance', 'tools': { 'rtl_power': { 'required': False, 'description': 'Wideband spectrum sweep for RF analysis', 'install': { 'apt': 'sudo apt install rtl-sdr', 'brew': 'brew install librtlsdr', 'manual': 'https://osmocom.org/projects/rtl-sdr/wiki' } }, 'rtl_fm': { 'required': True, 'description': 'RF signal demodulation', 'install': { 'apt': 'sudo apt install rtl-sdr', 'brew': 'brew install librtlsdr', 'manual': 'https://osmocom.org/projects/rtl-sdr/wiki' } }, 'rtl_433': { 'required': False, 'description': 'ISM band device decoding', 'install': { 'apt': 'sudo apt install rtl-433', 'brew': 'brew install rtl_433', 'manual': 'https://github.com/merbanan/rtl_433' } }, 'airmon-ng': { 'required': False, 'description': 'WiFi monitor mode for network scanning', 'install': { 'apt': 'sudo apt install aircrack-ng', 'brew': 'Not available on macOS', 'manual': 'https://aircrack-ng.org' } }, 'bluetoothctl': { 'required': False, 'description': 'Bluetooth device scanning', 'install': { 'apt': 'sudo apt install bluez', 'brew': 'Not available on macOS (use native)', 'manual': 'http://www.bluez.org' } } } } } def check_all_dependencies() -> dict[str, dict[str, Any]]: """Check all tool dependencies and return status.""" results: dict[str, dict[str, Any]] = {} for mode, config in TOOL_DEPENDENCIES.items(): mode_result = { 'name': config['name'], 'tools': {}, 'ready': True, 'missing_required': [] } for tool, tool_config in config['tools'].items(): # Check if it's a Python module if tool_config.get('python_module'): try: __import__(tool) installed = True except Exception as e: logger.debug(f"Failed to import {tool}: {type(e).__name__}: {e}") installed = False # Check using SoapySDRUtil if specified elif tool_config.get('soapy_factory'): installed = check_soapy_factory(tool_config['soapy_factory']) else: # Check for alternatives alternatives = tool_config.get('alternatives', []) installed = check_tool(tool) or any(check_tool(alt) for alt in alternatives) mode_result['tools'][tool] = { 'installed': installed, 'required': tool_config['required'], 'description': tool_config['description'], 'install': tool_config['install'] } if tool_config['required'] and not installed: mode_result['ready'] = False mode_result['missing_required'].append(tool) results[mode] = mode_result return results