summaryrefslogtreecommitdiffstats
path: root/libusbmuxd
diff options
context:
space:
mode:
authorGravatar Hector Martin2009-08-20 03:42:52 +0200
committerGravatar Hector Martin2009-08-20 06:51:10 +0200
commit1a0c58e4062da7db73b4c08963f741cf016f6aa5 (patch)
treeb250f094e978d48622ecf652e28e9f3b68873893 /libusbmuxd
parentf4854f3fd725b5ba49cd5157d941783cffa08c04 (diff)
parent79ca4d9a3c3a82bb5a3f9be1ac7a2533c7a89b05 (diff)
downloadusbmuxd-1a0c58e4062da7db73b4c08963f741cf016f6aa5.tar.gz
usbmuxd-1a0c58e4062da7db73b4c08963f741cf016f6aa5.tar.bz2
Merge the two development histories
Diffstat (limited to 'libusbmuxd')
-rw-r--r--libusbmuxd/CMakeLists.txt12
-rw-r--r--libusbmuxd/libusbmuxd.c248
-rw-r--r--libusbmuxd/sock_stuff.c301
-rw-r--r--libusbmuxd/sock_stuff.h28
-rw-r--r--libusbmuxd/usbmuxd-proto.h52
-rw-r--r--libusbmuxd/usbmuxd.h91
6 files changed, 732 insertions, 0 deletions
diff --git a/libusbmuxd/CMakeLists.txt b/libusbmuxd/CMakeLists.txt
new file mode 100644
index 0000000..61de1a8
--- /dev/null
+++ b/libusbmuxd/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_library (libusbmuxd libusbmuxd.c sock_stuff.c)
+
+# 'lib' is a UNIXism, the proper CMake target is usbmuxd
+# But we can't use that due to the conflict with the usbmuxd daemon,
+# so instead change the library output base name to usbmuxd here
+set_target_properties(libusbmuxd PROPERTIES OUTPUT_NAME usbmuxd)
+
+install(TARGETS libusbmuxd
+ ARCHIVE DESTINATION lib
+ LIBRARY DESTINATION lib
+)
+install(FILES usbmuxd.h usbmuxd-proto.h DESTINATION include)
diff --git a/libusbmuxd/libusbmuxd.c b/libusbmuxd/libusbmuxd.c
new file mode 100644
index 0000000..090695f
--- /dev/null
+++ b/libusbmuxd/libusbmuxd.c
@@ -0,0 +1,248 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+
+// usbmuxd public interface
+#include "usbmuxd.h"
+// usbmuxd protocol
+#include "usbmuxd-proto.h"
+// socket utility functions
+#include "sock_stuff.h"
+
+static int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t * result)
+{
+ struct usbmuxd_result res;
+ int recv_len;
+
+ if (!result) {
+ return -EINVAL;
+ }
+
+ if ((recv_len = recv_buf(sfd, &res, sizeof(res))) <= 0) {
+ perror("recv");
+ return -errno;
+ } else {
+ if ((recv_len == sizeof(res))
+ && (res.header.length == (uint32_t) recv_len)
+ && (res.header.reserved == 0)
+ && (res.header.type == USBMUXD_RESULT)
+ ) {
+ *result = res.result;
+ if (res.header.tag == tag) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+int usbmuxd_scan(usbmuxd_scan_result ** available_devices)
+{
+ struct usbmuxd_scan_request s_req;
+ int sfd;
+ int scan_success = 0;
+ uint32_t res;
+ uint32_t pktlen;
+ int recv_len;
+ usbmuxd_scan_result *newlist = NULL;
+ struct usbmuxd_device_info_record dev_info_pkt;
+ int dev_cnt = 0;
+
+ sfd = connect_unix_socket(USBMUXD_SOCKET_FILE);
+ if (sfd < 0) {
+ fprintf(stderr, "%s: error opening socket!\n", __func__);
+ return sfd;
+ }
+
+ s_req.header.length = sizeof(struct usbmuxd_scan_request);
+ s_req.header.reserved = 0;
+ s_req.header.type = USBMUXD_SCAN;
+ s_req.header.tag = 2;
+
+ // send scan request packet
+ if (send_buf(sfd, &s_req, s_req.header.length) ==
+ (int) s_req.header.length) {
+ res = -1;
+ // get response
+ if (usbmuxd_get_result(sfd, s_req.header.tag, &res) && (res == 0)) {
+ scan_success = 1;
+ } else {
+ fprintf(stderr,
+ "%s: Did not get response to scan request (with result=0)...\n",
+ __func__);
+ close(sfd);
+ return res;
+ }
+ }
+
+ if (!scan_success) {
+ fprintf(stderr, "%s: Could not send scan request!\n", __func__);
+ return -1;
+ }
+
+ *available_devices = NULL;
+ // receive device list
+ while (1) {
+ if (recv_buf_timeout(sfd, &pktlen, 4, MSG_PEEK, 1000) == 4) {
+ if (pktlen != sizeof(dev_info_pkt)) {
+ // invalid packet size received!
+ fprintf(stderr,
+ "%s: Invalid packet size (%d) received when expecting a device info record.\n",
+ __func__, pktlen);
+ break;
+ }
+
+ recv_len = recv_buf(sfd, &dev_info_pkt, pktlen);
+ if (recv_len <= 0) {
+ fprintf(stderr,
+ "%s: Error when receiving device info record\n",
+ __func__);
+ break;
+ } else if ((uint32_t) recv_len < pktlen) {
+ fprintf(stderr,
+ "%s: received less data than specified in header!\n",
+ __func__);
+ } else {
+ //fprintf(stderr, "%s: got device record with id %d, UUID=%s\n", __func__, dev_info_pkt.device_info.device_id, dev_info_pkt.device_info.serial_number);
+ newlist =
+ (usbmuxd_scan_result *) realloc(*available_devices,
+ sizeof
+ (usbmuxd_scan_result) *
+ (dev_cnt + 1));
+ if (newlist) {
+ newlist[dev_cnt].handle =
+ (int) dev_info_pkt.device.device_id;
+ newlist[dev_cnt].product_id =
+ dev_info_pkt.device.product_id;
+ memset(newlist[dev_cnt].serial_number, '\0',
+ sizeof(newlist[dev_cnt].serial_number));
+ memcpy(newlist[dev_cnt].serial_number,
+ dev_info_pkt.device.serial_number,
+ sizeof(dev_info_pkt.device.serial_number));
+ *available_devices = newlist;
+ dev_cnt++;
+ } else {
+ fprintf(stderr,
+ "%s: ERROR: out of memory when trying to realloc!\n",
+ __func__);
+ break;
+ }
+ }
+ } else {
+ // we _should_ have all of them now.
+ // or perhaps an error occured.
+ break;
+ }
+ }
+
+ // terminating zero record
+ newlist =
+ (usbmuxd_scan_result *) realloc(*available_devices,
+ sizeof(usbmuxd_scan_result) *
+ (dev_cnt + 1));
+ memset(newlist + dev_cnt, 0, sizeof(usbmuxd_scan_result));
+ *available_devices = newlist;
+
+ return dev_cnt;
+}
+
+int usbmuxd_connect(const int handle, const unsigned short tcp_port)
+{
+ int sfd;
+ struct usbmuxd_connect_request c_req;
+ int connected = 0;
+ uint32_t res = -1;
+
+ sfd = connect_unix_socket(USBMUXD_SOCKET_FILE);
+ if (sfd < 0) {
+ fprintf(stderr, "%s: Error: Connection to usbmuxd failed: %s\n",
+ __func__, strerror(errno));
+ return sfd;
+ }
+
+ c_req.header.length = sizeof(c_req);
+ c_req.header.reserved = 0;
+ c_req.header.type = USBMUXD_CONNECT;
+ c_req.header.tag = 3;
+ c_req.device_id = (uint32_t) handle;
+ c_req.tcp_dport = htons(tcp_port);
+ c_req.reserved = 0;
+
+ if (send_buf(sfd, &c_req, sizeof(c_req)) < 0) {
+ perror("send");
+ } else {
+ // read ACK
+ //fprintf(stderr, "%s: Reading connect result...\n", __func__);
+ if (usbmuxd_get_result(sfd, c_req.header.tag, &res)) {
+ if (res == 0) {
+ //fprintf(stderr, "%s: Connect success!\n", __func__);
+ connected = 1;
+ } else {
+ fprintf(stderr, "%s: Connect failed, Error code=%d\n",
+ __func__, res);
+ }
+ }
+ }
+
+ if (connected) {
+ return sfd;
+ }
+
+ close(sfd);
+
+ return -1;
+}
+
+int usbmuxd_disconnect(int sfd)
+{
+ return close(sfd);
+}
+
+int usbmuxd_send(int sfd, const char *data, uint32_t len, uint32_t *sent_bytes)
+{
+ int num_sent;
+
+ if (sfd < 0) {
+ return -EINVAL;
+ }
+
+ num_sent = send(sfd, (void*)data, len, 0);
+ if (num_sent < 0) {
+ *sent_bytes = 0;
+ fprintf(stderr, "%s: Error %d when sending: %s\n", __func__, num_sent, strerror(errno));
+ return num_sent;
+ } else if ((uint32_t)num_sent < len) {
+ fprintf(stderr, "%s: Warning: Did not send enough (only %d of %d)\n", __func__, num_sent, len);
+ }
+
+ *sent_bytes = num_sent;
+
+ return 0;
+}
+
+int usbmuxd_recv_timeout(int sfd, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout)
+{
+ int num_recv = recv_buf_timeout(sfd, (void*)data, len, 0, timeout);
+ if (num_recv < 0) {
+ *recv_bytes = 0;
+ return num_recv;
+ }
+
+ *recv_bytes = num_recv;
+
+ return 0;
+}
+
+int usbmuxd_recv(int sfd, char *data, uint32_t len, uint32_t *recv_bytes)
+{
+ return usbmuxd_recv_timeout(sfd, data, len, recv_bytes, 5000);
+}
+
diff --git a/libusbmuxd/sock_stuff.c b/libusbmuxd/sock_stuff.c
new file mode 100644
index 0000000..137375d
--- /dev/null
+++ b/libusbmuxd/sock_stuff.c
@@ -0,0 +1,301 @@
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include "sock_stuff.h"
+
+#define RECV_TIMEOUT 20000
+
+static int verbose = 0;
+
+void sock_stuff_set_verbose(int level)
+{
+ verbose = level;
+}
+
+int create_unix_socket(const char *filename)
+{
+ struct sockaddr_un name;
+ int sock;
+ size_t size;
+
+ // remove if still present
+ unlink(filename);
+
+ /* Create the socket. */
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ /* Bind a name to the socket. */
+ name.sun_family = AF_LOCAL;
+ strncpy(name.sun_path, filename, sizeof(name.sun_path));
+ name.sun_path[sizeof(name.sun_path) - 1] = '\0';
+
+ /* The size of the address is
+ the offset of the start of the filename,
+ plus its length,
+ plus one for the terminating null byte.
+ Alternatively you can just do:
+ size = SUN_LEN (&name);
+ */
+ size = (offsetof(struct sockaddr_un, sun_path)
+ + strlen(name.sun_path) + 1);
+
+ if (bind(sock, (struct sockaddr *) &name, size) < 0) {
+ perror("bind");
+ close(sock);
+ return -1;
+ }
+
+ if (listen(sock, 10) < 0) {
+ perror("listen");
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+int connect_unix_socket(const char *filename)
+{
+ struct sockaddr_un name;
+ int sfd = -1;
+ size_t size;
+ struct stat fst;
+
+ // check if socket file exists...
+ if (stat(filename, &fst) != 0) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: stat '%s': %s\n", __func__, filename,
+ strerror(errno));
+ return -1;
+ }
+ // ... and if it is a unix domain socket
+ if (!S_ISSOCK(fst.st_mode)) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: File '%s' is not a socket!\n", __func__,
+ filename);
+ return -1;
+ }
+ // make a new socket
+ if ((sfd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: socket: %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ // and connect to 'filename'
+ name.sun_family = AF_LOCAL;
+ strncpy(name.sun_path, filename, sizeof(name.sun_path));
+ name.sun_path[sizeof(name.sun_path) - 1] = 0;
+
+ size = (offsetof(struct sockaddr_un, sun_path)
+ + strlen(name.sun_path) + 1);
+
+ if (connect(sfd, (struct sockaddr *) &name, size) < 0) {
+ close(sfd);
+ if (verbose >= 2)
+ fprintf(stderr, "%s: connect: %s\n", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ return sfd;
+}
+
+int create_socket(uint16_t port)
+{
+ int sfd = -1;
+ int yes = 1;
+ struct sockaddr_in saddr;
+
+ if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
+ perror("socket()");
+ return -1;
+ }
+
+ if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
+ perror("setsockopt()");
+ close(sfd);
+ return -1;
+ }
+
+ memset((void *) &saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr.sin_port = htons(port);
+
+ if (0 > bind(sfd, (struct sockaddr *) &saddr, sizeof(saddr))) {
+ perror("bind()");
+ close(sfd);
+ return -1;
+ }
+
+ if (listen(sfd, 1) == -1) {
+ perror("listen()");
+ close(sfd);
+ return -1;
+ }
+
+ return sfd;
+}
+
+int connect_socket(const char *addr, uint16_t port)
+{
+ int sfd = -1;
+ int yes = 1;
+ struct hostent *hp;
+ struct sockaddr_in saddr;
+
+ if (!addr) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((hp = gethostbyname(addr)) == NULL) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: unknown host '%s'\n", __func__, addr);
+ return -1;
+ }
+
+ if (!hp->h_addr) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: gethostbyname returned NULL address!\n",
+ __func__);
+ return -1;
+ }
+
+ if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
+ perror("socket()");
+ return -1;
+ }
+
+ if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
+ perror("setsockopt()");
+ close(sfd);
+ return -1;
+ }
+
+ memset((void *) &saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = *(uint32_t *) hp->h_addr;
+ saddr.sin_port = htons(port);
+
+ if (connect(sfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
+ perror("connect");
+ close(sfd);
+ return -2;
+ }
+
+ return sfd;
+}
+
+int check_fd(int fd, fd_mode fdm, unsigned int timeout)
+{
+ fd_set fds;
+ int sret;
+ int eagain;
+ struct timeval to;
+
+ if (fd <= 0) {
+ if (verbose >= 2)
+ fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd);
+ return -1;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ to.tv_sec = (time_t) (timeout / 1000);
+ to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000);
+
+ sret = -1;
+
+ do {
+ eagain = 0;
+ switch (fdm) {
+ case FD_READ:
+ sret = select(fd + 1, &fds, NULL, NULL, &to);
+ break;
+ case FD_WRITE:
+ sret = select(fd + 1, NULL, &fds, NULL, &to);
+ break;
+ case FD_EXCEPT:
+ sret = select(fd + 1, NULL, NULL, &fds, &to);
+ break;
+ default:
+ return -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;
+ }
+ }
+ } while (eagain);
+
+ return sret;
+}
+
+int recv_buf(int fd, void *data, size_t length)
+{
+ return recv_buf_timeout(fd, data, length, 0, RECV_TIMEOUT);
+}
+
+int peek_buf(int fd, void *data, size_t length)
+{
+ return recv_buf_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT);
+}
+
+int recv_buf_timeout(int fd, void *data, size_t length, int flags,
+ unsigned int timeout)
+{
+ int res;
+ int result;
+
+ // check if data is available
+ res = check_fd(fd, FD_READ, timeout);
+ if (res <= 0) {
+ return res;
+ }
+ // if we get here, there _is_ data available
+ result = recv(fd, data, length, flags);
+ if (res > 0 && result == 0) {
+ // but this is an error condition
+ if (verbose >= 3)
+ fprintf(stderr, "%s: fd=%d recv returned 0\n", __func__, fd);
+ return -EAGAIN;
+ }
+ if (result < 0) {
+ return -errno;
+ }
+ return result;
+}
+
+int send_buf(int fd, void *data, size_t length)
+{
+ return send(fd, data, length, 0);
+}
diff --git a/libusbmuxd/sock_stuff.h b/libusbmuxd/sock_stuff.h
new file mode 100644
index 0000000..190f7e1
--- /dev/null
+++ b/libusbmuxd/sock_stuff.h
@@ -0,0 +1,28 @@
+#ifndef __SOCK_STUFF_H
+#define __SOCK_STUFF_H
+
+#include <stdint.h>
+
+enum fd_mode {
+ FD_READ,
+ FD_WRITE,
+ FD_EXCEPT
+};
+typedef enum fd_mode fd_mode;
+
+int create_unix_socket(const char *filename);
+int connect_unix_socket(const char *filename);
+int create_socket(uint16_t port);
+int connect_socket(const char *addr, uint16_t port);
+int check_fd(int fd, fd_mode fdm, unsigned int timeout);
+
+int recv_buf(int fd, void *data, size_t size);
+int peek_buf(int fd, void *data, size_t size);
+int recv_buf_timeout(int fd, void *data, size_t size, int flags,
+ unsigned int timeout);
+
+int send_buf(int fd, void *data, size_t size);
+
+void sock_stuff_set_verbose(int level);
+
+#endif /* __SOCK_STUFF_H */
diff --git a/libusbmuxd/usbmuxd-proto.h b/libusbmuxd/usbmuxd-proto.h
new file mode 100644
index 0000000..7f8c2d6
--- /dev/null
+++ b/libusbmuxd/usbmuxd-proto.h
@@ -0,0 +1,52 @@
+/* Protocol defintion for usbmuxd proxy protocol */
+
+#ifndef __USBMUXD_PROTO_H
+#define __USBMUXD_PROTO_H
+
+#include <stdint.h>
+
+#define USBMUXD_SOCKET_FILE "/var/run/usbmuxd"
+
+struct usbmuxd_header {
+ uint32_t length; // length of message, including header
+ uint32_t reserved; // always zero
+ uint32_t type; // message type
+ uint32_t tag; // responses to this query will echo back this tag
+} __attribute__((__packed__));
+
+struct usbmuxd_result {
+ struct usbmuxd_header header;
+ uint32_t result;
+} __attribute__((__packed__));
+
+struct usbmuxd_connect_request {
+ struct usbmuxd_header header;
+ uint32_t device_id;
+ uint16_t tcp_dport; // TCP port number
+ uint16_t reserved; // set to zero
+} __attribute__((__packed__));
+
+struct usbmuxd_device {
+ uint32_t device_id;
+ uint16_t product_id;
+ char serial_number[40];
+} __attribute__((__packed__));
+
+struct usbmuxd_device_info_record {
+ struct usbmuxd_header header;
+ struct usbmuxd_device device;
+ char padding[222];
+} __attribute__((__packed__));
+
+struct usbmuxd_scan_request {
+ struct usbmuxd_header header;
+} __attribute__((__packed__));
+
+enum {
+ USBMUXD_RESULT = 1,
+ USBMUXD_CONNECT = 2,
+ USBMUXD_SCAN = 3,
+ USBMUXD_DEVICE_INFO = 4,
+};
+
+#endif /* __USBMUXD_PROTO_H */
diff --git a/libusbmuxd/usbmuxd.h b/libusbmuxd/usbmuxd.h
new file mode 100644
index 0000000..ba45ec3
--- /dev/null
+++ b/libusbmuxd/usbmuxd.h
@@ -0,0 +1,91 @@
+#ifndef __USBMUXD_H
+#define __USBMUXD_H
+
+/**
+ * Array entry returned by 'usbmuxd_scan()' scanning.
+ *
+ * If more than one device is available, 'product_id' and
+ * 'serial_number' and be analysed to help make a selection.
+ * The relevant 'handle' should be passed to 'usbmuxd_connect()', to
+ * start a proxy connection. The value 'handle' should be considered
+ * opaque and no presumption made about the meaning of its value.
+ */
+typedef struct {
+ int handle;
+ int product_id;
+ char serial_number[41];
+} usbmuxd_scan_result;
+
+/**
+ * Contacts usbmuxd and performs a scan for connected devices.
+ *
+ * @param available_devices pointer to array of usbmuxd_scan_result.
+ * Array of available devices. The required 'handle'
+ * should be passed to 'usbmuxd_connect()'. The returned array
+ * is zero-terminated for convenience; the final (unused)
+ * entry containing handle == 0. The returned array pointer
+ * should be freed by passing to 'free()' after use.
+ *
+ * @return number of available devices, zero on no devices, or negative on error
+ */
+int usbmuxd_scan(usbmuxd_scan_result **available_devices);
+
+/**
+ * Request proxy connect to
+ *
+ * @param handle returned by 'usbmuxd_scan()'
+ *
+ * @param tcp_port TCP port number on device, in range 0-65535.
+ * common values are 62078 for lockdown, and 22 for SSH.
+ *
+ * @return file descriptor socket of the connection, or -1 on error
+ */
+int usbmuxd_connect(const int handle, const unsigned short tcp_port);
+
+/**
+ * Disconnect. For now, this just closes the socket file descriptor.
+ *
+ * @param sfd socker file descriptor returned by usbmuxd_connect()
+ *
+ * @return 0 on success, -1 on error.
+ */
+int usbmuxd_disconnect(int sfd);
+
+/**
+ * Send data to the specified socket.
+ *
+ * @param sfd socket file descriptor returned by usbmuxd_connect()
+ * @param data buffer to send
+ * @param len size of buffer to send
+ * @param sent_bytes how many bytes sent
+ *
+ * @return 0 on success, a negative errno value otherwise.
+ */
+int usbmuxd_send(int sfd, const char *data, uint32_t len, uint32_t *sent_bytes);
+
+/**
+ * Receive data from the specified socket.
+ *
+ * @param sfd socket file descriptor returned by usbmuxd_connect()
+ * @param data buffer to put the data to
+ * @param len number of bytes to receive
+ * @param recv_bytes number of bytes received
+ * @param timeout how many milliseconds to wait for data
+ *
+ * @return 0 on success, a negative errno value otherwise.
+ */
+int usbmuxd_recv_timeout(int sfd, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout);
+
+/**
+ * Receive data from the specified socket with a default timeout.
+ *
+ * @param sfd socket file descriptor returned by usbmuxd_connect()
+ * @param data buffer to put the data to
+ * @param len number of bytes to receive
+ * @param recv_bytes number of bytes received
+ *
+ * @return 0 on success, a negative errno value otherwise.
+ */
+int usbmuxd_recv(int sfd, char *data, uint32_t len, uint32_t *recv_bytes);
+
+#endif /* __USBMUXD_H */