diff options
| -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  | 
