diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..e995b30c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +github: nikias +patreon: nikias +custom: ["https://www.paypal.me/NikiasBassen"] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 56288796..1ced463f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,10 @@ name: build -on: [push] +on: + push: + pull_request: + schedule: + - cron: '0 0 1 * *' jobs: build-linux-ubuntu: @@ -14,28 +18,28 @@ jobs: run: | echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch libplist - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libplist-latest_${{env.target_triplet}} repo: libimobiledevice/libplist - name: fetch libusbmuxd - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libusbmuxd-latest_${{env.target_triplet}} repo: libimobiledevice/libusbmuxd - name: fetch libimobiledevice-glue - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml name: libimobiledevice-glue-latest_${{env.target_triplet}} repo: libimobiledevice/libimobiledevice-glue - name: fetch libimobiledevice - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v6 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml @@ -49,7 +53,7 @@ jobs: done sudo cp -r extract/* / sudo ldconfig - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: autogen @@ -65,9 +69,9 @@ jobs: run: | mkdir -p dest DESTDIR=`pwd`/dest make install - tar -C dest -cf usbmuxd.tar usr lib + tar -C dest -cf usbmuxd.tar --strip-components 1 . - name: publish artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: usbmuxd-latest_${{env.target_triplet}} path: usbmuxd.tar diff --git a/README.md b/README.md index 82493582..0725e318 100644 --- a/README.md +++ b/README.md @@ -135,8 +135,8 @@ We are still working on the guidelines so bear with us! ## Links * Homepage: https://libimobiledevice.org/ -* Repository: https://git.libimobiledevice.org/usbmuxd.git -* Repository (Mirror): https://github.com/libimobiledevice/usbmuxd.git +* Repository: https://github.com/libimobiledevice/usbmuxd.git +* Repository (Mirror): https://git.libimobiledevice.org/usbmuxd.git * Issue Tracker: https://github.com/libimobiledevice/usbmuxd/issues * Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel * Twitter: https://twitter.com/libimobiledev @@ -156,4 +156,4 @@ iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc. usbmuxd is an independent software application and has not been authorized, sponsored, or otherwise approved by Apple Inc. -README Updated on: 2022-04-04 +README Updated on: 2024-12-02 diff --git a/configure.ac b/configure.ac index b751cc2d..3bf88ef6 100644 --- a/configure.ac +++ b/configure.ac @@ -1,14 +1,19 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. -AC_PREREQ(2.68) -AC_INIT([usbmuxd], [1.1.2], [https://github.com/libimobiledevice/usbmuxd/issues],, [https://libimobiledevice.org]) +AC_PREREQ([2.68]) +AC_INIT([usbmuxd], [m4_esyscmd(./git-version-gen $RELEASE_VERSION)], [https://github.com/libimobiledevice/usbmuxd/issues], [], [https://libimobiledevice.org]) AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip check-news]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) AC_CONFIG_SRCDIR([src/]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) +# Check if we have a version defined +if test -z $PACKAGE_VERSION; then + AC_MSG_ERROR([PACKAGE_VERSION is not defined. Make sure to configure a source tree checked out from git or that .tarball-version is present.]) +fi + # Checks for programs. AC_PROG_CC AM_PROG_CC_C_O @@ -16,14 +21,14 @@ LT_INIT # Checks for libraries. PKG_CHECK_MODULES(libusb, libusb-1.0 >= 1.0.9) -PKG_CHECK_MODULES(libplist, libplist-2.0 >= 2.2.0) +PKG_CHECK_MODULES(libplist, libplist-2.0 >= 2.6.0) PKG_CHECK_MODULES(libimobiledevice, libimobiledevice-1.0 >= 1.3.0, have_limd=yes, have_limd=no) PKG_CHECK_MODULES(limd_glue, libimobiledevice-glue-1.0 >= 1.0.0) AC_ARG_WITH([preflight], [AS_HELP_STRING([--without-preflight], [do not build with preflight worker support @<:@default=yes@:>@])], - [with_preflight=no], + [with_preflight=$withval], [with_preflight=yes]) if test "x$have_limd" = "xyes"; then @@ -35,7 +40,7 @@ if test "x$have_limd" = "xyes"; then AC_SUBST(libimobiledevice_CFLAGS) AC_SUBST(libimobiledevice_LIBS) CACHED_CFLAGS="$CFLAGS" - CFLAGS+=" $libimobiledevice_CFLAGS" + CFLAGS="$CFLAGS $libimobiledevice_CFLAGS" AC_CACHE_CHECK(for enum idevice_connection_type, ac_cv_enum_idevice_connection_type, AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ #include @@ -70,7 +75,7 @@ fi AC_ARG_WITH([systemd], [AS_HELP_STRING([--without-systemd], [do not build with systemd support @<:@default=yes@:>@])], - [], + [with_systemd=$withval], [with_systemd=yes]) AC_ARG_WITH([systemdsystemunitdir], @@ -148,11 +153,16 @@ AM_CONDITIONAL(WIN32, test x$win32 = xtrue) AC_SUBST([UDEV_SUB]) AC_SUBST([SYSTEMD_SUB]) -AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-g -Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter") +AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter") AC_SUBST(GLOBAL_CFLAGS) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) +# workaround for older autoconf versions +if test "x$runstatedir" == "x"; then + runstatedir=$localstatedir/run +fi + AC_CONFIG_FILES([ Makefile src/Makefile diff --git a/git-version-gen b/git-version-gen new file mode 100755 index 00000000..3eb6a42e --- /dev/null +++ b/git-version-gen @@ -0,0 +1,19 @@ +#!/bin/sh +SRCDIR=`dirname $0` +if test -n "$1"; then + VER=$1 +else + if test -d "${SRCDIR}/.git" && test -x "`which git`" ; then + git update-index -q --refresh + if ! VER=`git describe --tags --dirty 2>/dev/null`; then + COMMIT=`git rev-parse --short HEAD` + DIRTY=`git diff --quiet HEAD || echo "-dirty"` + VER=`sed -n '1,/RE/s/Version \(.*\)/\1/p' ${SRCDIR}/NEWS`-git-${COMMIT}${DIRTY} + fi + else + if test -f "${SRCDIR}/.tarball-version"; then + VER=`cat "${SRCDIR}/.tarball-version"` + fi + fi +fi +printf %s "$VER" diff --git a/src/client.c b/src/client.c index 75a526d5..2ac04bfd 100644 --- a/src/client.c +++ b/src/client.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -451,10 +452,7 @@ static int send_listener_list(struct mux_client *client, uint32_t tag) plist_dict_set_item(l, "Blacklisted", plist_new_bool(0)); n = NULL; if (lc->info) { - n = plist_dict_get_item(lc->info, "BundleID"); - } - if (n) { - plist_dict_set_item(l, "BundleID", plist_copy(n)); + plist_dict_copy_item(l, lc->info, "BundleID", NULL); } plist_dict_set_item(l, "ConnType", plist_new_uint(0)); @@ -631,28 +629,11 @@ static char* plist_dict_get_string_val(plist_t dict, const char* key) static void update_client_info(struct mux_client *client, plist_t dict) { - plist_t node = NULL; plist_t info = plist_new_dict(); - - node = plist_dict_get_item(dict, "BundleID"); - if (node && (plist_get_node_type(node) == PLIST_STRING)) { - plist_dict_set_item(info, "BundleID", plist_copy(node)); - } - - node = plist_dict_get_item(dict, "ClientVersionString"); - if (node && (plist_get_node_type(node) == PLIST_STRING)) { - plist_dict_set_item(info, "ClientVersionString", plist_copy(node)); - } - - node = plist_dict_get_item(dict, "ProgName"); - if (node && (plist_get_node_type(node) == PLIST_STRING)) { - plist_dict_set_item(info, "ProgName", plist_copy(node)); - } - - node = plist_dict_get_item(dict, "kLibUSBMuxVersion"); - if (node && (plist_get_node_type(node) == PLIST_UINT)) { - plist_dict_set_item(info, "kLibUSBMuxVersion", plist_copy(node)); - } + plist_dict_copy_item(info, dict, "BundleID", NULL); + plist_dict_copy_item(info, dict, "ClientVersionString", NULL); + plist_dict_copy_item(info, dict, "ProgName", NULL); + plist_dict_copy_item(info, dict, "kLibUSBMuxVersion", NULL); plist_free(client->info); client->info = info; } diff --git a/src/conf.c b/src/conf.c index 84f43265..2f0968d8 100644 --- a/src/conf.c +++ b/src/conf.c @@ -34,18 +34,18 @@ #include #include #include - -#ifdef WIN32 -#include -#endif +#include #include +#include #include "conf.h" #include "utils.h" #include "log.h" #ifdef WIN32 +#include + #define DIR_SEP '\\' #define DIR_SEP_S "\\" #else @@ -179,13 +179,26 @@ static int mkdir_with_parents(const char *dir, int mode) /** * Creates a freedesktop compatible configuration directory. */ -static void config_create_config_dir(void) +static int config_create_config_dir(void) { const char *config_path = config_get_config_dir(); struct stat st; - if (stat(config_path, &st) != 0) { - mkdir_with_parents(config_path, 0755); + int res = stat(config_path, &st); + if (res != 0) { + res = mkdir_with_parents(config_path, 0755); + } + return res; +} + +int config_set_config_dir(const char* path) +{ + if (!path) { + return -1; } + free(__config_dir); + __config_dir = strdup(path); + usbmuxd_log(LL_DEBUG, "Setting config_dir to %s", __config_dir); + return config_create_config_dir(); } static int get_rand(int min, int max) @@ -232,7 +245,7 @@ static int internal_set_value(const char *config_file, const char *key, plist_t /* read file into plist */ plist_t config = NULL; - plist_read_from_filename(&config, config_file); + plist_read_from_file(config_file, &config, NULL); if (!config) { config = plist_new_dict(); plist_dict_set_item(config, key, value); @@ -256,7 +269,7 @@ static int internal_set_value(const char *config_file, const char *key, plist_t usbmuxd_log(LL_DEBUG, "Setting key %s in config file %s", key, config_file); } - int res = plist_write_to_filename(config, config_file, PLIST_FORMAT_XML); + int res = (plist_write_to_file(config, config_file, PLIST_FORMAT_XML, 0) == PLIST_ERR_SUCCESS); plist_free(config); @@ -269,14 +282,17 @@ static int config_set_value(const char *key, plist_t value) char *config_file = NULL; /* Make sure config directory exists */ - config_create_config_dir(); + if (config_create_config_dir() < 0) { + usbmuxd_log(LL_ERROR, "ERROR: Failed to create config directory\n"); + return -1; + } config_path = config_get_config_dir(); config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL); int result = internal_set_value(config_file, key, value); if (!result) { - usbmuxd_log(LL_ERROR, "ERROR: Failed to write to '%s': %s", config_file, strerror(errno)); + usbmuxd_log(LL_ERROR, "ERROR: Failed to write to '%s'", config_file); } free(config_file); @@ -290,7 +306,7 @@ static int internal_get_value(const char* config_file, const char *key, plist_t /* now parse file to get the SystemBUID */ plist_t config = NULL; - if (plist_read_from_filename(&config, config_file)) { + if (plist_read_from_file(config_file, &config, NULL) == PLIST_ERR_SUCCESS) { usbmuxd_log(LL_DEBUG, "Reading key %s from config file %s", key, config_file); plist_t n = plist_dict_get_item(config, key); if (n) { @@ -341,7 +357,10 @@ int config_has_device_record(const char *udid) if (!udid) return 0; /* ensure config directory exists */ - config_create_config_dir(); + if (config_create_config_dir() < 0) { + usbmuxd_log(LL_ERROR, "ERROR: Failed to create config directory\n"); + return -1; + } /* build file path */ const char *config_path = config_get_config_dir(); @@ -407,13 +426,19 @@ int config_set_device_record(const char *udid, char* record_data, uint64_t recor if (!udid || !record_data || record_size < 8) return -EINVAL; - plist_t plist = NULL; - if (memcmp(record_data, "bplist00", 8) == 0) { - plist_from_bin(record_data, record_size, &plist); - } else { - plist_from_xml(record_data, record_size, &plist); + /* verify udid input */ + const char* u = udid; + while (*u != '\0') { + if (!isalnum(*u) && (*u != '-')) { + usbmuxd_log(LL_ERROR, "ERROR: %s: udid contains invalid character.\n", __func__); + return -EINVAL; + } + u++; } + plist_t plist = NULL; + plist_from_memory(record_data, record_size, &plist, NULL); + if (!plist || plist_get_node_type(plist) != PLIST_DICT) { if (plist) plist_free(plist); @@ -421,7 +446,10 @@ int config_set_device_record(const char *udid, char* record_data, uint64_t recor } /* ensure config directory exists */ - config_create_config_dir(); + if (config_create_config_dir() < 0) { + usbmuxd_log(LL_ERROR, "ERROR: Failed to create config directory\n"); + return -1; + } /* build file path */ const char *config_path = config_get_config_dir(); @@ -430,7 +458,7 @@ int config_set_device_record(const char *udid, char* record_data, uint64_t recor remove(device_record_file); /* store file */ - if (!plist_write_to_filename(plist, device_record_file, PLIST_FORMAT_XML)) { + if (plist_write_to_file(plist, device_record_file, PLIST_FORMAT_XML, 0) != PLIST_ERR_SUCCESS) { usbmuxd_log(LL_DEBUG, "Could not open '%s' for writing: %s", device_record_file, strerror(errno)); res = -ENOENT; } @@ -457,7 +485,10 @@ int config_get_device_record(const char *udid, char **record_data, uint64_t *rec int res = 0; /* ensure config directory exists */ - config_create_config_dir(); + if (config_create_config_dir() < 0) { + usbmuxd_log(LL_ERROR, "ERROR: Failed to create config directory\n"); + return -1; + } /* build file path */ const char *config_path = config_get_config_dir(); diff --git a/src/conf.h b/src/conf.h index bbfa9653..294ca184 100644 --- a/src/conf.h +++ b/src/conf.h @@ -27,6 +27,7 @@ #include const char *config_get_config_dir(); +int config_set_config_dir(const char* path); void config_get_system_buid(char **system_buid); diff --git a/src/device.c b/src/device.c index 09280213..ce73718d 100644 --- a/src/device.c +++ b/src/device.c @@ -661,7 +661,7 @@ static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned if(!(th->th_flags & TH_RST)) { usbmuxd_log(LL_INFO, "No connection for device %d incoming packet %d->%d", dev->id, dport, sport); if(send_anon_rst(dev, sport, dport, ntohl(th->th_seq)) < 0) - usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, sport, dport); + usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", dev->id, sport, dport); } return; } diff --git a/src/main.c b/src/main.c index 8702a4b3..b09a187a 100644 --- a/src/main.c +++ b/src/main.c @@ -517,6 +517,7 @@ static void usage() #ifdef HAVE_SYSTEMD printf(" -s, --systemd\t\tRun in systemd operation mode (implies -z and -f).\n"); #endif + printf(" -C, --config-dir PATH\tSpecify different configuration directory.\n"); printf(" -S, --socket ADDR:PORT | PATH Specify source ADDR and PORT or a UNIX\n"); printf(" \t\tsocket PATH to use for the listening socket.\n"); printf(" \t\tDefault: %s\n", socket_path); @@ -549,6 +550,7 @@ static void parse_opts(int argc, char **argv) #ifdef HAVE_SYSTEMD {"systemd", no_argument, NULL, 's'}, #endif + {"config-dir", required_argument, NULL, 'C'}, {"socket", required_argument, NULL, 'S'}, {"pidfile", required_argument, NULL, 'P'}, {"exit", no_argument, NULL, 'x'}, @@ -560,11 +562,11 @@ static void parse_opts(int argc, char **argv) int c; #ifdef HAVE_SYSTEMD - const char* opts_spec = "hfvVuU:xXsnzl:pS:P:"; + const char* opts_spec = "hfvVuU:xXsnzl:pC:S:P:"; #elif HAVE_UDEV - const char* opts_spec = "hfvVuU:xXnzl:pS:P:"; + const char* opts_spec = "hfvVuU:xXnzl:pC:S:P:"; #else - const char* opts_spec = "hfvVU:xXnzl:pS:P:"; + const char* opts_spec = "hfvVU:xXnzl:pC:S:P:"; #endif while (1) { @@ -584,7 +586,7 @@ static void parse_opts(int argc, char **argv) ++verbose; break; case 'V': - printf("%s\n", PACKAGE_STRING); + printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); exit(0); case 'U': drop_privileges = 1; @@ -611,6 +613,14 @@ static void parse_opts(int argc, char **argv) case 'z': opt_enable_exit = 1; break; + case 'C': + if (!*optarg) { + usbmuxd_log(LL_FATAL, "ERROR: --config-dir requires an argument"); + usage(); + exit(2); + } + config_set_config_dir(optarg); + break; case 'S': if (!*optarg || *optarg == '-') { usbmuxd_log(LL_FATAL, "ERROR: --socket requires an argument"); @@ -796,11 +806,12 @@ int main(int argc, char *argv[]) #ifdef HAVE_LIBIMOBILEDEVICE const char* userprefdir = config_get_config_dir(); + usbmuxd_log(LL_NOTICE, "Configuration directory: %s", userprefdir); struct stat fst; memset(&fst, '\0', sizeof(struct stat)); if (stat(userprefdir, &fst) < 0) { if (mkdir(userprefdir, 0775) < 0) { - usbmuxd_log(LL_FATAL, "Failed to create required directory '%s': %s", userprefdir, strerror(errno)); + usbmuxd_log(LL_FATAL, "Failed to create configuration directory '%s': %s", userprefdir, strerror(errno)); res = -1; goto terminate; } diff --git a/src/preflight.c b/src/preflight.c index 5902f5d0..9c57e98b 100644 --- a/src/preflight.c +++ b/src/preflight.c @@ -148,7 +148,7 @@ static void* preflight_worker_handle_device_add(void* userdata) plist_t value = NULL; char* version_str = NULL; - char* platform_str = NULL; + char* deviceclass_str = NULL; usbmuxd_log(LL_INFO, "%s: Starting preflight on device %s...", __func__, _dev->udid); @@ -214,42 +214,43 @@ static void* preflight_worker_handle_device_add(void* userdata) lerr = lockdownd_get_value(lockdown, NULL, "ProductVersion", &value); if (lerr != LOCKDOWN_E_SUCCESS) { - usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get ProductVersion from device %s, lockdown error %d", __func__, _dev->udid, lerr); - goto leave; - } - - if (value && plist_get_node_type(value) == PLIST_STRING) { - plist_get_string_val(value, &version_str); - } - plist_free(value); + usbmuxd_log(LL_WARNING, "%s: Could not get ProductVersion from device %s, lockdown error %d", __func__, _dev->udid, lerr); + /* assume old iOS version */ + version_str = strdup("1.0"); + } else { + if (value && plist_get_node_type(value) == PLIST_STRING) { + plist_get_string_val(value, &version_str); + } + plist_free(value); - if (!version_str) { - usbmuxd_log(LL_ERROR, "%s: Could not get ProductVersion string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data); - goto leave; + if (!version_str) { + usbmuxd_log(LL_ERROR, "%s: Could not get ProductVersion string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data); + goto leave; + } } - lerr = lockdownd_get_value(lockdown, NULL, "ProductName", &value); + lerr = lockdownd_get_value(lockdown, NULL, "DeviceClass", &value); if (lerr != LOCKDOWN_E_SUCCESS) { - usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get ProductName from device %s, lockdown error %d", __func__, _dev->udid, lerr); + usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get DeviceClass from device %s, lockdown error %d", __func__, _dev->udid, lerr); goto leave; } if (value && plist_get_node_type(value) == PLIST_STRING) { - plist_get_string_val(value, &platform_str); + plist_get_string_val(value, &deviceclass_str); } plist_free(value); - if (!platform_str) { - usbmuxd_log(LL_ERROR, "%s: Could not get ProductName string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data); + if (!deviceclass_str) { + usbmuxd_log(LL_ERROR, "%s: Could not get DeviceClass string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data); goto leave; } int version_major = strtol(version_str, NULL, 10); - if ((!strcmp(platform_str, "iPhone OS") && version_major >= 7) - || ((!strcmp(platform_str, "watchOS") || !strcmp(platform_str, "Watch OS")) && version_major >= 2) - || (!strcmp(platform_str, "Apple TVOS") && version_major >= 9) + if (((!strcmp(deviceclass_str, "iPhone") || !strcmp(deviceclass_str, "iPad")) && version_major >= 7) + || (!strcmp(deviceclass_str, "Watch") && version_major >= 2) + || (!strcmp(deviceclass_str, "AppleTV") && version_major >= 9) ) { /* iOS 7.0 / watchOS 2.0 / tvOS 9.0 and later */ - usbmuxd_log(LL_INFO, "%s: Found %s %s device %s", __func__, platform_str, version_str, _dev->udid); + usbmuxd_log(LL_INFO, "%s: Found %s %s device %s", __func__, deviceclass_str, version_str, _dev->udid); lockdownd_set_untrusted_host_buid(lockdown); @@ -356,7 +357,7 @@ static void* preflight_worker_handle_device_add(void* userdata) } leave: - free(platform_str); + free(deviceclass_str); free(version_str); if (lockdown) lockdownd_client_free(lockdown); diff --git a/src/usb.c b/src/usb.c index 4ff2d07d..d0e9afcf 100644 --- a/src/usb.c +++ b/src/usb.c @@ -53,7 +53,7 @@ #define NUM_RX_LOOPS 3 struct usb_device { - libusb_device_handle *dev; + libusb_device_handle *handle; uint8_t bus, address; char serial[256]; int alive; @@ -65,6 +65,14 @@ struct usb_device { struct libusb_device_descriptor devdesc; }; +struct mode_context { + struct libusb_device* dev; + uint8_t bus, address; + uint8_t bRequest; + uint16_t wValue, wIndex, wLength; + unsigned int timeout; +}; + static struct collection device_list; static struct timeval next_dev_poll_time; @@ -75,7 +83,7 @@ static int device_hotplug = 1; static void usb_disconnect(struct usb_device *dev) { - if(!dev->dev) { + if(!dev->handle) { return; } @@ -106,9 +114,9 @@ static void usb_disconnect(struct usb_device *dev) collection_free(&dev->tx_xfers); collection_free(&dev->rx_xfers); - libusb_release_interface(dev->dev, dev->interface); - libusb_close(dev->dev); - dev->dev = NULL; + libusb_release_interface(dev->handle, dev->interface); + libusb_close(dev->handle); + dev->handle = NULL; collection_remove(&device_list, dev); free(dev); } @@ -169,7 +177,7 @@ int usb_send(struct usb_device *dev, const unsigned char *buf, int length) { int res; struct libusb_transfer *xfer = libusb_alloc_transfer(0); - libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_out, (void*)buf, length, tx_callback, dev, 0); + libusb_fill_bulk_transfer(xfer, dev->handle, dev->ep_out, (void*)buf, length, tx_callback, dev, 0); if((res = libusb_submit_transfer(xfer)) < 0) { usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %s", buf, length, dev->bus, dev->address, libusb_error_name(res)); libusb_free_transfer(xfer); @@ -181,7 +189,7 @@ int usb_send(struct usb_device *dev, const unsigned char *buf, int length) // Send Zero Length Packet xfer = libusb_alloc_transfer(0); void *buffer = malloc(1); - libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_out, buffer, 0, tx_callback, dev, 0); + libusb_fill_bulk_transfer(xfer, dev->handle, dev->ep_out, buffer, 0, tx_callback, dev, 0); if((res = libusb_submit_transfer(xfer)) < 0) { usbmuxd_log(LL_ERROR, "Failed to submit TX ZLP transfer to device %d-%d: %s", dev->bus, dev->address, libusb_error_name(res)); libusb_free_transfer(xfer); @@ -248,7 +256,7 @@ static int start_rx_loop(struct usb_device *dev) void *buf; struct libusb_transfer *xfer = libusb_alloc_transfer(0); buf = malloc(USB_MRU); - libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_in, buf, USB_MRU, rx_callback, dev, 0); + libusb_fill_bulk_transfer(xfer, dev->handle, dev->ep_in, buf, USB_MRU, rx_callback, dev, 0); if((res = libusb_submit_transfer(xfer)) != 0) { usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %s", dev->bus, dev->address, libusb_error_name(res)); libusb_free_transfer(xfer); @@ -349,7 +357,7 @@ static void get_langid_callback(struct libusb_transfer *transfer) libusb_fill_control_setup(transfer->buffer, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t)((LIBUSB_DT_STRING << 8) | usbdev->devdesc.iSerialNumber), langid, 1024 + LIBUSB_CONTROL_SETUP_SIZE); - libusb_fill_control_transfer(transfer, usbdev->dev, transfer->buffer, get_serial_callback, usbdev, 1000); + libusb_fill_control_transfer(transfer, usbdev->handle, transfer->buffer, get_serial_callback, usbdev, 1000); if((res = libusb_submit_transfer(transfer)) < 0) { usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d: %s", usbdev->bus, usbdev->address, libusb_error_name(res)); @@ -357,183 +365,238 @@ static void get_langid_callback(struct libusb_transfer *transfer) } } -static int usb_device_add(libusb_device* dev) +static int submit_vendor_specific(struct libusb_device_handle *handle, struct mode_context *context, libusb_transfer_cb_fn callback) +{ + struct libusb_transfer* ctrl_transfer = libusb_alloc_transfer(0); + int ret = 0; + unsigned char* buffer = calloc(LIBUSB_CONTROL_SETUP_SIZE + context->wLength, 1); + uint8_t bRequestType = LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_DEVICE; + libusb_fill_control_setup(buffer, bRequestType, context->bRequest, context->wValue, context->wIndex, context->wLength); + + ctrl_transfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER; + libusb_fill_control_transfer(ctrl_transfer, handle, buffer, callback, context, context->timeout); + + ret = libusb_submit_transfer(ctrl_transfer); + return ret; +} + +static struct usb_device* find_device(int bus, int address) { - int j, res; - // the following are non-blocking operations on the device list - uint8_t bus = libusb_get_bus_number(dev); - uint8_t address = libusb_get_device_address(dev); - struct libusb_device_descriptor devdesc; - struct libusb_transfer *transfer; - int found = 0; FOREACH(struct usb_device *usbdev, &device_list) { if(usbdev->bus == bus && usbdev->address == address) { - usbdev->alive = 1; - found = 1; - break; + return usbdev; } } ENDFOREACH - if(found) - return 0; //device already found + return NULL; +} - if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) { - usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %s", bus, address, libusb_error_name(res)); - return -1; +/// @brief guess the current mode +/// @param dev +/// @param usbdev +/// @param handle +/// @return 0 - undetermined, 1 - initial, 2 - valeria, 3 - cdc_ncm, 4 - usbeth+cdc_ncm, 5 - cdc_ncm direct +static int guess_mode(struct libusb_device* dev, struct usb_device *usbdev) +{ + int res, j; + int has_valeria = 0, has_cdc_ncm = 0, has_usbmux = 0; + struct libusb_device_descriptor devdesc = usbdev->devdesc; + struct libusb_config_descriptor *config; + int bus = usbdev->bus; + int address = usbdev->address; + + if(devdesc.bNumConfigurations == 1) { + // CDC-NCM Direct + return 5; } - if(devdesc.idVendor != VID_APPLE) - return -1; - if((devdesc.idProduct != PID_APPLE_T2_COPROCESSOR) && - ((devdesc.idProduct < PID_APPLE_SILICON_RESTORE_LOW) || - (devdesc.idProduct > PID_APPLE_SILICON_RESTORE_MAX)) && - ((devdesc.idProduct < PID_RANGE_LOW) || - (devdesc.idProduct > PID_RANGE_MAX))) - return -1; - libusb_device_handle *handle; - usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address); - // No blocking operation can follow: it may be run in the libusb hotplug callback and libusb will refuse any - // blocking call - if((res = libusb_open(dev, &handle)) != 0) { - usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %s", bus, address, libusb_error_name(res)); - return -1; + + if(devdesc.bNumConfigurations <= 4) { + // Assume this is initial mode + return 1; + } + + if(devdesc.bNumConfigurations == 6) { + // USB Ethernet + CDC-NCM + return 4; } - int desired_config = devdesc.bNumConfigurations; - if (desired_config > 4) { - if (desired_config > 5) { - usbmuxd_log(LL_ERROR, "Device %d-%d has more than 5 configurations, but usbmuxd doesn't support that. Choosing configuration 5 instead.", bus, address); - desired_config = 5; + if(devdesc.bNumConfigurations != 5) { + // No known modes with more then 5 configurations + return 0; + } + + if((res = libusb_get_config_descriptor_by_value(dev, 5, &config)) != 0) { + usbmuxd_log(LL_NOTICE, "Could not get configuration 5 descriptor for device %i-%i: %s", bus, address, libusb_error_name(res)); + return 0; + } + + // Require both usbmux and one of the other interfaces to determine this is a valid configuration + for(j = 0 ; j < config->bNumInterfaces ; j++) { + const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0]; + if(intf->bInterfaceClass == INTERFACE_CLASS && + intf->bInterfaceSubClass == 42 && + intf->bInterfaceProtocol == 255) { + has_valeria = 1; } - /* verify if the configuration 5 is actually usable */ - do { - struct libusb_config_descriptor *config; - const struct libusb_interface_descriptor *intf; - if (libusb_get_config_descriptor_by_value(dev, 5, &config) != 0) { - usbmuxd_log(LL_WARNING, "Device %d-%d: Failed to get config descriptor for configuration 5, choosing configuration 4 instead.", bus, address); - desired_config = 4; - break; - } - if (config->bNumInterfaces != 3) { - usbmuxd_log(LL_WARNING, "Device %d-%d: Ignoring possibly bad configuration 5, choosing configuration 4 instead.", bus, address); - desired_config = 4; - break; - } - intf = &config->interface[2].altsetting[0]; - if (intf->bInterfaceClass != 0xFF || intf->bInterfaceSubClass != 0x2A || intf->bInterfaceProtocol != 0xFF) { - usbmuxd_log(LL_WARNING, "Device %d-%d: Ignoring possibly bad configuration 5, choosing configuration 4 instead.", bus, address); - desired_config = 4; - break; - } - } while (0); + // https://github.com/torvalds/linux/blob/72a85e2b0a1e1e6fb4ee51ae902730212b2de25c/include/uapi/linux/usb/cdc.h#L22 + // 2 for Communication class, 0xd for CDC NCM subclass + if(intf->bInterfaceClass == 2 && + intf->bInterfaceSubClass == 0xd) { + has_cdc_ncm = 1; + } + if(intf->bInterfaceClass == INTERFACE_CLASS && + intf->bInterfaceSubClass == INTERFACE_SUBCLASS && + intf->bInterfaceProtocol == INTERFACE_PROTOCOL) { + has_usbmux = 1; + } + } + + libusb_free_config_descriptor(config); + + if(has_valeria && has_usbmux) { + usbmuxd_log(LL_NOTICE, "Found Valeria and Apple USB Multiplexor in device %i-%i configuration 5", bus, address); + return 2; } + + if(has_cdc_ncm && has_usbmux) { + usbmuxd_log(LL_NOTICE, "Found CDC-NCM and Apple USB Multiplexor in device %i-%i configuration 5", bus, address); + return 3; + } + + return 0; +} + +/// @brief Finds and sets the valid configuration, interface and endpoints on the usb_device +static int set_valid_configuration(struct libusb_device* dev, struct usb_device *usbdev, struct libusb_device_handle *handle) +{ + int j, k, res, found = 0; + struct libusb_config_descriptor *config; + const struct libusb_interface_descriptor *intf; + struct libusb_device_descriptor devdesc = usbdev->devdesc; + int bus = usbdev->bus; + int address = usbdev->address; int current_config = 0; + if((res = libusb_get_configuration(handle, ¤t_config)) != 0) { - usbmuxd_log(LL_WARNING, "Could not get configuration for device %d-%d: %s", bus, address, libusb_error_name(res)); - libusb_close(handle); + usbmuxd_log(LL_WARNING, "Could not get current configuration for device %d-%d: %s", bus, address, libusb_error_name(res)); return -1; } - if (current_config != desired_config) { - struct libusb_config_descriptor *config; + + for(j = devdesc.bNumConfigurations ; j > 0 ; j--) { + if((res = libusb_get_config_descriptor_by_value(dev, j, &config)) != 0) { + usbmuxd_log(LL_NOTICE, "Could not get configuration %i descriptor for device %i-%i: %s", j, bus, address, libusb_error_name(res)); + continue; + } + for(k = 0 ; k < config->bNumInterfaces ; k++) { + intf = &config->interface[k].altsetting[0]; + if(intf->bInterfaceClass == INTERFACE_CLASS || + intf->bInterfaceSubClass == INTERFACE_SUBCLASS || + intf->bInterfaceProtocol == INTERFACE_PROTOCOL) { + usbmuxd_log(LL_NOTICE, "Found usbmux interface for device %i-%i: %i", bus, address, intf->bInterfaceNumber); + if(intf->bNumEndpoints != 2) { + usbmuxd_log(LL_WARNING, "Endpoint count mismatch for interface %i of device %i-%i", intf->bInterfaceNumber, bus, address); + continue; + } + if((intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT && + (intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) { + usbdev->interface = intf->bInterfaceNumber; + usbdev->ep_out = intf->endpoint[0].bEndpointAddress; + usbdev->ep_in = intf->endpoint[1].bEndpointAddress; + usbmuxd_log(LL_INFO, "Found interface %i with endpoints %02x/%02x for device %i-%i", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address); + found = 1; + break; + } else if((intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT && + (intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) { + usbdev->interface = intf->bInterfaceNumber; + usbdev->ep_out = intf->endpoint[1].bEndpointAddress; + usbdev->ep_in = intf->endpoint[0].bEndpointAddress; + usbmuxd_log(LL_INFO, "Found interface %i with swapped endpoints %02x/%02x for device %i-%i", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address); + found = 1; + break; + } else { + usbmuxd_log(LL_WARNING, "Endpoint type mismatch for interface %i of device %i-%i", intf->bInterfaceNumber, bus, address); + } + } + } + if(!found) { + libusb_free_config_descriptor(config); + continue; + } + // If set configuration is required, try to first detach all kernel drivers if (current_config == 0) { usbmuxd_log(LL_DEBUG, "Device %d-%d is unconfigured", bus, address); - } else if ((res = libusb_get_active_config_descriptor(dev, &config)) != 0) { - usbmuxd_log(LL_NOTICE, "Could not get old configuration descriptor for device %d-%d: %s", bus, address, libusb_error_name(res)); - } else { - for(j=0; jbNumInterfaces; j++) { - const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0]; - if((res = libusb_kernel_driver_active(handle, intf->bInterfaceNumber)) < 0) { - usbmuxd_log(LL_NOTICE, "Could not check kernel ownership of interface %d for device %d-%d: %s", intf->bInterfaceNumber, bus, address, libusb_error_name(res)); + } + if(current_config == 0 || config->bConfigurationValue != current_config) { + usbmuxd_log(LL_NOTICE, "Changing configuration of device %i-%i: %i -> %i", bus, address, current_config, config->bConfigurationValue); + for(k=0 ; k < config->bNumInterfaces ; k++) { + const struct libusb_interface_descriptor *intf1 = &config->interface[k].altsetting[0]; + if((res = libusb_kernel_driver_active(handle, intf1->bInterfaceNumber)) < 0) { + usbmuxd_log(LL_NOTICE, "Could not check kernel ownership of interface %d for device %d-%d: %s", intf1->bInterfaceNumber, bus, address, libusb_error_name(res)); continue; } if(res == 1) { - usbmuxd_log(LL_INFO, "Detaching kernel driver for device %d-%d, interface %d", bus, address, intf->bInterfaceNumber); - if((res = libusb_detach_kernel_driver(handle, intf->bInterfaceNumber)) < 0) { + usbmuxd_log(LL_INFO, "Detaching kernel driver for device %d-%d, interface %d", bus, address, intf1->bInterfaceNumber); + if((res = libusb_detach_kernel_driver(handle, intf1->bInterfaceNumber)) < 0) { usbmuxd_log(LL_WARNING, "Could not detach kernel driver, configuration change will probably fail! %s", libusb_error_name(res)); continue; } } } - libusb_free_config_descriptor(config); - } - - usbmuxd_log(LL_INFO, "Setting configuration for device %d-%d, from %d to %d", bus, address, current_config, desired_config); - if((res = libusb_set_configuration(handle, desired_config)) != 0) { - usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %s", desired_config, bus, address, libusb_error_name(res)); - libusb_close(handle); - return -1; + if((res = libusb_set_configuration(handle, j)) != 0) { + usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %s", j, bus, address, libusb_error_name(res)); + libusb_free_config_descriptor(config); + continue; + } } + + libusb_free_config_descriptor(config); + break; } - struct libusb_config_descriptor *config; - if((res = libusb_get_active_config_descriptor(dev, &config)) != 0) { - usbmuxd_log(LL_WARNING, "Could not get configuration descriptor for device %d-%d: %s", bus, address, libusb_error_name(res)); - libusb_close(handle); + if(!found) { + usbmuxd_log(LL_WARNING, "Could not find a suitable USB interface for device %i-%i", bus, address); return -1; } - struct usb_device *usbdev; - usbdev = malloc(sizeof(struct usb_device)); - memset(usbdev, 0, sizeof(*usbdev)); + return 0; +} - for(j=0; jbNumInterfaces; j++) { - const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0]; - if(intf->bInterfaceClass != INTERFACE_CLASS || - intf->bInterfaceSubClass != INTERFACE_SUBCLASS || - intf->bInterfaceProtocol != INTERFACE_PROTOCOL) - continue; - if(intf->bNumEndpoints != 2) { - usbmuxd_log(LL_WARNING, "Endpoint count mismatch for interface %d of device %d-%d", intf->bInterfaceNumber, bus, address); - continue; - } - if((intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT && - (intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) { - usbdev->interface = intf->bInterfaceNumber; - usbdev->ep_out = intf->endpoint[0].bEndpointAddress; - usbdev->ep_in = intf->endpoint[1].bEndpointAddress; - usbmuxd_log(LL_INFO, "Found interface %d with endpoints %02x/%02x for device %d-%d", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address); - break; - } else if((intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT && - (intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) { - usbdev->interface = intf->bInterfaceNumber; - usbdev->ep_out = intf->endpoint[1].bEndpointAddress; - usbdev->ep_in = intf->endpoint[0].bEndpointAddress; - usbmuxd_log(LL_INFO, "Found interface %d with swapped endpoints %02x/%02x for device %d-%d", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address); - break; - } else { - usbmuxd_log(LL_WARNING, "Endpoint type mismatch for interface %d of device %d-%d", intf->bInterfaceNumber, bus, address); - } +static void device_complete_initialization(struct mode_context *context, struct libusb_device_handle *handle) +{ + struct usb_device *usbdev = find_device(context->bus, context->address); + if(!usbdev) { + usbmuxd_log(LL_ERROR, "Device %d-%d is missing from device list, aborting initialization", context->bus, context->address); + return; } + struct libusb_device *dev = context->dev; + struct libusb_device_descriptor devdesc = usbdev->devdesc; + int bus = context->bus; + int address = context->address; + int res; + struct libusb_transfer *transfer; - if(j == config->bNumInterfaces) { - usbmuxd_log(LL_WARNING, "Could not find a suitable USB interface for device %d-%d", bus, address); - libusb_free_config_descriptor(config); - libusb_close(handle); - free(usbdev); - return -1; + if((res = set_valid_configuration(dev, usbdev, handle)) != 0) { + usbdev->alive = 0; + return; } - libusb_free_config_descriptor(config); - if((res = libusb_claim_interface(handle, usbdev->interface)) != 0) { usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %s", usbdev->interface, bus, address, libusb_error_name(res)); - libusb_close(handle); - free(usbdev); - return -1; + usbdev->alive = 0; + return; } transfer = libusb_alloc_transfer(0); if(!transfer) { usbmuxd_log(LL_WARNING, "Failed to allocate transfer for device %d-%d: %s", bus, address, libusb_error_name(res)); - libusb_close(handle); - free(usbdev); - return -1; + usbdev->alive = 0; + return; } unsigned char *transfer_buffer = malloc(1024 + LIBUSB_CONTROL_SETUP_SIZE + 8); if (!transfer_buffer) { usbmuxd_log(LL_WARNING, "Failed to allocate transfer buffer for device %d-%d: %s", bus, address, libusb_error_name(res)); - libusb_close(handle); - free(usbdev); - return -1; + usbdev->alive = 0; + return; } memset(transfer_buffer, '\0', 1024 + LIBUSB_CONTROL_SETUP_SIZE + 8); @@ -542,7 +605,7 @@ static int usb_device_add(libusb_device* dev) usbdev->address = address; usbdev->devdesc = devdesc; usbdev->speed = 480000000; - usbdev->dev = handle; + usbdev->handle = handle; usbdev->alive = 1; usbdev->wMaxPacketSize = libusb_get_max_packet_size(dev, usbdev->ep_out); if (usbdev->wMaxPacketSize <= 0) { @@ -562,6 +625,9 @@ static int usb_device_add(libusb_device* dev) case LIBUSB_SPEED_SUPER: usbdev->speed = 5000000000; break; + case LIBUSB_SPEED_SUPER_PLUS: + usbdev->speed = 10000000000; + break; case LIBUSB_SPEED_HIGH: case LIBUSB_SPEED_UNKNOWN: default: @@ -583,17 +649,164 @@ static int usb_device_add(libusb_device* dev) if((res = libusb_submit_transfer(transfer)) < 0) { usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d: %s", usbdev->bus, usbdev->address, libusb_error_name(res)); libusb_free_transfer(transfer); - libusb_close(handle); free(transfer_buffer); - free(usbdev); + usbdev->alive = 0; + return; + } +} + +static void switch_mode_cb(struct libusb_transfer* transfer) +{ + // For old devices not supporting mode swtich, if anything goes wrong - continue in current mode + struct mode_context* context = transfer->user_data; + struct usb_device *dev = find_device(context->bus, context->address); + if(!dev) { + usbmuxd_log(LL_WARNING, "Device %d-%d is missing from device list", context->bus, context->address); + } + if(transfer->status != LIBUSB_TRANSFER_COMPLETED) { + usbmuxd_log(LL_ERROR, "Failed to request mode switch for device %i-%i (%i). Completing initialization in current mode", + context->bus, context->address, transfer->status); + device_complete_initialization(context, transfer->dev_handle); + } + else { + unsigned char *data = libusb_control_transfer_get_data(transfer); + if(data[0] != 0) { + usbmuxd_log(LL_INFO, "Received unexpected response for device %i-%i mode switch (%i). Completing initialization in current mode", + context->bus, context->address, data[0]); + device_complete_initialization(context, transfer->dev_handle); + } + } + free(context); + if(transfer->buffer) + free(transfer->buffer); +} + +static void get_mode_cb(struct libusb_transfer* transfer) +{ + // For old devices not supporting mode swtich, if anything goes wrong - continue in current mode + int res; + struct mode_context* context = transfer->user_data; + struct usb_device *dev = find_device(context->bus, context->address); + if(!dev) { + usbmuxd_log(LL_ERROR, "Device %d-%d is missing from device list, aborting mode switch", context->bus, context->address); + free(context); + return; + } + + if(transfer->status != LIBUSB_TRANSFER_COMPLETED) { + usbmuxd_log(LL_ERROR, "Failed to request get mode for device %i-%i (%i). Completing initialization in current mode", + context->bus, context->address, transfer->status); + device_complete_initialization(context, transfer->dev_handle); + free(context); + return; + } + + unsigned char *data = libusb_control_transfer_get_data(transfer); + + char* desired_mode_char = getenv(ENV_DEVICE_MODE); + int desired_mode = desired_mode_char ? atoi(desired_mode_char) : 1; + int guessed_mode = guess_mode(context->dev, dev); + + // Response is 3:3:3:0 for initial mode, 5:3:3:0 otherwise. + usbmuxd_log(LL_INFO, "Received response %i:%i:%i:%i for get_mode request for device %i-%i", data[0], data[1], data[2], data[3], context->bus, context->address); + if(desired_mode >= 1 && desired_mode <= 5 && + guessed_mode > 0 && // do not switch mode if guess failed + guessed_mode != desired_mode) { + usbmuxd_log(LL_WARNING, "Switching device %i-%i mode to %i", context->bus, context->address, desired_mode); + + context->bRequest = APPLE_VEND_SPECIFIC_SET_MODE; + context->wValue = 0; + context->wIndex = desired_mode; + context->wLength = 1; + + if((res = submit_vendor_specific(transfer->dev_handle, context, switch_mode_cb)) != 0) { + usbmuxd_log(LL_WARNING, "Could not request to switch mode %i for device %i-%i (%i)", context->wIndex, context->bus, context->address, res); + dev->alive = 0; + free(context); + } + } + else { + usbmuxd_log(LL_WARNING, "Skipping switch device %i-%i mode from %i to %i", context->bus, context->address, guessed_mode, desired_mode); + device_complete_initialization(context, transfer->dev_handle); + free(context); + } + if(transfer->buffer) + free(transfer->buffer); +} + +static int usb_device_add(libusb_device* dev) +{ + int res; + // the following are non-blocking operations on the device list + uint8_t bus = libusb_get_bus_number(dev); + uint8_t address = libusb_get_device_address(dev); + struct libusb_device_descriptor devdesc; + struct usb_device *usbdev = find_device(bus, address); + if(usbdev) { + usbdev->alive = 1; + return 0; //device already found + } + + if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) { + usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %s", bus, address, libusb_error_name(res)); + return -1; + } + if(devdesc.idVendor != VID_APPLE) + return -1; + if((devdesc.idProduct != PID_APPLE_T2_COPROCESSOR) && + ((devdesc.idProduct < PID_APPLE_SILICON_RESTORE_LOW) || + (devdesc.idProduct > PID_APPLE_SILICON_RESTORE_MAX)) && + ((devdesc.idProduct < PID_RANGE_LOW) || + (devdesc.idProduct > PID_RANGE_MAX))) + return -1; + libusb_device_handle *handle; + usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address); + // No blocking operation can follow: it may be run in the libusb hotplug callback and libusb will refuse any + // blocking call + if((res = libusb_open(dev, &handle)) != 0) { + usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %s", bus, address, libusb_error_name(res)); return -1; } + // Add the created handle to the device list, so we can close it in case of failure/disconnection + usbdev = malloc(sizeof(struct usb_device)); + memset(usbdev, 0, sizeof(*usbdev)); + + usbdev->serial[0] = 0; + usbdev->bus = bus; + usbdev->address = address; + usbdev->devdesc = devdesc; + usbdev->speed = 0; + usbdev->handle = handle; + usbdev->alive = 1; + collection_init(&usbdev->tx_xfers); collection_init(&usbdev->rx_xfers); collection_add(&device_list, usbdev); + // On top of configurations, Apple have multiple "modes" for devices, namely: + // 1: An "initial" mode with 4 configurations + // 2: "Valeria" mode, where configuration 5 is included with interface for H.265 video capture (activated when recording screen with QuickTime in macOS) + // 3: "CDC NCM" mode, where configuration 5 is included with interface for Ethernet/USB (activated using internet-sharing feature in macOS) + // Request current mode asynchroniously, so it can be changed in callback if needed + usbmuxd_log(LL_INFO, "Requesting current mode from device %i-%i", bus, address); + struct mode_context* context = malloc(sizeof(struct mode_context)); + context->dev = dev; + context->bus = bus; + context->address = address; + context->bRequest = APPLE_VEND_SPECIFIC_GET_MODE; + context->wValue = 0; + context->wIndex = 0; + context->wLength = 4; + context->timeout = 1000; + + if(submit_vendor_specific(handle, context, get_mode_cb) != 0) { + usbmuxd_log(LL_WARNING, "Could not request current mode from device %d-%d", bus, address); + // Schedule device for close and cleanup + usbdev->alive = 0; + return -1; + } return 0; } @@ -654,7 +867,7 @@ int usb_discover(void) const char *usb_get_serial(struct usb_device *dev) { - if(!dev->dev) { + if(!dev->handle) { return NULL; } return dev->serial; @@ -662,7 +875,7 @@ const char *usb_get_serial(struct usb_device *dev) uint32_t usb_get_location(struct usb_device *dev) { - if(!dev->dev) { + if(!dev->handle) { return 0; } return (dev->bus << 16) | dev->address; @@ -670,7 +883,7 @@ uint32_t usb_get_location(struct usb_device *dev) uint16_t usb_get_pid(struct usb_device *dev) { - if(!dev->dev) { + if(!dev->handle) { return 0; } return dev->devdesc.idProduct; @@ -678,7 +891,7 @@ uint16_t usb_get_pid(struct usb_device *dev) uint64_t usb_get_speed(struct usb_device *dev) { - if (!dev->dev) { + if (!dev->handle) { return 0; } return dev->speed; diff --git a/src/usb.h b/src/usb.h index b1b9bb91..4e44cced 100644 --- a/src/usb.h +++ b/src/usb.h @@ -50,6 +50,10 @@ #define PID_APPLE_SILICON_RESTORE_LOW 0x1901 #define PID_APPLE_SILICON_RESTORE_MAX 0x1905 +#define ENV_DEVICE_MODE "USBMUXD_DEFAULT_DEVICE_MODE" +#define APPLE_VEND_SPECIFIC_GET_MODE 0x45 +#define APPLE_VEND_SPECIFIC_SET_MODE 0x52 + struct usb_device; int usb_init(void); diff --git a/systemd/Makefile.am b/systemd/Makefile.am index a23f1d10..1d40c25f 100644 --- a/systemd/Makefile.am +++ b/systemd/Makefile.am @@ -1,7 +1,7 @@ edit = \ $(SED) -r \ -e 's|@sbindir[@]|$(sbindir)|g' \ - -e 's|@localstatedir[@]|$(localstatedir)|g' \ + -e 's|@runstatedir[@]|$(runstatedir)|g' \ < $< > $@ || rm $@ if WANT_SYSTEMD diff --git a/systemd/usbmuxd.service.in b/systemd/usbmuxd.service.in index bee2476b..3a27aee4 100644 --- a/systemd/usbmuxd.service.in +++ b/systemd/usbmuxd.service.in @@ -4,4 +4,4 @@ Documentation=man:usbmuxd(8) [Service] ExecStart=@sbindir@/usbmuxd --user usbmux --systemd -PIDFile=@localstatedir@/run/usbmuxd.pid +PIDFile=@runstatedir@/usbmuxd.pid diff --git a/udev/39-usbmuxd.rules.in b/udev/39-usbmuxd.rules.in index c10a1983..ac15593e 100644 --- a/udev/39-usbmuxd.rules.in +++ b/udev/39-usbmuxd.rules.in @@ -4,10 +4,13 @@ SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/190[1-5]/*|5ac/8600/*", TAG+="systemd" # Initialize iOS devices into "deactivated" USB configuration state and activate usbmuxd -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/190[1-5]/*|5ac/8600/*", ACTION=="add", ENV{USBMUX_SUPPORTED}="1", ATTR{bConfigurationValue}="0", OWNER="usbmux", @udev_activation_rule@ +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/190[1-5]/*", ACTION=="add", ENV{USBMUX_SUPPORTED}="1", ATTR{bConfigurationValue}="0", OWNER="usbmux", @udev_activation_rule@ +# but make sure iBridge (T1) doesn't end up in an unconfigured state +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/8600/*", ACTION=="add", ENV{USBMUX_SUPPORTED}="1", ATTR{bConfigurationValue}="1", OWNER="usbmux", @udev_activation_rule@ + # Make sure properties don't get lost when bind action is called -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/190[1-5]/*|5ac/8600/*", ACTION=="bind", ENV{USBMUX_SUPPORTED}="1", OWNER="usbmux", @udev_activation_rule@ +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/190[1-5]/*|5ac/8600/*", ACTION=="bind", ENV{USBMUX_SUPPORTED}="1", OWNER="usbmux" # Exit usbmuxd when the last device is removed SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*|5ac/190[1-5]/*|5ac/8600/*", ACTION=="remove", RUN+="@sbindir@/usbmuxd -x"