diff options
author | BALATON Zoltan | 2014-11-04 18:12:18 +0100 |
---|---|---|
committer | BALATON Zoltan | 2014-11-04 18:12:18 +0100 |
commit | 7fdcedea3aac19ef8a2a660cfe809bd8c0f5f57f (patch) | |
tree | d7aa72cae6b1a4b0eaef3cc4d9f27cc9bfdd69d0 | |
parent | aa4e3ff482972521213ad7505fea8a4627e22804 (diff) | |
download | idevicerestore-7fdcedea3aac19ef8a2a660cfe809bd8c0f5f57f.tar.gz idevicerestore-7fdcedea3aac19ef8a2a660cfe809bd8c0f5f57f.tar.bz2 |
Implement the FDR forwarder proxy service used during restore of recent iOS versions.
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/fdr.c | 491 | ||||
-rw-r--r-- | src/fdr.h | 53 | ||||
-rw-r--r-- | src/restore.c | 21 | ||||
-rw-r--r-- | src/restore.h | 2 | ||||
-rw-r--r-- | src/socket.c | 392 | ||||
-rw-r--r-- | src/socket.h | 65 | ||||
-rw-r--r-- | src/thread.c | 99 | ||||
-rw-r--r-- | src/thread.h | 56 |
9 files changed, 1179 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index d823c2c..f169708 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,7 @@ AM_LDADD = $(AC_LDADD) bin_PROGRAMS = idevicerestore -idevicerestore_SOURCES = idevicerestore.c common.c tss.c fls.c mbn.c img3.c img4.c ipsw.c normal.c dfu.c recovery.c restore.c asr.c limera1n.c download.c locking.c +idevicerestore_SOURCES = idevicerestore.c common.c tss.c fls.c mbn.c img3.c img4.c ipsw.c normal.c dfu.c recovery.c restore.c asr.c fdr.c limera1n.c download.c locking.c socket.c thread.c idevicerestore_CFLAGS = $(AM_CFLAGS) idevicerestore_LDFLAGS = $(AM_LDFLAGS) idevicerestore_LDADD = $(AM_LDADD) diff --git a/src/fdr.c b/src/fdr.c new file mode 100644 index 0000000..8d5c9c5 --- /dev/null +++ b/src/fdr.c @@ -0,0 +1,491 @@ +/* + * fdr.c + * Connection proxy service used by FDR + * + * Copyright (c) 2014 BALATON Zoltan. 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 + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <libimobiledevice/libimobiledevice.h> + +#include "socket.h" /* from libimobiledevice/common */ +#include "common.h" +#include "idevicerestore.h" +#include "fdr.h" + +#define CTRL_PROTO_VERSION 2 +#define CTRL_PORT 0x43a /*14852*/ +#define CTRLCMD "BeginCtrl" +#define HELLOCMD "HelloConn" + +#define FDR_SYNC_MSG 0x1 +#define FDR_PROXY_MSG 0x105 +#define FDR_PLIST_MSG 0xbbaa + +static uint64_t conn_port; +static int serial; + +static int fdr_receive_plist(fdr_client_t fdr, plist_t* data); +static int fdr_send_plist(fdr_client_t fdr, plist_t data); +static int fdr_ctrl_handshake(fdr_client_t fdr); +static int fdr_sync_handshake(fdr_client_t fdr); +static int fdr_handle_sync_cmd(fdr_client_t fdr); +static int fdr_handle_plist_cmd(fdr_client_t fdr); +static int fdr_handle_proxy_cmd(fdr_client_t fdr); + +int fdr_connect(idevice_t device, fdr_type_t type, fdr_client_t* fdr) +{ + int res = -1, i = 0; + int attempts = 10; + idevice_connection_t connection = NULL; + idevice_error_t device_error = IDEVICE_E_SUCCESS; + uint16_t port = (type == FDR_CONN ? conn_port : CTRL_PORT); + + *fdr = NULL; + + debug("Connecting to FDR client at port %u\n", port); + + for (i = 1; i <= attempts; i++) { + device_error = idevice_connect(device, port, &connection); + if (device_error == IDEVICE_E_SUCCESS) { + break; + } + + if (i >= attempts) { + error("ERROR: Unable to connect to FDR client (%d)\n", device_error); + return -1; + } + + sleep(2); + debug("Retrying connection...\n"); + } + + fdr_client_t fdr_loc = calloc(1, sizeof(struct fdr_client)); + if (!fdr_loc) { + error("ERROR: Unable to allocate memory\n"); + return -1; + } + fdr_loc->connection = connection; + fdr_loc->device = device; + fdr_loc->type = type; + + /* Do handshake */ + if (type == FDR_CTRL) + res = fdr_ctrl_handshake(fdr_loc); + else if (type == FDR_CONN) + res = fdr_sync_handshake(fdr_loc); + + if (res) { + fdr_free(fdr_loc); + return -1; + } + + *fdr = fdr_loc; + + return 0; +} + +void fdr_free(fdr_client_t fdr) +{ + if (!fdr) + return; + + if (fdr->connection) { + idevice_disconnect(fdr->connection); + fdr->connection = NULL; + } + free(fdr); + fdr = NULL; +} + +int fdr_poll_and_handle_message(fdr_client_t fdr) +{ + idevice_error_t device_error = IDEVICE_E_SUCCESS; + uint32_t bytes = 0; + uint16_t cmd; + + if (!fdr) { + error("ERROR: Invalid FDR client\n"); + return -1; + } + + device_error = idevice_connection_receive_timeout(fdr->connection, (char *)&cmd, sizeof(cmd), &bytes, 20000); + if (device_error != IDEVICE_E_SUCCESS) { + error("ERROR: Unable to receive message from FDR %p (%d). %u/%d bytes\n", + fdr, device_error, bytes, sizeof(cmd)); + return -1; + } else if (bytes != sizeof(cmd)) { + debug("FDR %p timeout waiting for command\n", fdr); + return 0; + } + + if (cmd == FDR_SYNC_MSG) { + debug("FDR %p got sync message\n", fdr); + return fdr_handle_sync_cmd(fdr); + } + + if (cmd == FDR_PROXY_MSG) { + debug("FDR %p got proxy message\n", fdr); + return fdr_handle_proxy_cmd(fdr); + } + + if (cmd == FDR_PLIST_MSG) { + debug("FDR %p got plist message\n", fdr); + return fdr_handle_plist_cmd(fdr); + } + + error("WARNING: FDR %p received unknown packet %#x of size %u\n", fdr, cmd, bytes); + return 0; +} + +void *fdr_listener_thread(void *cdata) +{ + fdr_client_t fdr = cdata; + int res; + + while (1) { + debug("FDR %p waiting for message...\n", fdr); + res = fdr_poll_and_handle_message(fdr); + if (fdr->type == FDR_CTRL && res >= 0) + continue; // main thread should always retry + if (res != 0) + break; + } + debug("FDR %p terminating...\n", fdr); + fdr_free(fdr); + return (void *)res; +} + +static int fdr_receive_plist(fdr_client_t fdr, plist_t* data) +{ + idevice_error_t device_error = IDEVICE_E_SUCCESS; + uint32_t len, bytes = 0; + char* buf = NULL; + + device_error = idevice_connection_receive(fdr->connection, (char*)&len, sizeof(len), &bytes); + if (device_error != IDEVICE_E_SUCCESS) { + error("ERROR: Unable to receive packet length from FDR (%d)\n", device_error); + return -1; + } + + buf = calloc(1, len); + if (!buf) { + error("ERROR: Unable to allocate memory for FDR receive buffer\n"); + return -1; + } + + device_error = idevice_connection_receive(fdr->connection, buf, len, &bytes); + if (device_error != IDEVICE_E_SUCCESS) { + error("ERROR: Unable to receive data from FDR\n"); + free(buf); + return -1; + } + plist_from_bin(buf, bytes, data); + free(buf); + + debug("FDR Received %d bytes\n", bytes); + + return 0; +} + +static int fdr_send_plist(fdr_client_t fdr, plist_t data) +{ + idevice_error_t device_error = IDEVICE_E_SUCCESS; + char *buf = NULL; + uint32_t len = 0, bytes = 0; + + if (!data) + return -1; + + plist_to_bin(data, &buf, &len); + if (!buf) + return -1; + + debug("FDR sending %d bytes:\n", len); + if (idevicerestore_debug) + debug_plist(data); + device_error = idevice_connection_send(fdr->connection, (char *)&len, sizeof(len), &bytes); + if (device_error != IDEVICE_E_SUCCESS || bytes != sizeof(len)) { + error("ERROR: FDR unable to send data length. (%d) Sent %u of %u bytes.\n", + device_error, bytes, sizeof(len)); + free(buf); + return -1; + } + device_error = idevice_connection_send(fdr->connection, buf, len, &bytes); + free(buf); + if (device_error != IDEVICE_E_SUCCESS || bytes != len) { + error("ERROR: FDR unable to send data (%d). Sent %u of %u bytes.\n", + device_error, bytes, len); + return -1; + } + + debug("FDR Sent %d bytes\n", bytes); + return 0; +} + +static int fdr_ctrl_handshake(fdr_client_t fdr) +{ + idevice_error_t device_error = IDEVICE_E_SUCCESS; + uint32_t bytes = 0, len = sizeof(CTRLCMD); + plist_t dict, node; + int res; + + debug("About to do ctrl handshake\n"); + + device_error = idevice_connection_send(fdr->connection, CTRLCMD, len, &bytes); + if (device_error != IDEVICE_E_SUCCESS || bytes != len) { + error("ERROR: FDR unable to send BeginCtrl. Sent %u of %u bytes.\n", bytes, len); + return -1; + } + + dict = plist_new_dict(); + plist_dict_set_item(dict, "Command", plist_new_string(CTRLCMD)); + plist_dict_set_item(dict, "CtrlProtoVersion", plist_new_uint(CTRL_PROTO_VERSION)); + res = fdr_send_plist(fdr, dict); + plist_free(dict); + if (res) { + error("ERROR: FDR could not send Begin command.\n"); + return -1; + } + + if (fdr_receive_plist(fdr, &dict)) { + error("ERROR: FDR did not get Begin command reply.\n"); + return -1; + } + if (idevicerestore_debug) + debug_plist(dict); + node = plist_dict_get_item(dict, "ConnPort"); + if (node && plist_get_node_type(node) == PLIST_UINT) + plist_get_uint_val(node, &conn_port); + else { + error("ERROR: Could not get FDR ConnPort value\n"); + return -1; + } + + plist_free(dict); + + debug("Ctrl handshake done (ConnPort = %u)\n", conn_port); + + return 0; +} + +static int fdr_sync_handshake(fdr_client_t fdr) +{ + idevice_error_t device_error = IDEVICE_E_SUCCESS; + uint32_t bytes = 0, len = sizeof(HELLOCMD); + plist_t reply; + + device_error = idevice_connection_send(fdr->connection, HELLOCMD, len, &bytes); + if (device_error != IDEVICE_E_SUCCESS || bytes != len) { + error("ERROR: FDR unable to send Hello. Sent %u of %u bytes.\n", bytes, len); + return -1; + } + if (fdr_receive_plist(fdr, &reply)) { + error("ERROR: FDR did not get Hello reply.\n"); + return -1; + } + + return 0; +} + +static int fdr_handle_sync_cmd(fdr_client_t fdr_ctrl) +{ + idevice_error_t device_error = IDEVICE_E_SUCCESS; + fdr_client_t fdr; + thread_t fdr_thread = 0; + int res = 0; + uint32_t bytes = 0; + char buf[4096]; + + device_error = idevice_connection_receive(fdr_ctrl->connection, buf, sizeof(buf), &bytes); + if (device_error != IDEVICE_E_SUCCESS || bytes != 2) { + error("ERROR: Unexpected data from FDR\n"); + return -1; + } + /* Open a new connection and wait for messages on it */ + if (fdr_connect(fdr_ctrl->device, FDR_CONN, &fdr)) { + error("ERROR: Failed to connect to FDR port\n"); + return -1; + } + debug("FDR connected in reply to sync message, starting command thread\n"); + res = thread_create(&fdr_thread, fdr_listener_thread, fdr); + if(res) { + error("ERROR: Failed to start FDR command thread\n"); + fdr_free(fdr); + } + return res; +} + +static int fdr_handle_plist_cmd(fdr_client_t fdr) +{ + int res = 0; + plist_t dict; + + if (fdr_receive_plist(fdr, &dict)) { + error("ERROR: FDR %p could not receive plist command.\n", fdr); + return -1; + } + plist_t node = plist_dict_get_item(dict, "Command"); + if (!node || (plist_get_node_type(node) != PLIST_STRING)) { + error("ERROR: FDR %p Could not find Command in plist command\n", fdr); + plist_free(dict); + return -1; + } + char *command = NULL; + plist_get_string_val(node, &command); + plist_free(dict); + + if (!command) { + info("FDR %p received empty plist command\n", fdr); + return -1; + } + + if (!strcmp(command, "Ping")) { + dict = plist_new_dict(); + plist_dict_set_item(dict, "Pong", plist_new_bool(1)); + res = fdr_send_plist(fdr, dict); + plist_free(dict); + if (res) { + error("ERROR: FDR %p could not send Ping command reply.\n", fdr); + free(command); + return -1; + } + } else { + error("WARNING: FDR %p received unknown plist command: %s\n", fdr, command); + free(command); + return -1; + } + + free(command); + return 1; /* should terminate thread */ +} + +static int fdr_handle_proxy_cmd(fdr_client_t fdr) +{ + idevice_error_t device_error = IDEVICE_E_SUCCESS; + char buf[16*1024]; + uint32_t sent = 0, bytes = 0; + char *host = NULL; + uint16_t port = 0; + + device_error = idevice_connection_receive(fdr->connection, buf, sizeof(buf), &bytes); + if (device_error != IDEVICE_E_SUCCESS) { + error("ERROR: FDR %p failed to read data for proxy command\n", fdr); + return -1; + } + debug("Got proxy command with %u bytes\n", bytes); + + /* Just return success here unconditionally because we don't know + * anything else and we will eventually abort on failure anyway */ + uint16_t ack = 5; + device_error = idevice_connection_send(fdr->connection, (char *)&ack, sizeof(ack), &sent); + if (device_error != IDEVICE_E_SUCCESS || sent != sizeof(ack)) { + error("ERROR: FDR %p unable to send ack. Sent %u of %u bytes.\n", + fdr, sent, sizeof(ack)); + return -1; + } + + if (bytes < 3) { + debug("FDR %p proxy command data too short, retrying\n", fdr); + return fdr_poll_and_handle_message(fdr); + } + + /* ack command data too */ + device_error = idevice_connection_send(fdr->connection, buf, bytes, &sent); + if (device_error != IDEVICE_E_SUCCESS || sent != bytes) { + error("ERROR: FDR %p unable to send data. Sent %u of %u bytes.\n", + fdr, sent, bytes); + return -1; + } + + /* Now try to handle actual messages */ + /* Connect: 0 3 hostlen <host> <port> */ + if (buf[0] == 0 && buf[1] == 3) { + uint16_t *p = (uint16_t *)&buf[bytes - 2]; + port = be16toh(*p); + buf[bytes - 2] = '\0'; + host = strdup(&buf[3]); + debug("FDR %p Proxy connect request to %s:%u\n", fdr, host, port); + } + + if (!host || !buf[2]) /* missing or zero length host name */ + return 0; + + /* else wait for messages and forward them */ + int sockfd = socket_connect(host, port); + free(host); + if (sockfd < 0) { + error("ERROR: Failed to connect socket: %s\n", strerror(errno)); + return -1; + } + + int res = 0, bytes_ret; + while (1) { + bytes = 0; + device_error = idevice_connection_receive_timeout(fdr->connection, buf, sizeof(buf), &bytes, 100); + if (device_error != IDEVICE_E_SUCCESS) { + error("ERROR: FDR %p Unable to receive proxy payload (%d)\n", fdr, device_error); + res = -1; + break; + } else if (!bytes) { + //debug("WARNING: Timeout waiting for proxy payload. %p\n", fdr); + } + if (bytes) { + debug("FDR %p got payload of %u bytes, now try to proxy it\n", fdr, bytes); + debug("Sending %u bytes of data\n", bytes); + sent = socket_send(sockfd, buf, bytes); + if (sent != bytes) { + error("ERROR: Sending proxy payload failed: %s\n", strerror(errno)); + socket_close(sockfd); + res = -1; + break; + } + } + bytes_ret = socket_receive_timeout(sockfd, buf, sizeof(buf), 0, 100); + if (bytes_ret < 0) { + if (errno) + error("ERROR: FDR %p receiving proxy payload failed: %s\n", + fdr, strerror(errno)); + else + res = 1; /* close connection if no data with no error */ + break; + } + + bytes = bytes_ret; + if (bytes) { + debug("FDR %p Received %u bytes reply data,%s sending to device\n", + fdr, bytes, (bytes ? "" : " not")); + + device_error = idevice_connection_send(fdr->connection, buf, bytes, &sent); + if (device_error != IDEVICE_E_SUCCESS || bytes != sent) { + error("ERROR: FDR %p unable to send data (%d). Sent %u of %u bytes.\n", + fdr, device_error, sent, bytes); + res = -1; + break; + } + } else serial++; + } + socket_close(sockfd); + return res; +} diff --git a/src/fdr.h b/src/fdr.h new file mode 100644 index 0000000..6139240 --- /dev/null +++ b/src/fdr.h @@ -0,0 +1,53 @@ +/* + * fdr.h + * Functions for handling FDR connections + * + * Copyright (c) 2014 BALATON Zoltan. 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 IDEVICERESTORE_FDR_H +#define IDEVICERESTORE_FDR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libimobiledevice/libimobiledevice.h> +#include "thread.h" /* from libimobiledevice/common */ + +typedef enum { + FDR_CTRL, + FDR_CONN +} fdr_type_t; + +struct fdr_client { + idevice_connection_t connection; + idevice_t device; + fdr_type_t type; +}; +typedef struct fdr_client *fdr_client_t; + +int fdr_connect(idevice_t device, fdr_type_t type, fdr_client_t *fdr); +void fdr_free(fdr_client_t fdr); +int fdr_poll_and_handle_message(fdr_client_t fdr); +void *fdr_listener_thread(void *cdata); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/restore.c b/src/restore.c index 3d70017..9775562 100644 --- a/src/restore.c +++ b/src/restore.c @@ -31,6 +31,7 @@ #include "idevicerestore.h" #include "asr.h" +#include "fdr.h" #include "fls.h" #include "mbn.h" #include "tss.h" @@ -1663,6 +1664,7 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit idevice_t device = NULL; restored_client_t restore = NULL; restored_error_t restore_error = RESTORE_E_SUCCESS; + thread_t fdr_thread = 0; restore_finished = 0; @@ -1743,6 +1745,25 @@ int restore_device(struct idevicerestore_client_t* client, plist_t build_identit client->restore->bbtss = plist_copy(client->tss); } + node = plist_access_path(build_identity, 2, "Info", "FDRSupport"); + if (node && plist_get_node_type(node) == PLIST_BOOLEAN) { + uint8_t b = 0; + plist_get_bool_val(node, &b); + if (b) { + fdr_client_t fdr_control_channel = NULL; + info("FDRSupport indicated, starting FDR listener thread\n"); + if (!fdr_connect(device, FDR_CTRL, &fdr_control_channel)) { + if(thread_create(&fdr_thread, fdr_listener_thread, fdr_control_channel)) { + error("ERROR: Failed to start FDR listener thread\n"); + fdr_thread = 0; /* undefined after failure */ + } + } else { + error("ERROR: Failed to start FDR Ctrl channel\n"); + // FIXME: We might want to return failure here as it will likely fail + } + } + } + plist_t opts = plist_new_dict(); // FIXME: required? //plist_dict_set_item(opts, "AuthInstallRestoreBehavior", plist_new_string("Erase")); diff --git a/src/restore.h b/src/restore.h index 7b3be7f..8dc9a45 100644 --- a/src/restore.h +++ b/src/restore.h @@ -58,7 +58,7 @@ int restore_send_kernelcache(restored_client_t restore, struct idevicerestore_cl int restore_device(struct idevicerestore_client_t* client, plist_t build_identity, const char* filesystem); int restore_open_with_timeout(struct idevicerestore_client_t* client); int restore_send_filesystem(struct idevicerestore_client_t* client, idevice_t device, const char* filesystem); - +int restore_send_fdr_trust_data(restored_client_t restore, idevice_t device); #ifdef __cplusplus } diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 0000000..c35de33 --- /dev/null +++ b/src/socket.c @@ -0,0 +1,392 @@ +/* + * socket.c + * + * Copyright (c) 2012 Martin Szulecki All Rights Reserved. + * Copyright (c) 2012 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 + */ + +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/time.h> +#include <sys/stat.h> +#ifdef WIN32 +#include <winsock2.h> +#include <windows.h> +static int wsa_init = 0; +#else +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#endif +#include "socket.h" + +#define RECV_TIMEOUT 20000 + +static int verbose = 0; + +void socket_set_verbose(int level) +{ + verbose = level; +} + +#ifndef WIN32 +int socket_create_unix(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"); + socket_close(sock); + return -1; + } + + if (listen(sock, 10) < 0) { + perror("listen"); + socket_close(sock); + return -1; + } + + return sock; +} + +int socket_connect_unix(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) { + socket_close(sfd); + if (verbose >= 2) + fprintf(stderr, "%s: connect: %s\n", __func__, + strerror(errno)); + return -1; + } + + return sfd; +} +#endif + +int socket_create(uint16_t port) +{ + int sfd = -1; + int yes = 1; +#ifdef WIN32 + WSADATA wsa_data; + if (!wsa_init) { + if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) { + fprintf(stderr, "WSAStartup failed!\n"); + ExitProcess(-1); + } + wsa_init = 1; + } +#endif + 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, (void*)&yes, sizeof(int)) == -1) { + perror("setsockopt()"); + socket_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()"); + socket_close(sfd); + return -1; + } + + if (listen(sfd, 1) == -1) { + perror("listen()"); + socket_close(sfd); + return -1; + } + + return sfd; +} + +int socket_connect(const char *addr, uint16_t port) +{ + int sfd = -1; + int yes = 1; + struct hostent *hp; + struct sockaddr_in saddr; +#ifdef WIN32 + WSADATA wsa_data; + if (!wsa_init) { + if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) { + fprintf(stderr, "WSAStartup failed!\n"); + ExitProcess(-1); + } + wsa_init = 1; + } +#endif + + 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, (void*)&yes, sizeof(int)) == -1) { + perror("setsockopt()"); + socket_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"); + socket_close(sfd); + return -2; + } + + return sfd; +} + +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; + } + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + 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; + } + + sret = -1; + + do { + 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; + } + + 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 socket_accept(int fd, uint16_t port) +{ +#ifdef WIN32 + int addr_len; +#else + socklen_t addr_len; +#endif + int result; + struct sockaddr_in addr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); + + addr_len = sizeof(addr); + result = accept(fd, (struct sockaddr*)&addr, &addr_len); + + return result; +} + +int socket_shutdown(int fd, int how) +{ + return shutdown(fd, how); +} + +int socket_close(int fd) { +#ifdef WIN32 + return closesocket(fd); +#else + return close(fd); +#endif +} + +int socket_receive(int fd, void *data, size_t length) +{ + return socket_receive_timeout(fd, data, length, 0, RECV_TIMEOUT); +} + +int socket_peek(int fd, void *data, size_t length) +{ + return socket_receive_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT); +} + +int socket_receive_timeout(int fd, void *data, size_t length, int flags, + unsigned int timeout) +{ + int res; + int result; + + // check if data is available + res = socket_check_fd(fd, FDM_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 socket_send(int fd, void *data, size_t length) +{ + return send(fd, data, length, 0); +} diff --git a/src/socket.h b/src/socket.h new file mode 100644 index 0000000..c2b2599 --- /dev/null +++ b/src/socket.h @@ -0,0 +1,65 @@ +/* + * socket.h + * + * Copyright (c) 2012 Martin Szulecki All Rights Reserved. + * Copyright (c) 2012 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 __SOCKET_SOCKET_H +#define __SOCKET_SOCKET_H + +#include <stdlib.h> +#include <stdint.h> + +enum fd_mode { + FDM_READ, + FDM_WRITE, + FDM_EXCEPT +}; +typedef enum fd_mode fd_mode; + +#ifdef WIN32 +#include <winsock2.h> +#define SHUT_RD SD_READ +#define SHUT_WR SD_WRITE +#define SHUT_RDWR SD_BOTH +#else +#include <sys/socket.h> +#endif + +#ifndef WIN32 +int socket_create_unix(const char *filename); +int socket_connect_unix(const char *filename); +#endif +int socket_create(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); + +int socket_shutdown(int fd, int how); +int socket_close(int fd); + +int socket_receive(int fd, void *data, size_t size); +int socket_peek(int fd, void *data, size_t size); +int socket_receive_timeout(int fd, void *data, size_t size, int flags, + unsigned int timeout); + +int socket_send(int fd, void *data, size_t size); + +void socket_set_verbose(int level); + +#endif /* __SOCKET_SOCKET_H */ diff --git a/src/thread.c b/src/thread.c new file mode 100644 index 0000000..d6d6c1a --- /dev/null +++ b/src/thread.c @@ -0,0 +1,99 @@ +/* + * thread.c + * + * Copyright (c) 2012 Martin Szulecki All Rights Reserved. + * Copyright (c) 2012 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 + */ + +#include "thread.h" + +int thread_create(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); + if (th == NULL) { + return -1; + } + *thread = th; + return 0; +#else + int res = pthread_create(thread, NULL, thread_func, data); + return res; +#endif +} + +void thread_join(thread_t thread) +{ + /* wait for thread to complete */ +#ifdef WIN32 + WaitForSingleObject(thread, INFINITE); +#else + pthread_join(thread, NULL); +#endif +} + +void mutex_init(mutex_t* mutex) +{ +#ifdef WIN32 + InitializeCriticalSection(mutex); +#else + pthread_mutex_init(mutex, NULL); +#endif +} + +void mutex_destroy(mutex_t* mutex) +{ +#ifdef WIN32 + DeleteCriticalSection(mutex); +#else + pthread_mutex_destroy(mutex); +#endif +} + +void mutex_lock(mutex_t* mutex) +{ +#ifdef WIN32 + EnterCriticalSection(mutex); +#else + pthread_mutex_lock(mutex); +#endif +} + +void mutex_unlock(mutex_t* mutex) +{ +#ifdef WIN32 + LeaveCriticalSection(mutex); +#else + pthread_mutex_unlock(mutex); +#endif +} + +void thread_once(thread_once_t *once_control, void (*init_routine)(void)) +{ +#ifdef WIN32 + while (InterlockedExchange(&(once_control->lock), 1) != 0) { + Sleep(1); + } + if (!once_control->state) { + once_control->state = 1; + init_routine(); + } + InterlockedExchange(&(once_control->lock), 0); +#else + pthread_once(once_control, init_routine); +#endif +} diff --git a/src/thread.h b/src/thread.h new file mode 100644 index 0000000..9b15cc4 --- /dev/null +++ b/src/thread.h @@ -0,0 +1,56 @@ +/* + * thread.h + * + * Copyright (c) 2012 Martin Szulecki All Rights Reserved. + * Copyright (c) 2012 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 __THREAD_H +#define __THREAD_H + +#ifdef WIN32 +#include <windows.h> +typedef HANDLE thread_t; +typedef CRITICAL_SECTION mutex_t; +typedef volatile struct { + LONG lock; + int state; +} thread_once_t; +#define THREAD_ONCE_INIT {0, 0} +#define THREAD_ID GetCurrentThreadId() +#else +#include <pthread.h> +typedef pthread_t thread_t; +typedef pthread_mutex_t mutex_t; +typedef pthread_once_t thread_once_t; +#define THREAD_ONCE_INIT PTHREAD_ONCE_INIT +#define THREAD_ID pthread_self() +#endif + +typedef void* (*thread_func_t)(void* data); + +int thread_create(thread_t* thread, thread_func_t thread_func, void* data); +void thread_join(thread_t thread); + +void mutex_init(mutex_t* mutex); +void mutex_destroy(mutex_t* mutex); +void mutex_lock(mutex_t* mutex); +void mutex_unlock(mutex_t* mutex); + +void thread_once(thread_once_t *once_control, void (*init_routine)(void)); + +#endif |