/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* gck-rpc-dispatch.h - receiver of our PKCS#11 protocol.

   Copyright (C) 2008, Stef Walter

   The Gnome Keyring Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The Gnome Keyring 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Author: Stef Walter <stef@memberwebs.com>
*/

#include "config.h"

#include "gck-rpc-layer.h"
#include "gck-rpc-private.h"

#include "pkcs11/pkcs11.h"
#include "pkcs11/pkcs11g.h"
#include "pkcs11/pkcs11i.h"

#include <sys/types.h>
#include <sys/param.h>
#ifdef __MINGW32__
# include <winsock2.h>
#else
# include <sys/socket.h>
# include <sys/un.h>
# include <arpa/inet.h>
# include <netinet/in.h>
# include <netinet/tcp.h>
#endif
#include <pthread.h>

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>

/* Where we dispatch the calls to */
static CK_FUNCTION_LIST_PTR pkcs11_module = NULL;

/* The error returned on protocol failures */
#define PARSE_ERROR CKR_DEVICE_ERROR
#define PREP_ERROR  CKR_DEVICE_MEMORY

/* -----------------------------------------------------------------------------
 * LOGGING and DEBUGGING
 */

#if DEBUG_OUTPUT
#define debug(x) gck_rpc_debug x
#else
#define debug(x)
#endif

#define warning(x) gck_rpc_warn x

#define return_val_if_fail(x, v) \
	if (!(x)) { rpc_warn ("'%s' not true at %s", #x, __func__); return v; }

void gck_rpc_log(const char *msg, ...)
{
	va_list ap;

	va_start(ap, msg);
	vfprintf(stdout, msg, ap);
	va_end(ap);
}

/* -------------------------------------------------------------------------------
 * CALL STRUCTURES
 */

typedef struct _CallState {
	GckRpcMessage *req;
	GckRpcMessage *resp;
	void *allocated;
	uint64_t appid;
} CallState;

static int call_init(CallState * cs)
{
	assert(cs);

	cs->req = gck_rpc_message_new((EggBufferAllocator) realloc);
	cs->resp = gck_rpc_message_new((EggBufferAllocator) realloc);
	if (!cs->req || !cs->resp) {
		gck_rpc_message_free(cs->req);
		gck_rpc_message_free(cs->resp);
		return 0;
	}

	cs->allocated = NULL;
	return 1;
}

static void *call_alloc(CallState * cs, size_t length)
{
	void **data;

	assert(cs);

	if (length > 0x7fffffff)
		return NULL;

	data = malloc(sizeof(void *) + length);
	if (!data)
		return NULL;

	/* Munch up the memory to help catch bugs */
	memset(data, 0xff, sizeof(void *) + length);

	/* Store pointer to next allocated block at beginning */
	*data = cs->allocated;
	cs->allocated = data;

	/* Data starts after first pointer */
	return (void *)(data + 1);
}

static void call_reset(CallState * cs)
{
	void *allocated;
	void **data;

	assert(cs);

	allocated = cs->allocated;
	while (allocated) {
		data = (void **)allocated;

		/* Pointer to the next allocation */
		allocated = *data;
		free(data);
	}

	cs->allocated = NULL;
	gck_rpc_message_reset(cs->req);
	gck_rpc_message_reset(cs->resp);
}

static void call_uninit(CallState * cs)
{
	assert(cs);

	call_reset(cs);

	gck_rpc_message_free(cs->req);
	gck_rpc_message_free(cs->resp);
}

/* -------------------------------------------------------------------
 * PROTOCOL CODE
 */

static CK_RV
proto_read_byte_buffer(CallState * cs, CK_BYTE_PTR * buffer,
		       CK_ULONG * n_buffer)
{
	GckRpcMessage *msg;
	uint32_t length;

	assert(cs);
	assert(buffer);
	assert(n_buffer);

	msg = cs->req;

	/* Check that we're supposed to be reading this at this point */
	assert(!msg->signature || gck_rpc_message_verify_part(msg, "fy"));

	/* The number of ulongs there's room for on the other end */
	if (!egg_buffer_get_uint32
	    (&msg->buffer, msg->parsed, &msg->parsed, &length))
		return PARSE_ERROR;

	*n_buffer = length;
	*buffer = NULL;

	/* If set to zero, then they just want the length */
	if (!length)
		return CKR_OK;

	*buffer = call_alloc(cs, length * sizeof(CK_BYTE));
	if (!*buffer)
		return CKR_DEVICE_MEMORY;

	return CKR_OK;
}

static CK_RV
proto_read_byte_array(CallState * cs, CK_BYTE_PTR * array, CK_ULONG * n_array)
{
	GckRpcMessage *msg;
	const unsigned char *data;
	unsigned char valid;
	size_t n_data;

	assert(cs);

	msg = cs->req;

	/* Check that we're supposed to have this at this point */
	assert(!msg->signature || gck_rpc_message_verify_part(msg, "ay"));

	/* Read out the byte which says whether data is present or not */
	if (!egg_buffer_get_byte
	    (&msg->buffer, msg->parsed, &msg->parsed, &valid))
		return PARSE_ERROR;

	if (!valid) {
		*array = NULL;
		*n_array = 0;
		return CKR_OK;
	}

	/* Point our arguments into the buffer */
	if (!egg_buffer_get_byte_array(&msg->buffer, msg->parsed, &msg->parsed,
				       &data, &n_data))
		return PARSE_ERROR;

	*array = (CK_BYTE_PTR) data;
	*n_array = n_data;
	return CKR_OK;
}

static CK_RV
proto_write_byte_array(CallState * cs, CK_BYTE_PTR array, CK_ULONG len,
		       CK_RV ret)
{
	assert(cs);

	/*
	 * When returning an byte array, in many cases we need to pass
	 * an invalid array along with a length, which signifies CKR_BUFFER_TOO_SMALL.
	 */

	switch (ret) {
	case CKR_BUFFER_TOO_SMALL:
		array = NULL;
		/* fall through */
	case CKR_OK:
		break;

		/* Pass all other errors straight through */
	default:
		return ret;
	};

	if (!gck_rpc_message_write_byte_array(cs->resp, array, len))
		return PREP_ERROR;

	return CKR_OK;
}

static CK_RV
proto_read_ulong_buffer(CallState * cs, CK_ULONG_PTR * buffer,
			CK_ULONG * n_buffer)
{
	GckRpcMessage *msg;
	uint32_t length;

	assert(cs);
	assert(buffer);
	assert(n_buffer);

	msg = cs->req;

	/* Check that we're supposed to be reading this at this point */
	assert(!msg->signature || gck_rpc_message_verify_part(msg, "fu"));

	/* The number of ulongs there's room for on the other end */
	if (!egg_buffer_get_uint32
	    (&msg->buffer, msg->parsed, &msg->parsed, &length))
		return PARSE_ERROR;

	*n_buffer = length;
	*buffer = NULL;

	/* If set to zero, then they just want the length */
	if (!length)
		return CKR_OK;

	*buffer = call_alloc(cs, length * sizeof(CK_ULONG));
	if (!*buffer)
		return CKR_DEVICE_MEMORY;

	return CKR_OK;
}

static CK_RV
proto_write_ulong_array(CallState * cs, CK_ULONG_PTR array, CK_ULONG len,
			CK_RV ret)
{
	assert(cs);

	/*
	 * When returning an ulong array, in many cases we need to pass
	 * an invalid array along with a length, which signifies CKR_BUFFER_TOO_SMALL.
	 */

	switch (ret) {
	case CKR_BUFFER_TOO_SMALL:
		array = NULL;
		/* fall through */
	case CKR_OK:
		break;

		/* Pass all other errors straight through */
	default:
		return ret;
	};

	if (!gck_rpc_message_write_ulong_array(cs->resp, array, len))
		return PREP_ERROR;

	return CKR_OK;
}

static CK_RV
proto_read_attribute_buffer(CallState * cs, CK_ATTRIBUTE_PTR * result,
			    CK_ULONG * n_result)
{
	CK_ATTRIBUTE_PTR attrs;
	GckRpcMessage *msg;
	uint32_t n_attrs, i;
	uint32_t value;

	assert(cs);
	assert(result);
	assert(n_result);

	msg = cs->req;

	/* Make sure this is in the rigth order */
	assert(!msg->signature || gck_rpc_message_verify_part(msg, "fA"));

	/* Read the number of attributes */
	if (!egg_buffer_get_uint32
	    (&msg->buffer, msg->parsed, &msg->parsed, &n_attrs))
		return PARSE_ERROR;

	/* Allocate memory for the attribute structures */
	attrs = call_alloc(cs, n_attrs * sizeof(CK_ATTRIBUTE));
	if (!attrs)
		return CKR_DEVICE_MEMORY;

	/* Now go through and fill in each one */
	for (i = 0; i < n_attrs; ++i) {

		/* The attribute type */
		if (!egg_buffer_get_uint32
		    (&msg->buffer, msg->parsed, &msg->parsed, &value))
			return PARSE_ERROR;

		attrs[i].type = value;

		/* The number of bytes to allocate */
		if (!egg_buffer_get_uint32
		    (&msg->buffer, msg->parsed, &msg->parsed, &value))
			return PARSE_ERROR;

		if (value == 0) {
			attrs[i].pValue = NULL;
			attrs[i].ulValueLen = 0;
		} else {
			attrs[i].pValue = call_alloc(cs, value);
			if (!attrs[i].pValue)
				return CKR_DEVICE_MEMORY;
			attrs[i].ulValueLen = value;
		}
	}

	*result = attrs;
	*n_result = n_attrs;
	return CKR_OK;
}

static CK_RV
proto_read_attribute_array(CallState * cs, CK_ATTRIBUTE_PTR * result,
			   CK_ULONG * n_result)
{
	CK_ATTRIBUTE_PTR attrs;
	const unsigned char *data;
	unsigned char valid;
	GckRpcMessage *msg;
	uint32_t n_attrs, i;
	uint32_t value;
	size_t n_data;

	assert(cs);
	assert(result);
	assert(n_result);

	msg = cs->req;

	/* Make sure this is in the rigth order */
	assert(!msg->signature || gck_rpc_message_verify_part(msg, "aA"));

	/* Read the number of attributes */
	if (!egg_buffer_get_uint32
	    (&msg->buffer, msg->parsed, &msg->parsed, &n_attrs))
		return PARSE_ERROR;

	/* Allocate memory for the attribute structures */
	attrs = call_alloc(cs, n_attrs * sizeof(CK_ATTRIBUTE));
	if (!attrs)
		return CKR_DEVICE_MEMORY;

	/* Now go through and fill in each one */
	for (i = 0; i < n_attrs; ++i) {

		/* The attribute type */
		if (!egg_buffer_get_uint32
		    (&msg->buffer, msg->parsed, &msg->parsed, &value))
			return PARSE_ERROR;

		attrs[i].type = value;

		/* Whether this one is valid or not */
		if (!egg_buffer_get_byte
		    (&msg->buffer, msg->parsed, &msg->parsed, &valid))
			return PARSE_ERROR;

		if (valid) {
			if (!egg_buffer_get_uint32
			    (&msg->buffer, msg->parsed, &msg->parsed, &value))
				return PARSE_ERROR;
			if (!egg_buffer_get_byte_array
			    (&msg->buffer, msg->parsed, &msg->parsed, &data,
			     &n_data))
				return PARSE_ERROR;

			if (data != NULL && n_data != value) {
				gck_rpc_warn
				    ("attribute length and data do not match");
				return PARSE_ERROR;
			}

			CK_ULONG a;

			if (value == sizeof (uint64_t) &&
			    value != sizeof (CK_ULONG) &&
			    gck_rpc_has_ulong_parameter(attrs[i].type)) {

				value = sizeof (CK_ULONG);
				a = *(uint64_t *)data;
				*(CK_ULONG *)data = a;
			}
			attrs[i].pValue = (CK_VOID_PTR) data;
			attrs[i].ulValueLen = value;
		} else {
			attrs[i].pValue = NULL;
			attrs[i].ulValueLen = -1;
		}
	}

	*result = attrs;
	*n_result = n_attrs;
	return CKR_OK;
}

static CK_RV
proto_write_attribute_array(CallState * cs, CK_ATTRIBUTE_PTR array,
			    CK_ULONG len, CK_RV ret)
{
	assert(cs);

	/*
	 * When returning an attribute array, certain errors aren't
	 * actually real errors, these are passed through to the other
	 * side along with the attribute array.
	 */

	switch (ret) {
	case CKR_ATTRIBUTE_SENSITIVE:
	case CKR_ATTRIBUTE_TYPE_INVALID:
	case CKR_BUFFER_TOO_SMALL:
	case CKR_OK:
		break;

		/* Pass all other errors straight through */
	default:
		return ret;
	};

	if (!gck_rpc_message_write_attribute_array(cs->resp, array, len) ||
	    !gck_rpc_message_write_ulong(cs->resp, ret))
		return PREP_ERROR;

	return CKR_OK;
}

static CK_RV proto_read_null_string(CallState * cs, CK_UTF8CHAR_PTR * val)
{
	GckRpcMessage *msg;
	const unsigned char *data;
	size_t n_data;

	assert(cs);
	assert(val);

	msg = cs->req;

	/* Check that we're supposed to have this at this point */
	assert(!msg->signature || gck_rpc_message_verify_part(msg, "z"));

	if (!egg_buffer_get_byte_array
	    (&msg->buffer, msg->parsed, &msg->parsed, &data, &n_data))
		return PARSE_ERROR;

	/* Allocate a block of memory for it */
	*val = call_alloc(cs, n_data);
	if (!*val)
		return CKR_DEVICE_MEMORY;

	memcpy(*val, data, n_data);
	(*val)[n_data] = 0;

	return CKR_OK;
}

static CK_RV proto_read_mechanism(CallState * cs, CK_MECHANISM_PTR mech)
{
	GckRpcMessage *msg;
	const unsigned char *data;
	uint32_t value;
	size_t n_data;

	assert(cs);
	assert(mech);

	msg = cs->req;

	/* Make sure this is in the right order */
	assert(!msg->signature || gck_rpc_message_verify_part(msg, "M"));

	/* The mechanism type */
	if (!egg_buffer_get_uint32
	    (&msg->buffer, msg->parsed, &msg->parsed, &value))
		return PARSE_ERROR;

	/* The mechanism data */
	if (!egg_buffer_get_byte_array
	    (&msg->buffer, msg->parsed, &msg->parsed, &data, &n_data))
		return PARSE_ERROR;

	mech->mechanism = value;
	mech->pParameter = (CK_VOID_PTR) data;
	mech->ulParameterLen = n_data;
	return CKR_OK;
}

static CK_RV proto_write_info(CallState * cs, CK_INFO_PTR info)
{
	GckRpcMessage *msg;

	assert(cs);
	assert(info);

	msg = cs->resp;

	if (!gck_rpc_message_write_version(msg, &info->cryptokiVersion) ||
	    !gck_rpc_message_write_space_string(msg, info->manufacturerID, 32)
	    || !gck_rpc_message_write_ulong(msg, info->flags)
	    || !gck_rpc_message_write_space_string(msg,
						   info->libraryDescription, 32)
	    || !gck_rpc_message_write_version(msg, &info->libraryVersion))
		return PREP_ERROR;

	return CKR_OK;
}

static CK_RV proto_write_slot_info(CallState * cs, CK_SLOT_INFO_PTR info)
{
	GckRpcMessage *msg;

	assert(cs);
	assert(info);

	msg = cs->resp;

	if (!gck_rpc_message_write_space_string(msg, info->slotDescription, 64)
	    || !gck_rpc_message_write_space_string(msg, info->manufacturerID,
						   32)
	    || !gck_rpc_message_write_ulong(msg, info->flags)
	    || !gck_rpc_message_write_version(msg, &info->hardwareVersion)
	    || !gck_rpc_message_write_version(msg, &info->firmwareVersion))
		return PREP_ERROR;

	return CKR_OK;
}

static CK_RV proto_write_token_info(CallState * cs, CK_TOKEN_INFO_PTR info)
{
	GckRpcMessage *msg;

	assert(cs);
	assert(info);

	msg = cs->resp;

	if (!gck_rpc_message_write_space_string(msg, info->label, 32) ||
	    !gck_rpc_message_write_space_string(msg, info->manufacturerID, 32)
	    || !gck_rpc_message_write_space_string(msg, info->model, 16)
	    || !gck_rpc_message_write_space_string(msg, info->serialNumber, 16)
	    || !gck_rpc_message_write_ulong(msg, info->flags)
	    || !gck_rpc_message_write_ulong(msg, info->ulMaxSessionCount)
	    || !gck_rpc_message_write_ulong(msg, info->ulSessionCount)
	    || !gck_rpc_message_write_ulong(msg, info->ulMaxRwSessionCount)
	    || !gck_rpc_message_write_ulong(msg, info->ulRwSessionCount)
	    || !gck_rpc_message_write_ulong(msg, info->ulMaxPinLen)
	    || !gck_rpc_message_write_ulong(msg, info->ulMinPinLen)
	    || !gck_rpc_message_write_ulong(msg, info->ulTotalPublicMemory)
	    || !gck_rpc_message_write_ulong(msg, info->ulFreePublicMemory)
	    || !gck_rpc_message_write_ulong(msg, info->ulTotalPrivateMemory)
	    || !gck_rpc_message_write_ulong(msg, info->ulFreePrivateMemory)
	    || !gck_rpc_message_write_version(msg, &info->hardwareVersion)
	    || !gck_rpc_message_write_version(msg, &info->firmwareVersion)
	    || !gck_rpc_message_write_space_string(msg, info->utcTime, 16))
		return PREP_ERROR;

	return CKR_OK;
}

static CK_RV
proto_write_mechanism_info(CallState * cs, CK_MECHANISM_INFO_PTR info)
{
	GckRpcMessage *msg;

	assert(cs);
	assert(info);

	msg = cs->resp;

	if (!gck_rpc_message_write_ulong(msg, info->ulMinKeySize) ||
	    !gck_rpc_message_write_ulong(msg, info->ulMaxKeySize) ||
	    !gck_rpc_message_write_ulong(msg, info->flags))
		return PREP_ERROR;

	return CKR_OK;
}

static CK_RV proto_write_session_info(CallState * cs, CK_SESSION_INFO_PTR info)
{
	GckRpcMessage *msg;

	assert(cs);
	assert(info);

	msg = cs->resp;

	if (!gck_rpc_message_write_ulong(msg, info->slotID) ||
	    !gck_rpc_message_write_ulong(msg, info->state) ||
	    !gck_rpc_message_write_ulong(msg, info->flags) ||
	    !gck_rpc_message_write_ulong(msg, info->ulDeviceError))
		return PREP_ERROR;

	return CKR_OK;
}

/* -------------------------------------------------------------------
 * CALL MACROS
 */

#define BEGIN_CALL(call_id) \
	debug ((#call_id ": enter")); \
	assert (cs); \
	assert (pkcs11_module); \
	{  \
		CK_ ## call_id _func = pkcs11_module-> call_id; \
		CK_RV _ret = CKR_OK; \
		if (!_func) { _ret = CKR_GENERAL_ERROR; goto _cleanup; }

#define PROCESS_CALL(args)\
	assert (gck_rpc_message_is_verified (cs->req)); \
	_ret = _func args

#define END_CALL \
	_cleanup: \
		debug (("ret: %d", _ret)); \
		return _ret; \
	}

#define IN_BYTE(val) \
	if (!gck_rpc_message_read_byte (cs->req, &val)) \
		{ _ret = PARSE_ERROR; goto _cleanup; }

#define IN_ULONG(val) \
	if (!gck_rpc_message_read_ulong (cs->req, &val)) \
		{ _ret = PARSE_ERROR; goto _cleanup; }

#define IN_STRING(val) \
	_ret = proto_read_null_string (cs, &val); \
	if (_ret != CKR_OK) goto _cleanup;

#define IN_BYTE_BUFFER(buffer, buffer_len) \
	_ret = proto_read_byte_buffer (cs, &buffer, &buffer_len); \
	if (_ret != CKR_OK) goto _cleanup;

#define IN_BYTE_ARRAY(buffer, buffer_len) \
	_ret = proto_read_byte_array (cs, &buffer, &buffer_len); \
	if (_ret != CKR_OK) goto _cleanup;

#define IN_ULONG_BUFFER(buffer, buffer_len) \
	_ret = proto_read_ulong_buffer (cs, &buffer, &buffer_len); \
	if (_ret != CKR_OK) goto _cleanup;

#define IN_ATTRIBUTE_BUFFER(buffer, buffer_len) \
	_ret = proto_read_attribute_buffer (cs, &buffer, &buffer_len); \
	if (_ret != CKR_OK) goto _cleanup;

#define IN_ATTRIBUTE_ARRAY(attrs, n_attrs) \
	_ret = proto_read_attribute_array (cs, &attrs, &n_attrs); \
	if (_ret != CKR_OK) goto _cleanup;

#define IN_MECHANISM(mech) \
	_ret = proto_read_mechanism (cs, &mech); \
	if (_ret != CKR_OK) goto _cleanup;

#define OUT_ULONG(val) \
	if (_ret == CKR_OK && !gck_rpc_message_write_ulong (cs->resp, val)) \
		_ret = PREP_ERROR;

#define OUT_BYTE_ARRAY(array, len) \
	/* Note how we filter return codes */ \
	_ret = proto_write_byte_array (cs, array, len, _ret);

#define OUT_ULONG_ARRAY(array, len) \
	/* Note how we filter return codes */ \
	_ret = proto_write_ulong_array (cs, array, len, _ret);

#define OUT_ATTRIBUTE_ARRAY(array, len) \
	/* Note how we filter return codes */ \
	_ret = proto_write_attribute_array (cs, array, len, _ret);

#define OUT_INFO(val) \
	if (_ret == CKR_OK) \
		_ret = proto_write_info (cs, &val);

#define OUT_SLOT_INFO(val) \
	if (_ret == CKR_OK) \
		_ret = proto_write_slot_info (cs, &val);

#define OUT_TOKEN_INFO(val) \
	if (_ret == CKR_OK) \
		_ret = proto_write_token_info (cs, &val);

#define OUT_MECHANISM_INFO(val) \
	if (_ret == CKR_OK) \
		_ret = proto_write_mechanism_info (cs, &val);

#define OUT_SESSION_INFO(val) \
	if (_ret == CKR_OK) \
		_ret = proto_write_session_info (cs, &val);

/* ---------------------------------------------------------------------------
 * DISPATCH SPECIFIC CALLS
 */

static CK_RV rpc_C_Initialize(CallState * cs)
{
	CK_BYTE_PTR handshake;
	CK_ULONG n_handshake;
	CK_RV ret = CKR_OK;

	debug(("C_Initialize: enter"));

	assert(cs);
	assert(pkcs11_module);

	ret = proto_read_byte_array(cs, &handshake, &n_handshake);
	if (ret == CKR_OK) {

		/* Check to make sure the header matches */
		if (n_handshake != GCK_RPC_HANDSHAKE_LEN ||
		    memcmp(handshake, GCK_RPC_HANDSHAKE, n_handshake) != 0) {
			gck_rpc_warn
			    ("invalid handshake received from connecting module");
			ret = CKR_GENERAL_ERROR;
		}

		assert(gck_rpc_message_is_verified(cs->req));
	}

	/*
	 * We don't actually C_Initialize lower layers. It's assumed
	 * that they'll already be initialzied by the code that loaded us.
	 */

	debug(("ret: %d", ret));
	return ret;
}

static CK_RV rpc_C_Finalize(CallState * cs)
{
	CK_SLOT_ID_PTR slots;
	CK_ULONG n_slots, i;
	CK_SLOT_ID appartment;
	CK_RV ret;

	debug(("C_Finalize: enter"));

	assert(cs);
	assert(pkcs11_module);

	/*
	 * We don't actually C_Finalize lower layers, since this would finalize
	 * for all appartments, client applications. Anyway this is done by
	 * the code that loaded us.
	 *
	 * But we do need to cleanup resources used by this client, so instead
	 * we call C_CloseAllSessions for each appartment for this client.
	 */

	ret = (pkcs11_module->C_GetSlotList) (TRUE, NULL, &n_slots);
	if (ret == CKR_OK) {
		slots = calloc(n_slots, sizeof(CK_SLOT_ID));
		if (slots == NULL) {
			ret = CKR_DEVICE_MEMORY;
		} else {
			ret =
			    (pkcs11_module->C_GetSlotList) (TRUE, slots,
							    &n_slots);
			for (i = 0; ret == CKR_OK && i < n_slots; ++i) {
				appartment = slots[i];
				ret =
				    (pkcs11_module->
				     C_CloseAllSessions) (appartment);
			}
			free(slots);
		}
	}

	debug(("ret: %d", ret));
	return ret;
}

static CK_RV rpc_C_GetInfo(CallState * cs)
{
	CK_INFO info;

	BEGIN_CALL(C_GetInfo);
	PROCESS_CALL((&info));
	OUT_INFO(info);
	END_CALL;
}

static CK_RV rpc_C_GetSlotList(CallState * cs)
{
	CK_BBOOL token_present;
	CK_SLOT_ID_PTR slot_list;
	CK_ULONG count;

	BEGIN_CALL(C_GetSlotList);
	IN_BYTE(token_present);
	IN_ULONG_BUFFER(slot_list, count);
	PROCESS_CALL((token_present, slot_list, &count));
	OUT_ULONG_ARRAY(slot_list, count);
	END_CALL;
}

static CK_RV rpc_C_GetSlotInfo(CallState * cs)
{
	CK_SLOT_ID slot_id;
	CK_SLOT_INFO info;

	/* Slot id becomes appartment so lower layers can tell clients apart. */

	BEGIN_CALL(C_GetSlotInfo);
	IN_ULONG(slot_id);
	PROCESS_CALL((slot_id, &info));
	OUT_SLOT_INFO(info);
	END_CALL;
}

static CK_RV rpc_C_GetTokenInfo(CallState * cs)
{
	CK_SLOT_ID slot_id;
	CK_TOKEN_INFO info;

	/* Slot id becomes appartment so lower layers can tell clients apart. */

	BEGIN_CALL(C_GetTokenInfo);
	IN_ULONG(slot_id);
	PROCESS_CALL((slot_id, &info));
	OUT_TOKEN_INFO(info);
	END_CALL;
}

static CK_RV rpc_C_GetMechanismList(CallState * cs)
{
	CK_SLOT_ID slot_id;
	CK_MECHANISM_TYPE_PTR mechanism_list;
	CK_ULONG count;

	/* Slot id becomes appartment so lower layers can tell clients apart. */

	BEGIN_CALL(C_GetMechanismList);
	IN_ULONG(slot_id);
	IN_ULONG_BUFFER(mechanism_list, count);
	PROCESS_CALL((slot_id, mechanism_list, &count));
	OUT_ULONG_ARRAY(mechanism_list, count);
	END_CALL;
}

static CK_RV rpc_C_GetMechanismInfo(CallState * cs)
{
	CK_SLOT_ID slot_id;
	CK_MECHANISM_TYPE type;
	CK_MECHANISM_INFO info;

	/* Slot id becomes appartment so lower layers can tell clients apart. */

	BEGIN_CALL(C_GetMechanismInfo);
	IN_ULONG(slot_id);
	IN_ULONG(type);
	PROCESS_CALL((slot_id, type, &info));
	OUT_MECHANISM_INFO(info);
	END_CALL;
}

static CK_RV rpc_C_InitToken(CallState * cs)
{
	CK_SLOT_ID slot_id;
	CK_UTF8CHAR_PTR pin;
	CK_ULONG pin_len;
	CK_UTF8CHAR_PTR label;

	/* Slot id becomes appartment so lower layers can tell clients apart. */

	BEGIN_CALL(C_InitToken);
	IN_ULONG(slot_id);
	IN_BYTE_ARRAY(pin, pin_len);
	IN_STRING(label);
	PROCESS_CALL((slot_id, pin, pin_len, label));
	END_CALL;
}

static CK_RV rpc_C_WaitForSlotEvent(CallState * cs)
{
	CK_FLAGS flags;
	CK_SLOT_ID slot_id;

	/* Get slot id from appartment lower layers use. */

	BEGIN_CALL(C_WaitForSlotEvent);
	IN_ULONG(flags);
	PROCESS_CALL((flags, &slot_id, NULL));
	slot_id = CK_GNOME_APPARTMENT_SLOT(slot_id);
	OUT_ULONG(slot_id);
	END_CALL;
}

static CK_RV rpc_C_OpenSession(CallState * cs)
{
	CK_SLOT_ID slot_id;
	CK_FLAGS flags;
	CK_SESSION_HANDLE session;

	/* Slot id becomes appartment so lower layers can tell clients apart. */

	BEGIN_CALL(C_OpenSession);
	IN_ULONG(slot_id);
	IN_ULONG(flags);
	PROCESS_CALL((slot_id, flags, NULL, NULL, &session));
	OUT_ULONG(session);
	END_CALL;
}

static CK_RV rpc_C_CloseSession(CallState * cs)
{
	CK_SESSION_HANDLE session;

	BEGIN_CALL(C_CloseSession);
	IN_ULONG(session);
	PROCESS_CALL((session));
	END_CALL;
}

static CK_RV rpc_C_CloseAllSessions(CallState * cs)
{
	CK_SLOT_ID slot_id;

	/* Slot id becomes appartment so lower layers can tell clients apart. */

	BEGIN_CALL(C_CloseAllSessions);
	IN_ULONG(slot_id);
	PROCESS_CALL((slot_id));
	END_CALL;
}

static CK_RV rpc_C_GetFunctionStatus(CallState * cs)
{
	CK_SESSION_HANDLE session;

	BEGIN_CALL(C_GetFunctionStatus);
	IN_ULONG(session);
	PROCESS_CALL((session));
	END_CALL;
}

static CK_RV rpc_C_CancelFunction(CallState * cs)
{
	CK_SESSION_HANDLE session;

	BEGIN_CALL(C_CancelFunction);
	IN_ULONG(session);
	PROCESS_CALL((session));
	END_CALL;
}

static CK_RV rpc_C_GetSessionInfo(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_SESSION_INFO info;

	/* Get slot id from appartment lower layers use. */

	BEGIN_CALL(C_GetSessionInfo);
	IN_ULONG(session);
	PROCESS_CALL((session, &info));
	info.slotID = CK_GNOME_APPARTMENT_SLOT(info.slotID);
	OUT_SESSION_INFO(info);
	END_CALL;
}

static CK_RV rpc_C_InitPIN(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_UTF8CHAR_PTR pin;
	CK_ULONG pin_len;

	BEGIN_CALL(C_InitPIN);
	IN_ULONG(session);
	IN_BYTE_ARRAY(pin, pin_len);
	PROCESS_CALL((session, pin, pin_len));
	END_CALL;
}

static CK_RV rpc_C_SetPIN(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_UTF8CHAR_PTR old_pin;
	CK_ULONG old_len;
	CK_UTF8CHAR_PTR new_pin;
	CK_ULONG new_len;

	BEGIN_CALL(C_SetPIN);
	IN_ULONG(session);
	IN_BYTE_ARRAY(old_pin, old_len);
	IN_BYTE_ARRAY(new_pin, new_len);
	PROCESS_CALL((session, old_pin, old_len, new_pin, new_len));
	END_CALL;
}

static CK_RV rpc_C_GetOperationState(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR operation_state;
	CK_ULONG operation_state_len;

	BEGIN_CALL(C_GetOperationState);
	IN_ULONG(session);
	IN_BYTE_BUFFER(operation_state, operation_state_len);
	PROCESS_CALL((session, operation_state, &operation_state_len));
	OUT_BYTE_ARRAY(operation_state, operation_state_len);
	END_CALL;
}

static CK_RV rpc_C_SetOperationState(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR operation_state;
	CK_ULONG operation_state_len;
	CK_OBJECT_HANDLE encryption_key;
	CK_OBJECT_HANDLE authentication_key;

	BEGIN_CALL(C_SetOperationState);
	IN_ULONG(session);
	IN_BYTE_ARRAY(operation_state, operation_state_len);
	IN_ULONG(encryption_key);
	IN_ULONG(authentication_key);
	PROCESS_CALL((session, operation_state, operation_state_len,
		      encryption_key, authentication_key));
	END_CALL;
}

static CK_RV rpc_C_Login(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_USER_TYPE user_type;
	CK_UTF8CHAR_PTR pin;
	CK_ULONG pin_len;

	BEGIN_CALL(C_Login);
	IN_ULONG(session);
	IN_ULONG(user_type);
	IN_BYTE_ARRAY(pin, pin_len);
	PROCESS_CALL((session, user_type, pin, pin_len));
	END_CALL;
}

static CK_RV rpc_C_Logout(CallState * cs)
{
	CK_SESSION_HANDLE session;

	BEGIN_CALL(C_Logout);
	IN_ULONG(session);
	PROCESS_CALL((session));
	END_CALL;
}

/* -----------------------------------------------------------------------------
 * OBJECT OPERATIONS
 */

static CK_RV rpc_C_CreateObject(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_ATTRIBUTE_PTR template;
	CK_ULONG count;
	CK_OBJECT_HANDLE new_object;

	BEGIN_CALL(C_CreateObject);
	IN_ULONG(session);
	IN_ATTRIBUTE_ARRAY(template, count);
	PROCESS_CALL((session, template, count, &new_object));
	OUT_ULONG(new_object);
	END_CALL;
}

static CK_RV rpc_C_CopyObject(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_OBJECT_HANDLE object;
	CK_ATTRIBUTE_PTR template;
	CK_ULONG count;
	CK_OBJECT_HANDLE new_object;

	BEGIN_CALL(C_CopyObject);
	IN_ULONG(session);
	IN_ULONG(object);
	IN_ATTRIBUTE_ARRAY(template, count);
	PROCESS_CALL((session, object, template, count, &new_object));
	OUT_ULONG(new_object);
	END_CALL;
}

static CK_RV rpc_C_DestroyObject(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_OBJECT_HANDLE object;

	BEGIN_CALL(C_DestroyObject);
	IN_ULONG(session);
	IN_ULONG(object);
	PROCESS_CALL((session, object));
	END_CALL;
}

static CK_RV rpc_C_GetObjectSize(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_OBJECT_HANDLE object;
	CK_ULONG size;

	BEGIN_CALL(C_GetObjectSize);
	IN_ULONG(session);
	IN_ULONG(object);
	PROCESS_CALL((session, object, &size));
	OUT_ULONG(size);
	END_CALL;
}

static CK_RV rpc_C_GetAttributeValue(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_OBJECT_HANDLE object;
	CK_ATTRIBUTE_PTR template;
	CK_ULONG count;

	BEGIN_CALL(C_GetAttributeValue);
	IN_ULONG(session);
	IN_ULONG(object);
	IN_ATTRIBUTE_BUFFER(template, count);
	PROCESS_CALL((session, object, template, count));
	OUT_ATTRIBUTE_ARRAY(template, count);
	END_CALL;
}

static CK_RV rpc_C_SetAttributeValue(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_OBJECT_HANDLE object;
	CK_ATTRIBUTE_PTR template;
	CK_ULONG count;

	BEGIN_CALL(C_SetAttributeValue);
	IN_ULONG(session);
	IN_ULONG(object);
	IN_ATTRIBUTE_ARRAY(template, count);
	PROCESS_CALL((session, object, template, count));
	END_CALL;
}

static CK_RV rpc_C_FindObjectsInit(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_ATTRIBUTE_PTR template;
	CK_ULONG count;

	BEGIN_CALL(C_FindObjectsInit);
	IN_ULONG(session);
	IN_ATTRIBUTE_ARRAY(template, count);
	PROCESS_CALL((session, template, count));
	END_CALL;
}

static CK_RV rpc_C_FindObjects(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_OBJECT_HANDLE_PTR objects;
	CK_ULONG max_object_count;
	CK_ULONG object_count;

	BEGIN_CALL(C_FindObjects);
	IN_ULONG(session);
	IN_ULONG_BUFFER(objects, max_object_count);
	PROCESS_CALL((session, objects, max_object_count, &object_count));
	OUT_ULONG_ARRAY(objects, object_count);
	END_CALL;
}

static CK_RV rpc_C_FindObjectsFinal(CallState * cs)
{
	CK_SESSION_HANDLE session;

	BEGIN_CALL(C_FindObjectsFinal);
	IN_ULONG(session);
	PROCESS_CALL((session));
	END_CALL;
}

static CK_RV rpc_C_EncryptInit(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_OBJECT_HANDLE key;

	BEGIN_CALL(C_EncryptInit);
	IN_ULONG(session);
	IN_MECHANISM(mechanism);
	IN_ULONG(key);
	PROCESS_CALL((session, &mechanism, key));
	END_CALL;

}

static CK_RV rpc_C_Encrypt(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR data;
	CK_ULONG data_len;
	CK_BYTE_PTR encrypted_data;
	CK_ULONG encrypted_data_len;

	BEGIN_CALL(C_Encrypt);
	IN_ULONG(session);
	IN_BYTE_ARRAY(data, data_len);
	IN_BYTE_BUFFER(encrypted_data, encrypted_data_len);
	PROCESS_CALL((session, data, data_len, encrypted_data,
		      &encrypted_data_len));
	OUT_BYTE_ARRAY(encrypted_data, encrypted_data_len);
	END_CALL;
}

static CK_RV rpc_C_EncryptUpdate(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR part;
	CK_ULONG part_len;
	CK_BYTE_PTR encrypted_part;
	CK_ULONG encrypted_part_len;

	BEGIN_CALL(C_EncryptUpdate);
	IN_ULONG(session);
	IN_BYTE_ARRAY(part, part_len);
	IN_BYTE_BUFFER(encrypted_part, encrypted_part_len);
	PROCESS_CALL((session, part, part_len, encrypted_part,
		      &encrypted_part_len));
	OUT_BYTE_ARRAY(encrypted_part, encrypted_part_len);
	END_CALL;
}

static CK_RV rpc_C_EncryptFinal(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR last_encrypted_part;
	CK_ULONG last_encrypted_part_len;

	BEGIN_CALL(C_EncryptFinal);
	IN_ULONG(session);
	IN_BYTE_BUFFER(last_encrypted_part, last_encrypted_part_len);
	PROCESS_CALL((session, last_encrypted_part, &last_encrypted_part_len));
	OUT_BYTE_ARRAY(last_encrypted_part, last_encrypted_part_len);
	END_CALL;
}

static CK_RV rpc_C_DecryptInit(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_OBJECT_HANDLE key;

	BEGIN_CALL(C_DecryptInit);
	IN_ULONG(session);
	IN_MECHANISM(mechanism);
	IN_ULONG(key);
	PROCESS_CALL((session, &mechanism, key));
	END_CALL;
}

static CK_RV rpc_C_Decrypt(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR encrypted_data;
	CK_ULONG encrypted_data_len;
	CK_BYTE_PTR data;
	CK_ULONG data_len;

	BEGIN_CALL(C_Decrypt);
	IN_ULONG(session);
	IN_BYTE_ARRAY(encrypted_data, encrypted_data_len);
	IN_BYTE_BUFFER(data, data_len);
	PROCESS_CALL((session, encrypted_data, encrypted_data_len, data,
		      &data_len));
	OUT_BYTE_ARRAY(data, data_len);
	END_CALL;
}

static CK_RV rpc_C_DecryptUpdate(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR encrypted_part;
	CK_ULONG encrypted_part_len;
	CK_BYTE_PTR part;
	CK_ULONG part_len;

	BEGIN_CALL(C_DecryptUpdate);
	IN_ULONG(session);
	IN_BYTE_ARRAY(encrypted_part, encrypted_part_len);
	IN_BYTE_BUFFER(part, part_len);
	PROCESS_CALL((session, encrypted_part, encrypted_part_len, part,
		      &part_len));
	OUT_BYTE_ARRAY(part, part_len);
	END_CALL;
}

static CK_RV rpc_C_DecryptFinal(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR last_part;
	CK_ULONG last_part_len;

	BEGIN_CALL(C_DecryptFinal);
	IN_ULONG(session);
	IN_BYTE_BUFFER(last_part, last_part_len);
	PROCESS_CALL((session, last_part, &last_part_len));
	OUT_BYTE_ARRAY(last_part, last_part_len);
	END_CALL;
}

static CK_RV rpc_C_DigestInit(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;

	BEGIN_CALL(C_DigestInit);
	IN_ULONG(session);
	IN_MECHANISM(mechanism);
	PROCESS_CALL((session, &mechanism));
	END_CALL;
}

static CK_RV rpc_C_Digest(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR data;
	CK_ULONG data_len;
	CK_BYTE_PTR digest;
	CK_ULONG digest_len;

	BEGIN_CALL(C_Digest);
	IN_ULONG(session);
	IN_BYTE_ARRAY(data, data_len);
	IN_BYTE_BUFFER(digest, digest_len);
	PROCESS_CALL((session, data, data_len, digest, &digest_len));
	OUT_BYTE_ARRAY(digest, digest_len);
	END_CALL;
}

static CK_RV rpc_C_DigestUpdate(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR part;
	CK_ULONG part_len;

	BEGIN_CALL(C_DigestUpdate);
	IN_ULONG(session);
	IN_BYTE_ARRAY(part, part_len);
	PROCESS_CALL((session, part, part_len));
	END_CALL;
}

static CK_RV rpc_C_DigestKey(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_OBJECT_HANDLE key;

	BEGIN_CALL(C_DigestKey);
	IN_ULONG(session);
	IN_ULONG(key);
	PROCESS_CALL((session, key));
	END_CALL;
}

static CK_RV rpc_C_DigestFinal(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR digest;
	CK_ULONG digest_len;

	BEGIN_CALL(C_DigestFinal);
	IN_ULONG(session);
	IN_BYTE_BUFFER(digest, digest_len);
	PROCESS_CALL((session, digest, &digest_len));
	OUT_BYTE_ARRAY(digest, digest_len);
	END_CALL;
}

static CK_RV rpc_C_SignInit(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_OBJECT_HANDLE key;

	BEGIN_CALL(C_SignInit);
	IN_ULONG(session);
	IN_MECHANISM(mechanism);
	IN_ULONG(key);
	PROCESS_CALL((session, &mechanism, key));
	END_CALL;
}

static CK_RV rpc_C_Sign(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR part;
	CK_ULONG part_len;
	CK_BYTE_PTR signature;
	CK_ULONG signature_len;

	BEGIN_CALL(C_Sign);
	IN_ULONG(session);
	IN_BYTE_ARRAY(part, part_len);
	IN_BYTE_BUFFER(signature, signature_len);
	PROCESS_CALL((session, part, part_len, signature, &signature_len));
	OUT_BYTE_ARRAY(signature, signature_len);
	END_CALL;

}

static CK_RV rpc_C_SignUpdate(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR part;
	CK_ULONG part_len;

	BEGIN_CALL(C_SignUpdate);
	IN_ULONG(session);
	IN_BYTE_ARRAY(part, part_len);
	PROCESS_CALL((session, part, part_len));
	END_CALL;
}

static CK_RV rpc_C_SignFinal(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR signature;
	CK_ULONG signature_len;

	BEGIN_CALL(C_SignFinal);
	IN_ULONG(session);
	IN_BYTE_BUFFER(signature, signature_len);
	PROCESS_CALL((session, signature, &signature_len));
	OUT_BYTE_ARRAY(signature, signature_len);
	END_CALL;
}

static CK_RV rpc_C_SignRecoverInit(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_OBJECT_HANDLE key;

	BEGIN_CALL(C_SignRecoverInit);
	IN_ULONG(session);
	IN_MECHANISM(mechanism);
	IN_ULONG(key);
	PROCESS_CALL((session, &mechanism, key));
	END_CALL;
}

static CK_RV rpc_C_SignRecover(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR data;
	CK_ULONG data_len;
	CK_BYTE_PTR signature;
	CK_ULONG signature_len;

	BEGIN_CALL(C_SignRecover);
	IN_ULONG(session);
	IN_BYTE_ARRAY(data, data_len);
	IN_BYTE_BUFFER(signature, signature_len);
	PROCESS_CALL((session, data, data_len, signature, &signature_len));
	OUT_BYTE_ARRAY(signature, signature_len);
	END_CALL;
}

static CK_RV rpc_C_VerifyInit(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_OBJECT_HANDLE key;

	BEGIN_CALL(C_VerifyInit);
	IN_ULONG(session);
	IN_MECHANISM(mechanism);
	IN_ULONG(key);
	PROCESS_CALL((session, &mechanism, key));
	END_CALL;
}

static CK_RV rpc_C_Verify(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR data;
	CK_ULONG data_len;
	CK_BYTE_PTR signature;
	CK_ULONG signature_len;

	BEGIN_CALL(C_Verify);
	IN_ULONG(session);
	IN_BYTE_ARRAY(data, data_len);
	IN_BYTE_ARRAY(signature, signature_len);
	PROCESS_CALL((session, data, data_len, signature, signature_len));
	END_CALL;
}

static CK_RV rpc_C_VerifyUpdate(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR part;
	CK_ULONG part_len;

	BEGIN_CALL(C_VerifyUpdate);
	IN_ULONG(session);
	IN_BYTE_ARRAY(part, part_len);
	PROCESS_CALL((session, part, part_len));
	END_CALL;
}

static CK_RV rpc_C_VerifyFinal(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR signature;
	CK_ULONG signature_len;

	BEGIN_CALL(C_VerifyFinal);
	IN_ULONG(session);
	IN_BYTE_ARRAY(signature, signature_len);
	PROCESS_CALL((session, signature, signature_len));
	END_CALL;
}

static CK_RV rpc_C_VerifyRecoverInit(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_OBJECT_HANDLE key;

	BEGIN_CALL(C_VerifyRecoverInit);
	IN_ULONG(session);
	IN_MECHANISM(mechanism);
	IN_ULONG(key);
	PROCESS_CALL((session, &mechanism, key));
	END_CALL;
}

static CK_RV rpc_C_VerifyRecover(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR signature;
	CK_ULONG signature_len;
	CK_BYTE_PTR data;
	CK_ULONG data_len;

	BEGIN_CALL(C_VerifyRecover);
	IN_ULONG(session);
	IN_BYTE_ARRAY(signature, signature_len);
	IN_BYTE_BUFFER(data, data_len);
	PROCESS_CALL((session, signature, signature_len, data, &data_len));
	OUT_BYTE_ARRAY(data, data_len);
	END_CALL;
}

static CK_RV rpc_C_DigestEncryptUpdate(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR part;
	CK_ULONG part_len;
	CK_BYTE_PTR encrypted_part;
	CK_ULONG encrypted_part_len;

	BEGIN_CALL(C_DigestEncryptUpdate);
	IN_ULONG(session);
	IN_BYTE_ARRAY(part, part_len);
	IN_BYTE_BUFFER(encrypted_part, encrypted_part_len);
	PROCESS_CALL((session, part, part_len, encrypted_part,
		      &encrypted_part_len));
	OUT_BYTE_ARRAY(encrypted_part, encrypted_part_len);
	END_CALL;
}

static CK_RV rpc_C_DecryptDigestUpdate(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR encrypted_part;
	CK_ULONG encrypted_part_len;
	CK_BYTE_PTR part;
	CK_ULONG part_len;

	BEGIN_CALL(C_DecryptDigestUpdate);
	IN_ULONG(session);
	IN_BYTE_ARRAY(encrypted_part, encrypted_part_len);
	IN_BYTE_BUFFER(part, part_len);
	PROCESS_CALL((session, encrypted_part, encrypted_part_len, part,
		      &part_len));
	OUT_BYTE_ARRAY(part, part_len);
	END_CALL;
}

static CK_RV rpc_C_SignEncryptUpdate(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR part;
	CK_ULONG part_len;
	CK_BYTE_PTR encrypted_part;
	CK_ULONG encrypted_part_len;

	BEGIN_CALL(C_SignEncryptUpdate);
	IN_ULONG(session);
	IN_BYTE_ARRAY(part, part_len);
	IN_BYTE_BUFFER(encrypted_part, encrypted_part_len);
	PROCESS_CALL((session, part, part_len, encrypted_part,
		      &encrypted_part_len));
	OUT_BYTE_ARRAY(encrypted_part, encrypted_part_len);
	END_CALL;
}

static CK_RV rpc_C_DecryptVerifyUpdate(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR encrypted_part;
	CK_ULONG encrypted_part_len;
	CK_BYTE_PTR part;
	CK_ULONG part_len;

	BEGIN_CALL(C_DecryptVerifyUpdate);
	IN_ULONG(session);
	IN_BYTE_ARRAY(encrypted_part, encrypted_part_len);
	IN_BYTE_BUFFER(part, part_len);
	PROCESS_CALL((session, encrypted_part, encrypted_part_len, part,
		      &part_len));
	OUT_BYTE_ARRAY(part, part_len);
	END_CALL;
}

/* -----------------------------------------------------------------------------
 * KEY OPERATIONS
 */

static CK_RV rpc_C_GenerateKey(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_ATTRIBUTE_PTR template;
	CK_ULONG count;
	CK_OBJECT_HANDLE key;

	BEGIN_CALL(C_GenerateKey);
	IN_ULONG(session);
	IN_MECHANISM(mechanism);
	IN_ATTRIBUTE_ARRAY(template, count);
	PROCESS_CALL((session, &mechanism, template, count, &key));
	OUT_ULONG(key);
	END_CALL;
}

static CK_RV rpc_C_GenerateKeyPair(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_ATTRIBUTE_PTR public_key_template;
	CK_ULONG public_key_attribute_count;
	CK_ATTRIBUTE_PTR private_key_template;
	CK_ULONG private_key_attribute_count;
	CK_OBJECT_HANDLE public_key;
	CK_OBJECT_HANDLE private_key;

	BEGIN_CALL(C_GenerateKeyPair);
	IN_ULONG(session);
	IN_MECHANISM(mechanism);
	IN_ATTRIBUTE_ARRAY(public_key_template, public_key_attribute_count);
	IN_ATTRIBUTE_ARRAY(private_key_template, private_key_attribute_count);
	PROCESS_CALL((session, &mechanism, public_key_template,
		      public_key_attribute_count, private_key_template,
		      private_key_attribute_count, &public_key, &private_key));
	OUT_ULONG(public_key);
	OUT_ULONG(private_key);
	END_CALL;

}

static CK_RV rpc_C_WrapKey(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_OBJECT_HANDLE wrapping_key;
	CK_OBJECT_HANDLE key;
	CK_BYTE_PTR wrapped_key;
	CK_ULONG wrapped_key_len;

	BEGIN_CALL(C_WrapKey);
	IN_ULONG(session);
	IN_MECHANISM(mechanism);
	IN_ULONG(wrapping_key);
	IN_ULONG(key);
	IN_BYTE_BUFFER(wrapped_key, wrapped_key_len);
	PROCESS_CALL((session, &mechanism, wrapping_key, key, wrapped_key,
		      &wrapped_key_len));
	OUT_BYTE_ARRAY(wrapped_key, wrapped_key_len);
	END_CALL;
}

static CK_RV rpc_C_UnwrapKey(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_OBJECT_HANDLE unwrapping_key;
	CK_BYTE_PTR wrapped_key;
	CK_ULONG wrapped_key_len;
	CK_ATTRIBUTE_PTR template;
	CK_ULONG attribute_count;
	CK_OBJECT_HANDLE key;

	BEGIN_CALL(C_UnwrapKey);
	IN_ULONG(session);
	IN_MECHANISM(mechanism);
	IN_ULONG(unwrapping_key);
	IN_BYTE_ARRAY(wrapped_key, wrapped_key_len);
	IN_ATTRIBUTE_ARRAY(template, attribute_count);
	PROCESS_CALL((session, &mechanism, unwrapping_key, wrapped_key,
		      wrapped_key_len, template, attribute_count, &key));
	OUT_ULONG(key);
	END_CALL;
}

static CK_RV rpc_C_DeriveKey(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_MECHANISM mechanism;
	CK_OBJECT_HANDLE base_key;
	CK_ATTRIBUTE_PTR template;
	CK_ULONG attribute_count;
	CK_OBJECT_HANDLE key;

	BEGIN_CALL(C_DeriveKey);
	IN_ULONG(session);
	IN_MECHANISM(mechanism);
	IN_ULONG(base_key);
	IN_ATTRIBUTE_ARRAY(template, attribute_count);
	PROCESS_CALL((session, &mechanism, base_key, template, attribute_count,
		      &key));
	OUT_ULONG(key);
	END_CALL;
}

static CK_RV rpc_C_SeedRandom(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR seed;
	CK_ULONG seed_len;

	BEGIN_CALL(C_SeedRandom);
	IN_ULONG(session);
	IN_BYTE_ARRAY(seed, seed_len);
	PROCESS_CALL((session, seed, seed_len));
	END_CALL;
}

static CK_RV rpc_C_GenerateRandom(CallState * cs)
{
	CK_SESSION_HANDLE session;
	CK_BYTE_PTR random_data;
	CK_ULONG random_len;

	BEGIN_CALL(C_GenerateRandom);
	IN_ULONG(session);
	IN_BYTE_BUFFER(random_data, random_len);
	PROCESS_CALL((session, random_data, random_len));
	OUT_BYTE_ARRAY(random_data, random_len);
	END_CALL;
}

/* ---------------------------------------------------------------------------
 * DISPATCH THREAD HANDLING
 */

static int dispatch_call(CallState * cs)
{
	GckRpcMessage *req, *resp;
	CK_RV ret = CKR_OK;

	assert(cs);

	req = cs->req;
	resp = cs->resp;

	/* This should have been checked by the parsing code */
	assert(req->call_id > GCK_RPC_CALL_ERROR);
	assert(req->call_id < GCK_RPC_CALL_MAX);

	/* Prepare a response for the function to fill in */
	if (!gck_rpc_message_prep(resp, req->call_id, GCK_RPC_RESPONSE)) {
		gck_rpc_warn("couldn't prepare message");
		return 0;
	}

	switch (req->call_id) {

#define CASE_CALL(name) \
		case GCK_RPC_CALL_##name: \
			ret = rpc_##name (cs); \
			break;
		CASE_CALL(C_Initialize)
		    CASE_CALL(C_Finalize)
		    CASE_CALL(C_GetInfo)
		    CASE_CALL(C_GetSlotList)
		    CASE_CALL(C_GetSlotInfo)
		    CASE_CALL(C_GetTokenInfo)
		    CASE_CALL(C_GetMechanismList)
		    CASE_CALL(C_GetMechanismInfo)
		    CASE_CALL(C_InitToken)
		    CASE_CALL(C_WaitForSlotEvent)
		    CASE_CALL(C_OpenSession)
		    CASE_CALL(C_CloseSession)
		    CASE_CALL(C_CloseAllSessions)
		    CASE_CALL(C_GetFunctionStatus)
		    CASE_CALL(C_CancelFunction)
		    CASE_CALL(C_GetSessionInfo)
		    CASE_CALL(C_InitPIN)
		    CASE_CALL(C_SetPIN)
		    CASE_CALL(C_GetOperationState)
		    CASE_CALL(C_SetOperationState)
		    CASE_CALL(C_Login)
		    CASE_CALL(C_Logout)
		    CASE_CALL(C_CreateObject)
		    CASE_CALL(C_CopyObject)
		    CASE_CALL(C_DestroyObject)
		    CASE_CALL(C_GetObjectSize)
		    CASE_CALL(C_GetAttributeValue)
		    CASE_CALL(C_SetAttributeValue)
		    CASE_CALL(C_FindObjectsInit)
		    CASE_CALL(C_FindObjects)
		    CASE_CALL(C_FindObjectsFinal)
		    CASE_CALL(C_EncryptInit)
		    CASE_CALL(C_Encrypt)
		    CASE_CALL(C_EncryptUpdate)
		    CASE_CALL(C_EncryptFinal)
		    CASE_CALL(C_DecryptInit)
		    CASE_CALL(C_Decrypt)
		    CASE_CALL(C_DecryptUpdate)
		    CASE_CALL(C_DecryptFinal)
		    CASE_CALL(C_DigestInit)
		    CASE_CALL(C_Digest)
		    CASE_CALL(C_DigestUpdate)
		    CASE_CALL(C_DigestKey)
		    CASE_CALL(C_DigestFinal)
		    CASE_CALL(C_SignInit)
		    CASE_CALL(C_Sign)
		    CASE_CALL(C_SignUpdate)
		    CASE_CALL(C_SignFinal)
		    CASE_CALL(C_SignRecoverInit)
		    CASE_CALL(C_SignRecover)
		    CASE_CALL(C_VerifyInit)
		    CASE_CALL(C_Verify)
		    CASE_CALL(C_VerifyUpdate)
		    CASE_CALL(C_VerifyFinal)
		    CASE_CALL(C_VerifyRecoverInit)
		    CASE_CALL(C_VerifyRecover)
		    CASE_CALL(C_DigestEncryptUpdate)
		    CASE_CALL(C_DecryptDigestUpdate)
		    CASE_CALL(C_SignEncryptUpdate)
		    CASE_CALL(C_DecryptVerifyUpdate)
		    CASE_CALL(C_GenerateKey)
		    CASE_CALL(C_GenerateKeyPair)
		    CASE_CALL(C_WrapKey)
		    CASE_CALL(C_UnwrapKey)
		    CASE_CALL(C_DeriveKey)
		    CASE_CALL(C_SeedRandom)
		    CASE_CALL(C_GenerateRandom)
#undef CASE_CALL
	default:
		/* This should have been caught by the parse code */
		assert(0 && "Unchecked call");
		break;
	};

	if (ret == CKR_OK) {

		/* Parsing errors? */
		if (gck_rpc_message_buffer_error(req)) {
			gck_rpc_warn
			    ("invalid request from module, probably too short");
			ret = PARSE_ERROR;
		}

		/* Out of memory errors? */
		if (gck_rpc_message_buffer_error(resp)) {
			gck_rpc_warn
			    ("out of memory error putting together message");
			ret = PREP_ERROR;
		}
	}

	/* A filled in response */
	if (ret == CKR_OK) {

		/*
		 * Since we're dealing with many many functions above generating
		 * these messages we want to make sure each of them actually
		 * does what it's supposed to.
		 */

		assert(gck_rpc_message_is_verified(resp));
		assert(resp->call_type == GCK_RPC_RESPONSE);
		assert(resp->call_id == req->call_id);
		assert(gck_rpc_calls[resp->call_id].response);
		assert(strcmp(gck_rpc_calls[resp->call_id].response,
			      resp->signature) == 0);

		/* Fill in an error respnose */
	} else {
		if (!gck_rpc_message_prep
		    (resp, GCK_RPC_CALL_ERROR, GCK_RPC_RESPONSE)
		    || !gck_rpc_message_write_ulong(resp, (uint32_t) ret)
		    || gck_rpc_message_buffer_error(resp)) {
			gck_rpc_warn("out of memory responding with error");
			return 0;
		}
	}

	return 1;
}

static int read_all(int sock, unsigned char *data, size_t len)
{
	int r;

	assert(sock >= 0);
	assert(data);
	assert(len > 0);

	while (len > 0) {

                r = recv(sock, (void *)data, len, 0);

		if (r == 0) {
			/* Connection was closed on client */
			return 0;
		} else if (r == -1) {
			if (errno != EAGAIN && errno != EINTR) {
				gck_rpc_warn("couldn't receive data: %s",
					     strerror(errno));
				return 0;
			}
		} else {
			data += r;
			len -= r;
		}
	}
	return 1;
}

static int write_all(int sock, unsigned char *data, size_t len)
{
	int r;

	assert(sock >= 0);
	assert(data);
	assert(len > 0);

	while (len > 0) {

                r = send(sock, (void *)data, len, 0);

		if (r == -1) {
			if (errno == EPIPE) {
				/* Connection closed from client */
				return 0;
			} else if (errno != EAGAIN && errno != EINTR) {
				gck_rpc_warn("couldn't send data: %s",
					     strerror(errno));
				return 0;
			}
		} else {
			data += r;
			len -= r;
		}
	}

	return 1;
}

static void run_dispatch_loop(int sock)
{
	CallState cs;
	unsigned char buf[4];
	uint32_t len;

	assert(sock != -1);

	/* The client application */
	if (!read_all(sock, (unsigned char *)&cs.appid, sizeof (cs.appid))) {
		return ;
	}
	gck_rpc_log("New session %d-%d\n", (uint32_t) (cs.appid >> 32),
		    (uint32_t) cs.appid);

	/* Setup our buffers */
	if (!call_init(&cs)) {
		gck_rpc_warn("out of memory");
		return;
	}

	/* The main thread loop */
	while (TRUE) {

		call_reset(&cs);

		/* Read the number of bytes ... */
		if (!read_all(sock, buf, 4))
			break;

		/* Calculate the number of bytes */
		len = egg_buffer_decode_uint32(buf);
		if (len >= 0x0FFFFFFF) {
			gck_rpc_warn
			    ("invalid message size from module: %u bytes", len);
			break;
		}

		/* Allocate memory */
		egg_buffer_reserve(&cs.req->buffer, cs.req->buffer.len + len);
		if (egg_buffer_has_error(&cs.req->buffer)) {
			gck_rpc_warn("error allocating buffer for message");
			break;
		}

		/* ... and read/parse in the actual message */
		if (!read_all(sock, cs.req->buffer.buf, len))
			break;

		egg_buffer_add_empty(&cs.req->buffer, len);

		if (!gck_rpc_message_parse(cs.req, GCK_RPC_REQUEST))
			break;

		/* ... send for processing ... */
		if (!dispatch_call(&cs))
			break;

		/* .. send back response length, and then response data */
		egg_buffer_encode_uint32(buf, cs.resp->buffer.len);
		if (!write_all(sock, buf, 4) ||
		    !write_all(sock, cs.resp->buffer.buf, cs.resp->buffer.len))
			break;
	}

	call_uninit(&cs);
}

static void *run_dispatch_thread(void *arg)
{
	int *sock = arg;
	assert(*sock != -1);

	run_dispatch_loop(*sock);

	/* The thread closes the socket and marks as done */
	assert(*sock != -1);
	close(*sock);
	*sock = -1;

	return NULL;
}

/* ---------------------------------------------------------------------------
 * MAIN THREAD
 */

typedef struct _DispatchState {
	struct _DispatchState *next;
	pthread_t thread;
	int socket;
} DispatchState;

/* The main daemon socket that we're listening on */
static int pkcs11_socket = -1;

/* The unix socket path, that we listen on */
static char pkcs11_socket_path[MAXPATHLEN] = { 0, };

/* A linked list of dispatcher threads */
static DispatchState *pkcs11_dispatchers = NULL;

void gck_rpc_layer_accept(void)
{
	struct sockaddr_un addr;
	DispatchState *ds, **here;
	int error;
	socklen_t addrlen;
	int new_fd;

	assert(pkcs11_socket != -1);

	/* Cleanup any completed dispatch threads */
	for (here = &pkcs11_dispatchers, ds = *here; ds != NULL; ds = *here) {
		if (ds->socket == -1) {
			pthread_join(ds->thread, NULL);
			*here = ds->next;
			free(ds);
		} else {
			here = &ds->next;
		}
	}

	addrlen = sizeof(addr);
	new_fd = accept(pkcs11_socket, (struct sockaddr *)&addr, &addrlen);
	if (new_fd < 0) {
		gck_rpc_warn("cannot accept pkcs11 connection: %s",
			     strerror(errno));
		return;
	}

	ds = calloc(1, sizeof(DispatchState));
	if (ds == NULL) {
		gck_rpc_warn("out of memory");
		close(new_fd);
		return;
	}

	ds->socket = new_fd;

	error = pthread_create(&ds->thread, NULL,
			       run_dispatch_thread, &(ds->socket));
	if (error) {
		gck_rpc_warn("couldn't start thread: %s", strerror(errno));
		close(new_fd);
		free(ds);
		return;
	}

	ds->next = pkcs11_dispatchers;
	pkcs11_dispatchers = ds;
}

int gck_rpc_layer_initialize(const char *prefix, CK_FUNCTION_LIST_PTR module)
{
	struct sockaddr_un addr;
	int sock;

#ifdef _DEBUG
	GCK_RPC_CHECK_CALLS();
#endif

	assert(module);
	assert(prefix);

	/* cannot be called more than once */
	assert(!pkcs11_module);
	assert(pkcs11_socket == -1);
	assert(pkcs11_dispatchers == NULL);

	memset(&addr, 0, sizeof(addr));

#ifdef  __MINGW32__
        {
		WSADATA wsaData;
		int iResult;

		iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
		if (iResult != 0) {
			gck_rpc_warn("WSAStartup failed: %d\n", iResult);
			return -1;
		}
        }
#endif

	if (!strncmp("tcp://", prefix, 6)) {
		int one = 1, port;
		char *p = NULL;
		const char *ip;

		ip = strdup(prefix + 6);
		if (ip)
			p = strchr(ip, ':');

		if (!ip) {
			gck_rpc_warn("invalid syntax for pkcs11 socket : %s",
				     prefix);
			return -1;
		}

		if (p) {
			*p = '\0';
			port = strtol(p + 1, NULL, 0);
		} else
			port = 0;

		sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

		if (sock < 0) {
			gck_rpc_warn("couldn't create pkcs11 socket: %s",
				     strerror(errno));
			return -1;
		}

                if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
                               (char *)&one, sizeof (one)) == -1) {
                        gck_rpc_warn("couldn't create set pkcs11 "
				 "socket options : %s", strerror (errno));
                        return -1;
                }

		if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                               (char *)&one, sizeof(one)) == -1) {
			gck_rpc_warn
			    ("couldn't create set pkcs11 socket options : %s",
			     strerror(errno));
			return -1;
		}

		addr.sun_family = AF_INET;
		if (inet_aton(ip, &((struct sockaddr_in *)&addr)->sin_addr) ==
		    0) {
			gck_rpc_warn("bad inet address : %s", ip);
			return -1;
		}
		((struct sockaddr_in *)&addr)->sin_port = htons(port);

		snprintf(pkcs11_socket_path, sizeof(pkcs11_socket_path),
			 "%s", prefix);
	} else {
		snprintf(pkcs11_socket_path, sizeof(pkcs11_socket_path),
			 "%s/socket.pkcs11", prefix);

		sock = socket(AF_UNIX, SOCK_STREAM, 0);

		if (sock < 0) {
			gck_rpc_warn("couldn't create pkcs11 socket: %s",
				     strerror(errno));
			return -1;
		}

		addr.sun_family = AF_UNIX;
		unlink(pkcs11_socket_path);
		strncpy(addr.sun_path, pkcs11_socket_path,
			sizeof(addr.sun_path));
	}

	if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		gck_rpc_warn("couldn't bind to pkcs11 socket: %s: %s",
			     pkcs11_socket_path, strerror(errno));
		return -1;
	}

	if (listen(sock, 128) < 0) {
		gck_rpc_warn("couldn't listen on pkcs11 socket: %s: %s",
			     pkcs11_socket_path, strerror(errno));
		return -1;
	}

	if (!strncmp("tcp://", prefix, 6)) {
		struct sockaddr_in sa;
		socklen_t sa_len;

		sa_len = sizeof(sa);

		if (getsockname(sock, (struct sockaddr *)&sa, &sa_len) == -1) {
			gck_rpc_warn("getsockname failed on pkcs11 socket: %s: %s",
				     pkcs11_socket_path, strerror(errno));
			return -1;
		}

		snprintf(pkcs11_socket_path, sizeof(pkcs11_socket_path),
			 "%s:%d", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
	}

	gck_rpc_log("Listening on: %s\n", pkcs11_socket_path);

	pkcs11_module = module;
	pkcs11_socket = sock;
	pkcs11_dispatchers = NULL;

	return sock;
}

void gck_rpc_layer_uninitialize(void)
{
	DispatchState *ds, *next;

	if (!pkcs11_module)
		return;

	/* Close our main listening socket */
	if (pkcs11_socket != -1)
		close(pkcs11_socket);
	pkcs11_socket = -1;

	/* Delete our unix socket */
	if (pkcs11_socket_path[0])
		unlink(pkcs11_socket_path);
	pkcs11_socket_path[0] = 0;

	/* Stop all of the dispatch threads */
	for (ds = pkcs11_dispatchers; ds; ds = next) {
		next = ds->next;

		/* Forcibly shutdown the connection */
		if (ds->socket)
			shutdown(ds->socket, SHUT_RDWR);
		pthread_join(ds->thread, NULL);

		/* This is always closed by dispatch thread */
		assert(ds->socket == -1);
		free(ds);
	}

	pkcs11_module = NULL;
}