diff --git a/routes/wifi.py b/routes/wifi.py index 3161368..0958660 100644 --- a/routes/wifi.py +++ b/routes/wifi.py @@ -150,6 +150,7 @@ def detect_wifi_interfaces(): def _get_interface_details(iface_name): """Get additional details about a WiFi interface (driver, chipset, MAC).""" + import os details = {'driver': '', 'chipset': '', 'mac': ''} # Get MAC address @@ -163,42 +164,82 @@ def _get_interface_details(iface_name): # Get driver name try: driver_link = f'/sys/class/net/{iface_name}/device/driver' - import os if os.path.islink(driver_link): driver_path = os.readlink(driver_link) details['driver'] = os.path.basename(driver_path) except (FileNotFoundError, IOError, OSError): pass - # Get chipset info from USB or PCI + # Try airmon-ng first for chipset info (most reliable for WiFi adapters) try: - # Check if USB device - device_path = f'/sys/class/net/{iface_name}/device' - import os - if os.path.exists(device_path): - # Try to get USB product name - for usb_path in [f'{device_path}/product', f'{device_path}/../product']: - try: - with open(usb_path, 'r') as f: - details['chipset'] = f.read().strip() - break - except (FileNotFoundError, IOError): - pass - - # If no USB product, try to get from uevent - if not details['chipset']: - try: - uevent_path = f'{device_path}/uevent' - with open(uevent_path, 'r') as f: - for line in f: - if line.startswith('PCI_ID=') or line.startswith('PRODUCT='): - details['chipset'] = line.split('=')[1].strip() - break - except (FileNotFoundError, IOError): - pass - except (FileNotFoundError, IOError, OSError): + result = subprocess.run(['airmon-ng'], capture_output=True, text=True, timeout=5) + for line in result.stdout.split('\n'): + # airmon-ng output format: PHY Interface Driver Chipset + parts = line.split('\t') + if len(parts) >= 4: + if parts[1].strip() == iface_name or parts[1].strip().startswith(iface_name): + if parts[2].strip(): + details['driver'] = parts[2].strip() + if parts[3].strip(): + details['chipset'] = parts[3].strip() + break + # Also try space-separated format + parts = line.split() + if len(parts) >= 4: + if parts[1] == iface_name or parts[1].startswith(iface_name): + details['driver'] = parts[2] + details['chipset'] = ' '.join(parts[3:]) + break + except (FileNotFoundError, subprocess.TimeoutExpired, subprocess.SubprocessError): pass + # Fallback: Get chipset info from USB or PCI sysfs + if not details['chipset']: + try: + device_path = f'/sys/class/net/{iface_name}/device' + if os.path.exists(device_path): + # Try to get USB product name + for usb_path in [f'{device_path}/product', f'{device_path}/../product']: + try: + with open(usb_path, 'r') as f: + details['chipset'] = f.read().strip() + break + except (FileNotFoundError, IOError): + pass + + # If no USB product, try lsusb for USB devices + if not details['chipset']: + try: + # Get USB bus/device info + uevent_path = f'{device_path}/uevent' + with open(uevent_path, 'r') as f: + for line in f: + if line.startswith('PRODUCT='): + # PRODUCT format: vendor/product/bcdDevice + product = line.split('=')[1].strip() + parts = product.split('/') + if len(parts) >= 2: + vid = parts[0].zfill(4) + pid = parts[1].zfill(4) + # Try lsusb to get device name + try: + lsusb = subprocess.run( + ['lsusb', '-d', f'{vid}:{pid}'], + capture_output=True, text=True, timeout=5 + ) + if lsusb.stdout: + # Format: Bus XXX Device YYY: ID vid:pid Name + usb_parts = lsusb.stdout.split(f'{vid}:{pid}') + if len(usb_parts) > 1: + details['chipset'] = usb_parts[1].strip() + except (FileNotFoundError, subprocess.TimeoutExpired): + pass + break + except (FileNotFoundError, IOError): + pass + except (FileNotFoundError, IOError, OSError): + pass + return details diff --git a/templates/index.html b/templates/index.html index 1f1d222..a42851f 100644 --- a/templates/index.html +++ b/templates/index.html @@ -4458,9 +4458,17 @@ .then(ifaceData => { const select = document.getElementById('wifiInterfaceSelect'); if (ifaceData.interfaces.length > 0) { - select.innerHTML = ifaceData.interfaces.map(i => - `` - ).join(''); + select.innerHTML = ifaceData.interfaces.map(i => { + let label = i.name; + let details = []; + if (i.chipset) details.push(i.chipset); + else if (i.driver) details.push(i.driver); + if (i.mac) details.push(i.mac.substring(0, 8) + '...'); + if (details.length > 0) label += ' - ' + details.join(' | '); + label += ` (${i.type})`; + if (i.monitor_capable) label += ' [Monitor OK]'; + return ``; + }).join(''); } }); } else { @@ -4502,33 +4510,79 @@ : 'Monitor mode: Inactive'; } - // Start WiFi scan - function startWifiScan() { + // Start WiFi scan - auto-enables monitor mode if needed + async function startWifiScan() { const band = document.getElementById('wifiBand').value; const channel = document.getElementById('wifiChannel').value; + // Auto-enable monitor mode if not already enabled if (!monitorInterface) { - alert('Enable monitor mode first'); - return; + const iface = document.getElementById('wifiInterfaceSelect').value; + if (!iface) { + showNotification('WiFi Error', 'No WiFi interface selected'); + return; + } + + // Show status + document.getElementById('statusText').textContent = 'Enabling monitor mode...'; + document.getElementById('statusDot').classList.add('running'); + + try { + const killProcesses = document.getElementById('killProcesses').checked; + const monitorResp = await fetch('/wifi/monitor', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({interface: iface, action: 'start', kill_processes: killProcesses}) + }); + const monitorData = await monitorResp.json(); + + if (monitorData.status === 'success') { + monitorInterface = monitorData.monitor_interface; + updateMonitorStatus(true); + showNotification('Monitor Mode', 'Enabled on ' + monitorInterface); + } else { + document.getElementById('statusText').textContent = 'Idle'; + document.getElementById('statusDot').classList.remove('running'); + showNotification('Monitor Error', monitorData.message || 'Failed to enable monitor mode'); + return; + } + } catch (err) { + document.getElementById('statusText').textContent = 'Idle'; + document.getElementById('statusDot').classList.remove('running'); + showNotification('Monitor Error', err.message); + return; + } } - fetch('/wifi/scan/start', { - method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({ - interface: monitorInterface, - band: band, - channel: channel || null - }) - }).then(r => r.json()) - .then(data => { - if (data.status === 'started') { - setWifiRunning(true); - startWifiStream(); - } else { - alert('Error: ' + data.message); - } - }); + // Now start the scan + document.getElementById('statusText').textContent = 'Starting scan...'; + + try { + const scanResp = await fetch('/wifi/scan/start', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ + interface: monitorInterface, + band: band, + channel: channel || null + }) + }); + const scanData = await scanResp.json(); + + if (scanData.status === 'started') { + setWifiRunning(true); + startWifiStream(); + showNotification('WiFi Scanner', 'Scanning started on ' + monitorInterface); + } else { + document.getElementById('statusText').textContent = 'Idle'; + document.getElementById('statusDot').classList.remove('running'); + showNotification('Scan Error', scanData.message || 'Failed to start scan'); + } + } catch (err) { + document.getElementById('statusText').textContent = 'Idle'; + document.getElementById('statusDot').classList.remove('running'); + showNotification('Scan Error', err.message); + } } // Stop WiFi scan