atomicshop 2.21.1__py3-none-any.whl → 3.0.0__py3-none-any.whl

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.

Potentially problematic release.


This version of atomicshop might be problematic. Click here for more details.

Files changed (29) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/basics/multiprocesses.py +228 -30
  3. atomicshop/dns.py +2 -0
  4. atomicshop/mitm/config_static.py +2 -1
  5. atomicshop/mitm/engines/create_module_template.py +2 -7
  6. atomicshop/mitm/import_config.py +30 -26
  7. atomicshop/mitm/initialize_engines.py +9 -24
  8. atomicshop/mitm/mitm_main.py +187 -59
  9. atomicshop/networks.py +448 -0
  10. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  11. atomicshop/wrappers/dockerw/dockerw.py +17 -21
  12. atomicshop/wrappers/mongodbw/mongodbw.py +1 -0
  13. atomicshop/wrappers/psutilw/{networks.py → psutil_networks.py} +3 -1
  14. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +76 -0
  15. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +262 -0
  16. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +51 -82
  17. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +235 -0
  18. atomicshop/wrappers/socketw/accepter.py +15 -1
  19. atomicshop/wrappers/socketw/creator.py +7 -1
  20. atomicshop/wrappers/socketw/dns_server.py +33 -39
  21. atomicshop/wrappers/socketw/exception_wrapper.py +20 -11
  22. atomicshop/wrappers/socketw/socket_wrapper.py +29 -78
  23. atomicshop/wrappers/winregw/winreg_network.py +20 -0
  24. {atomicshop-2.21.1.dist-info → atomicshop-3.0.0.dist-info}/METADATA +2 -1
  25. {atomicshop-2.21.1.dist-info → atomicshop-3.0.0.dist-info}/RECORD +28 -24
  26. atomicshop/wrappers/pywin32w/wmis/helpers.py +0 -131
  27. {atomicshop-2.21.1.dist-info → atomicshop-3.0.0.dist-info}/LICENSE.txt +0 -0
  28. {atomicshop-2.21.1.dist-info → atomicshop-3.0.0.dist-info}/WHEEL +0 -0
  29. {atomicshop-2.21.1.dist-info → atomicshop-3.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,466 @@
1
+ import ctypes
2
+ from ctypes import wintypes
3
+ import uuid
4
+
5
+
6
+ # Constants and functions for creating root-enumerated device nodes
7
+ # ---------------------------------------------------------------------------
8
+ # Compatibility shim: wintypes.ULONG_PTR is missing on some builds
9
+ # ---------------------------------------------------------------------------
10
+ if not hasattr(wintypes, "ULONG_PTR"):
11
+ if ctypes.sizeof(ctypes.c_void_p) == 8: # 64-bit Python
12
+ wintypes.ULONG_PTR = ctypes.c_uint64
13
+ else: # 32-bit Python
14
+ wintypes.ULONG_PTR = ctypes.c_uint32
15
+
16
+ # ------------------------------------------------------------------
17
+ # SetupDi* “device‑registry property” indices (SPDRP_…)
18
+ # From Microsoft’s setupapi.h – keep them as ints.
19
+ # ------------------------------------------------------------------
20
+ SPDRP_DEVICEDESC = 0 # REG_SZ – Device description (friendly name)
21
+ SPDRP_HARDWAREID = 1 # REG_MULTI_SZ – Hardware‑ID list
22
+ SPDRP_COMPATIBLEIDS = 2 # REG_MULTI_SZ – Compatible‑ID list
23
+ SPDRP_SERVICE = 4 # REG_SZ – Service/miniport to load
24
+ SPDRP_CLASS = 7 # REG_SZ – Class name (e.g. "Net")
25
+ SPDRP_CLASSGUID = 8 # REG_SZ – Class GUID in string form
26
+
27
+ # newdev.h (Windows SDK) / SetupAPI
28
+ DIF_REGISTERDEVICE = 0x00000019
29
+ DIF_REMOVE = 0x00000005
30
+ DICD_GENERATE_ID = 0x00000001
31
+ INSTALLFLAG_FORCE = 0x00000001 # install even if “better” driver exists
32
+ INSTALLFLAG_READONLY = 0x00000002 # don’t write driver to driver store
33
+ INSTALLFLAG_NONINTERACTIVE = 0x00000004 # never display UI (silent mode)
34
+
35
+ DIGCF_PRESENT = 0x00000002
36
+ ERROR_NO_MORE_ITEMS = 259
37
+
38
+ setupapi = ctypes.WinDLL("setupapi", use_last_error=True)
39
+ newdev = ctypes.WinDLL("newdev", use_last_error=True)
40
+
41
+ # ---------------------------------------------------------------------------
42
+ # Structures & prototypes
43
+ # ---------------------------------------------------------------------------
44
+ class SP_DEVINFO_DATA(ctypes.Structure):
45
+ _fields_ = [
46
+ ("cbSize", wintypes.DWORD),
47
+ ("ClassGuid", ctypes.c_byte * 16),
48
+ ("DevInst", wintypes.DWORD),
49
+ ("Reserved", wintypes.ULONG_PTR),
50
+ ]
51
+
52
+ # --- creation helpers ------------------------------------------------------
53
+ SetupDiCreateDeviceInfoList = setupapi.SetupDiCreateDeviceInfoList
54
+ SetupDiCreateDeviceInfoList.argtypes = [ctypes.POINTER(ctypes.c_byte * 16), wintypes.HWND]
55
+ SetupDiCreateDeviceInfoList.restype = wintypes.HANDLE
56
+
57
+ SetupDiCreateDeviceInfoW = setupapi.SetupDiCreateDeviceInfoW
58
+ SetupDiCreateDeviceInfoW.argtypes = [
59
+ wintypes.HANDLE, wintypes.LPCWSTR,
60
+ ctypes.POINTER(ctypes.c_byte * 16),
61
+ wintypes.LPCWSTR, wintypes.HWND, wintypes.DWORD,
62
+ ctypes.POINTER(SP_DEVINFO_DATA)
63
+ ]
64
+ SetupDiCreateDeviceInfoW.restype = wintypes.BOOL
65
+
66
+ SetupDiSetDeviceRegistryPropertyW = setupapi.SetupDiSetDeviceRegistryPropertyW
67
+ SetupDiSetDeviceRegistryPropertyW.argtypes = [
68
+ wintypes.HANDLE, ctypes.POINTER(SP_DEVINFO_DATA), wintypes.DWORD,
69
+ wintypes.LPBYTE, wintypes.DWORD
70
+ ]
71
+ SetupDiSetDeviceRegistryPropertyW.restype = wintypes.BOOL
72
+
73
+ SetupDiCallClassInstaller = setupapi.SetupDiCallClassInstaller
74
+ SetupDiCallClassInstaller.argtypes = [
75
+ wintypes.DWORD, wintypes.HANDLE, ctypes.POINTER(SP_DEVINFO_DATA)
76
+ ]
77
+ SetupDiCallClassInstaller.restype = wintypes.BOOL
78
+
79
+ # --- enumeration / removal -------------------------------------------------
80
+ SetupDiGetClassDevsW = setupapi.SetupDiGetClassDevsW
81
+ SetupDiGetClassDevsW.argtypes = [ctypes.POINTER(ctypes.c_byte * 16),
82
+ wintypes.LPCWSTR, wintypes.HWND, wintypes.DWORD]
83
+ SetupDiGetClassDevsW.restype = wintypes.HANDLE
84
+
85
+ SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
86
+ SetupDiEnumDeviceInfo.argtypes = [wintypes.HANDLE, wintypes.DWORD,
87
+ ctypes.POINTER(SP_DEVINFO_DATA)]
88
+ SetupDiEnumDeviceInfo.restype = wintypes.BOOL
89
+
90
+ SetupDiGetDeviceRegistryPropertyW = setupapi.SetupDiGetDeviceRegistryPropertyW
91
+ SetupDiGetDeviceRegistryPropertyW.argtypes = [
92
+ wintypes.HANDLE, ctypes.POINTER(SP_DEVINFO_DATA), wintypes.DWORD,
93
+ ctypes.POINTER(wintypes.DWORD), wintypes.PBYTE, wintypes.DWORD,
94
+ ctypes.POINTER(wintypes.DWORD)
95
+ ]
96
+ SetupDiGetDeviceRegistryPropertyW.restype = wintypes.BOOL
97
+
98
+ SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
99
+ SetupDiDestroyDeviceInfoList.argtypes = [wintypes.HANDLE]
100
+ SetupDiDestroyDeviceInfoList.restype = wintypes.BOOL
101
+
102
+ UpdateDriverForPlugAndPlayDevicesW = newdev.UpdateDriverForPlugAndPlayDevicesW
103
+ UpdateDriverForPlugAndPlayDevicesW.argtypes = [
104
+ wintypes.HWND, wintypes.LPCWSTR, wintypes.LPCWSTR,
105
+ wintypes.DWORD, ctypes.POINTER(wintypes.BOOL)
106
+ ]
107
+ UpdateDriverForPlugAndPlayDevicesW.restype = wintypes.BOOL
108
+
109
+
110
+ # ---------------------------------------------------------------------------
111
+ # 1. Create a root-enumerated devnode (idempotent)
112
+ # ---------------------------------------------------------------------------
113
+ def create_root_enumerated_devnode(
114
+ class_guid: str,
115
+ friendly_name: str, # what shows in Device Manager
116
+ hardware_ids: "list[str] | str",
117
+ compatible_ids: "list[str] | str | None" = None,
118
+ devdesc_override: str | None = None,
119
+ create_flags: int = DICD_GENERATE_ID,
120
+ existing_ok: bool = True,
121
+ ) -> None:
122
+ """
123
+ Programmatically create a *root‑enumerated* device node, set its
124
+ Hardware‑ID (and optional Compatible‑ID) list, then ask Plug and Play
125
+ to register/ install whatever driver matches those IDs.
126
+
127
+ Parameters
128
+ ----------
129
+ class_guid : string representation of a GUID.
130
+ Device‑class GUID (e.g. GUID_DEVCLASS_NET, GUID_DEVCLASS_MEDIA …).
131
+ Example:
132
+ class_guid="{4d36e972-e325-11ce-bfc1-08002be10318}"
133
+ This is the GUID for network adapters.
134
+
135
+ friendly_name : str
136
+ Initial instance name placed in the registry (DeviceDesc).
137
+ Also, will be shown in Device Manager.
138
+
139
+ hardware_ids : str | list[str]
140
+ One or more hardware IDs (MULTI_SZ). The *first* one is the key
141
+ identifier PnP uses when selecting an INF.
142
+
143
+ compatible_ids : str | list[str] | None
144
+ Optional Compatible‑ID list (another MULTI_SZ, lower priority).
145
+
146
+ devdesc_override : str | None
147
+ If supplied, written to SPDRP_DEVICEDESC (rarely necessary because
148
+ the INF’s own DeviceDesc usually replaces it).
149
+
150
+ create_flags : int
151
+ Flags for SetupDiCreateDeviceInfoW. Default is DICD_GENERATE_ID.
152
+
153
+ existing_ok : bool
154
+ If True, silently succeed when the devnode already exists.
155
+ """
156
+
157
+ class_guid_bytes = uuid.UUID(class_guid).bytes_le
158
+ class_guid_object = (ctypes.c_byte * 16).from_buffer_copy(class_guid_bytes)
159
+
160
+ # --- 1. Create a temporary empty device‑info set -------------------
161
+ hdi = SetupDiCreateDeviceInfoList(class_guid_object, None) # Open a new, empty set
162
+ if hdi == wintypes.HANDLE(-1).value: # INVALID_HANDLE_VALUE?
163
+ raise ctypes.WinError(ctypes.get_last_error()) # Bail out on failure
164
+
165
+ # Prepare the SP_DEVINFO_DATA structure -----------------------------
166
+ devinfo = SP_DEVINFO_DATA() # Zero‑initialised struct
167
+ devinfo.cbSize = ctypes.sizeof(devinfo) # Must set cbSize field
168
+
169
+ # --- 2. Create (or open) the devnode itself ------------------------
170
+ if not SetupDiCreateDeviceInfoW(
171
+ hdi, # Info‑set handle
172
+ friendly_name, # Instance name
173
+ class_guid_object, # Class GUID
174
+ None, None, # (Description, parent window)
175
+ create_flags, # e.g. DICD_GENERATE_ID
176
+ ctypes.byref(devinfo) # Receives devinfo data
177
+ ):
178
+ err = ctypes.get_last_error() # Capture error now
179
+ SetupDiDestroyDeviceInfoList(hdi) # Clean up handle
180
+ if not (existing_ok and err == 0xE0000217): # ERROR_DEVINST_ALREADY_EXISTS
181
+ raise ctypes.WinError(err) # Re‑raise unless allowed
182
+
183
+ # --- 3. Build MULTI_SZ buffers and write registry properties -------
184
+ def _multisz(lst_or_str): # Helper → MULTI_SZ buffer
185
+ buf = lst_or_str if isinstance(lst_or_str, str) else "\0".join(lst_or_str)
186
+ return ctypes.create_unicode_buffer(buf + "\0") # Extra trailing NUL
187
+
188
+ # Hardware‑ID list (required) ---------------------------
189
+ hwid = _multisz(hardware_ids) # Build MULTI_SZ buffer
190
+ if not SetupDiSetDeviceRegistryPropertyW(
191
+ hdi, ctypes.byref(devinfo), SPDRP_HARDWAREID, # Property to set
192
+ ctypes.cast(hwid, wintypes.LPBYTE), # Cast to LPBYTE
193
+ (len(hwid) + 1) * ctypes.sizeof(ctypes.c_wchar) # Size in bytes
194
+ ):
195
+ SetupDiDestroyDeviceInfoList(hdi)
196
+ raise ctypes.WinError(ctypes.get_last_error())
197
+
198
+ # Compatible‑ID list (optional) -------------------------
199
+ if compatible_ids:
200
+ cid = _multisz(compatible_ids) # Build MULTI_SZ buffer
201
+ if not SetupDiSetDeviceRegistryPropertyW(
202
+ hdi, ctypes.byref(devinfo), SPDRP_COMPATIBLEIDS,
203
+ ctypes.cast(cid, wintypes.LPBYTE),
204
+ (len(cid) + 1) * ctypes.sizeof(ctypes.c_wchar)
205
+ ):
206
+ SetupDiDestroyDeviceInfoList(hdi)
207
+ raise ctypes.WinError(ctypes.get_last_error())
208
+
209
+ # DeviceDesc override (optional) -----------------------
210
+ if devdesc_override:
211
+ desc = ctypes.create_unicode_buffer(devdesc_override + "\0")
212
+ if not SetupDiSetDeviceRegistryPropertyW(
213
+ hdi, ctypes.byref(devinfo), SPDRP_DEVICEDESC,
214
+ ctypes.cast(desc, wintypes.LPBYTE),
215
+ (len(desc) + 1) * ctypes.sizeof(ctypes.c_wchar)
216
+ ):
217
+ SetupDiDestroyDeviceInfoList(hdi)
218
+ raise ctypes.WinError(ctypes.get_last_error())
219
+
220
+ # --- 4. Hand the devnode to the class installer --------------------
221
+ if not SetupDiCallClassInstaller(
222
+ DIF_REGISTERDEVICE, # “Install this device”
223
+ hdi, ctypes.byref(devinfo)
224
+ ):
225
+ err = ctypes.get_last_error()
226
+ # ERROR_DI_DO_DEFAULT means “already registered / nothing to do”
227
+ if not (existing_ok and err == 0xE000020E):
228
+ SetupDiDestroyDeviceInfoList(hdi)
229
+ raise ctypes.WinError(err)
230
+
231
+ # --- 5. Final cleanup ----------------------------------------------
232
+ SetupDiDestroyDeviceInfoList(hdi) # Always release handle
233
+
234
+
235
+ # ---------------------------------------------------------------------------
236
+ # 2. Bind driver from netloop.inf (idempotent)
237
+ # ---------------------------------------------------------------------------
238
+ def update_driver_for_hwids(
239
+ hardware_ids: "str | list[str]",
240
+ inf_path: str,
241
+ force_install: bool = False,
242
+ quiet: bool = True,
243
+ existing_ok: bool = True,
244
+ ) -> bool:
245
+ """
246
+ Install / update the driver in *inf_path* for every present device whose first
247
+ Hardware‑ID matches *hardware_ids*.
248
+
249
+ Parameters
250
+ ----------
251
+ hardware_ids : str | list[str]
252
+ Single Hardware‑ID string or a list of IDs. Each is passed separately to
253
+ UpdateDriverForPlugAndPlayDevicesW.
254
+
255
+ inf_path : str
256
+ Full path to the target driver’s .INF file (must already be accessible or
257
+ pre‑staged).
258
+
259
+ force_install : bool, default False
260
+ If True the function sets INSTALLFLAG_FORCE so the specified INF will be
261
+ applied even when Windows thinks a “better” driver is already installed.
262
+
263
+ quiet : bool, default True
264
+ If True the installation runs without UI (parent window = NULL). Set
265
+ False if you want progress dialogs.
266
+
267
+ existing_ok : bool, default True
268
+ When *False*, a return code of ERROR_NO_MORE_ITEMS (no devices found) or
269
+ ERROR_DI_DO_DEFAULT (“already using this driver”) is treated as an error.
270
+
271
+ Returns
272
+ -------
273
+ bool
274
+ True → Windows signalled that a reboot is required.
275
+ False → No reboot required.
276
+
277
+ =====================================================================
278
+
279
+ Examples
280
+ --------
281
+ 1. Force‑install the Microsoft KM‑Test Loopback driver that ships with
282
+ Windows (the same scenario as the original hard‑coded function)::
283
+
284
+ reboot_needed = update_driver_for_hwids(
285
+ hardware_ids="*ROOT\\NET\\0000",
286
+ inf_path=r"C:\\Windows\\INF\\netloop.inf",
287
+ force_install=True
288
+ )
289
+
290
+ 2. Install an Intel i219‑V NIC driver only if Windows agrees it is the best
291
+ match (no force flag) for either of two possible PCI IDs::
292
+
293
+ reboot_needed = update_driver_for_hwids(
294
+ hardware_ids=[
295
+ "PCI\\VEN_8086&DEV_15B8",
296
+ "PCI\\VEN_8086&DEV_15BB"
297
+ ],
298
+ inf_path=r"D:\\Drivers\\PRO1000\\e1r.inf"
299
+ )
300
+ """
301
+ # Normalise to a Python list for uniform processing
302
+ ids: list[str] = [hardware_ids] if isinstance(hardware_ids, str) else list(hardware_ids)
303
+
304
+ # Build the flag word sent to UpdateDriverForPlugAndPlayDevicesW
305
+ flags = INSTALLFLAG_FORCE if force_install else 0
306
+ if quiet:
307
+ flags |= INSTALLFLAG_NONINTERACTIVE
308
+
309
+ any_reboot = False # track whether *any* call needs reboot
310
+ for hid in ids:
311
+ reboot = wintypes.BOOL(False)
312
+ ok = UpdateDriverForPlugAndPlayDevicesW(
313
+ None, # hwndParent → silent (we add INSTALLFLAG_NONINTERACTIVE)
314
+ hid, # first Hardware‑ID to match
315
+ inf_path, # target driver INF
316
+ flags, # INSTALLFLAG_* bitmask
317
+ ctypes.byref(reboot) # tells us if reboot required
318
+ )
319
+ if not ok:
320
+ err = ctypes.get_last_error()
321
+ # ERROR_NO_MORE_ITEMS (0xE000020B): no matching devices present
322
+ # ERROR_DI_DO_DEFAULT (0xE000020E): already using this driver
323
+ benign = {0xE000020B, 0xE000020E}
324
+ if not (existing_ok and err in benign):
325
+ raise ctypes.WinError(err)
326
+ any_reboot |= bool(reboot.value)
327
+
328
+ return any_reboot
329
+
330
+
331
+ def add_device(
332
+ class_guid: str,
333
+ friendly_name: str,
334
+ hardware_ids: "list[str] | str",
335
+ inf_path: str,
336
+ compatible_ids: "list[str] | str | None" = None,
337
+ devdesc_override: str | None = None,
338
+ create_flags: int = DICD_GENERATE_ID,
339
+ existing_ok: bool = True,
340
+ force_install: bool = False,
341
+ quiet: bool = True
342
+ ) -> None:
343
+ """
344
+ Create a root-enumerated device node and bind a driver to it.
345
+ This is a wrapper around the two functions create_root_enumerated_devnode() and
346
+ update_driver_for_hwids().
347
+
348
+ This adds the device to the system and binds the driver to it.
349
+
350
+ :param class_guid: string representation of a GUID.
351
+ Device class GUID (e.g. GUID_DEVCLASS_NET, GUID_DEVCLASS_MEDIA …).
352
+ Example:
353
+ class_guid="{4d36e972-e325-11ce-bfc1-08002be10318}"
354
+ This is the GUID for network adapters.
355
+ :param friendly_name: str
356
+ Initial instance name placed in the registry (DeviceDesc).
357
+ Also, will be shown in Device Manager.
358
+ :param hardware_ids: str | list[str]
359
+ One or more hardware IDs (MULTI_SZ). The *first* one is the key
360
+ identifier PnP uses when selecting an INF.
361
+ :param inf_path: str
362
+ Full path to the target driver’s .INF file (must already be accessible or
363
+ pre-staged).
364
+ :param compatible_ids: str | list[str] | None
365
+ Optional Compatible-ID list (another MULTI_SZ, lower priority).
366
+ :param devdesc_override: str | None
367
+ If supplied, written to SPDRP_DEVICEDESC (rarely necessary because
368
+ the INF’s own DeviceDesc usually replaces it).
369
+ :param create_flags: int
370
+ Flags for SetupDiCreateDeviceInfoW. Default is DICD_GENERATE_ID.
371
+ :param existing_ok: bool
372
+ If True, silently succeed when the devnode already exists.
373
+ :param force_install: bool, default False
374
+ If True the function sets INSTALLFLAG_FORCE so the specified INF will be
375
+ applied even when Windows thinks a “better” driver is already installed.
376
+ :param quiet: bool, default True
377
+ If True the installation runs without UI (parent window = NULL). Set
378
+ False if you want progress dialogs.
379
+ :return: None
380
+
381
+ =====================================================================
382
+ Examples
383
+ --------
384
+
385
+
386
+
387
+ """
388
+ create_root_enumerated_devnode(
389
+ class_guid=class_guid,
390
+ friendly_name=friendly_name,
391
+ hardware_ids=hardware_ids,
392
+ compatible_ids=compatible_ids,
393
+ devdesc_override=devdesc_override,
394
+ create_flags=create_flags,
395
+ existing_ok=existing_ok
396
+ )
397
+
398
+ update_driver_for_hwids(
399
+ hardware_ids=hardware_ids,
400
+ inf_path=inf_path,
401
+ force_install=force_install,
402
+ quiet=quiet,
403
+ existing_ok=existing_ok
404
+ )
405
+
406
+
407
+ def remove_device(
408
+ pnp_device_id: str,
409
+ class_guid: str
410
+ ) -> bool:
411
+ """
412
+ Delete the single device whose PNPDeviceID
413
+ equals the string you pass in (case-insensitive). Returns True on
414
+ success, False if no matching devnode was found.
415
+
416
+ :param pnp_device_id: PNPDeviceID of the device to remove.
417
+ If you're using the Win32_NetworkAdapter class, you can
418
+ get the PNPDeviceID from the object itself: network_config.PNPDeviceID
419
+ :param class_guid: string representation of the device class GUID.
420
+ Example: '{4d36e972-e325-11ce-bfc1-08002be10318}' for network adapters.
421
+ """
422
+
423
+ class_guid_bytes = uuid.UUID(class_guid).bytes_le
424
+ class_guid_object = (ctypes.c_byte * 16).from_buffer_copy(class_guid_bytes)
425
+
426
+ # Get only PRESENT devices in the Network class
427
+ hdi = SetupDiGetClassDevsW(class_guid_object, None, None, DIGCF_PRESENT)
428
+ if hdi == wintypes.HANDLE(-1).value:
429
+ raise ctypes.WinError(ctypes.get_last_error())
430
+
431
+ devinfo = SP_DEVINFO_DATA()
432
+ devinfo.cbSize = ctypes.sizeof(devinfo)
433
+ index = 0
434
+ removed = False
435
+
436
+ # Helper to fetch the instance-ID of the current element
437
+ instance_buf_len = 512
438
+ GetInstanceId = setupapi.SetupDiGetDeviceInstanceIdW
439
+ GetInstanceId.argtypes = [
440
+ wintypes.HANDLE, ctypes.POINTER(SP_DEVINFO_DATA),
441
+ wintypes.LPWSTR, wintypes.DWORD, ctypes.POINTER(wintypes.DWORD)
442
+ ]
443
+ GetInstanceId.restype = wintypes.BOOL
444
+ inst_buf = ctypes.create_unicode_buffer(instance_buf_len)
445
+
446
+ while SetupDiEnumDeviceInfo(hdi, index, ctypes.byref(devinfo)):
447
+ index += 1
448
+
449
+ if not GetInstanceId(hdi, ctypes.byref(devinfo),
450
+ inst_buf, instance_buf_len, None):
451
+ continue
452
+
453
+ if inst_buf.value.lower() != pnp_device_id.lower():
454
+ continue # not the target
455
+
456
+ # Found it → remove
457
+ if not SetupDiCallClassInstaller(DIF_REMOVE, hdi, ctypes.byref(devinfo)):
458
+ err = ctypes.get_last_error()
459
+ SetupDiDestroyDeviceInfoList(hdi)
460
+ raise ctypes.WinError(err)
461
+
462
+ removed = True
463
+ break
464
+
465
+ SetupDiDestroyDeviceInfoList(hdi)
466
+ return removed
@@ -172,6 +172,16 @@ def add_execution_permissions_for_file(image_id_or_name: str, file_path: str, pr
172
172
 
173
173
 
174
174
  def stop_remove_containers_by_image_name(image_name: str):
175
+ def stop_remove_container(container: Container):
176
+ """
177
+ Stop and remove a container.
178
+ :param container: Container, the docker container object.
179
+ :return:
180
+ """
181
+ if container.status == "running":
182
+ print_api(f"Stopping container: [{container.name}]. Short ID: [{container.short_id}]")
183
+ container.stop()
184
+ container.remove()
175
185
  """
176
186
  Remove all containers by image name.
177
187
  :param image_name: str, the name of the image.
@@ -179,8 +189,8 @@ def stop_remove_containers_by_image_name(image_name: str):
179
189
  """
180
190
  client = docker.from_env()
181
191
  all_containers = client.containers.list(all=True)
182
- for container in all_containers:
183
- if any(image_name in tag for tag in container.image.tags):
192
+ for current_container in all_containers:
193
+ if any(image_name in tag for tag in current_container.image.tags):
184
194
  if container.status == "running":
185
195
  print_api(f"Stopping container: [{container.name}]. Short ID: [{container.short_id}]")
186
196
  container.stop()
@@ -231,7 +241,7 @@ def start_container_without_stop(
231
241
  return client, container
232
242
 
233
243
 
234
- def run_command_in_running_container(container: Container, command: list) -> tuple[int, bytes, str]:
244
+ def run_command_in_running_container(container: Container, command: list) -> tuple[int, str]:
235
245
  """
236
246
  Run a command in a running container.
237
247
  :param container: Container, the docker container object.
@@ -240,22 +250,8 @@ def run_command_in_running_container(container: Container, command: list) -> tup
240
250
  """
241
251
 
242
252
  # Run the command inside the already running container
243
- exec_result = container.exec_run(cmd=command, stdout=True, stderr=True)
253
+ status_code, output_bytes = container.exec_run(cmd=command, stdout=True, stderr=True)
244
254
  # Capture logs
245
- output_text = exec_result.output.decode("utf-8", errors="replace")
246
- execution_result_message: str = str()
247
- if output_text:
248
- execution_result_message = f"Container execution result:\n{output_text}"
249
-
250
- if exec_result.exit_code != 0:
251
- # logging.warning(f"Extraction command returned code {exec_result.exit_code} for '{filename}'")
252
- code_message = f"Extraction command returned code {exec_result.exit_code}"
253
- else:
254
- code_message = "Extraction succeeded"
255
-
256
- if execution_result_message:
257
- execution_result_message += f"\n{code_message}"
258
- else:
259
- execution_result_message = code_message
260
-
261
- return exec_result.exit_code, exec_result.output, execution_result_message
255
+ output_text = output_bytes.decode("utf-8", errors="replace")
256
+
257
+ return status_code, output_text
@@ -859,6 +859,7 @@ def find(
859
859
  # if items:
860
860
  # collection_items = collection_items.skip(skip_items).limit(items)
861
861
 
862
+ # List consolidates the results into a list of dictionaries, collection_items cursor will not be available after this.
862
863
  entries: list[dict] = list(collection_items)
863
864
 
864
865
  if entries and convert_object_id_to_str and '_id' in entries[0]:
@@ -4,6 +4,8 @@ import socket
4
4
 
5
5
  import psutil
6
6
 
7
+ from ... import networks
8
+
7
9
 
8
10
  def get_process_using_port(ip_port: str) -> Union[dict, None]:
9
11
  """
@@ -61,7 +63,7 @@ def get_default_connection_name() -> Union[dict, None]:
61
63
  """
62
64
  # Get all interfaces.
63
65
  interfaces: dict = psutil.net_if_addrs()
64
- default_ip_address: str = socket.gethostbyname(socket.gethostname())
66
+ default_ip_address: str = networks.get_default_internet_ipv4()
65
67
 
66
68
  for interface, details in interfaces.items():
67
69
  for address in details:
@@ -0,0 +1,76 @@
1
+ from win32com.client import CDispatch
2
+
3
+ from . import wmi_helpers
4
+
5
+
6
+ def set_skip_as_source(
7
+ ip_addresses: list[str],
8
+ enable: bool = True,
9
+ wmi_instance: CDispatch = None
10
+ ) -> None:
11
+ """
12
+ Toggle SkipAsSource for every address in *ip_addrs*.
13
+
14
+ Parameters
15
+ ----------
16
+ ip_addresses : list/tuple/iterable of str
17
+ One or more literal IP strings, e.g. "192.168.157.3"
18
+ enable : bool
19
+ True → behave like Set‑NetIPAddress ‑SkipAsSource $true
20
+ False → behave like Set‑NetIPAddress ‑SkipAsSource $false
21
+ wmi_instance : CDispatch
22
+ WMI instance to use. If not provided, a new one will be created.
23
+ 'root\\StandardCimv2'
24
+
25
+ ================
26
+
27
+ Explanation.
28
+ When you add extra IPv4 addresses to the same NIC, Windows treats them all as “first‑class” unless you tell it otherwise.
29
+ Because the two new addresses (192.168.157.3 and .4) are numerically lower than the original one (.129), the TCP/IP stack now prefers one of those lower addresses as the default source for any socket whose program didn’t bind an explicit local IP.
30
+
31
+ What that looks like on the wire
32
+ Client sends SYN → 192.168.157.3 (or .4).
33
+ – Server replies with SYN/ACK ←192.168.157.3 → handshake completes, HTTP works.
34
+
35
+ Client sends SYN → 192.168.157.129.
36
+ – Stack still picks .3 as its favourite and answers SYN/ACK ← 192.168.157.3.
37
+ – Client discards the packet (wrong IP), retransmits the SYN, your code’s accept() wakes up again, and you see an “infinite accept loop”.
38
+
39
+ The flag that fixes it: SkipAsSource
40
+ Tell Windows not to use the extra addresses unless an application asks for them.
41
+
42
+ PowerShell.
43
+ # One‑off: mark the addresses you already added
44
+ Get-NetIPAddress -IPAddress 192.168.157.3 | Set-NetIPAddress -SkipAsSource $true
45
+ Get-NetIPAddress -IPAddress 192.168.157.4 | Set-NetIPAddress -SkipAsSource $true
46
+
47
+ # —OR— add new addresses the right way from the start
48
+ New-NetIPAddress -InterfaceAlias "Ethernet0" `
49
+ -IPAddress 192.168.157.3 `
50
+ -PrefixLength 24 `
51
+ -SkipAsSource $true
52
+ SkipAsSource = $true keeps the address fully routable for incoming traffic and lets programs bind to it explicitly.
53
+
54
+ Windows will never choose that address as the source of an outgoing packet unless the program bound the socket to it.
55
+
56
+ After you flip the flag (no reboot required) the three‑way handshake is symmetrical again and the endless accept() loop disappears.
57
+ """
58
+
59
+ if not wmi_instance:
60
+ wmi_instance, _ = wmi_helpers.get_wmi_instance(namespace='root\\StandardCimv2')
61
+
62
+ for ip in ip_addresses:
63
+ query = f"SELECT * FROM MSFT_NetIPAddress WHERE IPAddress='{ip}'"
64
+ matches = wmi_instance.ExecQuery(query)
65
+ if not matches:
66
+ print(f"[!] {ip}: no such address found")
67
+ continue
68
+
69
+ for obj in matches: # usually just one
70
+ if bool(obj.SkipAsSource) == enable:
71
+ print(f"[=] {ip}: SkipAsSource already {enable}")
72
+ continue
73
+
74
+ obj.SkipAsSource = enable
75
+ obj.Put_() # commit the change
76
+ print(f"[+] {ip}: SkipAsSource set to {enable}")