pymobiledevice3 4.27.7__tar.gz → 5.0.0__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.
Potentially problematic release.
This version of pymobiledevice3 might be problematic. Click here for more details.
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/PKG-INFO +1 -2
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/__main__.py +0 -1
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/_version.py +3 -3
- pymobiledevice3-5.0.0/pymobiledevice3/bonjour.py +402 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/bonjour.py +4 -4
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/lockdown.py +7 -2
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/remote.py +4 -3
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/lockdown.py +7 -12
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/tunnel_service.py +16 -8
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/utils.py +2 -2
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/tunneld/server.py +118 -79
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/usbmux.py +16 -12
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/utils.py +1 -1
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3.egg-info/PKG-INFO +1 -2
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3.egg-info/requires.txt +0 -1
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/requirements.txt +0 -1
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/conftest.py +0 -1
- pymobiledevice3-4.27.7/pymobiledevice3/bonjour.py +0 -145
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.gitattributes +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.github/FUNDING.yml +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.github/pull_request_template.md +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.github/workflows/codeql.yml +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.github/workflows/generate-executable.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.github/workflows/markdown-lint.yml +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.github/workflows/python-app.yml +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.github/workflows/python-publish.yml +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.gitignore +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/.pre-commit-config.yaml +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/CODE_OF_CONDUCT.md +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/CONTRIBUTING.md +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/LICENSE +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/README.md +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/example.gif +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/markdownlint-config.json +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/misc/DTServices-14.2.txt +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/misc/DTServices-14.5.txt +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/misc/RemoteXPC.md +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/misc/plist_sniffer.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/misc/pyinstaller.md +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/misc/remotexpc_sniffer.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/misc/understanding_idevice_protocol_layers.md +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/misc/usbmux_sniff.sh +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/ca.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/activation.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/afc.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/amfi.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/apps.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/backup.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/cli_common.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/companion_proxy.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/completions.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/crash.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/developer.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/diagnostics.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/mounter.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/notification.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/pcap.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/power_assertion.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/processes.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/profile.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/provision.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/restore.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/springboard.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/syslog.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/usbmux.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/version.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/cli/webinspector.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/common.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/exceptions.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/irecv.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/irecv_devices.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/lockdown_service_provider.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/osu/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/osu/os_utils.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/osu/posix_util.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/osu/win_util.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/pair_records.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/common.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/core_device/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/core_device/app_service.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/core_device/core_device_service.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/core_device/device_info.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/core_device/diagnostics_service.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/core_device/file_service.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/module_imports.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/remote_service.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/remote_service_discovery.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/remotexpc.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/remote/xpc_message.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/dsc_uuid_map.json +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/dsc_uuid_map.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/firmware_notifications.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/notifications.txt +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/webinspector/element_attribute.js +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/webinspector/element_clear.js +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/webinspector/enter_fullscreen.js +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/webinspector/find_nodes.js +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/webinspector/focus.js +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/webinspector/get_attribute.js +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/webinspector/is_displayed.js +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/webinspector/is_editable.js +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/resources/webinspector/is_enabled.js +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/asr.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/base_restore.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/consts.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/device.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/fdr.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/ftab.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/img4.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/mbn.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/recovery.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/restore.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/restore_options.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/restored_client.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/restore/tss.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/service_connection.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/accessibilityaudit.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/afc.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/amfi.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/companion.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/crash_reports.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/debugserver_applist.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/device_arbitration.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/device_link.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/diagnostics.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dtfetchsymbols.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/application_listing.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/condition_inducer.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/device_info.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/energy_monitor.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/graphics.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/location_simulation.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/location_simulation_base.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/network_monitor.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/notifications.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/process_control.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/screenshot.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/instruments/sysmontap.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/dvt/testmanaged/xcuitest.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/file_relay.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/heartbeat.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/house_arrest.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/installation_proxy.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/lockdown_service.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/misagent.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/mobile_activation.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/mobile_config.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/mobile_image_mounter.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/mobilebackup2.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/notification_proxy.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/os_trace.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/pcapd.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/power_assertion.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/preboard.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/remote_fetch_symbols.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/remote_server.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/restore_service.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/screenshot.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/simulate_location.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/springboard.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/syslog.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/web_protocol/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/web_protocol/alert.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/web_protocol/automation_session.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/web_protocol/cdp_screencast.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/web_protocol/cdp_server.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/web_protocol/cdp_target.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/web_protocol/driver.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/web_protocol/element.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/web_protocol/inspector_session.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/web_protocol/selenium_api.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/web_protocol/session_protocol.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/web_protocol/switch_to.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/services/webinspector.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/tcp_forwarder.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/tunneld/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3/tunneld/api.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3.egg-info/SOURCES.txt +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3.egg-info/dependency_links.txt +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3.egg-info/entry_points.txt +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pymobiledevice3.egg-info/top_level.txt +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pyproject.toml +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/pytest.ini +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/setup.cfg +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/cli/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/cli/test_cli.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/instruments/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/instruments/test_core_profile_session.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/instruments/test_dvt_secure_socket_proxy.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/instruments/test_fetch_symbols.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/instruments/test_location.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/instruments/test_screenshot.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_accessibility.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_afc.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_apps.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_backup2.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_bonjour.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_crash_reports.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_list_devices.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_lockdown.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_pcapd.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_springboard_services_relay.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_start_tunnel.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_syslog_relay.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_tcp_forwarder.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_web_protocol/__init__.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_web_protocol/common.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_web_protocol/conftest.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_web_protocol/test_driver.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_web_protocol/test_element.py +0 -0
- {pymobiledevice3-4.27.7 → pymobiledevice3-5.0.0}/tests/services/test_webinspector.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pymobiledevice3
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.0
|
|
4
4
|
Summary: Pure python3 implementation for working with iDevices (iPhone, etc...)
|
|
5
5
|
Author-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
|
|
6
6
|
Maintainer-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
|
|
@@ -47,7 +47,6 @@ Requires-Dist: nest_asyncio>=1.5.5
|
|
|
47
47
|
Requires-Dist: Pillow
|
|
48
48
|
Requires-Dist: inquirer3>=0.6.0
|
|
49
49
|
Requires-Dist: ipsw_parser>=1.3.4
|
|
50
|
-
Requires-Dist: zeroconf>=0.132.2
|
|
51
50
|
Requires-Dist: ifaddr
|
|
52
51
|
Requires-Dist: hyperframe
|
|
53
52
|
Requires-Dist: srptools
|
|
@@ -27,7 +27,6 @@ coloredlogs.install(level=logging.INFO)
|
|
|
27
27
|
|
|
28
28
|
logging.getLogger('quic').disabled = True
|
|
29
29
|
logging.getLogger('asyncio').disabled = True
|
|
30
|
-
logging.getLogger('zeroconf').disabled = True
|
|
31
30
|
logging.getLogger('parso.cache').disabled = True
|
|
32
31
|
logging.getLogger('parso.cache.pickle').disabled = True
|
|
33
32
|
logging.getLogger('parso.python.diff').disabled = True
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '
|
|
32
|
-
__version_tuple__ = version_tuple = (
|
|
31
|
+
__version__ = version = '5.0.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (5, 0, 0)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'ga2babc5a5'
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
# Async, dependency-light mDNS browser returning dataclasses with per-address interface names.
|
|
2
|
+
# Works for any DNS-SD type, e.g. "_remoted._tcp.local."
|
|
3
|
+
# - Uses ifaddr (optional) to map IPs -> local interfaces; otherwise iface will be None.
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import ipaddress
|
|
7
|
+
import socket
|
|
8
|
+
import struct
|
|
9
|
+
import sys
|
|
10
|
+
from collections import defaultdict
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import Dict, List, Optional, Set, Tuple
|
|
13
|
+
|
|
14
|
+
import ifaddr # pip install ifaddr
|
|
15
|
+
|
|
16
|
+
from pymobiledevice3.osu.os_utils import get_os_utils
|
|
17
|
+
|
|
18
|
+
REMOTEPAIRING_SERVICE_NAME = '_remotepairing._tcp.local.'
|
|
19
|
+
REMOTEPAIRING_MANUAL_PAIRING_SERVICE_NAME = '_remotepairing-manual-pairing._tcp.local.'
|
|
20
|
+
MOBDEV2_SERVICE_NAME = '_apple-mobdev2._tcp.local.'
|
|
21
|
+
REMOTED_SERVICE_NAME = '_remoted._tcp.local.'
|
|
22
|
+
OSUTILS = get_os_utils()
|
|
23
|
+
DEFAULT_BONJOUR_TIMEOUT = OSUTILS.bonjour_timeout
|
|
24
|
+
|
|
25
|
+
MDNS_PORT = 5353
|
|
26
|
+
MDNS_MCAST_V4 = "224.0.0.251"
|
|
27
|
+
MDNS_MCAST_V6 = "ff02::fb"
|
|
28
|
+
|
|
29
|
+
QTYPE_A = 1
|
|
30
|
+
QTYPE_PTR = 12
|
|
31
|
+
QTYPE_TXT = 16
|
|
32
|
+
QTYPE_AAAA = 28
|
|
33
|
+
QTYPE_SRV = 33
|
|
34
|
+
|
|
35
|
+
CLASS_IN = 0x0001
|
|
36
|
+
CLASS_QU = 0x8000 # unicast-response bit (we use multicast queries)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# ---------------- Dataclasses ----------------
|
|
40
|
+
|
|
41
|
+
# --- Dataclass decorator shim (adds slots only on 3.10+)
|
|
42
|
+
def dataclass_compat(*d_args, **d_kwargs):
|
|
43
|
+
if sys.version_info < (3, 10):
|
|
44
|
+
d_kwargs.pop("slots", None) # ignore on 3.9
|
|
45
|
+
return dataclass(*d_args, **d_kwargs)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass_compat(slots=True)
|
|
49
|
+
class Address:
|
|
50
|
+
ip: str
|
|
51
|
+
iface: Optional[str] # local interface name (e.g., "en0"), or None if unknown
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def full_ip(self) -> str:
|
|
55
|
+
if self.iface and self.ip.lower().startswith("fe80:"):
|
|
56
|
+
return f"{self.ip}%{self.iface}"
|
|
57
|
+
return self.ip
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass_compat(slots=True)
|
|
61
|
+
class ServiceInstance:
|
|
62
|
+
instance: str # "<Instance Name>._type._proto.local."
|
|
63
|
+
host: Optional[str] # "host.local" (without trailing dot), or None if unresolved
|
|
64
|
+
port: Optional[int] # SRV port
|
|
65
|
+
addresses: List[Address] = field(default_factory=list) # IPs with interface names
|
|
66
|
+
properties: Dict[str, str] = field(default_factory=dict) # TXT key/values
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# ---------------- DNS helpers ----------------
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def encode_name(name: str) -> bytes:
|
|
73
|
+
name = name.rstrip(".")
|
|
74
|
+
out = bytearray()
|
|
75
|
+
for label in name.split(".") if name else []:
|
|
76
|
+
b = label.encode("utf-8")
|
|
77
|
+
if len(b) > 63:
|
|
78
|
+
raise ValueError("label too long")
|
|
79
|
+
out.append(len(b))
|
|
80
|
+
out += b
|
|
81
|
+
out.append(0)
|
|
82
|
+
return bytes(out)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def decode_name(data: bytes, off: int) -> Tuple[str, int]:
|
|
86
|
+
labels = []
|
|
87
|
+
jumped = False
|
|
88
|
+
orig_end = off
|
|
89
|
+
for _ in range(128): # loop guard
|
|
90
|
+
if off >= len(data):
|
|
91
|
+
break
|
|
92
|
+
length = data[off]
|
|
93
|
+
if length == 0:
|
|
94
|
+
off += 1
|
|
95
|
+
break
|
|
96
|
+
if (length & 0xC0) == 0xC0:
|
|
97
|
+
if off + 1 >= len(data):
|
|
98
|
+
raise ValueError("truncated name pointer")
|
|
99
|
+
ptr = ((length & 0x3F) << 8) | data[off + 1]
|
|
100
|
+
if ptr >= len(data):
|
|
101
|
+
raise ValueError("bad name pointer")
|
|
102
|
+
if not jumped:
|
|
103
|
+
orig_end = off + 2
|
|
104
|
+
off = ptr
|
|
105
|
+
jumped = True
|
|
106
|
+
continue
|
|
107
|
+
off += 1
|
|
108
|
+
end = off + length
|
|
109
|
+
if end > len(data):
|
|
110
|
+
raise ValueError("truncated label")
|
|
111
|
+
labels.append(data[off:end].decode("utf-8", errors="replace"))
|
|
112
|
+
off = end
|
|
113
|
+
return ".".join(labels) + ".", (orig_end if jumped else off)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def build_query(name: str, qtype: int, unicast: bool = False) -> bytes:
|
|
117
|
+
hdr = struct.pack("!HHHHHH", 0, 0, 1, 0, 0, 0) # TXID=0, flags=0, 1 question
|
|
118
|
+
qclass = CLASS_IN | (CLASS_QU if unicast else 0)
|
|
119
|
+
return hdr + encode_name(name) + struct.pack("!HH", qtype, qclass)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def parse_rr(data: bytes, off: int):
|
|
123
|
+
name, off = decode_name(data, off)
|
|
124
|
+
if off + 10 > len(data):
|
|
125
|
+
raise ValueError("truncated RR header")
|
|
126
|
+
rtype, rclass, ttl, rdlen = struct.unpack("!HHIH", data[off: off + 10])
|
|
127
|
+
off += 10
|
|
128
|
+
rdata = data[off: off + rdlen]
|
|
129
|
+
off += rdlen
|
|
130
|
+
|
|
131
|
+
rr = {"name": name, "type": rtype, "class": rclass & 0x7FFF, "ttl": ttl}
|
|
132
|
+
if rtype == QTYPE_PTR:
|
|
133
|
+
target, _ = decode_name(data, off - rdlen)
|
|
134
|
+
rr["ptrdname"] = target
|
|
135
|
+
elif rtype == QTYPE_SRV and rdlen >= 6:
|
|
136
|
+
priority, weight, port = struct.unpack("!HHH", rdata[:6])
|
|
137
|
+
target, _ = decode_name(data, off - rdlen + 6)
|
|
138
|
+
rr.update(
|
|
139
|
+
{"priority": priority, "weight": weight, "port": port, "target": target}
|
|
140
|
+
)
|
|
141
|
+
elif rtype == QTYPE_TXT:
|
|
142
|
+
kv = {}
|
|
143
|
+
i = 0
|
|
144
|
+
while i < rdlen:
|
|
145
|
+
b = rdata[i]
|
|
146
|
+
i += 1
|
|
147
|
+
seg = rdata[i: i + b]
|
|
148
|
+
i += b
|
|
149
|
+
if not seg:
|
|
150
|
+
continue
|
|
151
|
+
if b"=" in seg:
|
|
152
|
+
k, v = seg.split(b"=", 1)
|
|
153
|
+
kv[k.decode()] = v.decode(errors="replace")
|
|
154
|
+
else:
|
|
155
|
+
kv[seg.decode()] = ""
|
|
156
|
+
rr["txt"] = kv
|
|
157
|
+
elif rtype == QTYPE_A and rdlen == 4:
|
|
158
|
+
rr["address"] = socket.inet_ntop(socket.AF_INET, rdata)
|
|
159
|
+
elif rtype == QTYPE_AAAA and rdlen == 16:
|
|
160
|
+
rr["address"] = socket.inet_ntop(socket.AF_INET6, rdata)
|
|
161
|
+
else:
|
|
162
|
+
rr["raw"] = rdata
|
|
163
|
+
return rr, off
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def parse_mdns_message(data: bytes):
|
|
167
|
+
if len(data) < 12:
|
|
168
|
+
return []
|
|
169
|
+
_, _, qd, an, ns, ar = struct.unpack("!HHHHHH", data[:12])
|
|
170
|
+
off = 12
|
|
171
|
+
for _ in range(qd):
|
|
172
|
+
_, off = decode_name(data, off)
|
|
173
|
+
off += 4
|
|
174
|
+
rrs = []
|
|
175
|
+
for _ in range(an + ns + ar):
|
|
176
|
+
rr, off = parse_rr(data, off)
|
|
177
|
+
rrs.append(rr)
|
|
178
|
+
return rrs
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# ---------------- Interface mapping helpers ----------------
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class _Adapters:
|
|
185
|
+
def __init__(self):
|
|
186
|
+
self.adapters = ifaddr.get_adapters() if ifaddr is not None else []
|
|
187
|
+
|
|
188
|
+
def pick_iface_for_ip(
|
|
189
|
+
self, ip_str: str, family: int, v6_scopeid: Optional[int]
|
|
190
|
+
) -> Optional[str]:
|
|
191
|
+
# Prefer scope id for IPv6 link-local
|
|
192
|
+
if family == socket.AF_INET6 and ip_str.lower().startswith("fe80:"):
|
|
193
|
+
if v6_scopeid:
|
|
194
|
+
try:
|
|
195
|
+
return socket.if_indextoname(v6_scopeid)
|
|
196
|
+
except OSError:
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
# Otherwise, try to match destination ip to local subnet via ifaddr
|
|
200
|
+
if not self.adapters:
|
|
201
|
+
return None
|
|
202
|
+
ip = ipaddress.ip_address(ip_str)
|
|
203
|
+
best = (None, -1) # (name, prefix_len)
|
|
204
|
+
for ad in self.adapters:
|
|
205
|
+
for ipn in ad.ips:
|
|
206
|
+
if isinstance(ipn.ip, str):
|
|
207
|
+
# IPv4
|
|
208
|
+
fam = socket.AF_INET
|
|
209
|
+
ipn_ip = ipn.ip
|
|
210
|
+
else:
|
|
211
|
+
# IPv6
|
|
212
|
+
fam = socket.AF_INET6
|
|
213
|
+
ipn_ip = ipn.ip[0]
|
|
214
|
+
if fam != family:
|
|
215
|
+
continue
|
|
216
|
+
net = ipaddress.ip_network(
|
|
217
|
+
f"{ipn_ip}/{ipn.network_prefix}", strict=False
|
|
218
|
+
)
|
|
219
|
+
if ip in net and ipn.network_prefix > best[1]:
|
|
220
|
+
best = (ad.nice_name or ad.name, ipn.network_prefix)
|
|
221
|
+
return best[0]
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# ---------------- async sockets ----------------
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class _DatagramProtocol(asyncio.DatagramProtocol):
|
|
228
|
+
def __init__(self, queue: asyncio.Queue):
|
|
229
|
+
self.queue = queue
|
|
230
|
+
|
|
231
|
+
def datagram_received(self, data, addr):
|
|
232
|
+
# addr: IPv4 -> (host, port); IPv6 -> (host, port, flowinfo, scopeid)
|
|
233
|
+
self.queue.put_nowait((data, addr))
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
async def _bind_ipv4(queue: asyncio.Queue):
|
|
237
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
238
|
+
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
239
|
+
if hasattr(socket, "SO_REUSEPORT"):
|
|
240
|
+
try:
|
|
241
|
+
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
|
242
|
+
except OSError:
|
|
243
|
+
pass
|
|
244
|
+
s.bind(("0.0.0.0", MDNS_PORT))
|
|
245
|
+
try:
|
|
246
|
+
mreq = struct.pack(
|
|
247
|
+
"=4s4s", socket.inet_aton(MDNS_MCAST_V4), socket.inet_aton("0.0.0.0")
|
|
248
|
+
)
|
|
249
|
+
s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
|
|
250
|
+
except OSError:
|
|
251
|
+
pass
|
|
252
|
+
transport, _ = await asyncio.get_running_loop().create_datagram_endpoint(
|
|
253
|
+
lambda: _DatagramProtocol(queue), sock=s
|
|
254
|
+
)
|
|
255
|
+
return transport, s
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
async def _bind_ipv6_all_ifaces(queue: asyncio.Queue):
|
|
259
|
+
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
|
260
|
+
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
261
|
+
if hasattr(socket, "SO_REUSEPORT"):
|
|
262
|
+
try:
|
|
263
|
+
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
|
264
|
+
except OSError:
|
|
265
|
+
pass
|
|
266
|
+
s.bind(("::", MDNS_PORT))
|
|
267
|
+
grp = socket.inet_pton(socket.AF_INET6, MDNS_MCAST_V6)
|
|
268
|
+
for ifindex, _ in socket.if_nameindex():
|
|
269
|
+
mreq6 = grp + struct.pack("@I", ifindex)
|
|
270
|
+
try:
|
|
271
|
+
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq6)
|
|
272
|
+
except OSError:
|
|
273
|
+
continue
|
|
274
|
+
transport, _ = await asyncio.get_running_loop().create_datagram_endpoint(
|
|
275
|
+
lambda: _DatagramProtocol(queue), sock=s
|
|
276
|
+
)
|
|
277
|
+
return transport, s
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
async def _open_mdns_sockets():
|
|
281
|
+
queue = asyncio.Queue()
|
|
282
|
+
transports: List[Tuple[asyncio.BaseTransport, socket.socket]] = []
|
|
283
|
+
t4, s4 = await _bind_ipv4(queue)
|
|
284
|
+
transports.append((t4, s4))
|
|
285
|
+
t6, s6 = await _bind_ipv6_all_ifaces(queue)
|
|
286
|
+
transports.append((t6, s6))
|
|
287
|
+
if not transports:
|
|
288
|
+
raise RuntimeError("Failed to open mDNS sockets (UDP/5353)")
|
|
289
|
+
return transports, queue
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
async def _send_query_all(transports, pkt: bytes):
|
|
293
|
+
for transport, sock in transports:
|
|
294
|
+
if sock.family == socket.AF_INET:
|
|
295
|
+
transport.sendto(pkt, (MDNS_MCAST_V4, MDNS_PORT))
|
|
296
|
+
else:
|
|
297
|
+
# Send once per iface index for better reachability
|
|
298
|
+
for ifindex, _ in socket.if_nameindex():
|
|
299
|
+
transport.sendto(pkt, (MDNS_MCAST_V6, MDNS_PORT, 0, ifindex))
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
# ---------------- Public API ----------------
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
async def browse_service(
|
|
306
|
+
service_type: str, timeout: float = 4.0
|
|
307
|
+
) -> List[ServiceInstance]:
|
|
308
|
+
"""
|
|
309
|
+
Discover a DNS-SD/mDNS service type (e.g. "_remoted._tcp.local.") on the local network.
|
|
310
|
+
|
|
311
|
+
Returns: List[ServiceInstance] with Address(ip, iface) entries.
|
|
312
|
+
"""
|
|
313
|
+
if not service_type.endswith("."):
|
|
314
|
+
service_type += "."
|
|
315
|
+
|
|
316
|
+
transports, queue = await _open_mdns_sockets()
|
|
317
|
+
adapters = _Adapters()
|
|
318
|
+
|
|
319
|
+
ptr_targets: Set[str] = set()
|
|
320
|
+
srv_map: Dict[str, Dict] = {}
|
|
321
|
+
txt_map: Dict[str, Dict] = {}
|
|
322
|
+
# host -> list[(ip, iface)]
|
|
323
|
+
host_addrs: Dict[str, List[Address]] = defaultdict(list)
|
|
324
|
+
|
|
325
|
+
def _record_addr(rr_name: str, ip_str: str, pkt_addr):
|
|
326
|
+
# Determine family and possible scopeid from the packet that delivered this RR
|
|
327
|
+
family = socket.AF_INET6 if ":" in ip_str else socket.AF_INET
|
|
328
|
+
scopeid = None
|
|
329
|
+
if isinstance(pkt_addr, tuple) and len(pkt_addr) == 4: # IPv6 remote tuple
|
|
330
|
+
scopeid = pkt_addr[3]
|
|
331
|
+
iface = adapters.pick_iface_for_ip(ip_str, family, scopeid)
|
|
332
|
+
# avoid duplicates for the same host/ip
|
|
333
|
+
existing = host_addrs[rr_name]
|
|
334
|
+
if not any(a.ip == ip_str for a in existing):
|
|
335
|
+
existing.append(Address(ip=ip_str, iface=iface))
|
|
336
|
+
|
|
337
|
+
try:
|
|
338
|
+
await _send_query_all(
|
|
339
|
+
transports, build_query(service_type, QTYPE_PTR, unicast=False)
|
|
340
|
+
)
|
|
341
|
+
loop = asyncio.get_running_loop()
|
|
342
|
+
end = loop.time() + timeout
|
|
343
|
+
while loop.time() < end:
|
|
344
|
+
try:
|
|
345
|
+
data, pkt_addr = await asyncio.wait_for(
|
|
346
|
+
queue.get(), timeout=end - loop.time()
|
|
347
|
+
)
|
|
348
|
+
except asyncio.TimeoutError:
|
|
349
|
+
break
|
|
350
|
+
for rr in parse_mdns_message(data):
|
|
351
|
+
t = rr.get("type")
|
|
352
|
+
if t == QTYPE_PTR and rr.get("name") == service_type:
|
|
353
|
+
ptr_targets.add(rr.get("ptrdname"))
|
|
354
|
+
elif t == QTYPE_SRV:
|
|
355
|
+
srv_map[rr["name"]] = {
|
|
356
|
+
"target": rr.get("target"),
|
|
357
|
+
"port": rr.get("port"),
|
|
358
|
+
}
|
|
359
|
+
elif t == QTYPE_TXT:
|
|
360
|
+
txt_map[rr["name"]] = rr.get("txt", {})
|
|
361
|
+
elif t == QTYPE_A and rr.get("address"):
|
|
362
|
+
_record_addr(rr["name"], rr["address"], pkt_addr)
|
|
363
|
+
elif t == QTYPE_AAAA and rr.get("address"):
|
|
364
|
+
_record_addr(rr["name"], rr["address"], pkt_addr)
|
|
365
|
+
finally:
|
|
366
|
+
for transport, _ in transports:
|
|
367
|
+
transport.close()
|
|
368
|
+
|
|
369
|
+
# Assemble dataclasses
|
|
370
|
+
results: List[ServiceInstance] = []
|
|
371
|
+
for inst in sorted(ptr_targets):
|
|
372
|
+
srv = srv_map.get(inst, {})
|
|
373
|
+
target = srv.get("target")
|
|
374
|
+
host = (target[:-1] if target and target.endswith(".") else target) or None
|
|
375
|
+
addrs = host_addrs.get(target, []) if target else []
|
|
376
|
+
props = txt_map.get(inst, {})
|
|
377
|
+
results.append(
|
|
378
|
+
ServiceInstance(
|
|
379
|
+
instance=inst,
|
|
380
|
+
host=host,
|
|
381
|
+
port=srv.get("port"),
|
|
382
|
+
addresses=addrs,
|
|
383
|
+
properties=props,
|
|
384
|
+
)
|
|
385
|
+
)
|
|
386
|
+
return results
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
async def browse_remoted(timeout: float = DEFAULT_BONJOUR_TIMEOUT) -> list[ServiceInstance]:
|
|
390
|
+
return await browse_service(REMOTED_SERVICE_NAME, timeout=timeout)
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
async def browse_mobdev2(timeout: float = DEFAULT_BONJOUR_TIMEOUT) -> list[ServiceInstance]:
|
|
394
|
+
return await browse_service(MOBDEV2_SERVICE_NAME, timeout=timeout)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
async def browse_remotepairing(timeout: float = DEFAULT_BONJOUR_TIMEOUT) -> list[ServiceInstance]:
|
|
398
|
+
return await browse_service(REMOTEPAIRING_SERVICE_NAME, timeout=timeout)
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
async def browse_remotepairing_manual_pairing(timeout: float = DEFAULT_BONJOUR_TIMEOUT) -> list[ServiceInstance]:
|
|
402
|
+
return await browse_service(REMOTEPAIRING_MANUAL_PAIRING_SERVICE_NAME, timeout=timeout)
|
|
@@ -41,8 +41,8 @@ def cli_mobdev2(timeout: float, pair_records: Optional[str]) -> None:
|
|
|
41
41
|
async def cli_remotepairing_task(timeout: float) -> None:
|
|
42
42
|
output = []
|
|
43
43
|
for answer in await browse_remotepairing(timeout=timeout):
|
|
44
|
-
for
|
|
45
|
-
output.append({'hostname':
|
|
44
|
+
for address in answer.addresses:
|
|
45
|
+
output.append({'hostname': address.full_ip, 'port': answer.port})
|
|
46
46
|
print_json(output)
|
|
47
47
|
|
|
48
48
|
|
|
@@ -56,8 +56,8 @@ def cli_remotepairing(timeout: float) -> None:
|
|
|
56
56
|
async def cli_remotepairing_manual_pairing_task(timeout: float) -> None:
|
|
57
57
|
output = []
|
|
58
58
|
for answer in await browse_remotepairing_manual_pairing(timeout=timeout):
|
|
59
|
-
for
|
|
60
|
-
output.append({'hostname':
|
|
59
|
+
for address in answer.addresses:
|
|
60
|
+
output.append({'hostname': address.full_ip, 'port': answer.port, 'name': answer.properties[b'name'].decode()})
|
|
61
61
|
print_json(output)
|
|
62
62
|
|
|
63
63
|
|
|
@@ -161,6 +161,12 @@ def lockdown_wifi_connections(service_provider: LockdownClient, state):
|
|
|
161
161
|
service_provider.enable_wifi_connections = state == 'on'
|
|
162
162
|
|
|
163
163
|
|
|
164
|
+
async def async_cli_start_tunnel(
|
|
165
|
+
service_provider: LockdownServiceProvider, script_mode: bool) -> None:
|
|
166
|
+
await tunnel_task(await CoreDeviceTunnelProxy.create(service_provider),
|
|
167
|
+
script_mode=script_mode, secrets=None, protocol=TunnelProtocol.TCP)
|
|
168
|
+
|
|
169
|
+
|
|
164
170
|
@lockdown_group.command('start-tunnel', cls=Command)
|
|
165
171
|
@click.option('--script-mode', is_flag=True,
|
|
166
172
|
help='Show only HOST and port number to allow easy parsing from external shell scripts')
|
|
@@ -168,8 +174,7 @@ def lockdown_wifi_connections(service_provider: LockdownClient, state):
|
|
|
168
174
|
def cli_start_tunnel(
|
|
169
175
|
service_provider: LockdownServiceProvider, script_mode: bool) -> None:
|
|
170
176
|
""" start tunnel """
|
|
171
|
-
|
|
172
|
-
asyncio.run(tunnel_task(service, script_mode=script_mode, secrets=None, protocol=TunnelProtocol.TCP), debug=True)
|
|
177
|
+
asyncio.run(async_cli_start_tunnel(service_provider, script_mode), debug=True)
|
|
173
178
|
|
|
174
179
|
|
|
175
180
|
@lockdown_group.command('assistive-touch', cls=Command)
|
|
@@ -220,9 +220,10 @@ async def start_remote_pair_task(device_name: str) -> None:
|
|
|
220
220
|
if device_name is not None and current_device_name != device_name:
|
|
221
221
|
continue
|
|
222
222
|
|
|
223
|
-
for
|
|
224
|
-
devices.append(
|
|
225
|
-
|
|
223
|
+
for address in answer.addresses:
|
|
224
|
+
devices.append(
|
|
225
|
+
RemotePairingManualPairingDevice(ip=address.full_ip, port=answer.port, device_name=current_device_name,
|
|
226
|
+
identifier=answer.properties[b'identifier'].decode()))
|
|
226
227
|
|
|
227
228
|
if len(devices) > 0:
|
|
228
229
|
device = prompt_device_list(devices)
|
|
@@ -848,7 +848,7 @@ def create_using_remote(service: ServiceConnection, identifier: str = None, labe
|
|
|
848
848
|
|
|
849
849
|
async def get_mobdev2_lockdowns(
|
|
850
850
|
udid: Optional[str] = None, pair_records: Optional[Path] = None, only_paired: bool = False,
|
|
851
|
-
timeout: float = DEFAULT_BONJOUR_TIMEOUT
|
|
851
|
+
timeout: float = DEFAULT_BONJOUR_TIMEOUT) \
|
|
852
852
|
-> AsyncIterable[tuple[str, TcpLockdownClient]]:
|
|
853
853
|
records = {}
|
|
854
854
|
if pair_records is None:
|
|
@@ -863,26 +863,21 @@ async def get_mobdev2_lockdowns(
|
|
|
863
863
|
record = plistlib.loads(file.read_bytes())
|
|
864
864
|
records[record['WiFiMACAddress']] = record
|
|
865
865
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
if '@' not in answer.name:
|
|
866
|
+
for answer in await browse_mobdev2(timeout=timeout):
|
|
867
|
+
if '@' not in answer.instance:
|
|
869
868
|
continue
|
|
870
|
-
wifi_mac_address = answer.
|
|
869
|
+
wifi_mac_address = answer.instance.split('@', 1)[0]
|
|
871
870
|
record = records.get(wifi_mac_address)
|
|
872
871
|
|
|
873
872
|
if only_paired and record is None:
|
|
874
873
|
continue
|
|
875
874
|
|
|
876
|
-
for
|
|
877
|
-
if ip in iterated_ips:
|
|
878
|
-
# skip ips we already iterated over, possibly from previous queries
|
|
879
|
-
continue
|
|
880
|
-
iterated_ips.add(ip)
|
|
875
|
+
for address in answer.addresses:
|
|
881
876
|
try:
|
|
882
|
-
lockdown = create_using_tcp(hostname=
|
|
877
|
+
lockdown = create_using_tcp(hostname=address.full_ip, autopair=False, pair_record=record)
|
|
883
878
|
except Exception:
|
|
884
879
|
continue
|
|
885
880
|
if only_paired and not lockdown.paired:
|
|
886
881
|
lockdown.close()
|
|
887
882
|
continue
|
|
888
|
-
yield
|
|
883
|
+
yield address.full_ip, lockdown
|
|
@@ -190,7 +190,7 @@ class RemotePairingTunnel(ABC):
|
|
|
190
190
|
self._tun_read_task = asyncio.create_task(self.tun_read_task(), name=f'tun-read-{address}')
|
|
191
191
|
|
|
192
192
|
async def stop_tunnel(self) -> None:
|
|
193
|
-
self._logger.debug('stopping tunnel')
|
|
193
|
+
self._logger.debug(f'[{asyncio.current_task().get_name()}] stopping tunnel')
|
|
194
194
|
self._tun_read_task.cancel()
|
|
195
195
|
with suppress(CancelledError):
|
|
196
196
|
await self._tun_read_task
|
|
@@ -223,6 +223,9 @@ class RemotePairingQuicTunnel(RemotePairingTunnel, QuicConnectionProtocol):
|
|
|
223
223
|
self._quic.send_datagram_frame(packet)
|
|
224
224
|
self.transmit()
|
|
225
225
|
|
|
226
|
+
# Allow other tasks to run
|
|
227
|
+
await asyncio.sleep(0)
|
|
228
|
+
|
|
226
229
|
async def request_tunnel_establish(self) -> dict:
|
|
227
230
|
stream_id = self._quic.get_next_available_stream_id()
|
|
228
231
|
# pad the data with random data to force the MTU size correctly
|
|
@@ -939,17 +942,21 @@ class RemotePairingManualPairingService(RemotePairingTunnelService):
|
|
|
939
942
|
class CoreDeviceTunnelProxy(StartTcpTunnel):
|
|
940
943
|
SERVICE_NAME = 'com.apple.internal.devicecompute.CoreDeviceProxy'
|
|
941
944
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
+
@classmethod
|
|
946
|
+
async def create(cls, lockdown: LockdownServiceProvider) -> 'CoreDeviceTunnelProxy':
|
|
947
|
+
return cls(await lockdown.aio_start_lockdown_service(cls.SERVICE_NAME), lockdown.udid)
|
|
948
|
+
|
|
949
|
+
def __init__(self, service: ServiceConnection, remote_identifier: str) -> None:
|
|
950
|
+
self._service: ServiceConnection = service
|
|
951
|
+
self._remote_identifier: str = remote_identifier
|
|
945
952
|
|
|
946
953
|
@property
|
|
947
954
|
def remote_identifier(self) -> str:
|
|
948
|
-
return self.
|
|
955
|
+
return self._remote_identifier
|
|
949
956
|
|
|
950
957
|
@asynccontextmanager
|
|
951
958
|
async def start_tcp_tunnel(self) -> AsyncGenerator['TunnelResult', None]:
|
|
952
|
-
self._service
|
|
959
|
+
assert self._service is not None, 'service must be connected first'
|
|
953
960
|
tunnel = RemotePairingTcpTunnel(self._service.reader, self._service.writer)
|
|
954
961
|
handshake_response = await tunnel.request_tunnel_establish()
|
|
955
962
|
tunnel.start_tunnel(handshake_response['clientParameters']['address'],
|
|
@@ -1078,13 +1085,14 @@ async def get_remote_pairing_tunnel_services(
|
|
|
1078
1085
|
udid: Optional[str] = None) -> list[RemotePairingTunnelService]:
|
|
1079
1086
|
result = []
|
|
1080
1087
|
for answer in await browse_remotepairing(timeout=bonjour_timeout):
|
|
1081
|
-
for
|
|
1088
|
+
for address in answer.addresses:
|
|
1082
1089
|
for identifier in iter_remote_paired_identifiers():
|
|
1083
1090
|
if udid is not None and identifier != udid:
|
|
1084
1091
|
continue
|
|
1085
1092
|
conn = None
|
|
1086
1093
|
try:
|
|
1087
|
-
conn = await create_core_device_tunnel_service_using_remotepairing(
|
|
1094
|
+
conn = await create_core_device_tunnel_service_using_remotepairing(
|
|
1095
|
+
identifier, address.full_ip, answer.port)
|
|
1088
1096
|
result.append(conn)
|
|
1089
1097
|
break
|
|
1090
1098
|
except ConnectionAbortedError:
|
|
@@ -17,8 +17,8 @@ async def get_rsds(bonjour_timeout: float = DEFAULT_BONJOUR_TIMEOUT, udid: Optio
|
|
|
17
17
|
result = []
|
|
18
18
|
with stop_remoted():
|
|
19
19
|
for answer in await browse_remoted(timeout=bonjour_timeout):
|
|
20
|
-
for
|
|
21
|
-
rsd = RemoteServiceDiscoveryService((
|
|
20
|
+
for address in answer.addresses:
|
|
21
|
+
rsd = RemoteServiceDiscoveryService((address.full_ip, RSD_PORT))
|
|
22
22
|
try:
|
|
23
23
|
await rsd.connect()
|
|
24
24
|
except ConnectionRefusedError:
|