summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Zach C2008-07-29 01:11:02 -0700
committerGravatar Matt Colyer2008-07-29 01:11:02 -0700
commite2ff1128351d75eafd5426af7f96f9719c1af3e6 (patch)
treec1c460b4de78cd5645d6d12e83d2646f56f30363
downloadlibplist-e2ff1128351d75eafd5426af7f96f9719c1af3e6.tar.gz
libplist-e2ff1128351d75eafd5426af7f96f9719c1af3e6.tar.bz2
First released version, 0.089.
-rw-r--r--AFC.c279
-rw-r--r--AFC.h74
-rwxr-xr-xbuildme.sh1
-rw-r--r--iphone.c156
-rw-r--r--iphone.h29
-rw-r--r--lockdown.c349
-rw-r--r--lockdown.h36
-rw-r--r--main.c84
-rw-r--r--main.h1
-rw-r--r--plist.c91
-rw-r--r--plist.h17
-rw-r--r--usbmux.c198
-rw-r--r--usbmux.h39
13 files changed, 1354 insertions, 0 deletions
diff --git a/AFC.c b/AFC.c
new file mode 100644
index 0000000..29c0f97
--- /dev/null
+++ b/AFC.c
@@ -0,0 +1,279 @@
+/*
+ * AFC.c -- contains functions for the built-in AFC client.
+ * Written by FxChiP
+ */
+
+#include "AFC.h"
+
+extern int debug;
+
+AFClient *afc_connect(iPhone *phone, int s_port, int d_port) {
+ if (!phone) return NULL;
+ AFClient *client = (AFClient*)malloc(sizeof(AFClient));
+ client->connection = mux_connect(phone, s_port, d_port);
+ if (!client->connection) { free(client); return NULL; }
+ else {
+ client->afc_packet = (AFCPacket*)malloc(sizeof(AFCPacket));
+ if (client->afc_packet) {
+ client->phone = phone;
+ client->afc_packet->packet_num = 0;
+ client->afc_packet->unknown1 = client->afc_packet->unknown2 = client->afc_packet->unknown3 = client->afc_packet->unknown4 = 0;
+ client->afc_packet->header1 = 0x36414643;
+ client->afc_packet->header2 = 0x4141504C;
+ client->file_handle = 0;
+ return client;
+ } else {
+ mux_close_connection(client->phone, client->connection);
+ free(client);
+ return NULL;
+ }
+ }
+
+ return NULL; // should never get to this point
+}
+
+void afc_disconnect(AFClient *client) {
+ // client and its members should never be NULL is assumed here.
+ if (!client || !client->connection || !client->phone || !client->afc_packet) return;
+ mux_close_connection(client->phone, client->connection);
+ free(client->afc_packet);
+ free(client);
+}
+
+int count_nullspaces(char *string, int number) {
+ int i = 0, nulls = 0;
+ for (i = 0; i < number; i++) {
+ if (string[i] == '\0') nulls++;
+ }
+ return nulls;
+}
+
+int dispatch_AFC_packet(AFClient *client, char *data, int length) {
+ char *buffer;
+ int bytes = 0;
+ if (!client || !client->connection || !client->phone || !client->afc_packet) return 0;
+ if (!data || !length) length = 0;
+
+ client->afc_packet->packet_num++;
+ client->afc_packet->entire_length = client->afc_packet->this_length = (length) ? sizeof(AFCPacket) + length + 1 : sizeof(AFCPacket);
+
+ if (!length) {
+ bytes = mux_send(client->phone, client->connection, (char*)client->afc_packet, client->afc_packet->this_length);
+ if (bytes <= 0) return 0;
+ else return bytes;
+ } else {
+ buffer = (char*)malloc(sizeof(char) * client->afc_packet->this_length);
+ memcpy(buffer, client->afc_packet, sizeof(AFCPacket));
+ memcpy(buffer+sizeof(AFCPacket), data, length);
+ buffer[client->afc_packet->this_length-1] = '\0';
+
+ bytes = mux_send(client->phone, client->connection, buffer, client->afc_packet->this_length);
+ free(buffer); // don't need it
+ if (bytes <= 0) return 0;
+ else return bytes;
+ }
+
+ return 0;
+}
+
+int receive_AFC_data(AFClient *client, char **dump_here) {
+ AFCPacket *r_packet;
+ char *buffer = (char*)malloc(sizeof(AFCPacket) * 4);
+ int bytes = 0, recv_len = 0;
+
+ bytes = mux_recv(client->phone, client->connection, buffer, sizeof(AFCPacket) * 4);
+ if (bytes <= 0) {
+ free(buffer);
+ printf("Just didn't get enough.\n");
+ *dump_here = NULL;
+ return 0;
+ }
+
+ r_packet = (AFCPacket*)malloc(sizeof(AFCPacket));
+ memcpy(r_packet, buffer, sizeof(AFCPacket));
+
+ if (r_packet->entire_length == r_packet->this_length && r_packet->entire_length > sizeof(AFCPacket) && r_packet->operation != AFC_ERROR) {
+ *dump_here = (char*)malloc(sizeof(char) * (r_packet->entire_length-sizeof(AFCPacket)));
+ memcpy(*dump_here, buffer+sizeof(AFCPacket), r_packet->entire_length-sizeof(AFCPacket));
+ free(buffer);
+ free(r_packet);
+ return r_packet->entire_length - sizeof(AFCPacket);
+ }
+
+ uint32 param1 = buffer[sizeof(AFCPacket)];
+ free(buffer);
+
+ if (r_packet->operation == 0x01) {
+ printf("Oops? Bad operation code received.\n");
+ if (param1 == 0) printf("... false alarm, but still\n");
+ else printf("Errno %i\n", param1);
+ free(r_packet);
+ *dump_here = NULL;
+ return 0;
+ } else {
+ printf("Operation code %x\nFull length %i and this length %i\n", r_packet->operation, r_packet->entire_length, r_packet->this_length);
+ }
+
+ recv_len = r_packet->entire_length - r_packet->this_length;
+ free(r_packet);
+ buffer = (char*)malloc(sizeof(char) * recv_len);
+ bytes = mux_recv(client->phone, client->connection, buffer, recv_len);
+ if (bytes <= 0) {
+ free(buffer);
+ printf("Didn't get it at the second pass.\n");
+ *dump_here = NULL;
+ return 0;
+ }
+
+ *dump_here = buffer; // what they do beyond this point = not my problem
+ return bytes;
+}
+
+char **afc_get_dir_list(AFClient *client, char *dir) {
+ client->afc_packet->operation = AFC_LIST_DIR;
+ int bytes = 0;
+ char *blah = NULL, **list = NULL;
+ bytes = dispatch_AFC_packet(client, dir, strlen(dir));
+ if (!bytes) return NULL;
+
+ bytes = receive_AFC_data(client, &blah);
+ if (!bytes && !blah) return NULL;
+
+ list = make_strings_list(blah, bytes);
+ free(blah);
+ return list;
+}
+
+char **make_strings_list(char *tokens, int true_length) {
+ if (!tokens || !true_length) return NULL;
+ int nulls = 0, i = 0, j = 0;
+ char **list = NULL;
+
+ nulls = count_nullspaces(tokens, true_length);
+ list = (char**)malloc(sizeof(char*) * (nulls + 1));
+ for (i = 0; i < nulls; i++) {
+ list[i] = strdup(tokens+j);
+ j += strlen(list[i]) + 1;
+ }
+ list[i] = strdup("");
+ return list;
+}
+
+AFCFile *afc_get_file_info(AFClient *client, char *path) {
+ client->afc_packet->operation = AFC_GET_INFO;
+ dispatch_AFC_packet(client, path, strlen(path));
+
+ char *received, **list;
+ AFCFile *my_file;
+ int length, i = 0;
+
+ length = receive_AFC_data(client, &received);
+ list = make_strings_list(received, length);
+ free(received);
+ if (list) {
+ my_file = (AFCFile *)malloc(sizeof(AFCFile));
+ for (i = 0; strcmp(list[i], ""); i++) {
+ if (!strcmp(list[i], "st_size")) {
+ my_file->size = atoi(list[i+1]);
+ }
+
+
+ if (!strcmp(list[i], "st_blocks")) {
+ my_file->blocks = atoi(list[i+1]);
+ }
+
+ if (!strcmp(list[i], "st_ifmt")) {
+ if (!strcmp(list[i+1], "S_IFREG")) {
+ my_file->type = S_IFREG;
+ } else if (!strcmp(list[i+1], "S_IFDIR")) {
+ my_file->type = S_IFDIR;
+ }
+ }
+ }
+ free_dictionary(list);
+ return my_file;
+ } else {
+ return NULL;
+ }
+}
+
+AFCFile *afc_open_file(AFClient *client, const char *filename, uint32 file_mode) {
+ if (file_mode != AFC_FILE_READ && file_mode != AFC_FILE_WRITE) return NULL;
+ if (!client ||!client->connection || !client->phone ||!client->afc_packet) return NULL;
+ char *further_data = (char*)malloc(sizeof(char) * (8 + strlen(filename) + 1));
+ AFCFile *file_infos = NULL;
+ memcpy(further_data, &file_mode, 4);
+ uint32 ag = 0;
+ memcpy(further_data+4, &ag, 4);
+ memcpy(further_data+8, filename, strlen(filename));
+ further_data[8+strlen(filename)] = '\0';
+ int bytes = 0, length_thing = 0;
+ client->afc_packet->operation = AFC_FILE_OPEN;
+
+ bytes = dispatch_AFC_packet(client, further_data, 8+strlen(filename));
+ free(further_data);
+ if (bytes <= 0) {
+ printf("didn't read enough\n");
+ return NULL;
+ } else {
+ printf("O HAI\n");
+ length_thing = receive_AFC_data(client, &further_data);
+ if (length_thing && further_data) {
+ printf("ARA\n");
+ file_infos = afc_get_file_info(client, filename);
+ memcpy(&file_infos->filehandle, further_data, 4);
+ printf("gr\n");
+ return file_infos;
+ } else {
+ printf("didn't get further data or something\n");
+ return NULL;
+ }
+ }
+ printf("what the fuck\n");
+ return NULL;
+}
+
+int afc_read_file(AFClient *client, AFCFile *file, char *data, int length) {
+ if (!client || !client->afc_packet || !client->phone || !client->connection || !file) return -1;
+ AFCFilePacket *packet = (AFCFilePacket*)malloc(sizeof(AFCFilePacket));
+ char *input = NULL;
+ packet->unknown1 = packet->unknown2 = 0;
+ packet->filehandle = file->filehandle;
+ packet->size = length;
+ int bytes = 0;
+
+ client->afc_packet->operation = AFC_READ;
+ bytes = dispatch_AFC_packet(client, packet, sizeof(AFCFilePacket));
+
+ if (bytes > 0) {
+ bytes = receive_AFC_data(client, &input);
+ if (bytes <= 0) {
+ return -1;
+ } else {
+ memcpy(data, input, (bytes > length) ? length : bytes);
+ return (bytes > length) ? length : bytes;
+ }
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+void afc_close_file(AFClient *client, AFCFile *file) {
+ char *buffer = malloc(sizeof(char) * 8);
+ uint32 zero = 0;
+ if (debug) printf("File handle %i\n", file->filehandle);
+ memcpy(buffer, &file->filehandle, sizeof(uint32));
+ memcpy(buffer, &zero, sizeof(zero));
+ client->afc_packet->operation = AFC_FILE_CLOSE;
+ int bytes = 0;
+ bytes = dispatch_AFC_packet(client, buffer, sizeof(char) * 8);
+ free(buffer);
+ if (!bytes) return;
+
+ bytes = receive_AFC_data(client, &buffer);
+ if (bytes<=0 && !buffer) printf("closefile: all went as expected\n");
+ else { printf("We have a buffer!??!?\nLength %i\n", bytes); fwrite(buffer, 1, bytes, stdout); printf("\n"); }
+ if (buffer) free(buffer); // we're *SUPPOSED* to get an "error" here.
+}
+
diff --git a/AFC.h b/AFC.h
new file mode 100644
index 0000000..281e624
--- /dev/null
+++ b/AFC.h
@@ -0,0 +1,74 @@
+/*
+ * AFC.h
+ * Defines and structs and the like for the built-in AFC client
+ * Written by FxChiP
+ */
+
+#include "usbmux.h"
+#include "iphone.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef struct {
+ //const uint32 header1 = 0x36414643; // '6AFC' or 'CFA6' when sent ;)
+ uint32 header1, header2;
+ //const uint32 header2 = 0x4141504C; // 'AAPL' or 'LPAA' when sent ;)
+ uint32 entire_length, unknown1, this_length, unknown2, packet_num, unknown3, operation, unknown4;
+} AFCPacket;
+
+typedef struct {
+ usbmux_tcp_header *connection;
+ iPhone *phone;
+ AFCPacket *afc_packet;
+ int file_handle;
+} AFClient;
+
+typedef struct {
+ uint32 filehandle, unknown1, size, unknown2;
+} AFCFilePacket;
+
+typedef struct {
+ uint32 filehandle, blocks, size, type;
+} AFCFile;
+
+typedef struct __AFCToken {
+ struct __AFCToken *last, *next;
+ char *token;
+} AFCToken;
+
+enum {
+ S_IFREG = 0,
+ S_IFDIR = 1
+};
+
+enum {
+ AFC_FILE_READ = 0x00000002,
+ AFC_FILE_WRITE = 0x00000003
+};
+
+enum {
+ AFC_ERROR = 0x00000001,
+ AFC_GET_INFO = 0x0000000a,
+ AFC_GET_DEVINFO = 0x0000000b,
+ AFC_LIST_DIR = 0x00000003,
+ AFC_SUCCESS_RESPONSE = 0x00000002,
+ AFC_FILE_OPEN = 0x0000000d,
+ AFC_FILE_CLOSE = 0x00000014,
+ AFC_FILE_HANDLE = 0x0000000e,
+ AFC_READ = 0x0000000f
+};
+
+AFClient *afc_connect(iPhone *phone, int s_port, int d_port);
+void afc_disconnect(AFClient *client);
+int count_nullspaces(char *string, int number);
+char **make_strings_list(char *tokens, int true_length);
+int dispatch_AFC_packet(AFClient *client, char *data, int length);
+int receive_AFC_data(AFClient *client, char **dump_here);
+
+char **afc_get_dir_list(AFClient *client, char *dir);
+AFCFile *afc_get_file_info(AFClient *client, char *path);
+AFCFile *afc_open_file(AFClient *client, const char *filename, uint32 file_mode);
+void afc_close_file(AFClient *client, AFCFile *file);
+int afc_read_file(AFClient *client, AFCFile *file, char *data, int length);
diff --git a/buildme.sh b/buildme.sh
new file mode 100755
index 0000000..75549ba
--- /dev/null
+++ b/buildme.sh
@@ -0,0 +1 @@
+gcc `xml2-config --cflags --libs` -o main usbmux.c main.c iphone.c plist.c lockdown.c AFC.c -lusb -lgnutls
diff --git a/iphone.c b/iphone.c
new file mode 100644
index 0000000..4ddb571
--- /dev/null
+++ b/iphone.c
@@ -0,0 +1,156 @@
+/* iPhone.c
+ * Functions for creating and initializing iPhone structures
+ */
+
+#include "usbmux.h"
+#include "iphone.h"
+#include <usb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* get_iPhone()
+ *
+ * Returns a structure with data on the first iPhone it finds.
+ * (Or NULL, on error)
+ */
+extern int debug;
+
+iPhone *get_iPhone() {
+ iPhone *phone = (iPhone*)malloc(sizeof(iPhone));
+ usbmux_version_header *version = version_header();
+
+ // initialize the struct
+ phone->device = NULL;
+ phone->__device = NULL;
+
+ // Initialize libusb.
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+ struct usb_bus *busses = usb_get_busses(), *bus;
+ struct usb_device *dev;
+
+ for (bus = busses; bus; bus = bus->next) {
+ for (dev = bus->devices; dev; dev = dev->next) {
+ if (dev->descriptor.idVendor == 0x05ac && (dev->descriptor.idProduct == 0x1290 || dev->descriptor.idProduct == 0x1291)) {
+ phone->__device = dev;
+ phone->device = usb_open(phone->__device);
+ usb_reset(phone->device);
+ }
+ }
+ }
+
+ phone->device = NULL; // :( sorry Daniel
+ phone->__device = NULL; // :( sorry Daniel
+
+ for (bus = busses; bus; bus = bus->next) { // do it again as per libusb documentation
+ for (dev = bus->devices; dev; dev = dev->next) {
+ if (dev->descriptor.idVendor == 0x05ac && (dev->descriptor.idProduct == 0x1290 || dev->descriptor.idProduct == 0x1291)) {
+ phone->__device = dev;
+ phone->device = usb_open(phone->__device);
+ usb_set_configuration(phone->device, 3);
+ usb_claim_interface(phone->device, 1);
+ break;
+ }
+ }
+ if (phone->__device && phone->device) break;
+ }
+
+ if (!phone->device || !phone->__device) { // nothing connected
+ free_iPhone(phone);
+ if (debug) printf("get_iPhone(): iPhone not found\n");
+ return NULL;
+ }
+
+ // Okay, initialize the phone now.
+ int bytes = 0;
+ bytes = usb_bulk_write(phone->device, BULKOUT, (char*)version, sizeof(*version), 800);
+ if (bytes < 20 && debug) {
+ printf("get_iPhone(): libusb did NOT send enough!\n");
+ if (bytes < 0) {
+ printf("get_iPhone(): libusb gave me the error: %s\n", usb_strerror());
+ }
+ }
+ bytes = usb_bulk_read(phone->device, BULKIN, (char*)version, sizeof(*version), 800);
+ if (bytes < 20) {
+ free_iPhone(phone);
+ if (debug) printf("get_iPhone(): Invalid version message -- header too short.\n");
+ if (debug && bytes < 0) printf("get_iPhone(): libusb error message: %s\n", usb_strerror());
+ return NULL;
+ } else {
+ if (ntohl(version->major) == 1 && ntohl(version->minor) == 0) {
+ // We're all ready to roll.
+ printf("get_iPhone() success\n");
+ return phone;
+ } else { // BAD HEADER
+ free_iPhone(phone);
+ if (debug) printf("get_iPhone(): Received a bad header/invalid version number.");
+ return NULL;
+ }
+ }
+
+ if (debug) printf("get_iPhone(): Unknown error.\n");
+ return NULL; // if it got to this point it's gotta be bad
+}
+
+/* free_iPhone(victim)
+ * This is a library-level function; deals directly with the iPhone to tear down relations,
+ * but otherwise is mostly internal.
+ *
+ * victim: a pointer to an iPhone structure
+ * Cleans up an iPhone structure, then frees the structure itself.
+ */
+
+void free_iPhone(iPhone *victim) {
+ if (victim->buffer) free(victim->buffer);
+ if (victim->device) {
+ usb_release_interface(victim->device, 1);
+ usb_reset(victim->device);
+ usb_close(victim->device);
+ }
+ free(victim);
+}
+
+/* send_to_phone(phone, data, datalen)
+ * This is a low-level (i.e. directly to phone) function.
+ *
+ * phone: the iPhone to send data to
+ * data: the data to send to the iPhone
+ * datalen: the length of the data
+ *
+ * Returns the number of bytes sent, or -1 on error or something.
+ */
+int send_to_phone(iPhone *phone, char *data, int datalen) {
+ if (!phone) return -1;
+ int bytes = 0;
+ // it may die here
+ if (debug) printf("dying here?\ndatalen = %i\ndata = %x\n", datalen, data);
+
+ bytes = usb_bulk_write(phone->device, BULKOUT, data, datalen, 800);
+ if (debug) printf("noooo...?\n");
+ if (bytes < datalen) {
+ return -1;
+ } else {
+ return bytes;
+ }
+
+ return -1;
+}
+
+/* recv_from_phone(phone, data, datalen):
+ * This function is a low-level (i.e. direct to iPhone) function.
+ *
+ * phone: the iPhone to receive data from
+ * data: where to put data read
+ * datalen: how much data to read in
+ *
+ * Returns: how many bytes were read in, or -1 on error.
+ */
+int recv_from_phone(iPhone *phone, char *data, int datalen) {
+ if (!phone) return -1;
+ int bytes = 0;
+ bytes = usb_bulk_read(phone->device, BULKIN, data, datalen, 3500);
+ return bytes;
+}
+
diff --git a/iphone.h b/iphone.h
new file mode 100644
index 0000000..a49b7ef
--- /dev/null
+++ b/iphone.h
@@ -0,0 +1,29 @@
+/* iphone.h
+ * iPhone struct
+ * Written by FxChiP */
+
+#ifndef IPHONE_H
+#define IPHONE_H
+
+#ifndef USBMUX_H
+#include "usbmux.h"
+#warning usbmux not included?
+#endif
+
+#include <usb.h>
+
+#define BULKIN 0x85
+#define BULKOUT 0x04
+
+typedef struct {
+ char *buffer;
+ struct usb_dev_handle *device;
+ struct usb_device *__device;
+} iPhone;
+
+// Function definitions
+void free_iPhone(iPhone *victim);
+iPhone *get_iPhone();
+int send_to_phone(iPhone *phone, char *data, int datalen);
+int recv_from_phone(iPhone *phone, char *data, int datalen);
+#endif
diff --git a/lockdown.c b/lockdown.c
new file mode 100644
index 0000000..5ca6001
--- /dev/null
+++ b/lockdown.c
@@ -0,0 +1,349 @@
+/*
+ * lockdown.c -- libiphone built-in lockdownd client
+ * Written by FxChiP
+ */
+
+#include "usbmux.h"
+#include "iphone.h"
+#include "lockdown.h"
+#include <errno.h>
+#include <string.h>
+
+extern int debug;
+
+lockdownd_client *new_lockdownd_client(iPhone *phone) {
+ if (!phone) return NULL;
+ lockdownd_client *control = (lockdownd_client*)malloc(sizeof(lockdownd_client));
+ control->connection = mux_connect(phone, 0x0a00, 0xf27e);
+ if (!control->connection) {
+ free(control);
+ return NULL;
+ }
+
+ control->ssl_session = (gnutls_session_t*)malloc(sizeof(gnutls_session_t));
+ control->in_SSL = 0;
+ control->iphone = phone;
+ control->gtls_buffer_hack_len = 0;
+ return control;
+}
+
+void lockdown_close(lockdownd_client *control) {
+ if (!control) return;
+ if (control->connection) {
+ mux_close_connection(control->iphone, control->connection);
+ }
+
+ if (control->ssl_session) free(control->ssl_session);
+ free(control);
+}
+
+
+int lockdownd_recv(lockdownd_client *control, char **dump_data) {
+ char *receive;
+ uint32 datalen = 0, bytes = 0;
+
+ if (!control->in_SSL) bytes = mux_recv(control->iphone, control->connection, &datalen, sizeof(datalen));
+ else bytes = gnutls_record_recv(*control->ssl_session, &datalen, sizeof(datalen));
+ datalen = ntohl(datalen);
+
+ receive = (char*)malloc(sizeof(char) * datalen);
+ if (!control->in_SSL) bytes = mux_recv(control->iphone, control->connection, receive, datalen);
+ else bytes = gnutls_record_recv(*control->ssl_session, receive, datalen);
+ *dump_data = receive;
+ return bytes;
+}
+
+int lockdownd_send(lockdownd_client *control, char *raw_data, uint32 length) {
+ char *real_query;
+ int bytes;
+
+ real_query = (char*)malloc(sizeof(char) * (length+4));
+ length = htonl(length);
+ memcpy(real_query, &length, sizeof(length));
+ memcpy(real_query+4, raw_data, ntohl(length));
+ if (!control->in_SSL) bytes = mux_send(control->iphone, control->connection, real_query, ntohl(length)+sizeof(length));
+ else gnutls_record_send(*control->ssl_session, real_query, ntohl(length)+sizeof(length));
+ return bytes;
+}
+
+int lockdownd_hello(lockdownd_client *control) {
+ xmlDocPtr plist = new_plist();
+ xmlNode *dict, *key;
+ char **dictionary;
+ int bytes = 0, i = 0;
+
+ dict = add_child_to_plist(plist, "dict", "\n", NULL, 0);
+ key = add_key_str_dict_element(plist, dict, "Request", "QueryType", 1);
+ char *XML_content;
+ uint32 length;
+
+ xmlDocDumpMemory(plist, &XML_content, &length);
+
+ bytes = lockdownd_send(control, XML_content, length);
+
+ xmlFree(XML_content);
+ xmlFreeDoc(plist); plist = NULL;
+
+ bytes = lockdownd_recv(control, &XML_content);
+
+ plist = xmlReadMemory(XML_content, bytes, NULL, NULL, 0);
+ if (!plist) return 0;
+ dict = xmlDocGetRootElement(plist);
+ for (dict = dict->children; dict; dict = dict->next) {
+ if (!xmlStrcmp(dict->name, "dict")) break;
+ }
+ if (!dict) return 0;
+
+ dictionary = read_dict_element_strings(dict);
+ xmlFreeDoc(plist);
+ free(XML_content);
+
+ for (i = 0; strcmp(dictionary[i], ""); i+=2) {
+ if (!strcmp(dictionary[i], "Result") && !strcmp(dictionary[i+1], "Success")) {
+ free_dictionary(dictionary);
+ return 1;
+ }
+ }
+
+ free_dictionary(dictionary);
+ return 0;
+}
+
+int lockdownd_start_SSL_session(lockdownd_client *control, const char *HostID) {
+ xmlDocPtr plist = new_plist();
+ xmlNode *dict = add_child_to_plist(plist, "dict", "\n", NULL, 0);
+ xmlNode *key;
+ char *what2send = NULL, **dictionary = NULL;
+ uint32 len = 0, bytes = 0, return_me = 0, i = 0;
+ // end variables
+
+ key = add_key_str_dict_element(plist, dict, "HostID", HostID, 1);
+ if (!key) {
+ if (debug) printf("Couldn't add a key.\n");
+ xmlFreeDoc(plist);
+ return 0;
+ }
+ key = add_key_str_dict_element(plist, dict, "Request", "StartSession", 1);
+ if (!key) {
+ if (debug) printf("Couldn't add a key.\n");
+ xmlFreeDoc(plist);
+ return 0;
+ }
+
+ xmlDocDumpMemory(plist, &what2send, &len);
+ bytes = lockdownd_send(control, what2send, len);
+
+ xmlFree(what2send);
+ xmlFreeDoc(plist);
+
+ if (bytes > 0) {
+ len = lockdownd_recv(control, &what2send);
+ plist = xmlReadMemory(what2send, len, NULL, NULL, 0);
+ dict = xmlDocGetRootElement(plist);
+ for (dict = dict->children; dict; dict = dict->next) {
+ if (!xmlStrcmp(dict->name, "dict")) break;
+ }
+ dictionary = read_dict_element_strings(dict);
+ xmlFreeDoc(plist);
+ free(what2send);
+ for (i = 0; strcmp(dictionary[i], ""); i+=2) {
+ if (!strcmp(dictionary[i], "Result") && !strcmp(dictionary[i+1], "Success")) {
+ // Set up GnuTLS...
+ gnutls_certificate_credentials_t xcred;
+
+ if (debug) printf("We started the session OK, now trying GnuTLS\n");
+ errno = 0;
+ gnutls_global_init();
+ gnutls_certificate_allocate_credentials(&xcred);
+ gnutls_certificate_set_x509_trust_file(xcred, "hostcert.pem", GNUTLS_X509_FMT_PEM);
+ gnutls_init(control->ssl_session, GNUTLS_CLIENT);
+ if ((return_me = gnutls_priority_set_direct(*control->ssl_session, "NORMAL:+VERS-SSL3.0", NULL)) < 0) {
+ printf("oops? bad options?\n");
+ gnutls_perror(return_me);
+ return 0;
+ }
+ gnutls_credentials_set(*control->ssl_session, GNUTLS_CRD_CERTIFICATE, xcred); // this part is killing me.
+
+ if (debug) printf("GnuTLS step 1...\n");
+ gnutls_transport_set_ptr(*control->ssl_session, (gnutls_transport_ptr_t) control);
+ if (debug) printf("GnuTLS step 2...\n");
+ gnutls_transport_set_push_function(*control->ssl_session, (gnutls_push_func)&lockdownd_secuwrite);
+ if (debug) printf("GnuTLS step 3...\n");
+ gnutls_transport_set_pull_function(*control->ssl_session, (gnutls_pull_func)&lockdownd_securead);
+ if (debug) printf("GnuTLS step 4 -- now handshaking...\n");
+
+ if (errno && debug) printf("WARN: errno says %s before handshake!\n", strerror(errno));
+ return_me = gnutls_handshake(*control->ssl_session);
+ if (debug) printf("GnuTLS handshake done...\n");
+
+ free_dictionary(dictionary);
+
+ if (return_me != GNUTLS_E_SUCCESS) {
+ if (debug) printf("GnuTLS reported something wrong.\n");
+ gnutls_perror(return_me);
+ if (debug) printf("oh.. errno says %s\n", strerror(errno));
+ return 0;
+ } else {
+ control->in_SSL = 1;
+ return 1;
+ }
+ }
+ }
+
+ if (debug) {
+ printf("Apparently failed negotiating with lockdownd.\n");
+ printf("Responding dictionary: \n");
+ for (i = 0; strcmp(dictionary[i], ""); i+=2) {
+ printf("\t%s: %s\n", dictionary[i], dictionary[i+1]);
+ }
+ }
+
+ free_dictionary(dictionary);
+ return 0;
+ } else {
+ if (debug) printf("Didn't get enough bytes.\n");
+ return 0;
+ }
+}
+
+ssize_t lockdownd_secuwrite(gnutls_transport_ptr_t transport, char *buffer, size_t length) {
+ int bytes = 0;
+ lockdownd_client *control;
+ control = (lockdownd_client*)transport;
+ if (debug) printf("lockdownd_secuwrite() called\n");
+ if (debug) printf("pre-send\nlength = %i\n", length);
+ bytes = mux_send(control->iphone, control->connection, buffer, length);
+ if (debug) printf("post-send\nsent %i bytes\n", bytes);
+ return bytes;
+}
+
+ssize_t lockdownd_securead(gnutls_transport_ptr_t transport, char *buffer, size_t length) {
+ int bytes = 0, pos_start_fill = 0;
+ char *hackhackhack = NULL;
+ lockdownd_client *control;
+ control = (lockdownd_client*)transport;
+ if (debug) printf("lockdownd_securead() called\nlength = %i\n", length);
+ // Buffering hack! Throw what we've got in our "buffer" into the stream first, then get more.
+ if (control->gtls_buffer_hack_len > 0) {
+ if (length > control->gtls_buffer_hack_len) { // If it's asking for more than we got
+ length -= control->gtls_buffer_hack_len; // Subtract what we have from their requested length
+ pos_start_fill = control->gtls_buffer_hack_len; // set the pos to start filling at
+ memcpy(buffer, control->gtls_buffer_hack, control->gtls_buffer_hack_len); // Fill their buffer partially
+ free(control->gtls_buffer_hack); // free our memory, it's not chained anymore
+ control->gtls_buffer_hack_len = 0; // we don't have a hack buffer anymore
+ if (debug) printf("Did a partial fill to help quench thirst for data\n");
+ } else if (length < control->gtls_buffer_hack_len) { // If it's asking for less...
+ control->gtls_buffer_hack_len -= length; // subtract what they're asking for
+ memcpy(buffer, control->gtls_buffer_hack, length); // fill their buffer
+ hackhackhack = (char*)malloc(sizeof(char) * control->gtls_buffer_hack_len); // strndup is NOT a good solution -- concatenates \0!!!! Anyway, make a new "hack" buffer.
+ memcpy(hackhackhack, control->gtls_buffer_hack+length, control->gtls_buffer_hack_len); // Move what's left into the new one
+ free(control->gtls_buffer_hack); // Free the old one
+ control->gtls_buffer_hack = hackhackhack; // And make it the new one.
+ hackhackhack = NULL;
+ if (debug) printf("Quenched the thirst for data; new hack length is %i\n", control->gtls_buffer_hack_len);
+ return length; // hand it over.
+ } else { // length == hack length
+ memcpy(buffer, control->gtls_buffer_hack, length); // copy our buffer into theirs
+ free(control->gtls_buffer_hack); // free our "obligation"
+ control->gtls_buffer_hack_len = 0; // free our "obligation"
+ if (debug) printf("Satiated the thirst for data; now we have to eventually receive again.\n");
+ return length; // hand it over
+ }
+ }
+ // End buffering hack!
+ char *recv_buffer = (char*)malloc(sizeof(char) * (length * 400)); // ensuring nothing stupid happens
+
+ if (debug) printf("pre-read\nclient wants %i bytes\n", length);
+ bytes = mux_recv(control->iphone, control->connection, recv_buffer, (length * 400));
+ if (debug) printf("post-read\nwe got %i bytes\n", bytes);
+ if (bytes >= length) {
+ if (bytes > length) {
+ if (debug) printf("lockdownd_securead: Client deliberately read less data than was there; resorting to GnuTLS buffering hack.\n");
+ if (!control->gtls_buffer_hack_len) { // if there's no hack buffer yet
+ //control->gtls_buffer_hack = strndup(recv_buffer+length, bytes-length); // strndup is NOT a good solution!
+ control->gtls_buffer_hack_len += bytes-length;
+ control->gtls_buffer_hack = (char*)malloc(sizeof(char) * control->gtls_buffer_hack_len);
+ memcpy(control->gtls_buffer_hack, recv_buffer+length, control->gtls_buffer_hack_len);
+ } else { // if there is.
+ control->gtls_buffer_hack = realloc(control->gtls_buffer_hack, control->gtls_buffer_hack_len + (bytes - length));
+ memcpy(control->gtls_buffer_hack+control->gtls_buffer_hack_len, recv_buffer+length, bytes-length);
+ control->gtls_buffer_hack_len += bytes - length;
+ }
+ }
+ memcpy(buffer+pos_start_fill, recv_buffer, length);
+ free(recv_buffer);
+ if (bytes == length) { if (debug) printf("Returning how much we received.\n"); return bytes; }
+ else { if (debug) printf("Returning what they want to hear.\nHack length: %i\n", control->gtls_buffer_hack_len); return length; }
+ }
+ return bytes;
+}
+
+int lockdownd_start_service(lockdownd_client *control, const char *service) {
+ if (!control) return 0;
+ if (!control->in_SSL && !lockdownd_start_SSL_session(control, "29942970-207913891623273984")) return 0;
+
+ char *XML_query, **dictionary;
+ uint32 length, i = 0, port = 0;
+ uint8 result = 0;
+
+ xmlDocPtr plist = new_plist();
+ xmlNode *dict = add_child_to_plist(plist, "dict", "\n", NULL, 0);
+ xmlNode *key;
+ key = add_key_str_dict_element(plist, dict, "Request", "StartService", 1);
+ if (!key) { xmlFreeDoc(plist); return 0; }
+ key = add_key_str_dict_element(plist, dict, "Service", service, 1);
+ if (!key) { xmlFreeDoc(plist); return 0; }
+
+ xmlDocDumpMemory(plist, &XML_query, &length);
+
+ lockdownd_send(control, XML_query, length);
+ free(XML_query);
+
+ length = lockdownd_recv(control, &XML_query);
+
+ xmlFreeDoc(plist);
+
+ if (length <= 0) return 0;
+ else {
+ plist = xmlReadMemory(XML_query, length, NULL, NULL, 0);
+ if (!plist) return 0;
+ dict = xmlDocGetRootElement(plist);
+ if (!dict) return 0;
+ for (dict = dict->children; dict; dict = dict->next) {
+ if (!xmlStrcmp(dict->name, "dict")) break;
+ }
+
+ if (!dict) return 0;
+ dictionary = read_dict_element_strings(dict);
+
+ for (i = 0; strcmp(dictionary[i], ""); i+=2) {
+ if (debug) printf("lockdownd_start_service() dictionary %s: %s\n", dictionary[i], dictionary[i+1]);
+
+ if (!xmlStrcmp(dictionary[i], "Port")) {
+ port = atoi(dictionary[i+1]);
+ if (debug) printf("lockdownd_start_service() atoi'd port: %i\n", port);
+ }
+
+ if (!xmlStrcmp(dictionary[i], "Result")) {
+ if (!xmlStrcmp(dictionary[i+1], "Success")) {
+ result = 1;
+ }
+ }
+ }
+
+ if (debug) {
+ printf("lockdownd_start_service(): DATA RECEIVED:\n\n");
+ fwrite(XML_query, 1, length, stdout);
+ printf("end data received by lockdownd_start_service()\n");
+ }
+
+ free(XML_query);
+ xmlFreeDoc(plist);
+ free_dictionary(dictionary);
+ if (port && result) return port;
+ else return 0;
+ }
+
+ return 0;
+}
+
diff --git a/lockdown.h b/lockdown.h
new file mode 100644
index 0000000..0acd624
--- /dev/null
+++ b/lockdown.h
@@ -0,0 +1,36 @@
+/*
+ * lockdown.h
+ * Defines lockdown stuff, like the client struct.
+ */
+
+#ifndef LOCKDOWND_H
+#define LOCKDOWND_H
+
+#include "plist.h"
+
+#include <gnutls/gnutls.h>
+#include <string.h>
+
+typedef struct {
+ usbmux_tcp_header *connection;
+ gnutls_session_t *ssl_session;
+ iPhone *iphone;
+ int in_SSL;
+ char *gtls_buffer_hack;
+ int gtls_buffer_hack_len;
+} lockdownd_client;
+
+lockdownd_client *new_lockdownd_client(iPhone *phone);
+int lockdownd_hello(lockdownd_client *control);
+int lockdownd_recv(lockdownd_client *control, char **dump_data);
+int lockdownd_send(lockdownd_client *control, char *raw_data, uint32 length);
+void lockdownd_close(lockdownd_client *control);
+
+// SSL functions
+int lockdownd_start_SSL_session(lockdownd_client *control, const char *HostID);
+ssize_t lockdownd_securead(gnutls_transport_ptr_t transport, char *buffer, size_t length);
+ssize_t lockdownd_secuwrite(gnutls_transport_ptr_t transport, char *buffer, size_t length);
+
+// Higher-level lockdownd stuff
+int lockdownd_start_service(lockdownd_client *control, const char *service);
+#endif
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..4cde3d9
--- /dev/null
+++ b/main.c
@@ -0,0 +1,84 @@
+/*
+ * libiphone main.c written by FxChiP
+ * With much help from Daniel Brownlees
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <usb.h>
+
+#include "usbmux.h"
+#include "iphone.h"
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include "plist.h"
+#include "lockdown.h"
+#include "AFC.h"
+
+int debug = 1;
+
+int main(int argc, char *argv[]) {
+ iPhone *phone = get_iPhone();
+ if (argc > 1 && !strcasecmp(argv[1], "--debug")) debug = 1;
+ else debug = 0;
+ char *response = (char*)malloc(sizeof(char) * 2048);
+ int bytes = 0, port = 0, i = 0;
+ if (phone) printf("I got a phone.\n");
+ else { printf("oops\n"); return -1; }
+
+ lockdownd_client *control = new_lockdownd_client(phone);
+ if (!lockdownd_hello(control)) {
+ printf("Something went wrong in the lockdownd client, go take a look.\n");
+ } else {
+ printf("We said hello. :)\n");
+ }
+
+ printf("Now starting SSL.\n");
+ if (!lockdownd_start_SSL_session(control, "29942970-207913891623273984")) {
+ printf("Error happened in GnuTLS...\n");
+ } else {
+ printf("... we're in SSL with the phone... !?\n");
+ }
+ port = lockdownd_start_service(control, "com.apple.afc");
+ if (port) {
+ printf("Start Service successful -- connect on port %i\n", port);
+ AFClient *afc = afc_connect(phone, 3432, port);
+ if (afc) {
+ char **dirs;
+ dirs = afc_get_dir_list(afc, "/eafaedf");
+ if (!dirs) dirs = afc_get_dir_list(afc, "/");
+ printf("Directory time.\n");
+ for (i = 0; strcmp(dirs[i], ""); i++) {
+ printf("/%s\n", dirs[i]);
+ }
+
+ free_dictionary(dirs);
+ AFCFile *my_file = afc_open_file(afc, "/iTunesOnTheGoPlaylist.plist", AFC_FILE_READ);
+ if (my_file) {
+ printf("A file size: %i\n", my_file->size);
+ char *file_data = (char*)malloc(sizeof(char) * my_file->size);
+ bytes = afc_read_file(afc, my_file, file_data, my_file->size);
+ if (bytes >= 0) {
+ printf("The file's data:\n");
+ fwrite(file_data, 1, bytes, stdout);
+ }
+ printf("\nClosing my file.\n");
+ afc_close_file(afc, my_file);
+ free(my_file);
+ free(file_data);
+ } else printf("couldn't open a file\n");
+ }
+ afc_disconnect(afc);
+ } else {
+ printf("Start service failure.\n");
+ }
+
+ printf("All done.\n");
+
+ free_iPhone(phone);
+
+ return 0;
+}
+
diff --git a/main.h b/main.h
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/main.h
@@ -0,0 +1 @@
+
diff --git a/plist.c b/plist.c
new file mode 100644
index 0000000..cbd6302
--- /dev/null
+++ b/plist.c
@@ -0,0 +1,91 @@
+/*
+ * plist.c
+ * Builds plist XML structures.
+ * Written by FxChiP
+ */
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <string.h>
+#include "plist.h"
+
+const char *plist_base = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
+<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\
+<plist version=\"1.0\">\n\
+</plist>\0";
+
+xmlDocPtr new_plist() {
+ char *plist = strdup(plist_base);
+ xmlDocPtr plist_xml = xmlReadMemory(plist, strlen(plist), NULL, NULL, 0);
+ if (!plist_xml) return NULL;
+ free(plist);
+ return plist_xml;
+}
+
+void free_plist(xmlDocPtr plist) {
+ if (!plist) return;
+ xmlFreeDoc(plist);
+}
+
+xmlNode *add_child_to_plist(xmlDocPtr plist, const char *name, const char *content, xmlNode *to_node, int depth) {
+ if (!plist) return NULL;
+ int i = 0;
+ xmlNode *child;
+ if (!to_node) to_node = xmlDocGetRootElement(plist);
+ for (i = 0; i < depth; i++) {
+ xmlNodeAddContent(to_node, "\t");
+ }
+ child = xmlNewChild(to_node, NULL, name, content);
+ xmlNodeAddContent(to_node, "\n");
+ return child;
+}
+
+xmlNode *add_key_str_dict_element(xmlDocPtr plist, xmlNode *dict, const char *key, const char *value, int depth) {
+ xmlNode *keyPtr;
+ keyPtr = add_child_to_plist(plist, "key", key, dict, depth);
+ add_child_to_plist(plist, "string", value, dict, depth);
+ return keyPtr;
+}
+
+char **read_dict_element_strings(xmlNode *dict) {
+ // reads a set of keys and strings into an array where each even number is a key and odd numbers are values.
+ // if the odd number is \0, that's the end of the list.
+ char **return_me = NULL, **old = NULL;
+ int current_length = 0;
+ int current_pos = 0;
+ xmlNode *dict_walker;
+
+ for (dict_walker = dict->children; dict_walker; dict_walker = dict_walker->next) {
+ if (!xmlStrcmp(dict_walker->name, "key")) {
+ current_length += 2;
+ old = return_me;
+ return_me = realloc(return_me, sizeof(char*) * current_length);
+ if (!return_me) {
+ free(old);
+ return NULL;
+ }
+ return_me[current_pos++] = xmlNodeGetContent(dict_walker);
+ return_me[current_pos++] = xmlNodeGetContent(dict_walker->next->next);
+ }
+ }
+
+ // one last thing...
+ old = return_me;
+ return_me = realloc(return_me, sizeof(char*) * current_length+1);
+ return_me[current_pos] = strdup("");
+
+ return return_me;
+}
+
+void free_dictionary(char **dictionary) {
+ if (!dictionary) return;
+ int i = 0;
+
+ for (i = 0; strcmp(dictionary[i], ""); i++) {
+ free(dictionary[i]);
+ }
+
+ free(dictionary[i]);
+ free(dictionary);
+}
+
diff --git a/plist.h b/plist.h
new file mode 100644
index 0000000..1f18eb4
--- /dev/null
+++ b/plist.h
@@ -0,0 +1,17 @@
+/* plist.h
+ * contains structures and the like for plists
+ * written by fxchip
+ */
+
+#ifndef PLIST_H
+#define PLIST_H
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+xmlNode *add_key_str_dict_element(xmlDocPtr plist, xmlNode *dict, const char *key, const char *value, int depth);
+xmlNode *add_child_to_plist(xmlDocPtr plist, const char *name, const char *content, xmlNode *to_node, int depth);
+void free_plist(xmlDocPtr plist);
+xmlDocPtr new_plist();
+void free_dictionary(char **dictionary);
+#endif
diff --git a/usbmux.c b/usbmux.c
new file mode 100644
index 0000000..8c5fc34
--- /dev/null
+++ b/usbmux.c
@@ -0,0 +1,198 @@
+
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "usbmux.h"
+
+extern int debug;
+
+usbmux_tcp_header *new_mux_packet(uint16 s_port, uint16 d_port) {
+ usbmux_tcp_header *conn = (usbmux_tcp_header*)malloc(sizeof(usbmux_tcp_header));
+ conn->type = htonl(6);
+ conn->length = 28;
+ conn->sport = htons(s_port);
+ conn->dport = htons(d_port);
+ conn->scnt = 0;
+ conn->ocnt = 0;
+ conn->offset = 0x50;
+ conn->window = htons(0x0200);
+ conn->nullnull = 0x0000;
+ conn->length16 = 28;
+ return conn;
+}
+
+usbmux_version_header *version_header() {
+ usbmux_version_header *version = (usbmux_version_header*)malloc(sizeof(usbmux_version_header));
+ version->type = 0;
+ version->length = htonl(20);
+ version->major = htonl(1);
+ version->minor = 0;
+ version->allnull = 0;
+ return version;
+}
+
+/* mux_connect(phone, s_port, d_port)
+ * This is a higher-level USBMuxTCP-type function.
+ * phone: the iPhone to initialize a connection on.
+ * s_port: the source port
+ * d_port: the destination port -- 0xf27e for lockdownd.
+ * Initializes a connection on phone, with source port s_port and destination port d_port
+ *
+ * Returns a mux TCP header for the connection which is used for tracking and data transfer.
+ */
+
+usbmux_tcp_header *mux_connect(iPhone *phone, uint16 s_port, uint16 d_port) {
+ if (!phone || !s_port || !d_port) return NULL;
+ int bytes = 0;
+ // Initialize connection stuff
+ usbmux_tcp_header *new_connection;
+ new_connection = new_mux_packet(s_port, d_port);
+ usbmux_tcp_header *response;
+ response = (usbmux_tcp_header*)malloc(sizeof(usbmux_tcp_header));
+ // blargg
+ if (new_connection) {
+ new_connection->tcp_flags = 0x02;
+ new_connection->length = htonl(new_connection->length);
+ new_connection->length16 = htons(new_connection->length16);
+
+ if (send_to_phone(phone, (char*)new_connection, sizeof(*new_connection)) >= 0) {
+ bytes = recv_from_phone(phone, (char*)response, sizeof(*response));
+ if (response->tcp_flags != 0x12) return NULL;
+ else {
+ new_connection->tcp_flags = 0x10;
+ new_connection->scnt = 1;
+ new_connection->ocnt = 1;
+ return new_connection;
+ }
+ } else {
+ return NULL;
+ }
+ }
+
+ // if we get to this point it's probably bad
+ return NULL;
+}
+
+/* mux_close_connection(phone, connection)
+ * This is a higher-level USBmuxTCP-type function.
+ * phone: the iPhone to close a connection with.
+ * connection: the connection to close.
+ *
+ * Doesn't return anything; WILL FREE THE CONNECTION'S MEMORY!!!
+ */
+void mux_close_connection(iPhone *phone, usbmux_tcp_header *connection) {
+ if (!phone || !connection) return;
+
+ connection->tcp_flags = 0x04;
+ connection->scnt = htonl(connection->scnt);
+ connection->ocnt = htonl(connection->ocnt);
+ int bytes = 0;
+
+ bytes = usb_bulk_write(phone->device, BULKOUT, (char*)connection, sizeof(*connection), 800);
+ bytes = usb_bulk_read(phone->device, BULKIN, (char*)connection, sizeof(*connection), 800);
+
+ free(connection);
+}
+
+/* mux_send(phone, connection, data, datalen)
+ * This is a higher-level USBMuxTCP-like function.
+ * phone: the iPhone to send to.
+ * connection: the connection we're sending data on.
+ * data: a pointer to the data to send.
+ * datalen: how much data we're sending.
+ *
+ * Returns number of bytes sent, minus the header (28), or -1 on error.
+ */
+int mux_send(iPhone *phone, usbmux_tcp_header *connection, char *data, uint32 datalen) {
+ if (!phone || !connection || !data || datalen == 0) return -1;
+ // connection->scnt and connection->ocnt should already be in host notation...
+ // we don't need to change them juuuust yet.
+ int bytes = 0;
+ if (debug) printf("mux_send(): client wants to send %i bytes\n", datalen);
+ char *buffer = (char*)malloc(sizeof(*connection) + datalen + 2); // allow 2 bytes of safety padding
+ // Set the length and pre-emptively htonl/htons it
+ connection->length = htonl(sizeof(*connection) + datalen);
+ connection->length16 = htons(sizeof(*connection) + datalen);
+
+ // Put scnt and ocnt into big-endian notation
+ connection->scnt = htonl(connection->scnt);
+ connection->ocnt = htonl(connection->ocnt);
+ // Concatenation of stuff in the buffer.
+ memcpy(buffer, connection, sizeof(*connection));
+ memcpy(buffer+sizeof(*connection)/*+sizeof(datalen)*/, data, datalen);
+
+ // We have a buffer full of data, we should now send it to the phone.
+ if (debug) printf("actually sending %i bytes of data at %x\n", sizeof(*connection)+datalen, buffer);
+
+
+ bytes = send_to_phone(phone, buffer, sizeof(*connection)+datalen);
+
+ // Now that we've sent it off, we can clean up after our sloppy selves.
+ free(buffer);
+
+ // Re-calculate scnt and ocnt
+ connection->scnt = ntohl(connection->scnt) + datalen;
+ connection->ocnt = ntohl(connection->ocnt);
+
+ // Revert lengths
+ connection->length = ntohl(connection->length);
+ connection->length16 = ntohs(connection->length16);
+
+ // Now return the bytes.
+ if (bytes < sizeof(*connection)+datalen) {
+ return -1; // blah
+ } else {
+ return bytes - 28; // actual length sent. :/
+ }
+
+ return bytes; // or something
+}
+
+/* mux_recv(phone, connection, data, datalen)
+ * This is a higher-level USBMuxTCP-like function
+ * phone: the phone to receive data from.
+ * connection: the connection to receive data on.
+ * data: where to put the data we receive.
+ * datalen: how much data to read.
+ *
+ * Returns: how many bytes were read, or -1 if something bad happens.
+ */
+
+int mux_recv(iPhone *phone, usbmux_tcp_header *connection, char *data, uint32 datalen) {
+ char *buffer = (char*)malloc(sizeof(*connection) + sizeof(datalen) + datalen);
+ int bytes = 0, my_datalen = 0;
+ if (debug) printf("mux_recv: datalen == %i\n", datalen);
+ bytes = recv_from_phone(phone, buffer, sizeof(*connection) + datalen);
+ if (debug) printf("mux_recv: bytes == %i\n", bytes);
+ if (bytes < datalen) {
+ if (bytes < 28) {
+ // if they didn't do that annoying thing, something else mighta happened.
+ if (debug) printf("mux_recv: bytes too low anyway!\n");
+ free(buffer);
+ return -1;
+ } else if (bytes == 28) { // no data...
+ free(buffer);
+ return 0;
+ } else { // bytes > 28
+ my_datalen = ntohl(buffer[4]) - 28;
+ connection->ocnt += my_datalen;
+ memcpy(data, buffer+28, bytes - 28);
+ free(buffer);
+ if (debug) printf("mux_recv: bytes received: %i\n", bytes - 28);
+ return bytes - 28;
+ }
+ } else {// all's good, they didn't do anything bonky.
+ my_datalen = ntohl(buffer[4]) - 28;
+ connection->ocnt += my_datalen;
+ memcpy(data, buffer+28, datalen);
+ free(buffer);
+ if (debug) printf("mux_recv: bytes received: %i\n", bytes - 28);
+ return bytes - 28;
+ }
+
+ return bytes;
+}
+
diff --git a/usbmux.h b/usbmux.h
new file mode 100644
index 0000000..921f4b7
--- /dev/null
+++ b/usbmux.h
@@ -0,0 +1,39 @@
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+
+#ifndef USBMUX_H
+#define USBMUX_H
+
+#ifndef IPHONE_H
+#include "iphone.h"
+#endif
+
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+typedef uint8_t uint8;
+
+
+typedef struct {
+ uint32 type, length;
+ uint16 sport, dport;
+ uint32 scnt, ocnt;
+ uint8 offset, tcp_flags;
+ uint16 window, nullnull, length16;
+} usbmux_tcp_header;
+
+usbmux_tcp_header *new_mux_packet(uint16 s_port, uint16 d_port);
+
+typedef struct {
+ uint32 type, length, major, minor, allnull;
+} usbmux_version_header;
+
+usbmux_version_header *version_header();
+
+usbmux_tcp_header *mux_connect(iPhone *phone, uint16 s_port, uint16 d_port);
+void mux_close_connection(iPhone *phone, usbmux_tcp_header *connection);
+int mux_send(iPhone *phone, usbmux_tcp_header *connection, char *data, uint32 datalen);
+int mux_recv(iPhone *phone, usbmux_tcp_header *connection, char *data, uint32 datalen);
+#endif