Skip to content

Local Area Network discovery tool with a modern Terminal User Interface (TUI). Discover, explore, and understand your LAN in an intuitive way.

License

Notifications You must be signed in to change notification settings

Logisek/WhoIsOutThere

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WhoIsOutThere

Local Area Network discovery tool with a modern Terminal User Interface (TUI). Discover, explore, and understand your LAN in an intuitive way.

WhoIsOutThere performs unprivileged, concurrent scans using mDNS, SSDP, and ARP discovery. It sweeps the local subnet to trigger ARP resolution, then reads the ARP cache to identify devices on your network. All discovered devices are enhanced with OUI lookups to display manufacturers when available.

Features

  • Modern TUI — Navigate and explore discovered devices with Textual
  • Fast & Concurrent — Leverages multiple discovery methods simultaneously
  • No Elevated Privileges Required — Runs entirely in user-space
  • Device Enrichment — MAC vendor lookup (built-in + online) and device type classification
  • Daemon Mode with HTTP API — Run in the background and integrate with other tools
  • Theming & Configuration — Personalize the look and behavior via YAML configuration
  • Integrated Port Scanner — Async TCP port scanning with configurable concurrency
  • Smart Classification — Automatically identifies device types (router, printer, phone, etc.)
  • Local Host Exclusion — Automatically excludes the host machine from scan results

Platform Support

  • Windows (primary, tested)
  • Linux (supported via /proc/net/arp)
  • macOS (planned)

Requirements

  • Python 3.10+
  • PyYAML (for configuration)
  • textual (for TUI)
  • aiohttp (for daemon HTTP API)
  • zeroconf (for mDNS scanning)

Installation

pip install -r requirements.txt

Or install with optional dependencies:

# Core only (scan command)
pip install PyYAML zeroconf

# With TUI support
pip install PyYAML zeroconf textual

# With daemon HTTP API
pip install PyYAML zeroconf aiohttp

# With clipboard support (for copying IPs)
pip install PyYAML zeroconf textual pyperclip

# All features
pip install PyYAML zeroconf textual aiohttp pyperclip

Usage

Interactive TUI

Run the TUI for interactive discovery (default command):

python -m whoisoutthere
# or explicitly:
python -m whoisoutthere tui

One-Time Scan

Run a single scan and print results to stdout:

python -m whoisoutthere scan

Auto-Select Mode (Recommended):

The simplest way to run a scan is with --auto, which automatically selects the best available scanners based on your system's capabilities:

# Automatically use the best scanners for your system
python -m whoisoutthere scan --auto

# Auto with port scanning
python -m whoisoutthere scan --auto --ports

# Auto with passive monitoring (if admin + libpcap available)
python -m whoisoutthere scan --auto --passive

When running with admin privileges and scapy/libpcap installed, --auto will use:

  • ActiveArpScanner — Raw ARP requests for reliable discovery
  • IcmpPingScanner — Raw ICMP for fast ping sweeps (10-100x faster)
  • MdnsScanner — mDNS/Bonjour service discovery
  • SsdpScanner — UPnP/SSDP discovery

Without admin privileges, it falls back to:

  • ArpScanner — OS ARP cache reading
  • PingScanner — System ping command
  • MdnsScanner & SsdpScanner — Service discovery

Include port scanning on discovered devices:

python -m whoisoutthere scan --ports
# or short form:
python -m whoisoutthere scan -p

Use privileged scanners (requires admin + scapy/libpcap):

# Windows: Run as Administrator
# Linux: Run with sudo
python -m whoisoutthere scan --privileged

The --privileged flag enables advanced scanners for more reliable device discovery:

  • ActiveArpScanner — Sends raw ARP requests instead of reading the ARP cache
  • IcmpPingScanner — Sends raw ICMP echo requests (10-100x faster than system ping)

Enable passive ARP monitoring to discover devices stealthily (requires admin + scapy/libpcap):

# Windows: Run as Administrator
# Linux: Run with sudo

# Monitor ARP traffic for 30 seconds (default)
python -m whoisoutthere scan --passive

# Monitor for a custom duration
python -m whoisoutthere scan --passive --passive-time 60

# Combine with active scanning for comprehensive discovery
python -m whoisoutthere scan --privileged --passive --passive-time 45

The --passive flag enables PassiveArpMonitor which sniffs ARP traffic without sending any packets. This is useful for:

  • Stealth discovery — Doesn't alert network intrusion detection systems
  • Intermittent devices — Catches devices that communicate periodically
  • Mobile devices — Detects phones/tablets as they wake up and communicate

Enable DHCP sniffing to catch devices as they join the network (requires admin + scapy/libpcap):

# Windows: Run as Administrator
# Linux: Run with sudo

# Monitor DHCP traffic for 60 seconds (default)
python -m whoisoutthere scan --dhcp

# Monitor for a custom duration
python -m whoisoutthere scan --dhcp --dhcp-time 120

# Combine with active and passive scanning for comprehensive discovery
python -m whoisoutthere scan --privileged --passive --dhcp

The --dhcp flag enables DhcpSniffer which monitors DHCP Discover/Request/ACK packets. This is especially useful for:

  • New devices — Catches devices the moment they obtain an IP address
  • Hostname extraction — Gets device hostname from DHCP options (Option 12)
  • Device identification — Extracts vendor class identifier for better classification
  • Dynamic networks — Ideal for networks with frequently joining/leaving devices

Enable TCP SYN (half-open) scanning for faster, stealthier port scans (requires admin + scapy/libpcap):

# Windows: Run as Administrator
# Linux: Run with sudo

# Use SYN scanning instead of TCP connect
python -m whoisoutthere scan --ports --syn

# Combine with privileged discovery for complete privileged scanning
python -m whoisoutthere scan --privileged --ports --syn

The --syn flag enables SynScanner which sends TCP SYN packets and analyzes responses without completing the three-way handshake. This provides:

  • Faster scanning — No full TCP connection overhead
  • Stealthier operation — Less likely to trigger application-level logging
  • Better results on firewalled hosts — Works even when hosts drop after SYN+ACK
  • Host detection — Discovers hosts even when all ports are closed (via RST responses)

Enable IPv6 Neighbor Discovery Protocol (NDP) scanning to find IPv6-only and dual-stack devices (requires admin + scapy/libpcap):

# Windows: Run as Administrator
# Linux: Run with sudo

# Scan for IPv6 devices on the local network
python -m whoisoutthere scan --ipv6

# Combine with other privileged scanners
python -m whoisoutthere scan --privileged --ipv6

# Full privileged scan including IPv6
python -m whoisoutthere scan --privileged --passive --ipv6 --ports --syn

The --ipv6 flag enables Ndp6Scanner which uses ICMPv6 Neighbor Discovery Protocol to find IPv6 hosts:

  • Multicast ping — Sends ICMPv6 Echo Request to ff02::1 (all-nodes multicast)
  • Router discovery — Sends Router Solicitation to ff02::2 to discover IPv6 routers
  • Dual-stack devices — Finds devices that have IPv6 addresses even if they also have IPv4
  • IPv6-only devices — Discovers devices that may not respond to IPv4 discovery methods

Enable LLDP/CDP sniffing to discover network infrastructure devices like switches and routers (requires admin + scapy/libpcap):

# Windows: Run as Administrator
# Linux: Run with sudo

# Monitor LLDP/CDP traffic for 60 seconds (default)
python -m whoisoutthere scan --lldp

# Monitor for a custom duration
python -m whoisoutthere scan --lldp --lldp-time 120

# Combine with other privileged scanners
python -m whoisoutthere scan --privileged --lldp

The --lldp flag enables LldpScanner which monitors Link Layer Discovery Protocol (LLDP) and Cisco Discovery Protocol (CDP) announcements. This is especially useful for:

  • Network infrastructure — Discovers switches, routers, and managed network devices
  • Port identification — Shows which switch port your device is connected to
  • Device details — Extracts model, software version, and capabilities
  • Management IPs — Gets management IP addresses for network devices
  • VLAN info — Captures native VLAN and VTP domain (CDP)

Enable NetBIOS/LLMNR sniffing to passively capture Windows hostnames (requires admin + scapy/libpcap):

# Windows: Run as Administrator
# Linux: Run with sudo

# Monitor NetBIOS/LLMNR traffic for 60 seconds (default)
python -m whoisoutthere scan --nbns

# Monitor for a custom duration
python -m whoisoutthere scan --nbns --nbns-time 120

# Combine with other privileged scanners
python -m whoisoutthere scan --privileged --nbns

The --nbns flag enables NbnsLlmnrSniffer which passively captures NetBIOS Name Service (UDP port 137) and Link-Local Multicast Name Resolution (LLMNR, UDP port 5355) traffic. This is especially useful for:

  • Windows hostnames — Captures Windows device hostnames without active queries
  • Service discovery — Extracts NetBIOS service types (Workstation, File Server, Domain Controller, etc.)
  • Name conflicts — Detects NetBIOS name registration and conflicts
  • Modern Windows — LLMNR captures work with Windows Vista and later when DNS fails
  • Passive operation — Works without sending any packets, completely stealthy

Comprehensive Privileged Scanning

For the most thorough network discovery, combine all privileged options:

# Windows: Run as Administrator
# Linux: Run with sudo

# Full privileged scan with all features (takes ~60s due to passive monitoring)
python -m whoisoutthere scan --privileged --passive --dhcp --ipv6 --lldp --nbns --ports --syn

# Faster privileged scan without passive monitoring (~10s)
python -m whoisoutthere scan --privileged --ports --syn

# Privileged discovery with standard TCP connect port scan
python -m whoisoutthere scan --privileged --ports

Note: The --privileged flag enables faster active scanning (ActiveArpScanner, IcmpPingScanner), --syn enables faster port scanning (SynScanner), --ipv6 enables IPv6 neighbor discovery (Ndp6Scanner), --lldp discovers network infrastructure (LldpScanner), and --nbns captures Windows hostnames (NbnsLlmnrSniffer). The passive options (--passive, --dhcp, --lldp, --nbns) require additional time but catch devices and infrastructure that active scanning might miss.

Daemon Mode

Run as a daemon with HTTP API:

python -m whoisoutthere daemon
python -m whoisoutthere daemon --port 8080
python -m whoisoutthere daemon --host 0.0.0.0 --port 8765

Check Capabilities

Check what scanner capabilities are available on your system:

python -m whoisoutthere capabilities

This shows:

  • System capabilities (admin privileges, libpcap availability)
  • Which scanners are available based on current privileges
  • Instructions for enabling advanced scanning features

Example output:

Scanner Capabilities
============================================================

System Capabilities:
----------------------------------------
  [+] ADMIN           available
  [+] LIBPCAP         available
  [+] RAW_SOCKETS     available

Scanner Availability:
----------------------------------------

  Basic Scanners (always available):
    [+] ArpScanner              OS ARP cache reading
    [+] PingScanner             System ping command
    [+] MdnsScanner             mDNS/Bonjour discovery
    [+] SsdpScanner             UPnP/SSDP discovery

  Privileged Scanners (require admin + libpcap):
    [+] ActiveArpScanner        Raw ARP requests (faster, more reliable)
    [+] IcmpPingScanner         Raw ICMP ping (10-100x faster)
    [+] SynScanner              TCP SYN half-open scanning
    ...

Quick Start:
----------------------------------------
  Privileged scanning is available!
  Use: whoisoutthere scan --auto

Version

python -m whoisoutthere version

Key Bindings (TUI)

Key Action
j Move cursor down
k Move cursor up
g Go to top of list
G Go to bottom of list
y Copy IP of selected device
Enter Show device details
p Port scan (in detail view)
Ctrl+T Toggle theme selector
Ctrl+C Quit application
Escape Go back / dismiss modal
q Go back (in detail view)
r Manual refresh

Environment Variables

Variable Description
WHOISOUTTHERE_CONFIG Path to the configuration file to override the default location
WHOISOUTTHERE_LOG Set the log level (e.g., debug, info, warn, error)

Configuration

WhoIsOutThere can be configured via a YAML configuration file. By default, it looks for the configuration file in the following order:

  1. Path specified via --config command-line argument
  2. Path specified in the WHOISOUTTHERE_CONFIG environment variable
  3. Platform-specific default paths:
    • Windows: %APPDATA%\WhoIsOutThere\config.yaml
    • Linux: $XDG_CONFIG_HOME/WhoIsOutThere/config.yaml or ~/.config/WhoIsOutThere/config.yaml

Example Configuration

# Discovery settings
discovery:
  timeout_seconds: 10.0          # How long to wait for discovery responses
  scan_interval_seconds: 30.0    # How often TUI rescans the network
  auto_select: false             # Automatically select best scanners based on capabilities
  mdns_enabled: true
  ssdp_enabled: true
  arp_enabled: true
  arp_sweep_rate: 1000           # UDP packets/sec for ARP sweep (0 = unlimited)
  ping_enabled: true             # Enable ICMP ping sweep
  ping_concurrency: 64           # Concurrent ping processes
  vendor_lookup_online: true     # Lookup unknown MAC vendors online
  privileged: false              # Use privileged scanners (requires admin + libpcap)
  passive_arp: false             # Enable passive ARP monitoring (requires admin + libpcap)
  passive_arp_time: 30.0         # Duration for passive ARP monitoring in seconds
  dhcp_sniff: false              # Enable DHCP traffic sniffing (requires admin + libpcap)
  dhcp_sniff_time: 60.0          # Duration for DHCP sniffing in seconds
  syn_scan: false                # Use TCP SYN scanning instead of TCP connect (requires admin + libpcap)
  ipv6_ndp: false                # Enable IPv6 Neighbor Discovery Protocol scanning (requires admin + libpcap)
  lldp_sniff: false              # Enable LLDP/CDP sniffing for network infrastructure (requires admin + libpcap)
  lldp_sniff_time: 60.0          # Duration for LLDP/CDP sniffing in seconds
  nbns_llmnr_sniff: false        # Enable NetBIOS/LLMNR sniffing for Windows hostname discovery (requires admin + libpcap)
  nbns_llmnr_sniff_time: 60.0    # Duration for NetBIOS/LLMNR sniffing in seconds
  # Uncomment to scan a specific network interface
  # interface: Wi-Fi

# Port scanner settings
port_scan:
  enabled: false          # Disabled by default; use --ports flag or enable here
  ports: [22, 80, 443, 445, 3389, 8080]  # Ports to scan
  timeout_seconds: 0.5    # Connection timeout per port
  concurrency: 100        # Max simultaneous TCP connections

# Daemon settings
daemon:
  host: 127.0.0.1
  port: 8765
  scan_interval_seconds: 30.0
  scan_timeout_seconds: 5.0

# Logging settings
logging:
  level: info
  to_stdout: true
  file: whoisoutthere.log

# UI settings
ui:
  theme: default
  show_splash: true

Capability Detection

WhoIsOutThere includes a capability detection module that checks for advanced scanning prerequisites. This enables the tool to automatically select the best available scanners based on system privileges and installed dependencies.

Checking Capabilities

from whoisoutthere.discovery.scanners import (
    check_admin,
    check_libpcap,
    get_available_scanners,
    get_capabilities,
)

# Check individual capabilities
print(f"Admin privileges: {check_admin()}")
print(f"Libpcap available: {check_libpcap()}")

# Get all available scanners based on current privileges
scanners = get_available_scanners()
print(f"Available scanners: {scanners}")

# Get detailed capability status
for cap, status in get_capabilities().items():
    if status.available:
        print(f"{cap.name}: Available")
    else:
        print(f"{cap.name}: {status.reason}")

Capabilities

Capability Description How to Enable
ADMIN Administrator/root privileges Run as Administrator (Windows) or with sudo (Linux)
LIBPCAP Packet capture library Install scapy + Npcap (Windows) or libpcap-dev (Linux)
RAW_SOCKETS Raw socket access Typically requires admin privileges

Available Scanners by Privilege Level

Unprivileged (always available):

  • ArpScanner (OS cache reading)
  • HostnameScanner (DNS/NetBIOS)
  • MdnsScanner (multicast)
  • PingScanner (system ping)
  • PortScanner (TCP connect)
  • SsdpScanner (multicast)

Privileged (requires admin + libpcap):

  • ActiveArpScanner (raw ARP requests)
  • PassiveArpMonitor (ARP sniffing)
  • DhcpSniffer (DHCP monitoring)
  • IcmpPingScanner (raw ICMP)
  • SynScanner (TCP SYN scanning)
  • Ndp6Scanner (IPv6 neighbor discovery)
  • LldpScanner (CDP/LLDP sniffing)
  • NbnsLlmnrSniffer (NetBIOS/LLMNR)

Installing Libpcap Dependencies

Windows:

  1. Install Npcap with "WinPcap API-compatible Mode" checked
  2. Install scapy: pip install scapy

Linux:

# Debian/Ubuntu
sudo apt install libpcap-dev
pip install scapy

# RHEL/CentOS/Fedora
sudo yum install libpcap-devel
pip install scapy

Performance Tuning

WhoIsOutThere provides several configurable parameters to control network scan aggressiveness. These settings help balance discovery speed against network stability.

Configuration Reference

Config Key Default Description
discovery.timeout_seconds 10.0 How long to wait for discovery responses
discovery.scan_interval_seconds 30.0 How often the TUI rescans the network
discovery.ping_enabled true Enable ICMP ping sweep for finding devices
discovery.ping_concurrency 64 Maximum concurrent ping processes
discovery.arp_enabled true Enable ARP-based discovery
discovery.arp_sweep_rate 1000 UDP packets per second for ARP sweep (0 = unlimited)
discovery.privileged false Use privileged scanners when available (ActiveArpScanner)
discovery.passive_arp false Enable passive ARP monitoring (requires admin + libpcap)
discovery.passive_arp_time 30.0 Duration for passive ARP monitoring in seconds
discovery.dhcp_sniff false Enable DHCP traffic sniffing (requires admin + libpcap)
discovery.dhcp_sniff_time 60.0 Duration for DHCP sniffing in seconds
discovery.syn_scan false Use TCP SYN scanning instead of TCP connect (requires admin + libpcap)
discovery.ipv6_ndp false Enable IPv6 Neighbor Discovery Protocol scanning (requires admin + libpcap)
discovery.lldp_sniff false Enable LLDP/CDP sniffing for network infrastructure (requires admin + libpcap)
discovery.lldp_sniff_time 60.0 Duration for LLDP/CDP sniffing in seconds
discovery.nbns_llmnr_sniff false Enable NetBIOS/LLMNR sniffing for Windows hostname discovery (requires admin + libpcap)
discovery.nbns_llmnr_sniff_time 60.0 Duration for NetBIOS/LLMNR sniffing in seconds
port_scan.enabled false Port scanning disabled by default; use --ports flag
port_scan.concurrency 100 Maximum simultaneous TCP connections
port_scan.timeout_seconds 0.5 Connection timeout per port

Tuning for Different Network Conditions

Default (works for most networks):

# These are the default values - no config needed
discovery:
  ping_concurrency: 64
  arp_sweep_rate: 1000
port_scan:
  concurrency: 100

Aggressive (fast, stable Gigabit network):

discovery:
  ping_concurrency: 128
  arp_sweep_rate: 2000
port_scan:
  concurrency: 200

Conservative (slower WiFi networks):

discovery:
  ping_concurrency: 32
  arp_sweep_rate: 500
port_scan:
  concurrency: 50

Minimal Network Impact:

discovery:
  scan_interval_seconds: 60.0
  ping_concurrency: 16
  arp_sweep_rate: 200
port_scan:
  concurrency: 25
  timeout_seconds: 1.0

Troubleshooting Network Issues

If you experience network connectivity issues during scans:

  1. Reduce ping_concurrency — Lower values reduce simultaneous ICMP packets
  2. Reduce arp_sweep_rate — Lower values slow down UDP packet transmission
  3. Reduce port_scan.concurrency — Lower values reduce simultaneous TCP connections
  4. Increase scan_interval_seconds — Longer intervals give the network time to recover

Python API with Custom Settings

from whoisoutthere.discovery import DiscoveryEngine
from whoisoutthere.discovery.scanners import ArpScanner, PingScanner, PortScanner

# Create scanners with custom concurrency settings
scanners = [
    ArpScanner(
        interface="Wi-Fi",
        sweep_rate=300,  # Slower UDP sweep
    ),
    PingScanner(
        interface="Wi-Fi",
        concurrency=16,  # Fewer concurrent pings
    ),
]

engine = DiscoveryEngine(scanners)
devices = engine.scan_sync(timeout=10)

# Port scan with reduced concurrency
scanner = PortScanner(
    targets=[d.ip for d in devices if d.ip],
    concurrency=25,  # Fewer simultaneous connections
    timeout=1.0,     # Longer timeout per port
)

Auto-Select Best Scanners (Recommended)

The simplest way to use the discovery engine is with automatic scanner selection:

from whoisoutthere.discovery import DiscoveryEngine

# Automatically select the best scanners based on system capabilities
engine = DiscoveryEngine.create_with_best_scanners()
devices = engine.scan_sync(timeout=10)

for device in devices:
    print(f"{device.ip} - {device.mac} - {device.vendor or 'unknown'}")

With more options:

from whoisoutthere.discovery import DiscoveryEngine

# Create engine with options
engine = DiscoveryEngine.create_with_best_scanners(
    target_network="192.168.1.0/24",  # Specific network (or None for auto-detect)
    prefer_privileged=True,            # Use advanced scanners if available
    include_passive=True,              # Include passive monitoring scanners
    passive_timeout=60.0,              # Duration for passive scanners
)

# Check what capabilities are available
caps = DiscoveryEngine.get_scanner_capabilities_info()
print(f"Admin: {caps['admin']}")
print(f"Libpcap: {caps['libpcap']}")
print(f"Privileged scanners: {caps['privileged_scanners']}")

# Get just the scanners without creating an engine
scanners = DiscoveryEngine.get_best_scanners(prefer_privileged=True)
print(f"Selected scanners: {[s.Name() for s in scanners]}")

Using Privileged Scanners (Admin + Scapy)

from whoisoutthere.discovery import DiscoveryEngine
from whoisoutthere.discovery.scanners import can_use_privileged_scanners

if can_use_privileged_scanners():
    # Use raw socket scanners for faster, more reliable discovery
    from whoisoutthere.discovery.scanners.arp_active import ActiveArpScanner
    from whoisoutthere.discovery.scanners.icmp import IcmpPingScanner
    
    scanners = [
        ActiveArpScanner(timeout=3.0),     # Raw ARP requests
        IcmpPingScanner(timeout=3.0),      # Raw ICMP (10-100x faster)
    ]
else:
    # Fall back to unprivileged scanners
    from whoisoutthere.discovery.scanners import ArpScanner, PingScanner
    
    scanners = [
        ArpScanner(),         # Uses OS ARP cache
        PingScanner(),        # Uses system ping command
    ]

engine = DiscoveryEngine(scanners)
devices = engine.scan_sync(timeout=10)

State and Logs

State and log files are stored in platform-specific directories:

Platform State Directory Log File
Windows %LOCALAPPDATA%\WhoIsOutThere %LOCALAPPDATA%\WhoIsOutThere\whoisoutthere.log
Linux $XDG_STATE_HOME/WhoIsOutThere or ~/.local/state/WhoIsOutThere Same directory

Set logging.file in config to override the log file location.

Daemon Mode HTTP API

When running WhoIsOutThere in daemon mode, it exposes a REST API with the following endpoints:

Method Endpoint Description
GET /devices Get list of all discovered devices
GET /device/{ip} Get details of a specific device
POST /device/{ip}/scan Run port scan on a specific device
POST /scan/ports Run port scan on all discovered devices
GET /health Health check

Example API Usage

# List all devices
curl http://localhost:8765/devices

# Get a specific device
curl http://localhost:8765/device/192.168.1.100

# Port scan a specific device
curl -X POST http://localhost:8765/device/192.168.1.100/scan

# Port scan with custom ports
curl -X POST http://localhost:8765/device/192.168.1.100/scan \
  -H "Content-Type: application/json" \
  -d '{"ports": [22, 80, 443, 8080], "timeout": 1.0}'

# Port scan all discovered devices
curl -X POST http://localhost:8765/scan/ports

# Health check
curl http://localhost:8765/health

Device JSON Response

{
  "ip": "192.168.1.100",
  "mac": "aa:bb:cc:dd:ee:ff",
  "hostname": "my-device.local",
  "vendor": "Apple, Inc.",
  "interface": "Wi-Fi",
  "first_seen": "2025-01-24T10:30:00Z",
  "last_seen": "2025-01-24T12:45:00Z",
  "sources": ["arp", "mdns", "portscan"],
  "services": ["_http._tcp.local."],
  "ips": ["192.168.1.100"],
  "ports": [22, 80, 443],
  "metadata": {}
}

Port Scan Response

{
  "ip": "192.168.1.100",
  "ports": [22, 80, 443],
  "count": 3
}

Bulk Port Scan Response

{
  "scanned": 15,
  "with_open_ports": 3,
  "results": {
    "192.168.1.1": [53, 80],
    "192.168.1.100": [22, 80, 443],
    "192.168.1.150": [22]
  }
}

Themes

Theme can be configured via the configuration file or selected at runtime via Ctrl+T.

Available Themes

Theme Description
default Dark theme with purple accents
dracula Popular dark color scheme
nord Arctic, bluish colors
gruvbox-dark Retro groove colors
tokyonight Tokyo night palette
catppuccin-mocha Soothing pastel theme
rose-pine Soho vibes
onedark Atom One Dark inspired
monokai Classic Monokai
cyberpunk Neon cyber colors
matrix Green terminal style
high-contrast High visibility
solarized-dark Solarized dark

Theme Configuration

ui:
  theme: dracula
  show_splash: true

Quick Start (Python API)

import asyncio
from whoisoutthere.discovery import DiscoveryEngine, ArpScanner, MdnsScanner, SsdpScanner, PortScanner

# Create scanners
scanners = [
    ArpScanner(interface="Wi-Fi"),  # Filter by interface name
    MdnsScanner(),
    SsdpScanner(),
]

# Run discovery (local host is automatically excluded)
engine = DiscoveryEngine(scanners)
devices = asyncio.run(engine.scan(timeout=5))

# To include local host in results, set exclude_local_host=False
# engine = DiscoveryEngine(scanners, exclude_local_host=False)

for device in devices:
    print(f"{device.ip} - {device.mac} - {device.hostname or 'unknown'}")

Port Scanning

import asyncio
from whoisoutthere.discovery.scanners import PortScanner

async def scan_ports():
    # Scan specific hosts for open ports
    scanner = PortScanner(
        targets=["192.168.1.1", "192.168.1.100"],
        ports=[22, 80, 443, 445, 3389, 8080],
        timeout=0.5,
        concurrency=50,  # Adjust based on network conditions
    )
    
    results = []
    async def collect(device):
        if device.ports:
            results.append(device)
            print(f"{device.ip}: {sorted(device.ports)}")
    
    await scanner.scan(collect)
    return results

asyncio.run(scan_ports())

Combined Discovery + Port Scan

import asyncio
from whoisoutthere.discovery import DiscoveryEngine, ArpScanner, PortScanner

async def full_scan():
    # Phase 1: Discover devices
    engine = DiscoveryEngine([ArpScanner()])
    devices = await engine.scan(timeout=5)
    
    # Phase 2: Port scan discovered devices
    target_ips = [d.ip for d in devices if d.ip]
    scanner = PortScanner(targets=target_ips, ports=[22, 80, 443])
    
    port_results = {}
    async def collect(device):
        if device.ip and device.ports:
            port_results[device.ip] = sorted(device.ports)
    
    await scanner.scan(collect)
    
    # Print results
    for device in devices:
        ports = port_results.get(device.ip, [])
        print(f"{device.ip} ({device.mac}): ports {ports}")

asyncio.run(full_scan())

Port Scanning

WhoIsOutThere includes a high-performance async TCP port scanner that integrates with all components.

Features

  • Async TCP Connect — Uses asyncio.open_connection for non-blocking scans
  • Configurable Concurrency — Control simultaneous connections (default: 50)
  • Timeout Control — Per-port connection timeout (default: 0.5s)
  • Default Ports — Common service ports: 21, 22, 23, 53, 80, 443, 139, 445, 3389, 5900, 8080, 8443

Enabling Port Scanning

Port scanning is disabled by default. You can enable it via CLI flag or configuration:

CLI Flag (recommended for one-time scans):

python -m whoisoutthere scan --ports

Configuration File (for persistent setting):

port_scan:
  enabled: true
  ports: [22, 53, 80, 443, 445, 3389, 8080]
  timeout_seconds: 0.5
  concurrency: 100  # Max simultaneous TCP connections

Usage

CLI Scan with Ports:

python -m whoisoutthere scan --ports
# or with custom config:
python -m whoisoutthere --config config.yaml scan

TUI: Open device details (Enter) and press p to scan ports on that device.

Daemon: Use the REST API endpoints /device/{ip}/scan or /scan/ports.

Example Output

IP               MAC                Hostname             Vendor               [Sources]
--------------------------------------------------------------------------------
192.168.1.1      a0:95:7f:ab:cb:11                                            [arp]
192.168.1.100    60:83:e7:d2:bb:18  mydevice                                  [arp, mdns]
--------------------------------------------------------------------------------
Found 2 device(s)

Scanning ports...

IP               Open Ports
--------------------------------------------------
192.168.1.1      53
192.168.1.100    22, 80, 443

Vendor Lookup

WhoIsOutThere identifies device manufacturers using MAC address OUI (Organizationally Unique Identifier) lookup.

Lookup Sources

  1. Built-in Database — Contains 2000+ common vendor OUI prefixes (Apple, Samsung, Intel, etc.)
  2. External OUI File — Optional oui.txt file in the data directory (IEEE format)
  3. Online API — Falls back to macvendors.com API for unknown MACs (results cached for 30 days)

Configuration

Online lookup is enabled by default. To disable it:

discovery:
  vendor_lookup_online: false

Cache Location

Vendor lookup results are cached in:

  • Windows: %LOCALAPPDATA%\WhoIsOutThere\vendor-cache.json
  • Linux: ~/.local/share/WhoIsOutThere/vendor-cache.json

Device Classification

Devices are automatically classified based on multiple signals:

Signal Examples
Open Ports Port 631 → Printer, Port 3389 → Computer, Port 32400 → Plex
MAC Vendor Synology → NAS, Nintendo → Game Console, Philips Hue → Smart Home
Services _printer._tcp → Printer, _googlecast._tcp → Streaming Device
Hostname Contains "router" → Router, "nas" → NAS, "iphone" → Smartphone

Device Types

Type Description
Computer Desktop, laptop, workstation
Server Web server, mail server, database
Router Gateway, firewall, network switch
Printer Network printers
Smartphone Mobile phones, tablets
Smart TV Television with network features
NAS Network attached storage
Smart Home Hue, Nest, Ring, etc.
Game Console PlayStation, Xbox, Nintendo
Virtual Machine VMware, VirtualBox, Hyper-V

Local Host Exclusion

By default, WhoIsOutThere automatically excludes the machine running the scan from the results. This prevents the host from appearing as a discovered device and provides cleaner scan output.

How It Works

The discovery engine detects all IP addresses assigned to the local machine using multiple methods:

  • Loopback addresses (127.0.0.1, ::1)
  • Primary IP via socket connection
  • All IPs from hostname resolution
  • Platform-specific enumeration (ipconfig on Windows, ip addr on Linux)

Any device matching a local IP address is automatically filtered out during the scan.

Disabling Local Host Exclusion

If you need to include the local host in scan results (e.g., for testing), you can disable this behavior:

from whoisoutthere.discovery import DiscoveryEngine, ArpScanner

# Include local host in results
engine = DiscoveryEngine([ArpScanner()], exclude_local_host=False)
devices = engine.scan_sync(timeout=5)

Programmatic Access to Local IPs

You can also retrieve the local IP addresses programmatically:

from whoisoutthere.discovery import get_local_ip_addresses

local_ips = get_local_ip_addresses()
print(f"Local IPs: {local_ips}")
# Output: Local IPs: {'127.0.0.1', '::1', '192.168.1.50', 'fe80::1234:5678:abcd:ef01'}

Windows-Specific Notes

PowerShell Execution

WhoIsOutThere works with both PowerShell 5.1 and PowerShell 7+:

# PowerShell 5.1 or 7+
python -m whoisoutthere

# Or run from source
cd C:\Dev\WhoIsOutThere
python -m whoisoutthere scan

Windows Terminal

For the best TUI experience, use Windows Terminal (available from Microsoft Store). It provides proper Unicode rendering and color support.

ARP Scanner

On Windows, the ARP scanner uses iphlpapi.dll via ctypes to read the ARP cache. This requires no elevated privileges and works with the standard Windows networking stack.

Implementation details:

  • Uses GetIpNetTable2 to retrieve the ARP cache (IPv4 and IPv6 entries)
  • Uses FreeMibTable to properly release allocated memory
  • Uses GetAdaptersAddresses for interface enumeration and filtering
  • Parses MIB_IPNET_ROW2 structures to extract IP, MAC, and interface index
  • Supports filtering by interface name (e.g., --interface "Wi-Fi")

The ARP sweeper sends UDP packets to port 9 (discard) across the local subnet to trigger ARP resolution before reading the cache. This populates the cache with devices that haven't communicated recently.

Configuring ARP Sweep Rate:

The arp_sweep_rate parameter controls how many UDP packets per second are sent during the sweep. The default of 1000 packets/second provides a good balance between speed and network stability.

discovery:
  arp_sweep_rate: 1000  # Packets/sec (0 = unlimited, reduce for slow networks)

Disclaimer

WhoIsOutThere is intended for use on networks where you have permission to perform network discovery and scanning, such as your own home network. Unauthorized scanning of networks may be illegal and unethical. Always obtain proper authorization before using this tool on any network.

Credits

  • whosthere by Ramon Vermeulen — A Local Area Network discovery tool with a modern Terminal User Interface (TUI) written in Go.

License

See LICENSE for details.

About

Local Area Network discovery tool with a modern Terminal User Interface (TUI). Discover, explore, and understand your LAN in an intuitive way.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages