diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58ec01d..71027da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,76 +2,12 @@ name: Build AltServer on: push: - workflow_dispatch: - inputs: - sync_upstream: - required: false - default: false - debug_enabled: - description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' - required: false - default: false - repository_dispatch: - - schedule: - - cron: "0 */4 * * *" # min hour day week year - env: REGISTRY: ghcr.io jobs: - check: - runs-on: ubuntu-latest - name: "Check Upstream Updates" - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - submodules: recursive - - - name: Check If Need Checking - id: needCheck - if: ${{ github.event_name == 'schedule' || ( github.event_name == 'workflow_dispatch' && github.event.inputs.sync_upstream ) }} - run: | - echo "needCheck=1" >> $GITHUB_ENV - - - name: Check Upstream Version - id: check - run: | - if [[ "$needCheck" == 1 ]]; then - echo "Checking changes in upstream_repo..." - - current_upstream=$(git ls-tree HEAD upstream_repo | awk '{ print $3 }') - echo "Current Upstream Commit: $current_upstream" - - git submodule update --remote -- upstream_repo - new_upstream=$( cd upstream_repo; git show -s --format=%H ) - echo "New Upstream Commit: $new_upstream" - - git reset --hard - git submodule update --init - if [[ "$current_upstream" != "$new_upstream" ]]; then - echo "Upstream got new commits, go updating!" - echo "::set-output name=updated::1" - else - echo "Upstream no change~" - echo "::set-output name=updated::0" - fi - else - echo "Needn't to check changes, directly return~" - echo "::set-output name=updated::0" - fi - - - uses: gautamkrishnar/keepalive-workflow@master - with: - commit_message: "[proj] keepalive-workflow auto commit" - outputs: - updated: ${{ steps.check.outputs.updated }} - build: - needs: [check] strategy: matrix: builder: [ghcr.io/nyamisty/altserver_builder_alpine_armv7, ghcr.io/nyamisty/altserver_builder_alpine_aarch64, ghcr.io/nyamisty/altserver_builder_alpine_amd64, ghcr.io/nyamisty/altserver_builder_alpine_i386] @@ -80,12 +16,9 @@ jobs: - name: Checkout uses: actions/checkout@v2 with: + ref: master # set the branch to merge to fetch-depth: 0 submodules: recursive - - name: Do Submodule Update - if: ${{ needs.check.outputs.updated == '1' }} - run: - git submodule update --remote -- upstream_repo - name: Set up QEMU uses: docker/setup-qemu-action@v1 @@ -101,9 +34,8 @@ jobs: run: | image=${{ matrix.builder }} docker pull $image - docker run -v ${PWD}:/workdir -w /workdir $image bash -c 'mkdir build; cd build; make -f ../Makefile -j3' - cp build/AltServer-* /tmp/build_output; chmod +x /tmp/build_output/* - sudo rm -rf build + docker run -v ${PWD}:/workdir -w /workdir $image make NO_USBMUXD_STUB=1 NO_UPNP_STUB=1 -j3 + mv AltServer-* /tmp/build_output git clean -fdX - name: Upload to github artifact uses: NyaMisty/upload-artifact-as-is@master @@ -112,7 +44,6 @@ jobs: release: runs-on: ubuntu-latest - if: ${{ startsWith(github.ref, 'refs/tags/') }} needs: [build] name: "release" steps: @@ -132,30 +63,9 @@ jobs: if [ "$(ls -A build_release)" ]; then exit 0; else exit 1; fi - name: Release uses: softprops/action-gh-release@v1 + if: ${{ startsWith(github.ref, 'refs/tags/') }} with: files: build_release/* env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - update_submodule: - runs-on: ubuntu-latest - needs: [check, build] - if: ${{ needs.check.outputs.updated == '1' }} - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - submodules: recursive - - name: Commit Submodule Update - run: | - git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" - git config --local user.name "github-actions[bot]" - git submodule update --remote -- upstream_repo - git commit -m "[server] Sync Upstream Repo commit" upstream_repo - - name: Push changes - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - branch: ${{ github.ref }} - \ No newline at end of file + diff --git a/.gitignore b/.gitignore index 9d1f8dd..6073a62 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ -/build *.o -.vscode \ No newline at end of file +*.a +/AltServer +/AltServerNet +/AltServerUPnP + +.vscode/** +.ccls-cache/** diff --git a/.gitmodules b/.gitmodules index e87767b..12433a6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,20 +1,18 @@ -[submodule "AltServer-Windows"] - path = upstream_repo - url = https://github.com/rileytestut/AltServer-Windows - branch = develop +[submodule "libraries/libimobiledevice"] + path = libraries/libimobiledevice + url = https://github.com/libimobiledevice/libimobiledevice [submodule "libraries/libplist"] path = libraries/libplist url = https://github.com/libimobiledevice/libplist - branch = master [submodule "libraries/libusbmuxd"] path = libraries/libusbmuxd url = https://github.com/libimobiledevice/libusbmuxd -[submodule "libraries/libimobiledevice"] - path = libraries/libimobiledevice - url = https://github.com/libimobiledevice/libimobiledevice [submodule "libraries/ideviceinstaller"] path = libraries/ideviceinstaller - url = https://github.com/libimobiledevice/ideviceinstaller -[submodule "libraries/libimobiledevice-glue"] - path = libraries/libimobiledevice-glue - url = https://github.com/libimobiledevice/libimobiledevice-glue + url = https://github.com/libimobiledevice/ideviceinstaller.git +[submodule "libraries/miniupnpc"] + path = libraries/miniupnpc + url = https://github.com/NyaMisty/minimalupnpc +[submodule "libraries/AltSign"] + path = libraries/AltSign + url = https://github.com/NyaMisty/AltSign-Linux diff --git a/Makefile b/Makefile index 0d3ce07..8740057 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,25 @@ -PROGRAM := AltServer +ifdef NO_USBMUXD_STUB + PROGRAM := AltServer +else + ifdef NO_UPNP_STUB + PROGRAM := AltServerNet + else + PROGRAM := AltServerUPnP + endif +endif + ARCH := $(shell gcc -dumpmachine | cut -d- -f 1) PROGRAM := $(PROGRAM)-$(ARCH) -CFLAGS := -DDEBUG -O0 -g +%.c.o : %.c + $(CC) $(CFLAGS) $(EXTRA_FLAGS) -o $@ -c $< + +%.cpp.o : %.cpp + $(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -o $@ -c $< + +CFLAGS := -DHAVE_CONFIG_H -DDEBUG -O0 -g ifeq ($(ARCH),i386) CFLAGS += -mno-default @@ -16,115 +31,79 @@ endif CXXFLAGS = $(CFLAGS) -std=c++17 -CFLAGS += -DNO_USBMUXD_STUB - -ROOT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) -include $(ROOT_DIR)/makefiles/main.mak - -$(BUILD_DIR)/libimobiledevice.a $(BUILD_DIR)/libplist.a : - $(MAKE) -f $(ROOT_DIR)/makefiles/libimobiledevice-build/libimobiledevice.mak -lib_libimobiledevice: $(BUILD_DIR)/libimobiledevice.a $(BUILD_DIR)/libplist.a -lib_libimobiledevice_clean : - $(MAKE) -f $(ROOT_DIR)/makefiles/libimobiledevice-build/libimobiledevice.mak clean -.PHONY: $(BUILD_DIR)/libimobiledevice.a $(BUILD_DIR)/libplist.a lib_libimobiledevice lib_libimobiledevice_clean - -$(BUILD_DIR)/AltSign.a: - $(MAKE) -f $(ROOT_DIR)/makefiles/AltSign-build/AltSign.mak EXTRA_FLAGS=$(libplist_include) -lib_AltSign : $(BUILD_DIR)/AltSign.a $(BUILD_DIR)/libplist.a -lib_AltSign_clean : - $(MAKE) -f $(ROOT_DIR)/makefiles/AltSign-build/AltSign.mak clean -.PHONY: $(BUILD_DIR)/AltSign.a $(BUILD_DIR)/libplist.a lib_AltSign lib_AltSign_clean - -$(BUILD_DIR)/dnssd_loader.a: - $(MAKE) -f $(ROOT_DIR)/makefiles/dnssd_loader-build/dnssd_loader.mak -lib_dnssd_loader : $(BUILD_DIR)/dnssd_loader.a -lib_dnssd_loader_clean : - $(MAKE) -f $(ROOT_DIR)/makefiles/dnssd_loader-build/dnssd_loader.mak clean -.PHONY: $(BUILD_DIR)/dnssd_loader.a lib_dnssd_loader lib_dnssd_loader_clean +main_src := $(wildcard src/*.c) $(wildcard src/*.cpp) + +libimobiledevice_src := $(wildcard libraries/libimobiledevice/src/*.c) $(wildcard libraries/libimobiledevice/common/*.c) +ifdef NO_USBMUXD_STUB + CFLAGS += -DNO_USBMUXD_STUB + libimobiledevice_src += $(wildcard libraries/libusbmuxd/src/*.c) + libimobiledevice_src += $(wildcard libraries/libusbmuxd/common/*.c) +else + main_src += src/phone/libusbmuxd-stub.c + ifdef NO_UPNP_STUB + CFLAGS += -DNO_UPNP_STUB + else + libimobiledevice_src := $(filter-out libraries/libimobiledevice/src/idevice.c, $(libimobiledevice_src)) + main_src += src/phone/idevice-stub.c + endif +endif +libimobiledevice_include := -Ilibraries/libimobiledevice/include -Ilibraries/libimobiledevice -Ilibraries/libusbmuxd/include -include $(ROOT_DIR)/makefiles/libimobiledevice-build/libimobiledevice-files.mak -include $(ROOT_DIR)/makefiles/AltSign-build/AltSign-files.mak -include $(ROOT_DIR)/makefiles/dnssd_loader-build/dnssd_loader-files.mak +libplist_src := $(wildcard libraries/libplist/src/*.c) libraries/libplist/libcnary/node.c libraries/libplist/libcnary/node_list.c +libplist_include := -Ilibraries/libplist/include -#libimobiledevice_include := -I$(LIB_DIR)/libimobiledevice/include -I$(LIB_DIR)/libimobiledevice -I$(LIB_DIR)/libusbmuxd/include -#libplist_include := -I$(LIB_DIR)/libplist/include -#altsign_include := -I$(BUILD_DIR)/AltSign_patched +miniupnpc_src = minissdpc.c miniwget.c minixml.c igd_desc_parse.c minisoap.c \ + miniupnpc.c upnpreplyparse.c upnpcommands.c upnperrors.c \ + connecthostport.c portlistingparse.c receivedata.c upnpdev.c \ + addr_is_reserved.c +miniupnpc_src := $(miniupnpc_src:%.c=libraries/miniupnpc/%.c) +miniupnpc_include := -Ilibraries/miniupnpc -#INC_CFLAGS := -Ilibraries +INC_CFLAGS := -Ilibraries INC_CFLAGS += $(libimobiledevice_include) INC_CFLAGS += $(libplist_include) -INC_CFLAGS += $(altsign_include) -INC_CFLAGS += $(dnssd_loader_include) - -include $(ROOT_DIR)/makefiles/AltWindowsShim.mak - -main_srcroot := $(UPSTREAM_DIR)/AltServer -main_override_srcroot := $(ROOT_DIR)/src -main_override_src := $(wildcard $(main_override_srcroot)/*.cpp) +INC_CFLAGS += $(miniupnpc_include) +INC_CFLAGS += -Ilibraries/AltSign -main_orisrc := $(wildcard $(main_srcroot)/*.cpp) $(wildcard $(main_srcroot)/*.c) +allsrc := $(main_src) +allsrc += $(libimobiledevice_src) +allsrc += $(libplist_src) +allsrc += $(miniupnpc_src) -main_orisrc := $(filter-out $(main_srcroot)/AltServer.cpp, $(main_orisrc)) -main_orisrc := $(filter-out $(main_override_src:$(main_override_srcroot)/%=$(main_srcroot)/%), $(main_orisrc)) -#main_orisrc := $(filter-out $(main_srcroot)/AltServerApp.cpp, $(main_orisrc)) -#main_orisrc := $(filter-out $(main_srcroot)/AnisetteDataManager.cpp, $(main_orisrc)) +allobj = $(addsuffix .o, $(allsrc)) -#$(BUILD_DIR)/objs/%.c.o : $(BUILD_DIR)/%.c -# python3 $(ROOT_DIR)/makefiles/AltSign-build/rewrite_altserver_source.py "$<" | $(CC) -x c -I`dirname $<` $(CFLAGS) $(EXTRA_FLAGS) -o $@ -c - -# #$(CC) $(CFLAGS) $(EXTRA_FLAGS) -o $@ -c $< -# -#$(BUILD_DIR)/objs/%.cpp.o : $(BUILD_DIR)/%.cpp -# python3 $(ROOT_DIR)/makefiles/AltSign-build/rewrite_altserver_source.py "$<" | $(CXX) -x c++ -I`dirname $<` $(CXXFLAGS) $(EXTRA_FLAGS) -o $@ -c - -# #$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -o $@ -c $< +$(addsuffix .o, $(libimobiledevice_src)) : EXTRA_FLAGS := -Ilibraries $(libimobiledevice_include) $(libplist_include) -Ilibraries/libimobiledevice/common -Ilibraries/libusbmuxd/common +libraries/libimobiledevice.a : $(addsuffix .o, $(libimobiledevice_src)) + ar rcs $@ $^ -main_patched_root := $(BUILD_DIR)/AltServer_patched -main_orifiles := $(wildcard $(main_srcroot)/*.*) -main_newfiles := $(main_orifiles:$(main_srcroot)/%=$(main_patched_root)/%) - -$(main_patched_root)/%: $(main_srcroot)/% - mkdir -p `dirname "$@"` - python3 $(ROOT_DIR)/makefiles/rewrite_altserver_source.py "$<" > $@ - -$(main_newfiles) : $(main_orifiles) - -preprocess : $(main_newfiles) -.PHONY : preprocess - - -$(BUILD_DIR)/objs/%.c.o : $(BUILD_DIR)/%.c - mkdir -p $(@D) - $(CC) $(CFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -$(BUILD_DIR)/objs/%.cpp.o : $(BUILD_DIR)/%.cpp - mkdir -p $(@D) - $(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -$(BUILD_DIR)/objs/%.c.o : $(ROOT_DIR)/%.c - mkdir -p $(@D) - $(CC) $(CFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -$(BUILD_DIR)/objs/%.cpp.o : $(ROOT_DIR)/%.cpp - mkdir -p $(@D) - $(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -o $@ -c $< +$(addsuffix .o, $(libplist_src)) : EXTRA_FLAGS := -Ilibraries $(libplist_include) -Ilibraries/libplist/libcnary/include -Ilibraries/libplist/src +libraries/libplist.a : $(addsuffix .o, $(libplist_src)) + ar rcs $@ $^ -main_newsrc := $(main_orisrc:$(main_srcroot)/%=$(main_patched_root)/%) -#main_objs = $(main_orisrc:$(UPSTREAM_DIR)/%=$(BUILD_DIR)/objs/%.o) +# $(miniupnpc_src:.c=.o) : $(miniupnpc_src) +# $(CC) $(CFLAGS) -Ilibraries -c $< +$(addsuffix .o, $(miniupnpc_src)) : EXTRA_FLAGS := -Ilibraries $(miniupnpc_include) +libraries/miniupnp.a : $(addsuffix .o, $(miniupnpc_src)) + ar rcs $@ $^ -main_objs = $(main_newsrc:$(BUILD_DIR)/%=$(BUILD_DIR)/objs/%.o) $(main_override_src:$(ROOT_DIR)/%=$(BUILD_DIR)/objs/%.o) $(shim_src:$(MAIN_DIR)/%=$(BUILD_DIR)/objs/%.o) +$(addsuffix .o, $(main_src)) : EXTRA_FLAGS := -Ilibraries $(INC_CFLAGS) -$(main_objs) : lib_AltSign lib_libimobiledevice lib_dnssd_loader +#%.o : %.c +# $(CC) $(CFLAGS) $(INC_CFLAGS) -c $< -o $@ -$(main_objs) : EXTRA_FLAGS := -I$(main_patched_root) -I$(ROOT_DIR)/src -fpermissive -include "common.h" $(INC_CFLAGS) +lib_AltSign: + $(MAKE) -C libraries/AltSign -LDFLAGS = -static -lssl -lcrypto -lpthread -lcorecrypto_static -lzip -lm -lz -lcpprest -lboost_system -lboost_filesystem -lstdc++ -lssl -lcrypto -luuid +LDFLAGS = libraries/AltSign/AltSign.a -static -lssl -lcrypto -lpthread -lcorecrypto_static -lzip -lm -lz -lcpprest -lboost_system -lboost_filesystem -lstdc++ -lssl -lcrypto -luuid +$(PROGRAM):: lib_AltSign -$(BUILD_DIR)/$(PROGRAM):: $(main_objs) $(BUILD_DIR)/libimobiledevice.a $(BUILD_DIR)/AltSign.a $(BUILD_DIR)/libplist.a $(BUILD_DIR)/dnssd_loader.a +$(PROGRAM):: $(addsuffix .o, $(main_src)) libraries/miniupnp.a libraries/libimobiledevice.a libraries/libplist.a $(CC) -o $@ $^ $(LDFLAGS) -.PHONY: clean all -clean:: lib_libimobiledevice_clean lib_AltSign_clean lib_dnssd_loader_clean - rm -f $(main_objs) $(BUILD_DIR)/$(PROGRAM) +.PHONY: clean all lib_AltSign +clean: + rm -f $(allobj) libraries/*.a $(PROGRAM) -all:: preprocess $(BUILD_DIR)/$(PROGRAM) +all: $(PROGRAM) .DEFAULT_GOAL := all diff --git a/README.md b/README.md index 345d77c..9db7a8d 100644 --- a/README.md +++ b/README.md @@ -3,51 +3,34 @@ AltServer for AltStore, but on-device ## Usage -- Install IPA: `./AltServer -u [UDID] -a [AppleID account] -p [AppleID password] [ipaPath.ipa]` -- Running as AltServer Daemon: `./AltServer` -- Full usage (maybe outdated, refer to `./AltServer -h` for the newest): -``` -Usage: AltServer-Linux options [ ipa-file ] - -h --help Display this usage information. - -u --udid UDID Device's UDID, only needed when installing IPA. - -a --appleID AppleID Apple ID to sign the ipa, only needed when installing IPA. - -p --password passwd Password of Apple ID, only needed when installing IPA. - -d --debug Print debug output, can be used several times to increase debug level. +- For build configuration 1 (AltServer): Works just like a normal AltServer on windows + - Install IPA: `./AltServer -u [UDID] -a [AppleID account] -p [AppleID password] [ipaPath.ipa]` + - Running as AltServer Daemon: `./AltServer` +- For build configuration 2 (AltServerNet): AltServer over Network + - Install IPA: `./AltServerNet -u [UDID] -P [jitterbug pair file] -i [device IP] -a [AppleID account] -p [AppleID password] [ipaPath.ipa]` + - Running as AltServer Daemon not supported +- For build configuration 2 (AltServerUPnP): AltServer over Network + - Install IPA: `./AltServerUPnP -u [UDID] -P [jitterbug pair file] -i [device IP] -a [AppleID account] -p [AppleID password] [ipaPath.ipa]` -The following environment var can be set for some special situation: - - ALTSERVER_ANISETTE_SERVER: Set to custom anisette server URL - if not set, the default one: https://armconverter.com/anisette/irGb3Quww8zrhgqnzmrx, is used - - ALTSERVER_NO_SUBSCRIBE: (*unused*) Please enable this for usbmuxd server that do not correctly usbmuxd_listen interfaces -``` - -## Download - -- Precompiled static binary can be downloaded in Release ( also have a look at pre-release ;) ) -- Nightly version is available as Github Actions artifacts - -## TODO / Special Features -- [x] Track upstream (AltServer-Windows) develop branch (i.e. Beta version) -- [x] Support Offline Anisette Data Generation (i.e. without Sideloadly) - - Finsihed, please run [alt_anisette_server](https://hub.docker.com/r/nyamisty/alt_anisette_server) & use `ALTSERVER_ANISETTE_SERVER` to specify custom server URL -- [x] Support Wi-Fi Refresh - - [netmuxd](https://github.com/jkcoxson/netmuxd) now supports network devices (needs version > v0.1.1, be sure to check pre-release) - - Download `netmuxd`, stop the original `usbmuxd`, and run `netmuxd` before running `AltServer-Linux` - - ~If netmuxd does not work, please try using special env var `ALTSERVER_NO_SUBSCRIBE`. Enabling this would disable **auto-refresh when plugged-in** of USB devices~ - ----- - -## Advanced: Build Instruction (check Github Actions if you cannot build) +## Build Instruction - Preparation: `git clone --recursive https://github.com/NyaMisty/AltServer-Linux` -- Install dependencies (see notes below): corecrypto_static, cpprestsdk static lib, boost static lib +- Install corecrypto_static, cpprestsdk static lib, boost static lib + +- Build Configuration 1 (AltServer): Simple AltServer running on normal Linux host, working through usbmuxd + ``` + make NO_USBMUXD_STUB=1 NO_UPNP_STUB=1 + ``` + +- Build Configuration 2 (AltServerNet): AltServer running on normal Linux host, working through manually specifying IP + ``` + make NO_UPNP_STUB=1 + ``` -- Build: +- Build Configuration 3 (AltServerUPnP): AltServer running on iSH, working through manually specifying IP in a UPnP enabled router ``` - cd AltServer-Linux - mkdir build - make -f ../Makefile -j3 - ls AltServer-* + make ``` - My own build note for you @@ -66,8 +49,11 @@ The following environment var can be set for some special situation: git clone https://github.com/nih-at/libzip; cd libzip; mkdir build; cd build; cmake -DBUILD_SHARED_LIBS=0 ..; make; make install 6. Compile AltServer-Linux git clone --recursive https://github.com/NyaMisty/AltServer-Linux - cd AltServer-Linux - make -f ../Makefile -j3 + make NO_USBMUXD_STUB=1 NO_UPNP_STUB=1 (if you're compiling for ARM, i.e. armv7 or aarch64, you'll have to remove the -mno-default flag in Makefile) ``` + +## TODO + +- [ ] Support Wi-Fi Refresh diff --git a/archlinux-buildenv/AltServer-Linux/PKGBUILD b/archlinux-buildenv/AltServer-Linux/PKGBUILD new file mode 100644 index 0000000..854feea --- /dev/null +++ b/archlinux-buildenv/AltServer-Linux/PKGBUILD @@ -0,0 +1,50 @@ +# Maintainer: Zeno Chen + +pkgname=AltServer-Linux +pkgver=0.0.2 +pkgrel=1 +pkgdesc='AltServer for AltStore, but on-device' +arch=(x86_64) +url=https://github.com/NyaMisty/AltServer-Linux +license=(GPL3) +depends=() +makedepends=( + git + unzip + clang boost websocketpp python-cryptography + cpprestsdk crypto++-static libzip-static openssl-static +) + +_tag=9a33a7981a011a7a8d82686ed6123cefb2e319fc +source=(git+https://github.com/NyaMisty/AltServer-Linux.git#tag=${_tag}) +sha256sums=(SKIP) + +_target='NO_USBMUXD_STUB=1 NO_UPNP_STUB=1' +pkgver() { + cd ${pkgname} + git describe --tags | sed 's/-.*$//g; s/v//g' +} + +prepare() { + cd ${pkgname} + git submodule init + #git config submodule.mysubmodule.url "$srcdir/mysubmodule" + git submodule update + sed -i 's/.\/AltServerData/\/var\/AltServerData/g' src/AltServerApp.cpp +} + +build() { + cd ${pkgname} + make ${_target} +} + +package() { + cd ${pkgname} + # create the data for altserver + mkdir -p "$pkgdir/usr/bin/" + # create the execute for alterserver + mkdir -p "$pkgdir/var/AltServerData" + install -Dm0755 "${srcdir}/${pkgname}/AltServer-x86_64" "$pkgdir/usr/bin/AltServer" +} + +# vim: ts=2 sw=2 et: diff --git a/archlinux-buildenv/cpprestsdk/PKGBUILD b/archlinux-buildenv/cpprestsdk/PKGBUILD new file mode 100644 index 0000000..583934e --- /dev/null +++ b/archlinux-buildenv/cpprestsdk/PKGBUILD @@ -0,0 +1,41 @@ +# Modifier: Zeno Chen +# Maintainer: Bart Libert +# Contributor: Michael Yang + +pkgname=cpprestsdk +pkgver=2.10.18 +pkgrel=1 +pkgdesc="A cross-platform, modern, and asynchronous library that enables developers to access and author connected applications" +arch=('i686' 'x86_64' 'armv7h') +url="https://github.com/Microsoft/cpprestsdk/" +license=('Apache') +depends=('openssl>=1.0.0' 'zlib' 'gcc-libs') +makedepends=('cmake>=2.6.0' 'boost' 'websocketpp') +conflicts=('casablanca' 'casablanca-git' 'cpprestsdk-git') +source=("${pkgname}-${pkgver}.tar.gz::https://github.com/Microsoft/cpprestsdk/archive/v${pkgver}.tar.gz") +sha512sums=('5f0699e7ba509e16d6a3000e6ac448f6dbc134b8e03de9ab174ba749ad7efa76cbfccb623b226d82f5dba35ef6292f0cdf121b5315d524a5a28454038d420fab') + +prepare() { + cd ${srcdir}/${pkgname}-${pkgver}/ + mkdir -p build +} + +build() { + cd ${srcdir}/${pkgname}-${pkgver}/build + cmake ../Release \ + -DBUILD_TESTS=OFF \ + -DBUILD_SAMPLES=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DBUILD_SHARED_LIBS=0 \ + -DWERROR=OFF + make -j`nproc` +} + +package() { + cd ${srcdir}/${pkgname}-${pkgver}/ + make -C build DESTDIR="${pkgdir}" install + install -Dm644 license.txt ${pkgdir}/usr/share/licenses/${pkgname}/LICENSE + install -Dm644 ThirdPartyNotices.txt ${pkgdir}/usr/share/licenses/${pkgname}/ThirdPartyNotices +} diff --git a/archlinux-buildenv/crypto++-static/PKGBUILD b/archlinux-buildenv/crypto++-static/PKGBUILD new file mode 100644 index 0000000..f995a53 --- /dev/null +++ b/archlinux-buildenv/crypto++-static/PKGBUILD @@ -0,0 +1,39 @@ +# Maintainer: Zeno Chen +pkgname=crypto++-static +pkgver=8.6.0 +pkgrel=1 +pkgdesc='A free C++ class library of cryptographic schemes' +arch=(x86_64) +url=https://www.cryptopp.com/ +license=(custom) +depends=(gcc-libs) +makedepends=( + git + unzip + crypto++ +) + +_tag=69bf6b53052b59ccb57ce068ce741988ae087317 +source=(git+https://github.com/weidai11/cryptopp.git#tag=${_tag}) +sha256sums=(SKIP) + +pkgver() { + cd cryptopp + git describe --tags | sed 's/^CRYPTOPP_//; s/_/./g' +} + +build() { + export CXXFLAGS="$CXXFLAGS -DNDEBUG -fPIC" + make PREFIX=/usr -C cryptopp static +} + +#check() { +# make PREFIX=/usr -C cryptopp test +#} + +package() { + make DESTDIR="${pkgdir}" PREFIX=/usr -C cryptopp install + rm -rf "${pkgdir}"/usr/{include,bin,share} +} + +# vim: ts=2 sw=2 et: diff --git a/archlinux-buildenv/libzip-static/PKGBUILD b/archlinux-buildenv/libzip-static/PKGBUILD new file mode 100644 index 0000000..e23add9 --- /dev/null +++ b/archlinux-buildenv/libzip-static/PKGBUILD @@ -0,0 +1,36 @@ +# Maintainer: Zeno Chen +pkgname=libzip-static +pkgver=1.8.0 +pkgrel=1 +pkgdesc="A C library for reading, creating, and modifying zip archives" +url="https://libzip.org" +license=('BSD') +arch=('x86_64') +depends=('zlib' 'bzip2' 'zstd' 'openssl' 'gnutls' 'libzip') +makedepends=('cmake') +provides=('libzip.a') +source=("${url}/download/libzip-${pkgver}.tar.xz") +sha256sums=('f0763bda24ba947e80430be787c4b068d8b6aa6027a26a19923f0acfa3dac97e') + +build() { + cd "libzip-${pkgver}" + + mkdir build && cd build + cmake -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DBUILD_SHARED_LIBS=OFF \ + .. + make +} + +package() { + cd "libzip-${pkgver}"/build + + make DESTDIR="${pkgdir}" install + + rm -rf "${pkgdir}"/usr/{include,bin,share} + + rm -rf "${pkgdir}/usr/lib/cmake" + + rm -rf "${pkgdir}/usr/lib/pkgconfig" +} diff --git a/archlinux-buildenv/openssl-static/PKGBUILD b/archlinux-buildenv/openssl-static/PKGBUILD new file mode 100644 index 0000000..0ffbbb8 --- /dev/null +++ b/archlinux-buildenv/openssl-static/PKGBUILD @@ -0,0 +1,63 @@ +# Maintainer: Zeno Chen +pkgname=openssl-static +_ver=1.1.1l +# use a pacman compatible version scheme +pkgver=${_ver/[a-z]/.${_ver//[0-9.]/}} +pkgrel=1 +pkgdesc='The Open Source toolkit for Secure Sockets Layer and Transport Layer Security' +arch=('x86_64') +url='https://www.openssl.org' +license=('custom:BSD') +depends=('glibc') +makedepends=('perl') +optdepends=('ca-certificates' 'perl' 'openssl') +replaces=('openssl-perl' 'openssl-doc') +backup=('etc/ssl/openssl.cnf') +source=("https://www.openssl.org/source/openssl-${_ver}.tar.gz" + 'ca-dir.patch') +sha256sums=('9ab4418d5a2f79f59927c1e27afe75dad519dc4afa1baacd03e5f620a8c3167a' + 'SKIP') +#validpgpkeys=('8657ABB260F056B1E5190839D9C4D26D0E604491' +# '7953AC1FBC3DC8B3B292393ED5E9E43F7DF9EE8C') + +prepare() { + cd "$srcdir/openssl-$_ver" + + # set ca dir to /etc/ssl by default + patch -p0 -i "$srcdir/ca-dir.patch" +} + +build() { + cd "$srcdir/openssl-$_ver" + + # mark stack as non-executable: http://bugs.archlinux.org/task/12434 + ./Configure --prefix=/usr --openssldir=/etc/ssl --libdir=lib \ + no-shared no-ssl3-method enable-ec_nistp_64_gcc_128 linux-x86_64 \ + "-Wa,--noexecstack ${CPPFLAGS} ${CFLAGS} ${LDFLAGS}" + + make depend + make +} + +check() { + cd "$srcdir/openssl-$_ver" + + # the test fails due to missing write permissions in /etc/ssl + # revert this patch for make test + patch -p0 -R -i "$srcdir/ca-dir.patch" + + make test + + patch -p0 -i "$srcdir/ca-dir.patch" + # re-run make to re-generate CA.pl from th patched .in file. + make apps/CA.pl +} + +package() { + cd "$srcdir/openssl-$_ver" + + make DESTDIR="$pkgdir" MANDIR=/usr/share/man MANSUFFIX=ssl install_sw install_ssldirs install_man_docs + + rm -rf "${pkgdir}"/etc + rm -rf "${pkgdir}"/usr/{bin,include,lib/engines-1.1,lib/pkgconfig,share} +} diff --git a/libraries/AltSign b/libraries/AltSign new file mode 160000 index 0000000..0daf107 --- /dev/null +++ b/libraries/AltSign @@ -0,0 +1 @@ +Subproject commit 0daf1076fc29682884bbae793453856de454befe diff --git a/makefiles/libimobiledevice-build/config.h_bak b/libraries/config.h similarity index 100% rename from makefiles/libimobiledevice-build/config.h_bak rename to libraries/config.h diff --git a/libraries/dnssd_loader/dns_sd.h b/libraries/dnssd_loader/dns_sd.h deleted file mode 100644 index 085067f..0000000 --- a/libraries/dnssd_loader/dns_sd.h +++ /dev/null @@ -1,1722 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _DNS_SD_H -#define _DNS_SD_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* standard calling convention under Win32 is __stdcall */ -/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */ -/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */ -#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64) -#define DNSSD_API __stdcall -#else -#define DNSSD_API -#endif - -/* stdint.h does not exist on FreeBSD 4.x; its types are defined in sys/types.h instead */ -#if defined(__FreeBSD__) && (__FreeBSD__ < 5) -#include - -/* Likewise, on Sun, standard integer types are in sys/types.h */ -#elif defined(__sun__) -#include - -/* EFI does not have stdint.h, or anything else equivalent */ -#elif defined(EFI32) || defined(EFI64) -typedef UINT8 uint8_t; -typedef INT8 int8_t; -typedef UINT16 uint16_t; -typedef INT16 int16_t; -typedef UINT32 uint32_t; -typedef INT32 int32_t; - -/* Windows has its own differences */ -#elif defined(_WIN32) -#include -#define _UNUSED -#define bzero(a, b) memset(a, 0, b) -#ifndef _MSL_STDINT_H -typedef UINT8 uint8_t; -typedef INT8 int8_t; -typedef UINT16 uint16_t; -typedef INT16 int16_t; -typedef UINT32 uint32_t; -typedef INT32 int32_t; -#endif - -/* All other Posix platforms use stdint.h */ -#else -#include -#endif - -/* DNSServiceRef, DNSRecordRef - * - * Opaque internal data types. - * Note: client is responsible for serializing access to these structures if - * they are shared between concurrent threads. - */ - -typedef struct _DNSServiceRef_t *DNSServiceRef; -typedef struct _DNSRecordRef_t *DNSRecordRef; - -/* General flags used in functions defined below */ -enum - { - kDNSServiceFlagsMoreComing = 0x1, - /* MoreComing indicates to a callback that at least one more result is - * queued and will be delivered following immediately after this one. - * Applications should not update their UI to display browse - * results when the MoreComing flag is set, because this would - * result in a great deal of ugly flickering on the screen. - * Applications should instead wait until until MoreComing is not set, - * and then update their UI. - * When MoreComing is not set, that doesn't mean there will be no more - * answers EVER, just that there are no more answers immediately - * available right now at this instant. If more answers become available - * in the future they will be delivered as usual. - */ - - kDNSServiceFlagsAdd = 0x2, - kDNSServiceFlagsDefault = 0x4, - /* Flags for domain enumeration and browse/query reply callbacks. - * "Default" applies only to enumeration and is only valid in - * conjuction with "Add". An enumeration callback with the "Add" - * flag NOT set indicates a "Remove", i.e. the domain is no longer - * valid. - */ - - kDNSServiceFlagsNoAutoRename = 0x8, - /* Flag for specifying renaming behavior on name conflict when registering - * non-shared records. By default, name conflicts are automatically handled - * by renaming the service. NoAutoRename overrides this behavior - with this - * flag set, name conflicts will result in a callback. The NoAutorename flag - * is only valid if a name is explicitly specified when registering a service - * (i.e. the default name is not used.) - */ - - kDNSServiceFlagsShared = 0x10, - kDNSServiceFlagsUnique = 0x20, - /* Flag for registering individual records on a connected - * DNSServiceRef. Shared indicates that there may be multiple records - * with this name on the network (e.g. PTR records). Unique indicates that the - * record's name is to be unique on the network (e.g. SRV records). - */ - - kDNSServiceFlagsBrowseDomains = 0x40, - kDNSServiceFlagsRegistrationDomains = 0x80, - /* Flags for specifying domain enumeration type in DNSServiceEnumerateDomains. - * BrowseDomains enumerates domains recommended for browsing, RegistrationDomains - * enumerates domains recommended for registration. - */ - - kDNSServiceFlagsLongLivedQuery = 0x100, - /* Flag for creating a long-lived unicast query for the DNSServiceQueryRecord call. */ - - kDNSServiceFlagsAllowRemoteQuery = 0x200, - /* Flag for creating a record for which we will answer remote queries - * (queries from hosts more than one hop away; hosts not directly connected to the local link). - */ - - kDNSServiceFlagsForceMulticast = 0x400, - /* Flag for signifying that a query or registration should be performed exclusively via multicast DNS, - * even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS. - */ - - kDNSServiceFlagsReturnCNAME = 0x800 - /* Flag for returning CNAME records in the DNSServiceQueryRecord call. CNAME records are - * normally followed without indicating to the client that there was a CNAME record. - */ - }; - -/* - * The values for DNS Classes and Types are listed in RFC 1035, and are available - * on every OS in its DNS header file. Unfortunately every OS does not have the - * same header file containing DNS Class and Type constants, and the names of - * the constants are not consistent. For example, BIND 8 uses "T_A", - * BIND 9 uses "ns_t_a", Windows uses "DNS_TYPE_A", etc. - * For this reason, these constants are also listed here, so that code using - * the DNS-SD programming APIs can use these constants, so that the same code - * can compile on all our supported platforms. - */ - -enum - { - kDNSServiceClass_IN = 1 /* Internet */ - }; - -enum - { - kDNSServiceType_A = 1, /* Host address. */ - kDNSServiceType_NS = 2, /* Authoritative server. */ - kDNSServiceType_MD = 3, /* Mail destination. */ - kDNSServiceType_MF = 4, /* Mail forwarder. */ - kDNSServiceType_CNAME = 5, /* Canonical name. */ - kDNSServiceType_SOA = 6, /* Start of authority zone. */ - kDNSServiceType_MB = 7, /* Mailbox domain name. */ - kDNSServiceType_MG = 8, /* Mail group member. */ - kDNSServiceType_MR = 9, /* Mail rename name. */ - kDNSServiceType_NULL = 10, /* Null resource record. */ - kDNSServiceType_WKS = 11, /* Well known service. */ - kDNSServiceType_PTR = 12, /* Domain name pointer. */ - kDNSServiceType_HINFO = 13, /* Host information. */ - kDNSServiceType_MINFO = 14, /* Mailbox information. */ - kDNSServiceType_MX = 15, /* Mail routing information. */ - kDNSServiceType_TXT = 16, /* One or more text strings. */ - kDNSServiceType_RP = 17, /* Responsible person. */ - kDNSServiceType_AFSDB = 18, /* AFS cell database. */ - kDNSServiceType_X25 = 19, /* X_25 calling address. */ - kDNSServiceType_ISDN = 20, /* ISDN calling address. */ - kDNSServiceType_RT = 21, /* Router. */ - kDNSServiceType_NSAP = 22, /* NSAP address. */ - kDNSServiceType_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */ - kDNSServiceType_SIG = 24, /* Security signature. */ - kDNSServiceType_KEY = 25, /* Security key. */ - kDNSServiceType_PX = 26, /* X.400 mail mapping. */ - kDNSServiceType_GPOS = 27, /* Geographical position (withdrawn). */ - kDNSServiceType_AAAA = 28, /* IPv6 Address. */ - kDNSServiceType_LOC = 29, /* Location Information. */ - kDNSServiceType_NXT = 30, /* Next domain (security). */ - kDNSServiceType_EID = 31, /* Endpoint identifier. */ - kDNSServiceType_NIMLOC = 32, /* Nimrod Locator. */ - kDNSServiceType_SRV = 33, /* Server Selection. */ - kDNSServiceType_ATMA = 34, /* ATM Address */ - kDNSServiceType_NAPTR = 35, /* Naming Authority PoinTeR */ - kDNSServiceType_KX = 36, /* Key Exchange */ - kDNSServiceType_CERT = 37, /* Certification record */ - kDNSServiceType_A6 = 38, /* IPv6 Address (deprecated) */ - kDNSServiceType_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ - kDNSServiceType_SINK = 40, /* Kitchen sink (experimentatl) */ - kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */ - kDNSServiceType_TKEY = 249, /* Transaction key */ - kDNSServiceType_TSIG = 250, /* Transaction signature. */ - kDNSServiceType_IXFR = 251, /* Incremental zone transfer. */ - kDNSServiceType_AXFR = 252, /* Transfer zone of authority. */ - kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */ - kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */ - kDNSServiceType_ANY = 255 /* Wildcard match. */ - }; - - -/* possible error code values */ -enum - { - kDNSServiceErr_NoError = 0, - kDNSServiceErr_Unknown = -65537, /* 0xFFFE FFFF */ - kDNSServiceErr_NoSuchName = -65538, - kDNSServiceErr_NoMemory = -65539, - kDNSServiceErr_BadParam = -65540, - kDNSServiceErr_BadReference = -65541, - kDNSServiceErr_BadState = -65542, - kDNSServiceErr_BadFlags = -65543, - kDNSServiceErr_Unsupported = -65544, - kDNSServiceErr_NotInitialized = -65545, - kDNSServiceErr_AlreadyRegistered = -65547, - kDNSServiceErr_NameConflict = -65548, - kDNSServiceErr_Invalid = -65549, - kDNSServiceErr_Firewall = -65550, - kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */ - kDNSServiceErr_BadInterfaceIndex = -65552, - kDNSServiceErr_Refused = -65553, - kDNSServiceErr_NoSuchRecord = -65554, - kDNSServiceErr_NoAuth = -65555, - kDNSServiceErr_NoSuchKey = -65556, - kDNSServiceErr_NATTraversal = -65557, - kDNSServiceErr_DoubleNAT = -65558, - kDNSServiceErr_BadTime = -65559 - /* mDNS Error codes are in the range - * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ - }; - - -/* Maximum length, in bytes, of a service name represented as a */ -/* literal C-String, including the terminating NULL at the end. */ - -#define kDNSServiceMaxServiceName 64 - -/* Maximum length, in bytes, of a domain name represented as an *escaped* C-String */ -/* including the final trailing dot, and the C-String terminating NULL at the end. */ - -#define kDNSServiceMaxDomainName 1005 - -/* - * Notes on DNS Name Escaping - * -- or -- - * "Why is kDNSServiceMaxDomainName 1005, when the maximum legal domain name is 255 bytes?" - * - * All strings used in DNS-SD are UTF-8 strings. - * With few exceptions, most are also escaped using standard DNS escaping rules: - * - * '\\' represents a single literal '\' in the name - * '\.' represents a single literal '.' in the name - * '\ddd', where ddd is a three-digit decimal value from 000 to 255, - * represents a single literal byte with that value. - * A bare unescaped '.' is a label separator, marking a boundary between domain and subdomain. - * - * The exceptions, that do not use escaping, are the routines where the full - * DNS name of a resource is broken, for convenience, into servicename/regtype/domain. - * In these routines, the "servicename" is NOT escaped. It does not need to be, since - * it is, by definition, just a single literal string. Any characters in that string - * represent exactly what they are. The "regtype" portion is, technically speaking, - * escaped, but since legal regtypes are only allowed to contain letters, digits, - * and hyphens, there is nothing to escape, so the issue is moot. The "domain" - * portion is also escaped, though most domains in use on the public Internet - * today, like regtypes, don't contain any characters that need to be escaped. - * As DNS-SD becomes more popular, rich-text domains for service discovery will - * become common, so software should be written to cope with domains with escaping. - * - * The servicename may be up to 63 bytes of UTF-8 text (not counting the C-String - * terminating NULL at the end). The regtype is of the form _service._tcp or - * _service._udp, where the "service" part is 1-14 characters, which may be - * letters, digits, or hyphens. The domain part of the three-part name may be - * any legal domain, providing that the resulting servicename+regtype+domain - * name does not exceed 255 bytes. - * - * For most software, these issues are transparent. When browsing, the discovered - * servicenames should simply be displayed as-is. When resolving, the discovered - * servicename/regtype/domain are simply passed unchanged to DNSServiceResolve(). - * When a DNSServiceResolve() succeeds, the returned fullname is already in - * the correct format to pass to standard system DNS APIs such as res_query(). - * For converting from servicename/regtype/domain to a single properly-escaped - * full DNS name, the helper function DNSServiceConstructFullName() is provided. - * - * The following (highly contrived) example illustrates the escaping process. - * Suppose you have an service called "Dr. Smith\Dr. Johnson", of type "_ftp._tcp" - * in subdomain "4th. Floor" of subdomain "Building 2" of domain "apple.com." - * The full (escaped) DNS name of this service's SRV record would be: - * Dr\.\032Smith\\Dr\.\032Johnson._ftp._tcp.4th\.\032Floor.Building\0322.apple.com. - */ - - -/* - * Constants for specifying an interface index - * - * Specific interface indexes are identified via a 32-bit unsigned integer returned - * by the if_nametoindex() family of calls. - * - * If the client passes 0 for interface index, that means "do the right thing", - * which (at present) means, "if the name is in an mDNS local multicast domain - * (e.g. 'local.', '254.169.in-addr.arpa.', '{8,9,A,B}.E.F.ip6.arpa.') then multicast - * on all applicable interfaces, otherwise send via unicast to the appropriate - * DNS server." Normally, most clients will use 0 for interface index to - * automatically get the default sensible behaviour. - * - * If the client passes a positive interface index, then for multicast names that - * indicates to do the operation only on that one interface. For unicast names the - * interface index is ignored unless kDNSServiceFlagsForceMulticast is also set. - * - * If the client passes kDNSServiceInterfaceIndexLocalOnly when registering - * a service, then that service will be found *only* by other local clients - * on the same machine that are browsing using kDNSServiceInterfaceIndexLocalOnly - * or kDNSServiceInterfaceIndexAny. - * If a client has a 'private' service, accessible only to other processes - * running on the same machine, this allows the client to advertise that service - * in a way such that it does not inadvertently appear in service lists on - * all the other machines on the network. - * - * If the client passes kDNSServiceInterfaceIndexLocalOnly when browsing - * then it will find *all* records registered on that same local machine. - * Clients explicitly wishing to discover *only* LocalOnly services can - * accomplish this by inspecting the interfaceIndex of each service reported - * to their DNSServiceBrowseReply() callback function, and discarding those - * where the interface index is not kDNSServiceInterfaceIndexLocalOnly. - */ - -#define kDNSServiceInterfaceIndexAny 0 -#define kDNSServiceInterfaceIndexLocalOnly ( (uint32_t) -1 ) - - -typedef uint32_t DNSServiceFlags; -typedef int32_t DNSServiceErrorType; - - -/********************************************************************************************* - * - * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions - * - *********************************************************************************************/ - - -/* DNSServiceRefSockFD() - * - * Access underlying Unix domain socket for an initialized DNSServiceRef. - * The DNS Service Discovery implmementation uses this socket to communicate between - * the client and the mDNSResponder daemon. The application MUST NOT directly read from - * or write to this socket. Access to the socket is provided so that it can be used as a - * run loop source, or in a select() loop: when data is available for reading on the socket, - * DNSServiceProcessResult() should be called, which will extract the daemon's reply from - * the socket, and pass it to the appropriate application callback. By using a run loop or - * select(), results from the daemon can be processed asynchronously. Without using these - * constructs, DNSServiceProcessResult() will block until the response from the daemon arrives. - * The client is responsible for ensuring that the data on the socket is processed in a timely - * fashion - the daemon may terminate its connection with a client that does not clear its - * socket buffer. - * - * sdRef: A DNSServiceRef initialized by any of the DNSService calls. - * - * return value: The DNSServiceRef's underlying socket descriptor, or -1 on - * error. - */ - -int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef); - - -/* DNSServiceProcessResult() - * - * Read a reply from the daemon, calling the appropriate application callback. This call will - * block until the daemon's response is received. Use DNSServiceRefSockFD() in - * conjunction with a run loop or select() to determine the presence of a response from the - * server before calling this function to process the reply without blocking. Call this function - * at any point if it is acceptable to block until the daemon's response arrives. Note that the - * client is responsible for ensuring that DNSServiceProcessResult() is called whenever there is - * a reply from the daemon - the daemon may terminate its connection with a client that does not - * process the daemon's responses. - * - * sdRef: A DNSServiceRef initialized by any of the DNSService calls - * that take a callback parameter. - * - * return value: Returns kDNSServiceErr_NoError on success, otherwise returns - * an error code indicating the specific failure that occurred. - */ - -DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); - - -/* DNSServiceRefDeallocate() - * - * Terminate a connection with the daemon and free memory associated with the DNSServiceRef. - * Any services or records registered with this DNSServiceRef will be deregistered. Any - * Browse, Resolve, or Query operations called with this reference will be terminated. - * - * Note: If the reference's underlying socket is used in a run loop or select() call, it should - * be removed BEFORE DNSServiceRefDeallocate() is called, as this function closes the reference's - * socket. - * - * Note: If the reference was initialized with DNSServiceCreateConnection(), any DNSRecordRefs - * created via this reference will be invalidated by this call - the resource records are - * deregistered, and their DNSRecordRefs may not be used in subsequent functions. Similarly, - * if the reference was initialized with DNSServiceRegister, and an extra resource record was - * added to the service via DNSServiceAddRecord(), the DNSRecordRef created by the Add() call - * is invalidated when this function is called - the DNSRecordRef may not be used in subsequent - * functions. - * - * Note: This call is to be used only with the DNSServiceRef defined by this API. It is - * not compatible with dns_service_discovery_ref objects defined in the legacy Mach-based - * DNSServiceDiscovery.h API. - * - * sdRef: A DNSServiceRef initialized by any of the DNSService calls. - * - */ - -void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); - - -/********************************************************************************************* - * - * Domain Enumeration - * - *********************************************************************************************/ - -/* DNSServiceEnumerateDomains() - * - * Asynchronously enumerate domains available for browsing and registration. - * - * The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains - * are to be found. - * - * Note that the names returned are (like all of DNS-SD) UTF-8 strings, - * and are escaped using standard DNS escaping rules. - * (See "Notes on DNS Name Escaping" earlier in this file for more details.) - * A graphical browser displaying a hierarchical tree-structured view should cut - * the names at the bare dots to yield individual labels, then de-escape each - * label according to the escaping rules, and then display the resulting UTF-8 text. - * - * DNSServiceDomainEnumReply Callback Parameters: - * - * sdRef: The DNSServiceRef initialized by DNSServiceEnumerateDomains(). - * - * flags: Possible values are: - * kDNSServiceFlagsMoreComing - * kDNSServiceFlagsAdd - * kDNSServiceFlagsDefault - * - * interfaceIndex: Specifies the interface on which the domain exists. (The index for a given - * interface is determined via the if_nametoindex() family of calls.) - * - * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise indicates - * the failure that occurred (other parameters are undefined if errorCode is nonzero). - * - * replyDomain: The name of the domain. - * - * context: The context pointer passed to DNSServiceEnumerateDomains. - * - */ - -typedef void (DNSSD_API *DNSServiceDomainEnumReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, - const char *replyDomain, - void *context - ); - - -/* DNSServiceEnumerateDomains() Parameters: - * - * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the enumeration operation will run indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). - * - * flags: Possible values are: - * kDNSServiceFlagsBrowseDomains to enumerate domains recommended for browsing. - * kDNSServiceFlagsRegistrationDomains to enumerate domains recommended - * for registration. - * - * interfaceIndex: If non-zero, specifies the interface on which to look for domains. - * (the index for a given interface is determined via the if_nametoindex() - * family of calls.) Most applications will pass 0 to enumerate domains on - * all interfaces. See "Constants for specifying an interface index" for more details. - * - * callBack: The function to be called when a domain is found or the call asynchronously - * fails. - * - * context: An application context pointer which is passed to the callback function - * (may be NULL). - * - * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous - * errors are delivered to the callback), otherwise returns an error code indicating - * the error that occurred (the callback is not invoked and the DNSServiceRef - * is not initialized.) - */ - -DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains - ( - DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceDomainEnumReply callBack, - void *context /* may be NULL */ - ); - - -/********************************************************************************************* - * - * Service Registration - * - *********************************************************************************************/ - -/* Register a service that is discovered via Browse() and Resolve() calls. - * - * - * DNSServiceRegisterReply() Callback Parameters: - * - * sdRef: The DNSServiceRef initialized by DNSServiceRegister(). - * - * flags: Currently unused, reserved for future use. - * - * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will - * indicate the failure that occurred (including name conflicts, - * if the kDNSServiceFlagsNoAutoRename flag was used when registering.) - * Other parameters are undefined if errorCode is nonzero. - * - * name: The service name registered (if the application did not specify a name in - * DNSServiceRegister(), this indicates what name was automatically chosen). - * - * regtype: The type of service registered, as it was passed to the callout. - * - * domain: The domain on which the service was registered (if the application did not - * specify a domain in DNSServiceRegister(), this indicates the default domain - * on which the service was registered). - * - * context: The context pointer that was passed to the callout. - * - */ - -typedef void (DNSSD_API *DNSServiceRegisterReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - DNSServiceErrorType errorCode, - const char *name, - const char *regtype, - const char *domain, - void *context - ); - - -/* DNSServiceRegister() Parameters: - * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the registration will remain active indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). - * - * interfaceIndex: If non-zero, specifies the interface on which to register the service - * (the index for a given interface is determined via the if_nametoindex() - * family of calls.) Most applications will pass 0 to register on all - * available interfaces. See "Constants for specifying an interface index" for more details. - * - * flags: Indicates the renaming behavior on name conflict (most applications - * will pass 0). See flag definitions above for details. - * - * name: If non-NULL, specifies the service name to be registered. - * Most applications will not specify a name, in which case the computer - * name is used (this name is communicated to the client via the callback). - * If a name is specified, it must be 1-63 bytes of UTF-8 text. - * If the name is longer than 63 bytes it will be automatically truncated - * to a legal length, unless the NoAutoRename flag is set, - * in which case kDNSServiceErr_BadParam will be returned. - * - * regtype: The service type followed by the protocol, separated by a dot - * (e.g. "_ftp._tcp"). The service type must be an underscore, followed - * by 1-14 characters, which may be letters, digits, or hyphens. - * The transport protocol must be "_tcp" or "_udp". New service types - * should be registered at . - * - * domain: If non-NULL, specifies the domain on which to advertise the service. - * Most applications will not specify a domain, instead automatically - * registering in the default domain(s). - * - * host: If non-NULL, specifies the SRV target host name. Most applications - * will not specify a host, instead automatically using the machine's - * default host name(s). Note that specifying a non-NULL host does NOT - * create an address record for that host - the application is responsible - * for ensuring that the appropriate address record exists, or creating it - * via DNSServiceRegisterRecord(). - * - * port: The port, in network byte order, on which the service accepts connections. - * Pass 0 for a "placeholder" service (i.e. a service that will not be discovered - * by browsing, but will cause a name conflict if another client tries to - * register that same name). Most clients will not use placeholder services. - * - * txtLen: The length of the txtRecord, in bytes. Must be zero if the txtRecord is NULL. - * - * txtRecord: The TXT record rdata. A non-NULL txtRecord MUST be a properly formatted DNS - * TXT record, i.e. ... - * Passing NULL for the txtRecord is allowed as a synonym for txtLen=1, txtRecord="", - * i.e. it creates a TXT record of length one containing a single empty string. - * RFC 1035 doesn't allow a TXT record to contain *zero* strings, so a single empty - * string is the smallest legal DNS TXT record. - * As with the other parameters, the DNSServiceRegister call copies the txtRecord - * data; e.g. if you allocated the storage for the txtRecord parameter with malloc() - * then you can safely free that memory right after the DNSServiceRegister call returns. - * - * callBack: The function to be called when the registration completes or asynchronously - * fails. The client MAY pass NULL for the callback - The client will NOT be notified - * of the default values picked on its behalf, and the client will NOT be notified of any - * asynchronous errors (e.g. out of memory errors, etc.) that may prevent the registration - * of the service. The client may NOT pass the NoAutoRename flag if the callback is NULL. - * The client may still deregister the service at any time via DNSServiceRefDeallocate(). - * - * context: An application context pointer which is passed to the callback function - * (may be NULL). - * - * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous - * errors are delivered to the callback), otherwise returns an error code indicating - * the error that occurred (the callback is never invoked and the DNSServiceRef - * is not initialized.) - */ - -DNSServiceErrorType DNSSD_API DNSServiceRegister - ( - DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - const char *name, /* may be NULL */ - const char *regtype, - const char *domain, /* may be NULL */ - const char *host, /* may be NULL */ - uint16_t port, - uint16_t txtLen, - const void *txtRecord, /* may be NULL */ - DNSServiceRegisterReply callBack, /* may be NULL */ - void *context /* may be NULL */ - ); - - -/* DNSServiceAddRecord() - * - * Add a record to a registered service. The name of the record will be the same as the - * registered service's name. - * The record can later be updated or deregistered by passing the RecordRef initialized - * by this function to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). - * - * Note that the DNSServiceAddRecord/UpdateRecord/RemoveRecord are *NOT* thread-safe - * with respect to a single DNSServiceRef. If you plan to have multiple threads - * in your program simultaneously add, update, or remove records from the same - * DNSServiceRef, then it's the caller's responsibility to use a mutext lock - * or take similar appropriate precautions to serialize those calls. - * - * - * Parameters; - * - * sdRef: A DNSServiceRef initialized by DNSServiceRegister(). - * - * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this - * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). - * If the above DNSServiceRef is passed to DNSServiceRefDeallocate(), RecordRef is also - * invalidated and may not be used further. - * - * flags: Currently ignored, reserved for future use. - * - * rrtype: The type of the record (e.g. kDNSServiceType_TXT, kDNSServiceType_SRV, etc) - * - * rdlen: The length, in bytes, of the rdata. - * - * rdata: The raw rdata to be contained in the added resource record. - * - * ttl: The time to live of the resource record, in seconds. Pass 0 to use a default value. - * - * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an - * error code indicating the error that occurred (the RecordRef is not initialized). - */ - -DNSServiceErrorType DNSSD_API DNSServiceAddRecord - ( - DNSServiceRef sdRef, - DNSRecordRef *RecordRef, - DNSServiceFlags flags, - uint16_t rrtype, - uint16_t rdlen, - const void *rdata, - uint32_t ttl - ); - - -/* DNSServiceUpdateRecord - * - * Update a registered resource record. The record must either be: - * - The primary txt record of a service registered via DNSServiceRegister() - * - A record added to a registered service via DNSServiceAddRecord() - * - An individual record registered by DNSServiceRegisterRecord() - * - * - * Parameters: - * - * sdRef: A DNSServiceRef that was initialized by DNSServiceRegister() - * or DNSServiceCreateConnection(). - * - * RecordRef: A DNSRecordRef initialized by DNSServiceAddRecord, or NULL to update the - * service's primary txt record. - * - * flags: Currently ignored, reserved for future use. - * - * rdlen: The length, in bytes, of the new rdata. - * - * rdata: The new rdata to be contained in the updated resource record. - * - * ttl: The time to live of the updated resource record, in seconds. - * - * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an - * error code indicating the error that occurred. - */ - -DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord - ( - DNSServiceRef sdRef, - DNSRecordRef RecordRef, /* may be NULL */ - DNSServiceFlags flags, - uint16_t rdlen, - const void *rdata, - uint32_t ttl - ); - - -/* DNSServiceRemoveRecord - * - * Remove a record previously added to a service record set via DNSServiceAddRecord(), or deregister - * an record registered individually via DNSServiceRegisterRecord(). - * - * Parameters: - * - * sdRef: A DNSServiceRef initialized by DNSServiceRegister() (if the - * record being removed was registered via DNSServiceAddRecord()) or by - * DNSServiceCreateConnection() (if the record being removed was registered via - * DNSServiceRegisterRecord()). - * - * recordRef: A DNSRecordRef initialized by a successful call to DNSServiceAddRecord() - * or DNSServiceRegisterRecord(). - * - * flags: Currently ignored, reserved for future use. - * - * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an - * error code indicating the error that occurred. - */ - -DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord - ( - DNSServiceRef sdRef, - DNSRecordRef RecordRef, - DNSServiceFlags flags - ); - - -/********************************************************************************************* - * - * Service Discovery - * - *********************************************************************************************/ - -/* Browse for instances of a service. - * - * - * DNSServiceBrowseReply() Parameters: - * - * sdRef: The DNSServiceRef initialized by DNSServiceBrowse(). - * - * flags: Possible values are kDNSServiceFlagsMoreComing and kDNSServiceFlagsAdd. - * See flag definitions for details. - * - * interfaceIndex: The interface on which the service is advertised. This index should - * be passed to DNSServiceResolve() when resolving the service. - * - * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will - * indicate the failure that occurred. Other parameters are undefined if - * the errorCode is nonzero. - * - * serviceName: The discovered service name. This name should be displayed to the user, - * and stored for subsequent use in the DNSServiceResolve() call. - * - * regtype: The service type, which is usually (but not always) the same as was passed - * to DNSServiceBrowse(). One case where the discovered service type may - * not be the same as the requested service type is when using subtypes: - * The client may want to browse for only those ftp servers that allow - * anonymous connections. The client will pass the string "_ftp._tcp,_anon" - * to DNSServiceBrowse(), but the type of the service that's discovered - * is simply "_ftp._tcp". The regtype for each discovered service instance - * should be stored along with the name, so that it can be passed to - * DNSServiceResolve() when the service is later resolved. - * - * domain: The domain of the discovered service instance. This may or may not be the - * same as the domain that was passed to DNSServiceBrowse(). The domain for each - * discovered service instance should be stored along with the name, so that - * it can be passed to DNSServiceResolve() when the service is later resolved. - * - * context: The context pointer that was passed to the callout. - * - */ - -typedef void (DNSSD_API *DNSServiceBrowseReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, - const char *serviceName, - const char *regtype, - const char *replyDomain, - void *context - ); - - -/* DNSServiceBrowse() Parameters: - * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the browse operation will run indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). - * - * flags: Currently ignored, reserved for future use. - * - * interfaceIndex: If non-zero, specifies the interface on which to browse for services - * (the index for a given interface is determined via the if_nametoindex() - * family of calls.) Most applications will pass 0 to browse on all available - * interfaces. See "Constants for specifying an interface index" for more details. - * - * regtype: The service type being browsed for followed by the protocol, separated by a - * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". - * - * domain: If non-NULL, specifies the domain on which to browse for services. - * Most applications will not specify a domain, instead browsing on the - * default domain(s). - * - * callBack: The function to be called when an instance of the service being browsed for - * is found, or if the call asynchronously fails. - * - * context: An application context pointer which is passed to the callback function - * (may be NULL). - * - * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous - * errors are delivered to the callback), otherwise returns an error code indicating - * the error that occurred (the callback is not invoked and the DNSServiceRef - * is not initialized.) - */ - -DNSServiceErrorType DNSSD_API DNSServiceBrowse - ( - DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - const char *regtype, - const char *domain, /* may be NULL */ - DNSServiceBrowseReply callBack, - void *context /* may be NULL */ - ); - - -/* DNSServiceResolve() - * - * Resolve a service name discovered via DNSServiceBrowse() to a target host name, port number, and - * txt record. - * - * Note: Applications should NOT use DNSServiceResolve() solely for txt record monitoring - use - * DNSServiceQueryRecord() instead, as it is more efficient for this task. - * - * Note: When the desired results have been returned, the client MUST terminate the resolve by calling - * DNSServiceRefDeallocate(). - * - * Note: DNSServiceResolve() behaves correctly for typical services that have a single SRV record - * and a single TXT record. To resolve non-standard services with multiple SRV or TXT records, - * DNSServiceQueryRecord() should be used. - * - * DNSServiceResolveReply Callback Parameters: - * - * sdRef: The DNSServiceRef initialized by DNSServiceResolve(). - * - * flags: Currently unused, reserved for future use. - * - * interfaceIndex: The interface on which the service was resolved. - * - * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will - * indicate the failure that occurred. Other parameters are undefined if - * the errorCode is nonzero. - * - * fullname: The full service domain name, in the form ... - * (This name is escaped following standard DNS rules, making it suitable for - * passing to standard system DNS APIs such as res_query(), or to the - * special-purpose functions included in this API that take fullname parameters. - * See "Notes on DNS Name Escaping" earlier in this file for more details.) - * - * hosttarget: The target hostname of the machine providing the service. This name can - * be passed to functions like gethostbyname() to identify the host's IP address. - * - * port: The port, in network byte order, on which connections are accepted for this service. - * - * txtLen: The length of the txt record, in bytes. - * - * txtRecord: The service's primary txt record, in standard txt record format. - * - * context: The context pointer that was passed to the callout. - * - * NOTE: In earlier versions of this header file, the txtRecord parameter was declared "const char *" - * This is incorrect, since it contains length bytes which are values in the range 0 to 255, not -128 to +127. - * Depending on your compiler settings, this change may cause signed/unsigned mismatch warnings. - * These should be fixed by updating your own callback function definition to match the corrected - * function signature using "const unsigned char *txtRecord". Making this change may also fix inadvertent - * bugs in your callback function, where it could have incorrectly interpreted a length byte with value 250 - * as being -6 instead, with various bad consequences ranging from incorrect operation to software crashes. - * If you need to maintain portable code that will compile cleanly with both the old and new versions of - * this header file, you should update your callback function definition to use the correct unsigned value, - * and then in the place where you pass your callback function to DNSServiceResolve(), use a cast to eliminate - * the compiler warning, e.g.: - * DNSServiceResolve(sd, flags, index, name, regtype, domain, (DNSServiceResolveReply)MyCallback, context); - * This will ensure that your code compiles cleanly without warnings (and more importantly, works correctly) - * with both the old header and with the new corrected version. - * - */ - -typedef void (DNSSD_API *DNSServiceResolveReply) - ( - DNSServiceRef sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, - const char *fullname, - const char *hosttarget, - uint16_t port, - uint16_t txtLen, - const unsigned char *txtRecord, - void *context - ); - - -/* DNSServiceResolve() Parameters - * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the resolve operation will run indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). - * - * flags: Currently ignored, reserved for future use. - * - * interfaceIndex: The interface on which to resolve the service. If this resolve call is - * as a result of a currently active DNSServiceBrowse() operation, then the - * interfaceIndex should be the index reported in the DNSServiceBrowseReply - * callback. If this resolve call is using information previously saved - * (e.g. in a preference file) for later use, then use interfaceIndex 0, because - * the desired service may now be reachable via a different physical interface. - * See "Constants for specifying an interface index" for more details. - * - * name: The name of the service instance to be resolved, as reported to the - * DNSServiceBrowseReply() callback. - * - * regtype: The type of the service instance to be resolved, as reported to the - * DNSServiceBrowseReply() callback. - * - * domain: The domain of the service instance to be resolved, as reported to the - * DNSServiceBrowseReply() callback. - * - * callBack: The function to be called when a result is found, or if the call - * asynchronously fails. - * - * context: An application context pointer which is passed to the callback function - * (may be NULL). - * - * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous - * errors are delivered to the callback), otherwise returns an error code indicating - * the error that occurred (the callback is never invoked and the DNSServiceRef - * is not initialized.) - */ - -DNSServiceErrorType DNSSD_API DNSServiceResolve - ( - DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - const char *name, - const char *regtype, - const char *domain, - DNSServiceResolveReply callBack, - void *context /* may be NULL */ - ); - - -/********************************************************************************************* - * - * Special Purpose Calls (most applications will not use these) - * - *********************************************************************************************/ - -/* DNSServiceCreateConnection() - * - * Create a connection to the daemon allowing efficient registration of - * multiple individual records. - * - * - * Parameters: - * - * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating - * the reference (via DNSServiceRefDeallocate()) severs the - * connection and deregisters all records registered on this connection. - * - * return value: Returns kDNSServiceErr_NoError on success, otherwise returns - * an error code indicating the specific failure that occurred (in which - * case the DNSServiceRef is not initialized). - */ - -DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); - - -/* DNSServiceRegisterRecord - * - * Register an individual resource record on a connected DNSServiceRef. - * - * Note that name conflicts occurring for records registered via this call must be handled - * by the client in the callback. - * - * - * DNSServiceRegisterRecordReply() parameters: - * - * sdRef: The connected DNSServiceRef initialized by - * DNSServiceCreateConnection(). - * - * RecordRef: The DNSRecordRef initialized by DNSServiceRegisterRecord(). If the above - * DNSServiceRef is passed to DNSServiceRefDeallocate(), this DNSRecordRef is - * invalidated, and may not be used further. - * - * flags: Currently unused, reserved for future use. - * - * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will - * indicate the failure that occurred (including name conflicts.) - * Other parameters are undefined if errorCode is nonzero. - * - * context: The context pointer that was passed to the callout. - * - */ - - typedef void (DNSSD_API *DNSServiceRegisterRecordReply) - ( - DNSServiceRef sdRef, - DNSRecordRef RecordRef, - DNSServiceFlags flags, - DNSServiceErrorType errorCode, - void *context - ); - - -/* DNSServiceRegisterRecord() Parameters: - * - * sdRef: A DNSServiceRef initialized by DNSServiceCreateConnection(). - * - * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this - * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). - * (To deregister ALL records registered on a single connected DNSServiceRef - * and deallocate each of their corresponding DNSServiceRecordRefs, call - * DNSServiceRefDealloocate()). - * - * flags: Possible values are kDNSServiceFlagsShared or kDNSServiceFlagsUnique - * (see flag type definitions for details). - * - * interfaceIndex: If non-zero, specifies the interface on which to register the record - * (the index for a given interface is determined via the if_nametoindex() - * family of calls.) Passing 0 causes the record to be registered on all interfaces. - * See "Constants for specifying an interface index" for more details. - * - * fullname: The full domain name of the resource record. - * - * rrtype: The numerical type of the resource record (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) - * - * rrclass: The class of the resource record (usually kDNSServiceClass_IN) - * - * rdlen: Length, in bytes, of the rdata. - * - * rdata: A pointer to the raw rdata, as it is to appear in the DNS record. - * - * ttl: The time to live of the resource record, in seconds. Pass 0 to use a default value. - * - * callBack: The function to be called when a result is found, or if the call - * asynchronously fails (e.g. because of a name conflict.) - * - * context: An application context pointer which is passed to the callback function - * (may be NULL). - * - * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous - * errors are delivered to the callback), otherwise returns an error code indicating - * the error that occurred (the callback is never invoked and the DNSRecordRef is - * not initialized.) - */ - -DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord - ( - DNSServiceRef sdRef, - DNSRecordRef *RecordRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, - const void *rdata, - uint32_t ttl, - DNSServiceRegisterRecordReply callBack, - void *context /* may be NULL */ - ); - - -/* DNSServiceQueryRecord - * - * Query for an arbitrary DNS record. - * - * - * DNSServiceQueryRecordReply() Callback Parameters: - * - * sdRef: The DNSServiceRef initialized by DNSServiceQueryRecord(). - * - * flags: Possible values are kDNSServiceFlagsMoreComing and - * kDNSServiceFlagsAdd. The Add flag is NOT set for PTR records - * with a ttl of 0, i.e. "Remove" events. - * - * interfaceIndex: The interface on which the query was resolved (the index for a given - * interface is determined via the if_nametoindex() family of calls). - * See "Constants for specifying an interface index" for more details. - * - * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will - * indicate the failure that occurred. Other parameters are undefined if - * errorCode is nonzero. - * - * fullname: The resource record's full domain name. - * - * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) - * - * rrclass: The class of the resource record (usually kDNSServiceClass_IN). - * - * rdlen: The length, in bytes, of the resource record rdata. - * - * rdata: The raw rdata of the resource record. - * - * ttl: The resource record's time to live, in seconds. - * - * context: The context pointer that was passed to the callout. - * - */ - -typedef void (DNSSD_API *DNSServiceQueryRecordReply) - ( - DNSServiceRef DNSServiceRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - DNSServiceErrorType errorCode, - const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, - const void *rdata, - uint32_t ttl, - void *context - ); - - -/* DNSServiceQueryRecord() Parameters: - * - * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds - * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, - * and the query operation will run indefinitely until the client - * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). - * - * flags: Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast - * query in a non-local domain. Without setting this flag, unicast queries - * will be one-shot - that is, only answers available at the time of the call - * will be returned. By setting this flag, answers (including Add and Remove - * events) that become available after the initial call is made will generate - * callbacks. This flag has no effect on link-local multicast queries. - * - * interfaceIndex: If non-zero, specifies the interface on which to issue the query - * (the index for a given interface is determined via the if_nametoindex() - * family of calls.) Passing 0 causes the name to be queried for on all - * interfaces. See "Constants for specifying an interface index" for more details. - * - * fullname: The full domain name of the resource record to be queried for. - * - * rrtype: The numerical type of the resource record to be queried for - * (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) - * - * rrclass: The class of the resource record (usually kDNSServiceClass_IN). - * - * callBack: The function to be called when a result is found, or if the call - * asynchronously fails. - * - * context: An application context pointer which is passed to the callback function - * (may be NULL). - * - * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous - * errors are delivered to the callback), otherwise returns an error code indicating - * the error that occurred (the callback is never invoked and the DNSServiceRef - * is not initialized.) - */ - -DNSServiceErrorType DNSSD_API DNSServiceQueryRecord - ( - DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - DNSServiceQueryRecordReply callBack, - void *context /* may be NULL */ - ); - - -/* DNSServiceReconfirmRecord - * - * Instruct the daemon to verify the validity of a resource record that appears to - * be out of date (e.g. because tcp connection to a service's target failed.) - * Causes the record to be flushed from the daemon's cache (as well as all other - * daemons' caches on the network) if the record is determined to be invalid. - * - * Parameters: - * - * flags: Currently unused, reserved for future use. - * - * interfaceIndex: If non-zero, specifies the interface of the record in question. - * Passing 0 causes all instances of this record to be reconfirmed. - * - * fullname: The resource record's full domain name. - * - * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) - * - * rrclass: The class of the resource record (usually kDNSServiceClass_IN). - * - * rdlen: The length, in bytes, of the resource record rdata. - * - * rdata: The raw rdata of the resource record. - * - */ - -DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord - ( - DNSServiceFlags flags, - uint32_t interfaceIndex, - const char *fullname, - uint16_t rrtype, - uint16_t rrclass, - uint16_t rdlen, - const void *rdata - ); - - -/********************************************************************************************* - * - * General Utility Functions - * - *********************************************************************************************/ - -/* DNSServiceConstructFullName() - * - * Concatenate a three-part domain name (as returned by the above callbacks) into a - * properly-escaped full domain name. Note that callbacks in the above functions ALREADY ESCAPE - * strings where necessary. - * - * Parameters: - * - * fullName: A pointer to a buffer that where the resulting full domain name is to be written. - * The buffer must be kDNSServiceMaxDomainName (1005) bytes in length to - * accommodate the longest legal domain name without buffer overrun. - * - * service: The service name - any dots or backslashes must NOT be escaped. - * May be NULL (to construct a PTR record name, e.g. - * "_ftp._tcp.apple.com."). - * - * regtype: The service type followed by the protocol, separated by a dot - * (e.g. "_ftp._tcp"). - * - * domain: The domain name, e.g. "apple.com.". Literal dots or backslashes, - * if any, must be escaped, e.g. "1st\. Floor.apple.com." - * - * return value: Returns 0 on success, -1 on error. - * - */ - -int DNSSD_API DNSServiceConstructFullName - ( - char *fullName, - const char *service, /* may be NULL */ - const char *regtype, - const char *domain - ); - - -/********************************************************************************************* - * - * TXT Record Construction Functions - * - *********************************************************************************************/ - -/* - * A typical calling sequence for TXT record construction is something like: - * - * Client allocates storage for TXTRecord data (e.g. declare buffer on the stack) - * TXTRecordCreate(); - * TXTRecordSetValue(); - * TXTRecordSetValue(); - * TXTRecordSetValue(); - * ... - * DNSServiceRegister( ... TXTRecordGetLength(), TXTRecordGetBytesPtr() ... ); - * TXTRecordDeallocate(); - * Explicitly deallocate storage for TXTRecord data (if not allocated on the stack) - */ - - -/* TXTRecordRef - * - * Opaque internal data type. - * Note: Represents a DNS-SD TXT record. - */ - -typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef; - - -/* TXTRecordCreate() - * - * Creates a new empty TXTRecordRef referencing the specified storage. - * - * If the buffer parameter is NULL, or the specified storage size is not - * large enough to hold a key subsequently added using TXTRecordSetValue(), - * then additional memory will be added as needed using malloc(). - * - * On some platforms, when memory is low, malloc() may fail. In this - * case, TXTRecordSetValue() will return kDNSServiceErr_NoMemory, and this - * error condition will need to be handled as appropriate by the caller. - * - * You can avoid the need to handle this error condition if you ensure - * that the storage you initially provide is large enough to hold all - * the key/value pairs that are to be added to the record. - * The caller can precompute the exact length required for all of the - * key/value pairs to be added, or simply provide a fixed-sized buffer - * known in advance to be large enough. - * A no-value (key-only) key requires (1 + key length) bytes. - * A key with empty value requires (1 + key length + 1) bytes. - * A key with non-empty value requires (1 + key length + 1 + value length). - * For most applications, DNS-SD TXT records are generally - * less than 100 bytes, so in most cases a simple fixed-sized - * 256-byte buffer will be more than sufficient. - * Recommended size limits for DNS-SD TXT Records are discussed in - * - * - * Note: When passing parameters to and from these TXT record APIs, - * the key name does not include the '=' character. The '=' character - * is the separator between the key and value in the on-the-wire - * packet format; it is not part of either the key or the value. - * - * txtRecord: A pointer to an uninitialized TXTRecordRef. - * - * bufferLen: The size of the storage provided in the "buffer" parameter. - * - * buffer: Optional caller-supplied storage used to hold the TXTRecord data. - * This storage must remain valid for as long as - * the TXTRecordRef. - */ - -void DNSSD_API TXTRecordCreate - ( - TXTRecordRef *txtRecord, - uint16_t bufferLen, - void *buffer - ); - - -/* TXTRecordDeallocate() - * - * Releases any resources allocated in the course of preparing a TXT Record - * using TXTRecordCreate()/TXTRecordSetValue()/TXTRecordRemoveValue(). - * Ownership of the buffer provided in TXTRecordCreate() returns to the client. - * - * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). - * - */ - -void DNSSD_API TXTRecordDeallocate - ( - TXTRecordRef *txtRecord - ); - - -/* TXTRecordSetValue() - * - * Adds a key (optionally with value) to a TXTRecordRef. If the "key" already - * exists in the TXTRecordRef, then the current value will be replaced with - * the new value. - * Keys may exist in four states with respect to a given TXT record: - * - Absent (key does not appear at all) - * - Present with no value ("key" appears alone) - * - Present with empty value ("key=" appears in TXT record) - * - Present with non-empty value ("key=value" appears in TXT record) - * For more details refer to "Data Syntax for DNS-SD TXT Records" in - * - * - * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). - * - * key: A null-terminated string which only contains printable ASCII - * values (0x20-0x7E), excluding '=' (0x3D). Keys should be - * 8 characters or less (not counting the terminating null). - * - * valueSize: The size of the value. - * - * value: Any binary value. For values that represent - * textual data, UTF-8 is STRONGLY recommended. - * For values that represent textual data, valueSize - * should NOT include the terminating null (if any) - * at the end of the string. - * If NULL, then "key" will be added with no value. - * If non-NULL but valueSize is zero, then "key=" will be - * added with empty value. - * - * return value: Returns kDNSServiceErr_NoError on success. - * Returns kDNSServiceErr_Invalid if the "key" string contains - * illegal characters. - * Returns kDNSServiceErr_NoMemory if adding this key would - * exceed the available storage. - */ - -DNSServiceErrorType DNSSD_API TXTRecordSetValue - ( - TXTRecordRef *txtRecord, - const char *key, - uint8_t valueSize, /* may be zero */ - const void *value /* may be NULL */ - ); - - -/* TXTRecordRemoveValue() - * - * Removes a key from a TXTRecordRef. The "key" must be an - * ASCII string which exists in the TXTRecordRef. - * - * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). - * - * key: A key name which exists in the TXTRecordRef. - * - * return value: Returns kDNSServiceErr_NoError on success. - * Returns kDNSServiceErr_NoSuchKey if the "key" does not - * exist in the TXTRecordRef. - */ - -DNSServiceErrorType DNSSD_API TXTRecordRemoveValue - ( - TXTRecordRef *txtRecord, - const char *key - ); - - -/* TXTRecordGetLength() - * - * Allows you to determine the length of the raw bytes within a TXTRecordRef. - * - * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). - * - * return value: Returns the size of the raw bytes inside a TXTRecordRef - * which you can pass directly to DNSServiceRegister() or - * to DNSServiceUpdateRecord(). - * Returns 0 if the TXTRecordRef is empty. - */ - -uint16_t DNSSD_API TXTRecordGetLength - ( - const TXTRecordRef *txtRecord - ); - - -/* TXTRecordGetBytesPtr() - * - * Allows you to retrieve a pointer to the raw bytes within a TXTRecordRef. - * - * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). - * - * return value: Returns a pointer to the raw bytes inside the TXTRecordRef - * which you can pass directly to DNSServiceRegister() or - * to DNSServiceUpdateRecord(). - */ - -const void * DNSSD_API TXTRecordGetBytesPtr - ( - const TXTRecordRef *txtRecord - ); - - -/********************************************************************************************* - * - * TXT Record Parsing Functions - * - *********************************************************************************************/ - -/* - * A typical calling sequence for TXT record parsing is something like: - * - * Receive TXT record data in DNSServiceResolve() callback - * if (TXTRecordContainsKey(txtLen, txtRecord, "key")) then do something - * val1ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key1", &len1); - * val2ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key2", &len2); - * ... - * bcopy(val1ptr, myval1, len1); - * bcopy(val2ptr, myval2, len2); - * ... - * return; - * - * If you wish to retain the values after return from the DNSServiceResolve() - * callback, then you need to copy the data to your own storage using bcopy() - * or similar, as shown in the example above. - * - * If for some reason you need to parse a TXT record you built yourself - * using the TXT record construction functions above, then you can do - * that using TXTRecordGetLength and TXTRecordGetBytesPtr calls: - * TXTRecordGetValue(TXTRecordGetLength(x), TXTRecordGetBytesPtr(x), key, &len); - * - * Most applications only fetch keys they know about from a TXT record and - * ignore the rest. - * However, some debugging tools wish to fetch and display all keys. - * To do that, use the TXTRecordGetCount() and TXTRecordGetItemAtIndex() calls. - */ - -/* TXTRecordContainsKey() - * - * Allows you to determine if a given TXT Record contains a specified key. - * - * txtLen: The size of the received TXT Record. - * - * txtRecord: Pointer to the received TXT Record bytes. - * - * key: A null-terminated ASCII string containing the key name. - * - * return value: Returns 1 if the TXT Record contains the specified key. - * Otherwise, it returns 0. - */ - -int DNSSD_API TXTRecordContainsKey - ( - uint16_t txtLen, - const void *txtRecord, - const char *key - ); - - -/* TXTRecordGetValuePtr() - * - * Allows you to retrieve the value for a given key from a TXT Record. - * - * txtLen: The size of the received TXT Record - * - * txtRecord: Pointer to the received TXT Record bytes. - * - * key: A null-terminated ASCII string containing the key name. - * - * valueLen: On output, will be set to the size of the "value" data. - * - * return value: Returns NULL if the key does not exist in this TXT record, - * or exists with no value (to differentiate between - * these two cases use TXTRecordContainsKey()). - * Returns pointer to location within TXT Record bytes - * if the key exists with empty or non-empty value. - * For empty value, valueLen will be zero. - * For non-empty value, valueLen will be length of value data. - */ - -const void * DNSSD_API TXTRecordGetValuePtr - ( - uint16_t txtLen, - const void *txtRecord, - const char *key, - uint8_t *valueLen - ); - - -/* TXTRecordGetCount() - * - * Returns the number of keys stored in the TXT Record. The count - * can be used with TXTRecordGetItemAtIndex() to iterate through the keys. - * - * txtLen: The size of the received TXT Record. - * - * txtRecord: Pointer to the received TXT Record bytes. - * - * return value: Returns the total number of keys in the TXT Record. - * - */ - -uint16_t DNSSD_API TXTRecordGetCount - ( - uint16_t txtLen, - const void *txtRecord - ); - - -/* TXTRecordGetItemAtIndex() - * - * Allows you to retrieve a key name and value pointer, given an index into - * a TXT Record. Legal index values range from zero to TXTRecordGetCount()-1. - * It's also possible to iterate through keys in a TXT record by simply - * calling TXTRecordGetItemAtIndex() repeatedly, beginning with index zero - * and increasing until TXTRecordGetItemAtIndex() returns kDNSServiceErr_Invalid. - * - * On return: - * For keys with no value, *value is set to NULL and *valueLen is zero. - * For keys with empty value, *value is non-NULL and *valueLen is zero. - * For keys with non-empty value, *value is non-NULL and *valueLen is non-zero. - * - * txtLen: The size of the received TXT Record. - * - * txtRecord: Pointer to the received TXT Record bytes. - * - * index: An index into the TXT Record. - * - * keyBufLen: The size of the string buffer being supplied. - * - * key: A string buffer used to store the key name. - * On return, the buffer contains a null-terminated C string - * giving the key name. DNS-SD TXT keys are usually - * 8 characters or less. To hold the maximum possible - * key name, the buffer should be 256 bytes long. - * - * valueLen: On output, will be set to the size of the "value" data. - * - * value: On output, *value is set to point to location within TXT - * Record bytes that holds the value data. - * - * return value: Returns kDNSServiceErr_NoError on success. - * Returns kDNSServiceErr_NoMemory if keyBufLen is too short. - * Returns kDNSServiceErr_Invalid if index is greater than - * TXTRecordGetCount()-1. - */ - -DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex - ( - uint16_t txtLen, - const void *txtRecord, - uint16_t index, - uint16_t keyBufLen, - char *key, - uint8_t *valueLen, - const void **value - ); - -#ifdef __APPLE_API_PRIVATE - -/* - * Mac OS X specific functionality - * 3rd party clients of this API should not depend on future support or availability of this routine - */ - -/* DNSServiceSetDefaultDomainForUser() - * - * Set the default domain for the caller's UID. Future browse and registration - * calls by this user that do not specify an explicit domain will browse and - * register in this wide-area domain in addition to .local. In addition, this - * domain will be returned as a Browse domain via domain enumeration calls. - * - * - * Parameters: - * - * flags: Pass kDNSServiceFlagsAdd to add a domain for a user. Call without - * this flag set to clear a previously added domain. - * - * domain: The domain to be used for the caller's UID. - * - * return value: Returns kDNSServiceErr_NoError on succeses, otherwise returns - * an error code indicating the error that occurred - */ - -DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser - ( - DNSServiceFlags flags, - const char *domain - ); - -#endif //__APPLE_API_PRIVATE - -// Some C compiler cleverness. We can make the compiler check certain things for us, -// and report errors at compile-time if anything is wrong. The usual way to do this would -// be to use a run-time "if" statement or the conventional run-time "assert" mechanism, but -// then you don't find out what's wrong until you run the software. This way, if the assertion -// condition is false, the array size is negative, and the complier complains immediately. - -struct DNS_SD_CompileTimeAssertionChecks - { - char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1]; - }; - -#ifdef __cplusplus - } -#endif - -#endif /* _DNS_SD_H */ \ No newline at end of file diff --git a/libraries/dnssd_loader/dnssd_loader.cpp b/libraries/dnssd_loader/dnssd_loader.cpp deleted file mode 100644 index ed90b1b..0000000 --- a/libraries/dnssd_loader/dnssd_loader.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "dns_sd.h" -#include -#include -#include -#include // prctl(), PR_SET_PDEATHSIG -#include // signals - - -DNSServiceErrorType DNSSD_API DNSServiceRegister - ( - DNSServiceRef *sdRef, - DNSServiceFlags flags, - uint32_t interfaceIndex, - const char *name, /* may be NULL */ - const char *regtype, - const char *domain, /* may be NULL */ - const char *host, /* may be NULL */ - uint16_t port, - uint16_t txtLen, - const void *txtRecord, /* may be NULL */ - DNSServiceRegisterReply callBack, /* may be NULL */ - void *context /* may be NULL */ - ) { - // python3 -c 'from ctypes import *; sdRef = c_int(); CDLL('libdns_sd.so').DNSServiceRegister(byref(sdRef), flags, interfaceIndex, name, regtype, domain, host, txtLen, txtRecord, None, None)' - std::string pyCommand = "from ctypes import *; dll = CDLL('libdns_sd.so'); "; - - pyCommand += "sdRef = c_int(); "; -#define INT_ARG(argname) (std::string("") + #argname " = " + std::to_string(argname) + "; ") -#define STR_ARG(argname) (argname ? std::string(#argname " = br'") + argname + "'; " : std::string(#argname " = None; ")) - pyCommand += INT_ARG(flags); - pyCommand += INT_ARG(interfaceIndex); - pyCommand += STR_ARG(name); - pyCommand += STR_ARG(regtype); - pyCommand += STR_ARG(domain); - pyCommand += STR_ARG(host); - pyCommand += INT_ARG(port); - pyCommand += INT_ARG(txtLen); - - std::string txtRecordHex = ""; - for (int i = 0; i < txtLen; i++) { - char buf[16] = { 0 }; - sprintf(buf, "\\x%02X", *((char *)txtRecord + i)); - txtRecordHex += buf; - } - pyCommand += "txtRecord = b'" + txtRecordHex + "'; "; - pyCommand += "ret = dll.DNSServiceRegister(byref(sdRef), flags, interfaceIndex, name, regtype, domain, host, port, txtLen, txtRecord, None, None); "; - pyCommand += "print('DNSServiceRegister result: %d' % ret); "; - - pyCommand += "from threading import Event; Event().wait(); "; - - printf("Running python3 command to advertise AltServer: %s\n", pyCommand.c_str()); - - pid_t ppid_before_fork = getpid(); - int child,status; - if ((child = fork()) < 0) { - perror("fork"); - return EXIT_FAILURE; - } - if(child == 0){ - int r = prctl(PR_SET_PDEATHSIG, SIGTERM); - if (r == -1) { perror(0); exit(1); } - // test in case the original parent exited just - // before the prctl() call - if (getppid() != ppid_before_fork) - exit(1); - execlp("python3", "python3", "-c", pyCommand.c_str(), NULL); - exit(1); - } else { - ; - } - return 0; - } - -int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) { - return 0xDEADBEEF; -} \ No newline at end of file diff --git a/libraries/ideviceinstaller b/libraries/ideviceinstaller index 534ddef..659e35c 160000 --- a/libraries/ideviceinstaller +++ b/libraries/ideviceinstaller @@ -1 +1 @@ -Subproject commit 534ddefac4b7118bd4781cd91f8781d8727ff49a +Subproject commit 659e35c047278661aedb4d4688f4d3aad5892cc2 diff --git a/libraries/libimobiledevice b/libraries/libimobiledevice index c6f89de..24abbb9 160000 --- a/libraries/libimobiledevice +++ b/libraries/libimobiledevice @@ -1 +1 @@ -Subproject commit c6f89deac00347faa187f2f5296e32840c4f26b4 +Subproject commit 24abbb9450c723617e10a6843978aa04a576523e diff --git a/libraries/libimobiledevice-glue b/libraries/libimobiledevice-glue deleted file mode 160000 index c2e237a..0000000 --- a/libraries/libimobiledevice-glue +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c2e237ab5449b42461639c8e1eabbc61d0c386b7 diff --git a/libraries/libplist b/libraries/libplist index db93bae..feb0bcd 160000 --- a/libraries/libplist +++ b/libraries/libplist @@ -1 +1 @@ -Subproject commit db93bae96d64140230ad050061632531644c46ad +Subproject commit feb0bcd102ff0abc34ffa04e8cabf26706ffdb38 diff --git a/libraries/libusbmuxd b/libraries/libusbmuxd index 36ffb7a..e32bf76 160000 --- a/libraries/libusbmuxd +++ b/libraries/libusbmuxd @@ -1 +1 @@ -Subproject commit 36ffb7ab6e2a7e33bd1b56398a88895b7b8c615a +Subproject commit e32bf7612912348d7af81afe1e8be2ecc93a93ca diff --git a/libraries/miniupnpc b/libraries/miniupnpc new file mode 160000 index 0000000..2a10356 --- /dev/null +++ b/libraries/miniupnpc @@ -0,0 +1 @@ +Subproject commit 2a10356007651672e038ccf63d120cc002a23a0d diff --git a/libraries/miniupnpcstrings.h b/libraries/miniupnpcstrings.h new file mode 100644 index 0000000..3b37c6c --- /dev/null +++ b/libraries/miniupnpcstrings.h @@ -0,0 +1,23 @@ +/* $Id: miniupnpcstrings.h.in,v 1.6 2014/11/04 22:31:55 nanard Exp $ */ +/* Project: miniupnp + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * Author: Thomas Bernard + * Copyright (c) 2005-2014 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef MINIUPNPCSTRINGS_H_INCLUDED +#define MINIUPNPCSTRINGS_H_INCLUDED + +#define OS_STRING "Ubuntu/20.04" +#define MINIUPNPC_VERSION_STRING "2.2.2" + +#if 0 +/* according to "UPnP Device Architecture 1.0" */ +#define UPNP_VERSION_STRING "UPnP/1.0" +#else +/* according to "UPnP Device Architecture 1.1" */ +#define UPNP_VERSION_STRING "UPnP/1.1" +#endif + +#endif + diff --git a/makefiles/AltSign-build/AltSign-files.mak b/makefiles/AltSign-build/AltSign-files.mak deleted file mode 100644 index 9361b8c..0000000 --- a/makefiles/AltSign-build/AltSign-files.mak +++ /dev/null @@ -1 +0,0 @@ -altsign_include := -I$(BUILD_DIR)/AltSign_patched \ No newline at end of file diff --git a/makefiles/AltSign-build/AltSign.mak b/makefiles/AltSign-build/AltSign.mak deleted file mode 100644 index 827df93..0000000 --- a/makefiles/AltSign-build/AltSign.mak +++ /dev/null @@ -1,83 +0,0 @@ -CC := clang -CXX := clang++ - -ROOT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) -include $(ROOT_DIR)/../main.mak - -ALTSIGN_ROOT := $(UPSTREAM_DIR)/AltSign -ALTSIGN_NEWROOT := $(BUILD_DIR)/AltSign_patched -MINIZIP_ROOT := $(UPSTREAM_DIR)/AltSign/Dependencies/minizip -LDID_ROOT := $(UPSTREAM_DIR)/ldid -LDID_NEWROOT := $(BUILD_DIR)/ldid_patched - -include $(MAIN_DIR)/makefiles/AltWindowsShim.mak - -CFLAGS += -I$(ALTSIGN_ROOT) -I$(MINIZIP_ROOT) -I$(LDID_ROOT) -mno-sse -#CFLAGS += -DLDID_NOTOOLS # will lose some symbol if enable this - -CXXFLAGS = $(CFLAGS) -std=c++17 - -altsign_orifiles := $(wildcard $(ALTSIGN_ROOT)/*.*) -altsign_newfiles := $(altsign_orifiles:$(ALTSIGN_ROOT)/%=$(ALTSIGN_NEWROOT)/%) - -$(ALTSIGN_NEWROOT)/%: $(ALTSIGN_ROOT)/% - mkdir -p `dirname "$@"` - python3 $(ROOT_DIR)/rewrite_altsign_source.py "$<" > $@ - -$(altsign_newfiles) : $(altsign_orifiles) -altsign_src := $(filter %.cpp,$(altsign_newfiles)) - - -ldid_orifiles := $(LDID_ROOT)/ldid.cpp $(LDID_ROOT)/lookup2.c -ldid_newfiles := $(ldid_orifiles:$(LDID_ROOT)/%=$(LDID_NEWROOT)/%) - -$(LDID_NEWROOT)/%: $(LDID_ROOT)/% - mkdir -p `dirname "$@"` - python3 $(ROOT_DIR)/rewrite_ldid_source.py "$<" > $@ - -$(ldid_newfiles) : $(ldid_orifiles) -ldid_src := $(ldid_newfiles) - - -preprocess : $(altsign_newfiles) $(ldid_newfiles) -.PHONY : preprocess - -minizip_src := $(MINIZIP_ROOT)/ioapi.c $(MINIZIP_ROOT)/zip.c $(MINIZIP_ROOT)/unzip.c - -objs += $(altsign_src:$(BUILD_DIR)/%=$(BUILD_DIR)/objs/%.o) -objs += $(ldid_src:$(BUILD_DIR)/%=$(BUILD_DIR)/objs/%.o) -objs += $(minizip_src:$(MAIN_DIR)/%=$(BUILD_DIR)/objs/%.o) - -#ldid/%.o : CC := gcc -#ldid/%.o : CXX := g++ - -$(BUILD_DIR)/objs/%.c.o : $(BUILD_DIR)/%.c - mkdir -p $(@D) - $(CC) $(CFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -$(BUILD_DIR)/objs/%.cpp.o : $(BUILD_DIR)/%.cpp - mkdir -p $(@D) - $(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -$(BUILD_DIR)/objs/%.c.o : $(MAIN_DIR)/%.c - mkdir -p $(@D) - $(CC) $(CFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -$(BUILD_DIR)/objs/%.cpp.o : $(MAIN_DIR)/%.cpp - mkdir -p $(@D) - $(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -#python3 $(ROOT_DIR)/rewrite_altsign_source.py "$<" | $(CC) -x c -I`dirname $<` $(CFLAGS) $(EXTRA_FLAGS) -o $@ -c - -#python3 $(ROOT_DIR)/rewrite_altsign_source.py "$<" | $(CXX) -x c++ -I`dirname $<` $(CXXFLAGS) $(EXTRA_FLAGS) -o $@ -c - - -$(BUILD_DIR)/AltSign.a : $(objs) - ar rcs $@ $^ - -clean:: - rm -rf $(ALTSIGN_NEWROOT) - rm -f $(objs) AltSign.a - -all :: preprocess $(BUILD_DIR)/AltSign.a -.PHONY : all - -.DEFAULT_GOAL := all diff --git a/makefiles/AltSign-build/rewrite_altsign_source.py b/makefiles/AltSign-build/rewrite_altsign_source.py deleted file mode 100644 index ba7cfa2..0000000 --- a/makefiles/AltSign-build/rewrite_altsign_source.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/python3 - -import re -import sys - -F = sys.argv[1] - -with open(F, 'rb') as f: - content = f.read() - -content = re.sub(br'L("([^"\\]|\\.)*")', br'U(\1)', content) -content = content.replace(b'std::wstring', b'std::string') -content = content.replace(b'boost/filesystem.hpp', b'filesystem') -content = content.replace(b'boost::filesystem', b'std::filesystem') - -content = content.replace(b'"%FT%T%z"', b'"%Y-%m-%dT%H:%M:%SZ"') -content = content.replace(b'localtime(', b'gmtime(') - -content = content.replace(b'winsock2.h', b'WinSock2.h') - -sys.stdout.buffer.write(content) \ No newline at end of file diff --git a/makefiles/AltSign-build/rewrite_ldid_source.py b/makefiles/AltSign-build/rewrite_ldid_source.py deleted file mode 100644 index 20f208d..0000000 --- a/makefiles/AltSign-build/rewrite_ldid_source.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/python3 - -import re -import sys - -F = sys.argv[1] - -with open(F, 'rb') as f: - content = f.read() - -content = content.replace(br'\\\\', br'/') # c escape + regex escape = 2*2 -content = content.replace(br'\\', br'/') -content = re.sub(br'/(\.[A-Za-z])', br'\\\\\1', content) - -content = content.replace(br'int main(', br'int __main(') - -sys.stdout.buffer.write(content) \ No newline at end of file diff --git a/makefiles/AltWindowsShim.mak b/makefiles/AltWindowsShim.mak deleted file mode 100644 index 21ae02a..0000000 --- a/makefiles/AltWindowsShim.mak +++ /dev/null @@ -1,8 +0,0 @@ -_ROOT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) -include $(_ROOT_DIR)/main.mak - -SHIM_DIR := $(MAIN_DIR)/shims - -CFLAGS += -I$(SHIM_DIR) -include windows_shim.h - -shim_src := $(wildcard $(SHIM_DIR)/*.cpp) $(wildcard $(SHIM_DIR)/*.c) \ No newline at end of file diff --git a/makefiles/dnssd_loader-build/config.h b/makefiles/dnssd_loader-build/config.h deleted file mode 100644 index fb37de8..0000000 --- a/makefiles/dnssd_loader-build/config.h +++ /dev/null @@ -1 +0,0 @@ -#define HAVE_LIBDL 1 \ No newline at end of file diff --git a/makefiles/dnssd_loader-build/dnssd_loader-files.mak b/makefiles/dnssd_loader-build/dnssd_loader-files.mak deleted file mode 100644 index 184161e..0000000 --- a/makefiles/dnssd_loader-build/dnssd_loader-files.mak +++ /dev/null @@ -1,2 +0,0 @@ -dnssd_loader_src := $(wildcard $(LIB_DIR)/dnssd_loader/*.c) $(wildcard $(LIB_DIR)/dnssd_loader/*.cpp) -dnssd_loader_include := -I$(LIB_DIR)/dnssd_loader diff --git a/makefiles/dnssd_loader-build/dnssd_loader.mak b/makefiles/dnssd_loader-build/dnssd_loader.mak deleted file mode 100644 index 6f47bee..0000000 --- a/makefiles/dnssd_loader-build/dnssd_loader.mak +++ /dev/null @@ -1,36 +0,0 @@ -ROOT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) -include $(ROOT_DIR)/../main.mak - -%.c.o : %.c - $(CC) $(CFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -%.cpp.o : %.cpp - $(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -CFLAGS += -DHAVE_CONFIG_H -DDEBUG -O0 -g - - -$(BUILD_DIR)/objs/%.c.o : $(MAIN_DIR)/%.c - mkdir -p $(@D) - $(CC) $(CFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -$(BUILD_DIR)/objs/%.cpp.o : $(MAIN_DIR)/%.cpp - mkdir -p $(@D) - $(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -include $(ROOT_DIR)/dnssd_loader-files.mak - -dnssd_loader_obj := $(dnssd_loader_src:$(MAIN_DIR)/%=$(BUILD_DIR)/objs/%.o) -$(dnssd_loader_obj) : EXTRA_FLAGS := -I$(ROOT_DIR) $(dnssd_loader_include) -$(BUILD_DIR)/dnssd_loader.a : $(dnssd_loader_obj) - ar rcs $@ $^ - -clean:: - rm -f $(dnssd_loader_obj) - rm -f $(BUILD_DIR)/dnssd_loader.a -.PHONY : clean - -all :: $(BUILD_DIR)/dnssd_loader.a -.PHONY : all - -.DEFAULT_GOAL := all diff --git a/makefiles/libimobiledevice-build/config.h b/makefiles/libimobiledevice-build/config.h deleted file mode 100644 index 1e7828c..0000000 --- a/makefiles/libimobiledevice-build/config.h +++ /dev/null @@ -1,195 +0,0 @@ -// config for Alpine 3.15 - -/* Define to 1 if you have the `asprintf' function. */ -#define HAVE_ASPRINTF 1 - -/* Define if the C compiler supports constructor/destructor attributes */ -#define HAVE_ATTRIBUTE_CONSTRUCTOR 1 - -/* define if struct dirent has member d_type */ -#define HAVE_DIRENT_D_TYPE 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_DLFCN_H 1 - -/* Define if compiled with -fvisibility=hidden */ -#define HAVE_FVISIBILITY 1 - -/* Define if you have libgcrypt support */ -/* #undef HAVE_GCRYPT */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_GCRYPT_H */ - -/* Define to 1 if you have the `getifaddrs' function. */ -#define HAVE_GETIFADDRS 1 - -/* Define if you have GnuTLS support */ -/* #undef HAVE_GNUTLS */ - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define to 1 if you have the `pthread' library (-lpthread). */ -#define HAVE_LIBPTHREAD 1 - -/* Define if you have MbedTLS support */ -/* #undef HAVE_MBEDTLS */ - -/* Define if you have OpenSSL support */ -#define HAVE_OPENSSL 1 - -/* Define if libplist has JSON support */ -#define HAVE_PLIST_JSON 1 - -/* If available, contains the Python version number currently in use. */ -/* #undef HAVE_PYTHON */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDIO_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the `stpcpy' function. */ -#define HAVE_STPCPY 1 - -/* Define to 1 if you have the `stpncpy' function. */ -#define HAVE_STPNCPY 1 - -/* Define to 1 if you have the `strcasecmp' function. */ -#define HAVE_STRCASECMP 1 - -/* Define to 1 if you have the `strdup' function. */ -#define HAVE_STRDUP 1 - -/* Define to 1 if you have the `strerror' function. */ -#define HAVE_STRERROR 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the `strndup' function. */ -#define HAVE_STRNDUP 1 - - - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TIME_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define to 1 if you have the `vasprintf' function. */ -#define HAVE_VASPRINTF 1 - -/* Define if building with wireless pairing support */ -#undef HAVE_WIRELESS_PAIRING - -/* Define to 1 if all of the C90 standard headers exist (not just the ones - required in a freestanding environment). This macro is provided for - backward compatibility; new code need not use it. */ -#define STDC_HEADERS 1 - -/* Define if debug message output code should not be built. */ -#undef STRIP_DEBUG_CODE - -/* Define if you have POSIX threads libraries and header files. */ -#define HAVE_PTHREAD 1 - -/* Define to 1 if you have the `pthread_cancel' function. */ -#define HAVE_PTHREAD_CANCEL 1 - -/* Define to 1 if you have the `pthread_once' function. */ -#define HAVE_PTHREAD_ONCE 1 - -/* Have PTHREAD_PRIO_INHERIT. */ -#define HAVE_PTHREAD_PRIO_INHERIT 1 - -/* Define to 1 if you have the `gmtime_r' function. */ -#define HAVE_GMTIME_R 1 - -/* Define to 1 if you have the `localtime_r' function. */ -#define HAVE_LOCALTIME_R 1 - -/* Define to 1 if you have the `memmem' function. */ -#define HAVE_MEMMEM 1 - -/* If available, contains the Python version number currently in use. */ -/* #undef HAVE_PYTHON */ - -/* Define to 1 if you have the `strptime' function. */ -#define HAVE_STRPTIME 1 - -/* Define to 1 if you have the `timegm' function. */ -#define HAVE_TIMEGM 1 - -/* Define if struct tm has a tm_gmtoff member */ -#define HAVE_TM_TM_GMTOFF 1 - -/* Define if struct tm has a tm_zone member */ -#define HAVE_TM_TM_ZONE 1 - -/* little endian */ -#define __LITTLE_ENDIAN__ 1 - -/* Define if you have inotify support (linux only) */ -#define HAVE_INOTIFY 1 - - -/* Define if you have program_invocation_short_name */ -#define HAVE_PROGRAM_INVOCATION_SHORT_NAME 1 - -/* Define if program_invocation_short_name is declared in errno.h */ -/* #undef HAVE_PROGRAM_INVOCATION_SHORT_NAME_ERRNO_H */ - -/* Define to 1 if you have the `pselect' function. */ -#define HAVE_PSELECT 1 - -/* Define to 1 if you have the `realloc' function. */ -#define HAVE_REALLOC 1 - -/* Define to 1 if you have the `sleep' function. */ -#define HAVE_SLEEP 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_INOTIFY_H 1 - -/* Define if lstat syscall is supported */ -#define HAVE_LSTAT 1 - -/* Define to 1 if your system has a GNU libc compatible `malloc' function, and - to 0 otherwise. */ -#define HAVE_MALLOC 1 - - - -/* Name of package */ -//#define PACKAGE "AltServer-Linux" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "AltServer-Linux" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "AltServer-Linux " - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "AltServer-Linux" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "https://github.com/NyaMisty/AltServer-Linux" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "1.0" diff --git a/makefiles/libimobiledevice-build/libimobiledevice-files.mak b/makefiles/libimobiledevice-build/libimobiledevice-files.mak deleted file mode 100644 index ddadbdd..0000000 --- a/makefiles/libimobiledevice-build/libimobiledevice-files.mak +++ /dev/null @@ -1,8 +0,0 @@ -libimobiledevice_src += $(wildcard $(LIB_DIR)/libimobiledevice/src/*.c) $(wildcard $(LIB_DIR)/libimobiledevice/common/*.c) -libimobiledevice_src += $(wildcard $(LIB_DIR)/libimobiledevice-glue/src/*.c) -libimobiledevice_src += $(wildcard $(LIB_DIR)/libusbmuxd/src/*.c) -libimobiledevice_src += $(wildcard $(LIB_DIR)/libusbmuxd/common/*.c) -libimobiledevice_include := -I$(LIB_DIR)/libimobiledevice/include -I$(LIB_DIR)/libimobiledevice-glue/include -I$(LIB_DIR)/libimobiledevice -I$(LIB_DIR)/libusbmuxd/include - -libplist_include := -I$(LIB_DIR)/libplist/include -libplist_src := $(wildcard $(LIB_DIR)/libplist/src/*.c) $(LIB_DIR)/libplist/libcnary/node.c $(LIB_DIR)/libplist/libcnary/node_list.c \ No newline at end of file diff --git a/makefiles/libimobiledevice-build/libimobiledevice.mak b/makefiles/libimobiledevice-build/libimobiledevice.mak deleted file mode 100644 index e28f3b7..0000000 --- a/makefiles/libimobiledevice-build/libimobiledevice.mak +++ /dev/null @@ -1,49 +0,0 @@ -ROOT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) -include $(ROOT_DIR)/../main.mak - -%.c.o : %.c - $(CC) $(CFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -%.cpp.o : %.cpp - $(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -CFLAGS += -DHAVE_CONFIG_H -DDEBUG -O0 -g - - -$(BUILD_DIR)/objs/%.c.o : $(MAIN_DIR)/%.c - mkdir -p $(@D) - $(CC) $(CFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -$(BUILD_DIR)/objs/%.cpp.o : $(MAIN_DIR)/%.cpp - mkdir -p $(@D) - $(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -o $@ -c $< - -include $(ROOT_DIR)/libimobiledevice-files.mak - -libimobiledevice_obj := $(libimobiledevice_src:$(MAIN_DIR)/%=$(BUILD_DIR)/objs/%.o) -$(libimobiledevice_obj) : EXTRA_FLAGS := -I$(ROOT_DIR) $(libimobiledevice_include) $(libplist_include) -I$(LIB_DIR)/libimobiledevice/common -I$(LIB_DIR)/libusbmuxd/common -$(BUILD_DIR)/libimobiledevice.a : $(libimobiledevice_obj) - ar rcs $@ $^ - -libplist_obj := $(libplist_src:$(MAIN_DIR)/%=$(BUILD_DIR)/objs/%.o) -$(libplist_obj) : EXTRA_FLAGS := -I$(ROOT_DIR) $(libplist_include) -I$(LIB_DIR)/libplist/libcnary/include -I$(LIB_DIR)/libplist/src -$(BUILD_DIR)/libplist.a : $(libplist_obj) - ar rcs $@ $^ - - -#allsrc += $(libimobiledevice_src) -#allsrc += $(libplist_src) -#allobj = $(addsuffix .o, $(allsrc)) - - -clean:: - rm -f $(libimobiledevice_obj) - rm -f $(libplist_obj) - rm -f $(BUILD_DIR)/libplist.a $(BUILD_DIR)/libimobiledevice.a -.PHONY : clean - -all :: $(BUILD_DIR)/libplist.a -all :: $(BUILD_DIR)/libimobiledevice.a -.PHONY : all - -.DEFAULT_GOAL := all diff --git a/makefiles/main.mak b/makefiles/main.mak deleted file mode 100644 index 44c1123..0000000 --- a/makefiles/main.mak +++ /dev/null @@ -1,9 +0,0 @@ -cur_dir := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) -MAIN_DIR := $(dir $(abspath $(cur_dir))) -MAIN_DIR := $(MAIN_DIR:/=) - -BUILD_DIR := $(CURDIR) - -UPSTREAM_DIR := $(MAIN_DIR)/upstream_repo - -LIB_DIR := $(MAIN_DIR)/libraries diff --git a/makefiles/rewrite_altserver_source.py b/makefiles/rewrite_altserver_source.py deleted file mode 100644 index 13de09c..0000000 --- a/makefiles/rewrite_altserver_source.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/python3 - -import re -import sys - -F = sys.argv[1] - -with open(F, 'rb') as f: - content = f.read() - -content = re.sub(br'L("([^"\\]|\\.)*")', br'U(\1)', content) -content = re.sub(br'\n(std::string StringFromWideString.*?\n\{[\s\S]+?\})', br'/*\1*/', content) -content = re.sub(br'\n(std::wstring WideStringFromString.*?\n\{[\s\S]+?\})', br'/*\1*/', content) -content = content.replace(b'std::wstring', b'std::string') -content = content.replace(b'std::string_convert', b'std::wstring_convert') - -content = content.replace(b'boost/filesystem.hpp', b'filesystem') -content = content.replace(b'boost::filesystem', b'std::filesystem') - -if F.endswith('AltServerApp.cpp'): - - # MessageBox - # IDCANCEL - # fs::path AltServerApp::appDataDirectoryPath - content = content.replace(b'\r', b'') - - content = content.replace(b'#include \n', b'') - content = content.replace(b'#include \n', b'') - content = content.replace(b'#include \n', b'') - content = content.replace(b'#include \n', b'') - content = content.replace(b'#include \n', b'') - content = content.replace(b'#pragma comment( lib, "gdiplus.lib" ) \n', b'') - content = content.replace(b'#include \n', b'') - content = content.replace(b'#include "resource.h"\n', b'') - - def removePart(content, start, end): - content = re.sub(br'\n' + start + br'[\S\s]+?(' + end + br')', br'\1', content) - return content - content = removePart(content, br'const char\* REGISTRY_ROOT_KEY', br'\nAltServerApp\* AltServerApp::_instance') - content = removePart(content, br'static int CALLBACK BrowseFolderCallback', br'\npplx::task> AltServerApp::InstallApplication') - content = removePart(content, br'\n.*? AltServerApp::Authenticate', br'\npplx::task> AltServerApp::FetchTeam') - content = removePart(content, br'void AltServerApp::ShowNotification', br'\nvoid AltServerApp::ShowErrorAlert') - content = removePart(content, br'bool AltServerApp::CheckDependencies', br'\nfs::path AltServerApp::certificatesDirectoryPath') - - def insertBefore(content, marker, newcontent): - content = content.replace(marker, newcontent + b'\n' + marker) - return content - - content = insertBefore(content, b'AltServerApp* AltServerApp::_instance = nullptr;', br''' -#define IDCANCEL 0 -#define MessageBox(x, content, title, xx) (this->ShowAlert(title, content " (Ctrl-C to avoid)"), 1) - -// Observes all exceptions that occurred in all tasks in the given range. -template -void observe_all_exceptions(InIt first, InIt last) -{ - // TODO: FIX THIS -} -''') - - content = insertBefore(content, b'fs::path AltServerApp::certificatesDirectoryPath', br''' -HWND AltServerApp::windowHandle() const -{ - return _windowHandle; -} - -HINSTANCE AltServerApp::instanceHandle() const -{ - return _instanceHandle; -} - - -bool AltServerApp::boolValueForRegistryKey(std::string key) const -{ - return false; -} - -void AltServerApp::setBoolValueForRegistryKey(bool value, std::string key) -{ - return; -} - -std::string AltServerApp::serverID() const -{ - //auto serverID = GetRegistryStringValue(SERVER_ID_KEY); - //return serverID; - return "1234567"; -} - -pplx::task, std::shared_ptr>> AltServerApp::Authenticate(std::string appleID, std::string password, std::shared_ptr anisetteData) -{ - auto verificationHandler = [=](void)->pplx::task> { - return pplx::create_task([=]() -> std::optional { - std::cout << "Enter two factor code" << std::endl; - std::string _verificationCode = ""; - std::cin >> _verificationCode; - auto verificationCode = std::make_optional(_verificationCode); - _verificationCode = ""; - - return verificationCode; - }); - }; - - return pplx::create_task([=]() { - if (anisetteData == NULL) - { - throw ServerError(ServerErrorCode::InvalidAnisetteData); - } - - return AppleAPI::getInstance()->Authenticate(appleID, password, anisetteData, verificationHandler); - }); -} - -void AltServerApp::HandleAnisetteError(AnisetteError& error) -{ - this->ShowAlert("AnisetteData error: ", error.localizedDescription()); -} - -void AltServerApp::ShowNotification(std::string title, std::string message) -{ - std::cout << "Notify: " << title << std::endl << " " << message << std::endl; -} - - -extern "C" int getchar(); -void AltServerApp::ShowAlert(std::string title, std::string message) -{ - std::cout << "Alert: " << title << std::endl << " " << message << std::endl; - std::cout << "Press any key to continue..." << std::endl; - //char a; - //std::cin >> a; - getchar(); -} - -fs::path AltServerApp::appDataDirectoryPath() const -{ - fs::path altserverDirectoryPath("./AltServerData"); - - if (!fs::exists(altserverDirectoryPath)) - { - fs::create_directory(altserverDirectoryPath); - } - - return altserverDirectoryPath; -} - -void AltServerApp::Start(HWND windowHandle, HINSTANCE instanceHandle) -{ - ConnectionManager::instance()->Start(); - - // DeviceManager only needs - const char *isNoUSB = getenv("ALTSERVER_NO_SUBSCRIBE"); - if (!isNoUSB) { - DeviceManager::instance()->Start(); - } -} - -void AltServerApp::Stop() -{ -} -''') - -sys.stdout.buffer.write(content) \ No newline at end of file diff --git a/shims/Psapi.h b/shims/Psapi.h deleted file mode 100644 index e69de29..0000000 diff --git a/shims/WS2tcpip.h b/shims/WS2tcpip.h deleted file mode 100644 index e684941..0000000 --- a/shims/WS2tcpip.h +++ /dev/null @@ -1,5 +0,0 @@ -#include -#include - -typedef int WSADATA; -#define WSAStartup(x, y) 0 \ No newline at end of file diff --git a/shims/WinSock2.h b/shims/WinSock2.h deleted file mode 100644 index abe0e35..0000000 --- a/shims/WinSock2.h +++ /dev/null @@ -1,5 +0,0 @@ -#include -#include - -#define SOCKADDR struct sockaddr -#define WSAGetLastError() errno \ No newline at end of file diff --git a/shims/direct.h b/shims/direct.h deleted file mode 100644 index e69de29..0000000 diff --git a/shims/io.h b/shims/io.h deleted file mode 100644 index dfe3400..0000000 --- a/shims/io.h +++ /dev/null @@ -1 +0,0 @@ -#include \ No newline at end of file diff --git a/shims/mman.h b/shims/mman.h deleted file mode 100644 index 8b05b1a..0000000 --- a/shims/mman.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/shims/old-linux-polyfill.c b/shims/old-linux-polyfill.c deleted file mode 100644 index 4387da6..0000000 --- a/shims/old-linux-polyfill.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include - -ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { - int randomData = open("/dev/urandom", O_RDONLY); - if (randomData < 0) - { - // something went wrong - return -1; - } - else - { - ssize_t result = read(randomData, buf, buflen); - if (result < 0) - { - // something went wrong - return -1; - } - return result; - } -} \ No newline at end of file diff --git a/shims/openssl/applink.c b/shims/openssl/applink.c deleted file mode 100644 index e69de29..0000000 diff --git a/shims/windows_shim.cpp b/shims/windows_shim.cpp deleted file mode 100644 index 91d12bf..0000000 --- a/shims/windows_shim.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "windows_shim.h" - -std::string StringFromWideString(std::string wideString) { - return wideString; -} - -std::string WideStringFromString(std::string string) { - return string; -} - -char *_itoa(int val, char *buf, int base) { - if (base != 10) return NULL; - sprintf(buf, "%d", val); - return buf; -} \ No newline at end of file diff --git a/shims/windows_shim.h b/shims/windows_shim.h deleted file mode 100644 index dfc70fc..0000000 --- a/shims/windows_shim.h +++ /dev/null @@ -1,29 +0,0 @@ -#define _chmod chmod -#define _close close -#define _open ::open -#define _stat stat -#define _fstat fstat -#define _access access -#define _mkdir(x) mkdir(x, 0777) - -char *_itoa(int val, char *buf, int base); - -#include - -#define __declspec(x) - -#define OutputDebugStringW(x) (std::wcout << x) -#define OutputDebugStringA(x) (std::cout << x) - -typedef struct timeval TIMEVAL; - -#include - -#ifdef __cplusplus -#include -#include -extern std::string StringFromWideString(std::string wideString); -extern std::string WideStringFromString(std::string string); -#endif - -#define Sleep(x) sleep((int)(x/1000)) diff --git a/src/AltServerApp.cpp b/src/AltServerApp.cpp new file mode 100644 index 0000000..2d22951 --- /dev/null +++ b/src/AltServerApp.cpp @@ -0,0 +1,1032 @@ +// +// AltServerApp.cpp +// AltServer-Windows +// +// Created by Riley Testut on 8/30/19. +// Copyright (c) 2019 Riley Testut. All rights reserved. +// +#include "AltServerApp.h" + +#include "AppleAPI.hpp" +#include "ConnectionManager.hpp" +#include "InstallError.hpp" +#include "Signer.hpp" +#include "DeviceManager.hpp" +#include "Archiver.hpp" +#include "ServerError.hpp" + +#include "AnisetteDataManager.h" + +#include +#include + +#include +#include +#include + +#include + +using namespace utility; // Common utilities like string conversions +using namespace web; // Common features like URIs. +using namespace web::http; // Common HTTP functionality +using namespace web::http::client; // HTTP client features +using namespace concurrency::streams; // Asynchronous streams + +extern std::string temporary_directory(); +extern std::string make_uuid(); +extern std::vector readFile(const char* filename); + + + +AltServerApp* AltServerApp::_instance = nullptr; + +AltServerApp* AltServerApp::instance() +{ + if (_instance == 0) + { + _instance = new AltServerApp(); + } + + return _instance; +} + +AltServerApp::AltServerApp() : _appGroupSemaphore(1) +{ +} + +AltServerApp::~AltServerApp() +{ +} + +void AltServerApp::Start(HWND windowHandle, HINSTANCE instanceHandle) +{ + ConnectionManager::instance()->Start(); + DeviceManager::instance()->Start(); +} + +void AltServerApp::Stop() +{ +} + +pplx::task> AltServerApp::InstallApplication(std::optional filepath, std::shared_ptr installDevice, std::string appleID, std::string password) +{ + return this->_InstallApplication(filepath, installDevice, appleID, password) + .then([=](pplx::task> task) -> pplx::task> { + try + { + auto application = task.get(); + return pplx::create_task([application]() { + return application; + }); + } + catch (APIError& error) + { + if ((APIErrorCode)error.code() == APIErrorCode::InvalidAnisetteData) + { + // Our attempt to re-provision the device as a Mac failed, so reset provisioning and try one more time. + // This appears to happen when iCloud is running simultaneously, and just happens to provision device at same time as AltServer. + AnisetteDataManager::instance()->ResetProvisioning(); + + this->ShowNotification("Registering PC with Apple...", "This may take a few seconds."); + + // Provisioning device can fail if attempted too soon after previous attempt. + // As a hack around this, we wait a bit before trying again. + // 10-11 seconds appears to be too short, so wait for 12 seconds instead. + sleep(12); + + return this->_InstallApplication(filepath, installDevice, appleID, password); + } + else + { + throw; + } + } + }) + .then([=](pplx::task> task) -> std::shared_ptr { + try + { + auto application = task.get(); + + std::stringstream ss; + ss << application->name() << " was successfully installed on " << installDevice->name() << "."; + + this->ShowNotification("Installation Succeeded", ss.str()); + + return application; + } + catch (InstallError& error) + { + if ((InstallErrorCode)error.code() == InstallErrorCode::Cancelled) + { + // Ignore + } + else + { + this->ShowAlert("Installation Failed", error.localizedDescription()); + throw; + } + } + catch (APIError& error) + { + if ((APIErrorCode)error.code() == APIErrorCode::InvalidAnisetteData) + { + AnisetteDataManager::instance()->ResetProvisioning(); + } + + this->ShowAlert("Installation Failed", error.localizedDescription()); + throw; + } + catch (AnisetteError& error) + { + this->ShowAlert("AnisetteData Failed", error.localizedDescription()); + throw; + } + catch (Error& error) + { + this->ShowAlert("Installation Failed", error.localizedDescription()); + throw; + } + catch (std::exception& exception) + { + odslog("Exception:" << exception.what()); + + this->ShowAlert("Installation Failed", exception.what()); + throw; + } + }); +} + +pplx::task> AltServerApp::_InstallApplication(std::optional filepath, std::shared_ptr installDevice, std::string appleID, std::string password) +{ + fs::path destinationDirectoryPath(temporary_directory()); + destinationDirectoryPath.append(make_uuid()); + + auto account = std::make_shared(); + auto app = std::make_shared(); + auto team = std::make_shared(); + auto device = std::make_shared(); + auto appID = std::make_shared(); + auto certificate = std::make_shared(); + auto profile = std::make_shared(); + + auto session = std::make_shared(); + + return pplx::create_task([=]() { + auto anisetteData = AnisetteDataManager::instance()->FetchAnisetteData(); + return this->Authenticate(appleID, password, anisetteData); + }) + .then([=](std::pair, std::shared_ptr> pair) + { + *account = *(pair.first); + *session = *(pair.second); + + odslog("Fetching team..."); + + return this->FetchTeam(account, session); + }) + .then([=](std::shared_ptr tempTeam) + { + odslog("Registering device..."); + + *team = *tempTeam; + return this->RegisterDevice(installDevice, team, session); + }) + .then([=](std::shared_ptr tempDevice) + { + odslog("Fetching certificate..."); + + *device = *tempDevice; + return this->FetchCertificate(team, session); + }) + .then([=](std::shared_ptr tempCertificate) + { + *certificate = *tempCertificate; + + if (filepath.has_value()) + { + odslog("Importing app..."); + + return pplx::create_task([filepath] { + return fs::path(*filepath); + }); + } + else + { + odslog("Downloading app..."); + + // Show alert before downloading AltStore. + this->ShowInstallationNotification("AltStore", device->name()); + return this->DownloadApp(); + } + }) + .then([=](fs::path downloadedAppPath) + { + odslog("Downloaded app!"); + + fs::create_directory(destinationDirectoryPath); + + auto appBundlePath = UnzipAppBundle(downloadedAppPath.string(), destinationDirectoryPath.string()); + auto app = std::make_shared(appBundlePath); + + if (filepath.has_value()) + { + // Show alert after "downloading" local .ipa. + this->ShowInstallationNotification(app->name(), device->name()); + } + else + { + // Remove downloaded app. + + try + { + fs::remove(downloadedAppPath); + } + catch (std::exception& e) + { + odslog("Failed to remove downloaded .ipa." << e.what()); + } + } + + return app; + }) + .then([=](std::shared_ptr tempApp) + { + odslog("Preparing provisioning profiles!"); + *app = *tempApp; + return this->PrepareAllProvisioningProfiles(app, device, team, session); + }) + .then([=](std::map> profiles) + { + odslog("Installing apps!"); + return this->InstallApp(app, device, team, certificate, profiles); + }) + .then([=](pplx::task> task) + { + if (fs::exists(destinationDirectoryPath)) + { + std::string comm = "rm -rf '"; + comm += destinationDirectoryPath.string(); + comm += "'"; + odslog("Removing tmp dir: " << comm); + system(comm.c_str()); + + // if (fs::exists(destinationDirectoryPath)) fs::remove_all(destinationDirectoryPath); + } + + try + { + auto application = task.get(); + return application; + } + catch (LocalizedError& error) + { + if (error.code() == -22421) + { + // Don't know what API call returns this error code, so assume any LocalizedError with -22421 error code + // means invalid anisette data, then throw the correct APIError. + throw APIError(APIErrorCode::InvalidAnisetteData); + } + else if (error.code() == -29004) + { + // Same with -29004, "Environment Mismatch" + throw APIError(APIErrorCode::InvalidAnisetteData); + } + else + { + throw; + } + } + }); +} + +pplx::task AltServerApp::DownloadApp() +{ + fs::path temporaryPath(temporary_directory()); + temporaryPath.append(make_uuid()); + + auto outputFile = std::make_shared(); + + // Open stream to output file. + auto task = fstream::open_ostream((temporaryPath.string())) + .then([=](ostream file) + { + *outputFile = file; + + uri_builder builder("https://cdn.altstore.io/file/altstore/altstore.ipa"); + + http_client client(builder.to_uri()); + return client.request(methods::GET); + }) + .then([=](http_response response) + { + printf("Received download response status code:%u\n", response.status_code()); + + // Write response body into the file. + return response.body().read_to_end(outputFile->streambuf()); + }) + .then([=](size_t) + { + outputFile->close(); + return temporaryPath; + }); + + return task; +} + +pplx::task, std::shared_ptr>> AltServerApp::Authenticate(std::string appleID, std::string password, std::shared_ptr anisetteData) +{ + auto verificationHandler = [=](void)->pplx::task> { + return pplx::create_task([=]() -> std::optional { + std::cout << "Enter two factor code" << std::endl; + std::string _verificationCode = ""; + std::cin >> _verificationCode; + auto verificationCode = std::make_optional(_verificationCode); + _verificationCode = ""; + + return verificationCode; + }); + }; + + return pplx::create_task([=]() { + if (anisetteData == NULL) + { + throw ServerError(ServerErrorCode::InvalidAnisetteData); + } + + return AppleAPI::getInstance()->Authenticate(appleID, password, anisetteData, verificationHandler); + }); +} + +pplx::task> AltServerApp::FetchTeam(std::shared_ptr account, std::shared_ptr session) +{ + auto task = AppleAPI::getInstance()->FetchTeams(account, session) + .then([](std::vector> teams) { + + for (auto& team : teams) + { + if (team->type() == Team::Type::Individual) + { + return team; + } + } + + for (auto& team : teams) + { + if (team->type() == Team::Type::Free) + { + return team; + } + } + + for (auto& team : teams) + { + return team; + } + + throw InstallError(InstallErrorCode::NoTeam); + }); + + return task; +} + +pplx::task> AltServerApp::FetchCertificate(std::shared_ptr team, std::shared_ptr session) +{ + auto task = AppleAPI::getInstance()->FetchCertificates(team, session) + .then([this, team, session](std::vector> certificates) + { + auto certificatesDirectoryPath = this->certificatesDirectoryPath(); + auto cachedCertificatePath = certificatesDirectoryPath.append(team->identifier() + ".p12"); + + std::shared_ptr preferredCertificate = nullptr; + + for (auto& certificate : certificates) + { + if (!certificate->machineName().has_value()) + { + continue; + } + + std::string prefix("AltStore"); + + if (certificate->machineName()->size() < prefix.size()) + { + // Machine name doesn't begin with "AltStore", so ignore. + continue; + } + else + { + auto result = std::mismatch(prefix.begin(), prefix.end(), certificate->machineName()->begin()); + if (result.first != prefix.end()) + { + // Machine name doesn't begin with "AltStore", so ignore. + continue; + } + } + + if (fs::exists(cachedCertificatePath) && certificate->machineIdentifier().has_value()) + { + try + { + auto data = readFile(cachedCertificatePath.string().c_str()); + auto cachedCertificate = std::make_shared(data, *certificate->machineIdentifier()); + + // Manually set machineIdentifier so we can encrypt + embed certificate if needed. + cachedCertificate->setMachineIdentifier(*certificate->machineIdentifier()); + + return pplx::create_task([cachedCertificate] { + return cachedCertificate; + }); + } + catch(std::exception &e) + { + // Ignore cached certificate errors. + odslog("Failed to load cached certificate:" << cachedCertificatePath << ". " << e.what()) + } + } + + preferredCertificate = certificate; + + // Machine name starts with AltStore. + + this->ShowAlert("Installing AltStore with Multiple AltServers Not Supported", + "Please use the same AltServer you previously used with this Apple ID, or else apps installed with other AltServers will stop working.\n\nAre you sure you want to continue? (Ctrl-C to avoid)"); + break; + } + + if (certificates.size() != 0) + { + auto certificate = (preferredCertificate != nullptr) ? preferredCertificate : certificates[0]; + return AppleAPI::getInstance()->RevokeCertificate(certificate, team, session).then([this, team, session](bool success) + { + return this->FetchCertificate(team, session); + }); + } + else + { + std::string machineName = "AltStore"; + + return AppleAPI::getInstance()->AddCertificate(machineName, team, session) + .then([team, session, cachedCertificatePath](std::shared_ptr addedCertificate) + { + auto privateKey = addedCertificate->privateKey(); + if (privateKey == std::nullopt) + { + throw InstallError(InstallErrorCode::MissingPrivateKey); + } + + return AppleAPI::getInstance()->FetchCertificates(team, session) + .then([privateKey, addedCertificate, cachedCertificatePath](std::vector> certificates) + { + std::shared_ptr certificate = nullptr; + + for (auto tempCertificate : certificates) + { + if (tempCertificate->serialNumber() == addedCertificate->serialNumber()) + { + certificate = tempCertificate; + break; + } + } + + if (certificate == nullptr) + { + throw InstallError(InstallErrorCode::MissingCertificate); + } + + certificate->setPrivateKey(privateKey); + + try + { + if (certificate->machineIdentifier().has_value()) + { + auto machineIdentifier = certificate->machineIdentifier(); + + auto encryptedData = certificate->encryptedP12Data(*machineIdentifier); + if (encryptedData.has_value()) + { + std::ofstream fout(cachedCertificatePath.string(), std::ios::out | std::ios::binary); + fout.write((const char*)encryptedData->data(), encryptedData->size()); + fout.close(); + } + } + } + catch (std::exception& e) + { + // Ignore caching certificate errors. + odslog("Failed to cache certificate:" << cachedCertificatePath << ". " << e.what()) + } + + return certificate; + }); + }); + } + }); + + return task; +} + +pplx::task>> AltServerApp::PrepareAllProvisioningProfiles( + std::shared_ptr application, + std::shared_ptr device, + std::shared_ptr team, + std::shared_ptr session) +{ + return this->PrepareProvisioningProfile(application, std::nullopt, device, team, session) + .then([=](std::shared_ptr profile) { + std::vector>>> tasks; + + auto task = pplx::create_task([application, profile]() -> std::pair> { + return std::make_pair(application->bundleIdentifier(), profile); + }); + tasks.push_back(task); + + for (auto appExtension : application->appExtensions()) + { + auto task = this->PrepareProvisioningProfile(appExtension, application, device, team, session) + .then([appExtension](std::shared_ptr profile) { + return std::make_pair(appExtension->bundleIdentifier(), profile); + }); + tasks.push_back(task); + } + + return pplx::when_all(tasks.begin(), tasks.end()) + .then([tasks](pplx::task>>> task) { + try + { + auto pairs = task.get(); + + std::map> profiles; + for (auto& pair : pairs) + { + profiles[pair.first] = pair.second; + } + + //observe_all_exceptions>>(tasks.begin(), tasks.end()); + return profiles; + } + catch (std::exception& e) + { + //observe_all_exceptions>>(tasks.begin(), tasks.end()); + throw; + } + }); + }); +} + +pplx::task> AltServerApp::PrepareProvisioningProfile( + std::shared_ptr app, + std::optional> parentApp, + std::shared_ptr device, + std::shared_ptr team, + std::shared_ptr session) +{ + std::string preferredName; + std::string parentBundleID; + + if (parentApp.has_value()) + { + parentBundleID = (*parentApp)->bundleIdentifier(); + preferredName = (*parentApp)->name() + " " + app->name(); + } + else + { + parentBundleID = app->bundleIdentifier(); + preferredName = app->name(); + } + + std::string updatedParentBundleID; + + if (app->isAltStoreApp()) + { + std::stringstream ss; + ss << "com." << team->identifier() << "." << parentBundleID; + + updatedParentBundleID = ss.str(); + } + else + { + updatedParentBundleID = parentBundleID + "." + team->identifier(); + } + + std::string bundleID = std::regex_replace(app->bundleIdentifier(), std::regex(parentBundleID), updatedParentBundleID); + + return this->RegisterAppID(preferredName, bundleID, team, session) + .then([=](std::shared_ptr appID) + { + return this->UpdateAppIDFeatures(appID, app, team, session); + }) + .then([=](std::shared_ptr appID) + { + return this->UpdateAppIDAppGroups(appID, app, team, session); + }) + .then([=](std::shared_ptr appID) + { + return this->FetchProvisioningProfile(appID, device, team, session); + }) + .then([=](std::shared_ptr profile) + { + return profile; + }); +} + +pplx::task> AltServerApp::RegisterAppID(std::string appName, std::string bundleID, std::shared_ptr team, std::shared_ptr session) +{ + auto task = AppleAPI::getInstance()->FetchAppIDs(team, session) + .then([bundleID, appName, team, session](std::vector> appIDs) + { + std::shared_ptr appID = nullptr; + + for (auto tempAppID : appIDs) + { + if (tempAppID->bundleIdentifier() == bundleID) + { + appID = tempAppID; + break; + } + } + + if (appID != nullptr) + { + return pplx::task>([appID]() + { + return appID; + }); + } + else + { + return AppleAPI::getInstance()->AddAppID(appName, bundleID, team, session); + } + }); + + return task; +} + +pplx::task> AltServerApp::UpdateAppIDFeatures(std::shared_ptr appID, std::shared_ptr app, std::shared_ptr team, std::shared_ptr session) +{ + //TODO: Add support for additional features besides app groups. + + std::map altstoreFeatures = appID->features(); + + auto boolNode = plist_new_bool(true); + altstoreFeatures[AppIDFeatureAppGroups] = boolNode; + + //TODO: Only update features if needed. + + std::shared_ptr copiedAppID = std::make_shared(*appID); + copiedAppID->setFeatures(altstoreFeatures); + + plist_free(boolNode); + + return AppleAPI::getInstance()->UpdateAppID(copiedAppID, team, session); +} + +pplx::task> AltServerApp::UpdateAppIDAppGroups(std::shared_ptr appID, std::shared_ptr app, std::shared_ptr team, std::shared_ptr session) +{ + return pplx::create_task([=]() -> pplx::task> { + auto applicationGroupsNode = app->entitlements()["com.apple.security.application-groups"]; + std::vector applicationGroups; + + if (applicationGroupsNode != nullptr) + { + for (int i = 0; i < plist_array_get_size(applicationGroupsNode); i++) + { + auto groupNode = plist_array_get_item(applicationGroupsNode, i); + + char* groupName = nullptr; + plist_get_string_val(groupNode, &groupName); + + applicationGroups.push_back(groupName); + } + } + + if (applicationGroups.size() == 0) + { + auto appGroupsNode = appID->features()["APG3427HIY"]; // App group feature ID + if (appGroupsNode != nullptr) + { + uint8_t isAppGroupsEnabled = 0; + plist_get_bool_val(appGroupsNode, &isAppGroupsEnabled); + + if (!isAppGroupsEnabled) + { + // No app groups, and we haven't enabled the feature already, so don't continue. + return pplx::create_task([appID]() { + return appID; + }); + } + } + } + + this->_appGroupSemaphore.wait(); + + return AppleAPI::getInstance()->FetchAppGroups(team, session) + .then([=](std::vector> fetchedGroups) { + + std::vector>> tasks; + + for (auto groupIdentifier : applicationGroups) + { + std::string adjustedGroupIdentifier = groupIdentifier + "." + team->identifier(); + std::optional> matchingGroup; + + for (auto group : fetchedGroups) + { + if (group->groupIdentifier() == adjustedGroupIdentifier) + { + matchingGroup = group; + break; + } + } + + if (matchingGroup.has_value()) + { + auto task = pplx::create_task([matchingGroup]() { return *matchingGroup; }); + tasks.push_back(task); + } + else + { + std::string name = std::regex_replace("AltStore " + groupIdentifier, std::regex("\\."), " "); + + auto task = AppleAPI::getInstance()->AddAppGroup(name, adjustedGroupIdentifier, team, session); + tasks.push_back(task); + } + } + + return pplx::when_all(tasks.begin(), tasks.end()).then([=](pplx::task>> task) { + try + { + auto groups = task.get(); + //observe_all_exceptions>(tasks.begin(), tasks.end()); + return groups; + } + catch (std::exception& e) + { + //observe_all_exceptions>(tasks.begin(), tasks.end()); + throw; + } + }); + }) + .then([=](std::vector> groups) { + return AppleAPI::getInstance()->AssignAppIDToGroups(appID, groups, team, session); + }) + .then([appID](bool result) { + return appID; + }) + .then([this](pplx::task> task) { + this->_appGroupSemaphore.notify(); + + auto appID = task.get(); + return appID; + }); + }); +} + +pplx::task> AltServerApp::RegisterDevice(std::shared_ptr device, std::shared_ptr team, std::shared_ptr session) +{ + auto task = AppleAPI::getInstance()->FetchDevices(team, device->type(), session) + .then([device, team, session](std::vector> devices) + { + std::shared_ptr matchingDevice = nullptr; + + for (auto tempDevice : devices) + { + odslog("Comparing device: " << tempDevice << "with " << device); + if (tempDevice->identifier() == device->identifier()) + { + matchingDevice = tempDevice; + break; + } + } + + if (matchingDevice != nullptr) + { + return pplx::task>([matchingDevice]() + { + return matchingDevice; + }); + } + else + { + return AppleAPI::getInstance()->RegisterDevice(device->name(), device->identifier(), device->type(), team, session); + } + }); + + return task; +} + +pplx::task> AltServerApp::FetchProvisioningProfile(std::shared_ptr appID, std::shared_ptr device, std::shared_ptr team, std::shared_ptr session) +{ + return AppleAPI::getInstance()->FetchProvisioningProfile(appID, device->type(), team, session); +} + +pplx::task> AltServerApp::InstallApp(std::shared_ptr app, + std::shared_ptr device, + std::shared_ptr team, + std::shared_ptr certificate, + std::map> profilesByBundleID) +{ + auto prepareInfoPlist = [profilesByBundleID](std::shared_ptr app, plist_t additionalValues){ + auto profile = profilesByBundleID.at(app->bundleIdentifier()); + + fs::path infoPlistPath(app->path()); + infoPlistPath.append("Info.plist"); + + auto data = readFile(infoPlistPath.string().c_str()); + + plist_t plist = nullptr; + plist_from_memory((const char*)data.data(), (int)data.size(), &plist); + if (plist == nullptr) + { + throw InstallError(InstallErrorCode::MissingInfoPlist); + } + + plist_dict_set_item(plist, "CFBundleIdentifier", plist_new_string(profile->bundleIdentifier().c_str())); + plist_dict_set_item(plist, "ALTBundleIdentifier", plist_new_string(app->bundleIdentifier().c_str())); + + if (additionalValues != NULL) + { + plist_dict_merge(&plist, additionalValues); + } + + plist_t entitlements = profile->entitlements(); + if (entitlements != nullptr) + { + plist_t appGroups = plist_copy(plist_dict_get_item(entitlements, "com.apple.security.application-groups")); + plist_dict_set_item(plist, "ALTAppGroups", appGroups); + } + + char* plistXML = nullptr; + uint32_t length = 0; + plist_to_xml(plist, &plistXML, &length); + + std::ofstream fout(infoPlistPath.string(), std::ios::out | std::ios::binary); + fout.write(plistXML, length); + fout.close(); + }; + + return pplx::task>([=]() { + fs::path infoPlistPath(app->path()); + infoPlistPath.append("Info.plist"); + + odslog("Signing: Reading InfoPlist..."); + auto data = readFile(infoPlistPath.string().c_str()); + + plist_t plist = nullptr; + plist_from_memory((const char *)data.data(), (int)data.size(), &plist); + if (plist == nullptr) + { + throw InstallError(InstallErrorCode::MissingInfoPlist); + } + + plist_t additionalValues = plist_new_dict(); + + std::string openAppURLScheme = "altstore-" + app->bundleIdentifier(); + + plist_t allURLSchemes = plist_dict_get_item(plist, "CFBundleURLTypes"); + if (allURLSchemes == nullptr) + { + allURLSchemes = plist_new_array(); + } + else + { + allURLSchemes = plist_copy(allURLSchemes); + } + + plist_t altstoreURLScheme = plist_new_dict(); + plist_dict_set_item(altstoreURLScheme, "CFBundleTypeRole", plist_new_string("Editor")); + plist_dict_set_item(altstoreURLScheme, "CFBundleURLName", plist_new_string(app->bundleIdentifier().c_str())); + + plist_t schemesNode = plist_new_array(); + plist_array_append_item(schemesNode, plist_new_string(openAppURLScheme.c_str())); + plist_dict_set_item(altstoreURLScheme, "CFBundleURLSchemes", schemesNode); + + plist_array_append_item(allURLSchemes, altstoreURLScheme); + plist_dict_set_item(additionalValues, "CFBundleURLTypes", allURLSchemes); + + if (app->isAltStoreApp()) + { + plist_dict_set_item(additionalValues, "ALTDeviceID", plist_new_string(device->identifier().c_str())); + + auto serverID = this->serverID(); + plist_dict_set_item(additionalValues, "ALTServerID", plist_new_string(serverID.c_str())); + + auto machineIdentifier = certificate->machineIdentifier(); + if (machineIdentifier.has_value()) + { + auto encryptedData = certificate->encryptedP12Data(*machineIdentifier); + if (encryptedData.has_value()) + { + plist_dict_set_item(additionalValues, "ALTCertificateID", plist_new_string(certificate->serialNumber().c_str())); + + // Embed encrypted certificate in app bundle. + fs::path certificatePath(app->path()); + certificatePath.append("ALTCertificate.p12"); + + std::ofstream fout(certificatePath.string(), std::ios::out | std::ios::binary); + fout.write((const char*)encryptedData->data(), encryptedData->size()); + fout.close(); + } + } + } + + odslog("Signing: Preparing InfoPlist..."); + prepareInfoPlist(app, additionalValues); + + for (auto appExtension : app->appExtensions()) + { + odslog("Signing: Preparing InfoPlist for extensions..."); + prepareInfoPlist(appExtension, NULL); + } + + odslog("Signing: Preparing provisioning profiles..."); + std::vector> profiles; + std::set profileIdentifiers; + for (auto pair : profilesByBundleID) + { + profiles.push_back(pair.second); + profileIdentifiers.insert(pair.second->bundleIdentifier()); + } + + odslog("Signing: Signing app..."); + Signer signer(team, certificate); + signer.SignApp(app->path(), profiles); + + std::optional> activeProfiles = std::nullopt; + if (team->type() == Team::Type::Free && app->isAltStoreApp()) + { + activeProfiles = profileIdentifiers; + } + + odslog("Signing: Installing app..."); + return DeviceManager::instance()->InstallApp(app->path(), device->identifier(), activeProfiles, [](double progress) { + odslog("Installation Progress: " << progress); + }) + .then([app] { + return app; + }); + }); +} + +void AltServerApp::ShowNotification(std::string title, std::string message) +{ + std::cout << "Notify: " << title << std::endl << " " << message << std::endl; +} + +void AltServerApp::ShowAlert(std::string title, std::string message) +{ + std::cout << "Alert: " << title << std::endl << " " << message << std::endl; + std::cout << "Press any key to continue..." << std::endl; + char a; + std::cin >> a; +} + +void AltServerApp::ShowInstallationNotification(std::string appName, std::string deviceName) +{ + std::stringstream ssTitle; + ssTitle << "Installing " << appName << " to " << deviceName << "..."; + + std::stringstream ssMessage; + ssMessage << "This may take a few seconds."; + + this->ShowNotification(ssTitle.str(), ssMessage.str()); +} + +HWND AltServerApp::windowHandle() const +{ + return _windowHandle; +} + +HINSTANCE AltServerApp::instanceHandle() const +{ + return _instanceHandle; +} + +std::string AltServerApp::serverID() const +{ + return "1234567"; +} + +fs::path AltServerApp::appDataDirectoryPath() const +{ + fs::path altserverDirectoryPath("./AltServerData"); + + if (!fs::exists(altserverDirectoryPath)) + { + fs::create_directory(altserverDirectoryPath); + } + + return altserverDirectoryPath; +} + +fs::path AltServerApp::certificatesDirectoryPath() const +{ + auto appDataPath = this->appDataDirectoryPath(); + auto certificatesDirectoryPath = appDataPath.append("Certificates"); + + if (!fs::exists(certificatesDirectoryPath)) + { + fs::create_directory(certificatesDirectoryPath); + } + + return certificatesDirectoryPath; +} \ No newline at end of file diff --git a/src/AltServerApp.h b/src/AltServerApp.h new file mode 100644 index 0000000..ca86a8e --- /dev/null +++ b/src/AltServerApp.h @@ -0,0 +1,128 @@ +// +// AltServerApp.hpp +// AltServer-Windows +// +// Created by Riley Testut on 8/30/19. +// Copyright (c) 2019 Riley Testut. All rights reserved. +// + +#pragma once + +#include "common.h" + +#include + +#include "Account.hpp" +#include "AppID.hpp" +#include "Application.hpp" +#include "Certificate.hpp" +#include "Device.hpp" +#include "ProvisioningProfile.hpp" +#include "Team.hpp" + +#include "AppleAPISession.h" +#include "AnisetteDataManager.h" + +#include "Semaphore.h" + +#include + +#ifdef _WIN32 +#include +#undef _WINSOCKAPI_ +#define _WINSOCKAPI_ /* prevents inclusion by */ +#include +namespace fs = std::filesystem; +#else +#include +namespace fs = boost::filesystem; +#endif + +class AltServerApp +{ +public: + static AltServerApp *instance(); + + void Start(HWND windowHandle, HINSTANCE instanceHandle); + void Stop(); + void CheckForUpdates(); + + pplx::task> InstallApplication(std::optional filepath, std::shared_ptr device, std::string appleID, std::string password); + + void ShowNotification(std::string title, std::string message); + void ShowAlert(std::string title, std::string message); + + HWND windowHandle() const; + HINSTANCE instanceHandle() const; + + bool automaticallyLaunchAtLogin() const; + void setAutomaticallyLaunchAtLogin(bool launch); + + std::string serverID() const; + void setServerID(std::string serverID); + + bool reprovisionedDevice() const; + void setReprovisionedDevice(bool reprovisionedDevice); + + std::string appleFolderPath() const; + std::string internetServicesFolderPath() const; + std::string applicationSupportFolderPath() const; +private: + AltServerApp(); + ~AltServerApp(); + + static AltServerApp *_instance; + + pplx::task> _InstallApplication(std::optional filepath, std::shared_ptr installDevice, std::string appleID, std::string password); + + bool CheckDependencies(); + bool CheckiCloudDependencies(); + + std::string BrowseForFolder(std::wstring title, std::string folderPath); + + bool _presentedNotification; + + HWND _windowHandle; + HINSTANCE _instanceHandle; + + Semaphore _appGroupSemaphore; + + bool presentedRunningNotification() const; + void setPresentedRunningNotification(bool presentedRunningNotification); + + void setAppleFolderPath(std::string appleFolderPath); + std::string defaultAppleFolderPath() const; + + fs::path appDataDirectoryPath() const; + fs::path certificatesDirectoryPath() const; + + pplx::task DownloadApp(); + + void ShowInstallationNotification(std::string appName, std::string deviceName); + + pplx::task, std::shared_ptr>> Authenticate(std::string appleID, std::string password, std::shared_ptr anisetteData); + pplx::task> FetchTeam(std::shared_ptr account, std::shared_ptr session); + pplx::task> FetchCertificate(std::shared_ptr team, std::shared_ptr session); + pplx::task>> PrepareAllProvisioningProfiles( + std::shared_ptr application, + std::shared_ptr device, + std::shared_ptr team, + std::shared_ptr session); + pplx::task> PrepareProvisioningProfile( + std::shared_ptr application, + std::optional> parentApp, + std::shared_ptr device, + std::shared_ptr team, + std::shared_ptr session); + pplx::task> RegisterAppID(std::string appName, std::string identifier, std::shared_ptr team, std::shared_ptr session); + pplx::task> UpdateAppIDFeatures(std::shared_ptr appID, std::shared_ptr app, std::shared_ptr team, std::shared_ptr session); + pplx::task> UpdateAppIDAppGroups(std::shared_ptr appID, std::shared_ptr app, std::shared_ptr team, std::shared_ptr session); + pplx::task> RegisterDevice(std::shared_ptr device, std::shared_ptr team, std::shared_ptr session); + pplx::task> FetchProvisioningProfile(std::shared_ptr appID, std::shared_ptr device, std::shared_ptr team, std::shared_ptr session); + + pplx::task> InstallApp(std::shared_ptr app, + std::shared_ptr device, + std::shared_ptr team, + std::shared_ptr certificate, + std::map> profiles); +}; diff --git a/src/AltServerMain.cpp b/src/AltServerMain.cpp index e169582..5142475 100644 --- a/src/AltServerMain.cpp +++ b/src/AltServerMain.cpp @@ -22,7 +22,7 @@ #include "AltServerApp.h" -#define odslog(msg) { std::stringstream ss; ss << msg << std::endl; OutputDebugStringA(ss.str().c_str()); } +#include "PhoneHelper.h" #include #include @@ -68,35 +68,52 @@ std::vector readFile(const char* filename) return vec; } -#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED -#include +#include +#include + +boost::asio::io_service io_service; +std::shared_ptr timer; +void *hbclient; + +void heartbeat_tick(const boost::system::error_code& /*e*/) { + int intervalSec = do_heartbeat(hbclient); + odslog("heartbeat_tick! interval: " << intervalSec); + if (!intervalSec) { + return; + } + // Reschedule the timer for 1 second in the future: + //timer->expires_from_now(boost::posix_time::seconds(4)); + // Posts the timer event + //timer->async_wait(heartbeat_tick); + + auto &ioService = crossplat::threadpool::shared_instance().service(); + boost::asio::steady_timer t(ioService, std::chrono::seconds(4)); + t.async_wait(heartbeat_tick); +} + +int setupHeartbeatTimer() { + auto &ioService = crossplat::threadpool::shared_instance().service(); -#include - -void print_help() { - printf("Usage: AltServer-Linux options [ ipa-file ]\n"); - printf( - " -h --help Display this usage information.\n" - " -u --udid UDID Device's UDID, only needed when installing IPA.\n" - " -a --appleID AppleID Apple ID to sign the ipa, only needed when installing IPA.\n" - " -p --password passwd Password of Apple ID, only needed when installing IPA.\n" - " -d --debug Print debug output, can be used several times to increase debug level.\n" - "\n" - "The following environment var can be set for some special situation:\n" - " - ALTSERVER_ANISETTE_SERVER: Set to custom anisette server URL\n" - " if not set, the default one: https://armconverter.com/anisette/irGb3Quww8zrhgqnzmrx, is used\n" - " - ALTSERVER_NO_SUBSCRIBE: (*unused*) Please enable this for usbmuxd server that do not correctly usbmuxd_listen interfaces\n" - ); + // timer = std::make_shared(ioService); + // timer->expires_from_now(boost::posix_time::seconds(1)); + // timer->async_wait(heartbeat_tick); + + boost::system::error_code err; + heartbeat_tick(err); + return 1; } +#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED +#include + int main(int argc, char *argv[]) { static struct option long_options[] = { {"udid", required_argument, 0, 'u'}, + {"ipaddr", required_argument, 0, 'i'}, {"appleID", required_argument, 0, 'a'}, {"password", required_argument, 0, 'p'}, - //{"ipaddr", required_argument, 0, 'i'}, - //{"pairData", required_argument, 0, 'P'}, + {"pairData", required_argument, 0, 'P'}, {"debug", no_argument, 0, 'd'}, {0, 0, 0, 0} }; @@ -108,7 +125,7 @@ int main(int argc, char *argv[]) { char *pairDataFile; char *ipaPath = NULL; - int debugLogLevel = 0; + bool debugLog = false; while (1) { int this_option_optind = optind ? optind : 1; @@ -134,23 +151,13 @@ int main(int argc, char *argv[]) { pairDataFile = optarg; break; case 'd': - //debugLog = true; - debugLogLevel++; + debugLog = true; break; - case 'h': - print_help(); - exit(0); - default: + default: printf("?? getopt returned character code 0%o ??\n", c); - print_help(); - exit(1); } } - if (argc == 1) { - printf("No argument supplied, if you want for help, please use -h or --help\n"); - } - bool installApp = true; if (optind == argc) { printf("Not supplying ipa, running in server mode!\n"); @@ -171,11 +178,39 @@ int main(int argc, char *argv[]) { srand(time(NULL)); - if (debugLogLevel) { - idevice_set_debug_level(debugLogLevel); - libusbmuxd_set_debug_level(debugLogLevel - 2); + if (debugLog) { + idevice_set_debug_level(1); } + +#ifndef NO_USBMUXD_STUB + setupPairInfo(udid, ipaddr, pairDataFile); + +#ifndef NO_UPNP_STUB + if (!initUPnP()) { + DEBUG_PRINT("failed to init upnp! exitting..."); + return 1; + } + DEBUG_PRINT("upnp init successfully!"); +#endif + + DEBUG_PRINT("Connect device..."); + if (!initGlobalDevice()) { + DEBUG_PRINT("failed to init device! exitting..."); + return 1; + } + if (!initHeartbeat(&hbclient)) { + DEBUG_PRINT("failed to init heartbeat! exitting..."); + return 1; + } + if (!setupHeartbeatTimer()) { + DEBUG_PRINT("failed to init heartbeat! exitting..."); + return 1; + } + DEBUG_PRINT("heartbeat init successfully!"); + +#endif + signal(SIGPIPE, SIG_IGN); if (installApp) { diff --git a/src/AnisetteDataManager.cpp b/src/AnisetteDataManager.cpp index 84ba608..afb0d06 100644 --- a/src/AnisetteDataManager.cpp +++ b/src/AnisetteDataManager.cpp @@ -11,8 +11,6 @@ #include "AnisetteData.h" #include "AltServerApp.h" -#define odslog(msg) { std::stringstream ss; ss << msg << std::endl; OutputDebugStringA(ss.str().c_str()); } - AnisetteDataManager* AnisetteDataManager::_instance = nullptr; AnisetteDataManager* AnisetteDataManager::instance() @@ -49,27 +47,15 @@ using namespace web; // Common features like URIs. using namespace web::http; // Common HTTP functionality using namespace web::http::client; // HTTP client features -std::string GetAnisetteURL() { - const char *server = getenv("ALTSERVER_ANISETTE_SERVER"); - if (server) { - return server; - } - return U("https://armconverter.com/anisette/irGb3Quww8zrhgqnzmrx"); -} - std::shared_ptr AnisetteDataManager::FetchAnisetteData() { - // auto client = web::http::client::http_client(U("https://armconverter.com")); - // std::string wideURI = ("/anisette/irGb3Quww8zrhgqnzmrx"); + std::string wideURI = ("/anisette/irGb3Quww8zrhgqnzmrx"); - // auto encodedURI = web::uri::encode_uri(wideURI); - // uri_builder builder(encodedURI); - - // http_request request(methods::GET); - // request.set_request_uri(builder.to_string()); + auto encodedURI = web::uri::encode_uri(wideURI); + uri_builder builder(encodedURI); - auto client = web::http::client::http_client(GetAnisetteURL()); http_request request(methods::GET); + request.set_request_uri(builder.to_string()); std::map headers = { {"User-Agent", "Xcode"}, @@ -87,6 +73,7 @@ std::shared_ptr AnisetteDataManager::FetchAnisetteData() std::shared_ptr anisetteData = NULL; + auto client = web::http::client::http_client(U("https://armconverter.com")); auto task = client.request(request) .then([=](http_response response) { @@ -119,11 +106,10 @@ std::shared_ptr AnisetteDataManager::FetchAnisetteData() } struct tm tm = { 0 }; - strptime(jsonVal.at("X-Apple-I-Client-Time").as_string().c_str(), "%Y-%m-%dT%H:%M:%SZ", &tm); - unsigned long ts = mktime(&tm); - struct timeval tv = { 0 }; + strptime(jsonVal.at("X-Apple-I-Client-Time").as_string().c_str(), "%FT%T%z", &tm); + time_t ts = mktime(&tm); + struct timeval tv = {0}; tv.tv_sec = ts; - tv.tv_usec = 0; odslog("Building anisetteData obj..."); anisetteData = std::make_shared( @@ -137,7 +123,7 @@ std::shared_ptr AnisetteDataManager::FetchAnisetteData() tv, jsonVal.at("X-Apple-Locale").as_string(), jsonVal.at("X-Apple-I-TimeZone").as_string()); - + //IterateJSONValue(); }); diff --git a/src/AnisetteDataManager.h b/src/AnisetteDataManager.h new file mode 100644 index 0000000..cc4b909 --- /dev/null +++ b/src/AnisetteDataManager.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include + +#include "Error.hpp" + +class AnisetteData; + +enum class AnisetteErrorCode +{ + iTunesNotInstalled, + iCloudNotInstalled, + MissingApplicationSupportFolder, + MissingAOSKit, + MissingObjc, + MissingFoundation, + InvalidiTunesInstallation, +}; + +class AnisetteError : public Error +{ +public: + AnisetteError(AnisetteErrorCode code) : Error((int)code) + { + } + + virtual std::string domain() const + { + return "com.rileytestut.AltServer.AnisetteError"; + } + + virtual std::string localizedDescription() const + { + if (this->_localizedDescription.size() > 0) + { + return this->_localizedDescription; + } + + switch ((AnisetteErrorCode)this->code()) + { + case AnisetteErrorCode::iTunesNotInstalled: return "iTunes Not Found"; + case AnisetteErrorCode::iCloudNotInstalled: return "iCloud Not Found"; + case AnisetteErrorCode::MissingApplicationSupportFolder: return "Missing 'Application Support' in 'Apple' Folder."; + case AnisetteErrorCode::MissingAOSKit: return "Missing 'AOSKit.dll' in 'Internet Services' Folder."; + case AnisetteErrorCode::MissingFoundation: return "Missing 'Foundation.dll' in 'Apple Application Support' Folder."; + case AnisetteErrorCode::MissingObjc: return "Missing 'objc.dll' in 'Apple Application Support' Folder."; + case AnisetteErrorCode::InvalidiTunesInstallation: return "Invalid iTunes installation."; + } + + return ""; + } + + void setLocalizedDescription(std::string localizedDescription) + { + _localizedDescription = localizedDescription; + } + +private: + std::string _localizedDescription; +}; + +class AnisetteDataManager +{ +public: + static AnisetteDataManager* instance(); + + std::shared_ptr FetchAnisetteData(); + bool LoadDependencies(); + + bool ResetProvisioning(); + +private: + AnisetteDataManager(); + ~AnisetteDataManager(); + + static AnisetteDataManager* _instance; + + bool ReprovisionDevice(std::function provisionCallback); + bool LoadiCloudDependencies(); + + bool loadedDependencies; +}; + diff --git a/src/ClientConnection.cpp b/src/ClientConnection.cpp new file mode 100644 index 0000000..714984a --- /dev/null +++ b/src/ClientConnection.cpp @@ -0,0 +1,430 @@ +#include "ClientConnection.h" + +#include +#include +#include +#include + +#include "DeviceManager.hpp" +#include "AnisetteDataManager.h" +#include "AnisetteData.h" + +#include "ServerError.hpp" + +extern std::string make_uuid(); +extern std::string temporary_directory(); + +using namespace web; + +namespace fs = std::filesystem; + +ClientConnection::ClientConnection() +{ +} + +ClientConnection::~ClientConnection() +{ + this->Disconnect(); +} + +void ClientConnection::Disconnect() +{ +} + +pplx::task ClientConnection::ProcessAppRequest() +{ + auto task = this->ReceiveRequest().then([this](web::json::value request) { + auto identifier = (request["identifier"].as_string()); + + if (identifier == "PrepareAppRequest") + { + return this->ProcessPrepareAppRequest(request); + } + else if (identifier == "AnisetteDataRequest") + { + return this->ProcessAnisetteDataRequest(request); + } + else if (identifier == "InstallProvisioningProfilesRequest") + { + return this->ProcessInstallProfilesRequest(request); + } + else if (identifier == "RemoveProvisioningProfilesRequest") + { + return this->ProcessRemoveProfilesRequest(request); + } + else if (identifier == "RemoveAppRequest") + { + return this->ProcessRemoveAppRequest(request); + } + else + { + throw ServerError(ServerErrorCode::UnknownRequest); + } + }).then([this](pplx::task task) { + try + { + task.get(); + } + catch (std::exception& e) + { + auto errorResponse = this->ErrorResponse(e); + this->SendResponse(errorResponse); + + throw; + } + }); + + return task; +} + +pplx::task ClientConnection::ProcessPrepareAppRequest(web::json::value request) +{ + utility::string_t* filepath = new utility::string_t; + std::string udid = (request["udid"].as_string()); + + return this->ReceiveApp(request).then([this, filepath](std::string path) { + *filepath = (path); + return this->ReceiveRequest(); + }) + .then([this, filepath, udid](web::json::value request) { + std::optional> activeProfiles = std::nullopt; + + if (request.has_array_field("activeProfiles")) + { + activeProfiles = std::set(); + + auto array = request["activeProfiles"].as_array(); + for (auto& value : array) + { + auto bundleIdentifier = value.as_string(); + activeProfiles->insert((bundleIdentifier)); + } + } + + return this->InstallApp((*filepath), udid, activeProfiles); + }) + .then([this, filepath, udid](pplx::task task) { + + if (filepath->size() > 0) + { + try + { + fs::remove(fs::path(*filepath)); + } + catch (std::exception& e) + { + odslog("Failed to remove received .ipa." << e.what()); + } + } + + delete filepath; + + try + { + task.get(); + + auto response = json::value::object(); + response["version"] = json::value::number(1); + response["identifier"] = json::value::string("InstallationProgressResponse"); + response["progress"] = json::value::number(1.0); + return this->SendResponse(response); + } + catch (std::exception& exception) + { + throw; + } + }); +} + +pplx::task ClientConnection::ProcessAnisetteDataRequest(web::json::value request) +{ + return pplx::create_task([this, &request]() { + + auto anisetteData = AnisetteDataManager::instance()->FetchAnisetteData(); + if (!anisetteData) + { + throw ServerError(ServerErrorCode::InvalidAnisetteData); + } + + auto response = json::value::object(); + response["version"] = json::value::number(1); + response["identifier"] = json::value::string("AnisetteDataResponse"); + response["anisetteData"] = anisetteData->json(); + return this->SendResponse(response); + }); +} + +pplx::task ClientConnection::ReceiveApp(web::json::value request) +{ + auto appSize = request["contentSize"].as_integer(); + std::cout << "Receiving app (" << appSize << " bytes)..." << std::endl; + + return this->ReceiveData(appSize).then([this](std::vector data) { + fs::path filepath = fs::path(temporary_directory()).append(make_uuid() + ".ipa"); + + std::ofstream file(filepath.string(), std::ios::out | std::ios::binary); + copy(data.cbegin(), data.cend(), std::ostreambuf_iterator(file)); + + return filepath.string(); + }); +} + +pplx::task ClientConnection::InstallApp(std::string filepath, std::string udid, std::optional> activeProfiles) +{ + return pplx::create_task([this, filepath, udid, activeProfiles]() { + try { + auto isSending = std::make_shared(); + + return DeviceManager::instance()->InstallApp(filepath, udid, activeProfiles, [this, isSending](double progress) { + if (*isSending) + { + return; + } + + *isSending = true; + + auto response = json::value::object(); + response["version"] = json::value::number(1); + response["identifier"] = json::value::string("InstallationProgressResponse"); + response["progress"] = json::value::number(progress); + + this->SendResponse(response).then([isSending](pplx::task task) { + *isSending = false; + }); + }); + } + catch (Error& error) + { + std::cout << error << std::endl; + + throw error; + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << std::endl; + + throw e; + } + std::cout << "Installed app!" << std::endl; + }); +} + +pplx::task ClientConnection::ProcessInstallProfilesRequest(web::json::value request) +{ + std::string udid = (request["udid"].as_string()); + + std::vector> provisioningProfiles; + + auto array = request["provisioningProfiles"].as_array(); + for (auto& value : array) + { + auto encodedData = value.as_string(); + auto data = utility::conversions::from_base64(encodedData); + + auto profile = std::make_shared(data); + if (profile != nullptr) + { + provisioningProfiles.push_back(profile); + } + } + + std::optional> activeProfiles = std::nullopt; + if (request.has_array_field("activeProfiles")) + { + activeProfiles = std::set(); + + auto array = request["activeProfiles"].as_array(); + for (auto& value : array) + { + auto bundleIdentifier = value.as_string(); + activeProfiles->insert((bundleIdentifier)); + } + } + + return DeviceManager::instance()->InstallProvisioningProfiles(provisioningProfiles, udid, activeProfiles) + .then([=](pplx::task task) { + try + { + task.get(); + + auto response = json::value::object(); + response["version"] = json::value::number(1); + response["identifier"] = json::value::string("InstallProvisioningProfilesResponse"); + return this->SendResponse(response); + } + catch (std::exception& exception) + { + throw; + } + }); +} + +pplx::task ClientConnection::ProcessRemoveProfilesRequest(web::json::value request) +{ + std::string udid = (request["udid"].as_string()); + + std::set bundleIdentifiers; + + auto array = request["bundleIdentifiers"].as_array(); + for (auto& value : array) + { + auto bundleIdentifier = (value.as_string()); + bundleIdentifiers.insert(bundleIdentifier); + } + + return DeviceManager::instance()->RemoveProvisioningProfiles(bundleIdentifiers, udid) + .then([=](pplx::task task) { + try + { + task.get(); + + auto response = json::value::object(); + response["version"] = json::value::number(1); + response["identifier"] = json::value::string("RemoveProvisioningProfilesResponse"); + return this->SendResponse(response); + } + catch (std::exception& exception) + { + throw; + } + }); +} + +pplx::task ClientConnection::ProcessRemoveAppRequest(web::json::value request) +{ + std::string udid = (request["udid"].as_string()); + auto bundleIdentifier = (request["bundleIdentifier"].as_string()); + + return DeviceManager::instance()->RemoveApp(bundleIdentifier, udid) + .then([=](pplx::task task) { + try + { + task.get(); + + auto response = json::value::object(); + response["version"] = json::value::number(1); + response["identifier"] = json::value::string("RemoveAppResponse"); + return this->SendResponse(response); + } + catch (std::exception& exception) + { + throw; + } + }); +} + +web::json::value ClientConnection::ErrorResponse(std::exception& exception) +{ + auto response = json::value::object(); + response["version"] = json::value::number(2); + response["identifier"] = json::value::string("ErrorResponse"); + + auto errorObject = json::value::object(); + + try + { + Error& error = dynamic_cast(exception); + + response["errorCode"] = json::value::number(error.code()); + errorObject["errorCode"] = json::value::number(error.code()); + + if (!error.userInfo().empty()) + { + auto userInfo = json::value::object(); + + for (auto& pair : error.userInfo()) + { + userInfo[(pair.first)] = json::value((pair.second)); + } + + errorObject["userInfo"] = userInfo; + } + } + catch (std::bad_cast) + { + response["errorCode"] = json::value::number((int)ServerErrorCode::Unknown); + errorObject["errorCode"] = json::value::number((int)ServerErrorCode::Unknown); + + auto userInfo = json::value::object(); + + if (std::string(exception.what()) == "vector too long") + { + userInfo["NSLocalizedFailureReason"] = json::value::string("Windows Defender Blocked Installation"); + userInfo["NSLocalizedRecoverySuggestion"] = json::value::string("Disable Windows real-time protection on your computer then try again."); + } + else + { + userInfo["NSLocalizedDescription"] = json::value::string((exception.what())); + userInfo["NSLocalizedFailureReason"] = json::value::string((exception.what())); + } + + errorObject["userInfo"] = userInfo; + } + + response["serverError"] = errorObject; + + return response; +} + +pplx::task ClientConnection::SendResponse(web::json::value json) +{ + auto serializedJSON = json.serialize(); + std::vector responseData(serializedJSON.begin(), serializedJSON.end()); + + int32_t size = (int32_t)responseData.size(); + + std::vector responseSizeData; + + if (responseSizeData.size() < sizeof(size)) + { + responseSizeData.resize(sizeof(size)); + } + + std::memcpy(responseSizeData.data(), &size, sizeof(size)); + + std::cout << "Represented Value: " << *((int32_t*)responseSizeData.data()) << std::endl; + + auto task = this->SendData(responseSizeData) + .then([this, responseData]() mutable { + return this->SendData(responseData); + }) + .then([](pplx::task task) { + try + { + task.get(); + } + catch (Error& error) + { + odslog("Failed to send response. " << error.localizedDescription()); + } + catch (std::exception& exception) + { + odslog("Failed to send response. " << exception.what()); + } + }); + + return task; +} + +pplx::task ClientConnection::ReceiveRequest() +{ + int size = sizeof(uint32_t); + + std::cout << "Receiving request size..." << std::endl; + + auto task = this->ReceiveData(size) + .then([this](std::vector data) { + int expectedBytes = *((int32_t*)data.data()); + std::cout << "Receiving " << expectedBytes << " bytes..." << std::endl; + + return this->ReceiveData(expectedBytes); + }) + .then([](std::vector data) { + std::string jsonString(data.begin(), data.end()); + + auto request = web::json::value::parse(jsonString); + return request; + }); + + return task; +} diff --git a/src/ClientConnection.h b/src/ClientConnection.h new file mode 100644 index 0000000..4e97c5b --- /dev/null +++ b/src/ClientConnection.h @@ -0,0 +1,43 @@ +#pragma once + +#include "common.h" +#include "Device.hpp" + +#include +#include + +#include +#include + +#include +#include + +class ClientConnection +{ +public: + ClientConnection(); + virtual ~ClientConnection(); + + virtual void Disconnect(); + + pplx::task ProcessAppRequest(); + + pplx::task ProcessPrepareAppRequest(web::json::value request); + pplx::task ProcessAnisetteDataRequest(web::json::value request); + pplx::task ProcessInstallProfilesRequest(web::json::value request); + pplx::task ProcessRemoveProfilesRequest(web::json::value request); + pplx::task ProcessRemoveAppRequest(web::json::value request); + + pplx::task SendResponse(web::json::value json); + pplx::task ReceiveRequest(); + + virtual pplx::task SendData(std::vector& data) = 0; + virtual pplx::task> ReceiveData(int size) = 0; + +private: + pplx::task ReceiveApp(web::json::value request); + pplx::task InstallApp(std::string filepath, std::string udid, std::optional> activeProfiles); + + web::json::value ErrorResponse(std::exception& exception); +}; + diff --git a/src/ConnectionManager.cpp b/src/ConnectionManager.cpp new file mode 100644 index 0000000..826c33c --- /dev/null +++ b/src/ConnectionManager.cpp @@ -0,0 +1,340 @@ +// +// ConnectionManager.cpp +// AltServer-Windows +// +// Created by Riley Testut on 8/13/19. +// Copyright © 2019 Riley Testut. All rights reserved. +// + +#include "ConnectionManager.hpp" + +#include +#include + +//#include + +#include + +/* +#include +#include +#include +*/ + +#ifdef HAVE_MDNS +#include "dns_sd.h" +#endif + +#include "AltServerApp.h" +#include "WirelessConnection.h" +#include "DeviceManager.hpp" +#include "Error.hpp" + +#include + +#define WIRED_SERVER_CONNECTION_AVAILABLE_REQUEST "io.altstore.Request.WiredServerConnectionAvailable" +#define WIRED_SERVER_CONNECTION_AVAILABLE_RESPONSE "io.altstore.Response.WiredServerConnectionAvailable" +#define WIRED_SERVER_CONNECTION_START_REQUEST "io.altstore.Request.WiredServerConnectionStart" + +#ifdef HAVE_MDNS +void DNSSD_API ConnectionManagerBonjourRegistrationFinished(DNSServiceRef service, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) +{ + std::cout << "Registered service: " << name << " (Error: " << errorCode << ")" << std::endl; +} +#endif + +void ConnectionManagerConnectedDevice(std::shared_ptr device) +{ + ConnectionManager::instance()->StartNotificationConnection(device); +} + +void ConnectionManagerDisconnectedDevice(std::shared_ptr device) +{ + ConnectionManager::instance()->StopNotificationConnection(device); +} + +ConnectionManager* ConnectionManager::_instance = nullptr; + +ConnectionManager* ConnectionManager::instance() +{ + if (_instance == 0) + { + _instance = new ConnectionManager(); + } + + return _instance; +} + +ConnectionManager::ConnectionManager() +{ + DeviceManager::instance()->setConnectedDeviceCallback(ConnectionManagerConnectedDevice); + DeviceManager::instance()->setDisconnectedDeviceCallback(ConnectionManagerDisconnectedDevice); +} + +void ConnectionManager::Start() +{ + auto listenFunction = [](void) { + ConnectionManager::instance()->Listen(); + }; + + _listeningThread = std::thread(listenFunction); +} + +void ConnectionManager::Disconnect(std::shared_ptr connection) +{ + connection->Disconnect(); + _connections.erase(connection); +} + +void ConnectionManager::StartAdvertising(int socketPort) +{ +#ifdef HAVE_MDNS + DNSServiceRef service = NULL; + uint16_t port = htons(socketPort); + + auto serverID = AltServerApp::instance()->serverID(); + + std::string txtValue("serverID=" + serverID); + char size = txtValue.size(); + + std::vector txtData; + txtData.reserve(size + 1); + txtData.push_back(size); + + for (auto& byte : txtValue) + { + txtData.push_back(byte); + } + + DNSServiceErrorType registrationResult = DNSServiceRegister(&service, 0, 0, NULL, "_altserver._tcp", NULL, NULL, port, txtData.size(), txtData.data(), ConnectionManagerBonjourRegistrationFinished, NULL); + if (registrationResult != kDNSServiceErr_NoError) + { + std::cout << "Bonjour Registration Error: " << registrationResult << std::endl; + return; + } + + int dnssd_socket = DNSServiceRefSockFD(service); + if (dnssd_socket == -1) + { + std::cout << "Failed to retrieve mDNSResponder socket." << std::endl; + } + + this->_mDNSResponderSocket = dnssd_socket; +#endif +} + +void ConnectionManager::Listen() +{ + int socket4 = socket(AF_INET, SOCK_STREAM, 0); + if (socket4 == 0) + { + std::cout << "Failed to create socket." << std::endl; + return; + } + + struct sockaddr_in address4; + memset(&address4, 0, sizeof(address4)); + //address4.sin_len = sizeof(address4); + address4.sin_family = AF_INET; + address4.sin_port = 0; // Choose for us. + address4.sin_addr.s_addr = INADDR_ANY; + + if (bind(socket4, (struct sockaddr *)&address4, sizeof(address4)) < 0) + { + std::cout << "Failed to bind socket." << std::endl; + return; + } + + if (listen(socket4, 0) != 0) + { + std::cout << "Failed to prepare listening socket." << std::endl; + } + + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + + if (getsockname(socket4, (struct sockaddr *)&sin, &len) == -1) + { + std::cout << "Failed to get socket name." << std::endl; + } + + int port4 = ntohs(sin.sin_port); + this->StartAdvertising(port4); + + fd_set input_set; + fd_set copy_set; + + while (true) + { + struct timeval tv; + tv.tv_sec = 1; /* 1 second timeout */ + tv.tv_usec = 0; /* no microseconds. */ + + /* Selection */ + FD_ZERO(&input_set ); /* Empty the FD Set */ + FD_SET(socket4, &input_set); /* Listen to the input descriptor */ + + FD_ZERO(©_set ); /* Empty the FD Set */ + FD_SET(socket4, ©_set); /* Listen to the input descriptor */ + + int ready_for_reading = select(socket4 + 1, &input_set, ©_set, NULL, &tv); + + /* Selection handling */ + if (ready_for_reading > 0) + { + + + struct sockaddr_in clientAddress; + memset(&clientAddress, 0, sizeof(clientAddress)); + + socklen_t addrlen = sizeof(clientAddress); + int other_socket = accept(socket4, (struct sockaddr *)&clientAddress, &addrlen); + + + char *ipaddress = inet_ntoa(((struct sockaddr_in)clientAddress).sin_addr); + int port2 = ntohs(((struct sockaddr_in)clientAddress).sin_port); + //int error = WSAGetLastError(); + int error = errno; + + odslog("Other Socket:" << other_socket << ". Port: " << port2 << ". Error: " << error); + + std::shared_ptr clientConnection(new WirelessConnection(other_socket)); + this->HandleRequest(clientConnection); + } + else if (ready_for_reading == -1) + { + /* Handle the error */ + std::cout << "Uh-oh" << std::endl; + } + else + { + // Do nothing + } + } +} + +void ConnectionManager::StartNotificationConnection(std::shared_ptr device) +{ + odslog("Starting notification connection to device: " << device->name().c_str()); + + DeviceManager::instance()->StartNotificationConnection(device) + .then([=](pplx::task> task) { + std::vector notifications = { WIRED_SERVER_CONNECTION_AVAILABLE_REQUEST, WIRED_SERVER_CONNECTION_START_REQUEST }; + + try + { + auto connection = task.get(); + + connection->StartListening(notifications); + connection->setReceivedNotificationHandler([=](std::string notification) { + this->HandleNotification(notification, connection); + }); + + this->_notificationConnections[device->identifier()] = connection; + } + catch (Error& e) + { + odslog("Failed to start notification connection. " << e.localizedDescription().c_str()); + } + catch (std::exception& e) + { + odslog("Failed to start notification connection. " << e.what()); + } + }); +} + +void ConnectionManager::StopNotificationConnection(std::shared_ptr device) +{ + if (this->notificationConnections().count(device->identifier()) == 0) + { + return; + } + + auto connection = this->notificationConnections()[device->identifier()]; + connection->Disconnect(); + + this->_notificationConnections.erase(device->identifier()); +} + +void ConnectionManager::HandleNotification(std::string notification, std::shared_ptr connection) +{ + if (notification == WIRED_SERVER_CONNECTION_AVAILABLE_REQUEST) + { + try + { + connection->SendNotification(WIRED_SERVER_CONNECTION_AVAILABLE_RESPONSE); + + odslog("Sent wired server connection available response!"); + } + catch (Error& e) + { + odslog("Error sending wired server connection response. " << e.localizedDescription().c_str()); + } + catch (std::exception& e) + { + odslog("Error sending wired server connection response. " << e.what()); + } + } + else if (notification == WIRED_SERVER_CONNECTION_START_REQUEST) + { + DeviceManager::instance()->StartWiredConnection(connection->device()) + .then([=](pplx::task> task) { + try + { + auto wiredConnection = task.get(); + auto connection = std::dynamic_pointer_cast(wiredConnection); + + odslog("Started wired server connection!"); + + this->HandleRequest(wiredConnection); + } + catch (Error& e) + { + odslog("Error starting wired server connection. " << e.localizedDescription().c_str()); + } + catch (std::exception& e) + { + odslog("Error starting wired server connection. " << e.what()); + } + }); + } +} + +void ConnectionManager::HandleRequest(std::shared_ptr clientConnection) +{ + this->_connections.insert(clientConnection); + + clientConnection->ProcessAppRequest().then([=](pplx::task task) { + try + { + task.get(); + + odslog("Finished handling request!"); + } + catch (Error& e) + { + odslog("Failed to handle request:" << e.localizedDescription().c_str()); + } + catch (std::exception& e) + { + odslog("Failed to handle request:" << e.what()); + } + + this->Disconnect(clientConnection); + }); +} + +int ConnectionManager::mDNSResponderSocket() const +{ + return _mDNSResponderSocket; +} + +std::set> ConnectionManager::connections() const +{ + return _connections; +} + +std::map> ConnectionManager::notificationConnections() const +{ + return _notificationConnections; +} diff --git a/src/ConnectionManager.hpp b/src/ConnectionManager.hpp new file mode 100644 index 0000000..3af6199 --- /dev/null +++ b/src/ConnectionManager.hpp @@ -0,0 +1,57 @@ +// +// ConnectionManager.hpp +// AltServer-Windows +// +// Created by Riley Testut on 8/13/19. +// Copyright © 2019 Riley Testut. All rights reserved. +// + +#ifndef ConnectionManager_hpp +#define ConnectionManager_hpp + +#include +#include +#include +#include + +#include "ClientConnection.h" +#include "NotificationConnection.h" + +class ConnectionManager +{ +public: + static ConnectionManager* instance(); + + void Start(); + void Disconnect(std::shared_ptr connection); + +private: + ConnectionManager(); + ~ConnectionManager(); + + static ConnectionManager* _instance; + + std::thread _listeningThread; + + int _mDNSResponderSocket; + std::set> _connections; + std::map> _notificationConnections; + + int mDNSResponderSocket() const; + std::set> connections() const; + std::map> notificationConnections() const; + + void Listen(); + void StartAdvertising(int port); + + void StartNotificationConnection(std::shared_ptr device); + void StopNotificationConnection(std::shared_ptr device); + + void HandleRequest(std::shared_ptr connection); + void HandleNotification(std::string notification, std::shared_ptr connection); + + friend void ConnectionManagerConnectedDevice(std::shared_ptr device); + friend void ConnectionManagerDisconnectedDevice(std::shared_ptr device); +}; + +#endif /* ConnectionManager_hpp */ diff --git a/src/DeviceManager.cpp b/src/DeviceManager.cpp new file mode 100644 index 0000000..cb5afde --- /dev/null +++ b/src/DeviceManager.cpp @@ -0,0 +1,1454 @@ +// +// DeviceManager.cpp +// AltServer-Windows +// +// Created by Riley Testut on 8/13/19. +// Copyright © 2019 Riley Testut. All rights reserved. +// + +#include "DeviceManager.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include "Archiver.hpp" +#include "ServerError.hpp" +#include "ProvisioningProfile.hpp" +#include "Application.hpp" + + +#define DEVICE_LISTENING_SOCKET 28151 + +void DeviceManagerUpdateStatus(plist_t command, plist_t status, void *udid); +void DeviceManagerUpdateAppDeletionStatus(plist_t command, plist_t status, void* udid); +void DeviceDidChangeConnectionStatus(const idevice_event_t* event, void* user_data); + +namespace fs = std::filesystem; + +extern std::string make_uuid(); +extern std::string temporary_directory(); +extern std::vector readFile(const char* filename); + +idevice_error_t idevice_new_all(idevice_t *idevice, const char *udid) { + return idevice_new_with_options(idevice, udid, (idevice_options)(IDEVICE_LOOKUP_USBMUX | IDEVICE_LOOKUP_NETWORK)); +} + +idevice_error_t idevice_new_ignore_network(idevice_t *idevice, const char *udid) { + return idevice_new_with_options(idevice, udid, IDEVICE_LOOKUP_USBMUX); +} + + +/// Returns a version of 'str' where every occurrence of +/// 'find' is substituted by 'replace'. +/// - Inspired by James Kanze. +/// - http://stackoverflow.com/questions/20406744/ +std::string replace_all( + const std::string& str, // where to work + const std::string& find, // substitute 'find' + const std::string& replace // by 'replace' +) { + using namespace std; + string result; + size_t find_len = find.size(); + size_t pos, from = 0; + while (string::npos != (pos = str.find(find, from))) { + result.append(str, from, pos - from); + result.append(replace); + from = pos + find_len; + } + result.append(str, from, string::npos); + return result; +} + +DeviceManager* DeviceManager::_instance = nullptr; + +DeviceManager* DeviceManager::instance() +{ + if (_instance == 0) + { + _instance = new DeviceManager(); + } + + return _instance; +} + +DeviceManager::DeviceManager() +{ +} + +void DeviceManager::Start() +{ + idevice_event_subscribe(DeviceDidChangeConnectionStatus, NULL); +} + +pplx::task DeviceManager::InstallApp(std::string appFilepath, std::string deviceUDID, std::optional> activeProfiles, std::function progressCompletionHandler) +{ + return pplx::task([=] { + // Enforce only one installation at a time. + this->_mutex.lock(); + + auto UUID = make_uuid(); + + char* uuidString = (char*)malloc(UUID.size() + 1); + strncpy(uuidString, (const char*)UUID.c_str(), UUID.size()); + uuidString[UUID.size()] = '\0'; + + idevice_t device = nullptr; + lockdownd_client_t client = NULL; + instproxy_client_t ipc = NULL; + afc_client_t afc = NULL; + misagent_client_t mis = NULL; + lockdownd_service_descriptor_t service = NULL; + + fs::path temporaryDirectory(temporary_directory()); + temporaryDirectory.append(make_uuid()); + + fs::create_directory(temporaryDirectory); + + auto installedProfiles = std::make_shared>>(); + auto cachedProfiles = std::make_shared>>(); + + auto finish = [this, installedProfiles, cachedProfiles, activeProfiles, temporaryDirectory, &uuidString] + (idevice_t device, lockdownd_client_t client, instproxy_client_t ipc, afc_client_t afc, misagent_client_t mis, lockdownd_service_descriptor_t service) + { + auto cleanUp = [=]() { + instproxy_client_free(ipc); + afc_client_free(afc); + lockdownd_client_free(client); + misagent_client_free(mis); + idevice_free(device); + lockdownd_service_descriptor_free(service); + + free(uuidString); + + this->_mutex.unlock(); + // if (fs::exists(temporaryDirectory)) fs::remove_all(temporaryDirectory); + }; + + try + { + if (activeProfiles.has_value()) + { + // Remove installed provisioning profiles if they're not active. + for (auto& installedProfile : *installedProfiles) + { + if (std::count(activeProfiles->begin(), activeProfiles->end(), installedProfile->bundleIdentifier()) == 0) + { + this->RemoveProvisioningProfile(installedProfile, mis); + } + } + } + + for (auto& pair : *cachedProfiles) + { + BOOL reinstall = true; + + for (auto& installedProfile : *installedProfiles) + { + if (installedProfile->bundleIdentifier() == pair.second->bundleIdentifier()) + { + // Don't reinstall cached profile because it was installed with app. + reinstall = false; + break; + } + } + + if (reinstall) + { + this->InstallProvisioningProfile(pair.second, mis); + } + } + } + catch (std::exception& exception) + { + cleanUp(); + throw; + } + + // Clean up outside scope so if an exception is thrown, we don't + // catch it ourselves again. + cleanUp(); + }; + + try + { + fs::path filepath(appFilepath); + + auto extension = filepath.extension().string(); + std::transform(extension.begin(), extension.end(), extension.begin(), [](unsigned char c) { + return std::tolower(c); + }); + + fs::path appBundlePath; + + if (extension == ".app") + { + appBundlePath = filepath; + } + else if (extension == ".ipa") + { + std::cout << "Unzipping .ipa..." << std::endl; + appBundlePath = UnzipAppBundle(filepath.string(), temporaryDirectory.string()); + } + else + { + throw SignError(SignErrorCode::InvalidApp); + } + + std::shared_ptr application = std::make_shared(appBundlePath.string()); + if (application == NULL) + { + throw SignError(SignErrorCode::InvalidApp); + } + + if (application->provisioningProfile()) + { + installedProfiles->push_back(application->provisioningProfile()); + } + + for (auto& appExtension : application->appExtensions()) + { + if (appExtension->provisioningProfile()) + { + installedProfiles->push_back(appExtension->provisioningProfile()); + } + } + + odslog("InstallApp: Finding Device...") + /* Find Device */ + if (idevice_new_all(&device, deviceUDID.c_str()) != IDEVICE_E_SUCCESS) + { + throw ServerError(ServerErrorCode::DeviceNotFound); + } + + odslog("InstallApp: Connecting Device...") + /* Connect to Device */ + if (lockdownd_client_new_with_handshake(device, &client, "altserver") != LOCKDOWN_E_SUCCESS) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + odslog("InstallApp: Starting instproxy...") + /* Connect to Installation Proxy */ + if ((lockdownd_start_service(client, "com.apple.mobile.installation_proxy", &service) != LOCKDOWN_E_SUCCESS) || service == NULL) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + odslog("InstallApp: Connecting instproxy...") + if (instproxy_client_new(device, service, &ipc) != INSTPROXY_E_SUCCESS) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + if (service) + { + lockdownd_service_descriptor_free(service); + service = NULL; + } + + + odslog("InstallApp: Starting misagent...") + /* Connect to Misagent */ + // Must connect now, since if we take too long writing files to device, connecting may fail later when managing profiles. + if (lockdownd_start_service(client, "com.apple.misagent", &service) != LOCKDOWN_E_SUCCESS || service == NULL) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + odslog("InstallApp: Connecting misagent...") + if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + + odslog("InstallApp: Starting afc...") + /* Connect to AFC service */ + if ((lockdownd_start_service(client, "com.apple.afc", &service) != LOCKDOWN_E_SUCCESS) || service == NULL) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + odslog("InstallApp: Connecting afc...") + if (afc_client_new(device, service, &afc) != AFC_E_SUCCESS) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + odslog("InstallApp: Preparing to write files to device...") + fs::path stagingPath("PublicStaging"); + + /* Prepare for installation */ + char** files = NULL; + if (afc_get_file_info(afc, (const char*)stagingPath.c_str(), &files) != AFC_E_SUCCESS) + { + if (afc_make_directory(afc, (const char*)stagingPath.c_str()) != AFC_E_SUCCESS) + { + throw ServerError(ServerErrorCode::DeviceWriteFailed); + } + } + + if (files) + { + int i = 0; + + while (files[i]) + { + free(files[i]); + i++; + } + + free(files); + } + + std::cout << "Writing to device..." << std::endl; + + plist_t options = instproxy_client_options_new(); + instproxy_client_options_add(options, "PackageType", "Developer", NULL); + + fs::path destinationPath = stagingPath.append(appBundlePath.filename().string()); + + int numberOfFiles = 0; + for (auto& item : fs::recursive_directory_iterator(appBundlePath)) + { + if (item.is_regular_file()) + { + numberOfFiles++; + } + } + + int writtenFiles = 0; + + try + { + this->WriteDirectory(afc, appBundlePath.string(), destinationPath.string(), [&writtenFiles, numberOfFiles, progressCompletionHandler](std::string filepath) { + writtenFiles++; + + double progress = (double)writtenFiles / (double)numberOfFiles; + double weightedProgress = progress * 0.75; + progressCompletionHandler(weightedProgress); + }); + } + catch (ServerError& e) + { + if (application->bundleIdentifier().find("science.xnu.undecimus") != std::string::npos) + { + auto userInfo = e.userInfo(); + userInfo["NSLocalizedRecoverySuggestion"] = "Make sure Windows real-time protection is disabled on your computer then try again."; + + throw ServerError((ServerErrorCode)e.code(), userInfo); + } + else + { + throw; + } + } + catch (std::exception& exception) + { + if (application->bundleIdentifier().find("science.xnu.undecimus") != std::string::npos) + { + std::map userInfo = { + { "NSLocalizedDescription", exception.what() }, + { "NSLocalizedRecoverySuggestion", "Make sure Windows real-time protection is disabled on your computer then try again." } + }; + + if (std::string(exception.what()) == std::string("vector too long")) + { + userInfo["NSLocalizedFailureReason"] = "Windows Defender Blocked Installation"; + } + else + { + userInfo["NSLocalizedFailureReason"] = exception.what(); + } + + throw ServerError(ServerErrorCode::Unknown, userInfo); + } + else + { + throw; + } + } + + std::cout << "Finished writing to device." << std::endl; + + + if (service) + { + lockdownd_service_descriptor_free(service); + service = NULL; + } + + /* Provisioning Profiles */ + bool shouldManageProfiles = (activeProfiles.has_value() || (application->provisioningProfile() != NULL && application->provisioningProfile()->isFreeProvisioningProfile())); + if (shouldManageProfiles) + { + // Free developer account was used to sign this app, so we need to remove all + // provisioning profiles in order to remain under sideloaded app limit. + + auto removedProfiles = this->RemoveAllFreeProvisioningProfilesExcludingBundleIdentifiers({}, mis); + for (auto& pair : removedProfiles) + { + if (activeProfiles.has_value()) + { + if (activeProfiles->count(pair.first) > 0) + { + // Only cache active profiles to reinstall afterwards. + (*cachedProfiles)[pair.first] = pair.second; + } + } + else + { + // Cache all profiles to reinstall afterwards if we didn't provide activeProfiles. + (*cachedProfiles)[pair.first] = pair.second; + } + } + } + + lockdownd_client_free(client); + client = NULL; + + std::mutex waitingMutex; + std::condition_variable cv; + + std::optional serverError = std::nullopt; + std::optional localizedError = std::nullopt; + + bool didBeginInstalling = false; + bool didFinishInstalling = false; + + this->_installationProgressHandlers[UUID] = [device, client, ipc, afc, mis, service, finish, progressCompletionHandler, + &waitingMutex, &cv, &didBeginInstalling, &didFinishInstalling, &serverError, &localizedError](double progress, int resultCode, char *name, char *description) { + double weightedProgress = progress * 0.25; + double adjustedProgress = weightedProgress + 0.75; + + if (progress == 0 && didBeginInstalling) + { + if (resultCode != 0 || name != NULL) + { + if (resultCode == -402620383) + { + std::map userInfo = { + { "NSLocalizedRecoverySuggestion", "Make sure 'Offload Unused Apps' is disabled in Settings > iTunes & App Stores, then install or delete all offloaded apps." } + }; + serverError = std::make_optional(ServerErrorCode::MaximumFreeAppLimitReached, userInfo); + } + else + { + std::string errorName(name); + + if (errorName == "DeviceOSVersionTooLow") + { + serverError = std::make_optional(ServerErrorCode::UnsupportediOSVersion); + } + else + { + localizedError = std::make_optional(resultCode, description); + } + } + } + + std::lock_guard lock(waitingMutex); + didFinishInstalling = true; + cv.notify_all(); + } + else + { + progressCompletionHandler(adjustedProgress); + } + + didBeginInstalling = true; + }; + + auto narrowDestinationPath = destinationPath.string(); + std::replace(narrowDestinationPath.begin(), narrowDestinationPath.end(), '\\', '/'); + + instproxy_install(ipc, narrowDestinationPath.c_str(), options, DeviceManagerUpdateStatus, uuidString); + instproxy_client_options_free(options); + + // Wait until we're finished installing; + std::unique_lock lock(waitingMutex); + cv.wait(lock, [&didFinishInstalling] { return didFinishInstalling; }); + + lock.unlock(); + + if (serverError.has_value()) + { + throw serverError.value(); + } + + if (localizedError.has_value()) + { + throw localizedError.value(); + } + } + catch (std::exception& exception) + { + try + { + // MUST finish so we restore provisioning profiles. + finish(device, client, ipc, afc, mis, service); + } + catch (std::exception& e) + { + // Ignore since we already caught an exception during installation. + } + + throw; + } + + // Call finish outside try-block so if an exception is thrown, we don't + // catch it ourselves and "finish" again. + finish(device, client, ipc, afc, mis, service); + }); +} + +void DeviceManager::WriteDirectory(afc_client_t client, std::string directoryPath, std::string destinationPath, std::function wroteFileCallback) +{ + std::replace(destinationPath.begin(), destinationPath.end(), '\\', '/'); + + afc_make_directory(client, destinationPath.c_str()); + + for (auto& file : fs::directory_iterator(directoryPath)) + { + auto filepath = file.path(); + + if (fs::is_directory(filepath)) + { + auto destinationDirectoryPath = fs::path(destinationPath).append(filepath.filename().string()); + this->WriteDirectory(client, filepath.string(), destinationDirectoryPath.string(), wroteFileCallback); + } + else + { + auto destinationFilepath = fs::path(destinationPath).append(filepath.filename().string()); + this->WriteFile(client, filepath.string(), destinationFilepath.string(), wroteFileCallback); + } + } +} + +void DeviceManager::WriteFile(afc_client_t client, std::string filepath, std::string destinationPath, std::function wroteFileCallback) +{ + std::replace(destinationPath.begin(), destinationPath.end(), '\\', '/'); + destinationPath = replace_all(destinationPath, "__colon__", ":"); + + odslog("Writing File: " << filepath.c_str() << " to: " << destinationPath.c_str()); + + auto data = readFile(filepath.c_str()); + + uint64_t af = 0; + if ((afc_file_open(client, destinationPath.c_str(), AFC_FOPEN_WRONLY, &af) != AFC_E_SUCCESS) || af == 0) + { + throw ServerError(ServerErrorCode::DeviceWriteFailed); + } + + uint32_t bytesWritten = 0; + + while (bytesWritten < data.size()) + { + uint32_t count = 0; + + if (afc_file_write(client, af, (const char *)data.data() + bytesWritten, (uint32_t)data.size() - bytesWritten, &count) != AFC_E_SUCCESS) + { + throw ServerError(ServerErrorCode::DeviceWriteFailed); + } + + bytesWritten += count; + } + + if (bytesWritten != data.size()) + { + throw ServerError(ServerErrorCode::DeviceWriteFailed); + } + + afc_file_close(client, af); + + wroteFileCallback(filepath); +} + +pplx::task DeviceManager::RemoveApp(std::string bundleIdentifier, std::string deviceUDID) +{ + return pplx::task([=] { + idevice_t device = NULL; + lockdownd_client_t client = NULL; + instproxy_client_t ipc = NULL; + lockdownd_service_descriptor_t service = NULL; + + auto cleanUp = [&]() { + if (service) { + lockdownd_service_descriptor_free(service); + } + + if (ipc) { + instproxy_client_free(ipc); + } + + if (client) { + lockdownd_client_free(client); + } + + if (device) { + idevice_free(device); + } + }; + + try + { + /* Find Device */ + if (idevice_new_all(&device, deviceUDID.c_str()) != IDEVICE_E_SUCCESS) + { + throw ServerError(ServerErrorCode::DeviceNotFound); + } + + /* Connect to Device */ + if (lockdownd_client_new_with_handshake(device, &client, "altserver") != LOCKDOWN_E_SUCCESS) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + /* Connect to Installation Proxy */ + if ((lockdownd_start_service(client, "com.apple.mobile.installation_proxy", &service) != LOCKDOWN_E_SUCCESS) || service == NULL) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + if (instproxy_client_new(device, service, &ipc) != INSTPROXY_E_SUCCESS) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + if (service) + { + lockdownd_service_descriptor_free(service); + service = NULL; + } + + auto UUID = make_uuid(); + + char* uuidString = (char*)malloc(UUID.size() + 1); + strncpy(uuidString, (const char*)UUID.c_str(), UUID.size()); + uuidString[UUID.size()] = '\0'; + + std::mutex waitingMutex; + std::condition_variable cv; + + std::optional serverError = std::nullopt; + + bool didFinishInstalling = false; + + this->_deletionCompletionHandlers[UUID] = [this, &waitingMutex, &cv, &didFinishInstalling, &serverError, &uuidString] + (bool success, int errorCode, char* errorName, char* errorDescription) { + if (!success) + { + std::map userInfo = { + { "NSLocalizedFailure", ServerError(ServerErrorCode::AppDeletionFailed).localizedDescription() }, + { "NSLocalizedFailureReason", errorDescription } + }; + serverError = std::make_optional(ServerErrorCode::AppDeletionFailed, userInfo); + } + + std::lock_guard lock(waitingMutex); + didFinishInstalling = true; + cv.notify_all(); + + free(uuidString); + }; + + instproxy_uninstall(ipc, bundleIdentifier.c_str(), NULL, DeviceManagerUpdateAppDeletionStatus, uuidString); + + // Wait until we're finished installing; + std::unique_lock lock(waitingMutex); + cv.wait(lock, [&didFinishInstalling] { return didFinishInstalling; }); + + lock.unlock(); + + if (serverError.has_value()) + { + throw serverError.value(); + } + + cleanUp(); + } + catch (std::exception& exception) { + cleanUp(); + throw; + } + }); +} + +pplx::task> DeviceManager::StartWiredConnection(std::shared_ptr altDevice) +{ + return pplx::create_task([=]() -> std::shared_ptr { + idevice_t device = NULL; + idevice_connection_t connection = NULL; + + /* Find Device */ + if (idevice_new_ignore_network(&device, altDevice->identifier().c_str()) != IDEVICE_E_SUCCESS) + { + throw ServerError(ServerErrorCode::DeviceNotFound); + } + + /* Connect to Listening Socket */ + if (idevice_connect(device, DEVICE_LISTENING_SOCKET, &connection) != IDEVICE_E_SUCCESS) + { + idevice_free(device); + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + // idevice_free(device); + + auto wiredConnection = std::make_shared(altDevice, connection); + return wiredConnection; + }); +} + +pplx::task DeviceManager::InstallProvisioningProfiles(std::vector> provisioningProfiles, std::string deviceUDID, std::optional> activeProfiles) +{ + return pplx::task([=] { + // Enforce only one installation at a time. + this->_mutex.lock(); + + idevice_t device = NULL; + lockdownd_client_t client = NULL; + afc_client_t afc = NULL; + misagent_client_t mis = NULL; + lockdownd_service_descriptor_t service = NULL; + + auto cleanUp = [&]() { + if (service) { + lockdownd_service_descriptor_free(service); + } + + if (mis) { + misagent_client_free(mis); + } + + if (afc) { + afc_client_free(afc); + } + + if (client) { + lockdownd_client_free(client); + } + + if (device) { + idevice_free(device); + } + + this->_mutex.unlock(); + }; + + try + { + /* Find Device */ + if (idevice_new_all(&device, deviceUDID.c_str()) != IDEVICE_E_SUCCESS) + { + throw ServerError(ServerErrorCode::DeviceNotFound); + } + + /* Connect to Device */ + if (lockdownd_client_new_with_handshake(device, &client, "altserver") != LOCKDOWN_E_SUCCESS) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + /* Connect to Misagent */ + if (lockdownd_start_service(client, "com.apple.misagent", &service) != LOCKDOWN_E_SUCCESS || service == NULL) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + if (activeProfiles.has_value()) + { + // Remove all non-active free provisioning profiles. + + auto excludedBundleIdentifiers = activeProfiles.value(); + for (auto& profile : provisioningProfiles) + { + // Ensure we DO remove old versions of profiles we're about to install, even if they are active. + excludedBundleIdentifiers.erase(profile->bundleIdentifier()); + } + + this->RemoveAllFreeProvisioningProfilesExcludingBundleIdentifiers(excludedBundleIdentifiers, mis); + } + else + { + // Remove only older versions of provisioning profiles we're about to install. + + std::set bundleIdentifiers; + for (auto& profile : provisioningProfiles) + { + bundleIdentifiers.insert(profile->bundleIdentifier()); + } + + this->RemoveProvisioningProfiles(bundleIdentifiers, mis); + } + + for (auto& provisioningProfile : provisioningProfiles) + { + this->InstallProvisioningProfile(provisioningProfile, mis); + } + + cleanUp(); + } + catch (std::exception &exception) + { + cleanUp(); + throw; + } + }); +} + +pplx::task DeviceManager::RemoveProvisioningProfiles(std::set bundleIdentifiers, std::string deviceUDID) +{ + return pplx::task([=] { + // Enforce only one removal at a time. + this->_mutex.lock(); + + idevice_t device = NULL; + lockdownd_client_t client = NULL; + afc_client_t afc = NULL; + misagent_client_t mis = NULL; + lockdownd_service_descriptor_t service = NULL; + + auto cleanUp = [&]() { + if (service) { + lockdownd_service_descriptor_free(service); + } + + if (mis) { + misagent_client_free(mis); + } + + if (afc) { + afc_client_free(afc); + } + + if (client) { + lockdownd_client_free(client); + } + + if (device) { + idevice_free(device); + } + + this->_mutex.unlock(); + }; + + try + { + /* Find Device */ + if (idevice_new_all(&device, deviceUDID.c_str()) != IDEVICE_E_SUCCESS) + { + throw ServerError(ServerErrorCode::DeviceNotFound); + } + + /* Connect to Device */ + if (lockdownd_client_new_with_handshake(device, &client, "altserver") != LOCKDOWN_E_SUCCESS) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + /* Connect to Misagent */ + if (lockdownd_start_service(client, "com.apple.misagent", &service) != LOCKDOWN_E_SUCCESS || service == NULL) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + if (misagent_client_new(device, service, &mis) != MISAGENT_E_SUCCESS) + { + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + this->RemoveProvisioningProfiles(bundleIdentifiers, mis); + + cleanUp(); + } + catch (std::exception& exception) + { + cleanUp(); + throw; + } + }); +} + +std::map> DeviceManager::RemoveProvisioningProfiles(std::set bundleIdentifiers, misagent_client_t mis) +{ + return this->RemoveAllProvisioningProfiles(bundleIdentifiers, std::nullopt, false, mis); +} + +std::map> DeviceManager::RemoveAllFreeProvisioningProfilesExcludingBundleIdentifiers(std::set excludedBundleIdentifiers, misagent_client_t mis) +{ + return this->RemoveAllProvisioningProfiles(std::nullopt, excludedBundleIdentifiers, true, mis); +} + +std::map> DeviceManager::RemoveAllProvisioningProfiles(std::optional> includedBundleIdentifiers, std::optional> excludedBundleIdentifiers, bool limitedToFreeProfiles, misagent_client_t mis) +{ + std::map> ignoredProfiles; + std::map> removedProfiles; + + auto provisioningProfiles = this->CopyProvisioningProfiles(mis); + + for (auto& provisioningProfile : provisioningProfiles) + { + if (limitedToFreeProfiles && !provisioningProfile->isFreeProvisioningProfile()) + { + continue; + } + + if (includedBundleIdentifiers.has_value() && includedBundleIdentifiers->count(provisioningProfile->bundleIdentifier()) == 0) + { + continue; + } + + if (excludedBundleIdentifiers.has_value() && excludedBundleIdentifiers->count(provisioningProfile->bundleIdentifier()) > 0) + { + // This provisioning profile has an excluded bundle identifier. + // Ignore it, unless we've already ignored one with the same bundle identifier, + // in which case remove whichever profile is the oldest. + + auto previousProfile = ignoredProfiles[provisioningProfile->bundleIdentifier()]; + if (previousProfile != NULL) + { + auto expirationDateA = provisioningProfile->expirationDate(); + auto expirationDateB = previousProfile->expirationDate(); + + // We've already ignored a profile with this bundle identifier, + // so make sure we only ignore the newest one and remove the oldest one. + BOOL newerThanPreviousProfile = (timercmp(&expirationDateA, &expirationDateB, >) != 0); + auto oldestProfile = newerThanPreviousProfile ? previousProfile : provisioningProfile; + auto newestProfile = newerThanPreviousProfile ? provisioningProfile : previousProfile; + + ignoredProfiles[provisioningProfile->bundleIdentifier()] = newestProfile; + + // Don't cache this profile or else it will be reinstalled, so just remove it without caching. + this->RemoveProvisioningProfile(oldestProfile, mis); + } + else + { + ignoredProfiles[provisioningProfile->bundleIdentifier()] = provisioningProfile; + } + + continue; + } + + auto preferredProfile = removedProfiles[provisioningProfile->bundleIdentifier()]; + if (preferredProfile != nullptr) + { + auto expirationDateA = provisioningProfile->expirationDate(); + auto expirationDateB = preferredProfile->expirationDate(); + + if (timercmp(&expirationDateA, &expirationDateB, > ) != 0) + { + // provisioningProfile exires later than preferredProfile, so use provisioningProfile instead. + removedProfiles[provisioningProfile->bundleIdentifier()] = provisioningProfile; + } + } + else + { + removedProfiles[provisioningProfile->bundleIdentifier()] = provisioningProfile; + } + + this->RemoveProvisioningProfile(provisioningProfile, mis); + } + + return removedProfiles; +} + +void DeviceManager::InstallProvisioningProfile(std::shared_ptr profile, misagent_client_t mis) +{ + plist_t pdata = plist_new_data((const char*)profile->data().data(), profile->data().size()); + + misagent_error_t result = misagent_install(mis, pdata); + plist_free(pdata); + + if (result == MISAGENT_E_SUCCESS) + { + odslog("Installed profile: " << (profile->bundleIdentifier()) << " (" << (profile->uuid()) << ")"); + } + else + { + int statusCode = misagent_get_status_code(mis); + odslog("Failed to install provisioning profile: " << (profile->bundleIdentifier()) << " (" << (profile->uuid()) << "). Error code: " << statusCode); + + switch (statusCode) + { + case -402620383: + { + std::map userInfo = { + { "NSLocalizedRecoverySuggestion", "Make sure 'Offload Unused Apps' is disabled in Settings > iTunes & App Stores, then install or delete all offloaded apps." } + }; + throw ServerError(ServerErrorCode::MaximumFreeAppLimitReached, userInfo); + } + + default: + { + std::ostringstream oss; + oss << "Could not install profile '" << profile->bundleIdentifier() << "'"; + + std::string localizedFailure = oss.str(); + + std::map userInfo = { + { LocalizedFailureErrorKey, localizedFailure }, + { ProvisioningProfileBundleIDErrorKey, profile->bundleIdentifier() }, + { UnderlyingErrorCodeErrorKey, std::to_string(statusCode) } + }; + + throw ServerError(ServerErrorCode::UnderlyingError, userInfo); + } + } + } +} + +void DeviceManager::RemoveProvisioningProfile(std::shared_ptr profile, misagent_client_t mis) +{ + std::string uuid = profile->uuid(); + std::transform(uuid.begin(), uuid.end(), uuid.begin(), [](unsigned char c) { return std::tolower(c); }); + + misagent_error_t result = misagent_remove(mis, uuid.c_str()); + if (result == MISAGENT_E_SUCCESS) + { + odslog("Removed profile: " << (profile->bundleIdentifier()) << " (" << (profile->uuid()) << ")"); + } + else + { + int statusCode = misagent_get_status_code(mis); + odslog("Failed to remove provisioning profile: " << (profile->bundleIdentifier()) << " (" << (profile->uuid()) << "). Error code: " << statusCode); + + switch (statusCode) + { + case -402620405: + { + std::map userInfo = { + { ProvisioningProfileBundleIDErrorKey, profile->bundleIdentifier() }, + }; + + throw ServerError(ServerErrorCode::ProfileNotFound, userInfo); + } + + default: + { + std::ostringstream oss; + oss << "Could not remove profile '" << profile->bundleIdentifier() << "'"; + + std::string localizedFailure = oss.str(); + + std::map userInfo = { + { LocalizedFailureErrorKey, localizedFailure }, + { ProvisioningProfileBundleIDErrorKey, profile->bundleIdentifier() }, + { UnderlyingErrorCodeErrorKey, std::to_string(statusCode) } + }; + + throw ServerError(ServerErrorCode::UnderlyingError, userInfo); + } + } + } +} + +std::vector> DeviceManager::CopyProvisioningProfiles(misagent_client_t mis) +{ + std::vector> provisioningProfiles; + + plist_t profiles = NULL; + + if (misagent_copy_all(mis, &profiles) != MISAGENT_E_SUCCESS) + { + int statusCode = misagent_get_status_code(mis); + + std::string localizedFailure = "Could not copy provisioning profiles."; + + std::map userInfo = { + { LocalizedFailureErrorKey, localizedFailure }, + { UnderlyingErrorCodeErrorKey, std::to_string(statusCode) } + }; + + throw ServerError(ServerErrorCode::UnderlyingError, userInfo); + } + + uint32_t profileCount = plist_array_get_size(profiles); + for (int i = 0; i < profileCount; i++) + { + plist_t profile = plist_array_get_item(profiles, i); + if (plist_get_node_type(profile) != PLIST_DATA) + { + continue; + } + + char* bytes = NULL; + uint64_t length = 0; + + plist_get_data_val(profile, &bytes, &length); + if (bytes == NULL) + { + continue; + } + + std::vector data; + data.reserve(length); + + for (int i = 0; i < length; i++) + { + data.push_back(bytes[i]); + } + + auto provisioningProfile = std::make_shared(data); + provisioningProfiles.push_back(provisioningProfile); + } + + plist_free(profiles); + + return provisioningProfiles; +} + +pplx::task> DeviceManager::StartNotificationConnection(std::shared_ptr altDevice) +{ + return pplx::create_task([=]() -> std::shared_ptr { + idevice_t device = NULL; + lockdownd_client_t lockdownClient = NULL; + lockdownd_service_descriptor_t service = NULL; + np_client_t client = NULL; + + /* Find Device */ + if (idevice_new_ignore_network(&device, altDevice->identifier().c_str()) != IDEVICE_E_SUCCESS) + { + throw ServerError(ServerErrorCode::DeviceNotFound); + } + + /* Connect to Device */ + if (lockdownd_client_new_with_handshake(device, &lockdownClient, "altserver") != LOCKDOWN_E_SUCCESS) + { + idevice_free(device); + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + /* Connect to Notification Proxy */ + if ((lockdownd_start_service(lockdownClient, "com.apple.mobile.notification_proxy", &service) != LOCKDOWN_E_SUCCESS) || service == NULL) + { + lockdownd_client_free(lockdownClient); + idevice_free(device); + + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + /* Connect to Client */ + if (np_client_new(device, service, &client) != NP_E_SUCCESS) + { + lockdownd_service_descriptor_free(service); + lockdownd_client_free(lockdownClient); + idevice_free(device); + + throw ServerError(ServerErrorCode::ConnectionFailed); + } + + lockdownd_service_descriptor_free(service); + lockdownd_client_free(lockdownClient); + idevice_free(device); + + auto notificationConnection = std::make_shared(altDevice, client); + return notificationConnection; + }); +} + +std::vector> DeviceManager::connectedDevices() const +{ + auto devices = this->availableDevices(false); + return devices; +} + +std::vector> DeviceManager::availableDevices() const +{ + auto devices = this->availableDevices(true); + return devices; +} + +std::vector> DeviceManager::availableDevices(bool includeNetworkDevices) const +{ + std::vector> availableDevices; + + int count = 0; + char **udids = NULL; + if (idevice_get_device_list(&udids, &count) < 0) + { + fprintf(stderr, "ERROR: Unable to retrieve device list!\n"); + return availableDevices; + } + + for (int i = 0; i < count; i++) + { + char *udid = udids[i]; + + idevice_t device = NULL; + + if (includeNetworkDevices) + { + idevice_new_all(&device, udid); + } + else + { + idevice_new_ignore_network(&device, udid); + } + + if (!device) + { + continue; + } + + lockdownd_client_t client = NULL; + int result = lockdownd_client_new(device, &client, "altserver"); + if (result != LOCKDOWN_E_SUCCESS) + { + fprintf(stderr, "ERROR: Connecting to device %s failed! (%d)\n", udid, result); + + idevice_free(device); + + continue; + } + + char *device_name = NULL; + if (lockdownd_get_device_name(client, &device_name) != LOCKDOWN_E_SUCCESS || device_name == NULL) + { + fprintf(stderr, "ERROR: Could not get device name!\n"); + + lockdownd_client_free(client); + idevice_free(device); + + continue; + } + + plist_t device_type_plist = NULL; + if (lockdownd_get_value(client, NULL, "ProductType", &device_type_plist) != LOCKDOWN_E_SUCCESS) + { + odslog("ERROR: Could not get device type for " << device_name); + + lockdownd_client_free(client); + idevice_free(device); + + continue; + } + + Device::Type deviceType = Device::Type::iPhone; + + char* device_type_string = NULL; + plist_get_string_val(device_type_plist, &device_type_string); + + if (std::string(device_type_string).find("iPhone") != std::string::npos || + std::string(device_type_string).find("iPod") != std::string::npos) + { + deviceType = Device::Type::iPhone; + } + else if (std::string(device_type_string).find("iPad") != std::string::npos) + { + deviceType = Device::Type::iPad; + } + else if (std::string(device_type_string).find("AppleTV") != std::string::npos) + { + deviceType = Device::Type::AppleTV; + } + else + { + odslog("Unknown device type " << device_type_string << " for " << device_name); + deviceType = Device::Type::None; + } + + lockdownd_client_free(client); + idevice_free(device); + + bool isDuplicate = false; + + for (auto& device : availableDevices) + { + if (device->identifier() == udid) + { + // Duplicate. + isDuplicate = true; + break; + } + } + + if (isDuplicate) + { + continue; + } + + auto altDevice = std::make_shared(device_name, udid, deviceType); + availableDevices.push_back(altDevice); + + if (device_name != NULL) + { + free(device_name); + } + } + + idevice_device_list_free(udids); + + return availableDevices; +} + +std::function)> DeviceManager::connectedDeviceCallback() const +{ + return _connectedDeviceCallback; +} + +void DeviceManager::setConnectedDeviceCallback(std::function)> callback) +{ + _connectedDeviceCallback = callback; +} + +std::function)> DeviceManager::disconnectedDeviceCallback() const +{ + return _disconnectedDeviceCallback; +} + +void DeviceManager::setDisconnectedDeviceCallback(std::function)> callback) +{ + _disconnectedDeviceCallback = callback; +} + +std::map>& DeviceManager::cachedDevices() +{ + return _cachedDevices; +} + +#pragma mark - Callbacks - + +void DeviceManagerUpdateStatus(plist_t command, plist_t status, void *uuid) +{ + if (DeviceManager::instance()->_installationProgressHandlers.count((char*)uuid) == 0) + { + return; + } + + int percent = 0; + instproxy_status_get_percent_complete(status, &percent); + + char* name = NULL; + char* description = NULL; + uint64_t code = 0; + instproxy_status_get_error(status, &name, &description, &code); + + double progress = ((double)percent / 100.0); + + auto progressHandler = DeviceManager::instance()->_installationProgressHandlers[(char*)uuid]; + progressHandler(progress, code, name, description); +} + +void DeviceManagerUpdateAppDeletionStatus(plist_t command, plist_t status, void* uuid) +{ + char *statusName = NULL; + instproxy_status_get_name(status, &statusName); + + char* errorName = NULL; + char* errorDescription = NULL; + uint64_t errorCode = 0; + instproxy_status_get_error(status, &errorName, &errorDescription, &errorCode); + + if (std::string(statusName) == std::string("Complete") || errorCode != 0 || errorName != NULL) + { + auto completionHandler = DeviceManager::instance()->_deletionCompletionHandlers[(char*)uuid]; + if (completionHandler != NULL) + { + if (errorName == NULL) + { + errorName = (char*)""; + } + + if (errorDescription == NULL) + { + errorDescription = (char*)""; + } + + if (errorCode != 0 || std::string(errorName) != std::string()) + { + odslog("Error removing app. " << errorCode << " (" << errorName << "). " << errorDescription); + completionHandler(false, errorCode, errorName, errorDescription); + } + else + { + odslog("Finished removing app!"); + completionHandler(true, 0, errorName, errorDescription); + } + + DeviceManager::instance()->_deletionCompletionHandlers.erase((char*)uuid); + } + } +} + +void DeviceDidChangeConnectionStatus(const idevice_event_t* event, void* user_data) +{ + switch (event->event) + { + case IDEVICE_DEVICE_ADD: + { + auto devices = DeviceManager::instance()->connectedDevices(); + std::shared_ptr device = NULL; + + for (auto& d : devices) + { + if (d->identifier() == event->udid) + { + device = d; + break; + } + } + + if (device == NULL) + { + return; + } + + if (DeviceManager::instance()->cachedDevices().count(device->identifier()) > 0) + { + return; + } + + odslog("Detected device:" << device->name().c_str()); + + DeviceManager::instance()->cachedDevices()[device->identifier()] = device; + + if (DeviceManager::instance()->connectedDeviceCallback() != NULL) + { + DeviceManager::instance()->connectedDeviceCallback()(device); + } + + break; + } + case IDEVICE_DEVICE_REMOVE: + { + auto devices = DeviceManager::instance()->cachedDevices(); + std::shared_ptr device = DeviceManager::instance()->cachedDevices()[event->udid]; + + if (device == NULL) + { + return; + } + + DeviceManager::instance()->cachedDevices().erase(device->identifier()); + + if (DeviceManager::instance()->disconnectedDeviceCallback() != NULL) + { + DeviceManager::instance()->disconnectedDeviceCallback()(device); + } + + break; + } + default: + break; + } +} diff --git a/src/DeviceManager.hpp b/src/DeviceManager.hpp new file mode 100644 index 0000000..ac27da7 --- /dev/null +++ b/src/DeviceManager.hpp @@ -0,0 +1,89 @@ +// +// DeviceManager.hpp +// AltServer-Windows +// +// Created by Riley Testut on 8/13/19. +// Copyright © 2019 Riley Testut. All rights reserved. +// + +#ifndef DeviceManager_hpp +#define DeviceManager_hpp + +#include "common.h" +#include "Device.hpp" +#include "ProvisioningProfile.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +#include "WiredConnection.h" +#include "NotificationConnection.h" + +class DeviceManager +{ +public: + static DeviceManager *instance(); + + DeviceManager(); + + std::vector> connectedDevices() const; + std::vector> availableDevices() const; + + void Start(); + + pplx::task InstallApp(std::string filepath, std::string deviceUDID, std::optional> activeProvisioningProfiles, std::function progressCompletionHandler); + pplx::task RemoveApp(std::string bundleIdentifier, std::string deviceUDID); + + pplx::task> StartWiredConnection(std::shared_ptr device); + pplx::task> StartNotificationConnection(std::shared_ptr device); + + pplx::task InstallProvisioningProfiles(std::vector> profiles, std::string deviceUDID, std::optional> activeProfiles); + pplx::task RemoveProvisioningProfiles(std::set bundleIdentifiers, std::string deviceUDID); + + std::map> RemoveProvisioningProfiles(std::set bundleIdentifiers, misagent_client_t misagent); + std::map> RemoveAllFreeProvisioningProfilesExcludingBundleIdentifiers(std::set excludedBundleIdentifiers, misagent_client_t misagent); + std::map> RemoveAllProvisioningProfiles(std::optional> includedBundleIdentifiers, std::optional> excludedBundleIdentifiers, bool limitedToFreeProfiles, misagent_client_t misagent); + + std::function)> connectedDeviceCallback() const; + void setConnectedDeviceCallback(std::function)> callback); + + std::function)> disconnectedDeviceCallback() const; + void setDisconnectedDeviceCallback(std::function)> callback); + +private: + ~DeviceManager(); + + static DeviceManager *_instance; + + std::mutex _mutex; + + std::map> _installationProgressHandlers; + std::map> _deletionCompletionHandlers; + + std::function)> _connectedDeviceCallback; + std::function)> _disconnectedDeviceCallback; + + std::map> _cachedDevices; + std::map>& cachedDevices(); + + std::vector> availableDevices(bool includeNetworkDevices) const; + + void WriteDirectory(afc_client_t client, std::string directoryPath, std::string destinationPath, std::function wroteFileCallback); + void WriteFile(afc_client_t client, std::string filepath, std::string destinationPath, std::function wroteFileCallback); + + void InstallProvisioningProfile(std::shared_ptr provisioningProfile, misagent_client_t mis); + void RemoveProvisioningProfile(std::shared_ptr provisioningProfile, misagent_client_t mis); + std::vector> CopyProvisioningProfiles(misagent_client_t mis); + + friend void DeviceManagerUpdateStatus(plist_t command, plist_t status, void* uuid); + friend void DeviceManagerUpdateAppDeletionStatus(plist_t command, plist_t status, void* udid); + friend void DeviceDidChangeConnectionStatus(const idevice_event_t* event, void* user_data); +}; + +#endif /* DeviceManager_hpp */ diff --git a/src/InstallError.hpp b/src/InstallError.hpp new file mode 100644 index 0000000..0e3df69 --- /dev/null +++ b/src/InstallError.hpp @@ -0,0 +1,58 @@ +// +// InstallError.h +// AltServer-Windows +// +// Created by Riley Testut on 8/31/19. +// Copyright © 2019 Riley Testut. All rights reserved. +// + +#ifndef InstallError_h +#define InstallError_h + +#include "Error.hpp" + +enum class InstallErrorCode +{ + Cancelled, + NoTeam, + MissingPrivateKey, + MissingCertificate, + MissingInfoPlist, +}; + +class InstallError: public Error +{ +public: + InstallError(InstallErrorCode code) : Error((int)code) + { + } + + virtual std::string domain() const + { + return "com.rileytestut.AltServer.InstallError"; + } + + virtual std::string localizedDescription() const + { + switch ((InstallErrorCode)this->code()) + { + case InstallErrorCode::Cancelled: + return "The operation was cancelled."; + + case InstallErrorCode::NoTeam: + return "You are not a member of any developer teams."; + + case InstallErrorCode::MissingPrivateKey: + return "The developer certificate's private key could not be found."; + + case InstallErrorCode::MissingCertificate: + return "The developer certificate could not be found."; + + case InstallErrorCode::MissingInfoPlist: + return "The app's Info.plist could not be found."; + } + } +}; + + +#endif /* InstallError_h */ diff --git a/src/NotificationConnection.cpp b/src/NotificationConnection.cpp new file mode 100644 index 0000000..42b4b64 --- /dev/null +++ b/src/NotificationConnection.cpp @@ -0,0 +1,102 @@ +#include "common.h" +#include "NotificationConnection.h" +#include "ServerError.hpp" + +#include + +void ALTDeviceReceivedNotification(const char* notification, void* user_data) +{ + NotificationConnection* connection = (NotificationConnection*)user_data; + + if (std::string(notification).size() > 0 && connection->active()) + { + connection->receivedNotificationHandler()(notification); + } +} + +NotificationConnection::NotificationConnection(std::shared_ptr device, np_client_t client) : _device(device), _client(client) +{ + this->setReceivedNotificationHandler([](std::string notification) { + odslog("Received Notification: " << notification); + }); +} + +NotificationConnection::~NotificationConnection() +{ + this->Disconnect(); +} + +void NotificationConnection::Disconnect() +{ + if (_client == NULL) + { + return; + } + + np_client_free(_client); + _client = NULL; + + // Prevent us from receiving callbacks after deallocation. + _active = false; +} + +void NotificationConnection::StartListening(std::vector notifications) +{ + const char** notificationNames = (const char**)malloc((notifications.size() + 1) * sizeof(char *)); + for (int i = 0; i < notifications.size(); i++) + { + const char* cName = notifications[i].c_str(); + notificationNames[i] = cName; + } + notificationNames[notifications.size()] = NULL; // Must have terminating NULL entry. + + np_error_t result = np_observe_notifications(this->client(), notificationNames); + if (result != NP_E_SUCCESS) + { + throw ServerError(ServerErrorCode::LostConnection); + } + + result = np_set_notify_callback(this->client(), ALTDeviceReceivedNotification, this); + if (result != NP_E_SUCCESS) + { + throw ServerError(ServerErrorCode::LostConnection); + } + + _active = true; + + free(notificationNames); +} + +void NotificationConnection::SendNotification(std::string notification) +{ + np_error_t result = np_post_notification(this->client(), notification.c_str()); + if (result != NP_E_SUCCESS) + { + throw ServerError(ServerErrorCode::LostConnection); + } +} + +std::shared_ptr NotificationConnection::device() const +{ + return _device; +} + +np_client_t NotificationConnection::client() const +{ + return _client; +} + +std::function NotificationConnection::receivedNotificationHandler() const +{ + return _receivedNotificationHandler; +} + +void NotificationConnection::setReceivedNotificationHandler(std::function handler) +{ + _receivedNotificationHandler = handler; +} + +bool NotificationConnection::active() const +{ + return _active; +} \ No newline at end of file diff --git a/src/NotificationConnection.h b/src/NotificationConnection.h new file mode 100644 index 0000000..5d1346d --- /dev/null +++ b/src/NotificationConnection.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Device.hpp" + +#include +#include +#include + +#include + +class NotificationConnection +{ +public: + NotificationConnection(std::shared_ptr device, np_client_t client); + ~NotificationConnection(); + + void Disconnect(); + + void StartListening(std::vector notifications); + void SendNotification(std::string notification); + + std::function receivedNotificationHandler() const; + void setReceivedNotificationHandler(std::function handler); + + std::shared_ptr device() const; + + bool active() const; + +private: + std::shared_ptr _device; + np_client_t _client; + std::function _receivedNotificationHandler; + + bool _active; + + np_client_t client() const; +}; + diff --git a/src/PhoneHelper.c b/src/PhoneHelper.c new file mode 100644 index 0000000..610f4c5 --- /dev/null +++ b/src/PhoneHelper.c @@ -0,0 +1,170 @@ +#include "PhoneHelper.h" +#include "phone/global.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/userpref.h" +#include "common/utils.h" + +#ifndef TOOL_NAME +#define TOOL_NAME "AltServerLinux" +#endif +#include "common.h" + +#include "miniwget.h" +#include "miniupnpc.h" +#include "upnpcommands.h" +#include "portlistingparse.h" +#include "upnperrors.h" +struct UPNPUrls _upnpUrls = { 0 }; +struct IGDdatas _upnpData = { 0 }; +char upnpExternalAddr[40] = { 0 }; + +int initUPnP() { + int error = 0; + char lanaddr[64] = "unset"; + int upnpRet = 0; + if (1) { + struct UPNPDev *devlist = upnpDiscover(2000, NULL, "", 0, 0, 2, &error); + upnpRet = UPNP_GetValidIGD(devlist, &_upnpUrls, &_upnpData, lanaddr, sizeof(lanaddr)); + } else { + //upnpRet = 1; UPNP_GetIGDFromUrl("http://192.168.1.1:1900/igd.xml", &_upnpUrls, &_upnpData, lanaddr, sizeof(lanaddr)); + } + upnpUrls = &_upnpUrls; + upnpData = &_upnpData; + if (upnpRet == 1) { + DEBUG_PRINT("Got good upnp igd: %s", _upnpUrls.controlURL); + } else if (upnpRet == 2) { + DEBUG_PRINT("Got not-connected igd: %s", _upnpUrls.controlURL); + } else { + DEBUG_PRINT("Found unknown upnp dev: %s", _upnpUrls.controlURL); + } + if (upnpRet != 1) { + return 0; + } + + char externalIPAddress[40] = { 0 }; + int r = UPNP_GetExternalIPAddress(upnpUrls->controlURL, + upnpData->first.servicetype, + externalIPAddress); + if(r != UPNPCOMMAND_SUCCESS) { + DEBUG_PRINT("GetExternalIPAddress failed. (errorcode=%d)\n", r); + return 0; + } + DEBUG_PRINT("Got ExternalIPAddress = %s\n", externalIPAddress); + strcpy(upnpExternalAddr, externalIPAddress); + return 1; +} + +int initGlobalDevice() { + idevice_error_t derr = IDEVICE_E_SUCCESS; + if ((derr = idevice_new_with_options(&g_device, pairUDID, IDEVICE_LOOKUP_NETWORK)) != IDEVICE_E_SUCCESS) { + DEBUG_PRINT("Failed to create device: %d", derr); + return 0; + } + return 1; +} + + +int do_heartbeat(void *_client) { + heartbeat_client_t client = _client; + plist_t ping; + uint64_t interval = 15; + //DEBUG_PRINT("Timer run!"); + if (heartbeat_receive_with_timeout(client, &ping, (uint32_t)interval * 1000) != HEARTBEAT_E_SUCCESS) { + DEBUG_PRINT("Did not recieve ping, canceling timer!"); + return 0; + } + plist_get_uint_val(plist_dict_get_item(ping, "Interval"), &interval); + //DEBUG_PRINT("Set new timer interval: %lu!", interval); + //DEBUG_PRINT("Sending heartbeat."); + heartbeat_send(client, ping); + plist_free(ping); + return (interval); +} + + +int initHeartbeat(void **_hbclient) { + //idevice_set_debug_level(1); + + lockdownd_error_t lerr = LOCKDOWN_E_SUCCESS; + DEBUG_PRINT("Start hb..."); + heartbeat_client_t hbclient; + heartbeat_error_t err = HEARTBEAT_E_UNKNOWN_ERROR; + int herr = heartbeat_client_start_service(g_device, &hbclient, TOOL_NAME); + if (herr != HEARTBEAT_E_SUCCESS) { + DEBUG_PRINT("Failed to create heartbeat service: %d", herr); + return 0; + } + *_hbclient = hbclient; + //idevice_set_debug_level(0); + return 1; +} + + +static void heartbeat_thread(heartbeat_client_t client) { + while (1) { + int interval = do_heartbeat(client); + if (!interval) break; + sleep(interval); + } +} +int startHeartbeat(void *hbclient) { + pthread_t thHeartbeat; + //pthread_create(&thHeartbeat, NULL, (void * (*)(void *))heartbeat_thread, hbclient); + int ret = fork(); + if (ret == 0) { + heartbeat_thread(hbclient); + } +} + +void setupPairInfo(const char *udid, const char *ipaddr, const char *pairDataFile) { + DEBUG_PRINT("Setup pairInfo..."); + strcpy(pairUDID, udid); + strcpy(pairDeviceAddress, ipaddr); + FILE *f = fopen(pairDataFile, "rb"); + fseek(f, 0, SEEK_END); + pairDataLen = ftell(f); + fseek(f, 0, SEEK_SET); + fread(pairData, 1, pairDataLen, f); +} + +// int main(int argc, char *argv[]) { +// setvbuf(stdin, NULL, _IONBF, 0); +// setvbuf(stdout, NULL, _IONBF, 0); +// setvbuf(stderr, NULL, _IONBF, 0); + +// srand(time(NULL)); +// DEBUG_PRINT("Setup pairInfo..."); +// strcpy(pairUDID, argv[1]); +// strcpy(pairDeviceAddress, argv[2]); +// FILE *f = fopen(argv[3], "rb"); +// fseek(f, 0, SEEK_END); +// pairDataLen = ftell(f); +// fseek(f, 0, SEEK_SET); +// fread(pairData, 1, pairDataLen, f); + +// if (!initUPnP()) { +// DEBUG_PRINT("failed to init upnp! exitting..."); +// return 1; +// } +// DEBUG_PRINT("upnp init successfully!"); + +// DEBUG_PRINT("Connect device..."); +// if (!initHeartbeat()) { +// DEBUG_PRINT("failed to init heartbeat! exitting..."); +// return 1; +// } + +// // DEBUG_PRINT("Start tool..."); +// // tool_main(argc - 3, argv + 3); +// } diff --git a/src/PhoneHelper.h b/src/PhoneHelper.h new file mode 100644 index 0000000..683b1f2 --- /dev/null +++ b/src/PhoneHelper.h @@ -0,0 +1,11 @@ +#ifdef __cplusplus +extern "C" { +#endif + int initGlobalDevice(); + int initHeartbeat(void **hbclient); + int do_heartbeat(void *_client); + int initUPnP(); + void setupPairInfo(const char *udid, const char *ipaddr, const char *pairDataFile); +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/Semaphore.h b/src/Semaphore.h new file mode 100644 index 0000000..9f6b7da --- /dev/null +++ b/src/Semaphore.h @@ -0,0 +1,40 @@ +// +// Semaphore.hpp +// AltServer-Windows +// +// Created by Riley Testut on 10/7/20. +// Copied from https://stackoverflow.com/a/19659736 +// + +#pragma once + +#include +#include + +class Semaphore { +public: + Semaphore(int count_ = 0) + : count(count_) {} + + inline void notify() + { + std::unique_lock lock(mtx); + count++; + cv.notify_one(); + } + + inline void wait() + { + std::unique_lock lock(mtx); + + while (count <= 0) { + cv.wait(lock); + } + count--; + } + +private: + std::mutex mtx; + std::condition_variable cv; + int count; +}; \ No newline at end of file diff --git a/src/ServerError.cpp b/src/ServerError.cpp new file mode 100644 index 0000000..6d1a127 --- /dev/null +++ b/src/ServerError.cpp @@ -0,0 +1,5 @@ +#include "ServerError.hpp" + +std::string LocalizedFailureErrorKey = "NSLocalizedFailure"; +std::string UnderlyingErrorCodeErrorKey = "underlyingErrorCode"; +std::string ProvisioningProfileBundleIDErrorKey = "bundleIdentifier"; \ No newline at end of file diff --git a/src/ServerError.hpp b/src/ServerError.hpp new file mode 100644 index 0000000..a154d88 --- /dev/null +++ b/src/ServerError.hpp @@ -0,0 +1,123 @@ +// +// ServerError.hpp +// AltServer-Windows +// +// Created by Riley Testut on 8/13/19. +// Copyright © 2019 Riley Testut. All rights reserved. +// + +#ifndef ServerError_hpp +#define ServerError_hpp + +#include "Error.hpp" + +extern std::string LocalizedFailureErrorKey; +extern std::string UnderlyingErrorCodeErrorKey; +extern std::string ProvisioningProfileBundleIDErrorKey; + +enum class ServerErrorCode +{ + UnderlyingError = -1, + + Unknown = 0, + ConnectionFailed = 1, + LostConnection = 2, + + DeviceNotFound = 3, + DeviceWriteFailed = 4, + + InvalidRequest = 5, + InvalidResponse = 6, + + InvalidApp = 7, + InstallationFailed = 8, + MaximumFreeAppLimitReached = 9, + UnsupportediOSVersion = 10, + + UnknownRequest = 11, + UnknownResponse = 12, + + InvalidAnisetteData = 13, + PluginNotFound = 14, + + ProfileNotFound = 15, + + AppDeletionFailed = 16 +}; + +class ServerError: public Error +{ +public: + ServerError(ServerErrorCode code) : Error((int)code) + { + } + + ServerError(ServerErrorCode code, std::map userInfo) : Error((int)code, userInfo) + { + } + + virtual std::string domain() const + { + return "com.rileytestut.AltServer"; + } + + virtual std::string localizedDescription() const + { + switch ((ServerErrorCode)this->code()) + { + case ServerErrorCode::UnderlyingError: + case ServerErrorCode::Unknown: + return "An unknown error occured."; + + case ServerErrorCode::ConnectionFailed: + return "Could not connect to device."; + + case ServerErrorCode::LostConnection: + return "Lost connection to AltServer."; + + case ServerErrorCode::DeviceNotFound: + return "AltServer could not find the device."; + + case ServerErrorCode::DeviceWriteFailed: + return "Failed to write app data to device."; + + case ServerErrorCode::InvalidRequest: + return "AltServer received an invalid request."; + + case ServerErrorCode::InvalidResponse: + return "AltServer sent an invalid response."; + + case ServerErrorCode::InvalidApp: + return "The app is invalid."; + + case ServerErrorCode::InstallationFailed: + return "An error occured while installing the app."; + + case ServerErrorCode::MaximumFreeAppLimitReached: + return "You have reached the limit of 3 apps per device.\n\nIf you're running iOS 13.5 or later, make sure 'Offload Unused Apps' is disabled in Settings > iTunes & App Stores, then install or delete all offloaded apps to prevent them from erroneously counting towards this limit."; + + case ServerErrorCode::UnsupportediOSVersion: + return "Your device must be running iOS 12.2 or later to install AltStore."; + + case ServerErrorCode::UnknownRequest: + return "AltServer does not support this request."; + + case ServerErrorCode::UnknownResponse: + return "Received an unknown response from AltServer."; + + case ServerErrorCode::InvalidAnisetteData: + return "Invalid anisette data. Please download the latest versions of iTunes and iCloud directly from Apple, and not from the Microsoft Store."; + + case ServerErrorCode::PluginNotFound: + return "Could not connect to Mail plug-in. Please make sure the plug-in is installed and Mail is running, then try again."; + + case ServerErrorCode::ProfileNotFound: + return "Could not find provisioning profile."; + + case ServerErrorCode::AppDeletionFailed: + return "An error occured while removing the app."; + } + } +}; + +#endif /* ServerError_hpp */ diff --git a/src/WiredConnection.h b/src/WiredConnection.h new file mode 100644 index 0000000..57ddcca --- /dev/null +++ b/src/WiredConnection.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Device.hpp" +#include "ClientConnection.h" + +#include +#include + +#include + +class WiredConnection: public ClientConnection +{ +public: + WiredConnection(std::shared_ptr device, idevice_connection_t connection); + virtual ~WiredConnection(); + + virtual void Disconnect(); + + virtual pplx::task SendData(std::vector& data); + virtual pplx::task> ReceiveData(int expectedSize); + + std::shared_ptr device() const; + +private: + std::shared_ptr _device; + idevice_connection_t _connection; + + idevice_connection_t connection() const; +}; + diff --git a/src/WirelessConnection.cpp b/src/WirelessConnection.cpp new file mode 100644 index 0000000..deb61dc --- /dev/null +++ b/src/WirelessConnection.cpp @@ -0,0 +1,137 @@ +#include "WirelessConnection.h" +#include +using std::min; +#include +#include + +#include "ServerError.hpp" + +#if SIZE_MAX == UINT_MAX +typedef int ssize_t; /* common 32 bit case */ +#elif SIZE_MAX == ULONG_MAX +typedef long ssize_t; /* linux 64 bits */ +#elif SIZE_MAX == ULLONG_MAX +typedef long long ssize_t; /* windows 64 bits */ +#elif SIZE_MAX == USHRT_MAX +typedef short ssize_t; /* is this even possible? */ +#else +#error platform has exotic SIZE_MAX +#endif + +WirelessConnection::WirelessConnection(int socket) : _socket(socket) +{ +} + +WirelessConnection::~WirelessConnection() +{ +} + +void WirelessConnection::Disconnect() +{ + if (this->socket() == 0) + { + return; + } + + closesocket(this->socket()); + _socket = 0; +} + +pplx::task WirelessConnection::SendData(std::vector& data) +{ + return pplx::create_task([this, data]() { + fd_set input_set; + fd_set copy_set; + + int64_t totalSentBytes = 0; + + while (true) + { + struct timeval tv; + tv.tv_sec = 1; /* 1 second timeout */ + tv.tv_usec = 0; /* no microseconds. */ + + /* Selection */ + FD_ZERO(&input_set); /* Empty the FD Set */ + FD_SET(this->socket(), &input_set); /* Listen to the input descriptor */ + + FD_ZERO(©_set); /* Empty the FD Set */ + FD_SET(this->socket(), ©_set); /* Listen to the input descriptor */ + + ssize_t sentBytes = send(this->socket(), (const char*)data.data(), (size_t)(data.size() - totalSentBytes), 0); + totalSentBytes += sentBytes; + + std::cout << "Sent Bytes Count: " << sentBytes << " (" << totalSentBytes << ")" << std::endl; + + if (totalSentBytes >= sentBytes) + { + break; + } + } + + std::cout << "Sent Data: " << totalSentBytes << " Bytes" << std::endl; + }); +} + +pplx::task> WirelessConnection::ReceiveData(int size) +{ + return pplx::create_task([this, size]() { + std::vector data; + data.reserve(size); + + char buffer[4096]; + + fd_set input_set; + fd_set copy_set; + + while (true) + { + struct timeval tv; + tv.tv_sec = 1; /* 1 second timeout */ + tv.tv_usec = 0; /* no microseconds. */ + + int socket = this->socket(); + std::cout << "Checking socket: " << socket << std::endl; + + /* Selection */ + FD_ZERO(&input_set); /* Empty the FD Set */ + FD_SET(socket, &input_set); /* Listen to the input descriptor */ + + FD_ZERO(©_set); /* Empty the FD Set */ + FD_SET(socket, ©_set); /* Listen to the input descriptor */ + + int result = select(this->socket() + 1, &input_set, ©_set, NULL, &tv); + + if (result == 0) + { + continue; + } + else if (result == -1) + { + std::cout << "Error!" << std::endl; + } + else + { + ssize_t readBytes = recv(this->socket(), buffer, min((ssize_t)4096, (ssize_t)(size - data.size())), 0); + for (int i = 0; i < readBytes; i++) + { + data.push_back(buffer[i]); + } + + odslog("Received bytes: " << data.size() << "(of " << size << ")"); + + if (data.size() >= size) + { + break; + } + } + } + + return data; + }); +} + +int WirelessConnection::socket() const +{ + return _socket; +} \ No newline at end of file diff --git a/src/WirelessConnection.h b/src/WirelessConnection.h new file mode 100644 index 0000000..3b3f470 --- /dev/null +++ b/src/WirelessConnection.h @@ -0,0 +1,21 @@ +#pragma once + +#include "ClientConnection.h" + +class WirelessConnection: public ClientConnection +{ +public: + WirelessConnection(int socket); + virtual ~WirelessConnection(); + + virtual void Disconnect(); + + virtual pplx::task SendData(std::vector& data); + virtual pplx::task> ReceiveData(int size); + + int socket() const; + +private: + int _socket; +}; + diff --git a/src/bak/ideviceinstaller-wrap._c b/src/bak/ideviceinstaller-wrap._c new file mode 100644 index 0000000..c203a55 --- /dev/null +++ b/src/bak/ideviceinstaller-wrap._c @@ -0,0 +1,16 @@ +#include "common.h" +#define main tool_main +#define idevice_new_with_options wrap_idevice_new_with_options +idevice_error_t wrap_idevice_new_with_options(idevice_t *device, const char *udid, enum idevice_options options) { + *device = g_device; + return IDEVICE_E_SUCCESS; +} +#define PACKAGE_NAME "-" +#define PACKAGE_VERSION "-" +#define PACKAGE_URL "-" +#define PACKAGE_BUGREPORT "-" +// #include <../libraries/libimobiledevice/tools/ideviceinfo.c> +#include <../libraries/ideviceinstaller/src/ideviceinstaller.c> +#undef main +#undef idevice_new_with_options +#undef lockdownd_client_new_with_handshake diff --git a/src/bak/lockdown-stub._c b/src/bak/lockdown-stub._c new file mode 100644 index 0000000..a48d88d --- /dev/null +++ b/src/bak/lockdown-stub._c @@ -0,0 +1,12 @@ +#define lockdownd_client_new_with_handshake wrap_lockdownd_client_new_with_handshake +#include "../libraries/libimobiledevice/src/lockdown.c" +#undef lockdownd_client_new_with_handshake + +LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label) { + lockdownd_error_t ret = wrap_lockdownd_client_new_with_handshake(device, client, label); + if (ret == LOCKDOWN_E_SUCCESS) { + //DEBUG_PRINT("Start hb..."); + } + + return ret; +} \ No newline at end of file diff --git a/src/common.h b/src/common.h index 3053b68..685ec85 100644 --- a/src/common.h +++ b/src/common.h @@ -14,10 +14,16 @@ typedef int HINSTANCE; #include #include +#include +#include #define closesocket close typedef struct timeval TIMEVAL; typedef int BOOL; -#ifdef __cplusplus -using std::min; -#endif \ No newline at end of file +#ifndef odslog +#define odslog(msg) { std::cout << msg << std::endl; } +#endif + + +#define OutputDebugStringW(x) (std::wcout << x) +#define OutputDebugStringA(x) (std::cout << x) \ No newline at end of file diff --git a/shims/muslfix.cpp b/src/muslfix.c similarity index 95% rename from shims/muslfix.cpp rename to src/muslfix.c index c1a58f3..c9c6b7f 100644 --- a/shims/muslfix.cpp +++ b/src/muslfix.c @@ -1,8 +1,5 @@ #include #include - -extern "C" { - void * __memcpy_chk (void *dest, const void *src, size_t len, size_t dstlen) { @@ -19,6 +16,4 @@ void * __memmove_chk (void *dest, const void *src, size_t len, size_t dstlen) { return memmove (dest, src, len); -} - } \ No newline at end of file diff --git a/src/phone/global.h b/src/phone/global.h new file mode 100644 index 0000000..75f59b5 --- /dev/null +++ b/src/phone/global.h @@ -0,0 +1,19 @@ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +char pairUDID[256]; +char pairDeviceAddress[256]; +char pairData[16384]; +uint32_t pairDataLen; + +struct UPNPUrls *upnpUrls; +struct IGDdatas *upnpData; +char upnpExternalAddr[40]; +idevice_t g_device; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/phone/idevice-stub.c b/src/phone/idevice-stub.c new file mode 100644 index 0000000..3159b76 --- /dev/null +++ b/src/phone/idevice-stub.c @@ -0,0 +1,80 @@ +#include "../common.h" +#include "global.h" + +#define idevice_connect wrap_idevice_connect +#include "../libraries/libimobiledevice/src/idevice.c" +#undef idevice_connect + +#include "miniwget.h" +#include "miniupnpc.h" +#include "upnpcommands.h" +#include "portlistingparse.h" +#include "upnperrors.h" +#include +#include + +static int add_upnp_redir(const char *addr, int port, int *retPort) { + char *proto = "TCP"; + char *description = "altserver"; + char *lease = "600"; + char iport[6] = {0}; + sprintf(iport, "%d", port); + int minport = 16384; + int maxport = 65536; + + for (int i = 0; i < 5; i++) { + int curport = rand() % (maxport - minport) + minport; + char eport[6] = {0}; + sprintf(eport, "%d", curport); + int r = UPNP_AddPortMapping(upnpUrls->controlURL, upnpData->first.servicetype, + eport, iport, addr, description, + proto, NULL, lease); + if(r==UPNPCOMMAND_SUCCESS) { + *retPort = curport; + goto good; + } else { + printf("AddAnyPortMapping failed with code %d (%s), retrying...\n", + r, strupnperror(r)); + continue; + } + } + return 0; + good: + return 1; +} + +LIBIMOBILEDEVICE_API idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) { + if (device->conn_type == CONNECTION_NETWORK) { + int redirPort = 0; + if (!add_upnp_redir(pairDeviceAddress, port, &redirPort)) { + DEBUG_PRINT("failed to add upnp port map!"); + return IDEVICE_E_SSL_ERROR; + } + + debug_info("Connecting to %s:%d via %s:%d...", pairDeviceAddress, port, upnpExternalAddr, redirPort); + + struct sockaddr_storage saddr_storage; + struct sockaddr_in* saddr = (struct sockaddr_in*)&saddr_storage; + saddr->sin_family = AF_INET; + inet_aton(upnpExternalAddr, &saddr->sin_addr); + + int sfd = socket_connect_addr((struct sockaddr *)saddr, redirPort); + if (sfd < 0) { + debug_info("ERROR: Connecting to network device failed: %d (%s)", errno, strerror(errno)); + return IDEVICE_E_NO_DEVICE; + } + + idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); + new_connection->type = CONNECTION_NETWORK; + new_connection->data = (void*)(long)sfd; + new_connection->ssl_data = NULL; + new_connection->device = device; + new_connection->ssl_recv_timeout = (unsigned int)-1; + + *connection = new_connection; + + return IDEVICE_E_SUCCESS; + } else { + return wrap_idevice_connect(device, port, connection); + } +} \ No newline at end of file diff --git a/src/phone/libusbmuxd-stub.c b/src/phone/libusbmuxd-stub.c new file mode 100644 index 0000000..4523df0 --- /dev/null +++ b/src/phone/libusbmuxd-stub.c @@ -0,0 +1,191 @@ +// +// Copyright © 2021 osy. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include + +#define USBMUXD_API __attribute__((visibility("default"))) + +// usbmuxd public interface +#include "usbmuxd.h" +// usbmuxd protocol +#include "usbmuxd-proto.h" +// custom functions +#include "common/userpref.h" +//#include "CacheStorage.h" +//#include "Jitterbug.h" + +#include "../common.h" +#include "global.h" + +char pairUDID[256] = { 0 }; +char pairDeviceAddress[256] = { 0 }; +char pairData[16384] = { 0 }; +uint32_t pairDataLen = 0; + +#pragma mark - Device listing + +struct sockaddr_in_darwin { + uint8_t sin_len; + uint8_t sin_family; + uint16_t sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; + +USBMUXD_API int usbmuxd_get_device_by_udid(const char *udid, usbmuxd_device_info_t *device) +{ + if (!udid) { + DEBUG_PRINT("udid cannot be null!"); + return -EINVAL; + } + if (!device) { + DEBUG_PRINT("device cannot be null!"); + return -EINVAL; + } + struct sockaddr_in_darwin addr = {0}; + addr.sin_len = sizeof(addr); + addr.sin_family = AF_INET; + inet_aton(pairDeviceAddress, &addr.sin_addr); + strcpy(device->udid, pairUDID); + memcpy(device->conn_data, &addr, sizeof(struct sockaddr_in_darwin)); + device->conn_type = CONNECTION_TYPE_NETWORK; + return 1; +} + +USBMUXD_API int usbmuxd_get_device(const char *udid, usbmuxd_device_info_t *device, enum usbmux_lookup_options options) +{ + options = DEVICE_LOOKUP_NETWORK; + if ((options & DEVICE_LOOKUP_USBMUX) != 0) { + DEBUG_PRINT("DEVICE_LOOKUP_USBMUX not supported!"); + return -EINVAL; + } else { + return usbmuxd_get_device_by_udid(udid, device); + } +} + +#pragma mark - Device pairing + +USBMUXD_API int usbmuxd_read_buid(char **buid) +{ + // ignore BUID + return -EINVAL; +} + +USBMUXD_API int usbmuxd_read_pair_record(const char* record_id, char **record_data, uint32_t *record_size) +{ + size_t len = pairDataLen; + void *data = malloc(len); + memcpy(data, pairData, len); + DEBUG_PRINT("returned pairRecord len: %lu", len); + *record_data = data; + *record_size = (uint32_t)len; + return 0; +} + +#pragma mark - Unimplemented functions + +USBMUXD_API int usbmuxd_events_subscribe(usbmuxd_subscription_context_t *context, usbmuxd_event_cb_t callback, void *user_data) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API int usbmuxd_events_unsubscribe(usbmuxd_subscription_context_t context) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API int usbmuxd_get_device_list(usbmuxd_device_info_t **device_list) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API int usbmuxd_device_list_free(usbmuxd_device_info_t **device_list) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API int usbmuxd_subscribe(usbmuxd_event_cb_t callback, void *user_data) +{ + //DEBUG_PRINT("unimpl"); abort(); + DEBUG_PRINT("usbmuxd_subscribe ignored"); + return 0; +} + +USBMUXD_API int usbmuxd_unsubscribe(void) +{ + //DEBUG_PRINT("unimpl"); abort(); + DEBUG_PRINT("usbmuxd_unsubscribe ignored"); + return 0; +} + +USBMUXD_API int usbmuxd_connect(const uint32_t handle, const unsigned short port) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API int usbmuxd_disconnect(int sfd) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API int usbmuxd_send(int sfd, const char *data, uint32_t len, uint32_t *sent_bytes) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API int usbmuxd_recv_timeout(int sfd, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API int usbmuxd_recv(int sfd, char *data, uint32_t len, uint32_t *recv_bytes) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API int usbmuxd_save_pair_record_with_device_id(const char* record_id, uint32_t device_id, const char *record_data, uint32_t record_size) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API int usbmuxd_save_pair_record(const char* record_id, const char *record_data, uint32_t record_size) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API int usbmuxd_delete_pair_record(const char* record_id) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API void libusbmuxd_set_use_inotify(int set) +{ + DEBUG_PRINT("unimpl"); abort(); +} + +USBMUXD_API void libusbmuxd_set_debug_level(int level) +{ + DEBUG_PRINT("unimpl"); abort(); +} + diff --git a/upstream_repo b/upstream_repo deleted file mode 160000 index 071b1dd..0000000 --- a/upstream_repo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 071b1dda13ff4d3623b1e3680ec93dd670ecaf45