iotsploit-core 0.0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iotsploit_core-0.0.6/PKG-INFO +117 -0
- iotsploit_core-0.0.6/README.md +89 -0
- iotsploit_core-0.0.6/pyproject.toml +40 -0
- iotsploit_core-0.0.6/src/iotsploit_core/__init__.py +7 -0
- iotsploit_core-0.0.6/src/iotsploit_core/context.py +27 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/__init__.py +0 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/base_plugin.py +290 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/device_config.py +105 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/device_manager.py +942 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/device_registry.py +118 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/device_scanner.py +74 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/device_spec.py +105 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/device_store.py +86 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/execution_backend.py +464 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/execution_queue.py +456 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/exploit_manager.py +728 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/exploit_spec.py +73 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/stream_manager.py +157 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/tool_config.py +430 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/tool_manager.py +1166 -0
- iotsploit_core-0.0.6/src/iotsploit_core/core/tool_service.py +936 -0
- iotsploit_core-0.0.6/src/iotsploit_core/domain/__init__.py +3 -0
- iotsploit_core-0.0.6/src/iotsploit_core/domain/device.py +70 -0
- iotsploit_core-0.0.6/src/iotsploit_core/domain/execution_plan.py +29 -0
- iotsploit_core-0.0.6/src/iotsploit_core/domain/plugin.py +17 -0
- iotsploit_core-0.0.6/src/iotsploit_core/domain/stream.py +63 -0
- iotsploit_core-0.0.6/src/iotsploit_core/domain/target.py +243 -0
- iotsploit_core-0.0.6/src/iotsploit_core/platforms/__init__.py +19 -0
- iotsploit_core-0.0.6/src/iotsploit_core/platforms/consts.py +25 -0
- iotsploit_core-0.0.6/src/iotsploit_core/ports/__init__.py +6 -0
- iotsploit_core-0.0.6/src/iotsploit_core/ports/driver_state_repo.py +15 -0
- iotsploit_core-0.0.6/src/iotsploit_core/ports/plugin_repo.py +26 -0
- iotsploit_core-0.0.6/src/iotsploit_core/ports/stream_backend.py +28 -0
- iotsploit_core-0.0.6/src/iotsploit_core/ports/task_runner.py +22 -0
- iotsploit_core-0.0.6/src/iotsploit_core/ports/wifi_backend.py +148 -0
- iotsploit_core-0.0.6/src/iotsploit_core/py.typed +2 -0
- iotsploit_core-0.0.6/src/iotsploit_core/utils/__init__.py +53 -0
- iotsploit_core-0.0.6/src/iotsploit_core/utils/exceptions.py +152 -0
- iotsploit_core-0.0.6/src/iotsploit_core/utils/helpers.py +65 -0
- iotsploit_core-0.0.6/src/iotsploit_core/utils/iots_logger.py +164 -0
- iotsploit_core-0.0.6/src/iotsploit_core/utils/result.py +41 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: iotsploit-core
|
|
3
|
+
Version: 0.0.6
|
|
4
|
+
Summary: IoTSploit core library (domain + ports + core) - IoT security testing framework
|
|
5
|
+
License: GPL-3.0-or-later
|
|
6
|
+
Keywords: iot,security,testing,pentest,vulnerability
|
|
7
|
+
Author: IoTSploit Team
|
|
8
|
+
Author-email: support@iotsploit.org
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Information Technology
|
|
13
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Security
|
|
20
|
+
Classifier: Topic :: System :: Hardware
|
|
21
|
+
Requires-Dist: dataclasses-json (>=0.6.3,<0.7.0)
|
|
22
|
+
Requires-Dist: pluggy (>=1.3.0,<2.0.0)
|
|
23
|
+
Project-URL: Documentation, https://www.iotsploit.org/
|
|
24
|
+
Project-URL: Homepage, https://www.iotsploit.org/
|
|
25
|
+
Project-URL: Repository, https://github.com/TKXB/iotsploit
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# iotsploit-core
|
|
29
|
+
|
|
30
|
+
Minimal, standalone **core** library extracted from `zeekr_sat_main`.
|
|
31
|
+
|
|
32
|
+
- **What it is**: domain models + ports (interfaces) + core orchestration.
|
|
33
|
+
- **What it is not**: Django/ORM/Celery/Channels integrations (those are application-layer adapters).
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
Poetry (recommended):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cd iotsploit-core
|
|
41
|
+
poetry install
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Pip editable:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install -e iotsploit-core
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Use (quick start)
|
|
51
|
+
|
|
52
|
+
Verify it imports:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import iotsploit_core
|
|
56
|
+
print(iotsploit_core.__version__)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Exploit plugins (manager)
|
|
60
|
+
|
|
61
|
+
You wire this manager with **your own adapters** that implement `iotsploit_core.ports`.
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from iotsploit_core.core.exploit_manager import ExploitPluginManager
|
|
65
|
+
|
|
66
|
+
mgr = ExploitPluginManager(
|
|
67
|
+
plugin_repo=..., # implements PluginMetaRepository
|
|
68
|
+
group_repo=..., # implements PluginGroupRepository
|
|
69
|
+
task_runner=..., # implements TaskRunner (optional async delegation)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
print(mgr.list_plugins())
|
|
73
|
+
# mgr.execute_plugin("SomePlugin", target=..., parameters={...})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Default exploit plugin dir:
|
|
77
|
+
- `IOTSPLOIT_EXPLOIT_PLUGINS_DIR` (or legacy `SAT_EXPLOIT_PLUGINS_DIR`)
|
|
78
|
+
- fallback: `./plugins/exploits`
|
|
79
|
+
|
|
80
|
+
### Device drivers (manager)
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from iotsploit_core.core.device_manager import DeviceDriverManager
|
|
84
|
+
|
|
85
|
+
dm = DeviceDriverManager(
|
|
86
|
+
driver_state_repo=..., # implements DriverStateRepository
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
print(dm.list_drivers())
|
|
90
|
+
# dm.scan_devices("drv_xxx")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Default device plugin dir:
|
|
94
|
+
- `IOTSPLOIT_DEVICE_PLUGINS_DIR` (or legacy `SAT_DEVICE_PLUGINS_DIR`)
|
|
95
|
+
- fallback: `<repo_root>/plugins/devices`
|
|
96
|
+
|
|
97
|
+
### Streaming (optional)
|
|
98
|
+
|
|
99
|
+
Core uses a safe **Noop** backend by default. In a web runtime, inject a real backend:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from iotsploit_core.core.stream_manager import StreamManager
|
|
103
|
+
|
|
104
|
+
# StreamManager.configure_backend(MyBackend())
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Package layout
|
|
108
|
+
|
|
109
|
+
- `iotsploit_core.domain`: pure dataclasses/enums (no I/O)
|
|
110
|
+
- `iotsploit_core.ports`: interfaces to be implemented by adapters
|
|
111
|
+
- `iotsploit_core.core`: orchestration (plugin/device/tool managers)
|
|
112
|
+
|
|
113
|
+
## Notes
|
|
114
|
+
|
|
115
|
+
- `iotsploit_core.core.device_config` is **deprecated** (device config should live in DB via app-layer adapters).
|
|
116
|
+
|
|
117
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# iotsploit-core
|
|
2
|
+
|
|
3
|
+
Minimal, standalone **core** library extracted from `zeekr_sat_main`.
|
|
4
|
+
|
|
5
|
+
- **What it is**: domain models + ports (interfaces) + core orchestration.
|
|
6
|
+
- **What it is not**: Django/ORM/Celery/Channels integrations (those are application-layer adapters).
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
Poetry (recommended):
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
cd iotsploit-core
|
|
14
|
+
poetry install
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Pip editable:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install -e iotsploit-core
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Use (quick start)
|
|
24
|
+
|
|
25
|
+
Verify it imports:
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import iotsploit_core
|
|
29
|
+
print(iotsploit_core.__version__)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Exploit plugins (manager)
|
|
33
|
+
|
|
34
|
+
You wire this manager with **your own adapters** that implement `iotsploit_core.ports`.
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from iotsploit_core.core.exploit_manager import ExploitPluginManager
|
|
38
|
+
|
|
39
|
+
mgr = ExploitPluginManager(
|
|
40
|
+
plugin_repo=..., # implements PluginMetaRepository
|
|
41
|
+
group_repo=..., # implements PluginGroupRepository
|
|
42
|
+
task_runner=..., # implements TaskRunner (optional async delegation)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
print(mgr.list_plugins())
|
|
46
|
+
# mgr.execute_plugin("SomePlugin", target=..., parameters={...})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Default exploit plugin dir:
|
|
50
|
+
- `IOTSPLOIT_EXPLOIT_PLUGINS_DIR` (or legacy `SAT_EXPLOIT_PLUGINS_DIR`)
|
|
51
|
+
- fallback: `./plugins/exploits`
|
|
52
|
+
|
|
53
|
+
### Device drivers (manager)
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from iotsploit_core.core.device_manager import DeviceDriverManager
|
|
57
|
+
|
|
58
|
+
dm = DeviceDriverManager(
|
|
59
|
+
driver_state_repo=..., # implements DriverStateRepository
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
print(dm.list_drivers())
|
|
63
|
+
# dm.scan_devices("drv_xxx")
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Default device plugin dir:
|
|
67
|
+
- `IOTSPLOIT_DEVICE_PLUGINS_DIR` (or legacy `SAT_DEVICE_PLUGINS_DIR`)
|
|
68
|
+
- fallback: `<repo_root>/plugins/devices`
|
|
69
|
+
|
|
70
|
+
### Streaming (optional)
|
|
71
|
+
|
|
72
|
+
Core uses a safe **Noop** backend by default. In a web runtime, inject a real backend:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from iotsploit_core.core.stream_manager import StreamManager
|
|
76
|
+
|
|
77
|
+
# StreamManager.configure_backend(MyBackend())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Package layout
|
|
81
|
+
|
|
82
|
+
- `iotsploit_core.domain`: pure dataclasses/enums (no I/O)
|
|
83
|
+
- `iotsploit_core.ports`: interfaces to be implemented by adapters
|
|
84
|
+
- `iotsploit_core.core`: orchestration (plugin/device/tool managers)
|
|
85
|
+
|
|
86
|
+
## Notes
|
|
87
|
+
|
|
88
|
+
- `iotsploit_core.core.device_config` is **deprecated** (device config should live in DB via app-layer adapters).
|
|
89
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "iotsploit-core"
|
|
3
|
+
version = "0.0.6"
|
|
4
|
+
description = "IoTSploit core library (domain + ports + core) - IoT security testing framework"
|
|
5
|
+
authors = ["IoTSploit Team <support@iotsploit.org>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = "GPL-3.0-or-later"
|
|
8
|
+
homepage = "https://www.iotsploit.org/"
|
|
9
|
+
repository = "https://github.com/TKXB/iotsploit"
|
|
10
|
+
documentation = "https://www.iotsploit.org/"
|
|
11
|
+
keywords = ["iot", "security", "testing", "pentest", "vulnerability"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 3 - Alpha",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"Intended Audience :: Information Technology",
|
|
16
|
+
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
|
17
|
+
"Topic :: Security",
|
|
18
|
+
"Topic :: System :: Hardware",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
]
|
|
24
|
+
packages = [{ include = "iotsploit_core", from = "src" }]
|
|
25
|
+
|
|
26
|
+
[tool.poetry.dependencies]
|
|
27
|
+
python = ">=3.10,<4.0"
|
|
28
|
+
pluggy = "^1.3.0"
|
|
29
|
+
dataclasses-json = "^0.6.3"
|
|
30
|
+
|
|
31
|
+
[tool.poetry.group.dev.dependencies]
|
|
32
|
+
pytest = "^7.4.0"
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["poetry-core>=1.8.0"]
|
|
36
|
+
build-backend = "poetry.core.masonry.api"
|
|
37
|
+
|
|
38
|
+
[tool.pytest.ini_options]
|
|
39
|
+
testpaths = ["tests"]
|
|
40
|
+
addopts = ["-q"]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Plugin Context for Backend Injection.
|
|
3
|
+
|
|
4
|
+
This module provides a typed context object for injecting platform-specific
|
|
5
|
+
backends into plugins. This avoids magic string keys and provides better
|
|
6
|
+
type safety and IDE support.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from iotsploit_core.ports.wifi_backend import WifiBackend
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class PluginContext:
|
|
17
|
+
"""
|
|
18
|
+
Context object for injecting platform-specific backends into plugins.
|
|
19
|
+
|
|
20
|
+
This provides a structured way to pass backends to plugins, avoiding
|
|
21
|
+
dictionary-based injection with magic string keys. As new backends are
|
|
22
|
+
added, they can be added as optional fields here.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
wifi: Optional WiFi backend instance
|
|
26
|
+
"""
|
|
27
|
+
wifi: Optional[WifiBackend] = None
|
|
File without changes
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Dict, Any, List, Optional
|
|
4
|
+
from iotsploit_core.core.stream_manager import StreamManager, StreamWrapper
|
|
5
|
+
from iotsploit_core.domain.device import Device
|
|
6
|
+
from iotsploit_core.core.device_spec import DeviceState
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
class BasePlugin:
|
|
11
|
+
def __init__(self, info: Dict[str, Any] = None):
|
|
12
|
+
self.info = info or {}
|
|
13
|
+
|
|
14
|
+
def update_info(self, new_info: Dict[str, Any]):
|
|
15
|
+
self.info.update(new_info)
|
|
16
|
+
|
|
17
|
+
def get_info(self) -> Dict[str, Any]:
|
|
18
|
+
return self.info
|
|
19
|
+
|
|
20
|
+
class BaseDeviceDriver(BasePlugin):
|
|
21
|
+
"""Base class for device drivers implementing the DevicePluginSpec interface"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, info: Dict[str, Any] = None):
|
|
24
|
+
super().__init__(info)
|
|
25
|
+
# Device management
|
|
26
|
+
self.device = None
|
|
27
|
+
self._devices: Dict[str, Device] = {}
|
|
28
|
+
|
|
29
|
+
# Stream management (core-safe: StreamManager is a facade that falls back to Noop if
|
|
30
|
+
# Django/Channels is unavailable).
|
|
31
|
+
self.stream_manager = StreamManager()
|
|
32
|
+
self.stream_wrapper = StreamWrapper(self.stream_manager)
|
|
33
|
+
|
|
34
|
+
# Acquisition management
|
|
35
|
+
self.acquisition_thread = None
|
|
36
|
+
self.is_acquiring = threading.Event()
|
|
37
|
+
|
|
38
|
+
# 确保 supported_commands 总是存在
|
|
39
|
+
if not hasattr(self, 'supported_commands'):
|
|
40
|
+
self.supported_commands = {} # format: {'command': 'description'}
|
|
41
|
+
|
|
42
|
+
def get_supported_commands(self) -> Dict[str, str]:
|
|
43
|
+
"""Get dictionary of supported commands and their descriptions"""
|
|
44
|
+
if not hasattr(self, 'supported_commands'):
|
|
45
|
+
return {}
|
|
46
|
+
return self.supported_commands
|
|
47
|
+
|
|
48
|
+
# Base implementations of device lifecycle methods
|
|
49
|
+
def scan(self) -> List[Device]:
|
|
50
|
+
"""Scan for available devices"""
|
|
51
|
+
try:
|
|
52
|
+
devices = self._scan_impl()
|
|
53
|
+
for device in devices:
|
|
54
|
+
self._register_device(device)
|
|
55
|
+
return devices
|
|
56
|
+
except Exception as e:
|
|
57
|
+
logger.error(f"Scan failed: {str(e)}")
|
|
58
|
+
raise
|
|
59
|
+
|
|
60
|
+
def initialize(self, device: Device) -> bool:
|
|
61
|
+
"""Initialize device"""
|
|
62
|
+
try:
|
|
63
|
+
return self._initialize_impl(device)
|
|
64
|
+
except Exception as e:
|
|
65
|
+
logger.error(f"Initialization failed: {str(e)}")
|
|
66
|
+
raise
|
|
67
|
+
|
|
68
|
+
def connect(self, device: Device) -> bool:
|
|
69
|
+
"""Connect to device"""
|
|
70
|
+
try:
|
|
71
|
+
return self._connect_impl(device)
|
|
72
|
+
except Exception as e:
|
|
73
|
+
logger.error(f"Connection failed: {str(e)}")
|
|
74
|
+
raise
|
|
75
|
+
|
|
76
|
+
def command(self, device: Device, command: str, args: Optional[Dict] = None) -> Optional[str]:
|
|
77
|
+
"""Execute command"""
|
|
78
|
+
try:
|
|
79
|
+
return self._command_impl(device, command, args)
|
|
80
|
+
except Exception as e:
|
|
81
|
+
logger.error(f"Command execution failed: {str(e)}")
|
|
82
|
+
raise
|
|
83
|
+
|
|
84
|
+
def reset(self, device: Device) -> bool:
|
|
85
|
+
"""Reset device"""
|
|
86
|
+
try:
|
|
87
|
+
return self._reset_impl(device)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
logger.error(f"Reset failed: {str(e)}")
|
|
90
|
+
raise
|
|
91
|
+
|
|
92
|
+
def close(self, device: Device) -> bool:
|
|
93
|
+
"""Close device"""
|
|
94
|
+
try:
|
|
95
|
+
return self._close_impl(device)
|
|
96
|
+
except Exception as e:
|
|
97
|
+
logger.error(f"Close failed: {str(e)}")
|
|
98
|
+
raise
|
|
99
|
+
|
|
100
|
+
def recovery(self, device: Device, recovery_type: str, **kwargs) -> dict:
|
|
101
|
+
"""
|
|
102
|
+
Execute recovery operations on device
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
device: Device to perform recovery on
|
|
106
|
+
recovery_type: Type of recovery operation (e.g., 'flash_firmware', 'openocd_attach')
|
|
107
|
+
**kwargs: Additional parameters for recovery operation
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
dict: Recovery operation result with standardized format
|
|
111
|
+
"""
|
|
112
|
+
try:
|
|
113
|
+
logger.info(f"Starting recovery operation '{recovery_type}' on device {device.device_id}")
|
|
114
|
+
result = self._recovery_impl(device, recovery_type, **kwargs)
|
|
115
|
+
|
|
116
|
+
# Ensure standardized response format
|
|
117
|
+
if not isinstance(result, dict):
|
|
118
|
+
result = {"status": "error", "message": "Invalid response format from recovery implementation"}
|
|
119
|
+
|
|
120
|
+
# Add standard fields if missing
|
|
121
|
+
if "status" not in result:
|
|
122
|
+
result["status"] = "unknown"
|
|
123
|
+
if "message" not in result:
|
|
124
|
+
result["message"] = f"Recovery operation '{recovery_type}' completed"
|
|
125
|
+
|
|
126
|
+
logger.info(f"Recovery operation '{recovery_type}' completed with status: {result.get('status')}")
|
|
127
|
+
return result
|
|
128
|
+
|
|
129
|
+
except NotImplementedError:
|
|
130
|
+
return {
|
|
131
|
+
"status": "error",
|
|
132
|
+
"message": f"Recovery operation '{recovery_type}' not supported by this driver"
|
|
133
|
+
}
|
|
134
|
+
except Exception as e:
|
|
135
|
+
logger.error(f"Recovery operation failed: {str(e)}")
|
|
136
|
+
return {
|
|
137
|
+
"status": "error",
|
|
138
|
+
"message": f"Recovery operation failed: {str(e)}"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
def get_supported_recovery_operations(self) -> list:
|
|
142
|
+
"""
|
|
143
|
+
Get list of supported recovery operations for this driver
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
list: List of supported recovery operation names
|
|
147
|
+
"""
|
|
148
|
+
try:
|
|
149
|
+
return self._get_supported_recovery_operations_impl()
|
|
150
|
+
except NotImplementedError:
|
|
151
|
+
return []
|
|
152
|
+
except Exception as e:
|
|
153
|
+
logger.error(f"Error getting supported recovery operations: {str(e)}")
|
|
154
|
+
return []
|
|
155
|
+
|
|
156
|
+
# Streaming and acquisition control
|
|
157
|
+
def start_streaming(self, device: Device):
|
|
158
|
+
"""启动设备数据流(包括数据采集和WebSocket分发)"""
|
|
159
|
+
try:
|
|
160
|
+
logger.info(f"Starting streaming for device {device.device_id}")
|
|
161
|
+
self.stream_wrapper.register_stream(device.device_id)
|
|
162
|
+
self.start_acquisition(device)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.error(f"Failed to start streaming: {e}")
|
|
165
|
+
self.stop_streaming(device)
|
|
166
|
+
raise
|
|
167
|
+
|
|
168
|
+
def stop_streaming(self, device: Device):
|
|
169
|
+
"""停止设备数据流(包括数据采集和WebSocket分发)"""
|
|
170
|
+
try:
|
|
171
|
+
logger.info(f"Stopping streaming for device {device.device_id}")
|
|
172
|
+
self.stop_acquisition(device)
|
|
173
|
+
self.stream_wrapper.unregister_stream(device.device_id)
|
|
174
|
+
self.stream_wrapper.stop_broadcast(device.device_id)
|
|
175
|
+
except Exception as e:
|
|
176
|
+
logger.error(f"Error stopping streaming: {e}")
|
|
177
|
+
raise
|
|
178
|
+
|
|
179
|
+
def start_acquisition(self, device: Device):
|
|
180
|
+
"""启动设备数据采集(不包括WebSocket分发)"""
|
|
181
|
+
try:
|
|
182
|
+
logger.info(f"Starting data acquisition for device {device.device_id}")
|
|
183
|
+
self._setup_acquisition(device)
|
|
184
|
+
if not self.is_acquiring.is_set():
|
|
185
|
+
self.is_acquiring.set()
|
|
186
|
+
self.acquisition_thread = threading.Thread(
|
|
187
|
+
target=self._acquisition_loop,
|
|
188
|
+
name=f'{self.__class__.__name__}_Acquisition'
|
|
189
|
+
)
|
|
190
|
+
self.acquisition_thread.daemon = True
|
|
191
|
+
self.acquisition_thread.start()
|
|
192
|
+
except Exception as e:
|
|
193
|
+
logger.error(f"Failed to start acquisition: {e}")
|
|
194
|
+
self.stop_acquisition(device)
|
|
195
|
+
raise
|
|
196
|
+
|
|
197
|
+
def stop_acquisition(self, device: Device):
|
|
198
|
+
"""停止设备数据采集(不包括WebSocket分发)"""
|
|
199
|
+
logger.info(f"Stopping data acquisition for device {device.device_id}")
|
|
200
|
+
self.is_acquiring.clear()
|
|
201
|
+
if self.acquisition_thread and self.acquisition_thread.is_alive():
|
|
202
|
+
self.acquisition_thread.join(timeout=1.0)
|
|
203
|
+
self.acquisition_thread = None
|
|
204
|
+
try:
|
|
205
|
+
self._cleanup_acquisition(device)
|
|
206
|
+
except Exception as e:
|
|
207
|
+
logger.error(f"Error in acquisition cleanup: {e}")
|
|
208
|
+
raise
|
|
209
|
+
|
|
210
|
+
# Methods to be implemented by derived classes
|
|
211
|
+
def _scan_impl(self) -> List[Device]:
|
|
212
|
+
"""Implementation of device scanning"""
|
|
213
|
+
raise NotImplementedError
|
|
214
|
+
|
|
215
|
+
def _initialize_impl(self, device: Device) -> bool:
|
|
216
|
+
"""Implementation of device initialization"""
|
|
217
|
+
raise NotImplementedError
|
|
218
|
+
|
|
219
|
+
def _connect_impl(self, device: Device) -> bool:
|
|
220
|
+
"""Implementation of device connection"""
|
|
221
|
+
raise NotImplementedError
|
|
222
|
+
|
|
223
|
+
def _command_impl(self, device: Device, command: str, args: Optional[Dict] = None) -> Optional[str]:
|
|
224
|
+
"""Implementation of command execution"""
|
|
225
|
+
raise NotImplementedError
|
|
226
|
+
|
|
227
|
+
def _reset_impl(self, device: Device) -> bool:
|
|
228
|
+
"""Implementation of device reset"""
|
|
229
|
+
raise NotImplementedError
|
|
230
|
+
|
|
231
|
+
def _close_impl(self, device: Device) -> bool:
|
|
232
|
+
"""Implementation of device closure"""
|
|
233
|
+
raise NotImplementedError
|
|
234
|
+
|
|
235
|
+
def _recovery_impl(self, device: Device, recovery_type: str, **kwargs) -> dict:
|
|
236
|
+
"""
|
|
237
|
+
Implementation of recovery operations
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
device: Device to perform recovery on
|
|
241
|
+
recovery_type: Type of recovery operation
|
|
242
|
+
**kwargs: Additional parameters
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
dict: Recovery result with format:
|
|
246
|
+
{
|
|
247
|
+
"status": "success|error|warning",
|
|
248
|
+
"message": "Human readable message",
|
|
249
|
+
"execution_time": float, # optional
|
|
250
|
+
"details": dict # optional additional details
|
|
251
|
+
}
|
|
252
|
+
"""
|
|
253
|
+
raise NotImplementedError
|
|
254
|
+
|
|
255
|
+
def _get_supported_recovery_operations_impl(self) -> list:
|
|
256
|
+
"""
|
|
257
|
+
Implementation of supported recovery operations listing
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
list: List of supported recovery operation names
|
|
261
|
+
"""
|
|
262
|
+
raise NotImplementedError
|
|
263
|
+
|
|
264
|
+
def _setup_acquisition(self, device: Device):
|
|
265
|
+
"""设备特定的采集初始化"""
|
|
266
|
+
pass
|
|
267
|
+
|
|
268
|
+
def _cleanup_acquisition(self, device: Device):
|
|
269
|
+
"""设备特定的采集清理"""
|
|
270
|
+
pass
|
|
271
|
+
|
|
272
|
+
def _acquisition_loop(self):
|
|
273
|
+
"""数据采集循环的具体实现"""
|
|
274
|
+
raise NotImplementedError
|
|
275
|
+
|
|
276
|
+
def get_device(self, device_id: str) -> Optional[Device]:
|
|
277
|
+
"""获取设备实例"""
|
|
278
|
+
return self._devices.get(device_id)
|
|
279
|
+
|
|
280
|
+
def _register_device(self, device: Device):
|
|
281
|
+
"""注册设备"""
|
|
282
|
+
self._devices[device.device_id] = device
|
|
283
|
+
|
|
284
|
+
def __del__(self):
|
|
285
|
+
"""Cleanup when object is destroyed"""
|
|
286
|
+
try:
|
|
287
|
+
if self.device and hasattr(self, 'state') and self.state != DeviceState.DISCONNECTED:
|
|
288
|
+
self.close(self.device)
|
|
289
|
+
except Exception as e:
|
|
290
|
+
logger.error(f"Error during cleanup: {e}")
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DEPRECATED: This module is deprecated and will be removed in a future version.
|
|
3
|
+
|
|
4
|
+
Device configuration is now managed entirely through the SQLite database.
|
|
5
|
+
Use an application-layer adapter (e.g. Django/SQLAlchemy) instead.
|
|
6
|
+
|
|
7
|
+
For importing devices from JSON, use the 'device_import' CLI command.
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
import json
|
|
11
|
+
import warnings
|
|
12
|
+
from typing import Dict, Optional
|
|
13
|
+
import logging
|
|
14
|
+
from iotsploit_core.domain.device import Device, DeviceType
|
|
15
|
+
from json import JSONEncoder
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
# Emit deprecation warning when module is imported
|
|
20
|
+
warnings.warn(
|
|
21
|
+
"device_config module is deprecated. Device configuration is now managed through the database. "
|
|
22
|
+
"Use an application-layer adapter (e.g. Django/SQLAlchemy) instead.",
|
|
23
|
+
DeprecationWarning,
|
|
24
|
+
stacklevel=2
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
class DeviceJSONEncoder(JSONEncoder):
|
|
28
|
+
"""自定义JSON编码器,处理特殊类型的序列化
|
|
29
|
+
|
|
30
|
+
DEPRECATED: This class is deprecated. Use database storage instead.
|
|
31
|
+
"""
|
|
32
|
+
def default(self, obj):
|
|
33
|
+
if isinstance(obj, DeviceType):
|
|
34
|
+
return obj.value
|
|
35
|
+
if isinstance(obj, Device):
|
|
36
|
+
# 使用 DeviceStore 中的序列化方法
|
|
37
|
+
from iotsploit_core.core.device_store import DeviceStore
|
|
38
|
+
return DeviceStore()._device_to_dict(obj)
|
|
39
|
+
return super().default(obj)
|
|
40
|
+
|
|
41
|
+
class DeviceConfigManager:
|
|
42
|
+
"""设备配置管理器
|
|
43
|
+
|
|
44
|
+
DEPRECATED: This class is deprecated.
|
|
45
|
+
Device configuration is now managed entirely through the SQLite database.
|
|
46
|
+
Use an application-layer adapter (e.g. Django/SQLAlchemy) for device operations.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self):
|
|
50
|
+
warnings.warn(
|
|
51
|
+
"DeviceConfigManager is deprecated. Use an application-layer adapter (e.g. Django/SQLAlchemy) instead.",
|
|
52
|
+
DeprecationWarning,
|
|
53
|
+
stacklevel=2
|
|
54
|
+
)
|
|
55
|
+
self.config_file = "conf/devices.json"
|
|
56
|
+
|
|
57
|
+
def load_configs(self) -> Dict[str, Dict]:
|
|
58
|
+
"""加载设备配置"""
|
|
59
|
+
if not os.path.exists(self.config_file):
|
|
60
|
+
return {"devices": []}
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
with open(self.config_file, 'r') as f:
|
|
64
|
+
return json.load(f)
|
|
65
|
+
except json.JSONDecodeError as e:
|
|
66
|
+
logger.error(f"Error decoding JSON from {self.config_file}: {e}")
|
|
67
|
+
return {"devices": []}
|
|
68
|
+
except Exception as e:
|
|
69
|
+
logger.error(f"Error loading config file {self.config_file}: {e}")
|
|
70
|
+
return {"devices": []}
|
|
71
|
+
|
|
72
|
+
def save_device_config(self, device_dict: Dict):
|
|
73
|
+
"""保存设备配置到持久化存储"""
|
|
74
|
+
try:
|
|
75
|
+
configs = self.load_configs()
|
|
76
|
+
# 确保configs中有devices键
|
|
77
|
+
if 'devices' not in configs:
|
|
78
|
+
configs['devices'] = []
|
|
79
|
+
|
|
80
|
+
# 更新或添加设备配置
|
|
81
|
+
device_id = device_dict['device_id']
|
|
82
|
+
device_updated = False
|
|
83
|
+
for i, device in enumerate(configs['devices']):
|
|
84
|
+
if device.get('device_id') == device_id:
|
|
85
|
+
configs['devices'][i] = device_dict
|
|
86
|
+
device_updated = True
|
|
87
|
+
break
|
|
88
|
+
|
|
89
|
+
if not device_updated:
|
|
90
|
+
configs['devices'].append(device_dict)
|
|
91
|
+
|
|
92
|
+
os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
|
|
93
|
+
with open(self.config_file, 'w') as f:
|
|
94
|
+
json.dump(configs, f, indent=2, cls=DeviceJSONEncoder)
|
|
95
|
+
|
|
96
|
+
except Exception as e:
|
|
97
|
+
logger.error(f"Error saving device configuration: {e}")
|
|
98
|
+
|
|
99
|
+
def get_device_config(self, device_id: str) -> Optional[Dict]:
|
|
100
|
+
"""获取特定设备的配置"""
|
|
101
|
+
configs = self.load_configs()
|
|
102
|
+
for device in configs.get('devices', []):
|
|
103
|
+
if device.get('device_id') == device_id:
|
|
104
|
+
return device
|
|
105
|
+
return None
|