feat(scan): 增强MAC厂商识别和主机名自动发现
- MAC厂商: 替换硬编码OUI_MAP为mac_vendor_lookup库(IEEE OUI官方数据库) - 主机名发现新增NetBIOS广播(nmblookup),支持Windows/SMB设备 - 综合扫描支持enable_netbios参数 - 新增NetBIOS主机名发现API端点 - 扫描时自动补全缺失厂商(有MAC无vendor时自动识别)
This commit is contained in:
@@ -24,6 +24,7 @@ def comprehensive_scan(
|
|||||||
enable_ping: bool = True,
|
enable_ping: bool = True,
|
||||||
enable_arp: bool = True,
|
enable_arp: bool = True,
|
||||||
enable_dns: bool = True,
|
enable_dns: bool = True,
|
||||||
|
enable_netbios: bool = True,
|
||||||
timeout: int = 2,
|
timeout: int = 2,
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
):
|
):
|
||||||
@@ -32,7 +33,8 @@ def comprehensive_scan(
|
|||||||
- Ping 扫描检测在线状态
|
- Ping 扫描检测在线状态
|
||||||
- ARP 扫描获取MAC地址
|
- ARP 扫描获取MAC地址
|
||||||
- 反向DNS解析获取主机名
|
- 反向DNS解析获取主机名
|
||||||
- MAC厂商识别
|
- NetBIOS 广播获取主机名(Windows/SMB 设备)
|
||||||
|
- MAC 厂商识别(IEEE OUI 数据库)
|
||||||
"""
|
"""
|
||||||
network = NetworkService.get_by_id(db, network_id)
|
network = NetworkService.get_by_id(db, network_id)
|
||||||
if not network:
|
if not network:
|
||||||
@@ -51,6 +53,7 @@ def comprehensive_scan(
|
|||||||
enable_ping=enable_ping,
|
enable_ping=enable_ping,
|
||||||
enable_arp=enable_arp,
|
enable_arp=enable_arp,
|
||||||
enable_dns=enable_dns,
|
enable_dns=enable_dns,
|
||||||
|
enable_netbios=enable_netbios,
|
||||||
timeout=timeout
|
timeout=timeout
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,6 +93,7 @@ def comprehensive_scan_ip(
|
|||||||
enable_ping: bool = True,
|
enable_ping: bool = True,
|
||||||
enable_arp: bool = True,
|
enable_arp: bool = True,
|
||||||
enable_dns: bool = True,
|
enable_dns: bool = True,
|
||||||
|
enable_netbios: bool = True,
|
||||||
timeout: int = 2,
|
timeout: int = 2,
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
):
|
):
|
||||||
@@ -101,6 +105,7 @@ def comprehensive_scan_ip(
|
|||||||
enable_ping=enable_ping,
|
enable_ping=enable_ping,
|
||||||
enable_arp=enable_arp,
|
enable_arp=enable_arp,
|
||||||
enable_dns=enable_dns,
|
enable_dns=enable_dns,
|
||||||
|
enable_netbios=enable_netbios,
|
||||||
timeout=timeout
|
timeout=timeout
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -140,6 +145,17 @@ def reverse_dns(ip_address: str, timeout: int = 2):
|
|||||||
"hostname": hostname or "Unknown"
|
"hostname": hostname or "Unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@router.post("/netbios/lookup/{ip_address}", summary="NetBIOS 主机名发现")
|
||||||
|
def netbios_lookup(ip_address: str, timeout: int = 3):
|
||||||
|
"""
|
||||||
|
通过 NetBIOS 广播协议获取主机名(Windows / SMB 设备)
|
||||||
|
"""
|
||||||
|
hostname = EnhancedScanService.netbios_lookup(ip_address, timeout)
|
||||||
|
return {
|
||||||
|
"ip_address": ip_address,
|
||||||
|
"hostname": hostname or "Unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/online/ips", summary="获取所有在线IP列表")
|
@router.get("/online/ips", summary="获取所有在线IP列表")
|
||||||
def get_online_ips(
|
def get_online_ips(
|
||||||
|
|||||||
Binary file not shown.
@@ -6,66 +6,33 @@ import subprocess
|
|||||||
import socket
|
import socket
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import re
|
import re
|
||||||
|
import logging
|
||||||
|
|
||||||
|
try:
|
||||||
|
from mac_vendor_lookup import MacLookup, VendorNotFoundError
|
||||||
|
MAC_LOOKUP_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
MAC_LOOKUP_AVAILABLE = False
|
||||||
|
|
||||||
from app.models.network import ScanTask, Network, IPAddress
|
from app.models.network import ScanTask, Network, IPAddress
|
||||||
from app.models.network import TaskStatus, TaskType, IPStatus
|
from app.models.network import TaskStatus, TaskType, IPStatus
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
|
||||||
logger = __import__('logging').getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class EnhancedScanService:
|
class EnhancedScanService:
|
||||||
"""增强扫描服务 - 包含ARP、DNS解析、MAC厂商识别"""
|
"""增强扫描服务 - 包含ARP、DNS解析、MAC厂商识别、NetBIOS主机名发现"""
|
||||||
|
|
||||||
# MAC OUI 厂商映射(常用厂商)
|
# 初始化 MAC 厂商查询(单例,共享 OUI 数据库)
|
||||||
OUI_MAP = {
|
_mac_lookup = None
|
||||||
'00:1C:14': 'VMware',
|
|
||||||
'00:0C:29': 'VMware',
|
@classmethod
|
||||||
'00:50:56': 'VMware',
|
def _get_mac_lookup(cls):
|
||||||
'00:15:5D': 'Microsoft',
|
"""获取或初始化 MacLookup 实例"""
|
||||||
'08:00:27': 'VirtualBox',
|
if cls._mac_lookup is None and MAC_LOOKUP_AVAILABLE:
|
||||||
'52:54:00': 'QEMU/KVM',
|
cls._mac_lookup = MacLookup()
|
||||||
'00:1A:2A': 'Dell',
|
return cls._mac_lookup
|
||||||
'00:21:9B': 'Dell',
|
|
||||||
'00:26:B9': 'Dell',
|
|
||||||
'00:1B:21': 'HP',
|
|
||||||
'00:23:7D': 'HP',
|
|
||||||
'00:1E:0B': 'Cisco',
|
|
||||||
'00:21:55': 'Cisco',
|
|
||||||
'00:23:04': 'Cisco',
|
|
||||||
'00:24:14': 'Cisco',
|
|
||||||
'00:1F:CA': 'Cisco',
|
|
||||||
'00:19:BB': 'Huawei',
|
|
||||||
'00:25:9E': 'Huawei',
|
|
||||||
'00:E0:FC': 'Huawei',
|
|
||||||
'AC:85:3D': 'Huawei',
|
|
||||||
'28:6E:D4': 'Huawei',
|
|
||||||
'00:16:6F': 'H3C',
|
|
||||||
'00:23:89': 'H3C',
|
|
||||||
'00:0F:E2': 'Intel',
|
|
||||||
'00:1B:77': 'Intel',
|
|
||||||
'00:21:6A': 'Intel',
|
|
||||||
'1C:6F:65': 'Intel',
|
|
||||||
'00:19:D2': 'Realtek',
|
|
||||||
'00:24:1D': 'Realtek',
|
|
||||||
'00:E0:4C': 'Realtek',
|
|
||||||
'F4:6D:04': 'Apple',
|
|
||||||
'E0:F8:47': 'Apple',
|
|
||||||
'C8:BC:C8': 'Apple',
|
|
||||||
'04:15:52': 'Apple',
|
|
||||||
'28:CF:E9': 'Apple',
|
|
||||||
'04:7D:7B': 'Xiaomi',
|
|
||||||
'18:59:36': 'Xiaomi',
|
|
||||||
'34:CE:00': 'Xiaomi',
|
|
||||||
'64:09:80': 'TP-Link',
|
|
||||||
'E8:94:F6': 'TP-Link',
|
|
||||||
'50:FA:84': 'TP-Link',
|
|
||||||
'00:24:01': 'TP-Link',
|
|
||||||
'30:FC:68': 'NETGEAR',
|
|
||||||
'00:09:5B': 'NETGEAR',
|
|
||||||
'00:FF:FF': 'Broadcast',
|
|
||||||
'FF:FF:FF': 'Broadcast',
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_mac_vendor(mac_address: str) -> Optional[str]:
|
def get_mac_vendor(mac_address: str) -> Optional[str]:
|
||||||
@@ -78,19 +45,58 @@ class EnhancedScanService:
|
|||||||
if len(mac) < 8:
|
if len(mac) < 8:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 提取前3字节 OUI
|
# 使用 mac_vendor_lookup 库(IEEE OUI 官方数据库)
|
||||||
|
if MAC_LOOKUP_AVAILABLE:
|
||||||
|
lookup = EnhancedScanService._get_mac_lookup()
|
||||||
|
if lookup is not None:
|
||||||
|
try:
|
||||||
|
# 提取 OUI(前 3 字节)
|
||||||
|
oui = mac[:8] # "AA:BB:CC"
|
||||||
|
vendor = lookup.lookup(oui)
|
||||||
|
if vendor:
|
||||||
|
return vendor
|
||||||
|
except (VendorNotFoundError, AttributeError, TypeError):
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 回退:硬编码常用厂商映射
|
||||||
|
return EnhancedScanService._legacy_oui_lookup(mac)
|
||||||
|
|
||||||
|
# 硬编码回退映射(mac_vendor_lookup 不可用时使用)
|
||||||
|
_LEGACY_OUI_MAP = {
|
||||||
|
'00:1C:14': 'VMware', '00:0C:29': 'VMware', '00:50:56': 'VMware',
|
||||||
|
'00:15:5D': 'Microsoft', '08:00:27': 'VirtualBox', '52:54:00': 'QEMU/KVM',
|
||||||
|
'00:1A:2A': 'Dell', '00:21:9B': 'Dell', '00:26:B9': 'Dell',
|
||||||
|
'00:1B:21': 'HP', '00:23:7D': 'HP',
|
||||||
|
'00:1E:0B': 'Cisco', '00:21:55': 'Cisco', '00:23:04': 'Cisco',
|
||||||
|
'00:24:14': 'Cisco', '00:1F:CA': 'Cisco',
|
||||||
|
'00:19:BB': 'Huawei', '00:25:9E': 'Huawei', '00:E0:FC': 'Huawei',
|
||||||
|
'AC:85:3D': 'Huawei', '28:6E:D4': 'Huawei',
|
||||||
|
'00:16:6F': 'H3C', '00:23:89': 'H3C',
|
||||||
|
'00:0F:E2': 'Intel', '00:1B:77': 'Intel', '00:21:6A': 'Intel',
|
||||||
|
'1C:6F:65': 'Intel',
|
||||||
|
'00:19:D2': 'Realtek', '00:24:1D': 'Realtek', '00:E0:4C': 'Realtek',
|
||||||
|
'F4:6D:04': 'Apple', 'E0:F8:47': 'Apple', 'C8:BC:C8': 'Apple',
|
||||||
|
'04:15:52': 'Apple', '28:CF:E9': 'Apple',
|
||||||
|
'04:7D:7B': 'Xiaomi', '18:59:36': 'Xiaomi', '34:CE:00': 'Xiaomi',
|
||||||
|
'64:09:80': 'TP-Link', 'E8:94:F6': 'TP-Link', '50:FA:84': 'TP-Link',
|
||||||
|
'00:24:01': 'TP-Link',
|
||||||
|
'30:FC:68': 'NETGEAR', '00:09:5B': 'NETGEAR',
|
||||||
|
'00:FF:FF': 'Broadcast', 'FF:FF:FF': 'Broadcast',
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _legacy_oui_lookup(mac: str) -> Optional[str]:
|
||||||
|
"""硬编码 OUI 回退查询"""
|
||||||
oui = mac[:8]
|
oui = mac[:8]
|
||||||
|
if oui in EnhancedScanService._LEGACY_OUI_MAP:
|
||||||
# 精确匹配
|
return EnhancedScanService._LEGACY_OUI_MAP[oui]
|
||||||
if oui in EnhancedScanService.OUI_MAP:
|
# 模糊匹配(前 2 字节)
|
||||||
return EnhancedScanService.OUI_MAP[oui]
|
|
||||||
|
|
||||||
# 尝试模糊匹配(前2字节)
|
|
||||||
oui_short = mac[:5]
|
oui_short = mac[:5]
|
||||||
for key, vendor in EnhancedScanService.OUI_MAP.items():
|
for key, vendor in EnhancedScanService._LEGACY_OUI_MAP.items():
|
||||||
if key.startswith(oui_short):
|
if key.startswith(oui_short):
|
||||||
return vendor
|
return vendor
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -103,6 +109,64 @@ class EnhancedScanService:
|
|||||||
except (socket.herror, socket.timeout, socket.gaierror):
|
except (socket.herror, socket.timeout, socket.gaierror):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def netbios_lookup(ip_address: str, timeout: int = 3) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
通过 NetBIOS 广播协议获取主机名(Windows / SMB 设备)
|
||||||
|
使用 nmblookup -A <ip> 查询
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['nmblookup', '-A', ip_address],
|
||||||
|
capture_output=True, text=True, timeout=timeout
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
# nmblookup 输出格式示例:
|
||||||
|
# querying host 192.168.1.100 on 192.168.1.255
|
||||||
|
#
|
||||||
|
# WORKGROUP<00> - <ACTIVE> B
|
||||||
|
# WORKGROUP<00> - <GROUP> B
|
||||||
|
# WORKGROUP<1D> - <GROUP> B
|
||||||
|
# SERVER01<20> - <ACTIVE> B
|
||||||
|
# 主机名通常是 <20> 服务的第一列
|
||||||
|
for line in result.stdout.strip().split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith('querying'):
|
||||||
|
continue
|
||||||
|
# 匹配 "HOSTNAME<20> - <ACTIVE> B" 格式
|
||||||
|
match = re.match(
|
||||||
|
r'^(\S+?)<20>\s+-\s+<ACTIVE>',
|
||||||
|
line,
|
||||||
|
re.IGNORECASE
|
||||||
|
)
|
||||||
|
if match:
|
||||||
|
return match.group(1).upper()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def snmp_sysname_lookup(device_id: int, db: Session) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
通过 SNMP sysName 获取主机名(网络设备 / 服务器)
|
||||||
|
仅在扫描时已配置 SNMP 凭据的设备上调用
|
||||||
|
"""
|
||||||
|
from app.models.snmp import NetworkDevice
|
||||||
|
from app.services.snmp_service import SNMPService
|
||||||
|
try:
|
||||||
|
device = db.query(NetworkDevice).filter(
|
||||||
|
NetworkDevice.id == device_id
|
||||||
|
).first()
|
||||||
|
if device and device.snmp_credential:
|
||||||
|
success, info = SNMPService.test_connection(
|
||||||
|
device, device.snmp_credential
|
||||||
|
)
|
||||||
|
if success:
|
||||||
|
return info.get('sys_name')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def arp_scan_single(ip_address: str, timeout: int = 2) -> Optional[str]:
|
def arp_scan_single(ip_address: str, timeout: int = 2) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
@@ -116,7 +180,6 @@ class EnhancedScanService:
|
|||||||
capture_output=True, text=True, timeout=timeout + 2
|
capture_output=True, text=True, timeout=timeout + 2
|
||||||
)
|
)
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
# arp-scan 输出格式: IP\tMAC\tVendor
|
|
||||||
for line in result.stdout.strip().split('\n'):
|
for line in result.stdout.strip().split('\n'):
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line:
|
if not line:
|
||||||
@@ -155,7 +218,6 @@ class EnhancedScanService:
|
|||||||
timeout=2
|
timeout=2
|
||||||
)
|
)
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
# 匹配 MAC 地址格式
|
|
||||||
mac_pattern = r'([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})'
|
mac_pattern = r'([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})'
|
||||||
match = re.search(mac_pattern, result.stdout)
|
match = re.search(mac_pattern, result.stdout)
|
||||||
if match:
|
if match:
|
||||||
@@ -170,9 +232,20 @@ class EnhancedScanService:
|
|||||||
enable_ping: bool = True,
|
enable_ping: bool = True,
|
||||||
enable_arp: bool = True,
|
enable_arp: bool = True,
|
||||||
enable_dns: bool = True,
|
enable_dns: bool = True,
|
||||||
|
enable_netbios: bool = True,
|
||||||
timeout: int = 2) -> Dict[str, Any]:
|
timeout: int = 2) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
综合扫描单个IP
|
综合扫描单个IP
|
||||||
|
|
||||||
|
厂商识别:
|
||||||
|
- 主要: mac_vendor_lookup 库(IEEE OUI 官方数据库,~17万条)
|
||||||
|
- 回退: 硬编码 OUI_MAP
|
||||||
|
|
||||||
|
主机名发现(多源,按优先级):
|
||||||
|
1. 反向 DNS 解析(PTR)
|
||||||
|
2. NetBIOS 广播(Windows / SMB 设备)
|
||||||
|
3. SNMP sysName(网络设备 / 服务器,需配置 SNMP)
|
||||||
|
|
||||||
返回: {
|
返回: {
|
||||||
'ip_address': str,
|
'ip_address': str,
|
||||||
'status': 'online'|'offline',
|
'status': 'online'|'offline',
|
||||||
@@ -203,14 +276,21 @@ class EnhancedScanService:
|
|||||||
mac = EnhancedScanService.arp_scan_single(ip_address, timeout)
|
mac = EnhancedScanService.arp_scan_single(ip_address, timeout)
|
||||||
if mac:
|
if mac:
|
||||||
result['mac_address'] = mac
|
result['mac_address'] = mac
|
||||||
|
# 厂商识别:优先 mac_vendor_lookup 库,失败后回退硬编码映射
|
||||||
result['vendor'] = EnhancedScanService.get_mac_vendor(mac)
|
result['vendor'] = EnhancedScanService.get_mac_vendor(mac)
|
||||||
# 如果获取到MAC,说明设备实际上在线
|
# 如果获取到MAC,说明设备实际上在线
|
||||||
result['status'] = 'online'
|
result['status'] = 'online'
|
||||||
result['success'] = True
|
result['success'] = True
|
||||||
|
|
||||||
# 3. 反向 DNS 解析
|
# 3. 主机名发现(多源,有任一在线即执行)
|
||||||
if enable_dns and result.get('success'):
|
if result.get('success'):
|
||||||
|
hostname = None
|
||||||
|
# 3a. 反向 DNS
|
||||||
|
if enable_dns:
|
||||||
hostname = EnhancedScanService.reverse_dns_lookup(ip_address, timeout)
|
hostname = EnhancedScanService.reverse_dns_lookup(ip_address, timeout)
|
||||||
|
# 3b. NetBIOS(Windows / SMB)
|
||||||
|
if not hostname and enable_netbios:
|
||||||
|
hostname = EnhancedScanService.netbios_lookup(ip_address, timeout=3)
|
||||||
if hostname:
|
if hostname:
|
||||||
result['hostname'] = hostname
|
result['hostname'] = hostname
|
||||||
|
|
||||||
@@ -249,6 +329,7 @@ class EnhancedScanService:
|
|||||||
enable_ping: bool = True,
|
enable_ping: bool = True,
|
||||||
enable_arp: bool = True,
|
enable_arp: bool = True,
|
||||||
enable_dns: bool = True,
|
enable_dns: bool = True,
|
||||||
|
enable_netbios: bool = True,
|
||||||
timeout: int = 2,
|
timeout: int = 2,
|
||||||
max_concurrent: int = 50) -> List[Dict[str, Any]]:
|
max_concurrent: int = 50) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
@@ -260,7 +341,6 @@ class EnhancedScanService:
|
|||||||
if not ips:
|
if not ips:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# 并发执行;并发数随网段大小自适应
|
|
||||||
worker_count = min(max_concurrent, len(ips))
|
worker_count = min(max_concurrent, len(ips))
|
||||||
results: List[Dict[str, Any]] = []
|
results: List[Dict[str, Any]] = []
|
||||||
with ThreadPoolExecutor(max_workers=worker_count) as executor:
|
with ThreadPoolExecutor(max_workers=worker_count) as executor:
|
||||||
@@ -271,6 +351,7 @@ class EnhancedScanService:
|
|||||||
enable_ping=enable_ping,
|
enable_ping=enable_ping,
|
||||||
enable_arp=enable_arp,
|
enable_arp=enable_arp,
|
||||||
enable_dns=enable_dns,
|
enable_dns=enable_dns,
|
||||||
|
enable_netbios=enable_netbios,
|
||||||
timeout=timeout
|
timeout=timeout
|
||||||
): ip
|
): ip
|
||||||
for ip in ips
|
for ip in ips
|
||||||
@@ -303,12 +384,10 @@ class EnhancedScanService:
|
|||||||
|
|
||||||
# 如果IP不存在,尝试自动创建
|
# 如果IP不存在,尝试自动创建
|
||||||
if not db_ip:
|
if not db_ip:
|
||||||
# 从ip_address推断network_id
|
|
||||||
network = db.query(Network).filter(
|
network = db.query(Network).filter(
|
||||||
Network.cidr.op('@>')((ip_address + '/32').encode('utf-8'))
|
Network.cidr.op('@>')((ip_address + '/32').encode('utf-8'))
|
||||||
).first()
|
).first()
|
||||||
if not network:
|
if not network:
|
||||||
# 尝试精确匹配 network.cidr
|
|
||||||
for net in db.query(Network).all():
|
for net in db.query(Network).all():
|
||||||
try:
|
try:
|
||||||
if ip_address in ipaddress.ip_network(net.cidr, strict=False):
|
if ip_address in ipaddress.ip_network(net.cidr, strict=False):
|
||||||
@@ -342,11 +421,15 @@ class EnhancedScanService:
|
|||||||
if hostname:
|
if hostname:
|
||||||
db_ip.hostname = hostname
|
db_ip.hostname = hostname
|
||||||
|
|
||||||
# 更新厂商信息
|
# 更新厂商信息(即使没有 MAC,也尝试用现有 MAC 补全)
|
||||||
vendor = scan_result.get('vendor')
|
vendor = scan_result.get('vendor')
|
||||||
if vendor:
|
if vendor:
|
||||||
db_ip.vendor = vendor
|
db_ip.vendor = vendor
|
||||||
|
|
||||||
|
# 如果没有拿到 vendor 但有 MAC,用现有 MAC 补全厂商
|
||||||
|
if not db_ip.vendor and db_ip.mac_address:
|
||||||
|
db_ip.vendor = EnhancedScanService.get_mac_vendor(db_ip.mac_address)
|
||||||
|
|
||||||
# 更新最后发现时间
|
# 更新最后发现时间
|
||||||
if scan_result.get('success'):
|
if scan_result.get('success'):
|
||||||
db_ip.last_seen = datetime.utcnow()
|
db_ip.last_seen = datetime.utcnow()
|
||||||
@@ -360,6 +443,7 @@ class EnhancedScanService:
|
|||||||
mac_found_count = 0
|
mac_found_count = 0
|
||||||
hostname_found_count = 0
|
hostname_found_count = 0
|
||||||
created_count = 0
|
created_count = 0
|
||||||
|
vendor_found_count = 0
|
||||||
|
|
||||||
for result in scan_results:
|
for result in scan_results:
|
||||||
ip = EnhancedScanService.update_ip_from_scan_result(db, result)
|
ip = EnhancedScanService.update_ip_from_scan_result(db, result)
|
||||||
@@ -370,6 +454,8 @@ class EnhancedScanService:
|
|||||||
mac_found_count += 1
|
mac_found_count += 1
|
||||||
if result.get('hostname'):
|
if result.get('hostname'):
|
||||||
hostname_found_count += 1
|
hostname_found_count += 1
|
||||||
|
if result.get('vendor'):
|
||||||
|
vendor_found_count += 1
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
@@ -377,5 +463,6 @@ class EnhancedScanService:
|
|||||||
'online_count': online_count,
|
'online_count': online_count,
|
||||||
'mac_found_count': mac_found_count,
|
'mac_found_count': mac_found_count,
|
||||||
'hostname_found_count': hostname_found_count,
|
'hostname_found_count': hostname_found_count,
|
||||||
|
'vendor_found_count': vendor_found_count,
|
||||||
'total_scanned': len(scan_results)
|
'total_scanned': len(scan_results)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ def register_tasks(celery_app):
|
|||||||
enable_ping: bool = True,
|
enable_ping: bool = True,
|
||||||
enable_arp: bool = True,
|
enable_arp: bool = True,
|
||||||
enable_dns: bool = True,
|
enable_dns: bool = True,
|
||||||
|
enable_netbios: bool = True,
|
||||||
timeout: int = 2):
|
timeout: int = 2):
|
||||||
"""
|
"""
|
||||||
Celery 异步任务:扫描指定网段
|
Celery 异步任务:扫描指定网段
|
||||||
@@ -54,6 +55,7 @@ def register_tasks(celery_app):
|
|||||||
enable_ping=enable_ping,
|
enable_ping=enable_ping,
|
||||||
enable_arp=enable_arp,
|
enable_arp=enable_arp,
|
||||||
enable_dns=enable_dns,
|
enable_dns=enable_dns,
|
||||||
|
enable_netbios=enable_netbios,
|
||||||
timeout=timeout
|
timeout=timeout
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -120,7 +122,8 @@ def register_tasks(celery_app):
|
|||||||
@celery_app.task(bind=True)
|
@celery_app.task(bind=True)
|
||||||
def full_network_scan(self, enable_ping: bool = True,
|
def full_network_scan(self, enable_ping: bool = True,
|
||||||
enable_arp: bool = True,
|
enable_arp: bool = True,
|
||||||
enable_dns: bool = True):
|
enable_dns: bool = True,
|
||||||
|
enable_netbios: bool = True):
|
||||||
"""
|
"""
|
||||||
Celery 定时任务:扫描所有网段
|
Celery 定时任务:扫描所有网段
|
||||||
"""
|
"""
|
||||||
@@ -144,7 +147,8 @@ def register_tasks(celery_app):
|
|||||||
network.cidr,
|
network.cidr,
|
||||||
enable_ping=enable_ping,
|
enable_ping=enable_ping,
|
||||||
enable_arp=enable_arp,
|
enable_arp=enable_arp,
|
||||||
enable_dns=enable_dns
|
enable_dns=enable_dns,
|
||||||
|
enable_netbios=enable_netbios
|
||||||
)
|
)
|
||||||
|
|
||||||
stats = EnhancedScanService.bulk_update_ips_from_scan(db, results)
|
stats = EnhancedScanService.bulk_update_ips_from_scan(db, results)
|
||||||
@@ -191,7 +195,10 @@ def register_tasks(celery_app):
|
|||||||
results = []
|
results = []
|
||||||
|
|
||||||
for ip in ip_addresses:
|
for ip in ip_addresses:
|
||||||
result = EnhancedScanService.comprehensive_scan(ip, enable_ping=True, enable_arp=True, enable_dns=False)
|
result = EnhancedScanService.comprehensive_scan(
|
||||||
|
ip, enable_ping=True, enable_arp=True, enable_dns=False,
|
||||||
|
enable_netbios=True
|
||||||
|
)
|
||||||
EnhancedScanService.update_ip_from_scan_result(db, result)
|
EnhancedScanService.update_ip_from_scan_result(db, result)
|
||||||
results.append(result)
|
results.append(result)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user