diff options
34 files changed, 3327 insertions, 550 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 31341af..1869190 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,9 @@ name: build -on: [push] +on: + push: + schedule: + - cron: '0 0 1 * *' jobs: build-linux-ubuntu: @@ -13,7 +16,7 @@ jobs: run: | echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch libplist - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml @@ -27,7 +30,7 @@ jobs: done sudo cp -r extract/* / sudo ldconfig - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: autogen run: ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig LDFLAGS="-Wl,-rpath=/usr/local/lib" - name: make @@ -40,7 +43,7 @@ jobs: DESTDIR=`pwd`/dest make install tar -C dest -cf libimobiledevice-glue.tar usr - name: publish artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: libimobiledevice-glue-latest_${{env.target_triplet}} path: libimobiledevice-glue.tar @@ -56,7 +59,7 @@ jobs: fi shell: bash - name: fetch libplist - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml @@ -69,7 +72,7 @@ jobs: tar -C extract -xvf $I done sudo cp -r extract/* / - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: autogen run: | SDKDIR=`xcrun --sdk macosx --show-sdk-path` @@ -93,7 +96,7 @@ jobs: DESTDIR=`pwd`/dest make install tar -C dest -cf libimobiledevice-glue.tar usr - name: publish artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: libimobiledevice-glue-latest_macOS path: libimobiledevice-glue.tar @@ -129,7 +132,7 @@ jobs: echo "dest=$dest" >> $GITHUB_ENV echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV - name: fetch libplist - uses: dawidd6/action-download-artifact@v2 + uses: dawidd6/action-download-artifact@v3 with: github_token: ${{secrets.GITHUB_TOKEN}} workflow: build.yml @@ -142,7 +145,7 @@ jobs: tar -C extract -xvf $I done cp -r extract/* / - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: autogen run: ./autogen.sh CC=gcc CXX=g++ - name: make @@ -155,7 +158,7 @@ jobs: DESTDIR=`pwd`/dest make install tar -C dest -cf libimobiledevice-glue.tar ${{ env.dest }} - name: publish artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: libimobiledevice-glue-latest_${{ matrix.arch }}-${{ env.dest }} path: libimobiledevice-glue.tar diff --git a/Makefile.am b/Makefile.am index 149678b..96d3c7b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,7 +3,12 @@ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src include EXTRA_DIST = \ - README.md + README.md \ + git-version-gen + +dist-hook: + @if ! git diff --quiet; then echo "Uncommitted changes present; not releasing"; exit 1; fi + echo $(VERSION) > $(distdir)/.tarball-version indent: indent -kr -ut -ts4 -l120 src/*.c src/*.h @@ -0,0 +1,34 @@ +Version 1.3.0 +~~~~~~~~~~~~~ + +- Changes: + * Add SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512 functions to interface + * socket: Create an IPv4 compatible IPv6 socket when NULL is passed to socket_create + * socket: Haiku compilation fixes + +Version 1.2.0 +~~~~~~~~~~~~~ + +- Changes: + * Add helper code to deal with NSKeyedArchiver plist data + +- Bugfixes: + * socket: Make sure errno is always set on error, and always return a meaningful error code + +Version 1.1.0 +~~~~~~~~~~~~~ + +- Changes: + * socket: Use poll() - when available - instead of select() + * socket: Allow NULL as address for socket_create() and socket_connect() + * win32: Remove windows.h from public headers + * Add version function to interface + +- Bugfixes: + * opack: Fixed 32bit buffer overflow + * opack: Fix parsing of 32 and 64 bit packed values + +Version 1.0.0 +~~~~~~~~~~~~~ + +First public release. @@ -5,6 +5,21 @@ Library with common code used by the libraries and tools around the  +## Table of Contents +- [Features](#features) +- [Building](#building) + - [Prerequisites](#prerequisites) + - [Linux (Debian/Ubuntu based)](#linux-debianubuntu-based) + - [macOS](#macos) + - [Windows](#windows) + - [Configuring the source tree](#configuring-the-source-tree) + - [Building and installation](#building-and-installation) +- [Usage](#usage) +- [Contributing](#contributing) +- [Links](#links) +- [License](#license) +- [Credits](#credits) + ## Features The main functionality provided by this library are **socket** helper @@ -13,7 +28,7 @@ Besides that it comes with a number of string, file, and plist helper functions, as well as some other commonly used code that was originally duplicated in the dedicated projects. -Test on Linux, macOS, Windows. +Tested on Linux, macOS, and Windows. ## Projects using this library @@ -23,48 +38,167 @@ Test on Linux, macOS, Windows. - [libirecovery](https://github.com/libimobiledevice/libirecovery) - [idevicerestore](https://github.com/libimobiledevice/idevicerestore) -## Installation / Getting started +## Building + +### Prerequisites + +You need to have a working compiler (gcc/clang) and development environent +available. This project uses autotools for the build process, allowing to +have common build steps across different platforms. +Only the prerequisites differ and they are described in this section. + +libimobiledevice-glue requires [libplist](https://github.com/libimobiledevice/libplist). +Check the [Building](https://github.com/libimobiledevice/libplist?tab=readme-ov-file#building) +section of the README on how to build it. Note that some platforms might have it as a package. + +#### Linux (Debian/Ubuntu based) + +* Install all required dependencies and build tools: + ```shell + sudo apt-get install \ + build-essential \ + pkg-config \ + checkinstall \ + git \ + autoconf \ + automake \ + libtool-bin \ + libplist-dev + ``` + + In case libplist-dev is not available, you can manually build and install it. See note above. + +#### macOS + +* Make sure the Xcode command line tools are installed. Then, use either [MacPorts](https://www.macports.org/) + or [Homebrew](https://brew.sh/) to install `automake`, `autoconf`, `libtool`, etc. + + Using MacPorts: + ```shell + sudo port install libtool autoconf automake pkgconfig + ``` + + Using Homebrew: + ```shell + brew install libtool autoconf automake pkg-config + ``` -### Debian / Ubuntu Linux +#### Windows + +* Using [MSYS2](https://www.msys2.org/) is the official way of compiling this project on Windows. Download the MSYS2 installer + and follow the installation steps. + + It is recommended to use the _MSYS2 MinGW 64-bit_ shell. Run it and make sure the required dependencies are installed: + + ```shell + pacman -S base-devel \ + git \ + mingw-w64-x86_64-gcc \ + make \ + libtool \ + autoconf \ + automake-wrapper \ + pkg-config + ``` + NOTE: You can use a different shell and different compiler according to your needs. Adapt the above command accordingly. + +### Configuring the source tree + +You can build the source code from a git checkout, or from a `.tar.bz2` release tarball from [Releases](https://github.com/libimobiledevice/libimobiledevice-glue/releases). +Before we can build it, the source tree has to be configured for building. The steps depend on where you got the source from. + +Since libimobiledevice-glue depends on other packages, you should set the pkg-config environment variable `PKG_CONFIG_PATH` +accordingly. Make sure to use a path with the same prefix as the dependencies. If they are installed in `/usr/local` you would do -First install all required dependencies and build tools: ```shell -sudo apt-get install \ - build-essential \ - pkg-config \ - checkinstall \ - git \ - autoconf \ - automake \ - libtool-bin \ - libplist-dev \ +export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ``` -Then clone the actual project repository: +* **From git** + + If you haven't done already, clone the actual project repository and change into the directory. + ```shell + git clone https://github.com/libimobiledevice/libimobiledevice-glue + cd libimobiledevice-glue + ``` + + Configure the source tree for building: + ```shell + ./autogen.sh + ``` + +* **From release tarball (.tar.bz2)** + + When using an official [release tarball](https://github.com/libimobiledevice/libimobiledevice-glue/releases) (`libimobiledevice-glue-x.y.z.tar.bz2`) + the procedure is slightly different. + + Extract the tarball: + ```shell + tar xjf libimobiledevice-glue-x.y.z.tar.bz2 + cd libimobiledevice-glue-x.y.z + ``` + + Configure the source tree for building: + ```shell + ./configure + ``` + +Both `./configure` and `./autogen.sh` (which generates and calls `configure`) accept a few options, for example `--prefix` to allow +building for a different target folder. You can simply pass them like this: + +```shell +./autogen.sh --prefix=/usr/local +``` +or ```shell -git clone https://github.com/libimobiledevice/libimobiledevice-glue.git -cd libimobiledevice-glue +./configure --prefix=/usr/local ``` -Now you can build and install it: +Once the command is successful, the last few lines of output will look like this: +``` +[...] +config.status: creating config.h +config.status: executing depfiles commands +config.status: executing libtool commands + +Configuration for libimobiledevice-glue 1.0.1: +------------------------------------------- + + Install prefix: .........: /usr/local + + Now type 'make' to build libimobiledevice-glue 1.0.1, + and then 'make install' for installation. +``` + +### Building and installation + +If you followed all the steps successfully, and `autogen.sh` or `configure` did not print any errors, +you are ready to build the project. This is simply done with + ```shell -./autogen.sh make -sudo make install ``` -If you require a custom prefix or other option being passed to `./configure` -you can pass them directly to `./autogen.sh` like this: -```bash -./autogen.sh --prefix=/opt/local -make +If no errors are emitted you are ready for installation. Depending on whether +the current user has permissions to write to the destination directory or not, +you would either run +```shell +make install +``` +_OR_ +```shell sudo make install ``` +If you are on Linux, you want to run `sudo ldconfig` after installation to +make sure the installed libraries are made available. + ## Usage This library is directly used by libusbmuxd, libimobiledevice, etc., so there -is no need to do anything in particular. +is no need to do anything in particular. Feel free to use it in your project, +check the header files for available functions. A documentation might be added +later. ## Contributing @@ -93,7 +227,7 @@ Please make sure your contribution adheres to: ## License -This library and utilities are licensed under the [GNU Lesser General Public License v2.1](https://www.gnu.org/licenses/lgpl-2.1.en.html), +This library is licensed under the [GNU Lesser General Public License v2.1](https://www.gnu.org/licenses/lgpl-2.1.en.html), also included in the repository in the `COPYING` file. ## Credits @@ -101,7 +235,8 @@ also included in the repository in the `COPYING` file. Apple, iPhone, iPad, iPod, iPod Touch, Apple TV, Apple Watch, Mac, iOS, iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc. -This project is an independent software and has not been authorized, sponsored, -or otherwise approved by Apple Inc. +This project is an independent software library and has not been authorized, +sponsored, or otherwise approved by Apple Inc. + +README Updated on: 2024-02-21 -README Updated on: 2022-04-04 diff --git a/configure.ac b/configure.ac index 4e40db1..cfdcddd 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,8 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. -AC_PREREQ(2.68) -AC_INIT([libimobiledevice-glue], [1.0.0], [https://github.com/libimobiledevice/libimobiledevice-glue/issues],, [https://libimobiledevice.org]) +AC_PREREQ([2.68]) +AC_INIT([libimobiledevice-glue], [m4_esyscmd(./git-version-gen $RELEASE_VERSION)], [https://github.com/libimobiledevice/libimobiledevice-glue/issues], [], [https://libimobiledevice.org]) AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip check-news]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) AC_CONFIG_SRCDIR([src/]) @@ -15,10 +15,15 @@ dnl libtool versioning # changes to the signature and the semantic) # ? :+1 : ? == just internal changes # CURRENT : REVISION : AGE -LIBIMOBILEDEVICE_GLUE_SO_VERSION=0:0:0 +LIBIMOBILEDEVICE_GLUE_SO_VERSION=3:0:3 + +# Check if we have a version defined +if test -z $PACKAGE_VERSION; then + AC_MSG_ERROR([PACKAGE_VERSION is not defined. Make sure to configure a source tree checked out from git or that .tarball-version is present.]) +fi dnl Minimum package versions -LIBPLIST_VERSION=2.2.0 +LIBPLIST_VERSION=2.3.0 AC_SUBST(LIBIMOBILEDEVICE_GLUE_SO_VERSION) AC_SUBST(LIBPLIST_VERSION) @@ -44,7 +49,9 @@ AC_TYPE_UINT32_T AC_TYPE_UINT8_T # Checks for library functions. -AC_CHECK_FUNCS([asprintf strcasecmp strdup strerror strndup stpcpy vasprintf getifaddrs]) +AC_CHECK_FUNCS([asprintf strcasecmp strdup strerror strndup stpcpy vasprintf getifaddrs poll]) +# Checks for additional library requirements +AC_SEARCH_LIBS(socket, network) AC_CHECK_HEADER(endian.h, [ac_cv_have_endian_h="yes"], [ac_cv_have_endian_h="no"]) if test "x$ac_cv_have_endian_h" = "xno"; then @@ -105,12 +112,12 @@ fi AC_CHECK_MEMBER(struct dirent.d_type, AC_DEFINE(HAVE_DIRENT_D_TYPE, 1, [define if struct dirent has member d_type]),, [#include <dirent.h>]) AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter -fsigned-char -fvisibility=hidden") -AC_SUBST(GLOBAL_CFLAGS) -case "$GLOBAL_CFLAGS" in - *-fvisibility=hidden*) - AC_DEFINE([HAVE_FVISIBILITY], [1], [Define if compiled with -fvisibility=hidden]) -esac +if test "x$enable_static" = "xyes" -a "x$enable_shared" = "xno"; then + GLOBAL_CFLAGS+=" -DLIMD_GLUE_STATIC" +fi + +AC_SUBST(GLOBAL_CFLAGS) # check for large file support AC_SYS_LARGEFILE diff --git a/git-version-gen b/git-version-gen new file mode 100755 index 0000000..d868952 --- /dev/null +++ b/git-version-gen @@ -0,0 +1,20 @@ +#!/bin/sh +SRCDIR=`dirname $0` +if test -n "$1"; then + VER=$1 +else + if test -r "${SRCDIR}/.git" && test -x "`which git`" ; then + git update-index -q --refresh + if ! VER=`git describe --tags --dirty 2>/dev/null`; then + COMMIT=`git rev-parse --short HEAD` + DIRTY=`git diff --quiet HEAD || echo "-dirty"` + VER=`sed -n '1,/RE/s/Version \(.*\)/\1/p' ${SRCDIR}/NEWS`-git-${COMMIT}${DIRTY} + fi + else + if test -f "${SRCDIR}/.tarball-version"; then + VER=`cat "${SRCDIR}/.tarball-version"` + fi + fi +fi +VER=`printf %s "$VER" | head -n1` +printf %s "$VER" diff --git a/include/Makefile.am b/include/Makefile.am index a071583..624814e 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -2,11 +2,14 @@ EXTRA_DIST = \ endianness.h nobase_include_HEADERS = \ + libimobiledevice-glue/glue.h \ libimobiledevice-glue/socket.h \ libimobiledevice-glue/thread.h \ libimobiledevice-glue/utils.h \ + libimobiledevice-glue/nskeyedarchive.h \ libimobiledevice-glue/collection.h \ libimobiledevice-glue/termcolors.h \ libimobiledevice-glue/cbuf.h \ libimobiledevice-glue/opack.h \ - libimobiledevice-glue/tlv.h + libimobiledevice-glue/tlv.h \ + libimobiledevice-glue/sha.h diff --git a/include/libimobiledevice-glue/cbuf.h b/include/libimobiledevice-glue/cbuf.h index 01e2f43..2463af7 100644 --- a/include/libimobiledevice-glue/cbuf.h +++ b/include/libimobiledevice-glue/cbuf.h @@ -22,14 +22,16 @@ #ifndef __CBUF_H #define __CBUF_H +#include <libimobiledevice-glue/glue.h> + struct char_buf { unsigned char* data; unsigned int length; unsigned int capacity; }; -struct char_buf* char_buf_new(); -void char_buf_free(struct char_buf* cbuf); -void char_buf_append(struct char_buf* cbuf, unsigned int length, unsigned char* data); +LIMD_GLUE_API struct char_buf* char_buf_new(); +LIMD_GLUE_API void char_buf_free(struct char_buf* cbuf); +LIMD_GLUE_API void char_buf_append(struct char_buf* cbuf, unsigned int length, unsigned char* data); #endif /* __CBUF_H */ diff --git a/include/libimobiledevice-glue/collection.h b/include/libimobiledevice-glue/collection.h index df1680c..46ef461 100644 --- a/include/libimobiledevice-glue/collection.h +++ b/include/libimobiledevice-glue/collection.h @@ -22,17 +22,19 @@ #ifndef COLLECTION_H #define COLLECTION_H +#include <libimobiledevice-glue/glue.h> + struct collection { void **list; int capacity; }; -void collection_init(struct collection *col); -void collection_add(struct collection *col, void *element); -int collection_remove(struct collection *col, void *element); -int collection_count(struct collection *col); -void collection_free(struct collection *col); -void collection_copy(struct collection *dest, struct collection *src); +LIMD_GLUE_API void collection_init(struct collection *col); +LIMD_GLUE_API void collection_add(struct collection *col, void *element); +LIMD_GLUE_API int collection_remove(struct collection *col, void *element); +LIMD_GLUE_API int collection_count(struct collection *col); +LIMD_GLUE_API void collection_free(struct collection *col); +LIMD_GLUE_API void collection_copy(struct collection *dest, struct collection *src); #define MERGE_(a,b) a ## _ ## b #define LABEL_(a,b) MERGE_(a, b) diff --git a/include/libimobiledevice-glue/glue.h b/include/libimobiledevice-glue/glue.h new file mode 100644 index 0000000..e1e9900 --- /dev/null +++ b/include/libimobiledevice-glue/glue.h @@ -0,0 +1,37 @@ +/* + * glue.h + * Common definitions + * + * Copyright (c) 2024 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __GLUE_H +#define __GLUE_H + +#ifndef LIMD_GLUE_API + #ifdef LIMD_GLUE_STATIC + #define LIMD_GLUE_API + #elif defined(_WIN32) + #define LIMD_GLUE_API __declspec(dllimport) + #else + #define LIMD_GLUE_API + #endif +#endif + +LIMD_GLUE_API const char* libimobiledevice_glue_version(); + +#endif diff --git a/include/libimobiledevice-glue/nskeyedarchive.h b/include/libimobiledevice-glue/nskeyedarchive.h new file mode 100644 index 0000000..5aad4d6 --- /dev/null +++ b/include/libimobiledevice-glue/nskeyedarchive.h @@ -0,0 +1,90 @@ +/* + * nskeyedarchive.h + * Helper code to work with plist files containing NSKeyedArchiver data. + * + * Copyright (c) 2019 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __NSKEYEDARCHIVE_H +#define __NSKEYEDARCHIVE_H + +#include <stdint.h> +#include <libimobiledevice-glue/glue.h> +#include <plist/plist.h> + +enum nskeyedarchive_class_type_t { + NSTYPE_INTEGER = 1, + NSTYPE_BOOLEAN, + NSTYPE_CHARS, + NSTYPE_STRING, + NSTYPE_REAL, + NSTYPE_ARRAY, + NSTYPE_DATA, + NSTYPE_INTREF, + NSTYPE_NSMUTABLESTRING, + NSTYPE_NSSTRING, + NSTYPE_NSMUTABLEARRAY, + NSTYPE_NSARRAY, + NSTYPE_NSMUTABLEDICTIONARY, + NSTYPE_NSDICTIONARY, + NSTYPE_NSDATE, + NSTYPE_NSURL, + NSTYPE_NSMUTABLEDATA, + NSTYPE_NSDATA, + NSTYPE_NSKEYEDARCHIVE, + NSTYPE_FROM_PLIST +}; + +typedef struct nskeyedarchive_st *nskeyedarchive_t; + +LIMD_GLUE_API nskeyedarchive_t nskeyedarchive_new(void); +LIMD_GLUE_API nskeyedarchive_t nskeyedarchive_new_from_plist(plist_t plist); +LIMD_GLUE_API nskeyedarchive_t nskeyedarchive_new_from_data(const void* data, uint32_t size); +LIMD_GLUE_API void nskeyedarchive_free(nskeyedarchive_t ka); + +LIMD_GLUE_API void nskeyedarchive_set_top_ref_key_name(nskeyedarchive_t ka, const char* keyname); + +LIMD_GLUE_API uint64_t nskeyedarchive_add_top_class(nskeyedarchive_t ka, const char* classname, ...) __attribute__ ((sentinel(0))); +LIMD_GLUE_API void nskeyedarchive_add_top_class_uid(nskeyedarchive_t ka, uint64_t uid); +LIMD_GLUE_API void nskeyedarchive_append_class(nskeyedarchive_t ka, const char* classname, ...) __attribute__ ((sentinel(0))); +LIMD_GLUE_API void nskeyedarchive_append_object(nskeyedarchive_t ka, plist_t object); + +LIMD_GLUE_API void nskeyedarchive_nsarray_append_item(nskeyedarchive_t ka, uint64_t uid, enum nskeyedarchive_class_type_t type, ...); +LIMD_GLUE_API void nskeyedarchive_nsdictionary_add_item(nskeyedarchive_t ka, uint64_t uid, const char* key, enum nskeyedarchive_class_type_t type, ...); + +LIMD_GLUE_API void nskeyedarchive_append_class_type_v(nskeyedarchive_t ka, enum nskeyedarchive_class_type_t type, va_list* va); +LIMD_GLUE_API void nskeyedarchive_append_class_type(nskeyedarchive_t ka, enum nskeyedarchive_class_type_t type, ...); + +LIMD_GLUE_API void nskeyedarchive_merge_object(nskeyedarchive_t ka, nskeyedarchive_t pka, plist_t object); + +LIMD_GLUE_API void nskeyedarchive_print(nskeyedarchive_t ka); +LIMD_GLUE_API plist_t nskeyedarchive_get_plist_ref(nskeyedarchive_t ka); +LIMD_GLUE_API plist_t nskeyedarchive_get_object_by_uid(nskeyedarchive_t ka, uint64_t uid); +LIMD_GLUE_API plist_t nskeyedarchive_get_class_by_uid(nskeyedarchive_t ka, uint64_t uid); +LIMD_GLUE_API plist_t nskeyedarchive_get_objects(nskeyedarchive_t ka); + +LIMD_GLUE_API uint64_t nskeyedarchive_get_class_uid(nskeyedarchive_t ka, const char* classref); +LIMD_GLUE_API const char* nskeyedarchive_get_classname(nskeyedarchive_t ka, uint64_t uid); + +LIMD_GLUE_API void nskeyedarchive_set_class_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, enum nskeyedarchive_class_type_t proptype, ...); +LIMD_GLUE_API int nskeyedarchive_get_class_uint64_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, uint64_t* value); +LIMD_GLUE_API int nskeyedarchive_get_class_int_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, int* value); +LIMD_GLUE_API int nskeyedarchive_get_class_string_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, char** value); +LIMD_GLUE_API int nskeyedarchive_get_class_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, plist_t* value); + +LIMD_GLUE_API plist_t nskeyedarchive_to_plist(nskeyedarchive_t ka); + +#endif diff --git a/include/libimobiledevice-glue/opack.h b/include/libimobiledevice-glue/opack.h index 43b1d1d..2d7a94a 100644 --- a/include/libimobiledevice-glue/opack.h +++ b/include/libimobiledevice-glue/opack.h @@ -21,9 +21,10 @@ #ifndef __OPACK_H #define __OPACK_H +#include <libimobiledevice-glue/glue.h> #include <plist/plist.h> -void opack_encode_from_plist(plist_t plist, unsigned char** out, unsigned int* out_len); -int opack_decode_to_plist(unsigned char* buf, unsigned int buf_len, plist_t* plist_out); +LIMD_GLUE_API void opack_encode_from_plist(plist_t plist, unsigned char** out, unsigned int* out_len); +LIMD_GLUE_API int opack_decode_to_plist(unsigned char* buf, unsigned int buf_len, plist_t* plist_out); #endif /* __OPACK_H */ diff --git a/include/libimobiledevice-glue/sha.h b/include/libimobiledevice-glue/sha.h new file mode 100644 index 0000000..3a00578 --- /dev/null +++ b/include/libimobiledevice-glue/sha.h @@ -0,0 +1,86 @@ +#ifndef __SHA_H +#define __SHA_H + +#include <stddef.h> +#include <stdint.h> + +#include <libimobiledevice-glue/glue.h> + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* SHA-1 */ +typedef struct sha1_context_ { + uint64_t length; + uint32_t state[5]; + size_t curlen; + unsigned char buf[64]; +} sha1_context; + +#define SHA1_DIGEST_LENGTH 20 + +LIMD_GLUE_API int sha1_init(sha1_context * md); +LIMD_GLUE_API int sha1_final(sha1_context * md, unsigned char *out); +LIMD_GLUE_API int sha1_update(sha1_context * md, const void *data, size_t inlen); +LIMD_GLUE_API int sha1(const unsigned char *message, size_t message_len, unsigned char *out); + +/* SHA-256 */ +typedef struct sha256_context_ { + uint64_t length; + uint32_t state[8]; + size_t curlen; + unsigned char buf[64]; + int num_dwords; +} sha256_context; + +#define SHA256_DIGEST_LENGTH 32 + +LIMD_GLUE_API int sha256_init(sha256_context * md); +LIMD_GLUE_API int sha256_final(sha256_context * md, unsigned char *out); +LIMD_GLUE_API int sha256_update(sha256_context * md, const void *data, size_t inlen); +LIMD_GLUE_API int sha256(const unsigned char *message, size_t message_len, unsigned char *out); + +/* SHA-224 */ +#define sha224_context sha256_context + +#define SHA224_DIGEST_LENGTH 28 + +LIMD_GLUE_API int sha224_init(sha224_context * md); +LIMD_GLUE_API int sha224_final(sha224_context * md, unsigned char *out); +LIMD_GLUE_API int sha224_update(sha224_context * md, const void *data, size_t inlen); +LIMD_GLUE_API int sha224(const unsigned char *message, size_t message_len, unsigned char *out); + +/* SHA-512 */ +typedef struct sha512_context_ { + uint64_t length, state[8]; + size_t curlen; + unsigned char buf[128]; + int num_qwords; +} sha512_context; + +#define SHA512_DIGEST_LENGTH 64 + +LIMD_GLUE_API int sha512_init(sha512_context * md); +LIMD_GLUE_API int sha512_final(sha512_context * md, unsigned char *out); +LIMD_GLUE_API int sha512_update(sha512_context * md, const void *data, size_t inlen); +LIMD_GLUE_API int sha512(const unsigned char *message, size_t message_len, unsigned char *out); + +/* SHA-384 */ +#define sha384_context sha512_context + +#define SHA384_DIGEST_LENGTH 48 + +LIMD_GLUE_API int sha384_init(sha384_context * md); +LIMD_GLUE_API int sha384_final(sha384_context * md, unsigned char *out); +LIMD_GLUE_API int sha384_update(sha384_context * md, const void *data, size_t inlen); +LIMD_GLUE_API int sha384(const unsigned char *message, size_t message_len, unsigned char *out); + +#endif diff --git a/include/libimobiledevice-glue/socket.h b/include/libimobiledevice-glue/socket.h index 53f58b8..39391c6 100644 --- a/include/libimobiledevice-glue/socket.h +++ b/include/libimobiledevice-glue/socket.h @@ -41,30 +41,32 @@ typedef enum fd_mode fd_mode; #include <sys/socket.h> #endif +#include <libimobiledevice-glue/glue.h> + #ifndef WIN32 -int socket_create_unix(const char *filename); -int socket_connect_unix(const char *filename); +LIMD_GLUE_API int socket_create_unix(const char *filename); +LIMD_GLUE_API int socket_connect_unix(const char *filename); #endif -int socket_create(const char *addr, uint16_t port); -int socket_connect_addr(struct sockaddr *addr, uint16_t port); -int socket_connect(const char *addr, uint16_t port); -int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout); -int socket_accept(int fd, uint16_t port); +LIMD_GLUE_API int socket_create(const char *addr, uint16_t port); +LIMD_GLUE_API int socket_connect_addr(struct sockaddr *addr, uint16_t port); +LIMD_GLUE_API int socket_connect(const char *addr, uint16_t port); +LIMD_GLUE_API int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout); +LIMD_GLUE_API int socket_accept(int fd, uint16_t port); -int socket_shutdown(int fd, int how); -int socket_close(int fd); +LIMD_GLUE_API int socket_shutdown(int fd, int how); +LIMD_GLUE_API int socket_close(int fd); -int socket_receive(int fd, void *data, size_t length); -int socket_peek(int fd, void *data, size_t length); -int socket_receive_timeout(int fd, void *data, size_t length, int flags, unsigned int timeout); -int socket_send(int fd, void *data, size_t length); +LIMD_GLUE_API int socket_receive(int fd, void *data, size_t length); +LIMD_GLUE_API int socket_peek(int fd, void *data, size_t length); +LIMD_GLUE_API int socket_receive_timeout(int fd, void *data, size_t length, int flags, unsigned int timeout); +LIMD_GLUE_API int socket_send(int fd, void *data, size_t length); -int socket_get_socket_port(int fd, uint16_t *port); +LIMD_GLUE_API int socket_get_socket_port(int fd, uint16_t *port); -void socket_set_verbose(int level); +LIMD_GLUE_API void socket_set_verbose(int level); -const char *socket_addr_to_string(struct sockaddr *addr, char *addr_out, size_t addr_out_size); +LIMD_GLUE_API const char *socket_addr_to_string(struct sockaddr *addr, char *addr_out, size_t addr_out_size); -int get_primary_mac_address(unsigned char mac_addr_buf[6]); +LIMD_GLUE_API int get_primary_mac_address(unsigned char mac_addr_buf[6]); #endif /* SOCKET_SOCKET_H */ diff --git a/include/libimobiledevice-glue/termcolors.h b/include/libimobiledevice-glue/termcolors.h index 3e03e97..d7799c3 100644 --- a/include/libimobiledevice-glue/termcolors.h +++ b/include/libimobiledevice-glue/termcolors.h @@ -26,6 +26,7 @@ #include <stdarg.h> #include <stdio.h> +#include <libimobiledevice-glue/glue.h> #define COLOR_RESET "\e[m" #define STYLE_NORMAL "\e[0m" @@ -74,14 +75,14 @@ #define BG_DEFAULT "\e[49m" /* automatically called by library constructor */ -void term_colors_init(); +LIMD_GLUE_API void term_colors_init(); /* enable / disable terminal colors */ -void term_colors_set_enabled(int en); +LIMD_GLUE_API void term_colors_set_enabled(int en); /* color-aware *printf variants */ -int cprintf(const char* fmt, ...); -int cfprintf(FILE* stream, const char* fmt, ...); -int cvfprintf(FILE* stream, const char* fmt, va_list vargs); +LIMD_GLUE_API int cprintf(const char* fmt, ...); +LIMD_GLUE_API int cfprintf(FILE* stream, const char* fmt, ...); +LIMD_GLUE_API int cvfprintf(FILE* stream, const char* fmt, va_list vargs); #endif diff --git a/include/libimobiledevice-glue/thread.h b/include/libimobiledevice-glue/thread.h index 2aadc6e..76646b8 100644 --- a/include/libimobiledevice-glue/thread.h +++ b/include/libimobiledevice-glue/thread.h @@ -23,16 +23,31 @@ #define __THREAD_H #include <stddef.h> +#include <libimobiledevice-glue/glue.h> #ifdef WIN32 -#include <windows.h> +typedef void* HANDLE; typedef HANDLE THREAD_T; -typedef CRITICAL_SECTION mutex_t; +#pragma pack(push, 8) +struct _CRITICAL_SECTION_ST { + void* DebugInfo; + long LockCount; + long RecursionCount; + HANDLE OwningThread; + HANDLE LockSemaphore; +#if defined(_WIN64) + unsigned __int64 SpinCount; +#else + unsigned long SpinCount; +#endif +}; +#pragma pack(pop) +typedef struct _CRITICAL_SECTION_ST mutex_t; typedef struct { HANDLE sem; } cond_t; typedef volatile struct { - LONG lock; + long lock; int state; } thread_once_t; #define THREAD_ONCE_INIT {0, 0} @@ -53,13 +68,13 @@ typedef pthread_once_t thread_once_t; typedef void* (*thread_func_t)(void* data); -int thread_new(THREAD_T* thread, thread_func_t thread_func, void* data); -void thread_detach(THREAD_T thread); -void thread_free(THREAD_T thread); -int thread_join(THREAD_T thread); -int thread_alive(THREAD_T thread); +LIMD_GLUE_API int thread_new(THREAD_T* thread, thread_func_t thread_func, void* data); +LIMD_GLUE_API void thread_detach(THREAD_T thread); +LIMD_GLUE_API void thread_free(THREAD_T thread); +LIMD_GLUE_API int thread_join(THREAD_T thread); +LIMD_GLUE_API int thread_alive(THREAD_T thread); -int thread_cancel(THREAD_T thread); +LIMD_GLUE_API int thread_cancel(THREAD_T thread); #ifdef WIN32 #undef HAVE_THREAD_CLEANUP @@ -71,17 +86,17 @@ int thread_cancel(THREAD_T thread); #endif #endif -void mutex_init(mutex_t* mutex); -void mutex_destroy(mutex_t* mutex); -void mutex_lock(mutex_t* mutex); -void mutex_unlock(mutex_t* mutex); +LIMD_GLUE_API void mutex_init(mutex_t* mutex); +LIMD_GLUE_API void mutex_destroy(mutex_t* mutex); +LIMD_GLUE_API void mutex_lock(mutex_t* mutex); +LIMD_GLUE_API void mutex_unlock(mutex_t* mutex); -void thread_once(thread_once_t *once_control, void (*init_routine)(void)); +LIMD_GLUE_API void thread_once(thread_once_t *once_control, void (*init_routine)(void)); -void cond_init(cond_t* cond); -void cond_destroy(cond_t* cond); -int cond_signal(cond_t* cond); -int cond_wait(cond_t* cond, mutex_t* mutex); -int cond_wait_timeout(cond_t* cond, mutex_t* mutex, unsigned int timeout_ms); +LIMD_GLUE_API void cond_init(cond_t* cond); +LIMD_GLUE_API void cond_destroy(cond_t* cond); +LIMD_GLUE_API int cond_signal(cond_t* cond); +LIMD_GLUE_API int cond_wait(cond_t* cond, mutex_t* mutex); +LIMD_GLUE_API int cond_wait_timeout(cond_t* cond, mutex_t* mutex, unsigned int timeout_ms); #endif diff --git a/include/libimobiledevice-glue/tlv.h b/include/libimobiledevice-glue/tlv.h index 895c883..42be4f6 100644 --- a/include/libimobiledevice-glue/tlv.h +++ b/include/libimobiledevice-glue/tlv.h @@ -22,6 +22,7 @@ #define __TLV_H #include <stdint.h> +#include <libimobiledevice-glue/glue.h> struct tlv_buf { unsigned char* data; @@ -30,13 +31,13 @@ struct tlv_buf { }; typedef struct tlv_buf* tlv_buf_t; -tlv_buf_t tlv_buf_new(); -void tlv_buf_free(tlv_buf_t tlv); +LIMD_GLUE_API tlv_buf_t tlv_buf_new(); +LIMD_GLUE_API void tlv_buf_free(tlv_buf_t tlv); -void tlv_buf_append(tlv_buf_t tlv, uint8_t tag, unsigned int length, void* data); -unsigned char* tlv_get_data_ptr(const void* tlv_data, void* tlv_end, uint8_t tag, uint8_t* length); -int tlv_data_get_uint(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint64_t* value); -int tlv_data_get_uint8(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint8_t* value); -int tlv_data_copy_data(const void* tlv_data, unsigned int tlv_length, uint8_t tag, void** out, unsigned int* out_len); +LIMD_GLUE_API void tlv_buf_append(tlv_buf_t tlv, uint8_t tag, unsigned int length, void* data); +LIMD_GLUE_API unsigned char* tlv_get_data_ptr(const void* tlv_data, void* tlv_end, uint8_t tag, uint8_t* length); +LIMD_GLUE_API int tlv_data_get_uint(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint64_t* value); +LIMD_GLUE_API int tlv_data_get_uint8(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint8_t* value); +LIMD_GLUE_API int tlv_data_copy_data(const void* tlv_data, unsigned int tlv_length, uint8_t tag, void** out, unsigned int* out_len); #endif /* __TLV_H */ diff --git a/include/libimobiledevice-glue/utils.h b/include/libimobiledevice-glue/utils.h index b8513c0..355f1da 100644 --- a/include/libimobiledevice-glue/utils.h +++ b/include/libimobiledevice-glue/utils.h @@ -25,38 +25,20 @@ #ifndef __UTILS_H #define __UTILS_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#ifdef WIN32 -#include <windows.h> -#endif - #include <stdio.h> -#include <plist/plist.h> +#include <stdint.h> +#include <libimobiledevice-glue/glue.h> #define MAC_EPOCH 978307200 -char *string_concat(const char *str, ...); -char *string_append(char *str, ...); -char *string_build_path(const char *elem, ...); -char *string_format_size(uint64_t size); -char *string_toupper(char *str); -char *generate_uuid(void); - -int buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length); -int buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length); - -enum plist_format_t { - PLIST_FORMAT_XML, - PLIST_FORMAT_BINARY -}; - -int plist_read_from_filename(plist_t *plist, const char *filename); -int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format); +LIMD_GLUE_API char *string_concat(const char *str, ...); +LIMD_GLUE_API char *string_append(char *str, ...); +LIMD_GLUE_API char *string_build_path(const char *elem, ...); +LIMD_GLUE_API char *string_format_size(uint64_t size); +LIMD_GLUE_API char *string_toupper(char *str); +LIMD_GLUE_API char *generate_uuid(void); -void plist_print_to_stream(plist_t plist, FILE* stream); -void plist_print_to_stream_with_indentation(plist_t plist, FILE* stream, unsigned int indentation); +LIMD_GLUE_API int buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length); +LIMD_GLUE_API int buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length); #endif diff --git a/src/Makefile.am b/src/Makefile.am index b709103..0089b5d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,11 +12,16 @@ libimobiledevice_glue_1_0_la_SOURCES = \ socket.c \ thread.c \ utils.c \ + nskeyedarchive.c \ collection.c \ termcolors.c \ cbuf.c \ opack.c \ tlv.c \ + sha1.c \ + sha256.c \ + sha512.c \ + fixedint.h \ common.h if WIN32 @@ -30,7 +30,7 @@ #include "common.h" #include "libimobiledevice-glue/cbuf.h" -LIBIMOBILEDEVICE_GLUE_API struct char_buf* char_buf_new() +struct char_buf* char_buf_new() { struct char_buf* cbuf = (struct char_buf*)malloc(sizeof(struct char_buf)); cbuf->capacity = 256; @@ -39,7 +39,7 @@ LIBIMOBILEDEVICE_GLUE_API struct char_buf* char_buf_new() return cbuf; } -LIBIMOBILEDEVICE_GLUE_API void char_buf_free(struct char_buf* cbuf) +void char_buf_free(struct char_buf* cbuf) { if (cbuf) { free(cbuf->data); @@ -47,7 +47,7 @@ LIBIMOBILEDEVICE_GLUE_API void char_buf_free(struct char_buf* cbuf) } } -LIBIMOBILEDEVICE_GLUE_API void char_buf_append(struct char_buf* cbuf, unsigned int length, unsigned char* data) +void char_buf_append(struct char_buf* cbuf, unsigned int length, unsigned char* data) { if (!cbuf || !cbuf->data) { return; diff --git a/src/collection.c b/src/collection.c index ef47217..0744427 100644 --- a/src/collection.c +++ b/src/collection.c @@ -37,7 +37,7 @@ #define CAPACITY_STEP 8 -LIBIMOBILEDEVICE_GLUE_API void collection_init(struct collection *col) +void collection_init(struct collection *col) { col->list = malloc(sizeof(void *) * CAPACITY_STEP); assert(col->list); @@ -45,14 +45,14 @@ LIBIMOBILEDEVICE_GLUE_API void collection_init(struct collection *col) col->capacity = CAPACITY_STEP; } -LIBIMOBILEDEVICE_GLUE_API void collection_free(struct collection *col) +void collection_free(struct collection *col) { free(col->list); col->list = NULL; col->capacity = 0; } -LIBIMOBILEDEVICE_GLUE_API void collection_add(struct collection *col, void *element) +void collection_add(struct collection *col, void *element) { int i; for(i=0; i<col->capacity; i++) { @@ -69,7 +69,7 @@ LIBIMOBILEDEVICE_GLUE_API void collection_add(struct collection *col, void *elem col->capacity += CAPACITY_STEP; } -LIBIMOBILEDEVICE_GLUE_API int collection_remove(struct collection *col, void *element) +int collection_remove(struct collection *col, void *element) { int i; for(i=0; i<col->capacity; i++) { @@ -82,7 +82,7 @@ LIBIMOBILEDEVICE_GLUE_API int collection_remove(struct collection *col, void *el return -1; } -LIBIMOBILEDEVICE_GLUE_API int collection_count(struct collection *col) +int collection_count(struct collection *col) { int i, cnt = 0; for(i=0; i<col->capacity; i++) { @@ -92,7 +92,7 @@ LIBIMOBILEDEVICE_GLUE_API int collection_count(struct collection *col) return cnt; } -LIBIMOBILEDEVICE_GLUE_API void collection_copy(struct collection *dest, struct collection *src) +void collection_copy(struct collection *dest, struct collection *src) { if (!dest || !src) return; dest->capacity = src->capacity; diff --git a/src/common.h b/src/common.h index bd22e3d..94b5fdd 100644 --- a/src/common.h +++ b/src/common.h @@ -25,14 +25,18 @@ #include <config.h> #endif -#ifdef WIN32 -#define LIBIMOBILEDEVICE_GLUE_API __declspec( dllexport ) +#ifdef LIMD_GLUE_STATIC + #define LIMD_GLUE_API +#elif defined(_WIN32) + #define LIMD_GLUE_API __declspec( dllexport ) #else -#ifdef HAVE_FVISIBILITY -#define LIBIMOBILEDEVICE_GLUE_API __attribute__((visibility("default"))) -#else -#define LIBIMOBILEDEVICE_GLUE_API -#endif + #if __GNUC__ >= 4 + #define LIMD_GLUE_API __attribute__((visibility("default"))) + #else + #define LIMD_GLUE_API + #endif #endif +#include "libimobiledevice-glue/glue.h" + #endif diff --git a/src/fixedint.h b/src/fixedint.h new file mode 100644 index 0000000..1a8745b --- /dev/null +++ b/src/fixedint.h @@ -0,0 +1,72 @@ +/* + Portable header to provide the 32 and 64 bits type. + + Not a compatible replacement for <stdint.h>, do not blindly use it as such. +*/ + +#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__WATCOMC__) && (defined(_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_) || defined(__UINT_FAST64_TYPE__)) )) && !defined(FIXEDINT_H_INCLUDED) + #include <stdint.h> + #define FIXEDINT_H_INCLUDED + + #if defined(__WATCOMC__) && __WATCOMC__ >= 1250 && !defined(UINT64_C) + #include <limits.h> + #define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) + #endif +#endif + + +#ifndef FIXEDINT_H_INCLUDED + #define FIXEDINT_H_INCLUDED + + #include <limits.h> + + /* (u)int32_t */ + #ifndef uint32_t + #if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long uint32_t; + #elif (UINT_MAX == 0xffffffffUL) + typedef unsigned int uint32_t; + #elif (USHRT_MAX == 0xffffffffUL) + typedef unsigned short uint32_t; + #endif + #endif + + + #ifndef int32_t + #if (LONG_MAX == 0x7fffffffL) + typedef signed long int32_t; + #elif (INT_MAX == 0x7fffffffL) + typedef signed int int32_t; + #elif (SHRT_MAX == 0x7fffffffL) + typedef signed short int32_t; + #endif + #endif + + + /* (u)int64_t */ + #if (defined(__STDC__) && defined(__STDC_VERSION__) && __STDC__ && __STDC_VERSION__ >= 199901L) + typedef long long int64_t; + typedef unsigned long long uint64_t; + + #define UINT64_C(v) v ##ULL + #define INT64_C(v) v ##LL + #elif defined(__GNUC__) + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; + + #define UINT64_C(v) v ##ULL + #define INT64_C(v) v ##LL + #elif defined(__MWERKS__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__APPLE_CC__) || defined(_LONG_LONG) || defined(_CRAYC) + typedef long long int64_t; + typedef unsigned long long uint64_t; + + #define UINT64_C(v) v ##ULL + #define INT64_C(v) v ##LL + #elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined(__BORLANDC__) && __BORLANDC__ > 0x460) || defined(__alpha) || defined(__DECC) + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + + #define UINT64_C(v) v ##UI64 + #define INT64_C(v) v ##I64 + #endif +#endif @@ -78,3 +78,11 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) #else #warning No compiler support for constructor/destructor attributes, some features might not be available. #endif + +const char* libimobiledevice_glue_version() +{ +#ifndef PACKAGE_VERSION +#error PACKAGE_VERSION is not defined! +#endif + return PACKAGE_VERSION; +} diff --git a/src/nskeyedarchive.c b/src/nskeyedarchive.c new file mode 100644 index 0000000..b3dbdc4 --- /dev/null +++ b/src/nskeyedarchive.c @@ -0,0 +1,1228 @@ +/* + * nskeyedarchive.c + * Helper code to work with plist files containing NSKeyedArchiver data. + * + * Copyright (c) 2019 Nikias Bassen, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <plist/plist.h> +#include "common.h" +#include "libimobiledevice-glue/nskeyedarchive.h" + +#define NS_KEYED_ARCHIVER_NAME "NSKeyedArchiver" +#define NS_KEYED_ARCHIVER_VERSION 100000 + +struct nskeyedarchive_st { + plist_t dict; + uint64_t uid; +}; + +nskeyedarchive_t nskeyedarchive_new(void) +{ + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "$version", plist_new_uint(NS_KEYED_ARCHIVER_VERSION)); + plist_t objects = plist_new_array(); + plist_array_append_item(objects, plist_new_string("$null")); + plist_dict_set_item(dict, "$objects", objects); + plist_dict_set_item(dict, "$archiver", plist_new_string(NS_KEYED_ARCHIVER_NAME)); + + nskeyedarchive_t nskeyed = (nskeyedarchive_t)malloc(sizeof(struct nskeyedarchive_st)); + nskeyed->dict = dict; + nskeyed->uid = 1; + return nskeyed; +} + +void nskeyedarchive_free(nskeyedarchive_t ka) +{ + if (ka) { + if (ka->dict) { + plist_free(ka->dict); + } + free(ka); + } +} + +void nskeyedarchive_set_top_ref_key_name(nskeyedarchive_t ka, const char* keyname) +{ + if (!ka) { + return; + } + plist_t top = plist_dict_get_item(ka->dict, "$top"); + if (top) { + plist_dict_iter iter = NULL; + plist_dict_new_iter(top, &iter); + if (iter) { + plist_t node = NULL; + char* keyn = NULL; + plist_dict_next_item(top, iter, &keyn, &node); + plist_t keynode = plist_dict_item_get_key(node); + plist_set_key_val(keynode, keyname); + free(keyn); + free(iter); + } + } +} + +plist_t nskeyedarchive_get_objects(nskeyedarchive_t ka) +{ + plist_t objects = plist_dict_get_item(ka->dict, "$objects"); + if (!objects || (plist_get_node_type(objects) != PLIST_ARRAY)) { + fprintf(stderr, "ERROR: $objects node not found!\n"); + return NULL; + } + + return objects; +} + +plist_t nskeyedarchive_get_object_by_uid(nskeyedarchive_t ka, uint64_t uid) +{ + plist_t objects = nskeyedarchive_get_objects(ka); + if (!objects) { + return NULL; + } + + plist_t obj = plist_array_get_item(objects, (uint32_t)uid); + if (!obj) { + fprintf(stderr, "ERROR: unable to get object node with uid %llu\n", (long long)uid); + return NULL; + } + + return obj; +} + +plist_t nskeyedarchive_get_class_by_uid(nskeyedarchive_t ka, uint64_t uid) +{ + plist_t ret = nskeyedarchive_get_object_by_uid(ka, uid); + if (ret && (plist_get_node_type(ret) != PLIST_DICT)) { + fprintf(stderr, "ERROR: the uid %llu does not reference a valid class with node type PLIST_DICT!\n", (long long)uid); + return NULL; + } + + return ret; +} + +void nskeyedarchive_append_object(nskeyedarchive_t ka, plist_t object) +{ + plist_t objects = nskeyedarchive_get_objects(ka); + if (objects && plist_get_node_type(objects) != PLIST_ARRAY) { + fprintf(stderr, "ERROR: unable to append object\n"); + return; + } + + plist_array_append_item(objects, object); +} + +static void nskeyedarchive_append_class_v(nskeyedarchive_t ka, const char* classname, va_list* va) +{ + if (!ka) { + fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__); + return; + } else if (!classname) { + fprintf(stderr, "%s: ERROR: missing classname!\n", __func__); + return; + } + + char* arg = NULL; + plist_t classes = NULL; + do { + arg = (char*)va_arg(*va, char*); + if (!arg) { + break; + } + if (!classes) { + classes = plist_new_array(); + plist_array_append_item(classes, plist_new_string(classname)); + } + plist_array_append_item(classes, plist_new_string(arg)); + } while (arg); + + plist_t cls = plist_new_dict(); + plist_dict_set_item(cls, "$class", plist_new_uid(++ka->uid)); + + nskeyedarchive_append_object(ka, cls); + + cls = plist_new_dict(); + if (classes) { + plist_dict_set_item(cls, "$classes", classes); + } + plist_dict_set_item(cls, "$classname", plist_new_string(classname)); + + nskeyedarchive_append_object(ka, cls); +} + +void nskeyedarchive_append_class(nskeyedarchive_t ka, const char* classname, ...) +{ + if (!ka) { + fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__); + return; + } else if (!classname) { + fprintf(stderr, "%s: ERROR: missing classname!\n", __func__); + return; + } + + va_list va; + va_start(va, classname); + nskeyedarchive_append_class_v(ka, classname, &va); + va_end(va); +} + +void nskeyedarchive_add_top_class_uid(nskeyedarchive_t ka, uint64_t uid) +{ + plist_t top = plist_dict_get_item(ka->dict, "$top"); + if (!top) { + top = plist_new_dict(); + plist_dict_set_item(top, "$0", plist_new_uid(uid)); + plist_dict_set_item(ka->dict, "$top", top); + } else { + uint32_t num = plist_dict_get_size(top); + char newkey[8]; + sprintf(newkey, "$%d", num); + plist_dict_set_item(top, newkey, plist_new_uid(uid)); + } +} + +uint64_t nskeyedarchive_add_top_class(nskeyedarchive_t ka, const char* classname, ...) +{ + if (!ka) { + fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__); + return 0; + } else if (!classname) { + fprintf(stderr, "%s: ERROR: missing classname!\n", __func__); + return 0; + } + + uint64_t uid = ka->uid; + + va_list va; + va_start(va, classname); + nskeyedarchive_append_class_v(ka, classname, &va); + va_end(va); + + nskeyedarchive_add_top_class_uid(ka, uid); + return uid; +} + +static void nskeyedarchive_set_class_property_v(nskeyedarchive_t ka, uint64_t uid, const char* propname, enum nskeyedarchive_class_type_t proptype, va_list* va); + +static void nskeyedarchive_nsarray_append(nskeyedarchive_t ka, plist_t array, enum nskeyedarchive_class_type_t type, ...); + +static void nskeyedarchive_nsarray_append_v(nskeyedarchive_t ka, plist_t array, enum nskeyedarchive_class_type_t type, va_list* va) +{ + if (!ka) { + fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__); + return; + } else if (!array) { + fprintf(stderr, "%s: ERROR: missing plist!\n", __func__); + return; + } + + uint64_t newuid; + + switch (type) { + case NSTYPE_INTEGER: + plist_array_append_item(array, plist_new_uint(va_arg(*va, int))); + break; + case NSTYPE_INTREF: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_uint(va_arg(*va, int))); + break; + case NSTYPE_BOOLEAN: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_bool(va_arg(*va, int))); + break; + case NSTYPE_CHARS: + plist_array_append_item(array, plist_new_string(va_arg(*va, char*))); + break; + case NSTYPE_STRING: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_string(va_arg(*va, char*))); + break; + case NSTYPE_REAL: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_real(va_arg(*va, double))); + break; + case NSTYPE_NSMUTABLESTRING: + case NSTYPE_NSSTRING: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + if (type == NSTYPE_NSMUTABLESTRING) { + nskeyedarchive_append_class(ka, "NSMutableString", "NSString", "NSObject", NULL); + } else { + nskeyedarchive_append_class(ka, "NSString", "NSObject", NULL); + } + nskeyedarchive_set_class_property(ka, newuid, "NS.string", NSTYPE_CHARS, va_arg(*va, char*)); + break; + case NSTYPE_NSMUTABLEARRAY: + case NSTYPE_NSARRAY: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + if (type == NSTYPE_NSMUTABLEARRAY) { + nskeyedarchive_append_class(ka, "NSMutableArray", "NSArray", "NSObject", NULL); + } else { + nskeyedarchive_append_class(ka, "NSArray", "NSObject", NULL); + } + { + plist_t arr = plist_new_array(); + do { + int ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_nsarray_append_v(ka, arr, ptype, va); + } while (1); + nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, arr); + plist_free(arr); + } + break; + case NSTYPE_NSMUTABLEDICTIONARY: + case NSTYPE_NSDICTIONARY: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + if (type == NSTYPE_NSMUTABLEDICTIONARY) { + nskeyedarchive_append_class(ka, "NSMutableDictionary", "NSDictionary", "NSObject", NULL); + } else { + nskeyedarchive_append_class(ka, "NSDictionary", "NSObject", NULL); + } + { + plist_t keyarr = plist_new_array(); + plist_t valarr = plist_new_array(); + do { + char* key = va_arg(*va, char*); + if (!key) + break; + int ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_nsarray_append(ka, keyarr, NSTYPE_STRING, key); + nskeyedarchive_nsarray_append_v(ka, valarr, ptype, va); + } while (1); + nskeyedarchive_set_class_property(ka, newuid, "NS.keys", NSTYPE_ARRAY, keyarr); + nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, valarr); + plist_free(keyarr); + plist_free(valarr); + } + break; + case NSTYPE_NSDATE: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSDate", "NSObject", NULL); + nskeyedarchive_set_class_property(ka, newuid, "NS.time", NSTYPE_REAL, va_arg(*va, double)); + break; + case NSTYPE_NSMUTABLEDATA: + case NSTYPE_NSDATA: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSMutableData", "NSData", "NSObject", NULL); + nskeyedarchive_set_class_property(ka, newuid, "NS.data", NSTYPE_DATA, va_arg(*va, char*)); + break; + case NSTYPE_NSURL: + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSURL", "NSObject", NULL); + { + int ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_set_class_property_v(ka, newuid, "NS.base", ptype, va); + ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_set_class_property_v(ka, newuid, "NS.relative", ptype, va); + } + break; + case NSTYPE_NSKEYEDARCHIVE: + { + nskeyedarchive_t pka = va_arg(*va, nskeyedarchive_t); + if (!pka) { + fprintf(stderr, "%s: ERROR: no nskeyedarchive argument given for type NSTYPE_NSKEYEDARCHIVE\n", __func__); + return; + } + uint64_t top = nskeyedarchive_get_class_uid(pka, NULL); + if (top != 0) { + plist_array_append_item(array, plist_new_uid(++ka->uid)); + + plist_t object = nskeyedarchive_get_object_by_uid(pka, top); + if (!object) { + fprintf(stderr, "%s: ERROR: can't get object for uid %lld\n", __func__, (long long)top); + return; + } + + plist_t objcopy = plist_copy(object); + nskeyedarchive_append_object(ka, objcopy); + nskeyedarchive_merge_object(ka, pka, objcopy); + } + } + break; + case NSTYPE_FROM_PLIST: + { + plist_t plist = va_arg(*va, plist_t); + if (!plist) { + fprintf(stderr, "%s: ERROR: no plist argument given for NSTYPE_PLIST\n", __func__); + return; + } + switch (plist_get_node_type(plist)) { + case PLIST_STRING: + { + char* str = NULL; + plist_get_string_val(plist, &str); + nskeyedarchive_nsarray_append(ka, array, NSTYPE_NSMUTABLESTRING, str, NULL); + } break; + case PLIST_DICT: + { + plist_array_append_item(array, plist_new_uid(++ka->uid)); + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSDictionary", "NSObject", NULL); + + plist_t keyarr = plist_new_array(); + plist_t valarr = plist_new_array(); + + plist_dict_iter iter = NULL; + plist_dict_new_iter(plist, &iter); + + char *key = NULL; + plist_t node = NULL; + + do { + plist_dict_next_item(plist, iter, &key, &node); + if (key) { + int ptype = 0; + uint8_t bv = 0; + uint64_t u64val = 0; + int intval = 0; + char *str = NULL; + void *val = NULL; + nskeyedarchive_nsarray_append(ka, keyarr, NSTYPE_STRING, key); + switch (plist_get_node_type(node)) { + case PLIST_BOOLEAN: { + ptype = NSTYPE_BOOLEAN; + plist_get_bool_val(node, &bv); + intval = bv; + val = &intval; + } break; + case PLIST_UINT: + ptype = NSTYPE_INTEGER; + plist_get_uint_val(node, &u64val); + val = &u64val; + break; + case PLIST_STRING: + ptype = NSTYPE_STRING; + plist_get_string_val(node, &str); + val = str; + break; + default: + fprintf(stderr, "Unhandled plist type when parsing plist_dict\n"); + break; + } + nskeyedarchive_nsarray_append(ka, valarr, ptype, val); + } + free(key); + } while (node); + + free(iter); + + nskeyedarchive_set_class_property(ka, newuid, "NS.keys", NSTYPE_ARRAY, keyarr); + nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, valarr); + plist_free(keyarr); + plist_free(valarr); + } break; + default: + fprintf(stderr, "%s: ERROR: unhandled plist type %d\n", __func__, plist_get_node_type(plist)); + return; + } + } + break; + default: + fprintf(stderr, "%s: unexpected type %d\n", __func__, type); + break; + } +} + +static void nskeyedarchive_nsarray_append(nskeyedarchive_t ka, plist_t array, enum nskeyedarchive_class_type_t type, ...) +{ + va_list va; + va_start(va, type); + nskeyedarchive_nsarray_append_v(ka, array, type, &va); + va_end(va); +} + +void nskeyedarchive_nsarray_append_item(nskeyedarchive_t ka, uint64_t uid, enum nskeyedarchive_class_type_t type, ...) +{ + if (!ka) { + return; + } + plist_t objects = NULL; + nskeyedarchive_get_class_property(ka, uid, "NS.objects", &objects); + if (!objects) { + fprintf(stderr, "ERROR: invalid NSArray object in archive: missing NS.objects property\n"); + return; + } + va_list va; + va_start(va, type); + nskeyedarchive_nsarray_append_v(ka, objects, type, &va); + va_end(va); +} + +void nskeyedarchive_nsdictionary_add_item(nskeyedarchive_t ka, uint64_t uid, const char* key, enum nskeyedarchive_class_type_t type, ...) +{ + if (!ka) { + return; + } + plist_t keys = NULL; + nskeyedarchive_get_class_property(ka, uid, "NS.keys", &keys); + if (!keys) { + fprintf(stderr, "ERROR: invalid NSDictionary object in archive: missing NS.keys property\n"); + return; + } + plist_t objects = NULL; + nskeyedarchive_get_class_property(ka, uid, "NS.objects", &objects); + if (!objects) { + fprintf(stderr, "ERROR: invalid NSDictionary object in archive: missing NS.objects property\n"); + return; + } + va_list va; + va_start(va, type); + nskeyedarchive_nsarray_append(ka, keys, NSTYPE_STRING, key); + nskeyedarchive_nsarray_append_v(ka, objects, type, &va); + va_end(va); +} + +void nskeyedarchive_append_class_type_v(nskeyedarchive_t ka, enum nskeyedarchive_class_type_t type, va_list* va) +{ + uint64_t newuid = 0; + + switch (type) { + case NSTYPE_INTEGER: + fprintf(stderr, "%s: ERROR: NSTYPE_INTEGER is not an object type, can't add it as class!.\n", __func__); + break; + case NSTYPE_INTREF: + nskeyedarchive_append_object(ka, plist_new_uint(va_arg(*va, int))); + break; + case NSTYPE_BOOLEAN: + nskeyedarchive_append_object(ka, plist_new_bool(va_arg(*va, int))); + break; + case NSTYPE_CHARS: + fprintf(stderr, "%s: ERROR: NSTYPE_CHARS is not an object type, can't add it as class!\n", __func__); + break; + case NSTYPE_STRING: + { + char* strval = va_arg(*va, char*); + if (strval) { + if (strcmp(strval, "$null") != 0) { + nskeyedarchive_append_object(ka, plist_new_string(strval)); + } else { + // make $null the top class if required + plist_t top = plist_dict_get_item(ka->dict, "$top"); + if (!top) { + top = plist_new_dict(); + plist_dict_set_item(top, "$0", plist_new_uid(0)); + plist_dict_set_item(ka->dict, "$top", top); + } + } + } + } + break; + case NSTYPE_REAL: + nskeyedarchive_append_object(ka, plist_new_real(va_arg(*va, double))); + break; + case NSTYPE_ARRAY: + fprintf(stderr, "%s: ERROR: NSTYPE_ARRAY is not an object type, can't add it as class!\n", __func__); + return; + case NSTYPE_NSMUTABLESTRING: + case NSTYPE_NSSTRING: + newuid = ka->uid; + if (type == NSTYPE_NSMUTABLESTRING) { + nskeyedarchive_append_class(ka, "NSMutableString", "NSString", "NSObject", NULL); + } else { + nskeyedarchive_append_class(ka, "NSString", "NSObject", NULL); + } + nskeyedarchive_set_class_property(ka, newuid, "NS.string", NSTYPE_STRING, va_arg(*va, char*)); + break; + case NSTYPE_NSMUTABLEARRAY: + case NSTYPE_NSARRAY: + newuid = ka->uid; + if (type == NSTYPE_NSMUTABLEARRAY) { + nskeyedarchive_append_class(ka, "NSMutableArray", "NSArray", "NSObject", NULL); + } else { + nskeyedarchive_append_class(ka, "NSArray", "NSObject", NULL); + } + { + plist_t arr = plist_new_array(); + do { + int ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_nsarray_append_v(ka, arr, ptype, va); + } while (1); + nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, arr); + plist_free(arr); + } + break; + case NSTYPE_NSMUTABLEDICTIONARY: + case NSTYPE_NSDICTIONARY: + newuid = ka->uid; + if (type == NSTYPE_NSMUTABLEDICTIONARY) { + nskeyedarchive_append_class(ka, "NSMutableDictionary", "NSDictionary", "NSObject", NULL); + } else { + nskeyedarchive_append_class(ka, "NSDictionary", "NSObject", NULL); + } + { + plist_t keyarr = plist_new_array(); + plist_t valarr = plist_new_array(); + do { + char* key = va_arg(*va, char*); + if (!key) + break; + int ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_nsarray_append(ka, keyarr, NSTYPE_STRING, key); + nskeyedarchive_nsarray_append_v(ka, valarr, ptype, va); + } while (1); + nskeyedarchive_set_class_property(ka, newuid, "NS.keys", NSTYPE_ARRAY, keyarr); + nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, valarr); + plist_free(keyarr); + plist_free(valarr); + } + break; + case NSTYPE_NSDATE: + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSDate", "NSObject", NULL); + nskeyedarchive_set_class_property(ka, newuid, "NS.time", NSTYPE_REAL, va_arg(*va, double)); + break; + case NSTYPE_NSMUTABLEDATA: + case NSTYPE_NSDATA: + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSMutableData", "NSData", "NSObject", NULL); + nskeyedarchive_set_class_property(ka, newuid, "NS.data", NSTYPE_DATA, va_arg(*va, char*)); + break; + case NSTYPE_NSURL: + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSURL", "NSObject", NULL); + { + int ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_set_class_property_v(ka, newuid, "NS.base", ptype, va); + ptype = va_arg(*va, int); + if (ptype == 0) + break; + nskeyedarchive_set_class_property_v(ka, newuid, "NS.relative", ptype, va); + } + break; + default: + fprintf(stderr, "unexpected class type %d\n", type); + return; + } + + plist_t top = plist_dict_get_item(ka->dict, "$top"); + if (!top) { + top = plist_new_dict(); + plist_dict_set_item(top, "$0", plist_new_uid(1)); + plist_dict_set_item(ka->dict, "$top", top); + } +} + +void nskeyedarchive_append_class_type(nskeyedarchive_t ka, enum nskeyedarchive_class_type_t type, ...) +{ + if (!ka) { + fprintf(stderr, "%s: ERROR: invalid keyed archive!\n", __func__); + return; + } else if (!type) { + fprintf(stderr, "%s: ERROR: invalid class type!\n", __func__); + return; + } + + va_list va; + va_start(va, type); + nskeyedarchive_append_class_type_v(ka, type, &va); + va_end(va); +} + +void nskeyedarchive_merge_object(nskeyedarchive_t ka, nskeyedarchive_t pka, plist_t object) +{ + if (!ka || !pka || !object) { + return; + } + + switch (plist_get_node_type(object)) { + case PLIST_DICT: + { + plist_dict_iter iter = NULL; + plist_dict_new_iter(object, &iter); + if (iter) { + plist_t val; + char* key; + do { + key = NULL; + val = NULL; + plist_dict_next_item(object, iter, &key, &val); + if (key) { + switch (plist_get_node_type(val)) { + case PLIST_UID: + { + uint64_t thisuid = 0; + plist_get_uid_val(val, &thisuid); + if (thisuid > 0) { + // remap thisuid to ka->uid+1 + plist_t nextobj = nskeyedarchive_get_object_by_uid(pka, thisuid); + plist_set_uid_val(val, ++ka->uid); + plist_t nextcopy = plist_copy(nextobj); + nskeyedarchive_append_object(ka, nextcopy); + nskeyedarchive_merge_object(ka, pka, nextcopy); + } + } + break; + case PLIST_DICT: + case PLIST_ARRAY: + nskeyedarchive_merge_object(ka, pka, val); + break; + default: + break; + } + free(key); + } + } while (val); + free(iter); + } + } + break; + case PLIST_ARRAY: + { + uint32_t i; + for (i = 0; i < plist_array_get_size(object); i++) { + plist_t val = plist_array_get_item(object, i); + switch (plist_get_node_type(val)) { + case PLIST_UID: + { + uint64_t thisuid = 0; + plist_get_uid_val(val, &thisuid); + if (thisuid > 0) { + // remap thisuid to ka->uid+1 + plist_t nextobj = nskeyedarchive_get_object_by_uid(pka, thisuid); + plist_set_uid_val(val, ++ka->uid); + plist_t nextcopy = plist_copy(nextobj); + nskeyedarchive_append_object(ka, nextcopy); + nskeyedarchive_merge_object(ka, pka, nextcopy); + } + } + break; + case PLIST_ARRAY: + case PLIST_DICT: + nskeyedarchive_merge_object(ka, pka, val); + break; + default: + break; + } + } + } + break; + default: + break; + } + +} + +static void nskeyedarchive_set_class_property_v(nskeyedarchive_t ka, uint64_t uid, const char* propname, enum nskeyedarchive_class_type_t proptype, va_list* va) +{ + plist_t a = nskeyedarchive_get_class_by_uid(ka, uid); + if (a == NULL) + return; + + uint64_t newuid; + + switch (proptype) { + case NSTYPE_INTEGER: + plist_dict_set_item(a, propname, plist_new_uint(va_arg(*va, int))); + break; + case NSTYPE_INTREF: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_uint(va_arg(*va, int))); + break; + case NSTYPE_BOOLEAN: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_bool(va_arg(*va, int))); + break; + case NSTYPE_CHARS: + plist_dict_set_item(a, propname, plist_new_string(va_arg(*va, char*))); + break; + case NSTYPE_STRING: + { + char* strval = va_arg(*va, char*); + if (strval && (strcmp(strval, "$null") == 0)) { + plist_dict_set_item(a, propname, plist_new_uid(0)); + } else { + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_object(ka, plist_new_string(strval)); + } + } + break; + case NSTYPE_REAL: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_ARRAY: + plist_dict_set_item(a, propname, plist_copy(va_arg(*va, plist_t))); + break; + case NSTYPE_NSMUTABLESTRING: + case NSTYPE_NSSTRING: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_NSMUTABLEARRAY: + case NSTYPE_NSARRAY: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_NSMUTABLEDICTIONARY: + case NSTYPE_NSDICTIONARY: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_NSDATE: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_NSMUTABLEDATA: + case NSTYPE_NSDATA: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_NSURL: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + nskeyedarchive_append_class_type_v(ka, proptype, va); + break; + case NSTYPE_NSKEYEDARCHIVE: + { + nskeyedarchive_t pka = va_arg(*va, nskeyedarchive_t); + if (!pka) { + fprintf(stderr, "%s: ERROR: no nskeyedarchive argument given for type NSTYPE_NSKEYEDARCHIVE\n", __func__); + return; + } + uint64_t top = nskeyedarchive_get_class_uid(pka, NULL); + if (top != 0) { + plist_t object = nskeyedarchive_get_object_by_uid(pka, top); + if (!object) { + fprintf(stderr, "%s: ERROR: can't get object for uid %lld\n", __func__, (long long)top); + return; + } + + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + plist_t objcopy = plist_copy(object); + nskeyedarchive_append_object(ka, objcopy); + nskeyedarchive_merge_object(ka, pka, objcopy); + } else { + plist_dict_set_item(a, propname, plist_new_uid(0)); + } + } + break; + case NSTYPE_FROM_PLIST: + { + plist_t plist = va_arg(*va, plist_t); + if (!plist) { + fprintf(stderr, "%s: ERROR: no plist argument given for NSTYPE_PLIST\n", __func__); + return; + } + switch (plist_get_node_type(plist)) { + case PLIST_ARRAY: + plist_dict_set_item(a, propname, plist_new_uid(++ka->uid)); + newuid = ka->uid; + nskeyedarchive_append_class(ka, "NSMutableArray", "NSArray", "NSObject", NULL); + { + plist_t arr = plist_new_array(); + uint32_t ai; + for (ai = 0; ai < plist_array_get_size(plist); ai++) { + plist_t ae = plist_array_get_item(plist, ai); + nskeyedarchive_nsarray_append(ka, arr, NSTYPE_FROM_PLIST, ae); + } + nskeyedarchive_set_class_property(ka, newuid, "NS.objects", NSTYPE_ARRAY, arr); + } + break; + default: + fprintf(stderr, "%s: sorry, plist type %d is not implemented for conversion.\n", __func__, plist_get_node_type(plist)); + break; + } + } + break; + default: + fprintf(stderr, "unexpected property type %d\n", proptype); + break; + } +} + +void nskeyedarchive_set_class_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, enum nskeyedarchive_class_type_t proptype, ...) +{ + plist_t a = nskeyedarchive_get_class_by_uid(ka, uid); + if (a == NULL) { + return; + } + + va_list va; + va_start(va, proptype); + nskeyedarchive_set_class_property_v(ka, uid, propname, proptype, &va); + va_end(va); +} + + +void nskeyedarchive_print(nskeyedarchive_t ka) +{ + char* xml = NULL; + uint32_t xlen = 0; + plist_to_xml(ka->dict, &xml, &xlen); + puts(xml); + free(xml); +} + +plist_t nskeyedarchive_get_plist_ref(nskeyedarchive_t ka) +{ + return ka->dict; +} + +nskeyedarchive_t nskeyedarchive_new_from_plist(plist_t plist) +{ + if (!plist || (plist_get_node_type(plist) != PLIST_DICT)) { + fprintf(stderr, "%s: ERROR: invalid parameter, PLIST_DICT expected\n", __func__); + return NULL; + } + + plist_t node; + char* strval = NULL; + uint64_t uintval = 0; + + // check for $archiver key + node = plist_dict_get_item(plist, "$archiver"); + if (node && (plist_get_node_type(node) == PLIST_STRING)) { + plist_get_string_val(node, &strval); + } + if (!strval || (strcmp(strval, "NSKeyedArchiver") != 0)) { + fprintf(stderr, "%s: ERROR: plist is not in NSKeyedArchiver format ($archiver key not found or invalid)!\n", __func__); + if (strval) + free(strval); + return NULL; + } + if (strval) + free(strval); + strval = NULL; + + // check for $version key + node = plist_dict_get_item(plist, "$version"); + if (node && (plist_get_node_type(node) == PLIST_UINT)) { + plist_get_uint_val(node, &uintval); + } + if (uintval != 100000) { + fprintf(stderr, "%s: ERROR: unexpected NSKeyedArchiver version encountered (%lld != 100000)!\n", __func__, (long long int)uintval); + return NULL; + } + uintval = 0; + + // check for $top key + node = plist_dict_get_item(plist, "$top"); + if (!node || (plist_get_node_type(node) != PLIST_DICT)) { + fprintf(stderr, "%s: ERROR: $top node not found\n", __func__); + return NULL; + } + plist_t topuid = plist_dict_get_item(node, "$0"); + if (!topuid) { + topuid = plist_dict_get_item(node, "root"); + } + if (!topuid || (plist_get_node_type(topuid) != PLIST_UID)) { + fprintf(stderr, "%s: ERROR: uid '$0' or 'root' not found in $top dict!\n", __func__); + return NULL; + } + + uintval = -1LL; + plist_get_uid_val(topuid, (uint64_t*)&uintval); + + if (uintval == (uint64_t)-1LL) { + fprintf(stderr, "%s: ERROR: could not get UID value.\n", __func__); + return NULL; + } + + // check for $objects key + plist_t objects = plist_dict_get_item(plist, "$objects"); + if (!objects || (plist_get_node_type(objects) != PLIST_ARRAY)) { + fprintf(stderr, "%s: ERROR: $objects node not found!\n", __func__); + return NULL; + } + plist_t obj = plist_array_get_item(objects, (uint32_t)uintval); + if (!obj) { + fprintf(stderr, "%s: ERROR: can't get object node\n", __func__); + return NULL; + } + + nskeyedarchive_t archive = (nskeyedarchive_t)malloc(sizeof(struct nskeyedarchive_st)); + archive->dict = plist_copy(plist); + archive->uid = plist_array_get_size(objects) - 1; + + return archive; +} + +nskeyedarchive_t nskeyedarchive_new_from_data(const void* data, uint32_t size) +{ + if (!data || (size < 8)) { + fprintf(stderr, "%s: ERROR: invalid parameter\n", __func__); + return NULL; + } + + + plist_t plist = NULL; + if (memcmp(data, "bplist00", 8) == 0) { + plist_from_bin(data, size, &plist); + } else if ((memcmp(data, "<?xml", 5) == 0) || (memcmp(data, "<plist", 6) == 0)) { + plist_from_xml(data, size, &plist); + } else { + // fail silently + return NULL; + } + if (!plist) { + fprintf(stderr, "%s: ERROR: Can't parse plist from data\n", __func__); + return NULL; + } + + nskeyedarchive_t ka = nskeyedarchive_new_from_plist(plist); + plist_free(plist); + return ka; +} + +uint64_t nskeyedarchive_get_class_uid(nskeyedarchive_t ka, const char* classref) +{ + uint64_t uintval = -1LL; + if (!ka) { + return uintval; + } + if (!ka->dict) { + return uintval; + } + + plist_t node; + + node = plist_dict_get_item(ka->dict, "$top"); + if (!node || (plist_get_node_type(node) != PLIST_DICT)) { + fprintf(stderr, "%s: ERROR: $top node not found\n", __func__); + return 0; + } + plist_t topuid = plist_dict_get_item(node, (classref) ? classref : "$0"); + if (!topuid && !classref) { + topuid = plist_dict_get_item(node, "root"); + } + if (!topuid || (plist_get_node_type(topuid) != PLIST_UID)) { + fprintf(stderr, "%s: ERROR: uid for '%s' not found in $top dict!\n", __func__, classref); + return 0; + } + + plist_get_uid_val(topuid, (uint64_t*)&uintval); + + return uintval; +} + +const char* nskeyedarchive_get_classname(nskeyedarchive_t ka, uint64_t uid) +{ + if (!ka) { + return NULL; + } + if (!ka->dict) { + return NULL; + } + + plist_t obj = nskeyedarchive_get_object_by_uid(ka, uid); + if (!obj) { + return NULL; + } + + plist_t classuid = plist_dict_get_item(obj, "$class"); + if (plist_get_node_type(classuid) != PLIST_UID) { + fprintf(stderr, "ERROR: $class is not a uid node\n"); + return NULL; + } + + uint64_t uintval = 0; + plist_get_uid_val(classuid, &uintval); + if (uintval == 0) { + fprintf(stderr, "ERROR: can't get $class uid val\n"); + return NULL; + } + + plist_t cls = nskeyedarchive_get_class_by_uid(ka, uintval); + if (!cls) { + return NULL; + } + + plist_t classname = plist_dict_get_item(cls, "$classname"); + if (!classname || (plist_get_node_type(classname) != PLIST_STRING)) { + fprintf(stderr, "ERROR: invalid $classname in class dict\n"); + return NULL; + } + + return plist_get_string_ptr(classname, NULL); +} + +int nskeyedarchive_get_class_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, plist_t* value) +{ + if (!ka || !ka->dict || !value) { + return -1; + } + + plist_t obj = nskeyedarchive_get_class_by_uid(ka, uid); + if (!obj) { + return -1; + } + + *value = plist_dict_get_item(obj, propname); + if (!*value) { + return -1; + } + + return 0; +} + +int nskeyedarchive_get_class_uint64_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, uint64_t* value) +{ + plist_t prop = NULL; + + nskeyedarchive_get_class_property(ka, uid, propname, &prop); + if (!prop) { + fprintf(stderr, "%s: ERROR: no such property '%s'\n", __func__, propname); + return -1; + } + + if (plist_get_node_type(prop) == PLIST_UID) { + uint64_t uintval = 0; + plist_get_uid_val(prop, &uintval); + prop = nskeyedarchive_get_object_by_uid(ka, uintval); + } + + if (plist_get_node_type(prop) != PLIST_UINT) { + fprintf(stderr, "%s: ERROR: property '%s' is not of type integer.\n", __func__, propname); + return -1; + } + + plist_get_uint_val(prop, value); + + return 0; +} + +int nskeyedarchive_get_class_int_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, int* value) +{ + uint64_t uintval = 0; + int res = nskeyedarchive_get_class_uint64_property(ka, uid, propname, &uintval); + if (res < 0) { + return res; + } + *value = (int)uintval; + return 0; +} + +int nskeyedarchive_get_class_string_property(nskeyedarchive_t ka, uint64_t uid, const char* propname, char** value) +{ + plist_t node = NULL; + + nskeyedarchive_get_class_property(ka, uid, propname, &node); + if (!node || (plist_get_node_type(node) != PLIST_UID)) { + return -1; + } + + uint64_t uintval = 0; + plist_get_uid_val(node, &uintval); + + plist_t prop = nskeyedarchive_get_object_by_uid(ka, uintval); + if (!prop || (plist_get_node_type(prop) != PLIST_STRING)) { + fprintf(stderr, "%s: ERROR: property '%s' is not a string\n", __func__, propname); + return -1; + } + + plist_get_string_val(prop, value); + + return 0; +} + +static plist_t _nska_parse_object(nskeyedarchive_t ka, uint64_t uid) +{ + plist_t obj = nskeyedarchive_get_object_by_uid(ka, uid); + switch (plist_get_node_type(obj)) { + case PLIST_BOOLEAN: + case PLIST_INT: + case PLIST_STRING: + return plist_copy(obj); + default: + break; + } + + const char* clsn = nskeyedarchive_get_classname(ka, uid); + plist_t pl = NULL; + if (!strcmp(clsn, "NSMutableDictionary") || !strcmp(clsn, "NSDictionary")) { + plist_t keys = NULL; + nskeyedarchive_get_class_property(ka, uid, "NS.keys", &keys); + plist_t vals = NULL; + nskeyedarchive_get_class_property(ka, uid, "NS.objects", &vals); + + uint32_t count = plist_array_get_size(keys); + if (count != plist_array_get_size(vals)) { + printf("ERROR: %s: inconsistent number of keys vs. values in dictionary object\n", __func__); + return NULL; + } + pl = plist_new_dict(); + uint32_t i = 0; + for (i = 0; i < count; i++) { + plist_t knode = plist_array_get_item(keys, i); + plist_t vnode = plist_array_get_item(vals, i); + + uint64_t subuid = 0; + plist_get_uid_val(knode, &subuid); + plist_t newkey = _nska_parse_object(ka, subuid); + + subuid = 0; + plist_get_uid_val(vnode, &subuid); + plist_t newval = _nska_parse_object(ka, subuid); + + if (!PLIST_IS_STRING(newkey)) { + printf("ERROR: %s: key node is not of string type.\n", __func__); + return NULL; + } + + plist_dict_set_item(pl, plist_get_string_ptr(newkey, NULL), newval); + plist_free(newkey); + } + } else if (!strcmp(clsn, "NSMutableArray") || !strcmp(clsn, "NSArray")) { + plist_t vals = NULL; + nskeyedarchive_get_class_property(ka, uid, "NS.objects", &vals); + + uint32_t count = plist_array_get_size(vals); + pl = plist_new_array(); + uint32_t i = 0; + for (i = 0; i < count; i++) { + plist_t vnode = plist_array_get_item(vals, i); + uint64_t subuid = 0; + plist_get_uid_val(vnode, &subuid); + plist_t newval = _nska_parse_object(ka, subuid); + plist_array_append_item(pl, newval); + } + } else { + printf("ERROR: %s: unhandled class type '%s'\n", __func__, clsn); + } + return pl; +} + +plist_t nskeyedarchive_to_plist(nskeyedarchive_t ka) +{ + uint64_t obj_uid = nskeyedarchive_get_class_uid(ka, NULL); + return _nska_parse_object(ka, obj_uid); +} diff --git a/src/opack.c b/src/opack.c index 9e7fa73..8a9264a 100644 --- a/src/opack.c +++ b/src/opack.c @@ -206,7 +206,7 @@ static void opack_encode_node(plist_t node, struct char_buf* cbuf) if (len >> 32) { uint8_t blen = 0x94; char_buf_append(cbuf, 1, &blen); - uint32_t u64val = htole64(len); + uint64_t u64val = htole64(len); char_buf_append(cbuf, 8, (unsigned char*)&u64val); } else { uint8_t blen = 0x93; @@ -237,7 +237,7 @@ static void opack_encode_node(plist_t node, struct char_buf* cbuf) } } -LIBIMOBILEDEVICE_GLUE_API void opack_encode_from_plist(plist_t plist, unsigned char** out, unsigned int* out_len) +void opack_encode_from_plist(plist_t plist, unsigned char** out, unsigned int* out_len) { if (!plist || !out || !out_len) { return; @@ -304,11 +304,11 @@ static int opack_decode_obj(unsigned char** p, unsigned char* end, plist_t* plis } else if (type == 0x32) { uint32_t u32val = *(uint32_t*)*p; value = (int32_t)le32toh(u32val); - (p)+=4; + (*p)+=4; } else if (type == 0x33) { uint64_t u64val = *(uint64_t*)*p; value = le64toh(u64val); - (p)+=8; + (*p)+=8; } else { fprintf(stderr, "%s: ERROR: Invalid encoded byte '%02x'\n", __func__, type); *p = end; @@ -462,7 +462,7 @@ static int opack_decode_obj(unsigned char** p, unsigned char* end, plist_t* plis return 0; } -LIBIMOBILEDEVICE_GLUE_API int opack_decode_to_plist(unsigned char* buf, unsigned int buf_len, plist_t* plist_out) +int opack_decode_to_plist(unsigned char* buf, unsigned int buf_len, plist_t* plist_out) { if (!buf || buf_len == 0 || !plist_out) { return -1; diff --git a/src/sha1.c b/src/sha1.c new file mode 100644 index 0000000..46bb8c0 --- /dev/null +++ b/src/sha1.c @@ -0,0 +1,517 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com + */ + +#include "common.h" +#include "libimobiledevice-glue/sha.h" + +#include "fixedint.h" + +#define ROLc(x, y) \ + ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | \ + (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define ROL ROLc + +#define STORE32H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \ + (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } + +#define LOAD32H(x, y) \ + { x = ((unsigned long)((y)[0] & 255)<<24) | \ + ((unsigned long)((y)[1] & 255)<<16) | \ + ((unsigned long)((y)[2] & 255)<<8) | \ + ((unsigned long)((y)[3] & 255)); } + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define F0(x,y,z) (z ^ (x & (y ^ z))) +#define F1(x,y,z) (x ^ y ^ z) +#define F2(x,y,z) ((x & y) | (z & (x | y))) +#define F3(x,y,z) (x ^ y ^ z) +#ifndef MIN + #define MIN(x, y) ( ((x)<(y))?(x):(y) ) +#endif + +static int sha1_compress(sha1_context *md, unsigned char *buf) +{ + uint32_t a,b,c,d,e,W[80],i; + uint32_t t; + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32H(W[i], buf + (4*i)); + } + /* copy state */ + a = md->state[0]; + b = md->state[1]; + c = md->state[2]; + d = md->state[3]; + e = md->state[4]; + /* expand it */ + for (i = 16; i < 80; i++) { + W[i] = ROL(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1); + } + /* compress */ + /* round one */ + #define FF0(a,b,c,d,e,i) e = (ROLc(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30); + #define FF1(a,b,c,d,e,i) e = (ROLc(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30); + #define FF2(a,b,c,d,e,i) e = (ROLc(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30); + #define FF3(a,b,c,d,e,i) e = (ROLc(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30); + + for (i = 0; i < 20; ) { + FF0(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t; + } + for (; i < 40; ) { + FF1(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t; + } + for (; i < 60; ) { + FF2(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t; + } + for (; i < 80; ) { + FF3(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t; + } + + #undef FF0 + #undef FF1 + #undef FF2 + #undef FF3 + + /* store */ + md->state[0] = md->state[0] + a; + md->state[1] = md->state[1] + b; + md->state[2] = md->state[2] + c; + md->state[3] = md->state[3] + d; + md->state[4] = md->state[4] + e; + return 0; +} + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return 0 if successful +*/ +int sha1_init(sha1_context * md) +{ + if (md == NULL) return 1; + md->state[0] = 0x67452301UL; + md->state[1] = 0xefcdab89UL; + md->state[2] = 0x98badcfeUL; + md->state[3] = 0x10325476UL; + md->state[4] = 0xc3d2e1f0UL; + md->curlen = 0; + md->length = 0; + return 0; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param data The data to hash + @param inlen The length of the data (octets) + @return 0 if successful +*/ +int sha1_update (sha1_context * md, const void *data, size_t inlen) +{ + const unsigned char* in = (const unsigned char*)data; + size_t n; + size_t i; + int err; + if (md == NULL) return 1; + if (in == NULL) return 1; + if (md->curlen > sizeof(md->buf)) { + return 1; + } + while (inlen > 0) { + if (md->curlen == 0 && inlen >= 64) { + if ((err = sha1_compress (md, (unsigned char *)in)) != 0) { + return err; + } + md->length += 64 * 8; + in += 64; + inlen -= 64; + } else { + n = MIN(inlen, (64 - md->curlen)); + + for (i = 0; i < n; i++) { + md->buf[i + md->curlen] = in[i]; + } + + + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == 64) { + if ((err = sha1_compress (md, md->buf)) != 0) { + return err; + } + md->length += 8*64; + md->curlen = 0; + } + } + } + return 0; +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (20 bytes) + @return 0 if successful +*/ +int sha1_final(sha1_context * md, unsigned char *out) +{ + int i; + if (md == NULL) return 1; + if (out == NULL) return 1; + if (md->curlen >= sizeof(md->buf)) { + return 1; + } + /* increase the length of the message */ + md->length += md->curlen * 8; + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char)0x80; + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 56) { + while (md->curlen < 64) { + md->buf[md->curlen++] = (unsigned char)0; + } + sha1_compress(md, md->buf); + md->curlen = 0; + } + /* pad upto 56 bytes of zeroes */ + while (md->curlen < 56) { + md->buf[md->curlen++] = (unsigned char)0; + } + /* store length */ + STORE64H(md->length, md->buf+56); + sha1_compress(md, md->buf); + /* copy output */ + for (i = 0; i < 5; i++) { + STORE32H(md->state[i], out+(4*i)); + } + return 0; +} + +int sha1(const unsigned char *message, size_t message_len, unsigned char *out) +{ + sha1_context ctx; + int ret; + if ((ret = sha1_init(&ctx))) return ret; + if ((ret = sha1_update(&ctx, message, message_len))) return ret; + if ((ret = sha1_final(&ctx, out))) return ret; + return 0; +} + +#if 0 + +/* +SHA-1 in C +By Steve Reid <steve@edmweb.com> +100% Public Domain +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#define SHA1HANDSOFF + +#include <stdio.h> +#include <string.h> + +/* for uint32_t */ +#include <stdint.h> + +#include "common.h" +#include "libimobiledevice-glue/sha.h" + + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#elif BYTE_ORDER == BIG_ENDIAN +#define blk0(i) block->l[i] +#else +#error "Endianness not defined!" +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +static void SHA1Transform( + uint32_t state[5], + const unsigned char buffer[64] +) +{ + uint32_t a, b, c, d, e; + + typedef union + { + unsigned char c[64]; + uint32_t l[16]; + } CHAR64LONG16; + +#ifdef SHA1HANDSOFF + CHAR64LONG16 block[1]; /* use array to appear as a pointer */ + + memcpy(block, buffer, 64); +#else + /* The following had better never be used because it causes the + * pointer-to-const buffer to be cast into a pointer to non-const. + * And the result is written through. I threw a "const" in, hoping + * this will cause a diagnostic. + */ + CHAR64LONG16 *block = (const CHAR64LONG16 *) buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a, b, c, d, e, 0); + R0(e, a, b, c, d, 1); + R0(d, e, a, b, c, 2); + R0(c, d, e, a, b, 3); + R0(b, c, d, e, a, 4); + R0(a, b, c, d, e, 5); + R0(e, a, b, c, d, 6); + R0(d, e, a, b, c, 7); + R0(c, d, e, a, b, 8); + R0(b, c, d, e, a, 9); + R0(a, b, c, d, e, 10); + R0(e, a, b, c, d, 11); + R0(d, e, a, b, c, 12); + R0(c, d, e, a, b, 13); + R0(b, c, d, e, a, 14); + R0(a, b, c, d, e, 15); + R1(e, a, b, c, d, 16); + R1(d, e, a, b, c, 17); + R1(c, d, e, a, b, 18); + R1(b, c, d, e, a, 19); + R2(a, b, c, d, e, 20); + R2(e, a, b, c, d, 21); + R2(d, e, a, b, c, 22); + R2(c, d, e, a, b, 23); + R2(b, c, d, e, a, 24); + R2(a, b, c, d, e, 25); + R2(e, a, b, c, d, 26); + R2(d, e, a, b, c, 27); + R2(c, d, e, a, b, 28); + R2(b, c, d, e, a, 29); + R2(a, b, c, d, e, 30); + R2(e, a, b, c, d, 31); + R2(d, e, a, b, c, 32); + R2(c, d, e, a, b, 33); + R2(b, c, d, e, a, 34); + R2(a, b, c, d, e, 35); + R2(e, a, b, c, d, 36); + R2(d, e, a, b, c, 37); + R2(c, d, e, a, b, 38); + R2(b, c, d, e, a, 39); + R3(a, b, c, d, e, 40); + R3(e, a, b, c, d, 41); + R3(d, e, a, b, c, 42); + R3(c, d, e, a, b, 43); + R3(b, c, d, e, a, 44); + R3(a, b, c, d, e, 45); + R3(e, a, b, c, d, 46); + R3(d, e, a, b, c, 47); + R3(c, d, e, a, b, 48); + R3(b, c, d, e, a, 49); + R3(a, b, c, d, e, 50); + R3(e, a, b, c, d, 51); + R3(d, e, a, b, c, 52); + R3(c, d, e, a, b, 53); + R3(b, c, d, e, a, 54); + R3(a, b, c, d, e, 55); + R3(e, a, b, c, d, 56); + R3(d, e, a, b, c, 57); + R3(c, d, e, a, b, 58); + R3(b, c, d, e, a, 59); + R4(a, b, c, d, e, 60); + R4(e, a, b, c, d, 61); + R4(d, e, a, b, c, 62); + R4(c, d, e, a, b, 63); + R4(b, c, d, e, a, 64); + R4(a, b, c, d, e, 65); + R4(e, a, b, c, d, 66); + R4(d, e, a, b, c, 67); + R4(c, d, e, a, b, 68); + R4(b, c, d, e, a, 69); + R4(a, b, c, d, e, 70); + R4(e, a, b, c, d, 71); + R4(d, e, a, b, c, 72); + R4(c, d, e, a, b, 73); + R4(b, c, d, e, a, 74); + R4(a, b, c, d, e, 75); + R4(e, a, b, c, d, 76); + R4(d, e, a, b, c, 77); + R4(c, d, e, a, b, 78); + R4(b, c, d, e, a, 79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +#ifdef SHA1HANDSOFF + memset(block, '\0', sizeof(block)); +#endif +} + + +/* SHA1Init - Initialize new context */ + +void sha1_init( + sha1_context * context +) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void sha1_update( + sha1_context * context, + const void *data, + size_t len +) +{ + size_t i; + + size_t j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1]++; + context->count[1] += (len >> 29); + j = (j >> 3) & 63; + if ((j + len) > 63) + { + memcpy(&context->buffer[j], data, (i = 64 - j)); + SHA1Transform(context->state, context->buffer); + for (; i + 63 < len; i += 64) + { + SHA1Transform(context->state, (unsigned char*)data + i); + } + j = 0; + } + else + i = 0; + memcpy(&context->buffer[j], (unsigned char*)data + i, len - i); +} + + +/* Add padding and return the message digest. */ + +void sha1_final( + sha1_context * context, + unsigned char digest[20] +) +{ + unsigned i; + + unsigned char finalcount[8]; + + unsigned char c; + +#if 0 /* untested "improvement" by DHR */ + /* Convert context->count to a sequence of bytes + * in finalcount. Second element first, but + * big-endian order within element. + * But we do it all backwards. + */ + unsigned char *fcp = &finalcount[8]; + + for (i = 0; i < 2; i++) + { + uint32_t t = context->count[i]; + + int j; + + for (j = 0; j < 4; t >>= 8, j++) + *--fcp = (unsigned char) t} +#else + for (i = 0; i < 8; i++) + { + finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ + } +#endif + c = 0200; + sha1_update(context, &c, 1); + while ((context->count[0] & 504) != 448) + { + c = 0000; + sha1_update(context, &c, 1); + } + sha1_update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) + { + digest[i] = (unsigned char) + ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); + } + /* Wipe variables */ + memset(context, '\0', sizeof(*context)); + memset(&finalcount, '\0', sizeof(finalcount)); +} + +void sha1( + const unsigned char *str, + size_t len, + unsigned char *hash_out +) +{ + sha1_context ctx; + size_t ii; + + sha1_init(&ctx); + for (ii=0; ii<len; ii+=1) + sha1_update(&ctx, str + ii, 1); + sha1_final(&ctx, hash_out); +} + + +#endif diff --git a/src/sha256.c b/src/sha256.c new file mode 100644 index 0000000..71be516 --- /dev/null +++ b/src/sha256.c @@ -0,0 +1,265 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com + */ + +#include "common.h" +#include "libimobiledevice-glue/sha.h" + +#include "fixedint.h" + +/* the K array */ +static const uint32_t K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Various logical functions */ + +#define RORc(x, y) \ + ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | \ + ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) + +#define STORE32H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \ + (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } + +#define LOAD32H(x, y) \ + { x = ((unsigned long)((y)[0] & 255)<<24) | \ + ((unsigned long)((y)[1] & 255)<<16) | \ + ((unsigned long)((y)[2] & 255)<<8) | \ + ((unsigned long)((y)[3] & 255)); } + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x),(n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) +#ifndef MIN + #define MIN(x, y) ( ((x)<(y))?(x):(y) ) +#endif + +/* compress 256-bits */ +static int sha256_compress(sha256_context * md, unsigned char *buf) +{ + uint32_t S[8], W[64], t0, t1; + uint32_t t; + int i; + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32H(W[i], buf + (4*i)); + } + /* fill W[16..63] */ + for (i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + /* Compress */ + #define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + for (i = 0; i < 64; ++i) { + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } + #undef RND + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + return 0; +} + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +int sha256_init(sha256_context * md) +{ + if (md == NULL) return 1; + md->curlen = 0; + md->length = 0; + md->state[0] = 0x6A09E667UL; + md->state[1] = 0xBB67AE85UL; + md->state[2] = 0x3C6EF372UL; + md->state[3] = 0xA54FF53AUL; + md->state[4] = 0x510E527FUL; + md->state[5] = 0x9B05688CUL; + md->state[6] = 0x1F83D9ABUL; + md->state[7] = 0x5BE0CD19UL; + md->num_dwords = 8; + return 0; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param data The data to hash + @param inlen The length of the data (octets) + @return 0 if successful +*/ +int sha256_update (sha256_context * md, const void *data, size_t inlen) +{ + const unsigned char* in = (const unsigned char*)data; + size_t n; + size_t i; + int err; + if (md == NULL) return 1; + if (in == NULL) return 1; + if (md->curlen > sizeof(md->buf)) { + return 1; + } + while (inlen > 0) { + if (md->curlen == 0 && inlen >= 64) { + if ((err = sha256_compress (md, (unsigned char *)in)) != 0) { + return err; + } + md->length += 64 * 8; + in += 64; + inlen -= 64; + } else { + n = MIN(inlen, (64 - md->curlen)); + + for (i = 0; i < n; i++) { + md->buf[i + md->curlen] = in[i]; + } + + + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == 64) { + if ((err = sha256_compress (md, md->buf)) != 0) { + return err; + } + md->length += 8*64; + md->curlen = 0; + } + } + } + return 0; +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (32 bytes) + @return 0 if successful +*/ +int sha256_final(sha256_context * md, unsigned char *out) +{ + int i; + if (md == NULL) return 1; + if (out == NULL) return 1; + if (md->curlen >= sizeof(md->buf)) { + return 1; + } + /* increase the length of the message */ + md->length += md->curlen * 8; + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char)0x80; + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 56) { + while (md->curlen < 64) { + md->buf[md->curlen++] = (unsigned char)0; + } + sha256_compress(md, md->buf); + md->curlen = 0; + } + /* pad upto 56 bytes of zeroes */ + while (md->curlen < 56) { + md->buf[md->curlen++] = (unsigned char)0; + } + /* store length */ + STORE64H(md->length, md->buf+56); + sha256_compress(md, md->buf); + /* copy output */ + for (i = 0; i < md->num_dwords; i++) { + STORE32H(md->state[i], out+(4*i)); + } + return 0; +} + +int sha256(const unsigned char *message, size_t message_len, unsigned char *out) +{ + sha256_context ctx; + int ret; + if ((ret = sha256_init(&ctx))) return ret; + if ((ret = sha256_update(&ctx, message, message_len))) return ret; + if ((ret = sha256_final(&ctx, out))) return ret; + return 0; +} + +int sha224_init(sha224_context * md) { + if (md == NULL) return 1; + + md->curlen = 0; + md->length = 0; + md->state[0] = 0xc1059ed8UL; + md->state[1] = 0x367cd507UL; + md->state[2] = 0x3070dd17UL; + md->state[3] = 0xf70e5939UL; + md->state[4] = 0xffc00b31UL; + md->state[5] = 0x68581511UL; + md->state[6] = 0x64f98fa7UL; + md->state[7] = 0xbefa4fa4UL; + md->num_dwords = 6; + + return 0; +} + +int sha224_update(sha224_context * md, const void *data, size_t inlen) +{ + return sha256_update(md, data, inlen); +} + +int sha224_final(sha224_context * md, unsigned char* out) +{ + return sha256_final(md, out); +} + +int sha224(const unsigned char *message, size_t message_len, unsigned char *out) +{ + sha224_context ctx; + int ret; + if ((ret = sha224_init(&ctx))) return ret; + if ((ret = sha224_update(&ctx, message, message_len))) return ret; + if ((ret = sha224_final(&ctx, out))) return ret; + return 0; +} diff --git a/src/sha512.c b/src/sha512.c new file mode 100644 index 0000000..62c7159 --- /dev/null +++ b/src/sha512.c @@ -0,0 +1,315 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include "fixedint.h" + +#include "common.h" +#include "libimobiledevice-glue/sha.h" + +/* the K array */ +static const uint64_t K[80] = { + UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), + UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), + UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), + UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), + UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), + UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), + UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), + UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), + UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), + UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), + UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), + UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), + UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), + UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), + UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), + UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), + UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), + UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), + UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), + UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), + UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), + UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), + UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), + UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), + UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), + UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), + UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), + UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), + UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), + UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), + UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), + UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), + UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), + UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), + UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), + UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), + UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), + UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), + UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), + UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) +}; + +/* Various logical functions */ + +#define ROR64c(x, y) \ + ( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)(y)&UINT64_C(63))) | \ + ((x)<<((uint64_t)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF)) + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y) \ + { x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \ + (((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \ + (((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \ + (((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); } + + +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR64c(x, n) +#define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n)) +#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) +#ifndef MIN + #define MIN(x, y) ( ((x)<(y))?(x):(y) ) +#endif + +/* compress 1024-bits */ +static int sha512_compress(sha512_context *md, unsigned char *buf) +{ + uint64_t S[8], W[80], t0, t1; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 1024-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD64H(W[i], buf + (8*i)); + } + + /* fill W[16..79] */ + for (i = 16; i < 80; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + + /* Compress */ + #define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c);\ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 80; i += 8) { + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); + } + + #undef RND + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + + return 0; +} + + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return 0 if successful +*/ +int sha512_init(sha512_context * md) { + if (md == NULL) return 1; + + md->curlen = 0; + md->length = 0; + md->state[0] = UINT64_C(0x6a09e667f3bcc908); + md->state[1] = UINT64_C(0xbb67ae8584caa73b); + md->state[2] = UINT64_C(0x3c6ef372fe94f82b); + md->state[3] = UINT64_C(0xa54ff53a5f1d36f1); + md->state[4] = UINT64_C(0x510e527fade682d1); + md->state[5] = UINT64_C(0x9b05688c2b3e6c1f); + md->state[6] = UINT64_C(0x1f83d9abfb41bd6b); + md->state[7] = UINT64_C(0x5be0cd19137e2179); + md->num_qwords = 8; + + return 0; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param data The data to hash + @param inlen The length of the data (octets) + @return 0 if successful +*/ +int sha512_update (sha512_context * md, const void *data, size_t inlen) +{ + const unsigned char* in = (const unsigned char*)data; + size_t n; + size_t i; + int err; + if (md == NULL) return 1; + if (in == NULL) return 1; + if (md->curlen > sizeof(md->buf)) { + return 1; + } + while (inlen > 0) { + if (md->curlen == 0 && inlen >= 128) { + if ((err = sha512_compress (md, (unsigned char *)in)) != 0) { + return err; + } + md->length += 128 * 8; + in += 128; + inlen -= 128; + } else { + n = MIN(inlen, (128 - md->curlen)); + + for (i = 0; i < n; i++) { + md->buf[i + md->curlen] = in[i]; + } + + + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == 128) { + if ((err = sha512_compress (md, md->buf)) != 0) { + return err; + } + md->length += 8*128; + md->curlen = 0; + } + } + } + return 0; +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (64 bytes) + @return 0 if successful +*/ +int sha512_final(sha512_context * md, unsigned char *out) +{ + int i; + + if (md == NULL) return 1; + if (out == NULL) return 1; + + if (md->curlen >= sizeof(md->buf)) { + return 1; + } + + /* increase the length of the message */ + md->length += md->curlen * UINT64_C(8); + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char)0x80; + + /* if the length is currently above 112 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 112) { + while (md->curlen < 128) { + md->buf[md->curlen++] = (unsigned char)0; + } + sha512_compress(md, md->buf); + md->curlen = 0; + } + + /* pad upto 120 bytes of zeroes + * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash + * > 2^64 bits of data... :-) + */ + while (md->curlen < 120) { + md->buf[md->curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64H(md->length, md->buf+120); + sha512_compress(md, md->buf); + + /* copy output */ + for (i = 0; i < md->num_qwords; i++) { + STORE64H(md->state[i], out+(8*i)); + } + + return 0; +} + +int sha512(const unsigned char *message, size_t message_len, unsigned char *out) +{ + sha512_context ctx; + int ret; + if ((ret = sha512_init(&ctx))) return ret; + if ((ret = sha512_update(&ctx, message, message_len))) return ret; + if ((ret = sha512_final(&ctx, out))) return ret; + return 0; +} + +int sha384_init(sha384_context * md) { + if (md == NULL) return 1; + + md->curlen = 0; + md->length = 0; + md->state[0] = UINT64_C(0xcbbb9d5dc1059ed8); + md->state[1] = UINT64_C(0x629a292a367cd507); + md->state[2] = UINT64_C(0x9159015a3070dd17); + md->state[3] = UINT64_C(0x152fecd8f70e5939); + md->state[4] = UINT64_C(0x67332667ffc00b31); + md->state[5] = UINT64_C(0x8eb44a8768581511); + md->state[6] = UINT64_C(0xdb0c2e0d64f98fa7); + md->state[7] = UINT64_C(0x47b5481dbefa4fa4); + md->num_qwords = 6; + + return 0; +} + +int sha384_update(sha384_context * md, const void *data, size_t inlen) +{ + return sha512_update(md, data, inlen); +} + +int sha384_final(sha384_context * md, unsigned char* out) +{ + return sha512_final(md, out); +} + +int sha384(const unsigned char *message, size_t message_len, unsigned char *out) +{ + sha384_context ctx; + int ret; + if ((ret = sha384_init(&ctx))) return ret; + if ((ret = sha384_update(&ctx, message, message_len))) return ret; + if ((ret = sha384_final(&ctx, out))) return ret; + return 0; +} diff --git a/src/socket.c b/src/socket.c index 1029a71..3375e5b 100644 --- a/src/socket.c +++ b/src/socket.c @@ -55,7 +55,7 @@ static int wsa_init = 0; #ifdef AF_INET6 #include <net/if.h> #include <ifaddrs.h> -#if defined (__APPLE__) || defined (__FreeBSD__) +#if defined (__APPLE__) || defined (__FreeBSD__) || defined (__HAIKU__) #include <net/if_dl.h> #endif #ifdef __linux__ @@ -65,6 +65,9 @@ static int wsa_init = 0; #endif #include "common.h" #include "libimobiledevice-glue/socket.h" +#ifdef HAVE_POLL +#include <sys/poll.h> +#endif #define RECV_TIMEOUT 20000 #define SEND_TIMEOUT 10000 @@ -82,12 +85,12 @@ static int wsa_init = 0; static int verbose = 0; -LIBIMOBILEDEVICE_GLUE_API void socket_set_verbose(int level) +void socket_set_verbose(int level) { verbose = level; } -LIBIMOBILEDEVICE_GLUE_API const char *socket_addr_to_string(struct sockaddr *addr, char *addr_out, size_t addr_out_size) +const char *socket_addr_to_string(struct sockaddr *addr, char *addr_out, size_t addr_out_size) { #ifdef WIN32 WSADATA wsa_data; @@ -140,8 +143,186 @@ LIBIMOBILEDEVICE_GLUE_API const char *socket_addr_to_string(struct sockaddr *add return NULL; } +enum poll_status +{ + poll_status_success, + poll_status_timeout, + poll_status_error +}; + +#ifdef WIN32 +static inline __attribute__((always_inline)) int WSAError_to_errno(int wsaerr) +{ + switch (wsaerr) { + case WSAEINVAL: + return EINVAL; + case WSAENOTSOCK: + return ENOTSOCK; + case WSAENOTCONN: + return ENOTCONN; + case WSAESHUTDOWN: + return ENOTCONN; + case WSAECONNRESET: + return ECONNRESET; + case WSAECONNABORTED: + return ECONNABORTED; + case WSAECONNREFUSED: + return ECONNREFUSED; + case WSAENETDOWN: + return ENETDOWN; + case WSAENETRESET: + return ENETRESET; + case WSAEHOSTUNREACH: + return EHOSTUNREACH; + case WSAETIMEDOUT: + return ETIMEDOUT; + case WSAEWOULDBLOCK: + return EWOULDBLOCK; + case WSAEINPROGRESS: + return EINPROGRESS; + case WSAENOBUFS: + return ENOBUFS; + case WSAEINTR: + return EINTR; + case WSAEACCES: + return EACCES; + case WSAEFAULT: + return EFAULT; + default: + break; + } + return wsaerr; +} +#endif + +// timeout of -1 means infinity +static inline __attribute__((always_inline)) enum poll_status poll_wrapper(int fd, fd_mode mode, int timeout) +{ +#ifdef HAVE_POLL + // https://man7.org/linux/man-pages/man2/select.2.html + // Correspondence between select() and poll() notifications + // #define POLLIN_SET (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN | + // EPOLLHUP | EPOLLERR) + // /* Ready for reading */ + // #define POLLOUT_SET (EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT | + // EPOLLERR) + // /* Ready for writing */ + // #define POLLEX_SET (EPOLLPRI) + // /* Exceptional condition */ + + short events; + switch (mode) { + case FDM_READ: + events = POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR; + break; + case FDM_WRITE: + events = POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR; + break; + case FDM_EXCEPT: + events = POLLPRI; + break; + default: + if (verbose >= 2) + fprintf(stderr, "%s: fd_mode %d unsupported\n", __func__, mode); + return poll_status_error; + } + while (1) { + struct pollfd pfd = { + .fd = fd, + .events = events, + }; + switch (poll(&pfd, 1, timeout)) { + case 1: + if((pfd.revents & (POLLNVAL | POLLERR)) != 0) + { + if (verbose >= 2) + fprintf(stderr, "%s: poll unexpected events: %d\n", __func__, (int)pfd.revents); + return poll_status_error; + } + return poll_status_success; + case 0: + return poll_status_timeout; + case -1: + if(errno == EINTR) + { + if (verbose >= 2) + fprintf(stderr, "%s: EINTR\n", __func__); + continue; + } + // fallthrough + default: + if (verbose >= 2) + fprintf(stderr, "%s: poll failed: %s\n", __func__, strerror(errno)); + return poll_status_error; + } + } +#else + fd_set fds; + int sret; + int eagain; + struct timeval to; + struct timeval *pto; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + sret = poll_status_error; + + do { + if (timeout > 0) { + to.tv_sec = (time_t) (timeout / 1000); + to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000); + pto = &to; + } else { + pto = NULL; + } + eagain = 0; + switch (mode) { + case FDM_READ: + sret = select(fd + 1, &fds, NULL, NULL, pto); + break; + case FDM_WRITE: + sret = select(fd + 1, NULL, &fds, NULL, pto); + break; + case FDM_EXCEPT: + sret = select(fd + 1, NULL, NULL, &fds, pto); + break; + default: + if (verbose >= 2) + fprintf(stderr, "%s: fd_mode %d unsupported\n", __func__, mode); + return poll_status_error; + } + + if (sret == 1) { + return poll_status_success; + } else if (sret == 0) { + return poll_status_timeout; + } else { + switch (errno) { + case EINTR: + // interrupt signal in select + if (verbose >= 2) + fprintf(stderr, "%s: EINTR\n", __func__); + eagain = 1; + break; + case EAGAIN: + if (verbose >= 2) + fprintf(stderr, "%s: EAGAIN\n", __func__); + break; + default: + if (verbose >= 2) + fprintf(stderr, "%s: select failed: %s\n", __func__, strerror(errno)); + return poll_status_error; + } + } + } while (eagain); + + return sret; +#endif +} + #ifndef WIN32 -LIBIMOBILEDEVICE_GLUE_API int socket_create_unix(const char *filename) +int socket_create_unix(const char *filename) { struct sockaddr_un name; int sock; @@ -187,7 +368,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_create_unix(const char *filename) return sock; } -LIBIMOBILEDEVICE_GLUE_API int socket_connect_unix(const char *filename) +int socket_connect_unix(const char *filename) { struct sockaddr_un name; int sfd = -1; @@ -246,20 +427,22 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_unix(const char *filename) break; } if (errno == EINPROGRESS) { - fd_set fds; - FD_ZERO(&fds); - FD_SET(sfd, &fds); - - struct timeval timeout; - timeout.tv_sec = CONNECT_TIMEOUT / 1000; - timeout.tv_usec = (CONNECT_TIMEOUT - (timeout.tv_sec * 1000)) * 1000; - if (select(sfd + 1, NULL, &fds, NULL, &timeout) == 1) { + if (poll_wrapper(sfd, FDM_WRITE, CONNECT_TIMEOUT) == poll_status_success) { int so_error; socklen_t len = sizeof(so_error); getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); if (so_error == 0) { + errno = 0; break; } + errno = so_error; + } else { + int so_error = 0; + socklen_t len = sizeof(so_error); + getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); + if (so_error != 0) { + errno = so_error; + } } } socket_close(sfd); @@ -276,10 +459,11 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_unix(const char *filename) } #endif -LIBIMOBILEDEVICE_GLUE_API int socket_create(const char* addr, uint16_t port) +int socket_create(const char* addr, uint16_t port) { int sfd = -1; int yes = 1; + int no = 0; struct addrinfo hints; struct addrinfo *result, *rp; char portstr[8]; @@ -303,9 +487,6 @@ LIBIMOBILEDEVICE_GLUE_API int socket_create(const char* addr, uint16_t port) sprintf(portstr, "%d", port); - if (!addr) { - addr = "localhost"; - } res = getaddrinfo(addr, portstr, &hints, &result); if (res != 0) { fprintf(stderr, "%s: getaddrinfo: %s\n", __func__, gai_strerror(res)); @@ -334,7 +515,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_create(const char* addr, uint16_t port) #if defined(AF_INET6) && defined(IPV6_V6ONLY) if (rp->ai_family == AF_INET6) { - if (setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&yes, sizeof(int)) == -1) { + if (setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, (addr) ? (void*)&yes : (void*)&no, sizeof(int)) == -1) { perror("setsockopt() IPV6_V6ONLY"); } } @@ -699,7 +880,7 @@ static int getifaddrs(struct ifaddrs** ifap) #endif #endif -LIBIMOBILEDEVICE_GLUE_API int get_primary_mac_address(unsigned char mac_addr_buf[6]) +int get_primary_mac_address(unsigned char mac_addr_buf[6]) { int result = -1; struct ifaddrs *ifaddr = NULL, *ifa = NULL; @@ -714,13 +895,13 @@ LIBIMOBILEDEVICE_GLUE_API int get_primary_mac_address(unsigned char mac_addr_buf if (ifa->ifa_flags & IFF_LOOPBACK) { continue; } -#if defined(__APPLE__) || defined (__FreeBSD__) +#if defined(__APPLE__) || defined (__FreeBSD__) || defined (__HAIKU__) if (ifa->ifa_addr->sa_family != AF_LINK) { continue; } #if defined (__APPLE__) if (!strcmp(ifa->ifa_name, "en0")) { -#elif defined (__FreeBSD__) +#elif defined (__FreeBSD__) || defined (__HAIKU__) { #endif memcpy(mac_addr_buf, (unsigned char *)LLADDR((struct sockaddr_dl *)(ifa)->ifa_addr), 6); @@ -742,6 +923,12 @@ LIBIMOBILEDEVICE_GLUE_API int get_primary_mac_address(unsigned char mac_addr_buf result = 0; break; } +#elif defined(__CYGWIN__) + if (ifa->ifa_data) { + memcpy(mac_addr_buf, ((struct ifaddrs_hwdata *)ifa->ifa_data)->ifa_hwaddr.sa_data, 6); + result = 0; + break; + } #else #error get_primary_mac_address is not supported on this platform. #endif @@ -787,10 +974,12 @@ static int32_t _sockaddr_in6_scope_id(struct sockaddr_in6* addr) continue; } +#ifndef __HAIKU__ /* skip if not running */ if ((ifa->ifa_flags & IFF_RUNNING) == 0) { continue; } +#endif struct sockaddr_in6* addr_in = (struct sockaddr_in6*)ifa->ifa_addr; @@ -840,7 +1029,7 @@ static int32_t _sockaddr_in6_scope_id(struct sockaddr_in6* addr) } #endif -LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_t port) +int socket_connect_addr(struct sockaddr* addr, uint16_t port) { int sfd = -1; int yes = 1; @@ -924,14 +1113,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_ if (errno == EINPROGRESS) #endif { - fd_set fds; - FD_ZERO(&fds); - FD_SET(sfd, &fds); - - struct timeval timeout; - timeout.tv_sec = CONNECT_TIMEOUT / 1000; - timeout.tv_usec = (CONNECT_TIMEOUT - (timeout.tv_sec * 1000)) * 1000; - if (select(sfd + 1, NULL, &fds, NULL, &timeout) == 1) { + if (poll_wrapper(sfd, FDM_WRITE, CONNECT_TIMEOUT) == poll_status_success) { int so_error; socklen_t len = sizeof(so_error); getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); @@ -939,7 +1121,20 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_ errno = 0; break; } +#ifdef WIN32 + so_error = WSAError_to_errno(so_error); +#endif errno = so_error; + } else { + int so_error = 0; + socklen_t len = sizeof(so_error); + getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); + if (so_error != 0) { +#ifdef WIN32 + so_error = WSAError_to_errno(so_error); +#endif + errno = so_error; + } } } socket_close(sfd); @@ -970,7 +1165,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_ return sfd; } -LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port) +int socket_connect(const char *addr, uint16_t port) { int sfd = -1; int yes = 1; @@ -993,11 +1188,6 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port) int flags = 0; #endif - if (!addr) { - errno = EINVAL; - return -1; - } - memset(&hints, '\0', sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; @@ -1048,20 +1238,28 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port) if (errno == EINPROGRESS) #endif { - fd_set fds; - FD_ZERO(&fds); - FD_SET(sfd, &fds); - - struct timeval timeout; - timeout.tv_sec = CONNECT_TIMEOUT / 1000; - timeout.tv_usec = (CONNECT_TIMEOUT - (timeout.tv_sec * 1000)) * 1000; - if (select(sfd + 1, NULL, &fds, NULL, &timeout) == 1) { + if (poll_wrapper(sfd, FDM_WRITE, CONNECT_TIMEOUT) == poll_status_success) { int so_error; socklen_t len = sizeof(so_error); getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); if (so_error == 0) { + errno = 0; break; } +#ifdef WIN32 + so_error = WSAError_to_errno(so_error); +#endif + errno = so_error; + } else { + int so_error = 0; + socklen_t len = sizeof(so_error); + getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len); + if (so_error != 0) { +#ifdef WIN32 + so_error = WSAError_to_errno(so_error); +#endif + errno = so_error; + } } } socket_close(sfd); @@ -1090,75 +1288,39 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port) return sfd; } -LIBIMOBILEDEVICE_GLUE_API int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout) +int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout) { - fd_set fds; - int sret; - int eagain; - struct timeval to; - struct timeval *pto; - if (fd < 0) { if (verbose >= 2) fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd); - return -1; + return -EINVAL; } - FD_ZERO(&fds); - FD_SET(fd, &fds); - - sret = -1; - - do { - if (timeout > 0) { - to.tv_sec = (time_t) (timeout / 1000); - to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000); - pto = &to; - } else { - pto = NULL; - } - eagain = 0; - switch (fdm) { - case FDM_READ: - sret = select(fd + 1, &fds, NULL, NULL, pto); - break; - case FDM_WRITE: - sret = select(fd + 1, NULL, &fds, NULL, pto); - break; - case FDM_EXCEPT: - sret = select(fd + 1, NULL, NULL, &fds, pto); - break; - default: - return -1; - } + int timeout_ms; + if (timeout > 0) { + timeout_ms = (int)timeout; + if (timeout_ms <= 0) + timeout_ms = -1; + } else { + timeout_ms = -1; + } - if (sret < 0) { - switch (errno) { - case EINTR: - // interrupt signal in select - if (verbose >= 2) - fprintf(stderr, "%s: EINTR\n", __func__); - eagain = 1; - break; - case EAGAIN: - if (verbose >= 2) - fprintf(stderr, "%s: EAGAIN\n", __func__); - break; - default: - if (verbose >= 2) - fprintf(stderr, "%s: select failed: %s\n", __func__, - strerror(errno)); - return -1; - } - } else if (sret == 0) { + switch (poll_wrapper(fd, fdm, timeout_ms)) { + case poll_status_success: + return 1; + case poll_status_timeout: return -ETIMEDOUT; - } - } while (eagain); + case poll_status_error: + default: + if (verbose >= 2) + fprintf(stderr, "%s: poll_wrapper failed\n", __func__); + return -ECONNRESET; + } - return sret; + return -ECONNRESET; } -LIBIMOBILEDEVICE_GLUE_API int socket_accept(int fd, uint16_t port) +int socket_accept(int fd, uint16_t port) { #ifdef WIN32 int addr_len; @@ -1174,12 +1336,12 @@ LIBIMOBILEDEVICE_GLUE_API int socket_accept(int fd, uint16_t port) return result; } -LIBIMOBILEDEVICE_GLUE_API int socket_shutdown(int fd, int how) +int socket_shutdown(int fd, int how) { return shutdown(fd, how); } -LIBIMOBILEDEVICE_GLUE_API int socket_close(int fd) { +int socket_close(int fd) { #ifdef WIN32 return closesocket(fd); #else @@ -1187,17 +1349,17 @@ LIBIMOBILEDEVICE_GLUE_API int socket_close(int fd) { #endif } -LIBIMOBILEDEVICE_GLUE_API int socket_receive(int fd, void *data, size_t length) +int socket_receive(int fd, void *data, size_t length) { return socket_receive_timeout(fd, data, length, 0, RECV_TIMEOUT); } -LIBIMOBILEDEVICE_GLUE_API int socket_peek(int fd, void *data, size_t length) +int socket_peek(int fd, void *data, size_t length) { return socket_receive_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT); } -LIBIMOBILEDEVICE_GLUE_API int socket_receive_timeout(int fd, void *data, size_t length, int flags, unsigned int timeout) +int socket_receive_timeout(int fd, void *data, size_t length, int flags, unsigned int timeout) { int res; int result; @@ -1209,19 +1371,22 @@ LIBIMOBILEDEVICE_GLUE_API int socket_receive_timeout(int fd, void *data, size_t } // if we get here, there _is_ data available result = recv(fd, data, length, flags); - if (res > 0 && result == 0) { + if (result == 0) { // but this is an error condition if (verbose >= 3) fprintf(stderr, "%s: fd=%d recv returned 0\n", __func__, fd); return -ECONNRESET; } if (result < 0) { +#ifdef WIN32 + errno = WSAError_to_errno(WSAGetLastError()); +#endif return -errno; } return result; } -LIBIMOBILEDEVICE_GLUE_API int socket_send(int fd, void *data, size_t length) +int socket_send(int fd, void *data, size_t length) { int flags = 0; int res = socket_check_fd(fd, FDM_WRITE, SEND_TIMEOUT); @@ -1231,10 +1396,17 @@ LIBIMOBILEDEVICE_GLUE_API int socket_send(int fd, void *data, size_t length) #ifdef MSG_NOSIGNAL flags |= MSG_NOSIGNAL; #endif - return send(fd, data, length, flags); + int s = (int)send(fd, data, length, flags); + if (s < 0) { +#ifdef WIN32 + errno = WSAError_to_errno(WSAGetLastError()); +#endif + return -errno; + } + return s; } -LIBIMOBILEDEVICE_GLUE_API int socket_get_socket_port(int fd, uint16_t *port) +int socket_get_socket_port(int fd, uint16_t *port) { #ifdef WIN32 int addr_len; diff --git a/src/termcolors.c b/src/termcolors.c index 710c9df..5c436f2 100644 --- a/src/termcolors.c +++ b/src/termcolors.c @@ -92,7 +92,7 @@ static int bgcolor_map[8] = { static int WIN32_LEGACY_MODE = 0; #endif -LIBIMOBILEDEVICE_GLUE_API void term_colors_init() +void term_colors_init() { #ifdef WIN32 DWORD conmode = 0; @@ -126,12 +126,12 @@ LIBIMOBILEDEVICE_GLUE_API void term_colors_init() } } -LIBIMOBILEDEVICE_GLUE_API void term_colors_set_enabled(int en) +void term_colors_set_enabled(int en) { use_colors = en; } -LIBIMOBILEDEVICE_GLUE_API int cvfprintf(FILE* stream, const char* fmt, va_list vargs) +int cvfprintf(FILE* stream, const char* fmt, va_list vargs) { int res = 0; int colorize = use_colors; @@ -298,7 +298,7 @@ LIBIMOBILEDEVICE_GLUE_API int cvfprintf(FILE* stream, const char* fmt, va_list v return res; } -LIBIMOBILEDEVICE_GLUE_API int cfprintf(FILE* stream, const char* fmt, ...) +int cfprintf(FILE* stream, const char* fmt, ...) { int res = 0; va_list va; @@ -308,7 +308,7 @@ LIBIMOBILEDEVICE_GLUE_API int cfprintf(FILE* stream, const char* fmt, ...) return res; } -LIBIMOBILEDEVICE_GLUE_API int cprintf(const char* fmt, ...) +int cprintf(const char* fmt, ...) { int res = 0; va_list va; diff --git a/src/thread.c b/src/thread.c index 6efacec..50639dd 100644 --- a/src/thread.c +++ b/src/thread.c @@ -22,13 +22,16 @@ #ifdef HAVE_CONFIG_H #include <config.h> #endif +#ifdef WIN32 +#include <windows.h> +#endif #include "common.h" #include "libimobiledevice-glue/thread.h" -LIBIMOBILEDEVICE_GLUE_API int thread_new(THREAD_T *thread, thread_func_t thread_func, void* data) +int thread_new(THREAD_T *thread, thread_func_t thread_func, void* data) { #ifdef WIN32 - HANDLE th = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_func, data, 0, NULL); + HANDLE th = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(void*)thread_func, data, 0, NULL); if (th == NULL) { return -1; } @@ -40,7 +43,7 @@ LIBIMOBILEDEVICE_GLUE_API int thread_new(THREAD_T *thread, thread_func_t thread_ #endif } -LIBIMOBILEDEVICE_GLUE_API void thread_detach(THREAD_T thread) +void thread_detach(THREAD_T thread) { #ifdef WIN32 CloseHandle(thread); @@ -49,14 +52,14 @@ LIBIMOBILEDEVICE_GLUE_API void thread_detach(THREAD_T thread) #endif } -LIBIMOBILEDEVICE_GLUE_API void thread_free(THREAD_T thread) +void thread_free(THREAD_T thread) { #ifdef WIN32 CloseHandle(thread); #endif } -LIBIMOBILEDEVICE_GLUE_API int thread_join(THREAD_T thread) +int thread_join(THREAD_T thread) { /* wait for thread to complete */ #ifdef WIN32 @@ -66,7 +69,7 @@ LIBIMOBILEDEVICE_GLUE_API int thread_join(THREAD_T thread) #endif } -LIBIMOBILEDEVICE_GLUE_API int thread_alive(THREAD_T thread) +int thread_alive(THREAD_T thread) { if (!thread) return 0; @@ -77,7 +80,7 @@ LIBIMOBILEDEVICE_GLUE_API int thread_alive(THREAD_T thread) #endif } -LIBIMOBILEDEVICE_GLUE_API int thread_cancel(THREAD_T thread) +int thread_cancel(THREAD_T thread) { #ifdef WIN32 return -1; @@ -90,43 +93,43 @@ LIBIMOBILEDEVICE_GLUE_API int thread_cancel(THREAD_T thread) #endif } -LIBIMOBILEDEVICE_GLUE_API void mutex_init(mutex_t* mutex) +void mutex_init(mutex_t* mutex) { #ifdef WIN32 - InitializeCriticalSection(mutex); + InitializeCriticalSection((LPCRITICAL_SECTION)mutex); #else pthread_mutex_init(mutex, NULL); #endif } -LIBIMOBILEDEVICE_GLUE_API void mutex_destroy(mutex_t* mutex) +void mutex_destroy(mutex_t* mutex) { #ifdef WIN32 - DeleteCriticalSection(mutex); + DeleteCriticalSection((LPCRITICAL_SECTION)mutex); #else pthread_mutex_destroy(mutex); #endif } -LIBIMOBILEDEVICE_GLUE_API void mutex_lock(mutex_t* mutex) +void mutex_lock(mutex_t* mutex) { #ifdef WIN32 - EnterCriticalSection(mutex); + EnterCriticalSection((LPCRITICAL_SECTION)mutex); #else pthread_mutex_lock(mutex); #endif } -LIBIMOBILEDEVICE_GLUE_API void mutex_unlock(mutex_t* mutex) +void mutex_unlock(mutex_t* mutex) { #ifdef WIN32 - LeaveCriticalSection(mutex); + LeaveCriticalSection((LPCRITICAL_SECTION)mutex); #else pthread_mutex_unlock(mutex); #endif } -LIBIMOBILEDEVICE_GLUE_API void thread_once(thread_once_t *once_control, void (*init_routine)(void)) +void thread_once(thread_once_t *once_control, void (*init_routine)(void)) { #ifdef WIN32 while (InterlockedExchange(&(once_control->lock), 1) != 0) { @@ -142,7 +145,7 @@ LIBIMOBILEDEVICE_GLUE_API void thread_once(thread_once_t *once_control, void (*i #endif } -LIBIMOBILEDEVICE_GLUE_API void cond_init(cond_t* cond) +void cond_init(cond_t* cond) { #ifdef WIN32 cond->sem = CreateSemaphore(NULL, 0, 32767, NULL); @@ -151,7 +154,7 @@ LIBIMOBILEDEVICE_GLUE_API void cond_init(cond_t* cond) #endif } -LIBIMOBILEDEVICE_GLUE_API void cond_destroy(cond_t* cond) +void cond_destroy(cond_t* cond) { #ifdef WIN32 CloseHandle(cond->sem); @@ -160,7 +163,7 @@ LIBIMOBILEDEVICE_GLUE_API void cond_destroy(cond_t* cond) #endif } -LIBIMOBILEDEVICE_GLUE_API int cond_signal(cond_t* cond) +int cond_signal(cond_t* cond) { #ifdef WIN32 int result = 0; @@ -173,7 +176,7 @@ LIBIMOBILEDEVICE_GLUE_API int cond_signal(cond_t* cond) #endif } -LIBIMOBILEDEVICE_GLUE_API int cond_wait(cond_t* cond, mutex_t* mutex) +int cond_wait(cond_t* cond, mutex_t* mutex) { #ifdef WIN32 mutex_unlock(mutex); @@ -189,7 +192,7 @@ LIBIMOBILEDEVICE_GLUE_API int cond_wait(cond_t* cond, mutex_t* mutex) #endif } -LIBIMOBILEDEVICE_GLUE_API int cond_wait_timeout(cond_t* cond, mutex_t* mutex, unsigned int timeout_ms) +int cond_wait_timeout(cond_t* cond, mutex_t* mutex, unsigned int timeout_ms) { #ifdef WIN32 mutex_unlock(mutex); @@ -32,7 +32,7 @@ #include "libimobiledevice-glue/tlv.h" #include "endianness.h" -LIBIMOBILEDEVICE_GLUE_API tlv_buf_t tlv_buf_new() +tlv_buf_t tlv_buf_new() { tlv_buf_t tlv = malloc(sizeof(struct tlv_buf)); tlv->capacity = 1024; @@ -41,7 +41,7 @@ LIBIMOBILEDEVICE_GLUE_API tlv_buf_t tlv_buf_new() return tlv; } -LIBIMOBILEDEVICE_GLUE_API void tlv_buf_free(tlv_buf_t tlv) +void tlv_buf_free(tlv_buf_t tlv) { if (tlv) { free(tlv->data); @@ -49,7 +49,7 @@ LIBIMOBILEDEVICE_GLUE_API void tlv_buf_free(tlv_buf_t tlv) } } -LIBIMOBILEDEVICE_GLUE_API void tlv_buf_append(tlv_buf_t tlv, uint8_t tag, unsigned int length, void* data) +void tlv_buf_append(tlv_buf_t tlv, uint8_t tag, unsigned int length, void* data) { if (!tlv || !tlv->data) { return; @@ -86,7 +86,7 @@ LIBIMOBILEDEVICE_GLUE_API void tlv_buf_append(tlv_buf_t tlv, uint8_t tag, unsign tlv->length = p - tlv->data; } -LIBIMOBILEDEVICE_GLUE_API unsigned char* tlv_get_data_ptr(const void* tlv_data, void* tlv_end, uint8_t tag, uint8_t* length) +unsigned char* tlv_get_data_ptr(const void* tlv_data, void* tlv_end, uint8_t tag, uint8_t* length) { unsigned char* p = (unsigned char*)tlv_data; unsigned char* end = (unsigned char*)tlv_end; @@ -102,7 +102,7 @@ LIBIMOBILEDEVICE_GLUE_API unsigned char* tlv_get_data_ptr(const void* tlv_data, return NULL; } -LIBIMOBILEDEVICE_GLUE_API int tlv_data_get_uint(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint64_t* value) +int tlv_data_get_uint(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint64_t* value) { if (!tlv_data || tlv_length < 2 || !value) { return 0; @@ -136,7 +136,7 @@ LIBIMOBILEDEVICE_GLUE_API int tlv_data_get_uint(const void* tlv_data, unsigned i return 1; } -LIBIMOBILEDEVICE_GLUE_API int tlv_data_get_uint8(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint8_t* value) +int tlv_data_get_uint8(const void* tlv_data, unsigned int tlv_length, uint8_t tag, uint8_t* value) { if (!tlv_data || tlv_length < 2 || !value) { return 0; @@ -156,7 +156,7 @@ LIBIMOBILEDEVICE_GLUE_API int tlv_data_get_uint8(const void* tlv_data, unsigned return 1; } -LIBIMOBILEDEVICE_GLUE_API int tlv_data_copy_data(const void* tlv_data, unsigned int tlv_length, uint8_t tag, void** out, unsigned int* out_len) +int tlv_data_copy_data(const void* tlv_data, unsigned int tlv_length, uint8_t tag, void** out, unsigned int* out_len) { if (!tlv_data || tlv_length < 2 || !out || !out_len) { return 0; diff --git a/src/utils.c b/src/utils.c index 9baef7e..ad8e9c4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -3,7 +3,7 @@ * Miscellaneous utilities for string manipulation, * file I/O and plist helper. * - * Copyright (c) 2014-2019 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2014-2023 Nikias Bassen, All Rights Reserved. * Copyright (c) 2013-2014 Martin Szulecki, All Rights Reserved. * Copyright (c) 2013 Federico Mena Quintero * @@ -75,7 +75,7 @@ char *stpcpy(char *s1, const char *s2) * @return a newly allocated string, or NULL if @str is NULL. This will also * return NULL and set errno to ENOMEM if memory is exhausted. */ -LIBIMOBILEDEVICE_GLUE_API char *string_concat(const char *str, ...) +char *string_concat(const char *str, ...) { size_t len; va_list args; @@ -119,7 +119,7 @@ LIBIMOBILEDEVICE_GLUE_API char *string_concat(const char *str, ...) return result; } -LIBIMOBILEDEVICE_GLUE_API char *string_append(char* str, ...) +char *string_append(char* str, ...) { size_t len = 0; size_t slen; @@ -163,7 +163,7 @@ LIBIMOBILEDEVICE_GLUE_API char *string_append(char* str, ...) return result; } -LIBIMOBILEDEVICE_GLUE_API char *string_build_path(const char *elem, ...) +char *string_build_path(const char *elem, ...) { if (!elem) return NULL; @@ -191,7 +191,7 @@ LIBIMOBILEDEVICE_GLUE_API char *string_build_path(const char *elem, ...) return out; } -LIBIMOBILEDEVICE_GLUE_API char *string_format_size(uint64_t size) +char *string_format_size(uint64_t size) { char buf[80]; double sz; @@ -213,7 +213,7 @@ LIBIMOBILEDEVICE_GLUE_API char *string_format_size(uint64_t size) return strdup(buf); } -LIBIMOBILEDEVICE_GLUE_API char *string_toupper(char* str) +char *string_toupper(char* str) { char *res = strdup(str); size_t i; @@ -229,7 +229,7 @@ static int get_rand(int min, int max) return retval; } -LIBIMOBILEDEVICE_GLUE_API char *generate_uuid() +char *generate_uuid() { const char *chars = "ABCDEF0123456789"; int i = 0; @@ -251,7 +251,7 @@ LIBIMOBILEDEVICE_GLUE_API char *generate_uuid() return uuid; } -LIBIMOBILEDEVICE_GLUE_API int buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length) +int buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length) { FILE *f; uint64_t size; @@ -295,7 +295,7 @@ LIBIMOBILEDEVICE_GLUE_API int buffer_read_from_filename(const char *filename, ch return ret; } -LIBIMOBILEDEVICE_GLUE_API int buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length) +int buffer_write_to_filename(const char *filename, const char *buffer, uint64_t length) { FILE *f; @@ -318,241 +318,3 @@ LIBIMOBILEDEVICE_GLUE_API int buffer_write_to_filename(const char *filename, con return 0; } } - -LIBIMOBILEDEVICE_GLUE_API int plist_read_from_filename(plist_t *plist, const char *filename) -{ - char *buffer = NULL; - uint64_t length; - - if (!filename) - return 0; - - if (!buffer_read_from_filename(filename, &buffer, &length)) { - return 0; - } - - plist_from_memory(buffer, length, plist); - - free(buffer); - - return 1; -} - -LIBIMOBILEDEVICE_GLUE_API int plist_write_to_filename(plist_t plist, const char *filename, enum plist_format_t format) -{ - char *buffer = NULL; - uint32_t length; - - if (!plist || !filename) - return 0; - - if (format == PLIST_FORMAT_XML) - plist_to_xml(plist, &buffer, &length); - else if (format == PLIST_FORMAT_BINARY) - plist_to_bin(plist, &buffer, &length); - else - return 0; - - int res = buffer_write_to_filename(filename, buffer, length); - - free(buffer); - - return res; -} - -static const char base64_str[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char base64_pad = '='; - -static char *base64encode(const unsigned char *buf, size_t size) -{ - if (!buf || !(size > 0)) return NULL; - int outlen = (size / 3) * 4; - char *outbuf = (char*)malloc(outlen+5); // 4 spare bytes + 1 for '\0' - size_t n = 0; - size_t m = 0; - unsigned char input[3]; - unsigned int output[4]; - while (n < size) { - input[0] = buf[n]; - input[1] = (n+1 < size) ? buf[n+1] : 0; - input[2] = (n+2 < size) ? buf[n+2] : 0; - output[0] = input[0] >> 2; - output[1] = ((input[0] & 3) << 4) + (input[1] >> 4); - output[2] = ((input[1] & 15) << 2) + (input[2] >> 6); - output[3] = input[2] & 63; - outbuf[m++] = base64_str[(int)output[0]]; - outbuf[m++] = base64_str[(int)output[1]]; - outbuf[m++] = (n+1 < size) ? base64_str[(int)output[2]] : base64_pad; - outbuf[m++] = (n+2 < size) ? base64_str[(int)output[3]] : base64_pad; - n+=3; - } - outbuf[m] = 0; // 0-termination! - return outbuf; -} - -static void plist_node_print_to_stream(plist_t node, int* indent_level, FILE* stream); - -static void plist_array_print_to_stream(plist_t node, int* indent_level, FILE* stream) -{ - /* iterate over items */ - int i, count; - plist_t subnode = NULL; - - count = plist_array_get_size(node); - - for (i = 0; i < count; i++) { - subnode = plist_array_get_item(node, i); - fprintf(stream, "%*s", *indent_level, ""); - fprintf(stream, "%d: ", i); - plist_node_print_to_stream(subnode, indent_level, stream); - } -} - -static void plist_dict_print_to_stream(plist_t node, int* indent_level, FILE* stream) -{ - /* iterate over key/value pairs */ - plist_dict_iter it = NULL; - - char* key = NULL; - plist_t subnode = NULL; - plist_dict_new_iter(node, &it); - plist_dict_next_item(node, it, &key, &subnode); - while (subnode) - { - fprintf(stream, "%*s", *indent_level, ""); - fprintf(stream, "%s", key); - if (plist_get_node_type(subnode) == PLIST_ARRAY) - fprintf(stream, "[%d]: ", plist_array_get_size(subnode)); - else - fprintf(stream, ": "); - free(key); - key = NULL; - plist_node_print_to_stream(subnode, indent_level, stream); - plist_dict_next_item(node, it, &key, &subnode); - } - free(it); -} - -static void plist_node_print_to_stream(plist_t node, int* indent_level, FILE* stream) -{ - char *s = NULL; - char *data = NULL; - double d; - uint8_t b; - uint64_t u = 0; - struct timeval tv = { 0, 0 }; - - plist_type t; - - if (!node) - return; - - t = plist_get_node_type(node); - - switch (t) { - case PLIST_BOOLEAN: - plist_get_bool_val(node, &b); - fprintf(stream, "%s\n", (b ? "true" : "false")); - break; - - case PLIST_UINT: - plist_get_uint_val(node, &u); - fprintf(stream, "%"PRIu64"\n", u); - break; - - case PLIST_REAL: - plist_get_real_val(node, &d); - fprintf(stream, "%f\n", d); - break; - - case PLIST_STRING: - plist_get_string_val(node, &s); - fprintf(stream, "%s\n", s); - free(s); - break; - - case PLIST_KEY: - plist_get_key_val(node, &s); - fprintf(stream, "%s: ", s); - free(s); - break; - - case PLIST_DATA: - plist_get_data_val(node, &data, &u); - if (u > 0) { - s = base64encode((unsigned char*)data, u); - free(data); - if (s) { - fprintf(stream, "%s\n", s); - free(s); - } else { - fprintf(stream, "\n"); - } - } else { - fprintf(stream, "\n"); - } - break; - - case PLIST_DATE: - plist_get_date_val(node, (int32_t*)&tv.tv_sec, (int32_t*)&tv.tv_usec); - { - time_t ti = (time_t)tv.tv_sec + MAC_EPOCH; - struct tm *btime = localtime(&ti); - if (btime) { - s = (char*)malloc(24); - memset(s, 0, 24); - if (strftime(s, 24, "%Y-%m-%dT%H:%M:%SZ", btime) <= 0) { - free (s); - s = NULL; - } - } - } - if (s) { - fprintf(stream, "%s\n", s); - free(s); - } else { - fprintf(stream, "\n"); - } - break; - - case PLIST_ARRAY: - fprintf(stream, "\n"); - (*indent_level)++; - plist_array_print_to_stream(node, indent_level, stream); - (*indent_level)--; - break; - - case PLIST_DICT: - fprintf(stream, "\n"); - (*indent_level)++; - plist_dict_print_to_stream(node, indent_level, stream); - (*indent_level)--; - break; - - default: - break; - } -} - -LIBIMOBILEDEVICE_GLUE_API void plist_print_to_stream_with_indentation(plist_t plist, FILE* stream, unsigned int indentation) -{ - if (!plist || !stream) - return; - - int indent = indentation; - switch (plist_get_node_type(plist)) { - case PLIST_DICT: - plist_dict_print_to_stream(plist, &indent, stream); - break; - case PLIST_ARRAY: - plist_array_print_to_stream(plist, &indent, stream); - break; - default: - plist_node_print_to_stream(plist, &indent, stream); - } -} - -LIBIMOBILEDEVICE_GLUE_API void plist_print_to_stream(plist_t plist, FILE* stream) -{ - plist_print_to_stream_with_indentation(plist, stream, 0); -} |