gnunet

Main GNUnet Logic
Log | Files | Refs | Submodules | README | LICENSE

commit 22ab4538a2f7a21c0c831599a56d50c147de0ae5
parent 1a4262ba723c6806e2260f6431e273e29c1143b9
Author: Christian Grothoff <christian@grothoff.org>
Date:   Fri, 23 Aug 2013 13:39:32 +0000

-starting to rename vectorproduct to scalarproduct, as this is not doing a cross product

Diffstat:
Mconfigure.ac | 10+++++-----
Msrc/Makefile.am | 4++--
Msrc/include/Makefile.am | 1+
Rsrc/include/gnunet_vectorproduct_service.h -> src/include/gnunet_scalarproduct_service.h | 0
Asrc/scalarproduct/Makefile.am | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scalarproduct/gnunet-service-vectorproduct.c | 2067+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scalarproduct/gnunet-vectorproduct.c | 410+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scalarproduct/gnunet_vectorproduct.h | 274+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scalarproduct/test_vectorproduct_api.c | 865+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scalarproduct/test_vectorproduct_api_4peers.c | 1084+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scalarproduct/test_vectorproduct_api_data.conf | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scalarproduct/test_vectorproduct_api_regression.c | 852+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scalarproduct/test_vectorproduct_api_regression2.c | 931+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scalarproduct/vectorproduct.conf | 7+++++++
Asrc/scalarproduct/vectorproduct.conf.in | 7+++++++
Asrc/scalarproduct/vectorproduct_api.c | 715+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scalarproduct/vectorproduct_testing.h | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
17 files changed, 7557 insertions(+), 7 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -1291,10 +1291,11 @@ src/gns/Makefile src/gns/gns.conf src/gns/nss/Makefile src/hello/Makefile -src/include/Makefile -src/include/gnunet_directories.h src/identity/Makefile src/identity/identity.conf +src/include/Makefile +src/include/gnunet_directories.h +src/integration-tests/Makefile src/hostlist/Makefile src/lockmanager/Makefile src/lockmanager/lockmanager.conf @@ -1313,6 +1314,8 @@ src/postgres/Makefile src/pt/Makefile src/regex/Makefile src/regex/regex.conf +src/scalarproduct/Makefile +src/scalarproduct/vectorproduct.conf src/set/Makefile src/set/set.conf src/statistics/Makefile @@ -1329,9 +1332,6 @@ src/util/Makefile src/util/resolver.conf src/vpn/Makefile src/vpn/vpn.conf -src/vectorproduct/Makefile -src/vectorproduct/vectorproduct.conf -src/integration-tests/Makefile pkgconfig/Makefile pkgconfig/gnunetats.pc pkgconfig/gnunetarm.pc diff --git a/src/Makefile.am b/src/Makefile.am @@ -7,12 +7,12 @@ if HAVE_TESTING TESTBED = testbed CONSENSUS = consensus EXPERIMENTATION = experimentation - VECTORPRODUCT = vectorproduct + SCALARPRODUCT = scalarproduct endif if HAVE_EXPERIMENTAL EXP_DIR = dv $(CONSENSUS) $(EXPERIMENTATION) -#note: vectorproduct is not being listed here yet as the crypto is being reworked at the moment +#note: scalarproduct is not being listed here yet as the crypto is being reworked at the moment endif if HAVE_MYSQL diff --git a/src/include/Makefile.am b/src/include/Makefile.am @@ -70,6 +70,7 @@ gnunetinclude_HEADERS = \ gnunet_protocols.h \ gnunet_resolver_service.h \ gnunet_regex_service.h \ + gnunet_scalarproduct_service.h \ gnunet_scheduler_lib.h \ gnunet_server_lib.h \ gnunet_service_lib.h \ diff --git a/src/include/gnunet_vectorproduct_service.h b/src/include/gnunet_scalarproduct_service.h diff --git a/src/scalarproduct/Makefile.am b/src/scalarproduct/Makefile.am @@ -0,0 +1,112 @@ +INCLUDES = -I$(top_srcdir)/src/include + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +pkgcfg_DATA = \ + vectorproduct.conf + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + +bin_PROGRAMS = \ + gnunet-vectorproduct + +libexec_PROGRAMS = \ + gnunet-service-vectorproduct + +# FIXME: add option "configure --with-evil"? +#if HAVE_EXPERIMENTAL +#libexec_PROGRAMS += \ +# gnunet-service-evil-consensus +#endif + +lib_LTLIBRARIES = \ + libgnunetvectorproduct.la + +gnunet_vectorproduct_SOURCES = \ + gnunet-vectorproduct.c +gnunet_vectorproduct_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/vectorproduct/libgnunetvectorproduct.la \ + -lgcrypt \ + $(GN_LIBINTL) +gnunet_vectorproduct_DEPENDENCIES = \ + libgnunetvectorproduct.la + +gnunet_service_vectorproduct_SOURCES = \ + gnunet-service-vectorproduct.c +gnunet_service_vectorproduct_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/mesh/libgnunetmesh.la \ + $(top_builddir)/src/set/libgnunetset.la \ + -lgcrypt \ + $(GN_LIBINTL) + +libgnunetvectorproduct_la_SOURCES = \ + vectorproduct_api.c +libgnunetvectorproduct_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + -lgcrypt \ + $(LTLIBINTL) +libgnunetvectorproduct_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) + +check_PROGRAMS = \ + test_vectorproduct_api_regression \ + test_vectorproduct_api \ + test_vectorproduct_api_4peers +#FIXME unfinished +#test_vectorproduct_api_regression2 + +if ENABLE_TEST_RUN + TESTS = $(check_PROGRAMS) +endif + +test_consensus_api_SOURCES = \ + test_consensus_api.c +test_consensus_api_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/consensus/libgnunetconsensus.la + +test_vectorproduct_api_SOURCES = \ + test_vectorproduct_api.c +test_vectorproduct_api_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/vectorproduct/libgnunetvectorproduct.la \ + -lgcrypt + +#FIXME unfinished +#test_vectorproduct_api_regression2_SOURCES = \ +# test_vectorproduct_api_regression2.c +#test_vectorproduct_api_regression2_LDADD = \ +# $(top_builddir)/src/vectorproduct/libgnunetvectorproduct.la \ +# $(top_builddir)/src/util/libgnunetutil.la \ +# -lgcrypt + +test_vectorproduct_api_regression_SOURCES = \ + test_vectorproduct_api_regression.c +test_vectorproduct_api_regression_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/vectorproduct/libgnunetvectorproduct.la \ + -lgcrypt + +test_vectorproduct_api_4peers_SOURCES = \ + test_vectorproduct_api_4peers.c +test_vectorproduct_api_4peers_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/vectorproduct/libgnunetvectorproduct.la \ + -lgcrypt + +EXTRA_DIST = \ + test_vectorproduct.conf + diff --git a/src/scalarproduct/gnunet-service-vectorproduct.c b/src/scalarproduct/gnunet-service-vectorproduct.c @@ -0,0 +1,2067 @@ +/* + This file is part of GNUnet. + (C) 2013 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file vectorproduct/gnunet-service-vectorproduct.c + * @brief vectorproduct service implementation + * @author Christian M. Fuchs + */ +#include <limits.h> +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_core_service.h" +#include "gnunet_mesh_service.h" +#include "gnunet_applications.h" +#include "gnunet_protocols.h" +#include "gnunet_vectorproduct_service.h" +#include "gnunet_vectorproduct.h" + +/////////////////////////////////////////////////////////////////////////////// +// Global Variables +/////////////////////////////////////////////////////////////////////////////// + +/** + * Handle to the core service (NULL until we've connected to it). + */ +static struct GNUNET_CORE_Handle *my_core; + +/** + * Handle to the core service (NULL until we've connected to it). + */ +static struct GNUNET_MESH_Handle *my_mesh; + +/** + * The identity of this host. + */ +static struct GNUNET_PeerIdentity me; + +/** + * Service's own public key represented as string + */ +static unsigned char * my_pubkey_external; + +/** + * Service's own public key represented as string + */ +static uint16_t my_pubkey_external_length = 0; + +/** + * Service's own n + */ +static gcry_mpi_t my_n; + +/** + * Service's own n^2 (kept for performance) + */ +static gcry_mpi_t my_nsquare; + +/** + * Service's own public exponent + */ +static gcry_mpi_t my_g; + +/** + * Service's own private multiplier + */ +static gcry_mpi_t my_mu; + +/** + * Service's own private exponent + */ +static gcry_mpi_t my_lambda; + +/** + * Head of our double linked list for client-requests sent to us. + * for all of these elements we calculate a vector product with a remote peer + * split between service->service and client->service for simplicity + */ +static struct ServiceSession * from_client_head; +/** + * Tail of our double linked list for client-requests sent to us. + * for all of these elements we calculate a vector product with a remote peer + * split between service->service and client->service for simplicity + */ +static struct ServiceSession * from_client_tail; + +/** + * Head of our double linked list for service-requests sent to us. + * for all of these elements we help the requesting service in calculating a vector product + * split between service->service and client->service for simplicity + */ +static struct ServiceSession * from_service_head; + +/** + * Tail of our double linked list for service-requests sent to us. + * for all of these elements we help the requesting service in calculating a vector product + * split between service->service and client->service for simplicity + */ +static struct ServiceSession * from_service_tail; + +/** + * Certain events (callbacks for server & mesh operations) must not be queued after shutdown. + */ +static int do_shutdown; + +/////////////////////////////////////////////////////////////////////////////// +// Helper Functions +/////////////////////////////////////////////////////////////////////////////// + +/** + * Generates an Paillier private/public keyset and extracts the values using libgrcypt only + */ +static void +generate_keyset () +{ + gcry_sexp_t gen_parms; + gcry_sexp_t key; + gcry_sexp_t tmp_sexp; + gcry_mpi_t p; + gcry_mpi_t q; + gcry_mpi_t tmp1; + gcry_mpi_t tmp2; + gcry_mpi_t gcd; + + size_t erroff = 0; + + // we can still use the RSA keygen for generating p,q,n, but using e is pointless. + GNUNET_assert (0 == gcry_sexp_build (&gen_parms, &erroff, + "(genkey(rsa(nbits %d)(rsa-use-e 3:257)))", + KEYBITS)); + + GNUNET_assert (0 == gcry_pk_genkey (&key, gen_parms)); + gcry_sexp_release (gen_parms); + + // get n and d of our publickey as MPI + tmp_sexp = gcry_sexp_find_token (key, "n", 0); + GNUNET_assert (tmp_sexp); + my_n = gcry_sexp_nth_mpi (tmp_sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (tmp_sexp); + tmp_sexp = gcry_sexp_find_token (key, "p", 0); + GNUNET_assert (tmp_sexp); + p = gcry_sexp_nth_mpi (tmp_sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (tmp_sexp); + tmp_sexp = gcry_sexp_find_token (key, "q", 0); + GNUNET_assert (tmp_sexp); + q = gcry_sexp_nth_mpi (tmp_sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (key); + + tmp1 = gcry_mpi_new (0); + tmp2 = gcry_mpi_new (0); + gcd = gcry_mpi_new (0); + my_g = gcry_mpi_new (0); + my_mu = gcry_mpi_new (0); + my_nsquare = gcry_mpi_new (0); + my_lambda = gcry_mpi_new (0); + + // calculate lambda + // lambda = \frac{(p-1)*(q-1)}{gcd(p-1,q-1)} + gcry_mpi_sub_ui (tmp1, p, 1); + gcry_mpi_sub_ui (tmp2, q, 1); + gcry_mpi_gcd (gcd, tmp1, tmp2); + gcry_mpi_set (my_lambda, tmp1); + gcry_mpi_mul (my_lambda, my_lambda, tmp2); + gcry_mpi_div (my_lambda, NULL, my_lambda, gcd, 0); + + // generate a g + gcry_mpi_mul (my_nsquare, my_n, my_n); + do + { + // find a matching g + do + { + gcry_mpi_randomize (my_g, KEYBITS * 2, GCRY_WEAK_RANDOM); + // g must be smaller than n^2 + if (0 >= gcry_mpi_cmp (my_g, my_nsquare)) + continue; + + // g must have gcd == 1 with n^2 + gcry_mpi_gcd (gcd, my_g, my_nsquare); + } + while (gcry_mpi_cmp_ui (gcd, 1)); + + // is this a valid g? + // if so, gcd(((g^lambda mod n^2)-1 )/n, n) = 1 + gcry_mpi_powm (tmp1, my_g, my_lambda, my_nsquare); + gcry_mpi_sub_ui (tmp1, tmp1, 1); + gcry_mpi_div (tmp1, NULL, tmp1, my_n, 0); + gcry_mpi_gcd (gcd, tmp1, my_n); + } + while (gcry_mpi_cmp_ui (gcd, 1)); + + // calculate our mu based on g and n. + // mu = (((g^lambda mod n^2)-1 )/n)^-1 mod n + gcry_mpi_invm (my_mu, tmp1, my_n); + + GNUNET_assert (0 == gcry_sexp_build (&key, &erroff, + "(public-key (paillier (n %M)(g %M)))", + my_n, my_g)); + + // get the length of this sexpression + my_pubkey_external_length = gcry_sexp_sprint (key, + GCRYSEXP_FMT_CANON, + NULL, + UINT16_MAX); + + GNUNET_assert (my_pubkey_external_length > 0); + my_pubkey_external = GNUNET_malloc (my_pubkey_external_length); + + // convert the sexpression to canonical format + gcry_sexp_sprint (key, + GCRYSEXP_FMT_CANON, + my_pubkey_external, + my_pubkey_external_length); + + gcry_sexp_release (key); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Generated key set with key length %d bits.\n"), KEYBITS); +} + + +/** + * If target != size, move target bytes to the + * end of the size-sized buffer and zero out the + * first target-size bytes. + * + * @param buf original buffer + * @param size number of bytes in the buffer + * @param target target size of the buffer + */ +static void +adjust (unsigned char *buf, size_t size, size_t target) +{ + if (size < target) + { + memmove (&buf[target - size], buf, size); + memset (buf, 0, target - size); + } +} + + +/** + * encrypts an element using the paillier crypto system + * + * @param c ciphertext (output) + * @param m plaintext + * @param g the public base + * @param r random base (optional) gets generated and if not NULL but uninitialized + * @param n the module from which which r is chosen (Z*_n) + * @param n_square the module for encryption, for performance reasons. + */ +static void +encrypt_element (gcry_mpi_t c, gcry_mpi_t m, gcry_mpi_t g, gcry_mpi_t r, gcry_mpi_t n, gcry_mpi_t n_square) +{ +#ifndef DISABLE_CRYPTO + gcry_mpi_t tmp; + int release_r = GNUNET_NO; + + GNUNET_assert (tmp = gcry_mpi_new (0)); + if (NULL == r) + { + GNUNET_assert (r = gcry_mpi_new (0)); + release_r = GNUNET_YES; + + while (0 <= gcry_mpi_cmp (r, n) || 0 >= gcry_mpi_cmp_ui (r, 1)) + { + gcry_mpi_randomize (r, KEYBITS, GCRY_WEAK_RANDOM); + // r must be 1 < r < n + } + } + + + gcry_mpi_powm (c, g, m, n_square); + gcry_mpi_powm (tmp, r, n, n_square); + gcry_mpi_mulm (c, tmp, c, n_square); + + gcry_mpi_release (tmp); + if (GNUNET_YES == release_r) + gcry_mpi_release (r); +#else + gcry_mpi_set (c, m); +#endif +} + + +/** + * decrypts an element using the paillier crypto system + * + * @param m plaintext (output) + * @param c the ciphertext + * @param mu the modifier to correct encryption + * @param lambda the private exponent + * @param n the outer module for decryption + * @param n_square the inner module for decryption + */ +static void +decrypt_element (gcry_mpi_t m, gcry_mpi_t c, gcry_mpi_t mu, gcry_mpi_t lambda, gcry_mpi_t n, gcry_mpi_t n_square) +{ +#ifndef DISABLE_CRYPTO + gcry_mpi_powm (m, c, lambda, n_square); + gcry_mpi_sub_ui (m, m, 1); + gcry_mpi_div (m, NULL, m, n, 0); + gcry_mpi_mulm (m, m, mu, n); +#else + gcry_mpi_set (m, c); +#endif +} + + +/** + * computes the square sum over a vector of a given length. + * + * @param vector the vector to encrypt + * @param length the length of the vector + * @return an MPI value containing the calculated sum, never NULL + */ +static gcry_mpi_t +compute_square_sum (gcry_mpi_t * vector, uint16_t length) +{ + gcry_mpi_t elem; + gcry_mpi_t sum; + int32_t i; + + GNUNET_assert (sum = gcry_mpi_new (0)); + GNUNET_assert (elem = gcry_mpi_new (0)); + + // calculare E(sum (ai ^ 2), publickey) + for (i = 0; i < length; i++) + { + gcry_mpi_mul (elem, vector[i], vector[i]); + gcry_mpi_add (sum, sum, elem); + } + gcry_mpi_release (elem); + + return sum; +} + + +/** + * Primitive callback for copying over a message, as they + * usually are too complex to be handled in the callback itself. + * clears a session-callback, if a session was handed over and the transmit handle was stored + * + * @param cls the message object + * @param size the size of the buffer we got + * @param buf the buffer to copy the message to + * @return 0 if we couldn't copy, else the size copied over + */ +static size_t +do_send_message (void *cls, size_t size, void *buf) +{ + struct MessageObject * info = cls; + struct GNUNET_MessageHeader * msg; + size_t written = 0; + + GNUNET_assert (info); + msg = info->msg; + GNUNET_assert (msg); + GNUNET_assert (buf); + + if (ntohs (msg->size) == size) + { + memcpy (buf, msg, size); + written = size; + } + + // reset the transmit handle, if necessary + if (info->transmit_handle) + *info->transmit_handle = NULL; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Sent a message of type %hu.\n"), ntohs (msg->type)); + GNUNET_free(msg); + GNUNET_free(info); + return written; +} + + +/** + * initializes a new vector with fresh MPI values (=0) of a given length + * + * @param length of the vector to create + * @return the initialized vector, never NULL + */ +static gcry_mpi_t * +initialize_mpi_vector (uint16_t length) +{ + uint32_t i; + gcry_mpi_t * output = GNUNET_malloc (sizeof (gcry_mpi_t) * length); + + for (i = 0; i < length; i++) + GNUNET_assert (NULL != (output[i] = gcry_mpi_new (0))); + return output; +} + + +/** + * permutes an MPI vector according to the given permutation vector + * + * @param vector the vector to permuted + * @param perm the permutation to use + * @param length the length of the vectors + * @return the permuted vector (same as input), never NULL + */ +static gcry_mpi_t * +permute_vector (gcry_mpi_t * vector, + unsigned int * perm, + uint32_t length) +{ + gcry_mpi_t tmp[length]; + uint32_t i; + + GNUNET_assert (length > 0); + + // backup old layout + memcpy (tmp, vector, length * sizeof (gcry_mpi_t)); + + // permute vector according to given + for (i = 0; i < length; i++) + vector[i] = tmp[perm[i]]; + + return vector; +} + + +/** + * Populate a vector with random integer values and convert them to + * + * @param length the length of the vector we must generate + * @return an array of MPI values with random values + */ +static gcry_mpi_t * +generate_random_vector (uint16_t length) +{ + gcry_mpi_t * random_vector; + int32_t value; + uint32_t i; + + random_vector = initialize_mpi_vector (length); + for (i = 0; i < length; i++) + { + value = (int32_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX); + + // long to gcry_mpi_t + if (value < 0) + gcry_mpi_sub_ui (random_vector[i], + random_vector[i], + -value); + else + random_vector[i] = gcry_mpi_set_ui (random_vector[i], value); + } + + return random_vector; +} + + +/** + * Finds a not terminated client/service session in the + * given DLL based on session key, element count and state. + * + * @param tail - the tail of the DLL + * @param my - the session to compare it to + * @return a pointer to a matching session, + * else NULL + */ +static struct ServiceSession * +find_matching_session (struct ServiceSession * tail, + struct GNUNET_HashCode * key, + uint16_t element_count, + enum SessionState * state, + const struct GNUNET_PeerIdentity * peerid) +{ + struct ServiceSession * curr; + + for (curr = tail; NULL != curr; curr = curr->prev) + { + // if the key matches, and the element_count is same + if ((!memcmp (&curr->key, key, sizeof (struct GNUNET_HashCode))) + && (curr->element_count == element_count)) + { + // if incoming state is NULL OR is same as state of the queued request + if ((NULL == state) || (curr->state == *state)) + { + // if peerid is NULL OR same as the peer Id in the queued request + if ((NULL == peerid) + || (!memcmp (&curr->peer, peerid, sizeof (struct GNUNET_PeerIdentity)))) + // matches and is not an already terminated session + return curr; + } + } + } + + return NULL; +} + + +static void +destroy_tunnel (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ServiceSession * session = cls; + + if (session->tunnel) + { + GNUNET_MESH_tunnel_destroy (session->tunnel); + session->tunnel = NULL; + } + session->service_transmit_handle = NULL; + // we need to set this to NULL so there is no problem with double-cancel later on. +} + + +static void +free_session (struct ServiceSession * session) +{ + int i; + + if (FINALIZED != session->state) + { + if (session->a) + { + for (i = 0; i < session->used_element_count; i++) + gcry_mpi_release (session->a[i]); + + GNUNET_free (session->a); + } + if (session->product) + gcry_mpi_release (session->product); + + if (session->remote_pubkey) + gcry_sexp_release (session->remote_pubkey); + + GNUNET_free_non_null (session->vector); + } + + GNUNET_free (session); +} +/////////////////////////////////////////////////////////////////////////////// +// Event and Message Handlers +/////////////////////////////////////////////////////////////////////////////// + + +/** + * A client disconnected. + * + * Remove the associated session(s), release datastructures + * and cancel pending outgoing transmissions to the client. + * if the session has not yet completed, we also cancel Alice's request to Bob. + * + * @param cls closure, NULL + * @param client identification of the client + */ +static void +handle_client_disconnect (void *cls, + struct GNUNET_SERVER_Client + * client) +{ + struct ServiceSession * elem; + struct ServiceSession * next; + + // start from the tail, old stuff will be there... + for (elem = from_client_head; NULL != elem; elem = next) + { + next = elem->next; + if (elem->client != client) + continue; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Client (%p) disconnected from us.\n"), client); + GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, elem); + + if (!(elem->role == BOB && elem->state == FINALIZED)) + { + //we MUST terminate any client message underway + if (elem->service_transmit_handle && elem->tunnel) + GNUNET_MESH_notify_transmit_ready_cancel (elem->service_transmit_handle); + if (elem->tunnel && elem->state == WAITING_FOR_RESPONSE_FROM_SERVICE) + destroy_tunnel (elem, NULL); + } + free_session (elem); + } +} + + +/** + * Notify the client that the session has succeeded or failed completely. + * This message gets sent to + * * alice's client if bob disconnected or to + * * bob's client if the operation completed or alice disconnected + * + * @param client_session the associated client session + * @return GNUNET_NO, if we could not notify the client + * GNUNET_YES if we notified it. + */ +static void +prepare_client_end_notification (void * cls, + const struct GNUNET_SCHEDULER_TaskContext * tc) +{ + struct ServiceSession * session = cls; + struct GNUNET_VECTORPRODUCT_client_response * msg; + struct MessageObject * msg_obj; + + GNUNET_assert (NULL != session); + + msg = GNUNET_new (struct GNUNET_VECTORPRODUCT_client_response); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_SERVICE_TO_CLIENT); + memcpy (&msg->key, &session->key, sizeof (struct GNUNET_HashCode)); + memcpy (&msg->peer, &session->peer, sizeof ( struct GNUNET_PeerIdentity)); + msg->header.size = htons (sizeof (struct GNUNET_VECTORPRODUCT_client_response)); + // 0 size and the first char in the product is 0, which should never be zero if encoding is used. + msg->product_length = htonl (0); + + msg_obj = GNUNET_malloc (sizeof (struct MessageObject)); + msg_obj->msg = (struct GNUNET_MessageHeader *) msg; + msg_obj->transmit_handle = NULL; // do not reset the transmit handle, please + + //transmit this message to our client + session->client_transmit_handle = + GNUNET_SERVER_notify_transmit_ready (session->client, + sizeof (struct GNUNET_VECTORPRODUCT_client_response), + GNUNET_TIME_UNIT_FOREVER_REL, + &do_send_message, + msg_obj); + + + // if we could not even queue our request, something is wrong + if ( ! session->client_transmit_handle) + { + + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not send message to client (%p)! This is OK if it was disconnected beforehand already.\n"), session->client); + // usually gets freed by do_send_message + GNUNET_free (msg_obj); + GNUNET_free (msg); + } + else + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Sending session-end notification to client (%p) for session %s\n"), &session->client, GNUNET_h2s (&session->key)); + + free_session(session); +} + + +/** + * Bob executes: + * generates the response message to be sent to alice after computing + * the values (1), (2), S and S' + * (1)[]: $E_A(a_{pi(i)}) times E_A(- r_{pi(i)} - b_{pi(i)}) &= E_A(a_{pi(i)} - r_{pi(i)} - b_{pi(i)})$ + * (2)[]: $E_A(a_{pi'(i)}) times E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$ + * S: $S := E_A(sum (r_i + b_i)^2)$ + * S': $S' := E_A(sum r_i^2)$ + * + * @param kp (1)[]: $E_A(a_{pi(i)}) times E_A(- r_{pi(i)} - b_{pi(i)}) &= E_A(a_{pi(i)} - r_{pi(i)} - b_{pi(i)})$ + * @param kq (2)[]: $E_A(a_{pi'(i)}) times E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$ + * @param s S: $S := E_A(sum (r_i + b_i)^2)$ + * @param stick S': $S' := E_A(sum r_i^2)$ + * @param request the associated requesting session with alice + * @param response the associated responder session with bob's client + * @return GNUNET_SYSERR if the function was called with NULL parameters or if there was an error + * GNUNET_NO if we could not send our message + * GNUNET_OK if the operation succeeded + */ +static int +prepare_service_response (gcry_mpi_t * kp, + gcry_mpi_t * kq, + gcry_mpi_t s, + gcry_mpi_t stick, + struct ServiceSession * request, + struct ServiceSession * response) +{ + struct GNUNET_VECTORPRODUCT_service_response * msg; + uint16_t msg_length = 0; + unsigned char * current = NULL; + unsigned char * element_exported = NULL; + size_t element_length = 0; + int i; + + GNUNET_assert (request); + + msg_length = sizeof (struct GNUNET_VECTORPRODUCT_service_response) + + 2 * request->used_element_count * PAILLIER_ELEMENT_LENGTH // kp, kq + + 2 * PAILLIER_ELEMENT_LENGTH; // s, stick + + msg = GNUNET_malloc (msg_length); + GNUNET_assert (msg); + + msg->header.type = htons (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_BOB_TO_ALICE); + msg->header.size = htons (msg_length); + msg->element_count = htons (request->element_count); + msg->used_element_count = htons (request->used_element_count); + memcpy (&msg->key, &request->key, sizeof (struct GNUNET_HashCode)); + current = (unsigned char *) &msg[1]; + + // 4 times the same logics with slight variations. + // doesn't really justify having 2 functions for that + // so i put it into blocks to enhance readability + // convert s + { + element_exported = GNUNET_malloc (PAILLIER_ELEMENT_LENGTH); + GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG, + element_exported, PAILLIER_ELEMENT_LENGTH, + &element_length, + s)); + adjust (element_exported, element_length, PAILLIER_ELEMENT_LENGTH); + memcpy (current, element_exported, PAILLIER_ELEMENT_LENGTH); + GNUNET_free (element_exported); + current += PAILLIER_ELEMENT_LENGTH; + } + + // convert stick + { + element_exported = GNUNET_malloc (PAILLIER_ELEMENT_LENGTH); + GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG, + element_exported, PAILLIER_ELEMENT_LENGTH, + &element_length, + stick)); + adjust (element_exported, element_length, PAILLIER_ELEMENT_LENGTH); + memcpy (current, element_exported, PAILLIER_ELEMENT_LENGTH); + GNUNET_free (element_exported); + current += PAILLIER_ELEMENT_LENGTH; + } + + // convert kp[] + for (i = 0; i < request->used_element_count; i++) + { + element_exported = GNUNET_malloc (PAILLIER_ELEMENT_LENGTH); + GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG, + element_exported, PAILLIER_ELEMENT_LENGTH, + &element_length, + kp[i])); + adjust (element_exported, element_length, PAILLIER_ELEMENT_LENGTH); + memcpy (current, element_exported, PAILLIER_ELEMENT_LENGTH); + GNUNET_free (element_exported); + current += PAILLIER_ELEMENT_LENGTH; + } + + + // convert kq[] + for (i = 0; i < request->used_element_count; i++) + { + element_exported = GNUNET_malloc (PAILLIER_ELEMENT_LENGTH); + GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG, + element_exported, PAILLIER_ELEMENT_LENGTH, + &element_length, + kq[i])); + adjust (element_exported, element_length, PAILLIER_ELEMENT_LENGTH); + memcpy (current, element_exported, PAILLIER_ELEMENT_LENGTH); + GNUNET_free (element_exported); + current += PAILLIER_ELEMENT_LENGTH; + } + + if (GNUNET_SERVER_MAX_MESSAGE_SIZE >= msg_length) + { + struct MessageObject * msg_obj; + + msg_obj = GNUNET_new (struct MessageObject); + msg_obj->msg = (struct GNUNET_MessageHeader *) msg; + msg_obj->transmit_handle = (void *) &request->service_transmit_handle; //and reset the transmit handle + request->service_transmit_handle = + GNUNET_MESH_notify_transmit_ready (request->tunnel, + GNUNET_YES, + GNUNET_TIME_UNIT_FOREVER_REL, + &request->peer, //must be specified, we are a slave/participant/non-owner + msg_length, + &do_send_message, + msg_obj); + // we don't care if it could be send or not. either way, the session is over for us. + request->state = FINALIZED; + response->state = FINALIZED; + } + else + { + // TODO FEATURE: fallback to fragmentation, in case the message is too long + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Message too large, fragmentation is currently not supported!)\n")); + } + + //disconnect our client + if ( ! request->service_transmit_handle) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Could not send service-response message via mesh!)\n")); + GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, response); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &prepare_client_end_notification, + response); + return GNUNET_NO; + } + return GNUNET_OK; +} + + +/** + * executed by bob: + * compute the values + * (1)[]: $E_A(a_{pi(i)}) times E_A(- r_{pi(i)} - b_{pi(i)}) &= E_A(a_{pi(i)} - r_{pi(i)} - b_{pi(i)})$ + * (2)[]: $E_A(a_{pi'(i)}) times E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$ + * S: $S := E_A(sum (r_i + b_i)^2)$ + * S': $S' := E_A(sum r_i^2)$ + * + * @param request the requesting session + bob's requesting peer + * @param response the responding session + bob's client handle + * @return GNUNET_SYSERR if the computation failed + * GNUNET_OK if everything went well. + */ +static int +compute_service_response (struct ServiceSession * request, + struct ServiceSession * response) +{ + int i, j, ret = GNUNET_SYSERR; + unsigned int * p; + unsigned int * q; + uint16_t count; + gcry_mpi_t * r = NULL; + gcry_mpi_t * kp = NULL; + gcry_mpi_t * kq = NULL; + gcry_mpi_t * b; + gcry_mpi_t * ap; + gcry_mpi_t * aq; + gcry_mpi_t * bp; + gcry_mpi_t * bq; + gcry_mpi_t * rp; + gcry_mpi_t * rq; + gcry_mpi_t s = NULL; + gcry_mpi_t stick = NULL; + gcry_mpi_t remote_n = NULL; + gcry_mpi_t remote_nsquare; + gcry_mpi_t remote_g = NULL; + gcry_sexp_t tmp_exp; + uint32_t value; + + GNUNET_assert (request != NULL && response != NULL); + count = request->used_element_count; + + b = GNUNET_malloc (sizeof (gcry_mpi_t) * count); + ap = GNUNET_malloc (sizeof (gcry_mpi_t) * count); + bp = GNUNET_malloc (sizeof (gcry_mpi_t) * count); + aq = GNUNET_malloc (sizeof (gcry_mpi_t) * count); + bq = GNUNET_malloc (sizeof (gcry_mpi_t) * count); + rp = GNUNET_malloc (sizeof (gcry_mpi_t) * count); + rq = GNUNET_malloc (sizeof (gcry_mpi_t) * count); + + // convert responder session to from long to mpi + for (i = 0, j = 0; i < response->element_count && j < count; i++) + { + if (request->mask[i / 8] & (1 << (i % 8))) + { + value = response->vector[i] >= 0 ? response->vector[i] : -response->vector[i]; + // long to gcry_mpi_t + if (0 > response->vector[i]) + { + b[j] = gcry_mpi_new (0); + gcry_mpi_sub_ui (b[j], b[j], value); + } + else + { + b[j] = gcry_mpi_set_ui (NULL, value); + } + j++; + } + } + GNUNET_free (response->vector); + response->vector = NULL; + + tmp_exp = gcry_sexp_find_token (request->remote_pubkey, "n", 0); + if ( ! tmp_exp) + { + GNUNET_break_op (0); + gcry_sexp_release (request->remote_pubkey); + request->remote_pubkey = NULL; + goto except; + } + remote_n = gcry_sexp_nth_mpi (tmp_exp, 1, GCRYMPI_FMT_USG); + if ( ! remote_n) + { + GNUNET_break (0); + gcry_sexp_release (tmp_exp); + goto except; + } + remote_nsquare = gcry_mpi_new (KEYBITS + 1); + gcry_mpi_mul (remote_nsquare, remote_n, remote_n); + gcry_sexp_release (tmp_exp); + tmp_exp = gcry_sexp_find_token (request->remote_pubkey, "g", 0); + gcry_sexp_release (request->remote_pubkey); + request->remote_pubkey = NULL; + if ( ! tmp_exp) + { + GNUNET_break_op (0); + gcry_mpi_release (remote_n); + goto except; + } + remote_g = gcry_sexp_nth_mpi (tmp_exp, 1, GCRYMPI_FMT_USG); + if ( ! remote_g) + { + GNUNET_break (0); + gcry_mpi_release (remote_n); + gcry_sexp_release (tmp_exp); + goto except; + } + gcry_sexp_release (tmp_exp); + + // generate r, p and q + r = generate_random_vector (count); + p = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, count); + q = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, count); + //initialize the result vectors + kp = initialize_mpi_vector (count); + kq = initialize_mpi_vector (count); + + // copy the REFERNCES of a, b and r into aq and bq. we will not change + // those values, thus we can work with the references + memcpy (ap, request->a, sizeof (gcry_mpi_t) * count); + memcpy (aq, request->a, sizeof (gcry_mpi_t) * count); + memcpy (bp, b, sizeof (gcry_mpi_t) * count); + memcpy (bq, b, sizeof (gcry_mpi_t) * count); + memcpy (rp, r, sizeof (gcry_mpi_t) * count); + memcpy (rq, r, sizeof (gcry_mpi_t) * count); + + // generate p and q permutations for a, b and r + GNUNET_assert (permute_vector (ap, p, count)); + GNUNET_assert (permute_vector (bp, p, count)); + GNUNET_assert (permute_vector (rp, p, count)); + GNUNET_assert (permute_vector (aq, q, count)); + GNUNET_assert (permute_vector (bq, q, count)); + GNUNET_assert (permute_vector (rq, q, count)); + + // encrypt the element + // for the sake of readability I decided to have dedicated permutation + // vectors, which get rid of all the lookups in p/q. + // however, ap/aq are not absolutely necessary but are just abstraction + // Calculate Kp = E(a_pi) + E(-r_pi - b_pi) + for (i = 0; i < count; i++) + { + // E(-r_pi - b_pi) + gcry_mpi_sub (kp[i], kp[i], rp[i]); + gcry_mpi_sub (kp[i], kp[i], bp[i]); + encrypt_element (kp[i], kp[i], NULL, remote_g, remote_n, remote_nsquare); + + // E(-r_pi - b_pi) * E(a_pi) == E(a + (-r -b)) + //gcry_mpi_mulm (kp[i], kp[i], ap[i], remote_nsquare); + gcry_mpi_add (kp[i], kp[i], ap[i]); + } + GNUNET_free (ap); + GNUNET_free (bp); + GNUNET_free (rp); + + // Calculate Kq = E(a_qi) + E( -r_qi) + for (i = 0; i < count; i++) + { + // E(-r_qi) + gcry_mpi_sub (kq[i], kq[i], rq[i]); + encrypt_element (kq[i], kq[i], NULL, remote_g, remote_n, remote_nsquare); + + // E(-r_qi) * E(a_qi) == E(aqi + (- rqi)) + //gcry_mpi_mulm (kq[i], kq[i], aq[i], remote_nsquare); + gcry_mpi_add (kq[i], kq[i], aq[i]); + } + GNUNET_free (aq); + GNUNET_free (bq); + GNUNET_free (rq); + + // Calculate S' = E(SUM( r_i^2 )) + stick = compute_square_sum (r, count); + encrypt_element (stick, stick, NULL, remote_g, remote_n, remote_nsquare); + + // Calculate S = E(SUM( (r_i + b_i)^2 )) + for (i = 0; i < count; i++) + { + gcry_mpi_add (r[i], r[i], b[i]); + } + s = compute_square_sum (r, count); + encrypt_element (s, s, NULL, remote_g, remote_n, remote_nsquare); + gcry_mpi_release (remote_n); + gcry_mpi_release (remote_g); + gcry_mpi_release (remote_nsquare); + + // release r and tmp + for (i = 0; i < count; i++) + // rp, rq, aq, ap, bp, bq are released along with a, r, b respectively, (a and b are handled at except:) + gcry_mpi_release (r[i]); + + // copy the Kp[], Kq[], S and Stick into a new message + if (GNUNET_YES != prepare_service_response (kp, kq, s, stick, request, response)) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Computation of values for alice failed!\n")); + else + ret = GNUNET_OK; + + for (i = 0; i < count; i++) + { + gcry_mpi_release (kq[i]); + gcry_mpi_release (kp[i]); + } + + gcry_mpi_release (s); + gcry_mpi_release (stick); + +except: + for (i = 0; i < count; i++) + { + gcry_mpi_release (b[i]); + gcry_mpi_release (request->a[i]); + } + + GNUNET_free (b); + GNUNET_free (request->a); + request->a = NULL; + + return ret; +} + + +/** + * Executed by Alice, fills in a service-request message and sends it to the given peer + * + * @param session the session associated with this request, then also holds the CORE-handle + * @return GNUNET_SYSERR if we could not send the message + * GNUNET_NO if the message was too large + * GNUNET_OK if we sent it + */ +static void +prepare_service_request (void *cls, + const struct GNUNET_PeerIdentity * peer, + const struct GNUNET_ATS_Information * atsi) +{ + struct ServiceSession * session = cls; + unsigned char * current; + struct GNUNET_VECTORPRODUCT_service_request * msg; + struct MessageObject * msg_obj; + unsigned int i; + unsigned int j; + uint16_t msg_length; + size_t element_length = 0; //gets initialized by gcry_mpi_print, but the compiler doesn't know that + gcry_mpi_t a; + gcry_mpi_t r; + uint32_t value; + + GNUNET_assert (NULL != cls); + GNUNET_assert (NULL != peer); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Successfully created new tunnel to peer (%s)!\n"), GNUNET_i2s (peer)); + + msg_length = sizeof (struct GNUNET_VECTORPRODUCT_service_request) + + session->used_element_count * PAILLIER_ELEMENT_LENGTH + + session->mask_length + + my_pubkey_external_length; + + if (GNUNET_SERVER_MAX_MESSAGE_SIZE < sizeof (struct GNUNET_VECTORPRODUCT_service_request) + + session->used_element_count * PAILLIER_ELEMENT_LENGTH + + session->mask_length + + my_pubkey_external_length) + { + // TODO FEATURE: fallback to fragmentation, in case the message is too long + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Message too large, fragmentation is currently not supported!\n")); + GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, session); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &prepare_client_end_notification, + session); + return; + } + msg = GNUNET_malloc (msg_length); + + msg->header.type = htons (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_ALICE_TO_BOB); + memcpy (&msg->key, &session->key, sizeof (struct GNUNET_HashCode)); + msg->mask_length = htons (session->mask_length); + msg->pk_length = htons (my_pubkey_external_length); + msg->used_element_count = htons (session->used_element_count); + msg->element_count = htons (session->element_count); + msg->header.size = htons (msg_length); + + // fill in the payload + current = (unsigned char *) &msg[1]; + // copy over the mask + memcpy (current, session->mask, session->mask_length); + // copy over our public key + current += session->mask_length; + memcpy (current, my_pubkey_external, my_pubkey_external_length); + current += my_pubkey_external_length; + + // now copy over the element vector + session->a = GNUNET_malloc (sizeof (gcry_mpi_t) * session->used_element_count); + a = gcry_mpi_new (KEYBITS * 2); + r = gcry_mpi_new (KEYBITS * 2); + // encrypt our vector and generate string representations + for (i = 0, j = 0; i < session->element_count; i++) + { + // if this is a used element... + if (session->mask[i / 8] & 1 << (i % 8)) + { + unsigned char * element_exported = GNUNET_malloc (PAILLIER_ELEMENT_LENGTH); + value = session->vector[i] >= 0 ? session->vector[i] : -session->vector[i]; + + // long to gcry_mpi_t + if (session->vector[i] < 0) + { + a = gcry_mpi_set_ui (NULL, 0); + gcry_mpi_sub_ui (a, a, value); + } + else + a = gcry_mpi_set_ui (NULL, value); + + // multiply with a given factor to avoid disclosing 1 + session->a[j++] = gcry_mpi_set (NULL, a); + encrypt_element (a, a, r, my_g, my_n, my_nsquare); + + // get representation as string + // we always supply some value, so gcry_mpi_print fails only if it can't reserve memory + GNUNET_assert ( ! gcry_mpi_print (GCRYMPI_FMT_USG, + element_exported, PAILLIER_ELEMENT_LENGTH, + &element_length, + a)); + + // move buffer content to the end of the buffer so it can easily be read by libgcrypt. also this now has fixed size + adjust (element_exported, element_length, PAILLIER_ELEMENT_LENGTH); + + // copy over to the message + memcpy (current, element_exported, PAILLIER_ELEMENT_LENGTH); + current += PAILLIER_ELEMENT_LENGTH; + } + } + gcry_mpi_release (a); + gcry_mpi_release (r); + + msg_obj = GNUNET_malloc (sizeof (struct MessageObject)); + msg_obj->msg = (struct GNUNET_MessageHeader *) msg; + msg_obj->transmit_handle = (void *) &session->service_transmit_handle; //and reset the transmit handle + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transmitting service request.\n")); + + //transmit via mesh messaging + session->state = WAITING_FOR_RESPONSE_FROM_SERVICE; + session->service_transmit_handle = GNUNET_MESH_notify_transmit_ready (session->tunnel, GNUNET_YES, + GNUNET_TIME_UNIT_FOREVER_REL, + peer, //multicast to all targets, maybe useful in the future + msg_length, + &do_send_message, + msg_obj); + if ( ! session->service_transmit_handle) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Could not send mutlicast message to tunnel!\n")); + GNUNET_free (msg_obj); + GNUNET_free (msg); + GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, session); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &prepare_client_end_notification, + session); + return; + } +} + + +/** + * Method called whenever a peer has disconnected from the tunnel. + * Implementations of this callback must NOT call + * GNUNET_MESH_tunnel_destroy immediately, but instead schedule those + * to run in some other task later. However, calling + * "GNUNET_MESH_notify_transmit_ready_cancel" is allowed. + * + * @param cls closure + * @param peer peer identity the tunnel stopped working with + */ +static void +tunnel_peer_disconnect_handler (void *cls, const struct GNUNET_PeerIdentity * peer) +{ + // as we have only one peer connected in each session, just remove the session and say good bye + struct ServiceSession * session = cls; + struct ServiceSession * curr; + GNUNET_assert(cls); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Peer (%s) disconnected from our tunnel!\n"), GNUNET_i2s (peer)); + + if ((session->role == ALICE) && (FINALIZED != session->state) && ( ! do_shutdown)) + { + for (curr = from_client_head; NULL != curr; curr = curr->next) + if (curr == session) + { + GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, session); + break; + } + GNUNET_SCHEDULER_add_now (&destroy_tunnel, + session); + // if this happened before we received the answer, we must terminate the session + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &prepare_client_end_notification, + session); + } +} + + +/** + * Handler for a client request message. + * Can either be type A or B + * A: request-initiation to compute a scalar product with a peer + * B: response role, keep the values + session and wait for a matching session or process a waiting request + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + */ +static void +handle_client_request (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_VECTORPRODUCT_client_request * msg = (struct GNUNET_VECTORPRODUCT_client_request *) message; + struct ServiceSession * session; + uint16_t element_count; + uint16_t mask_length; + uint16_t msg_type; + int32_t * vector; + uint32_t i; + + GNUNET_assert (message); + + //we need at least a peer and one message id to compare + if (sizeof (struct GNUNET_VECTORPRODUCT_client_request) > ntohs (msg->header.size)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Too short message received from client!\n")); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + msg_type = ntohs (msg->header.type); + element_count = ntohs (msg->element_count); + mask_length = ntohs (msg->mask_length); + + //sanity check: is the message as long as the message_count fields suggests? + if (( ntohs (msg->header.size) != (sizeof (struct GNUNET_VECTORPRODUCT_client_request) + element_count * sizeof (int32_t) + mask_length)) + || (0 == element_count)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Invalid message received from client, session information incorrect!\n")); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + // do we have a duplicate session here already? + if (NULL != find_matching_session (from_client_tail, + &msg->key, + element_count, + NULL, NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Duplicate session information received, cannot create new session with key `%s'\n"), GNUNET_h2s (&msg->key)); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + session = GNUNET_malloc (sizeof (struct ServiceSession)); + session->client = client; + session->element_count = element_count; + session->mask_length = mask_length; + // get our transaction key + memcpy (&session->key, &msg->key, sizeof (struct GNUNET_HashCode)); + //allocate memory for vector and encrypted vector + session->vector = GNUNET_malloc (sizeof (int32_t) * element_count); + vector = (int32_t *) & msg[1]; + + if (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_CLIENT_TO_ALICE == msg_type) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Got client-request-session with key %s, preparing tunnel to remote service.\n"), GNUNET_h2s (&session->key)); + + session->role = ALICE; + // fill in the mask + session->mask = GNUNET_malloc (mask_length); + memcpy (session->mask, &vector[element_count], mask_length); + + // copy over the elements + session->used_element_count = 0; + for (i = 0; i < element_count; i++) + { + session->vector[i] = ntohl (vector[i]); + if (session->vector[i] == 0) + session->mask[i / 8] &= ~(1 << (i % 8)); + if (session->mask[i / 8] & (1 << (i % 8))) + session->used_element_count++; + } + + if ( ! session->used_element_count) + { + GNUNET_break_op (0); + GNUNET_free (session->vector); + GNUNET_free (session->a); + GNUNET_free (session); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + //session with ourself makes no sense! + if ( ! memcmp (&msg->peer, &me, sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_break (0); + GNUNET_free (session->vector); + GNUNET_free (session->a); + GNUNET_free (session); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + // get our peer ID + memcpy (&session->peer, &msg->peer, sizeof (struct GNUNET_PeerIdentity)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Creating new tunnel to for session with key %s.\n"), GNUNET_h2s (&session->key)); + GNUNET_CONTAINER_DLL_insert (from_client_head, from_client_tail, session); + session->tunnel = GNUNET_MESH_tunnel_create (my_mesh, session, + prepare_service_request, + tunnel_peer_disconnect_handler, + session); + if ( ! session->tunnel) + { + GNUNET_break (0); + GNUNET_free (session->vector); + GNUNET_free (session->a); + GNUNET_free (session); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_MESH_peer_request_connect_add (session->tunnel, &session->peer); + GNUNET_SERVER_receive_done (client, GNUNET_YES); + session->state = WAITING_FOR_BOBS_CONNECT; + } + else + { + struct ServiceSession * requesting_session; + enum SessionState needed_state = REQUEST_FROM_SERVICE_RECEIVED; + + session->role = BOB; + session->mask = NULL; + // copy over the elements + session->used_element_count = element_count; + for (i = 0; i < element_count; i++) + session->vector[i] = ntohl (vector[i]); + session->state = MESSAGE_FROM_RESPONDING_CLIENT_RECEIVED; + + GNUNET_CONTAINER_DLL_insert (from_client_head, from_client_tail, session); + GNUNET_SERVER_receive_done (client, GNUNET_YES); + //check if service queue contains a matching request + requesting_session = find_matching_session (from_service_tail, + &session->key, + session->element_count, + &needed_state, NULL); + if (NULL != requesting_session) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Got client-responder-session with key %s and a matching service-request-session set, processing.\n"), GNUNET_h2s (&session->key)); + if (GNUNET_OK != compute_service_response (requesting_session, session)) + { + GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, session); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &prepare_client_end_notification, + session); + } + } + else + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Got client-responder-session with key %s but NO matching service-request-session set, queuing element for later use.\n"), GNUNET_h2s (&session->key)); + // no matching session exists yet, store the response + // for later processing by handle_service_request() + } +} + + +/** + * Function called for inbound tunnels. + * + * @param cls closure + * @param tunnel new handle to the tunnel + * @param initiator peer that started the tunnel + * @param atsi performance information for the tunnel + * @return initial tunnel context for the tunnel + * (can be NULL -- that's not an error) + */ +static void * +tunnel_incoming_handler (void *cls, struct GNUNET_MESH_Tunnel *tunnel, + const struct GNUNET_PeerIdentity *initiator, + const struct GNUNET_ATS_Information *atsi) +{ + + struct ServiceSession * c = GNUNET_new (struct ServiceSession); + + memcpy (&c->peer, initiator, sizeof (struct GNUNET_PeerIdentity)); + c->tunnel = tunnel; + c->role = BOB; + return c; +} + + +/** + * Function called whenever an inbound tunnel is destroyed. Should clean up + * any associated state. + * + * @param cls closure (set from GNUNET_MESH_connect) + * @param tunnel connection to the other end (henceforth invalid) + * @param tunnel_ctx place where local state associated + * with the tunnel is stored (our 'struct TunnelState') + */ +static void +tunnel_destruction_handler (void *cls, + const struct GNUNET_MESH_Tunnel *tunnel, + void *tunnel_ctx) +{ + struct ServiceSession * service_session = tunnel_ctx; + struct ServiceSession * client_session; + struct ServiceSession * curr; + + GNUNET_assert (service_session); + if (!memcmp (&service_session->peer, &me, sizeof (struct GNUNET_PeerIdentity))) + return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Tunnel destroyed, terminating session with peer (%s)\n"), GNUNET_i2s (&service_session->peer)); + // remove the session, unless it has already been dequeued, but somehow still active + // this could bug without the IF in case the queue is empty and the service session was the only one know to the service + for (curr = from_service_head; NULL != curr; curr = curr->next) + if (curr == service_session) + { + GNUNET_CONTAINER_DLL_remove (from_service_head, from_service_tail, curr); + break; + } + // there is a client waiting for this service session, terminate it, too! + // i assume the tupel of key and element count is unique. if it was not the rest of the code would not work either. + client_session = find_matching_session (from_client_tail, + &service_session->key, + service_session->element_count, + NULL, NULL); + free_session (service_session); + + // the client has to check if it was waiting for a result + // or if it was a responder, no point in adding more statefulness + if (client_session && ( ! do_shutdown)) + { + // remove the session, we just found it in the queue, so it must be there + GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, client_session); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &prepare_client_end_notification, + client_session); + } +} + + +/** + * Compute our scalar product, done by Alice + * + * @param session - the session associated with this computation + * @param kp - (1) from the protocol definition: + * $E_A(a_{pi(i)}) times E_A(- r_{pi(i)} - b_{pi(i)}) &= E_A(a_{pi(i)} - r_{pi(i)} - b_{pi(i)})$ + * @param kq - (2) from the protocol definition: + * $E_A(a_{pi'(i)}) times E_A(- r_{pi'(i)}) &= E_A(a_{pi'(i)} - r_{pi'(i)})$ + * @param s - S from the protocol definition: + * $S := E_A(sum (r_i + b_i)^2)$ + * @param stick - S' from the protocol definition: + * $S' := E_A(sum r_i^2)$ + * @return product as MPI, never NULL + */ +static gcry_mpi_t +compute_scalar_product (struct ServiceSession * session, + gcry_mpi_t * kp, gcry_mpi_t * kq, gcry_mpi_t s, gcry_mpi_t stick) +{ + uint16_t count; + gcry_mpi_t divider; + gcry_mpi_t t; + gcry_mpi_t u; + gcry_mpi_t utick; + gcry_mpi_t p; + gcry_mpi_t ptick; + gcry_mpi_t product; + gcry_mpi_t tmp; + unsigned int i; + + count = session->used_element_count; + tmp = gcry_mpi_new (KEYBITS); + for (i = 0; i < count; i++) + { + decrypt_element (kp[i], kp[i], my_mu, my_lambda, my_n, my_nsquare); + decrypt_element (kq[i], kq[i], my_mu, my_lambda, my_n, my_nsquare); + } + + // calculate t = E(sum(ai)) + t = compute_square_sum (session->a, count); + encrypt_element (t, t, NULL, my_g, my_n, my_nsquare); + + // calculate U + u = gcry_mpi_new (0); + tmp = compute_square_sum (kp, count); + gcry_mpi_sub (u, u, tmp); + encrypt_element (u, u, NULL, my_g, my_n, my_nsquare); + gcry_mpi_release (tmp); + + //calculate U' + utick = gcry_mpi_new (0); + tmp = compute_square_sum (kq, count); + gcry_mpi_sub (utick, utick, tmp); + encrypt_element (utick, utick, NULL, my_g, my_n, my_nsquare); + gcry_mpi_release (tmp); + + GNUNET_assert (p = gcry_mpi_new (0)); + GNUNET_assert (ptick = gcry_mpi_new (0)); + + // compute P + gcry_mpi_add (p, s, t); + //gcry_mpi_mulm (p, p, u, my_nsquare); + gcry_mpi_add (p, p, u); + decrypt_element (p, p, my_mu, my_lambda, my_n, my_nsquare); + + // compute P' + gcry_mpi_add (ptick, stick, t); + //gcry_mpi_mulm (ptick, ptick, utick, my_nsquare); + gcry_mpi_add (ptick, ptick, utick); + decrypt_element (ptick, ptick, my_mu, my_lambda, my_n, my_nsquare); + + gcry_mpi_release (t); + gcry_mpi_release (u); + gcry_mpi_release (utick); + + // compute product + GNUNET_assert (product = gcry_mpi_new (0)); + gcry_mpi_sub (product, p, ptick); + gcry_mpi_release (p); + gcry_mpi_release (ptick); + divider = gcry_mpi_set_ui (NULL, 2); + gcry_mpi_div (product, NULL, product, divider, 0); + + gcry_mpi_release (divider); + for (i = 0; i < count; i++) + gcry_mpi_release (session->a[i]); + GNUNET_free (session->a); + session->a = NULL; + + return product; +} + + +/** + * prepare the response we will send to alice or bobs' clients. + * in Bobs case the product will be NULL. + * + * @param session the session associated with our client. + */ +static void +prepare_client_response (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ServiceSession * session = cls; + struct GNUNET_VECTORPRODUCT_client_response * msg; + unsigned char * product_exported = NULL; + size_t product_length = 0; + uint16_t msg_length = 0; + struct MessageObject * msg_obj; + + GNUNET_assert (session); + + if (session->product) + { + // get representation as string + GNUNET_assert ( ! gcry_mpi_aprint (GCRYMPI_FMT_USG, + &product_exported, + &product_length, + session->product)); + gcry_mpi_release (session->product); + session->product = NULL; + } + + msg_length = sizeof (struct GNUNET_VECTORPRODUCT_client_response) +product_length; + msg = GNUNET_malloc (msg_length); + memcpy (&msg[1], product_exported, product_length); + GNUNET_free_non_null (product_exported); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_SERVICE_TO_CLIENT); + msg->header.size = htons (msg_length); + memcpy (&msg->key, &session->key, sizeof (struct GNUNET_HashCode)); + memcpy (&msg->peer, &session->peer, sizeof ( struct GNUNET_PeerIdentity)); + msg->product_length = htonl (product_length); + + msg_obj = GNUNET_malloc (sizeof (struct MessageObject)); + msg_obj->msg = (struct GNUNET_MessageHeader *) msg; + msg_obj->transmit_handle = NULL; // don't reset the transmit handle + + //transmit this message to our client + session->client_transmit_handle = + GNUNET_SERVER_notify_transmit_ready (session->client, + msg_length, + GNUNET_TIME_UNIT_FOREVER_REL, + &do_send_message, + msg_obj); + if ( ! session->client_transmit_handle) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not send message to client (%p)! This probably is OK if the client disconnected before us.\n"), session->client); + session->client = NULL; + // callback was not called! + GNUNET_free (msg_obj); + GNUNET_free (msg); + } + else + // gracefully sent message, just terminate session structure + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Sent result to client (%p), this session (%s) has ended!\n"), session->client, GNUNET_h2s (&session->key)); + free_session (session); +} + + +/** + * Handle a request from another service to calculate a vectorproduct with us. + * + * @param cls closure (set from GNUNET_MESH_connect) + * @param tunnel connection to the other end + * @param tunnel_ctx place to store local state associated with the tunnel + * @param sender who sent the message + * @param message the actual message + * @param atsi performance data for the connection + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_service_request (void *cls, + struct GNUNET_MESH_Tunnel * tunnel, + void **tunnel_ctx, + const struct GNUNET_PeerIdentity * sender, + const struct GNUNET_MessageHeader * message, + const struct GNUNET_ATS_Information * atsi) +{ + struct ServiceSession * session; + struct GNUNET_VECTORPRODUCT_service_request * msg = (struct GNUNET_VECTORPRODUCT_service_request *) message; + uint16_t mask_length; + uint16_t pk_length; + uint16_t used_elements; + uint16_t element_count; + uint16_t msg_length; + unsigned char * current; + struct ServiceSession * responder_session; + int32_t i = -1; + enum SessionState needed_state; + + GNUNET_assert (NULL != message); + GNUNET_assert (NULL != sender); + GNUNET_assert (NULL != tunnel_ctx); + session = (struct ServiceSession *) * tunnel_ctx; + // is this tunnel already in use? + if ( (session->next) || (from_service_head == session)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Got a service request over a tunnel that is already in use, ignoring!\n")); + return GNUNET_SYSERR; + } + // Check if message was sent by me, which would be bad! + if ( ! memcmp (sender, &me, sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_break (0); + GNUNET_free (session); + return GNUNET_SYSERR; + } + // this protocol can at best be 1:N, but never M:N! + // Check if the sender is not the peer, I am connected to, which would be bad! + if (memcmp (sender, &session->peer, sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_break (0); + GNUNET_free (session); + return GNUNET_SYSERR; + } + + //we need at least a peer and one message id to compare + if (ntohs (msg->header.size) < sizeof (struct GNUNET_VECTORPRODUCT_service_request)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Too short message received from peer!\n")); + GNUNET_free (session); + return GNUNET_SYSERR; + } + mask_length = ntohs (msg->mask_length); + pk_length = ntohs (msg->pk_length); + used_elements = ntohs (msg->used_element_count); + element_count = ntohs (msg->element_count); + msg_length = sizeof (struct GNUNET_VECTORPRODUCT_service_request) + + mask_length + pk_length + used_elements * PAILLIER_ELEMENT_LENGTH; + + //sanity check: is the message as long as the message_count fields suggests? + if ((ntohs (msg->header.size) != msg_length) || (element_count < used_elements) + || (used_elements == 0) || (mask_length != (element_count / 8 + (element_count % 8 ? 1 : 0))) + ) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Invalid message received from peer, message count does not match message length!\n")); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Used elements: %hu\nElement Count: %hu\nExpected Mask Length: %hu\nCalculated Masklength: %d\n"), used_elements, element_count, mask_length, (element_count / 8 + (element_count % 8 ? 1 : 0))); + GNUNET_free (session); + return GNUNET_SYSERR; + } + if (find_matching_session (from_service_tail, + &msg->key, + element_count, + NULL, + sender)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Got message with duplicate session key (`%s'), ignoring service request.\n"), (const char *) &(msg->key)); + GNUNET_free (session); + return GNUNET_SYSERR; + } + + memcpy (&session->peer, sender, sizeof (struct GNUNET_PeerIdentity)); + session->state = REQUEST_FROM_SERVICE_RECEIVED; + session->element_count = ntohs (msg->element_count); + session->used_element_count = used_elements; + session->tunnel = tunnel; + + // session key + memcpy (&session->key, &msg->key, sizeof (struct GNUNET_HashCode)); + current = (unsigned char *) &msg[1]; + //preserve the mask, we will need that later on + session->mask = GNUNET_malloc (mask_length); + memcpy (session->mask, current, mask_length); + //the public key + current += mask_length; + + //convert the publickey to sexp + if (gcry_sexp_new (&session->remote_pubkey, current, pk_length, 1)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not translate remote public key to sexpression!\n")); + GNUNET_free (session->mask); + GNUNET_free (session); + return GNUNET_SYSERR; + } + + current += pk_length; + + //check if service queue contains a matching request + needed_state = MESSAGE_FROM_RESPONDING_CLIENT_RECEIVED; + responder_session = find_matching_session (from_client_tail, + &session->key, + session->element_count, + &needed_state, NULL); + + session->a = GNUNET_malloc (sizeof (gcry_mpi_t) * used_elements); + + if (GNUNET_SERVER_MAX_MESSAGE_SIZE >= sizeof (struct GNUNET_VECTORPRODUCT_service_request) + +pk_length + + mask_length + + used_elements * PAILLIER_ELEMENT_LENGTH) + { + gcry_error_t ret = 0; + session->a = GNUNET_malloc (sizeof (gcry_mpi_t) * used_elements); + // Convert each vector element to MPI_value + for (i = 0; i < used_elements; i++) + { + size_t read = 0; + + ret = gcry_mpi_scan (&session->a[i], + GCRYMPI_FMT_USG, + &current[i * PAILLIER_ELEMENT_LENGTH], + PAILLIER_ELEMENT_LENGTH, + &read); + if (ret) // read < GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not translate E[a%d] to MPI!\n%s/%s\n"), + i, gcry_strsource (ret), gcry_strerror (ret)); + goto except; + } + } + GNUNET_CONTAINER_DLL_insert (from_service_head, from_service_tail, session); + if (responder_session) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Got session with key %s and a matching element set, processing.\n"), GNUNET_h2s (&session->key)); + if (GNUNET_OK != compute_service_response (session, responder_session)) + { + //something went wrong, remove it again... + GNUNET_CONTAINER_DLL_remove (from_service_head, from_service_tail, session); + goto except; + } + } + else + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Got session with key %s without a matching element set, queueing.\n"), GNUNET_h2s (&session->key)); + return GNUNET_OK; + } + else + { + // TODO FEATURE: fallback to fragmentation, in case the message is too long + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Message too large, fragmentation is currently not supported!\n")); + goto except; + } +except: + for (i = 0; i < used_elements; i++) + if (session->a[i]) + gcry_mpi_release (session->a[i]); + gcry_sexp_release (session->remote_pubkey); + session->remote_pubkey = NULL; + GNUNET_free_non_null (session->a); + session->a = NULL; + free_session (session); + // and notify our client-session that we could not complete the session + if (responder_session) + { + // we just found the responder session in this queue + GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, responder_session); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &prepare_client_end_notification, + responder_session); + } + return GNUNET_SYSERR; +} + + +/** + * Handle a response we got from another service we wanted to calculate a vectorproduct with. + * + * @param cls closure (set from GNUNET_MESH_connect) + * @param tunnel connection to the other end + * @param tunnel_ctx place to store local state associated with the tunnel + * @param sender who sent the message + * @param message the actual message + * @param atsi performance data for the connection + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_service_response (void *cls, + struct GNUNET_MESH_Tunnel * tunnel, + void **tunnel_ctx, + const struct GNUNET_PeerIdentity * sender, + const struct GNUNET_MessageHeader * message, + const struct GNUNET_ATS_Information * atsi) +{ + + struct ServiceSession * session; + struct GNUNET_VECTORPRODUCT_service_response * msg = (struct GNUNET_VECTORPRODUCT_service_response *) message; + unsigned char * current; + uint16_t count; + gcry_mpi_t s = NULL; + gcry_mpi_t stick = NULL; + size_t read; + size_t i; + uint16_t used_element_count; + size_t msg_size; + gcry_mpi_t * kp = NULL; + gcry_mpi_t * kq = NULL; + + GNUNET_assert (NULL != message); + GNUNET_assert (NULL != sender); + GNUNET_assert (NULL != tunnel_ctx); + session = (struct ServiceSession *) * tunnel_ctx; + GNUNET_assert (NULL != session); + count = session->used_element_count; + session->product = NULL; + + if (memcmp (&session->peer, sender, sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_break_op (0); + goto invalid_msg; + } + //we need at least a peer and one message id to compare + if (sizeof (struct GNUNET_VECTORPRODUCT_service_response) > ntohs (msg->header.size)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Too short message received from peer!\n")); + goto invalid_msg; + } + used_element_count = ntohs (msg->used_element_count); + msg_size = sizeof (struct GNUNET_VECTORPRODUCT_service_response) + + 2 * used_element_count * PAILLIER_ELEMENT_LENGTH + + 2 * PAILLIER_ELEMENT_LENGTH; + //sanity check: is the message as long as the message_count fields suggests? + if ((ntohs (msg->header.size) != msg_size) || (count != used_element_count)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Invalid message received from peer!\n")); + goto invalid_msg; + } + if (GNUNET_SERVER_MAX_MESSAGE_SIZE < msg_size) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Message too large, fragmentation is currently not supported!\n")); + goto invalid_msg; + } + + //convert s + current = (unsigned char *) &msg[1]; + if (gcry_mpi_scan (&s, GCRYMPI_FMT_USG, current, + PAILLIER_ELEMENT_LENGTH, &read)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not translate s to an MPI value!\n")); + goto invalid_msg; + } + current += PAILLIER_ELEMENT_LENGTH; + //convert stick + if (gcry_mpi_scan (&stick, GCRYMPI_FMT_USG, current, + PAILLIER_ELEMENT_LENGTH, &read)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not translate s' to an MPI value!\n")); + goto invalid_msg; + } + current += PAILLIER_ELEMENT_LENGTH; + + kp = GNUNET_malloc (sizeof (gcry_mpi_t) * count); + // Convert each kp[] to its MPI_value + for (i = 0; i < count; i++) + { + if (gcry_mpi_scan (&kp[i], GCRYMPI_FMT_USG, current, + PAILLIER_ELEMENT_LENGTH, &read)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not translate Kp[%d]to an MPI value!\n"), i); + goto invalid_msg; + } + current += PAILLIER_ELEMENT_LENGTH; + } + + + kq = GNUNET_malloc (sizeof (gcry_mpi_t) * count); + // Convert each kq[] to its MPI_value + for (i = 0; i < count; i++) + { + if (gcry_mpi_scan (&kq[i], GCRYMPI_FMT_USG, current, + PAILLIER_ELEMENT_LENGTH, &read)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Could not translate Kq[%d]to an MPI value!\n"), i); + goto invalid_msg; + } + current += PAILLIER_ELEMENT_LENGTH; + } + + session->product = compute_scalar_product (session, kp, kq, s, stick); + +invalid_msg: + if (s) + gcry_mpi_release (s); + if (stick) + gcry_mpi_release (stick); + for (i = 0; kp && i < count; i++) + if (kp[i]) gcry_mpi_release (kp[i]); + for (i = 0; kq && i < count; i++) + if (kq[i]) gcry_mpi_release (kq[i]); + GNUNET_free_non_null (kp); + GNUNET_free_non_null (kq); + + session->state = FINALIZED; + // the tunnel has done its job, terminate our connection and the tunnel + // the peer will be notified that the tunnel was destroyed via tunnel_destruction_handler + GNUNET_CONTAINER_DLL_remove (from_client_head, from_client_tail, session); + GNUNET_SCHEDULER_add_now (&destroy_tunnel, session); + // send message with product to client + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &prepare_client_response, session); + return GNUNET_OK; + // if success: terminate the session gracefully, else terminate with error +} + + +/** + * Task run during shutdown. + * + * @param cls unused + * @param tc unused + */ +static void +shutdown_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ServiceSession * curr; + struct ServiceSession * next; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Shutting down, initiating cleanup.\n")); + + do_shutdown = GNUNET_YES; + // terminate all owned open tunnels. + for (curr = from_client_head; NULL != curr; curr = next) + { + next = curr->next; + if (FINALIZED != curr->state) + { + destroy_tunnel (curr, NULL); + curr->state = FINALIZED; + } + } + + if (my_core) + { + GNUNET_CORE_disconnect (my_core); + my_core = NULL; + } + + if (my_mesh) + { + GNUNET_MESH_disconnect (my_mesh); + my_mesh = NULL; + } +} + + +/** + * To be called on core init/fail. + * + * @param cls closure, NULL + * @param server handle to the server for this service + * @param my_identity the public identity of this peer + */ +static void +core_init (void *cls, struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *my_identity) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Core initialized\n")); + me = *my_identity; +} + + +/** + * Initialization of the program and message handlers + * + * @param cls closure + * @param server the initialized server + * @param c configuration to use + */ +static void +run (void *cls, + struct GNUNET_SERVER_Handle *server, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + static const struct GNUNET_SERVER_MessageHandler server_handlers[] = { + {&handle_client_request, NULL, GNUNET_MESSAGE_TYPE_VECTORPRODUCT_CLIENT_TO_ALICE, 0}, + {&handle_client_request, NULL, GNUNET_MESSAGE_TYPE_VECTORPRODUCT_CLIENT_TO_BOB, 0}, + {NULL, NULL, 0, 0} + }; + static const struct GNUNET_MESH_MessageHandler mesh_handlers[] = { + { &handle_service_request, GNUNET_MESSAGE_TYPE_VECTORPRODUCT_ALICE_TO_BOB, 0}, + { &handle_service_response, GNUNET_MESSAGE_TYPE_VECTORPRODUCT_BOB_TO_ALICE, 0}, + {NULL, 0, 0} + }; + static const struct GNUNET_CORE_MessageHandler core_handlers[] = { + {NULL, 0, 0} + }; + static GNUNET_MESH_ApplicationType mesh_types[] = { + GNUNET_APPLICATION_TYPE_VECTORPRODUCT, + GNUNET_APPLICATION_TYPE_END + }; + + //generate private/public key set + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Generating rsa-key.\n")); + generate_keyset (); + // register server callbacks and disconnect handler + GNUNET_SERVER_add_handlers (server, server_handlers); + GNUNET_SERVER_disconnect_notify (server, + &handle_client_disconnect, + NULL); + + my_core = GNUNET_CORE_connect (c, NULL, &core_init, NULL, NULL, NULL, + GNUNET_NO, NULL, GNUNET_NO, core_handlers); + if (!my_core) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Connect to CORE failed\n")); + return; + } + my_mesh = GNUNET_MESH_connect (c, NULL, + &tunnel_incoming_handler, + &tunnel_destruction_handler, + mesh_handlers, mesh_types); + if (!my_mesh) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Connect to MESH failed\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Mesh initialized\n")); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &shutdown_task, + NULL); +} + + +/** + * The main function for the vectorproduct service. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + return (GNUNET_OK == + GNUNET_SERVICE_run (argc, argv, + "vectorproduct", + GNUNET_SERVICE_OPTION_NONE, + &run, NULL)) ? 0 : 1; +} + +/* end of gnunet-service-ext.c */ diff --git a/src/scalarproduct/gnunet-vectorproduct.c b/src/scalarproduct/gnunet-vectorproduct.c @@ -0,0 +1,410 @@ +/* + This file is part of GNUnet. + (C) 2013 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file vectorproduct/gnunet-vectorproduct.c + * @brief vectorproduct client + * @author Christian M. Fuchs + */ +#define GCRYPT_NO_DEPRECATED +#include <gcrypt.h> +#include <inttypes.h> + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_vectorproduct_service.h" +#include "gnunet_protocols.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "gnunet-vectorproduct",__VA_ARGS__) +/** + * Option -p: destination peer identity for checking message-ids with + */ +static char *input_peer_id = NULL; + +/** + * Option -p: destination peer identity for checking message-ids with + */ +static char *input_key = NULL; + +/** + * Option -e: vector to calculate a vectorproduct with + */ +static char *input_elements = NULL; + +/** + * Option -m: message-ids to calculate a vectorproduct with + */ +static char *input_mask = NULL; + +/** + * the count of the messages sent to the service for processing + */ +static unsigned short element_count; + +/** + * the count of the mask bytes + */ +unsigned short mask_length = 0; + +/** + * the count of the number of mask bytes + */ +unsigned short mask_bytes; + +/** + * the array of converted message IDs to send to our service + */ +static int32_t * elements = NULL; + +/** + * the array of converted message IDs to send to our service + */ +static unsigned char * mask = NULL; + +/** + * information about the peer we are comparing with + */ +struct GNUNET_PeerIdentity peer; + +/** + * information about the peer we are comparing with + */ +struct GNUNET_HashCode key; + +/** + * Pointer to the GNUNET_VECTORPRODUCT_Handle + */ +struct GNUNET_VECTORPRODUCT_Handle *handle; + +/** + * Global return value + */ +static int ret; + +struct GNUNET_VECTORPRODUCT_TestCls +{ + struct GNUNET_VECTORPRODUCT_Handle * h; +}; + +struct GNUNET_VECTORPRODUCT_TestCls test_cls; + + +/** + * Callback called if we are initiating a new computation session + * + * @param cls unused + * @param status if our job was successfully processed + */ +static void +responder_callback (void *cls, + const struct GNUNET_HashCode * key, + enum GNUNET_VECTORPRODUCT_ResponseStatus status) +{ + ret = -1; + + switch (status) + { + case GNUNET_VECTORPRODUCT_Status_Success: + ret = 0; + LOG (GNUNET_ERROR_TYPE_INFO, "Session %s concluded.\n", GNUNET_h2s (key)); + break; + case GNUNET_VECTORPRODUCT_Status_InvalidResponse: + LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: invalid response\n", GNUNET_h2s (key)); + break; + case GNUNET_VECTORPRODUCT_Status_Timeout: + LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: timeout\n", GNUNET_h2s (key)); + break; + case GNUNET_VECTORPRODUCT_Status_Failure: + LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service failure\n", GNUNET_h2s (key)); + case GNUNET_VECTORPRODUCT_Status_ServiceDisconnected: + LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service disconnect!!\n", GNUNET_h2s (key)); + break; + default: + LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: return code %d\n", GNUNET_h2s (key), (int) status); + } + + GNUNET_VECTORPRODUCT_disconnect (handle); + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Callback called if we are initiating a new computation session + * + * @param cls unused + * @param key unused + * @param peer unused + * @param status if our job was successfully processed + * @param size size of the msg returned + * @param msg the response we got. + * @param type of the message received + */ +static void +requester_callback (void *cls, + const struct GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity * peer, + enum GNUNET_VECTORPRODUCT_ResponseStatus status, + const struct GNUNET_VECTORPRODUCT_client_response *msg) +{ + uint32_t product_len; + ret = -1; + + switch (status) + { + case GNUNET_VECTORPRODUCT_Status_Success: + product_len = ntohl (msg->product_length); + + LOG (GNUNET_ERROR_TYPE_INFO, "Session %s concluded.\n", GNUNET_h2s (key)); + + if (0 < product_len && NULL != &msg[1]) + { + gcry_mpi_t result; + size_t read = 0; + + if (0 != gcry_mpi_scan (&result, GCRYMPI_FMT_USG, &msg[1], product_len, &read)) + LOG (GNUNET_ERROR_TYPE_ERROR, "Could not convert to mpi to value!\n"); + else + { + unsigned char * buf; + gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, result); + + printf ("Successfully computed result for session %s: %s\n", GNUNET_h2s (key), buf); + ret = 0; + } + } + else + { //currently not used, but if we get more info due to MESH we will need this + LOG (GNUNET_ERROR_TYPE_ERROR, "Service-side error in session %s, return code: %d\n", GNUNET_h2s (key), product_len); + } + break; + case GNUNET_VECTORPRODUCT_Status_InvalidResponse: + LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: invalid response\n", GNUNET_h2s (key)); + break; + case GNUNET_VECTORPRODUCT_Status_Timeout: + LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: timeout\n", GNUNET_h2s (key)); + break; + case GNUNET_VECTORPRODUCT_Status_Failure: + LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: service failure\n", GNUNET_h2s (key)); + case GNUNET_VECTORPRODUCT_Status_ServiceDisconnected: + LOG (GNUNET_ERROR_TYPE_ERROR, "Disconnected from service.\n", GNUNET_h2s (key)); + break; + default: + LOG (GNUNET_ERROR_TYPE_ERROR, "Session %s failed: return code %d\n", GNUNET_h2s (key), (int) status); + } + GNUNET_VECTORPRODUCT_disconnect (handle); + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char * begin = input_elements; + char * end; + int32_t element; + int i; + ret = -1; + + if (NULL == input_elements) + { + FPRINTF (stderr, "%s", _ ("You must specify at least one message ID to check!\n")); + return; + } + + if (NULL == input_key) + { + FPRINTF (stderr, "%s", _ ("This program needs a session identifier for comparing vectors.\n")); + return; + } + + if (1 > strnlen (input_key, sizeof (struct GNUNET_HashCode))) + { + FPRINTF (stderr, _ ("Please give a session key for --input_key!\n")); + return; + } + GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key); + + if (input_peer_id && GNUNET_OK != GNUNET_CRYPTO_hash_from_string (input_peer_id, + (struct GNUNET_HashCode *) &peer)) + { + FPRINTF (stderr, _ ("Tried to set initiator mode, as peer ID was given. " + "However, `%s' is not a valid peer identifier.\n"), + input_peer_id); + return; + } + + int exit_loop = 0; + /* Read input_elements_peer1, and put in elements_peer1 array */ + do + { + unsigned int mcount = element_count; + //ignore empty rows of ,,,,,, + while (*begin == ',') + begin++; + // get the length of the current element and replace , with null + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (*end == ',') + *end = '\0'; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + return; + } + + GNUNET_array_append (elements, mcount, element); + element_count++; + + begin = ++end; + } + while (!exit_loop); + + GNUNET_assert (elements != NULL); + GNUNET_assert (element_count > 1); + mask_length = element_count / 8 + (element_count % 8 ? 1 : 0); + mask = GNUNET_malloc ((element_count / 8) + 2); + + /* Read input_mask_peer1 and read in mask_peer1 array */ + if (NULL != input_mask) + { + begin = input_mask; + unsigned short mask_count = 0; + int exit_loop = 0; + + do + { + //ignore empty rows of ,,,,,, + while (* begin == ',') + begin++; + // get the length of the current element and replace , with null + // gnunet_ascii-armor uses base32, thus we can use , as separator! + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (*end == ',') + *end = '\0'; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + return; + } + + GNUNET_assert (mask_count <= element_count); + + if (element) + mask[mask_count / 8] = mask[mask_count / 8] | 1 << (mask_count % 8); + + mask_count++; + begin = ++end; + } + while (!exit_loop); + // +1 to see if we would have more data, which would indicate malformed/superficial input + GNUNET_assert (mask_count == element_count); + } + else if (input_peer_id) + { + for (i = 0; i <= mask_length; i++) + mask[i] = UCHAR_MAX; // all 1's + } + + handle = GNUNET_VECTORPRODUCT_connect (cfg); + if (handle == NULL) + { + FPRINTF (stderr, _ ("Could not connect to the GNUNET Vector Product Service\n")); + return; + } + + test_cls.h = handle; + + if (input_peer_id && !GNUNET_VECTORPRODUCT_request (handle, + &key, + &peer, + element_count, + mask_length, + elements, mask, + GNUNET_TIME_UNIT_MINUTES, + &requester_callback, + (void *) &test_cls)) + return; + if ( !input_peer_id && !GNUNET_VECTORPRODUCT_prepare_response (handle, + &key, + element_count, + elements, + GNUNET_TIME_UNIT_MINUTES, + &responder_callback, + (void *) &test_cls)) + return; + + ret = 0; +} + + +/** + * The main function to the vectorproduct client. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + {'e', "elements", "\"val1,val2,...,valn\"", + gettext_noop ("A comma separated list of elements to compare as vector with our remote peer."), + 1, &GNUNET_GETOPT_set_string, &input_elements}, + {'m', "mask", "\"0,1,...,maskn\"", + gettext_noop ("A comma separated mask to select which elements should actually be compared."), + 1, &GNUNET_GETOPT_set_string, &input_mask}, + {'p', "peer", "PEERID", + gettext_noop ("[Optional] peer to calculate our vectorproduct with. If this parameter is not given, the service will wait for a remote peer to compute the request."), + 1, &GNUNET_GETOPT_set_string, &input_peer_id}, + {'k', "key", "TRANSACTION_ID", + gettext_noop ("Transaction ID shared with peer."), + 1, &GNUNET_GETOPT_set_string, &input_key}, + GNUNET_GETOPT_OPTION_END + }; + + return (GNUNET_OK == + GNUNET_PROGRAM_run (argc, + argv, + "gnunet-vectorproduct", + gettext_noop ("Calculate the Vectorproduct with a GNUnet peer."), + options, &run, NULL)) ? ret : 1; +} + diff --git a/src/scalarproduct/gnunet_vectorproduct.h b/src/scalarproduct/gnunet_vectorproduct.h @@ -0,0 +1,274 @@ +/* + This file is part of GNUnet. + (C) 2013 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file include/gnunet_vectorproduct.h + * @brief API to the vectorproduct service + * @author Christian M. Fuchs + */ + +#ifndef GNUNET_VECTORPRODUCT_H +#define GNUNET_VECTORPRODUCT_H + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// +#define DISABLE_CRYPTO + +/** + * Length of the key used for encryption + */ +#define KEYBITS 2048 + +/** + * When performing our crypto, we may add two encrypted values with each + * a maximal length of GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH. + * thus we can receive a slightly longer element (+1 byte) + */ +#define PAILLIER_ELEMENT_LENGTH (2*KEYBITS/8 +1) + +#ifdef __cplusplus +extern "C" +{ +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Service Structure Definitions +/////////////////////////////////////////////////////////////////////////////// + +/** + * Message type passed from requesting service Alice to responding service Bob + * to initiate a request and make bob participate in our protocol + */ +struct GNUNET_VECTORPRODUCT_service_request { + /** + * GNUNET message header + */ + struct GNUNET_MessageHeader header; + + /** + * how many bytes the mask has + */ + uint16_t mask_length GNUNET_PACKED; + + /** + * the length of the publickey contained within this message + */ + uint16_t pk_length GNUNET_PACKED; + + /** + * the transaction/session key used to identify a session + */ + struct GNUNET_HashCode key; + + /** + * how many elements the vector in payload contains + */ + uint16_t element_count GNUNET_PACKED; + + /** + * how many elements are actually included after the mask was applied. + */ + uint16_t used_element_count GNUNET_PACKED; + + /** + * followed by mask | public_key | vector[used_element_count] + */ +}; + +/** + * Message type passed from responding service Bob to responding service Alice + * to complete a request and allow Alice to compute the result + */ +struct GNUNET_VECTORPRODUCT_service_response { + /** + * GNUNET message header + */ + struct GNUNET_MessageHeader header; + + /** + * how many elements the vector in payload contains + */ + uint16_t element_count GNUNET_PACKED; + + /** + * how many elements are actually included after the mask was applied. + */ + uint16_t used_element_count GNUNET_PACKED; + + /** + * the transaction/session key used to identify a session + */ + struct GNUNET_HashCode key; + + /** + * followed by s | s' | kp[] | kq[] + */ +}; + +/////////////////////////////////////////////////////////////////////////////// +// Service Structure Definitions +/////////////////////////////////////////////////////////////////////////////// + +/** + * state a session can be in + */ +enum SessionState +{ + WAITING_FOR_BOBS_CONNECT, + MESSAGE_FROM_RESPONDING_CLIENT_RECEIVED, + WAITING_FOR_RESPONSE_FROM_SERVICE, + REQUEST_FROM_SERVICE_RECEIVED, + FINALIZED +}; + +/** + * role a peer in a session can assume + */ +enum PeerRole +{ + ALICE, + BOB +}; +/** + * A vectorproduct session which tracks: + * + * a request form the client to our final response. + * or + * a request from a service to us(service). + */ +struct ServiceSession +{ + /** + * the role this peer has + */ + enum PeerRole role; + + /** + * session information is kept in a DLL + */ + struct ServiceSession *next; + + /** + * session information is kept in a DLL + */ + struct ServiceSession *prev; + + /** + * (hopefully) unique transaction ID + */ + struct GNUNET_HashCode key; + + /** + * state of the session + */ + enum SessionState state; + + /** + * Alice or Bob's peerID + */ + struct GNUNET_PeerIdentity peer; + + /** + * the client this request is related to + */ + struct GNUNET_SERVER_Client * client; + + /** + * how many elements we were supplied with from the client + */ + uint16_t element_count; + + /** + * how many elements actually are used after applying the mask + */ + uint16_t used_element_count; + + /** + * how many bytes the mask is long. + * just for convenience so we don't have to re-re-re calculate it each time + */ + uint16_t mask_length; + + /** + * all the vector elements we received + */ + int32_t * vector; + + /** + * mask of which elements to check + */ + unsigned char * mask; + + /** + * Public key of the remote service, only used by bob + */ + gcry_sexp_t remote_pubkey; + + /** + * E(ai)(Bob) or ai(Alice) after applying the mask + */ + gcry_mpi_t * a; + + /** + * The computed scalar + */ + gcry_mpi_t product; + + /** + * My transmit handle for the current message to a alice/bob + */ + struct GNUNET_MESH_TransmitHandle * service_transmit_handle; + + /** + * My transmit handle for the current message to the client + */ + struct GNUNET_SERVER_TransmitHandle * client_transmit_handle; + + /** + * tunnel-handle associated with our mesh handle + */ + struct GNUNET_MESH_Tunnel * tunnel; + +}; + +/** + * We need to do a minimum of bookkeeping to maintain track of our transmit handles. + * each msg is associated with a session and handle. using this information we can determine which msg was sent. + */ +struct MessageObject +{ + /** + * The handle used to transmit with this request + */ + void ** transmit_handle; + + /** + * The message to send + */ + struct GNUNET_MessageHeader * msg; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* GNUNET_VECTORPRODUCT_H */ + diff --git a/src/scalarproduct/test_vectorproduct_api.c b/src/scalarproduct/test_vectorproduct_api.c @@ -0,0 +1,865 @@ +/* + This file is part of GNUnet. + (C) 2013 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * Aim of test_vectorproduct_api : This test creates two peers. Peer1 is the + * responder peer, Bob and Peer2 is the initiator peer, Alice. Both peers + * connect to VectorProduct Service, and use the API to issue requests to + * service. Test passes, when the expected scalar product is received from the + * service. + */ + +/** + * @file vectorproduct/testbed_vectorproduct_api.c + * @brief VectorProduct API testing between 4 peers using testing API + * @author Gaurav Kukreja + * @author Christian Fuchs + */ + +#include <string.h> + +#include <inttypes.h> +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testbed_service.h" +#include "gnunet_common.h" +#include "gnunet_vectorproduct_service.h" +#include "gnunet_protocols.h" + +#define NUM_PEERS 2 + +#define LOG(kind,...) GNUNET_log_from (kind, "test-vectorproduct-api",__VA_ARGS__) + +/** + * Structure for holding peer's sockets and IO Handles + */ +struct PeerData +{ + /** + * Handle to testbed peer + */ + struct GNUNET_TESTBED_Peer *peer; + + /** + * The service connect operation to stream + */ + struct GNUNET_TESTBED_Operation *op; + + /** + * Our Peer id + */ + struct GNUNET_PeerIdentity our_id; + + /** + * Pointer to Vector Product Handle + */ + struct GNUNET_VECTORPRODUCT_Handle *vh; +}; + +/** + * Different states in test setup + */ +enum SetupState +{ + /** + * Get the identity of peer 1 + */ + PEER1_GET_IDENTITY, + + /** + * Get the identity of peer 2 + */ + PEER2_GET_IDENTITY, + + /** + * Connect to stream service of peer 1 + */ + PEER1_VECTORPRODUCT_CONNECT, + + /** + * Connect to stream service of peer 2 + */ + PEER2_VECTORPRODUCT_CONNECT + +}; + +/****************************************************************************** + *** Global Variables ***************************** + ******************************************************************************/ + +/** + * Maximum allowed message-ids we can check in one go (with one GNUNET_message) + */ +static unsigned int max_mids; + +/** + * Session Key used by both the test peers + */ +char input_key[103] = "helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhe"; + +/** + * Input elements for peer1 + */ +char input_elements_peer1[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11"; +//char input_elements_peer1[] = "11,11,11"; + +/** + * Input Mask for peer 1 + */ +char input_mask_peer1[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; +//char input_mask_peer1[] = "1,1,1"; + +/** + * the array of converted message IDs to send to our service + */ +static int32_t * elements_peer1 = NULL; + +/** + * Number of elements + */ +uint16_t element_count_peer1 = 0; + +/** + * Input elements for peer2 + */ +char input_elements_peer2[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11"; +//char input_elements_peer2[] = "11,11,11"; + +/** + * Input Mask for peer 2 + */ +char input_mask_peer2[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; +//char input_mask_peer2[] = "1,1,1"; + +/** + * the array of converted message IDs to send to our service + */ +static int32_t * elements_peer2 = NULL; + +/** + * the array of converted message IDs to send to our service + */ +static unsigned char * mask_peer2 = NULL; + +/** + * Number of elements + */ +uint16_t element_count_peer2 = 0; + +/** + * Data context for peer 1 + */ +static struct PeerData peer1; + +/** + * Data context for peer 2 + */ +static struct PeerData peer2; + +/** + * Various states during test setup + */ +static enum SetupState setup_state; + +/** + * Testbed operation handle + */ +static struct GNUNET_TESTBED_Operation *op; + +static int ok; + +static int responder_ok; + +static int requester_ok; + +static GNUNET_SCHEDULER_TaskIdentifier abort_task; +/****************************************************************************** + *** Static Functions ***************************** + ******************************************************************************/ + +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Close sockets and stop testing deamons nicely + */ +static void +do_close (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + + if (peer1.op != NULL) + do_shutdown (&peer1, NULL); + + if (peer2.op != NULL) + do_shutdown (&peer2, NULL); + + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + + GNUNET_SCHEDULER_shutdown (); /* For shutting down testbed */ +} + +/** + * Shutdown a peer + * + * @param cls pointer to "struct PeerData" of the peer to be disconnected + * @param tc Task Context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + static int shutdown; + shutdown++; + struct PeerData* peer = (struct PeerData*) cls; + + if (peer == &peer1) + LOG (GNUNET_ERROR_TYPE_INFO, "Disconnecting Peer1\n\n"); + else if (peer == &peer2) + LOG (GNUNET_ERROR_TYPE_INFO, "Disconnecting Peer2\n\n"); + + // peer->op contains handle to the TESTBED_connect_service operation + // calling operation done, leads to call to vectorproduct_da + if (peer->op != NULL) + { + GNUNET_TESTBED_operation_done (peer->op); + peer->op = NULL; + } + + if (shutdown >= 2) + GNUNET_SCHEDULER_add_now (&do_close, NULL); +} + + +/** + * Something went wrong and timed out. Kill everything and set error flag + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: ABORT due to Timeout\n"); + ok = GNUNET_SYSERR; + abort_task = 0; + do_close (cls, tc); +} + + +/** + * Controller event callback + * + * @param cls NULL + * @param event the controller event + */ +static void +controller_event_cb (void *cls, + const struct GNUNET_TESTBED_EventInformation *event) +{ + GNUNET_assert (event->type == GNUNET_TESTBED_ET_OPERATION_FINISHED); + + switch (setup_state) + { + case PEER1_VECTORPRODUCT_CONNECT: + case PEER2_VECTORPRODUCT_CONNECT: + GNUNET_assert (NULL == event->details.operation_finished.emsg); + break; + default: + GNUNET_assert (0); + } +} + + +static void +responder_callback (void *cls, + const struct GNUNET_HashCode * key, + enum GNUNET_VECTORPRODUCT_ResponseStatus status) +{ + + if (status == GNUNET_VECTORPRODUCT_Status_Failure) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received status failure\n"); + responder_ok = -1; + } + else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received status invalid response\n"); + responder_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Timeout == status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received timeout occured\n"); + responder_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received service disconnected!!\n"); + responder_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Success == status) + { + LOG (GNUNET_ERROR_TYPE_INFO, "Responder Client expected response received!\n"); + responder_ok = 1; + } + else + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client status = %d!\n", (int) status); + responder_ok = -1; + } + // TODO : Responder Session Complete. Shutdown Test Cleanly!!! + //do_shutdown(&peer1, NULL); + GNUNET_SCHEDULER_add_now (&do_shutdown, &peer1); + return; +} + + +static void +requester_callback (void *cls, + const struct GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity * peer, + enum GNUNET_VECTORPRODUCT_ResponseStatus status, + const struct GNUNET_VECTORPRODUCT_client_response *msg) +{ + uint32_t product_len; + + if (status == GNUNET_VECTORPRODUCT_Status_Failure) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client received status failure\n"); + requester_ok = -1; + } + else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client received status invalid response\n"); + requester_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Timeout == status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client timeout occured\n"); + requester_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client service disconnected!!\n"); + requester_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Success != status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client Status = %d\n", (int) status); + requester_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Success == status) + { + LOG (GNUNET_ERROR_TYPE_INFO, "Requester Client expected response received!\n"); + product_len = ntohl(msg->product_length); + + if (0 < product_len) + { + gcry_mpi_t result; + gcry_error_t ret = 0; + size_t read = 0; + ret = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, &msg[1], product_len, &read); + + if (0 != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not convert to mpi to value!\n"); + ok = -1; + } + else + { + uint16_t i = 0; + + // calculate expected product + gcry_mpi_t expected_result; + gcry_mpi_t v1; + gcry_mpi_t v2; + gcry_mpi_t v1_v2_prod; + + expected_result = gcry_mpi_new (0); + + for (i = 0; i < element_count_peer1; i++) + { + uint32_t value; + v1_v2_prod = gcry_mpi_new (0); + + // long to gcry_mpi_t + value = elements_peer1[i] >= 0 ? elements_peer1[i] : -elements_peer1[i]; + if (elements_peer1[i] < 0) + { + v1 = gcry_mpi_new (0); + gcry_mpi_sub_ui (v1, v1, value); + } + else + v1 = gcry_mpi_set_ui (NULL, value); + + // long to gcry_mpi_t + value = elements_peer2[i] >= 0 ? elements_peer2[i] : -elements_peer2[i]; + if (elements_peer2[i] < 0) + { + v2 = gcry_mpi_new (0); + gcry_mpi_sub_ui (v2, v2, value); + } + else + v2 = gcry_mpi_set_ui (NULL, value); + + gcry_mpi_mul (v1_v2_prod, v1, v2); + gcry_mpi_add (expected_result, expected_result, v1_v2_prod); + + gcry_mpi_release (v1); + gcry_mpi_release (v2); + gcry_mpi_release (v1_v2_prod); + + } + + // compare the result + if (!gcry_mpi_cmp (expected_result, result)) + { + LOG (GNUNET_ERROR_TYPE_INFO, "Scalar Product matches expected Result!!\n"); + requester_ok = 1; + } + else + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Scalar Product DOES NOT match expected Result!!\n"); + requester_ok = -1; + } + gcry_mpi_release (result); + gcry_mpi_release (expected_result); + } + } + else + { //currently not used, but if we get more info due to MESH we will need this + LOG (GNUNET_ERROR_TYPE_WARNING, "Error during computation of vector product, return code: %d\n", product_len); + requester_ok = -1; + } + } + + //do_shutdown(&peer2, NULL); + GNUNET_SCHEDULER_add_now (&do_shutdown, &peer2); + return; +} + +/** + * Prepare the message to be sent by peer2 to its vectorproduct service, to + * initiate a request to peer1. + */ +static struct GNUNET_VECTORPRODUCT_QueueEntry * +requester_request () +{ + unsigned int i; + int exit_loop; + uint16_t mask_length = 0; + char * begin = input_elements_peer2; + char * end; + int32_t element; + struct GNUNET_VECTORPRODUCT_QueueEntry *qe; + struct GNUNET_HashCode key; + + GNUNET_assert (peer2.vh != NULL); + + GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key); + + /* Read input_elements_peer2, and put in elements_peer2 array */ + exit_loop = 0; + do + { + unsigned int mcount = element_count_peer2; + //ignore empty rows of ,,,,,, + while (*begin == ',') + begin++; + // get the length of the current element and replace , with null + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return NULL; + } + + GNUNET_array_append (elements_peer2, mcount, element); + element_count_peer2++; + + begin = ++end; + } + while (!exit_loop && element_count_peer2 < max_mids); + GNUNET_assert (elements_peer2 != NULL); + GNUNET_assert (element_count_peer2 >= 1); + + /* Read input_mask_peer2 and read in mask_peer2 array */ + mask_length = element_count_peer2 / 8 + (element_count_peer2 % 8 ? 1 : 0); + mask_peer2 = GNUNET_malloc ((element_count_peer2 / 8) + 2); + GNUNET_assert (NULL != mask_peer2); + if (NULL != input_mask_peer2) + { + begin = input_mask_peer2; + unsigned short mask_count = 0; + int exit_loop = 0; + + do + { + //ignore empty rows of ,,,,,, + while (* begin == ',') + begin++; + // get the length of the current element and replace , with null + // gnunet_ascii-armor uses base32, thus we can use , as separator! + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return NULL; + } + + GNUNET_assert (mask_count <= element_count_peer2); + + if (element) + mask_peer2[mask_count / 8] = mask_peer2[mask_count / 8] | 1 << (mask_count % 8); + + mask_count++; + begin = ++end; + } + while (!exit_loop); + // +1 to see if we would have more data, which would indicate malformed/superficial input + GNUNET_assert (mask_count == element_count_peer2); + } + else + { + for (i = 0; i <= mask_length; i++) + mask_peer2[i] = UCHAR_MAX; // all 1's + } + + qe = GNUNET_VECTORPRODUCT_request (peer2.vh, + &key, + &peer1.our_id, + element_count_peer2, + mask_length, + elements_peer2, mask_peer2, + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10), + &requester_callback, + NULL); + + if (qe == NULL) + { + LOG(GNUNET_ERROR_TYPE_ERROR, "Could not send request to vectorproduct service! Exitting!"); + ok = -1; + return NULL; + } + + return qe; +} + + +/** + * Function prepares the message to be sent by peer1 to its vectorproduct service + * to prepare response, and wait for a request session to be initiated by peer1 + */ +static struct GNUNET_VECTORPRODUCT_QueueEntry * +responder_prepare_response () +{ + GNUNET_assert (peer1.vh != NULL); + + char * begin = input_elements_peer1; + char * end; + int32_t element; + struct GNUNET_VECTORPRODUCT_QueueEntry *qe; + struct GNUNET_HashCode key; + + GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key); + + /* Read input_elements_peer1, and put in elements_peer1 array */ + int exit_loop = 0; + do + { + unsigned int mcount = element_count_peer1; + //ignore empty rows of ,,,,,, + while (*begin == ',') + begin++; + // get the length of the current element and replace , with null + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return NULL; + } + + GNUNET_array_append (elements_peer1, mcount, element); + element_count_peer1++; + + begin = ++end; + } + while (!exit_loop && element_count_peer1 < max_mids); + GNUNET_assert (elements_peer1 != NULL); + GNUNET_assert (element_count_peer1 >= 1); + + qe = GNUNET_VECTORPRODUCT_prepare_response (peer1.vh, + &key, + element_count_peer1, + elements_peer1, + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10), + &responder_callback, + NULL); + + if (qe == NULL) + { + LOG(GNUNET_ERROR_TYPE_ERROR, "Could not send request to vectorproduct service! Exitting!"); + ok = -1; + return NULL; + } + + return qe; +} + + +/** + * Scheduler task to initiate requester client + * + * @param cls void* to struct PeerData + * @param tc Task Context + */ +static void +request_task(void *cls, + const struct GNUNET_SCHEDULER_TaskContext + * tc) +{ + requester_request(); + return; +} + +/** + * Scheduler task to initiate responder client + * + * @param cls void* to struct PeerData + * @param tc Task Context + */ +static void +prepare_response_task(void *cls, + const struct GNUNET_SCHEDULER_TaskContext + * tc) +{ + responder_prepare_response(); + return; +} + + +/** + * Adapter function called to destroy a connection to + * a service. This function is called when GNUNET_TESTBED_operation_done is + * called for peer->op, which holds the handle for GNUNET_TESTBED_service_connect + * operation. + * + * @param cls closure + * @param op_result service handle returned from the connect adapter + */ +static void +vectorproduct_da (void *cls, void *op_result) +{ + struct PeerData* peer = (struct PeerData*) cls; + + GNUNET_VECTORPRODUCT_disconnect (peer->vh); + return; +} + + +/** + * Adapter function called to establish a connection to + * a service. This function is called to by GNUNET_TESTBED_service_connect. + * + * @param cls closure + * @param cfg configuration of the peer to connect to; will be available until + * GNUNET_TESTBED_operation_done() is called on the operation returned + * from GNUNET_TESTBED_service_connect() + * @return service handle to return in 'op_result', NULL on error + */ +static void * +vectorproduct_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct PeerData *p = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %u (`%s') started\n", (&peer1 == p) ? 1 : 2, + GNUNET_i2s (&p->our_id)); + + switch (setup_state) + { + case PEER1_VECTORPRODUCT_CONNECT: + /* Connect peer 2 to vectorproduct service */ + { + peer2.op = GNUNET_TESTBED_service_connect (&peer2, peer2.peer, "vectorproduct", + NULL, NULL, vectorproduct_ca, + vectorproduct_da, &peer2); + setup_state = PEER2_VECTORPRODUCT_CONNECT; + } + + /* Actually connect peer 1 to vectorproduct service */ + peer1.vh = GNUNET_VECTORPRODUCT_connect (cfg); + return peer1.vh; + + case PEER2_VECTORPRODUCT_CONNECT: + /* Actually connect peer 2 to vectorproduct service */ + peer2.vh = GNUNET_VECTORPRODUCT_connect (cfg); + + /* Schedule tasks to initiate request from peer2 and prepare_response from peer1 */ + if(peer1.vh != NULL && peer2.vh != NULL) + { + GNUNET_SCHEDULER_add_now(&prepare_response_task, &peer1); + GNUNET_SCHEDULER_add_now(&request_task, &peer2); + } + + return peer2.vh; + default: + GNUNET_assert (0); + } +} + + +/** + * Callback to be called when the requested peer information is available + * + * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information() + * @param op the operation this callback corresponds to + * @param pinfo the result; will be NULL if the operation has failed + * @param emsg error message if the operation has failed; will be NULL if the + * operation is successfull + */ +static void +peerinfo_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op_, + const struct GNUNET_TESTBED_PeerInformation *pinfo, + const char *emsg) +{ + GNUNET_assert (NULL == emsg); + GNUNET_assert (op == op_); + switch (setup_state) + { + case PEER1_GET_IDENTITY: + { + memcpy (&peer1.our_id, pinfo->result.id, + sizeof (struct GNUNET_PeerIdentity)); + GNUNET_TESTBED_operation_done (op); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 1 id: %s\n", GNUNET_i2s_full + (&peer1.our_id)); + + /* Request for peer id of peer 2*/ + op = GNUNET_TESTBED_peer_get_information (peer2.peer, + GNUNET_TESTBED_PIT_IDENTITY, + &peerinfo_cb, NULL); + setup_state = PEER2_GET_IDENTITY; + } + break; + case PEER2_GET_IDENTITY: + { + memcpy (&peer2.our_id, pinfo->result.id, + sizeof (struct GNUNET_PeerIdentity)); + GNUNET_TESTBED_operation_done (op); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 2 id: %s\n", GNUNET_i2s_full + (&peer2.our_id)); + + /* Connect peer 1 to vectorproduct service */ + peer1.op = GNUNET_TESTBED_service_connect (&peer1, peer1.peer, "vectorproduct", + NULL, NULL, vectorproduct_ca, + vectorproduct_da, &peer1); + setup_state = PEER1_VECTORPRODUCT_CONNECT; + } + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Signature of a main function for a testcase. + * + * @param cls closure + * @param num_peers number of peers in 'peers' + * @param peers handle to peers run in the testbed + */ +static void +test_master (void *cls, unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers) +{ + GNUNET_assert (NULL != peers); + GNUNET_assert (NULL != peers[0]); + GNUNET_assert (NULL != peers[1]); + peer1.peer = peers[0]; + peer2.peer = peers[1]; + /* Get the peer identity and configuration of peer 1 */ + op = GNUNET_TESTBED_peer_get_information (peer1.peer, + GNUNET_TESTBED_PIT_IDENTITY, + &peerinfo_cb, NULL); + setup_state = PEER1_GET_IDENTITY; + + /* Abort task for stopping test on timeout */ + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort, + NULL); +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + uint64_t event_mask; + + ok = GNUNET_NO; + event_mask = 0; + event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED); + max_mids = (GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_MessageHeader)) + / sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1; + + (void) GNUNET_TESTBED_test_run ("test_vectorproduct_api", + "test_vectorproduct_api_data.conf", + NUM_PEERS, event_mask, &controller_event_cb, + NULL, + &test_master, NULL); + + if (GNUNET_SYSERR == ok) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Test failing due to some error before calling API for request or prepare_response\n"); + return 1; + } + else if (GNUNET_SYSERR == responder_ok) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Test failing due to some error in responding_client\n"); + return 1; + } + else if (GNUNET_SYSERR == requester_ok) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Test failing due to some error in requesting client\n"); + return 1; + } + else + return 0; +} + + diff --git a/src/scalarproduct/test_vectorproduct_api_4peers.c b/src/scalarproduct/test_vectorproduct_api_4peers.c @@ -0,0 +1,1084 @@ + +/* + This file is part of GNUnet. + (C) 2013 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * AIM OF THIS TEST + * + * The aim for the extended test is to verify the queuing functionality in the + * service and the API. The API queues requests received from the clients. The + * service queues requests that are received from other services. + * + * To test this, we create 4 peers. peer1 and peer2 are designated responders, + * and peer3 and peer4 are designated as requesters. Each peer calls API for the + * vectorproduct service accordingly. + * + * * peer1 tells the service to prepare response for requests with keys + * input_key_p1_p3(shared key b/w peer1 and peer3) and input_key_p1_p4. + * Similarly peer2 tells service to prepare response for requests with keys + * input_key_p2_p3, and input_key_p2_p4. + * * Simultaneously, peer3 tells its service to send a request to peer1 with key + * input_key_p1_p3, and a request to peer2 with key input_key_p2_p3. Similarly, + * peer 4 sends requests with appropriate keys. + * + * Each peer sends 2 requests to its service, which tests the queuing in API. + * Each service receives 2 requests from other service, which tests the queuing + * functionality in the service. + */ + + +/** + * @file vectorproduct/test_vectorproduct_api_4peers.c + * @brief Vectorproduct API testing between 4 peers using testing API + * @author Gaurav Kukreja + * @author Christian Fuchs + */ + +#include <string.h> + +#include <inttypes.h> +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testbed_service.h" +#include "gnunet_common.h" +#include "gnunet_vectorproduct_service.h" +#include "gnunet_protocols.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "test-vectorproduct-api-4peers",__VA_ARGS__) + +#define NUM_PEERS 4 + +/** + * Structure for holding peer's sockets and IO Handles + */ +struct PeerData +{ + /** + * Handle to testbed peer + */ + struct GNUNET_TESTBED_Peer *peer; + + /** + * The service connect operation to stream + */ + struct GNUNET_TESTBED_Operation *op; + + /** + * Our Peer id + */ + struct GNUNET_PeerIdentity our_id; + + /** + * Pointer to Vector Product Handle + */ + struct GNUNET_VECTORPRODUCT_Handle *vh; + + /** + * Input elements for peer + */ + char * input_elements; + + /** + * Input Mask for peer + */ + char * input_mask; + + /** + * 2 Input keys for peer for 2 sessions of each peer + */ + char * input_keys[2]; + + /** + * Number of requests(or prepare_response) sent by the peer + */ + int request_num; + + /** + * Number of callbacks received by the peer + */ + int callback_num; + + /** + * PeerData of the peers, this peer will talk to + */ + struct PeerData * peers[2]; + + +}; + +/** + * Different states in test setup + */ +enum SetupState +{ + /** + * Get the identity of peer 1 + */ + PEER1_GET_IDENTITY, + + /** + * Get the identity of peer 2 + */ + PEER2_GET_IDENTITY, + + /** + * Get the identity of peer 3 + */ + PEER3_GET_IDENTITY, + + /** + * Get the identity of peer 4 + */ + PEER4_GET_IDENTITY, + + /** + * Connect to stream service of peer 1 + */ + PEER1_VECTORPRODUCT_CONNECT, + + /** + * Connect to stream service of peer 2 + */ + PEER2_VECTORPRODUCT_CONNECT, + + /** + * Connect to stream service of peer 3 + */ + PEER3_VECTORPRODUCT_CONNECT, + + /** + * Connect to stream service of peer 4 + */ + PEER4_VECTORPRODUCT_CONNECT + +}; + +/****************************************************************************** + *** Global Variables ***************************** + ******************************************************************************/ + +/** + * Maximum allowed message-ids we can check in one go (with one GNUNET_message) + */ +static unsigned int max_mids; + +/** + * Session Key used by both the test peers + */ +char input_key_p1_p3[103] = "111111111111111111111111111111111111111111111111113333333333333333333333333333333333333333333333333333"; + +/** + * Session Key used by both the test peers + */ +char input_key_p1_p4[103] = "111111111111111111111111111111111111111111111111114444444444444444444444444444444444444444444444444444"; + +/** + * Session Key used by both the test peers + */ +char input_key_p2_p3[103] = "222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333"; + +/** + * Session Key used by both the test peers + */ +char input_key_p2_p4[103] = "222222222222222222222222222222222222222222222222224444444444444444444444444444444444444444444444444444"; + +/** + * Input elements for peer1 + */ +//char input_elements_peer1[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11"; +char input_elements_peer1[] = "11,11,11"; + +/** + * Input Mask for peer 1 + */ +//char input_mask_peer1[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; +char input_mask_peer1[] = "1,1,1"; + +/** + * Input elements for peer2 + */ +//char input_elements_peer2[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11"; +char input_elements_peer2[] = "11,11,11"; +/** + * Input Mask for peer 2 + */ +//char input_mask_peer2[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; +char input_mask_peer2[] = "1,1,1"; + +/** + * Input elements for peer3 + */ +//char input_elements_peer3[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11"; +char input_elements_peer3[] = "11,11,11"; + +/** + * Input Mask for peer 3 + */ +//char input_mask_peer3[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; +char input_mask_peer3[] = "1,1,1"; + +/** + * Input elements for peer4 + */ +//char input_elements_peer4[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11"; +char input_elements_peer4[] = "11,11,11"; +/** + * Input Mask for peer 4 + */ +//char input_mask_peer4[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; +char input_mask_peer4[] = "1,1,1"; + + +/** + * Data context for peer 1 + */ +static struct PeerData peer1; + +/** + * Data context for peer 2 + */ +static struct PeerData peer2; + +/** + * Data context for peer 3 + */ +static struct PeerData peer3; + +/** + * Data context for peer 4 + */ +static struct PeerData peer4; + +/** + * Various states during test setup + */ +static enum SetupState setup_state; + +/** + * Testbed operation handle + */ +static struct GNUNET_TESTBED_Operation *op; + +/** + * Return value for the test + */ +static int ok; + +/** + * Abort Task for timeout + */ +static GNUNET_SCHEDULER_TaskIdentifier abort_task; +/****************************************************************************** + *** Static Functions ***************************** + ******************************************************************************/ + +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Close sockets and stop testing deamons nicely + */ +static void +do_close (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (peer1.op != NULL) + GNUNET_SCHEDULER_add_now (&do_shutdown, &peer1); + + if (peer2.op != NULL) + GNUNET_SCHEDULER_add_now (&do_shutdown, &peer2); + + if (peer3.op != NULL) + GNUNET_SCHEDULER_add_now (&do_shutdown, &peer3); + + if (peer4.op != NULL) + GNUNET_SCHEDULER_add_now (&do_shutdown, &peer4); + + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + + GNUNET_SCHEDULER_shutdown (); /* For shutting down testbed */ +} + + +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + static int shutdown; + shutdown++; + struct PeerData* peer = (struct PeerData*) cls; + + if (peer == &peer1) + LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down Peer 1!!! \n"); + else if (peer == &peer2) + LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down Peer 2!!! \n"); + else if (peer == &peer3) + LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down Peer 3!!! \n"); + else if (peer == &peer4) + LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down Peer 4!!! \n"); + + // peer->op contains handle to the TESTBED_connect_service operation + // calling operation done, leads to call to vectorproduct_da + GNUNET_TESTBED_operation_done (peer->op); + peer->op = NULL; + + if (shutdown == 4) + GNUNET_SCHEDULER_add_now (&do_close, NULL); +} + + +/** + * Something went wrong and timed out. Kill everything and set error flag + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: ABORT due to Timeout\n"); + ok = GNUNET_SYSERR; + abort_task = 0; + do_close (cls, tc); +} + + +/** + * Controller event callback + * + * @param cls NULL + * @param event the controller event + */ +static void +controller_event_cb (void *cls, + const struct GNUNET_TESTBED_EventInformation *event) +{ + switch (event->type) + { + case GNUNET_TESTBED_ET_OPERATION_FINISHED: + switch (setup_state) + { + case PEER1_VECTORPRODUCT_CONNECT: + case PEER2_VECTORPRODUCT_CONNECT: + GNUNET_assert (NULL == event->details.operation_finished.emsg); + break; + default: + GNUNET_assert (0); + } + break; + default: + GNUNET_assert (0); + } +} + + +static void +responder_callback (void *cls, + const struct GNUNET_HashCode * key, + enum GNUNET_VECTORPRODUCT_ResponseStatus status) +{ + struct PeerData * peer = cls; + + peer->callback_num++; + + if (peer == &peer1) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Peer1 received callback!!!\n"); + } + else if (peer == &peer2) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Peer2 received callback!!!\n"); + } + else + LOG (GNUNET_ERROR_TYPE_ERROR, "Requester callback received, but peer is neither peer1 nor peer2!!!\n"); + + + if (status == GNUNET_VECTORPRODUCT_Status_Failure) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Responder Client received status failure\n"); + ok = -1; + } + else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Responder Client received status invalid response\n"); + ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Timeout == status) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Responder Client received timeout occured\n"); + ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Responder Client received service disconnected!!\n"); + ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Success == status) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Responder Client expected response received!\n"); + ok = 1; + } + else + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Responder Client status = %d!\n", (int) status); + ok = -1; + } + + // TODO : Responder Session Complete. Shutdown Test Cleanly!!! + if (peer->callback_num == 2) + GNUNET_SCHEDULER_add_now (&do_shutdown, peer); +} + + +static void +requester_callback (void *cls, + const struct GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity * peer, + enum GNUNET_VECTORPRODUCT_ResponseStatus status, + const struct GNUNET_VECTORPRODUCT_client_response *msg) +{ + struct PeerData * peer_ = cls; + uint32_t product_len; + + peer_->callback_num++; + + if (peer_ == &peer3) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Peer3 received callback!!!\n"); + } + else if (peer_ == &peer4) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Peer4 received callback!!!\n"); + } + else + LOG (GNUNET_ERROR_TYPE_ERROR, "Requester callback received, but peer is neither peer3 nor peer4!!!\n"); + + + if (status == GNUNET_VECTORPRODUCT_Status_Failure) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Requester Client received status failure\n"); + ok = -1; + } + else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Requester Client received status invalid response\n"); + ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Timeout == status) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Requester Client timeout occured\n"); + ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Requester Client service disconnected!!\n"); + ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Success != status) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Requester Client Status = %d\n", (int) status); + ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Success == status) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Requester client received status successful!\n"); + product_len = ntohl (msg->product_length); + + if (0 < product_len) + { + gcry_mpi_t result; + gcry_error_t ret = 0; + size_t read = 0; + + ret = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, (void*) &(msg[1]), product_len, &read); + + if (0 != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not convert to mpi to value!\n"); + } + else + { + gcry_mpi_release (result); + } + ok = 1; + } + else + { + //currently not used, but if we get more info due to MESH we will need this + LOG (GNUNET_ERROR_TYPE_ERROR, "Error during computation of vector product, return code: %d\n", product_len); + ok = -1; + } + } + + if (peer_->callback_num == 2) + GNUNET_SCHEDULER_add_now (&do_shutdown, peer_); +} + + +static struct GNUNET_VECTORPRODUCT_QueueEntry * +requester_request (char * input_elements, + char * input_mask, + char * input_key, + struct PeerData * peer, + struct PeerData * to_peer) +{ + + + unsigned int i; + uint16_t element_count = 0; + int32_t * elements = NULL; + uint16_t mask_length = 0; + unsigned char * mask = NULL; + int32_t element; + struct GNUNET_VECTORPRODUCT_QueueEntry *qe; + struct GNUNET_HashCode key; + int exit_loop; + char * begin = input_elements; + char * end; + + GNUNET_assert (peer->vh != NULL); + + GNUNET_CRYPTO_hash_from_string (input_key, &key); + + exit_loop = 0; + /* Read input_elements, and put in elements array */ + do + { + unsigned int mcount = element_count; + //ignore empty rows of ,,,,,, + while (*begin == ',') + begin++; + // get the length of the current element and replace , with null + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return NULL; + } + + GNUNET_array_append (elements, mcount, element); + element_count++; + + begin = ++end; + } + while (!exit_loop && element_count < max_mids); + GNUNET_assert (elements != NULL); + GNUNET_assert (element_count >= 1); + + /* Read input_mask and read in mask array */ + mask_length = element_count / 8 + (element_count % 8 ? 1 : 0); + mask = GNUNET_malloc ((element_count / 8) + 2); + GNUNET_assert (NULL != mask); + if (NULL != input_mask) + { + begin = input_mask; + unsigned short mask_count = 0; + int exit_loop = 0; + + do + { + //ignore empty rows of ,,,,,, + while (* begin == ',') + begin++; + // get the length of the current element and replace , with null + // gnunet_ascii-armor uses base32, thus we can use , as separator! + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return NULL; + } + + GNUNET_assert (mask_count <= element_count); + + if (element) + mask[mask_count / 8] = mask[mask_count / 8] | 1 << (mask_count % 8); + + mask_count++; + begin = ++end; + } + while (!exit_loop); + // +1 to see if we would have more data, which would indicate malformed/superficial input + GNUNET_assert (mask_count == element_count); + } + else + { + for (i = 0; i <= mask_length; i++) + mask[i] = UCHAR_MAX; // all 1's + } + + qe = GNUNET_VECTORPRODUCT_request (peer->vh, + &key, + &to_peer->our_id, + element_count, + mask_length, + elements, mask, + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60), + &requester_callback, + peer); + + if (qe == NULL) + { + LOG(GNUNET_ERROR_TYPE_WARNING, "Could not send request to vectorproduct service! Exitting!"); + ok = -1; + return NULL; + } + + return qe; +} + + +/** + * Function prepares the message to be sent by peer1 to its vectorproduct service + * to prepare response, and wait for a request session to be initiated by peer1 + */ +static struct GNUNET_VECTORPRODUCT_QueueEntry * +responder_prepare_response (char * input_elements, + char * input_mask, + char * input_key, + struct PeerData * peer) +{ + GNUNET_assert (peer->vh != NULL); + + unsigned int i; + uint16_t element_count = 0; + int32_t * elements = NULL; + unsigned short mask_length = 0; + unsigned char * mask = NULL; + int32_t element; + struct GNUNET_VECTORPRODUCT_QueueEntry *qe; + struct GNUNET_HashCode key; + int exit_loop; + char * begin; + char * end; + + GNUNET_CRYPTO_hash_from_string (input_key, &key); + + /* Read input_elements, and put in elements array */ + exit_loop = 0; + begin = input_elements; + do + { + unsigned int mcount = element_count; + //ignore empty rows of ,,,,,, + while (*begin == ',') + begin++; + // get the length of the current element and replace , with null + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return NULL; + } + + GNUNET_array_append (elements, mcount, element); + element_count++; + + begin = ++end; + } + while (!exit_loop && element_count < max_mids); + GNUNET_assert (elements != NULL); + GNUNET_assert (element_count >= 1); + + /* Read input_mask and read in mask array */ + mask_length = element_count / 8 + (element_count % 8 ? 1 : 0); + mask = GNUNET_malloc ((element_count / 8) + 2); + GNUNET_assert (NULL != mask); + if (NULL != input_mask) + { + begin = input_mask; + unsigned short mask_count = 0; + int exit_loop = 0; + + do + { + //ignore empty rows of ,,,,,, + while (* begin == ',') + begin++; + // get the length of the current element and replace , with null + // gnunet_ascii-armor uses base32, thus we can use , as separator! + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return NULL; + } + + GNUNET_assert (mask_count <= element_count); + + if (element) + mask[mask_count / 8] = mask[mask_count / 8] | 1 << (mask_count % 8); + + mask_count++; + begin = ++end; + } + while (!exit_loop); + // +1 to see if we would have more data, which would indicate malformed/superficial input + GNUNET_assert (mask_count == element_count); + } + else + { + for (i = 0; i <= mask_length; i++) + mask[i] = UCHAR_MAX; // all 1's + } + + qe = GNUNET_VECTORPRODUCT_prepare_response (peer->vh, + &key, + element_count, + elements, + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60), + &responder_callback, + peer); + + if (qe == NULL) + { + LOG(GNUNET_ERROR_TYPE_ERROR, "Could not send request to vectorproduct service! Exitting!"); + ok = -1; + return NULL; + } + + return qe; +} + + +static void +request_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext + * tc) +{ + struct PeerData * peer = cls; + + requester_request (peer->input_elements, peer->input_mask, peer->input_keys[peer->request_num], peer, peer->peers[peer->request_num]); + peer->request_num++; + return; +} + + +static void +prepare_response_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext + * tc) +{ + struct PeerData * peer = cls; + + responder_prepare_response (peer->input_elements, peer->input_mask, peer->input_keys[peer->request_num], peer); + peer->request_num++; + return; +} + + +/** + * Adapter function called to destroy a connection to + * a service. This function is called when GNUNET_TESTBED_operation_done is + * called for peer->op, which holds the handle for GNUNET_TESTBED_service_connect + * operation. + * + * @param cls closure + * @param op_result service handle returned from the connect adapter + */ +static void +vectorproduct_da (void *cls, void *op_result) +{ + struct PeerData* peer = (struct PeerData*) cls; + + GNUNET_VECTORPRODUCT_disconnect (peer->vh); + return; + + GNUNET_assert (0); +} + + +/** + * Adapter function called to establish a connection to + * a service. This function is called to by GNUNET_TESTBED_service_connect. + * + * @param cls closure + * @param cfg configuration of the peer to connect to; will be available until + * GNUNET_TESTBED_operation_done() is called on the operation returned + * from GNUNET_TESTBED_service_connect() + * @return service handle to return in 'op_result', NULL on error + */ +static void * +vectorproduct_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct PeerData *p = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %u (`%s') started\n", (&peer1 == p) ? 1 : 2, + GNUNET_i2s (&p->our_id)); + + switch (setup_state) + { + case PEER1_VECTORPRODUCT_CONNECT: + /* Connect peer 2 to vectorproduct service */ + { + peer2.op = GNUNET_TESTBED_service_connect (&peer2, peer2.peer, "vectorproduct", + NULL, NULL, vectorproduct_ca, + vectorproduct_da, &peer2); + setup_state = PEER2_VECTORPRODUCT_CONNECT; + } + + peer1.vh = GNUNET_VECTORPRODUCT_connect (cfg); + return peer1.vh; + + case PEER2_VECTORPRODUCT_CONNECT: + /* Connect peer 3 to vectorproduct service */ + { + peer3.op = GNUNET_TESTBED_service_connect (&peer3, peer3.peer, "vectorproduct", + NULL, NULL, vectorproduct_ca, + vectorproduct_da, &peer3); + setup_state = PEER3_VECTORPRODUCT_CONNECT; + } + + peer2.vh = GNUNET_VECTORPRODUCT_connect (cfg); + return peer2.vh; + + case PEER3_VECTORPRODUCT_CONNECT: + /* Connect peer 4 to vectorproduct service */ + { + peer4.op = GNUNET_TESTBED_service_connect (&peer4, peer4.peer, "vectorproduct", + NULL, NULL, vectorproduct_ca, + vectorproduct_da, &peer4); + setup_state = PEER4_VECTORPRODUCT_CONNECT; + } + + peer3.vh = GNUNET_VECTORPRODUCT_connect (cfg); + return peer3.vh; + + case PEER4_VECTORPRODUCT_CONNECT: + peer4.vh = GNUNET_VECTORPRODUCT_connect (cfg); + + /* Schedule the tasks to issue prepare_response calls from peer1 and peer2 + * for peer3 and peer4. + */ + GNUNET_SCHEDULER_add_now (&prepare_response_task, &peer1); + GNUNET_SCHEDULER_add_now (&prepare_response_task, &peer1); + GNUNET_SCHEDULER_add_now (&prepare_response_task, &peer2); + GNUNET_SCHEDULER_add_now (&prepare_response_task, &peer2); + + /* + * Schedule the tasks to issue requests calls from peer3 and peer4 + * to peer1 and peer2 + */ + GNUNET_SCHEDULER_add_now (&request_task, &peer3); + GNUNET_SCHEDULER_add_now (&request_task, &peer3); + GNUNET_SCHEDULER_add_now (&request_task, &peer4); + GNUNET_SCHEDULER_add_now (&request_task, &peer4); + + return peer2.vh; + default: + GNUNET_assert (0); + } +} + + +/** + * Callback to be called when the requested peer information is available + * + * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information() + * @param op the operation this callback corresponds to + * @param pinfo the result; will be NULL if the operation has failed + * @param emsg error message if the operation has failed; will be NULL if the + * operation is successfull + */ +static void +peerinfo_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op_, + const struct GNUNET_TESTBED_PeerInformation *pinfo, + const char *emsg) +{ + GNUNET_assert (NULL == emsg); + GNUNET_assert (op == op_); + switch (setup_state) + { + case PEER1_GET_IDENTITY: + { + memcpy (&peer1.our_id, pinfo->result.id, + sizeof (struct GNUNET_PeerIdentity)); + GNUNET_TESTBED_operation_done (op); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 1 id: %s\n", GNUNET_i2s_full + (&peer1.our_id)); + + /* Request for peer id of peer 2*/ + op = GNUNET_TESTBED_peer_get_information (peer2.peer, + GNUNET_TESTBED_PIT_IDENTITY, + &peerinfo_cb, NULL); + setup_state = PEER2_GET_IDENTITY; + } + break; + case PEER2_GET_IDENTITY: + { + memcpy (&peer2.our_id, pinfo->result.id, + sizeof (struct GNUNET_PeerIdentity)); + GNUNET_TESTBED_operation_done (op); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 2 id: %s\n", GNUNET_i2s_full + (&peer2.our_id)); + + /* Request for peer id of peer 3*/ + op = GNUNET_TESTBED_peer_get_information (peer3.peer, + GNUNET_TESTBED_PIT_IDENTITY, + &peerinfo_cb, NULL); + setup_state = PEER3_GET_IDENTITY; + } + break; + case PEER3_GET_IDENTITY: + { + memcpy (&peer3.our_id, pinfo->result.id, + sizeof (struct GNUNET_PeerIdentity)); + GNUNET_TESTBED_operation_done (op); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 3 id: %s\n", GNUNET_i2s_full + (&peer3.our_id)); + + /* Request for peer id of peer 4*/ + op = GNUNET_TESTBED_peer_get_information (peer4.peer, + GNUNET_TESTBED_PIT_IDENTITY, + &peerinfo_cb, NULL); + setup_state = PEER4_GET_IDENTITY; + } + break; + case PEER4_GET_IDENTITY: + { + memcpy (&peer4.our_id, pinfo->result.id, + sizeof (struct GNUNET_PeerIdentity)); + GNUNET_TESTBED_operation_done (op); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 2 id: %s\n", GNUNET_i2s_full + (&peer2.our_id)); + + /* Connect peer 1 to vectorproduct service */ + peer1.op = GNUNET_TESTBED_service_connect (&peer1, peer1.peer, "vectorproduct", + NULL, NULL, vectorproduct_ca, + vectorproduct_da, &peer1); + setup_state = PEER1_VECTORPRODUCT_CONNECT; + } + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Signature of a main function for a testcase. + * + * @param cls closure + * @param num_peers number of peers in 'peers' + * @param peers handle to peers run in the testbed + */ +static void +test_master (void *cls, unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers) +{ + GNUNET_assert (NULL != peers); + GNUNET_assert (NULL != peers[0]); + GNUNET_assert (NULL != peers[1]); + GNUNET_assert (NULL != peers[2]); + GNUNET_assert (NULL != peers[3]); + peer1.peer = peers[0]; + peer1.input_elements = input_elements_peer1; + peer1.input_mask = input_mask_peer1; + peer1.request_num = 0; + peer1.callback_num = 0; + peer1.input_keys[0] = input_key_p1_p3; + peer1.input_keys[1] = input_key_p1_p4; + + peer2.peer = peers[1]; + peer2.input_elements = input_elements_peer2; + peer2.input_mask = input_mask_peer2; + peer2.request_num = 0; + peer2.callback_num = 0; + peer2.input_keys[0] = input_key_p2_p3; + peer2.input_keys[1] = input_key_p2_p4; + + peer3.peer = peers[2]; + peer3.input_elements = input_elements_peer3; + peer3.input_mask = input_mask_peer3; + peer3.request_num = 0; + peer3.callback_num = 0; + peer3.input_keys[0] = input_key_p1_p3; + peer3.input_keys[1] = input_key_p2_p3; + peer3.peers[0] = &peer1; + peer3.peers[1] = &peer2; + + + peer4.peer = peers[3]; + peer4.input_elements = input_elements_peer4; + peer4.input_mask = input_mask_peer4; + peer4.request_num = 0; + peer4.callback_num = 0; + peer4.input_keys[0] = input_key_p1_p4; + peer4.input_keys[1] = input_key_p2_p4; + peer4.peers[0] = &peer1; + peer4.peers[1] = &peer2; + + /* Get the peer identity and configuration of peer 1 */ + op = GNUNET_TESTBED_peer_get_information (peer1.peer, + GNUNET_TESTBED_PIT_IDENTITY, + &peerinfo_cb, NULL); + setup_state = PEER1_GET_IDENTITY; + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 120), &do_abort, + NULL); +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + uint64_t event_mask; + + ok = GNUNET_NO; + event_mask = 0; + event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED); + max_mids = (GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_MessageHeader)) + / sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1; + (void) GNUNET_TESTBED_test_run ("test_vectorproduct_api_4peers", + "test_vectorproduct_api_data.conf", + NUM_PEERS, event_mask, &controller_event_cb, + NULL, + &test_master, NULL); + if (GNUNET_SYSERR == ok) + return 1; + return 0; +} diff --git a/src/scalarproduct/test_vectorproduct_api_data.conf b/src/scalarproduct/test_vectorproduct_api_data.conf @@ -0,0 +1,96 @@ +[arm] +DEFAULTSERVICES = core transport vectorproduct mesh testbed +PORT = 12366 + +[core] +PORT = 12092 + +[vectorproduct] +#AUTOSTART = YES +BINARY = gnunet-service-vectorproduct +UNIXPATH = /tmp/gnunet-service-vectorproduct.sock +HOME = $SERVICEHOME +HOSTNAME = localhost +PORT = 2087 + +[testbed] +OVERLAY_TOPOLOGY = CLIQUE + +[lockmanager] +AUTOSTART = NO +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +PORT = 12101 + +[statistics] +AUTOSTART = YES +ACCEPT_FROM = 127.0.0.1; +PORT = 12102 + +[fs] +AUTOSTART = NO + +[resolver] +AUTOSTART = NO + +[mesh] +# AUTOSTART = YES +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +PORT = 10700 +# PREFIX = valgrind --leak-check=full +# PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args + +[dht] +AUTOSTART = YES +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +PORT = 12100 + +[block] +plugins = dht test + +[dhtcache] +QUOTA = 1 MB +DATABASE = sqlite + +[transport] +PLUGINS = tcp +ACCEPT_FROM6 = ::1; +ACCEPT_FROM = 127.0.0.1; +NEIGHBOUR_LIMIT = 50 +PORT = 12365 + +[ats] +WAN_QUOTA_OUT = 3932160 +WAN_QUOTA_IN = 3932160 + +[transport-tcp] +TIMEOUT = 300 s +PORT = 12368 + +[TESTING] +WEAKRANDOM = YES + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[PATHS] +SERVICEHOME = /tmp/test-vectorproduct/ + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[vpn] +AUTOSTART = NO + +[nat] +RETURN_LOCAL_ADDRESSES = YES + +[consensus] +AUTOSTART = NO + diff --git a/src/scalarproduct/test_vectorproduct_api_regression.c b/src/scalarproduct/test_vectorproduct_api_regression.c @@ -0,0 +1,852 @@ + +/* + This file is part of GNUnet. + (C) 2013 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file vectorproduct/test_vectorproduct_api_regression.c + * @brief VectorProduct API regression test + * @author Gaurav Kukreja + * @author Christian Fuchs + */ + +/** + * AIM of the regression test + * + * This test tries to check whether the service can handle abrupt client disconnect. + * + * 1. We create a responder peer, and ask the service to prepare_response. After this, + * we disconnect responder peer from service. + * + * 2. Then we create a requester peer, and ask service to request another peer. We + * should check that the service on responder peer is still active and receives + * request from the requester. We then disconnect requester peer from service. Both + * the requester and responder service should handle this cleanly. + */ + +#include <string.h> + +#include <inttypes.h> +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testbed_service.h" +#include "gnunet_common.h" +#include "gnunet_vectorproduct_service.h" +#include "gnunet_protocols.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "test-vectorproduct-api-regression",__VA_ARGS__) +#define NUM_PEERS 2 + +/** + * Structure for holding peer's sockets and IO Handles + */ +struct PeerData +{ + /** + * Handle to testbed peer + */ + struct GNUNET_TESTBED_Peer *peer; + + /** + * The service connect operation to stream + */ + struct GNUNET_TESTBED_Operation *op; + + /** + * Our Peer id + */ + struct GNUNET_PeerIdentity our_id; + + /** + * Pointer to Vector Product Handle + */ + struct GNUNET_VECTORPRODUCT_Handle *vh; +}; + +/** + * Different states in test setup + */ +enum SetupState +{ + /** + * Get the identity of peer 1 + */ + PEER1_GET_IDENTITY, + + /** + * Get the identity of peer 2 + */ + PEER2_GET_IDENTITY, + + /** + * Connect to stream service of peer 1 + */ + PEER1_VECTORPRODUCT_CONNECT, + + /** + * Connect to stream service of peer 2 + */ + PEER2_VECTORPRODUCT_CONNECT + +}; + +/****************************************************************************** + *** Global Variables ***************************** + ******************************************************************************/ + +/** + * Maximum allowed message-ids we can check in one go (with one GNUNET_message) + */ +static unsigned int max_mids; + +/** + * Session Key used by both the test peers + */ +char input_key[103] = "helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhe"; + +/** + * Input elements for peer1 + */ +//char input_elements_peer1[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11"; +char input_elements_peer1[] = "11,11,11"; + +/** + * Input Mask for peer 1 + */ +//char input_mask_peer1[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; +char input_mask_peer1[] = "1,1,1"; + +/** + * the array of converted message IDs to send to our service + */ +static int32_t * elements_peer1 = NULL; + +/** + * Input elements for peer2 + */ +//char input_elements_peer2[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11"; +char input_elements_peer2[] = "11,11,11"; +/** + * Input Mask for peer 2 + */ +//char input_mask_peer2[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; +char input_mask_peer2[] = "1,1,1"; +/** + * the array of converted message IDs to send to our service + */ +static int32_t * elements_peer2 = NULL; + +/** + * the array of converted message IDs to send to our service + */ +static unsigned char * mask_peer2 = NULL; + +/** + * Data context for peer 1 + */ +static struct PeerData peer1; + +/** + * Data context for peer 2 + */ +static struct PeerData peer2; + +/** + * Various states during test setup + */ +static enum SetupState setup_state; + +/** + * Testbed operation handle + */ +static struct GNUNET_TESTBED_Operation *op; + +/** + * Return value of the test. + */ +static int ok; + +/** + * Abort Task for timeout + */ +static GNUNET_SCHEDULER_TaskIdentifier abort_task; +/****************************************************************************** + *** Static Functions ***************************** + ******************************************************************************/ + +/** + * Helper function to shutdown a test peer + * + * @param cls void* to struct PeerData of the peer to be disconnected + * @param tc Task Context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Helper function to connect a test peer + * + * @param cls void* to struct PeerData of the peer to be connected + * @param tc Task Context + */ +static void +connect_peer (void *cls, + const struct GNUNET_SCHEDULER_TaskContext * tc); + + +/** + * Close sockets and stop testing deamons nicely + */ +static void +do_close (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + + if (peer1.op != NULL) + do_shutdown (&peer1, NULL); + + if (peer2.op != NULL) + do_shutdown (&peer2, NULL); + + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + GNUNET_SCHEDULER_cancel (abort_task); + + GNUNET_SCHEDULER_shutdown (); /* For shutting down testbed */ +} + + +/** + * Helper function to shutdown a test peer + * + * @param cls void* to struct PeerData of the peer to be disconnected + * @param tc Task Context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + static int shutdown; + shutdown++; + struct PeerData* peer = (struct PeerData*) cls; + + if (peer == &peer1) + LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting Peer1\n\n"); + else if (peer == &peer2) + LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting Peer2\n\n"); + + // peer->op contains handle to the TESTBED_connect_service operation + // calling operation done, leads to call to vectorproduct_da + if (peer->op != NULL) + { + GNUNET_TESTBED_operation_done (peer->op); + peer->op = NULL; + } + + if (shutdown >= 2) + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 10), &do_close, NULL); +} + + +/** + * Something went wrong and timed out. Kill everything and set error flag + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: ABORT due to Timeout\n"); + ok = GNUNET_SYSERR; + abort_task = 0; + do_close (cls, tc); +} + + +/** + * Controller event callback + * + * @param cls NULL + * @param event the controller event + */ +static void +controller_event_cb (void *cls, + const struct GNUNET_TESTBED_EventInformation *event) +{ + switch (event->type) + { + case GNUNET_TESTBED_ET_OPERATION_FINISHED: + switch (setup_state) + { + case PEER1_VECTORPRODUCT_CONNECT: + case PEER2_VECTORPRODUCT_CONNECT: + GNUNET_assert (NULL == event->details.operation_finished.emsg); + break; + default: + GNUNET_assert (0); + } + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Callback function called for the responder peer i.e. peer1 + * + * @param cls + * @param key Session key + * @param status Status of the message + */ +static void +responder_callback (void *cls, + const struct GNUNET_HashCode * key, + enum GNUNET_VECTORPRODUCT_ResponseStatus status) +{ + if (status == GNUNET_VECTORPRODUCT_Status_Failure) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received status failure\n"); + ok = -1; + } + else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received status invalid response\n"); + ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Timeout == status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received timeout occured\n"); + ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received service disconnected!!\n"); + ok = 1; + } + else if (GNUNET_VECTORPRODUCT_Status_Success == status) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Responder Client expected response received!\n"); + ok = -1; + } + else + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client status = %d!\n", (int) status); + ok = -1; + } + + // Not shutting down this time, only for this regression test. We have shutdown explicitly earlier. + // Shutting down again is causing problems. + + // if(peer1.vh != NULL) + // { + // GNUNET_SCHEDULER_add_now(&do_shutdown, &peer1); + // } + return; +} + + +/** + * Callback function called for the requester peer i.e. peer2 + * + * @param cls + * @param key Session key + * @param status Status of the message + */ +static void +requester_callback (void *cls, + const struct GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity * peer, + enum GNUNET_VECTORPRODUCT_ResponseStatus status, + const struct GNUNET_VECTORPRODUCT_client_response *msg) +{ + uint32_t product_len; + + if (status == GNUNET_VECTORPRODUCT_Status_Failure) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client received status failure\n"); + ok = -1; + } + else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client received status invalid response\n"); + ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Timeout == status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client timeout occured\n"); + ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client service disconnected!!\n"); + ok = 1; + } + else if (GNUNET_VECTORPRODUCT_Status_Success != status) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Requester Client Status = %d\n", (int) status); + ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Success == status) + { + product_len = ntohl (msg->product_length); + + if (0 < product_len) + { + gcry_mpi_t result; + gcry_error_t ret = 0; + size_t read = 0; + + ret = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, (void *) &msg[1], product_len, &read); + + if (0 != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Could not convert to mpi to value!\n"); + } + else + { + gcry_mpi_dump (result); + gcry_mpi_release (result); + } + ok = -1; + } + else + { //currently not used, but if we get more info due to MESH we will need this + LOG (GNUNET_ERROR_TYPE_WARNING, "Error during computation of vector product, return code: %d\n", product_len); + ok = -1; + } + } + + // Not shutting down this time, only for this regression test. We have shutdown explicitly earlier. + // Shutting down again is causing problems. + + // if(peer2.vh != NULL) + // { + // GNUNET_SCHEDULER_add_now(&do_shutdown, &peer2); + // } + return; +} + + +static void +requester_request (void *cls, + const struct GNUNET_SCHEDULER_TaskContext * tc) +{ + GNUNET_assert (peer2.vh != NULL); + + unsigned int i; + uint16_t element_count = 0; + uint16_t mask_length = 0; + char * begin = input_elements_peer2; + char * end; + int32_t element; + struct GNUNET_VECTORPRODUCT_QueueEntry *qe; + struct GNUNET_HashCode key; + int exit_loop = 0; + + GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key); + + /* Read input_elements_peer2, and put in elements_peer2 array */ + exit_loop = 0; + do + { + unsigned int mcount = element_count; + //ignore empty rows of ,,,,,, + while (*begin == ',') + begin++; + // get the length of the current element and replace , with null + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return; + } + + GNUNET_array_append (elements_peer2, mcount, element); + element_count++; + + begin = ++end; + } + while (!exit_loop && element_count < max_mids); + GNUNET_assert (elements_peer2 != NULL); + GNUNET_assert (element_count >= 1); + + /* Read input_mask_peer2 and read in mask_peer2 array */ + mask_length = element_count / 8 + (element_count % 8 ? 1 : 0); + mask_peer2 = GNUNET_malloc ((element_count / 8) + 2); + GNUNET_assert (NULL != mask_peer2); + if (NULL != input_mask_peer2) + { + begin = input_mask_peer2; + unsigned short mask_count = 0; + exit_loop = 0; + + do + { + //ignore empty rows of ,,,,,, + while (* begin == ',') + begin++; + // get the length of the current element and replace , with null + // gnunet_ascii-armor uses base32, thus we can use , as separator! + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return; + } + + GNUNET_assert (mask_count <= element_count); + + if (element) + mask_peer2[mask_count / 8] = mask_peer2[mask_count / 8] | 1 << (mask_count % 8); + + mask_count++; + begin = ++end; + } + while (!exit_loop); + // +1 to see if we would have more data, which would indicate malformed/superficial input + GNUNET_assert (mask_count == element_count); + } + else + { + for (i = 0; i <= mask_length; i++) + mask_peer2[i] = UCHAR_MAX; // all 1's + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Responder peer key %s\n", &peer1.our_id); + + qe = GNUNET_VECTORPRODUCT_request (peer2.vh, + &key, + &peer1.our_id, + element_count, + mask_length, + elements_peer2, mask_peer2, + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10), + &requester_callback, + NULL); + + if (qe == NULL) + { + FPRINTF (stderr, "%s", _ ("Could not send request to vectorproduct service! Exitting!")); + ok = -1; + return; + } + + /** + * For regression, we shutdown the initiator peer, peer2, one second after + * issuing a request. Hopefully, peer1 notices that the tunnel has been + * been destroyed, and will shutdown cleanly. + */ + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &do_shutdown, &peer2); + + return; +} + + +/** + * Function prepares the message to be sent by peer1 to its vectorproduct service + * to prepare response, and wait for a request session to be initiated by peer1 + */ +static void +responder_prepare_response (void *cls, + const struct GNUNET_SCHEDULER_TaskContext * tc) +{ + GNUNET_assert (peer1.vh != NULL); + + uint16_t element_count = 0; + char * begin = input_elements_peer1; + char * end; + int32_t element; + + struct GNUNET_VECTORPRODUCT_QueueEntry *qe; + + struct GNUNET_HashCode key; + GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key); + + int exit_loop = 0; + /* Read input_elements_peer1, and put in elements_peer1 array */ + do + { + unsigned int mcount = element_count; + //ignore empty rows of ,,,,,, + while (*begin == ',') + begin++; + // get the length of the current element and replace , with null + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return; + } + + GNUNET_array_append (elements_peer1, mcount, element); + element_count++; + + begin = ++end; + } + while (!exit_loop && element_count < max_mids); + + GNUNET_assert (elements_peer1 != NULL); + GNUNET_assert (element_count >= 1); + + qe = GNUNET_VECTORPRODUCT_prepare_response (peer1.vh, + &key, + element_count, + elements_peer1, + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10), + &responder_callback, + NULL); + + if (qe == NULL) + { + FPRINTF (stderr, "%s", _ ("Could not send request to vectorproduct service! Exitting!")); + ok = -1; + return; + } + + // connect the second peer + setup_state = PEER2_VECTORPRODUCT_CONNECT; + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1), &connect_peer, &peer2); + + // while the service is waiting for a matching request, disconnect the test client + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &do_shutdown, &peer1); + + return; +} + + +/** + * Adapter function called to destroy a connection to + * a service. This function is called when GNUNET_TESTBED_operation_done is + * called for peer->op, which holds the handle for GNUNET_TESTBED_service_connect + * operation. + * + * @param cls closure + * @param op_result service handle returned from the connect adapter + */ +static void +vectorproduct_da (void *cls, void *op_result) +{ + struct PeerData* peer = (struct PeerData*) cls; + + GNUNET_VECTORPRODUCT_disconnect (peer->vh); + peer->vh = NULL; + return; +} + + +/** + * Adapter function called to establish a connection to + * a service. This function is called to by GNUNET_TESTBED_service_connect. + * + * @param cls closure + * @param cfg configuration of the peer to connect to; will be available until + * GNUNET_TESTBED_operation_done() is called on the operation returned + * from GNUNET_TESTBED_service_connect() + * @return service handle to return in 'op_result', NULL on error + */ +static void * +vectorproduct_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct PeerData *p = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %u (`%s') started\n", (&peer1 == p) ? 1 : 2, + GNUNET_i2s (&p->our_id)); + + switch (setup_state) + { + case PEER1_VECTORPRODUCT_CONNECT: + peer1.vh = GNUNET_VECTORPRODUCT_connect (cfg); + + if (peer1.vh != NULL) + { + /* prepare_response from peer1 */ + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, &responder_prepare_response, NULL); + } + else + { + ok = -1; + return NULL; + } + + return peer1.vh; + + case PEER2_VECTORPRODUCT_CONNECT: + /* Actually connect peer 2 to vectorproduct service */ + peer2.vh = GNUNET_VECTORPRODUCT_connect (cfg); + + if (peer2.vh != NULL) + { + /* initiate request from peer2 */ + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, &requester_request, NULL); + } + else + { + ok = -1; + return NULL; + } + + return peer2.vh; + default: + GNUNET_assert (0); + } +} + + +/** + * Helper function to connect a test peer + * + * @param cls void* to struct PeerData of the peer to be connected + * @param tc Task Context + */ +static void +connect_peer (void *cls, + const struct GNUNET_SCHEDULER_TaskContext * tc) +{ + struct PeerData *peer = cls; + + peer->op = GNUNET_TESTBED_service_connect (peer, peer->peer, "vectorproduct", + NULL, NULL, vectorproduct_ca, + vectorproduct_da, peer); + +} + + +/** + * Callback to be called when the requested peer information is available + * + * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information() + * @param op the operation this callback corresponds to + * @param pinfo the result; will be NULL if the operation has failed + * @param emsg error message if the operation has failed; will be NULL if the + * operation is successfull + */ +static void +peerinfo_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op_, + const struct GNUNET_TESTBED_PeerInformation *pinfo, + const char *emsg) +{ + GNUNET_assert (NULL == emsg); + GNUNET_assert (op == op_); + + switch (setup_state) + { + case PEER1_GET_IDENTITY: + { + memcpy (&peer1.our_id, pinfo->result.id, + sizeof (struct GNUNET_PeerIdentity)); + GNUNET_TESTBED_operation_done (op); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 1 id: %s\n", GNUNET_i2s_full + (&peer1.our_id)); + + /* Request for peer id of peer 2*/ + setup_state = PEER2_GET_IDENTITY; + op = GNUNET_TESTBED_peer_get_information (peer2.peer, + GNUNET_TESTBED_PIT_IDENTITY, + &peerinfo_cb, NULL); + } + break; + case PEER2_GET_IDENTITY: + { + memcpy (&peer2.our_id, pinfo->result.id, + sizeof (struct GNUNET_PeerIdentity)); + GNUNET_TESTBED_operation_done (op); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 2 id: %s\n", GNUNET_i2s_full + (&peer2.our_id)); + + /* Connect peer 1 to vectorproduct service */ + setup_state = PEER1_VECTORPRODUCT_CONNECT; + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, &connect_peer, &peer1); + } + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Signature of a main function for a testcase. + * + * @param cls closure + * @param num_peers number of peers in 'peers' + * @param peers handle to peers run in the testbed + */ +static void +test_master (void *cls, unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers) +{ + GNUNET_assert (NULL != peers); + GNUNET_assert (NULL != peers[0]); + GNUNET_assert (NULL != peers[1]); + peer1.peer = peers[0]; + peer2.peer = peers[1]; + + /* Get the peer identity and configuration of peer 1 */ + setup_state = PEER1_GET_IDENTITY; + op = GNUNET_TESTBED_peer_get_information (peer1.peer, + GNUNET_TESTBED_PIT_IDENTITY, + &peerinfo_cb, NULL); + + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort, + NULL); +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + uint64_t event_mask; + + ok = GNUNET_NO; + event_mask = 0; + event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED); + max_mids = (GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_MessageHeader)) + / sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1; + + (void) GNUNET_TESTBED_test_run ("test_vectorproduct_api_regression", + "test_vectorproduct_api_data.conf", + NUM_PEERS, event_mask, &controller_event_cb, + NULL, + &test_master, NULL); + + if (GNUNET_SYSERR == ok) + return 1; + return 0; +} + + diff --git a/src/scalarproduct/test_vectorproduct_api_regression2.c b/src/scalarproduct/test_vectorproduct_api_regression2.c @@ -0,0 +1,931 @@ +/* + This file is part of GNUnet. + (C) 2013 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file vectorproduct/test_vectorproduct_api_regression2.c + * @brief Regression test, destroys requester service before receiving response + * responder service + * @author Gaurav Kukreja + * @author Christian Fuchs + */ + +#include <string.h> + +#include <inttypes.h> +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testbed_service.h" +#include "gnunet_common.h" +#include "gnunet_vectorproduct_service.h" +#include "gnunet_protocols.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "test-vectorproduct-api-regression2",__VA_ARGS__) +#define NUM_PEERS 2 + +/** + * Structure for holding peer's sockets and IO Handles + */ +struct PeerData +{ + /** + * Handle to testbed peer + */ + struct GNUNET_TESTBED_Peer *peer; + + /** + * The service connect operation to stream + */ + struct GNUNET_TESTBED_Operation *op; + + /** + * Our Peer id + */ + struct GNUNET_PeerIdentity our_id; + + /** + * Pointer to Vector Product Handle + */ + struct GNUNET_VECTORPRODUCT_Handle *vh; +}; + +/** + * Different states in test setup + */ +enum SetupState +{ + /** + * Get the identity of peer 1 + */ + PEER1_GET_IDENTITY, + + /** + * Get the identity of peer 2 + */ + PEER2_GET_IDENTITY, + + /** + * Connect to stream service of peer 1 + */ + PEER1_VECTORPRODUCT_CONNECT, + + /** + * Connect to stream service of peer 2 + */ + PEER2_VECTORPRODUCT_CONNECT + +}; + +/****************************************************************************** + *** Global Variables ***************************** + ******************************************************************************/ + +/** + * Maximum allowed message-ids we can check in one go (with one GNUNET_message) + */ +static unsigned int max_mids; + +/** + * Session Key used by both the test peers + */ +char input_key[103] = "helloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhelloworldhe"; + +/** + * Input elements for peer1 + */ +char input_elements_peer1[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11"; +//char input_elements_peer1[] = "11,11,11"; + +/** + * Input Mask for peer 1 + */ +char input_mask_peer1[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; +//char input_mask_peer1[] = "1,1,1"; + +/** + * the array of converted message IDs to send to our service + */ +static int32_t * elements_peer1 = NULL; + +/** + * the array of converted message IDs to send to our service + */ +static unsigned char * mask_peer1 = NULL; + +/** + * Number of elements + */ +uint16_t element_count_peer1 = 0; + +/** + * Input elements for peer2 + */ +char input_elements_peer2[] = "11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11"; +//char input_elements_peer2[] = "11,11,11"; + +/** + * Input Mask for peer 2 + */ +char input_mask_peer2[] = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; +//char input_mask_peer2[] = "1,1,1"; + +/** + * the array of converted message IDs to send to our service + */ +static int32_t * elements_peer2 = NULL; + +/** + * the array of converted message IDs to send to our service + */ +static unsigned char * mask_peer2 = NULL; + +/** + * Number of elements + */ +uint16_t element_count_peer2 = 0; + +/** + * Data context for peer 1 + */ +static struct PeerData peer1; + +/** + * Data context for peer 2 + */ +static struct PeerData peer2; + +/** + * Various states during test setup + */ +static enum SetupState setup_state; + +/** + * Testbed operation handle + */ +static struct GNUNET_TESTBED_Operation *op; + +static int ok; + +static int responder_ok; + +static int requester_ok; + +static GNUNET_SCHEDULER_TaskIdentifier abort_task; +/****************************************************************************** + *** Static Functions ***************************** + ******************************************************************************/ + +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Close sockets and stop testing deamons nicely + */ +static void +do_close (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + static int closed; + + if (peer1.op != NULL) + { + do_shutdown (&peer1, NULL); + } + + if (peer2.op != NULL) + { + do_shutdown (&peer2, NULL); + } + + if (GNUNET_SCHEDULER_NO_TASK != abort_task) + { + GNUNET_SCHEDULER_cancel (abort_task); + abort_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_shutdown (); /* For shutting down testbed */ + } + + if (!closed) + { + closed++; + GNUNET_SCHEDULER_shutdown (); /* For shutting down testbed */ + } +} + +/** + * Shutdown a peer + * + * @param cls closure is a pointer to the struct PeerData of the peer to be disconnected + * @param tc Task Context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + static int shutdown; + shutdown++; + struct PeerData* peer = (struct PeerData*) cls; + + // peer->op contains handle to the TESTBED_connect_service operation + // calling operation done, leads to call to vectorproduct_da + if (peer->op != NULL) + { + if (peer == &peer1) + LOG (GNUNET_ERROR_TYPE_INFO, "Disconnecting Peer1\n\n"); + else if (peer == &peer2) + LOG (GNUNET_ERROR_TYPE_INFO, "Disconnecting Peer2\n\n"); + + GNUNET_TESTBED_operation_done (peer->op); + peer->op = NULL; + } + + if (peer1.op == NULL && peer2.op == NULL) + GNUNET_SCHEDULER_add_now (&do_close, NULL); +} + + +/** + * Something went wrong and timed out. Kill everything and set error flag + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: ABORT due to Timeout\n"); + ok = GNUNET_SYSERR; + abort_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_add_now (&do_shutdown, &peer1); + GNUNET_SCHEDULER_add_now (&do_shutdown, &peer2); +} + + +/** + * Controller event callback + * + * @param cls NULL + * @param event the controller event + */ +static void +controller_event_cb (void *cls, + const struct GNUNET_TESTBED_EventInformation *event) +{ + switch (event->type) + { + case GNUNET_TESTBED_ET_OPERATION_FINISHED: + switch (setup_state) + { + case PEER1_VECTORPRODUCT_CONNECT: + case PEER2_VECTORPRODUCT_CONNECT: + GNUNET_assert (NULL == event->details.operation_finished.emsg); + break; + default: + GNUNET_assert (0); + } + break; + default: + GNUNET_assert (0); + } +} + + +static void +responder_callback (void *cls, + const struct GNUNET_HashCode * key, + enum GNUNET_VECTORPRODUCT_ResponseStatus status) +{ + if (status == GNUNET_VECTORPRODUCT_Status_Failure) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received status failure\n"); + responder_ok = -1; + } + else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received status invalid response\n"); + responder_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Timeout == status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received timeout occured\n"); + // In this regression test, requester is supposed to fail due to timeout + // therefore responder_ok is set to 1, to make regression test pass + responder_ok = 1; + } + else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client received service disconnected!!\n"); + responder_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Success == status) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Responder Client expected response received!\n"); + responder_ok = 1; + } + else + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Responder Client status = %d!\n", (int) status); + responder_ok = -1; + } + // TODO : Responder Session Complete. Shutdown Test Cleanly!!! + //do_shutdown(&peer1, NULL); + GNUNET_SCHEDULER_add_now (&do_shutdown, &peer1); + return; +} + + +static void +requester_callback (void *cls, + const struct GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity * peer, + enum GNUNET_VECTORPRODUCT_ResponseStatus status, + uint16_t size, struct GNUNET_VECTORPRODUCT_client_response *msg, + uint16_t type) +{ + uint32_t product_len; + + if (status == GNUNET_VECTORPRODUCT_Status_Failure) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client received status failure\n"); + // In this regression test, requester is supposed to receive status failure + // therefore requester_ok is set to 1, to make regression test pass + requester_ok = 1; + } + else if (status == GNUNET_VECTORPRODUCT_Status_InvalidResponse) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client received status invalid response\n"); + requester_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Timeout == status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client timeout occured\n"); + requester_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_ServiceDisconnected == status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client service disconnected!!\n"); + requester_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Success != status) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Requester Client Status = %d\n", (int) status); + requester_ok = -1; + } + else if (GNUNET_VECTORPRODUCT_Status_Success == status) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Requester Client expected response received!\n"); + + product_len = ntohl (msg->product_length); + + if (0 < product_len) + { + gcry_mpi_t result; + gcry_error_t ret = 0; + size_t read = 0; + + ret = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, (void *) &msg[1], product_len, &read); + + if (0 != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not convert to mpi to value!\n"); + ok = -1; + } + else + { + uint16_t i = 0; + + // calculate expected product + gcry_mpi_t expected_result; + gcry_mpi_t v1; + gcry_mpi_t v2; + gcry_mpi_t v1_v2_prod; + + expected_result = gcry_mpi_new (0); + + for (i = 0; i < element_count_peer1; i++) + { + uint32_t value; + v1_v2_prod = gcry_mpi_new (0); + + // long to gcry_mpi_t + value = elements_peer1[i] >= 0 ? elements_peer1[i] : -elements_peer1[i]; + if (elements_peer1[i] < 0) + { + v1 = gcry_mpi_new (0); + gcry_mpi_sub_ui (v1, v1, value); + } + else + v1 = gcry_mpi_set_ui (NULL, value); + + // long to gcry_mpi_t + value = elements_peer2[i] >= 0 ? elements_peer2[i] : -elements_peer2[i]; + if (elements_peer2[i] < 0) + { + v2 = gcry_mpi_new (0); + gcry_mpi_sub_ui (v2, v2, value); + } + else + v2 = gcry_mpi_set_ui (NULL, value); + + gcry_mpi_mul (v1_v2_prod, v1, v2); + gcry_mpi_add (expected_result, expected_result, v1_v2_prod); + + gcry_mpi_release (v1); + gcry_mpi_release (v2); + gcry_mpi_release (v1_v2_prod); + + } + + // compare the result + if (!gcry_mpi_cmp (expected_result, result)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Scalar Product matches expected Result!!\n"); + requester_ok = 1; + } + else + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Scalar Product DOES NOT match expected Result!!\n"); + requester_ok = -1; + } + gcry_mpi_release (result); + gcry_mpi_release (expected_result); + } + } + else + { //currently not used, but if we get more info due to MESH we will need this + LOG (GNUNET_ERROR_TYPE_WARNING, "Error during computation of vector product, return code: %d\n", product_len); + requester_ok = -1; + } + } + + //do_shutdown(&peer2, NULL); + GNUNET_SCHEDULER_add_now (&do_shutdown, &peer2); + return; +} + + +static struct GNUNET_VECTORPRODUCT_QueueEntry * +requester_request () +{ + GNUNET_assert (peer2.vh != NULL); + + unsigned int i; + //uint16_t element_count_peer2 = 0; + uint16_t mask_length = 0; + char * begin = input_elements_peer2; + char * end; + int32_t element; + + struct GNUNET_VECTORPRODUCT_QueueEntry *qe; + + struct GNUNET_HashCode key; + GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key); + + int exit_loop = 0; + /* Read input_elements_peer2, and put in elements_peer2 array */ + do + { + unsigned int mcount = element_count_peer2; + //ignore empty rows of ,,,,,, + while (*begin == ',') + begin++; + // get the length of the current element and replace , with null + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return NULL; + } + + GNUNET_array_append (elements_peer2, mcount, element); + element_count_peer2++; + + begin = ++end; + } + while (!exit_loop && element_count_peer2 < max_mids); + + GNUNET_assert (elements_peer2 != NULL); + GNUNET_assert (element_count_peer2 >= 1); + mask_length = element_count_peer2 / 8 + (element_count_peer2 % 8 ? 1 : 0); + mask_peer2 = GNUNET_malloc ((element_count_peer2 / 8) + 2); + GNUNET_assert (NULL != mask_peer2); + + /* Read input_mask_peer2 and read in mask_peer2 array */ + if (NULL != input_mask_peer2) + { + begin = input_mask_peer2; + unsigned short mask_count = 0; + int exit_loop = 0; + + do + { + //ignore empty rows of ,,,,,, + while (* begin == ',') + begin++; + // get the length of the current element and replace , with null + // gnunet_ascii-armor uses base32, thus we can use , as separator! + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return NULL; + } + + GNUNET_assert (mask_count <= element_count_peer2); + + if (element) + mask_peer2[mask_count / 8] = mask_peer2[mask_count / 8] | 1 << (mask_count % 8); + + mask_count++; + begin = ++end; + } + while (!exit_loop); + // +1 to see if we would have more data, which would indicate malformed/superficial input + GNUNET_assert (mask_count == element_count_peer2); + } + else + { + for (i = 0; i <= mask_length; i++) + mask_peer2[i] = UCHAR_MAX; // all 1's + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Responder peer key %s\n", &peer1.our_id); + + // TODO : Create the mask array + qe = GNUNET_VECTORPRODUCT_request (peer2.vh, + &key, + &peer1.our_id, + element_count_peer2, + mask_length, + elements_peer2, mask_peer2, + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10), + &requester_callback, + NULL); + + if (qe == NULL) + { + FPRINTF (stderr, "%s", _ ("Could not send request to vectorproduct service! Exitting!")); + ok = -1; + return NULL; + } + + return qe; +} + + +/** + * Function prepares the message to be sent by peer1 to its vectorproduct service + * to prepare response, and wait for a request session to be initiated by peer1 + */ +static struct GNUNET_VECTORPRODUCT_QueueEntry * +responder_prepare_response () +{ + GNUNET_assert (peer1.vh != NULL); + + unsigned int i; + //uint16_t element_count_peer1 = 0; + char * begin = input_elements_peer1; + char * end; + int32_t element; + + struct GNUNET_VECTORPRODUCT_QueueEntry *qe; + + struct GNUNET_HashCode key; + GNUNET_CRYPTO_hash (input_key, strlen (input_key), &key); + + int exit_loop = 0; + /* Read input_elements_peer1, and put in elements_peer1 array */ + do + { + unsigned int mcount = element_count_peer1; + //ignore empty rows of ,,,,,, + while (*begin == ',') + begin++; + // get the length of the current element and replace , with null + for (end = begin; *end && *end != ','; end++); + + if (*end == '\0') + exit_loop = 1; + + if (*end == ',') + *end = '\0'; + + if (1 != sscanf (begin, "%" SCNd32, &element)) + { + FPRINTF (stderr, _ ("Could not convert `%s' to int32_t.\n"), begin); + ok = -1; + return NULL; + } + + GNUNET_array_append (elements_peer1, mcount, element); + element_count_peer1++; + + begin = ++end; + } + while (!exit_loop && element_count_peer1 < max_mids); + + GNUNET_assert (elements_peer1 != NULL); + GNUNET_assert (element_count_peer1 >= 1); + + qe = GNUNET_VECTORPRODUCT_prepare_response (peer1.vh, + &key, + element_count_peer1, + elements_peer1, + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10), + &responder_callback, + NULL); + + if (qe == NULL) + { + FPRINTF (stderr, "%s", _ ("Could not send request to vectorproduct service! Exitting!")); + ok = -1; + return NULL; + } + return qe; +} + + +/** + * Scheduler task to initiate requester client + * + * @param cls void* to struct PeerData + * @param tc Task Context + */ +static void +request_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext + * tc) +{ + struct PeerData * peer = cls; + + requester_request (); + return; +} + + +/** + * Scheduler task to initiate responder client + * + * @param cls void* to struct PeerData + * @param tc Task Context + */ +static void +prepare_response_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext + * tc) +{ + struct PeerData * peer = cls; + + responder_prepare_response (); + return; +} + + +static void +peer_stop_callback (void *cls, + const char *emsg) +{ + GNUNET_TESTBED_peer_destroy (peer2.peer); +} + +/** + * Destroys Peer2 i.e. the initiator peer (Alice) This function is scheduled to + * run a few milliseconds after the request has been sent to the Responding Peer (Bob). + * This function tries to emulate a crash of Peer2. + * + * @param cls Not used + * @param tc Task Context - Not used + */ +static void +destroy_server (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + LOG (GNUNET_ERROR_TYPE_INFO, "\n***\nKilling the Requesting Client, hopefully before it receives response\n***\n"); + do_shutdown (&peer2, NULL); + GNUNET_TESTBED_peer_stop (peer2.peer, &peer_stop_callback, NULL); +} + + +/** + * Adapter function called to destroy a connection to + * a service. This function is called when GNUNET_TESTBED_operation_done is + * called for peer->op, which holds the handle for GNUNET_TESTBED_service_connect + * operation. + * + * @param cls closure + * @param op_result service handle returned from the connect adapter + */ +static void +vectorproduct_da (void *cls, void *op_result) +{ + struct PeerData* peer = (struct PeerData*) cls; + + GNUNET_VECTORPRODUCT_disconnect (peer->vh); + return; +} + + +/** + * Adapter function called to establish a connection to + * a service. This function is called to by GNUNET_TESTBED_service_connect. + * + * @param cls closure + * @param cfg configuration of the peer to connect to; will be available until + * GNUNET_TESTBED_operation_done() is called on the operation returned + * from GNUNET_TESTBED_service_connect() + * @return service handle to return in 'op_result', NULL on error + */ +static void * +vectorproduct_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct PeerData *p = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer %u (`%s') started\n", (&peer1 == p) ? 1 : 2, + GNUNET_i2s (&p->our_id)); + + switch (setup_state) + { + case PEER1_VECTORPRODUCT_CONNECT: + /* Connect peer 2 to vectorproduct service */ + /* The connect adapter vectorproduct_ca will be called to perform the actual connection */ + { + peer2.op = GNUNET_TESTBED_service_connect (&peer2, peer2.peer, "vectorproduct", + NULL, NULL, vectorproduct_ca, + vectorproduct_da, &peer2); + setup_state = PEER2_VECTORPRODUCT_CONNECT; + } + + /* Actually connect peer 1 to vectorproduct service */ + peer1.vh = GNUNET_VECTORPRODUCT_connect (cfg); + return peer1.vh; + + case PEER2_VECTORPRODUCT_CONNECT: + /* Actually connect peer 2 to vectorproduct service */ + peer2.vh = GNUNET_VECTORPRODUCT_connect (cfg); + + + if (peer1.vh != NULL && peer2.vh != NULL) + { + GNUNET_SCHEDULER_add_now (&prepare_response_task, &peer1); + GNUNET_SCHEDULER_add_now (&request_task, &peer2); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 6), + &destroy_server, NULL); + } + else + { + // TODO : Handle error. One of the peers is not connected. Cleanly shutdown + ok = -1; + return NULL; + } + return peer2.vh; + default: + GNUNET_assert (0); + } +} + + +/** + * Callback to be called when the requested peer information is available + * + * @param cb_cls the closure from GNUNET_TETSBED_peer_get_information() + * @param op the operation this callback corresponds to + * @param pinfo the result; will be NULL if the operation has failed + * @param emsg error message if the operation has failed; will be NULL if the + * operation is successfull + */ +static void +peerinfo_cb (void *cb_cls, struct GNUNET_TESTBED_Operation *op_, + const struct GNUNET_TESTBED_PeerInformation *pinfo, + const char *emsg) +{ + GNUNET_assert (NULL == emsg); + GNUNET_assert (op == op_); + switch (setup_state) + { + case PEER1_GET_IDENTITY: + { + memcpy (&peer1.our_id, pinfo->result.id, + sizeof (struct GNUNET_PeerIdentity)); + GNUNET_TESTBED_operation_done (op); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 1 id: %s\n", GNUNET_i2s_full + (&peer1.our_id)); + + /* Request for peer id of peer 2*/ + op = GNUNET_TESTBED_peer_get_information (peer2.peer, + GNUNET_TESTBED_PIT_IDENTITY, + &peerinfo_cb, NULL); + setup_state = PEER2_GET_IDENTITY; + } + break; + case PEER2_GET_IDENTITY: + { + memcpy (&peer2.our_id, pinfo->result.id, + sizeof (struct GNUNET_PeerIdentity)); + GNUNET_TESTBED_operation_done (op); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer 2 id: %s\n", GNUNET_i2s_full + (&peer2.our_id)); + + /* Connect peer 1 to vectorproduct service */ + /* The connect adapter vectorproduct_ca will be called to perform the actual connection */ + peer1.op = GNUNET_TESTBED_service_connect (&peer1, peer1.peer, "vectorproduct", + NULL, NULL, vectorproduct_ca, + vectorproduct_da, &peer1); + setup_state = PEER1_VECTORPRODUCT_CONNECT; + } + break; + default: + GNUNET_assert (0); + } +} + + +/** + * Signature of a main function for a testcase. + * + * @param cls closure + * @param num_peers number of peers in 'peers' + * @param peers handle to peers run in the testbed + */ +static void +test_master (void *cls, unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers) +{ + GNUNET_assert (NULL != peers); + GNUNET_assert (NULL != peers[0]); + GNUNET_assert (NULL != peers[1]); + peer1.peer = peers[0]; + peer2.peer = peers[1]; + /* Get the peer identity and configuration of peer 1 */ + op = GNUNET_TESTBED_peer_get_information (peer1.peer, + GNUNET_TESTBED_PIT_IDENTITY, + &peerinfo_cb, NULL); + setup_state = PEER1_GET_IDENTITY; + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort, + NULL); +} + + +/** + * Main function + */ +int +main (int argc, char **argv) +{ + uint64_t event_mask; + + ok = GNUNET_NO; + event_mask = 0; + event_mask |= (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED); + max_mids = (GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_MessageHeader)) + / sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1; + + (void) GNUNET_TESTBED_test_run ("test_vectorproduct_api_regression2", + "test_vectorproduct_api_data.conf", + NUM_PEERS, event_mask, &controller_event_cb, + NULL, + &test_master, NULL); + if (GNUNET_SYSERR == ok) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Test failing due to some error before calling API for request or prepare_response\n"); + return 1; + } + else if (GNUNET_SYSERR == responder_ok) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Test failing due to some error in response for responding_client\n"); + return 1; + } + else if (GNUNET_SYSERR == requester_ok) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Test failing due to some error in response for requesting client\n"); + return 1; + } + else + return 0; +} + + diff --git a/src/scalarproduct/vectorproduct.conf b/src/scalarproduct/vectorproduct.conf @@ -0,0 +1,7 @@ +[vectorproduct] +BINARY = gnunet-service-vectorproduct +UNIXPATH = /tmp/gnunet-service-vectorproduct.sock +HOME = $SERVICEHOME +# PORT = 2106 +# PORT = 2087 + diff --git a/src/scalarproduct/vectorproduct.conf.in b/src/scalarproduct/vectorproduct.conf.in @@ -0,0 +1,7 @@ +[vectorproduct] +BINARY = gnunet-service-vectorproduct +UNIXPATH = /tmp/gnunet-service-vectorproduct.sock +HOME = $SERVICEHOME +# PORT = 2106 +@UNIXONLY@ PORT = 2087 + diff --git a/src/scalarproduct/vectorproduct_api.c b/src/scalarproduct/vectorproduct_api.c @@ -0,0 +1,715 @@ +/* + This file is part of GNUnet. + (C) 2013 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file vectorproduct/vectorproduct_api.c + * @brief API for the vectorproduct + * @author Christian Fuchs + * @author Gaurav Kukreja + * + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_vectorproduct_service.h" +#include "gnunet_protocols.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "vectorproduct-api",__VA_ARGS__) + +/************************************************************** + *** Datatype Declarations ********** + **************************************************************/ + +/** + * Entry in the request queue per client + */ +struct GNUNET_VECTORPRODUCT_QueueEntry +{ + /** + * This is a linked list. + */ + struct GNUNET_VECTORPRODUCT_QueueEntry *next; + + /** + * This is a linked list. + */ + struct GNUNET_VECTORPRODUCT_QueueEntry *prev; + + /** + * Handle to the master context. + */ + struct GNUNET_VECTORPRODUCT_Handle *h; + + /** + * Size of the message + */ + uint16_t message_size; + + /** + * Message to be sent to the vectorproduct service + */ + struct GNUNET_VECTORPRODUCT_client_request* msg; + + union + { + /** + * Function to call after transmission of the request. + */ + GNUNET_VECTORPRODUCT_ContinuationWithStatus cont_status; + + /** + * Function to call after transmission of the request. + */ + GNUNET_VECTORPRODUCT_DatumProcessor cont_datum; + }; + + /** + * Closure for 'cont'. + */ + void *cont_cls; + + /** + * Has this message been transmitted to the service? + * Only ever GNUNET_YES for the head of the queue. + * Note that the overall struct should end at a + * multiple of 64 bits. + */ + int16_t was_transmitted; + + /** + * Timeout for the current operation. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Task for timeout signaling. + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; + + /** + * Response Processor for response from the service. This function calls the + * continuation function provided by the client. + */ + GNUNET_VECTORPRODUCT_ResponseMessageHandler response_proc; +}; + +/************************************************************** + *** Function Declarations ********** + **************************************************************/ + +/** + * Creates a new entry at the tail of the DLL + * + * @param h handle to the master context + * + * @return pointer to the entry + */ +static struct GNUNET_VECTORPRODUCT_QueueEntry * +make_queue_entry (struct GNUNET_VECTORPRODUCT_Handle *h); + +/** + * Removes the head entry from the queue + * + * @param h Handle to the master context + */ +static struct GNUNET_VECTORPRODUCT_QueueEntry * +free_queue_head_entry (struct GNUNET_VECTORPRODUCT_Handle * h); + +/** + * Triggered when timeout occurs for a request in queue + * + * @param cls The pointer to the QueueEntry + * @param tc Task Context + */ +static void +timeout_queue_entry (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + +/** + * Called when a response is received from the service. After basic check + * handler in qe->response_proc is called. This functions handles the response + * to the client which used the API. + * + * @param cls Pointer to the Master Context + * @param msg Pointer to the data received in response + */ +static void +receive_cb (void *cls, const struct GNUNET_MessageHeader *msg); + +/** + * Transmits the request to the VectorProduct Sevice + * + * @param cls Closure + * @param size Size of the buffer + * @param buf Pointer to the buffer + * + * @return Size of the message sent + */ +static size_t transmit_request (void *cls, size_t size, + void *buf); + +/** + * Issues transmit request for the new entries in the queue + * + * @param h handle to the master context + */ +static void +process_queue (struct GNUNET_VECTORPRODUCT_Handle *h); + +/************************************************************** + *** Static Function Declarations ********** + **************************************************************/ + + +/** + * Creates a new entry at the tail of the DLL + * + * @param h handle to the master context + * + * @return pointer to the entry + */ +static struct GNUNET_VECTORPRODUCT_QueueEntry * +make_queue_entry (struct GNUNET_VECTORPRODUCT_Handle *h) +{ + struct GNUNET_VECTORPRODUCT_QueueEntry *qe; + + qe = GNUNET_new (struct GNUNET_VECTORPRODUCT_QueueEntry); + + // if queue empty + if (NULL == h->queue_head && NULL == h->queue_tail) + { + qe->next = NULL; + qe->prev = NULL; + h->queue_head = qe; + h->queue_tail = qe; + } + else + { + qe->prev = h->queue_tail; + h->queue_tail->next = qe; + h->queue_tail = qe; + } + + return qe; +} + + +/** + * Removes the head entry from the queue + * + * @param h Handle to the master context + */ +static struct GNUNET_VECTORPRODUCT_QueueEntry * +free_queue_head_entry (struct GNUNET_VECTORPRODUCT_Handle * h) +{ + struct GNUNET_VECTORPRODUCT_QueueEntry * qe = NULL; + + GNUNET_assert (NULL != h); + if (NULL == h->queue_head && NULL == h->queue_tail) + { + // The queue is empty. Just return. + LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue was empty when free_queue_head_entry was called.\n"); + } + else if (h->queue_head == h->queue_tail) //only one entry + { + qe = h->queue_head; + qe->next = NULL; + qe->prev = NULL; + h->queue_head = NULL; + h->queue_tail = NULL; + } + else + { + qe = h->queue_head; + h->queue_head = h->queue_head->next; + h->queue_head->prev = NULL; + qe->next = NULL; + qe->prev = NULL; + } + return qe; +} + + +/** + * Triggered when timeout occurs for a request in queue + * + * @param cls The pointer to the QueueEntry + * @param tc Task Context + */ +static void +timeout_queue_entry (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_VECTORPRODUCT_QueueEntry * qe = cls; + + // Update Statistics + GNUNET_STATISTICS_update (qe->h->stats, + gettext_noop ("# queue entry timeouts"), 1, + GNUNET_NO); + + // Clear the timeout_task + qe->timeout_task = GNUNET_SCHEDULER_NO_TASK; + + // transmit_request is supposed to cancel timeout task. + // If message was not transmitted, there is definitely an error. + GNUNET_assert (GNUNET_NO == qe->was_transmitted); + + LOG (GNUNET_ERROR_TYPE_INFO, "Timeout of request in datastore queue\n"); + + // remove the queue_entry for the queue + GNUNET_CONTAINER_DLL_remove (qe->h->queue_head, qe->h->queue_tail, qe); + qe->response_proc (qe, NULL, GNUNET_VECTORPRODUCT_Status_Timeout); +} + + +/** + * Handles the RESULT received in reply of prepare_response from the + * service + * + * @param cls Handle to the Master Context + * @param msg Pointer to the response received + */ +static void +process_status_message (void *cls, + const struct GNUNET_MessageHeader *msg, + enum GNUNET_VECTORPRODUCT_ResponseStatus status) +{ + struct GNUNET_VECTORPRODUCT_QueueEntry *qe = cls; + + GNUNET_assert (qe != NULL); + + if (qe->cont_status != NULL) + qe->cont_status (qe->cont_cls, &qe->msg->key, status); +} + + +/** + * Handles the RESULT received in reply of prepare_response from the + * service + * + * @param cls Handle to the Master Context + * @param msg Pointer to the response received + */ +static void +process_result_message (void *cls, + const struct GNUNET_MessageHeader *msg, + enum GNUNET_VECTORPRODUCT_ResponseStatus status) +{ + struct GNUNET_VECTORPRODUCT_QueueEntry *qe = cls; + + GNUNET_assert (qe != NULL); + + if (msg == NULL && qe->cont_datum != NULL) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Timeout reached or session terminated.\n"); + } + if (qe->cont_datum != NULL) + { + qe->cont_datum (qe->cont_cls, &qe->msg->key, &qe->msg->peer, status, (struct GNUNET_VECTORPRODUCT_client_response *) msg); + } +} + + +/** + * Called when a response is received from the service. After basic check + * handler in qe->response_proc is called. This functions handles the response + * to the client which used the API. + * + * @param cls Pointer to the Master Context + * @param msg Pointer to the data received in response + */ +static void +receive_cb (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_VECTORPRODUCT_Handle *h = cls; + struct GNUNET_VECTORPRODUCT_QueueEntry *qe; + int16_t was_transmitted; + struct GNUNET_VECTORPRODUCT_client_response *message = + (struct GNUNET_VECTORPRODUCT_client_response *) msg; + + h->in_receive = GNUNET_NO; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply from VectorProduct\n"); + + if (NULL == (qe = free_queue_head_entry (h))) + { + /** + * The queue head will be NULL if the client disconnected, + * * In case of Alice, client disconnected after sending request, before receiving response + * * In case of Bob, client disconnected after preparing response, before getting request from Alice. + */ + process_queue (h); + return; + } + + if (h->client == NULL) + { + // GKUKREJA : handle this correctly + /** + * The queue head will be NULL if the client disconnected, + * * In case of Alice, client disconnected after sending request, before receiving response + * * In case of Bob, client disconnected after preparing response, before getting request from Alice. + */ + process_queue (h); + return; + } + + was_transmitted = qe->was_transmitted; + // Control will only come here, when the request was transmitted to service, + // and service responded. + GNUNET_assert (was_transmitted == GNUNET_YES); + + if (msg == NULL) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Service responded with NULL!\n"); + qe->response_proc (qe, NULL, GNUNET_VECTORPRODUCT_Status_Failure); + } + else if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_VECTORPRODUCT_SERVICE_TO_CLIENT)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid Message Received\n"); + qe->response_proc (qe, msg, GNUNET_VECTORPRODUCT_Status_InvalidResponse); + } + else if (ntohl (message->product_length) == 0) + { + // response for the responder client, successful + GNUNET_STATISTICS_update (h->stats, + gettext_noop ("# SUC responder result messages received"), 1, + GNUNET_NO); + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Received message from service without product attached.\n"); + qe->response_proc (qe, msg, GNUNET_VECTORPRODUCT_Status_Success); + } + else if (ntohl (message->product_length) > 0) + { + // response for the requester client, successful + GNUNET_STATISTICS_update (h->stats, + gettext_noop ("# SUC requester result messages received"), 1, + GNUNET_NO); + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Received message from requester service for requester client.\n"); + qe->response_proc (qe, msg, GNUNET_VECTORPRODUCT_Status_Success); + } + + GNUNET_free (qe); + process_queue (h); +} + + +/** + * Transmits the request to the VectorProduct Sevice + * + * @param cls Closure + * @param size Size of the buffer + * @param buf Pointer to the buffer + * + * @return Size of the message sent + */ +static size_t +transmit_request (void *cls, size_t size, + void *buf) +{ + struct GNUNET_VECTORPRODUCT_Handle *h = cls; + struct GNUNET_VECTORPRODUCT_QueueEntry *qe; + size_t msize; + + if (NULL == (qe = h->queue_head)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue head is NULL!\n\n"); + return 0; + } + + GNUNET_SCHEDULER_cancel (qe->timeout_task); + qe->timeout_task = GNUNET_SCHEDULER_NO_TASK; + + h->th = NULL; + if (NULL == (qe = h->queue_head)) + return 0; /* no entry in queue */ + if (buf == NULL) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Failed to transmit request to VECTORPRODUCT.\n"); + GNUNET_STATISTICS_update (h->stats, + gettext_noop ("# transmission request failures"), + 1, GNUNET_NO); + GNUNET_VECTORPRODUCT_disconnect (h); + return 0; + } + if (size < (msize = qe->message_size)) + { + process_queue (h); + return 0; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmitting %u byte request to VECTORPRODUCT\n", + msize); + + memcpy (buf, qe->msg, size); + GNUNET_free (qe->msg); + qe->was_transmitted = GNUNET_YES; + + GNUNET_assert (GNUNET_NO == h->in_receive); + h->in_receive = GNUNET_YES; + + GNUNET_CLIENT_receive (h->client, &receive_cb, h, + GNUNET_TIME_UNIT_FOREVER_REL); + +#if INSANE_STATISTICS + GNUNET_STATISTICS_update (h->stats, + gettext_noop ("# bytes sent to vectorproduct"), 1, + GNUNET_NO); +#endif + return size; +} + + +/** + * Issues transmit request for the new entries in the queue + * + * @param h handle to the master context + */ +static void +process_queue (struct GNUNET_VECTORPRODUCT_Handle *h) +{ + struct GNUNET_VECTORPRODUCT_QueueEntry *qe; + + if (NULL == (qe = h->queue_head)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue empty\n"); + return; /* no entry in queue */ + } + if (qe->was_transmitted == GNUNET_YES) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Head request already transmitted\n"); + return; /* waiting for replies */ + } + if (h->th != NULL) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Pending transmission request\n"); + return; /* request pending */ + } + if (h->client == NULL) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Not connected\n"); + return; /* waiting for reconnect */ + } + if (GNUNET_YES == h->in_receive) + { + /* wait for response to previous query */ + return; + } + + h->th = + GNUNET_CLIENT_notify_transmit_ready (h->client, qe->message_size, + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_YES, + &transmit_request, h); + + if (h->th == NULL) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _ ("Failed to send a message to the vectorproduct service\n")); + return; + } + + GNUNET_assert (GNUNET_NO == h->in_receive); + GNUNET_break (NULL != h->th); +} + + + +/************************************************************** + *** API ********** + **************************************************************/ + + +/** + * Called by the responder client to prepare response + * + * @param h handle to the master context + * @param key Session key - unique to the requesting client + * @param element_count Number of elements in the vector + * @param mask_length number of bytes in the mask + * @param elements Array of elements of the vector + * @param mask Array of the mask + * @param timeout Relative timeout for the operation + * @param cont Callback function + * @param cont_cls Closure for the callback function + */ +struct GNUNET_VECTORPRODUCT_QueueEntry * +GNUNET_VECTORPRODUCT_prepare_response (struct GNUNET_VECTORPRODUCT_Handle *h, + const struct GNUNET_HashCode * key, + uint16_t element_count, + int32_t * elements, + struct GNUNET_TIME_Relative timeout, + GNUNET_VECTORPRODUCT_ContinuationWithStatus cont, + void *cont_cls) +{ + struct GNUNET_VECTORPRODUCT_QueueEntry *qe = make_queue_entry (h); + int32_t * vector; + uint16_t size; + unsigned int i; + + GNUNET_assert (GNUNET_SERVER_MAX_MESSAGE_SIZE >= sizeof (struct GNUNET_VECTORPRODUCT_client_request) + +element_count * sizeof (int32_t)); + size = sizeof (struct GNUNET_VECTORPRODUCT_client_request) +element_count * sizeof (int32_t); + + qe->message_size = size; + qe->msg = GNUNET_malloc (size); + qe->msg->header.size = htons (size); + qe->msg->header.type = htons (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_CLIENT_TO_BOB); + qe->msg->element_count = htons (element_count); + qe->msg->mask_length = htons (0); + memcpy (&qe->msg->key, key, sizeof (struct GNUNET_HashCode)); + qe->cont_status = cont; + qe->cont_cls = cont_cls; + qe->was_transmitted = GNUNET_NO; + qe->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout, &timeout_queue_entry, qe); + qe->response_proc = &process_status_message; + qe->timeout = GNUNET_TIME_relative_to_absolute (timeout); + + vector = (int32_t *) & qe->msg[1]; + // copy each element over to the message + for (i = 0; i < element_count; i++) + vector[i] = htonl (elements[i]); + + process_queue (h); + return qe; +} + + +/** + * Request the Scalar Product Evaluation + * + * @param h handle to the master context + * @param key Session key - unique to the requesting client + * @param peer PeerID of the other peer + * @param element_count Number of elements in the vector + * @param mask_length number of bytes in the mask + * @param elements Array of elements of the vector + * @param mask Array of the mask + * @param timeout Relative timeout for the operation + * @param cont Callback function + * @param cont_cls Closure for the callback function + */ +struct GNUNET_VECTORPRODUCT_QueueEntry * +GNUNET_VECTORPRODUCT_request (struct GNUNET_VECTORPRODUCT_Handle *h, + const struct GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity * peer, + uint16_t element_count, + uint16_t mask_length, + int32_t * elements, + const unsigned char * mask, + struct GNUNET_TIME_Relative timeout, + GNUNET_VECTORPRODUCT_DatumProcessor cont, + void *cont_cls) +{ + struct GNUNET_VECTORPRODUCT_QueueEntry *qe = make_queue_entry (h); + int32_t * vector; + uint16_t size; + unsigned int i; + + GNUNET_assert (GNUNET_SERVER_MAX_MESSAGE_SIZE >= sizeof (struct GNUNET_VECTORPRODUCT_client_request) + +element_count * sizeof (int32_t) + + mask_length); + size = sizeof (struct GNUNET_VECTORPRODUCT_client_request) +element_count * sizeof (int32_t) + mask_length; + + qe->message_size = size; + qe->msg = GNUNET_malloc (size); + qe->msg->header.size = htons (size); + qe->msg->header.type = htons (GNUNET_MESSAGE_TYPE_VECTORPRODUCT_CLIENT_TO_ALICE); + memcpy (&qe->msg->peer, peer, sizeof (struct GNUNET_PeerIdentity)); + qe->msg->element_count = htons (element_count); + qe->msg->mask_length = htons (mask_length); + memcpy (&qe->msg->key, key, sizeof (struct GNUNET_HashCode)); + qe->cont_datum = cont; + qe->cont_cls = cont_cls; + qe->was_transmitted = GNUNET_NO; + qe->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout, &timeout_queue_entry, qe); + qe->response_proc = &process_result_message; + qe->timeout = GNUNET_TIME_relative_to_absolute (timeout); + + vector = (int32_t*) & qe->msg[1]; + // copy each element over to the message + for (i = 0; i < element_count; i++) + vector[i] = htonl (elements[i]); + + // fill in the mask + memcpy (&vector[element_count], mask, mask_length); + + process_queue (h); + return qe; +} + + +/** + * Connect to the vectorproduct service. + * + * @param cfg configuration to use + * @return handle to use to access the service + */ +struct GNUNET_VECTORPRODUCT_Handle * +GNUNET_VECTORPRODUCT_connect (const struct GNUNET_CONFIGURATION_Handle * cfg) +{ + struct GNUNET_CLIENT_Connection *client; + struct GNUNET_VECTORPRODUCT_Handle *h; + + client = GNUNET_CLIENT_connect ("vectorproduct", cfg); + + if (NULL == client) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _ ("Failed to connect to the vectorproduct service\n")); + return NULL; + } + + h = GNUNET_malloc (sizeof (struct GNUNET_VECTORPRODUCT_Handle) + + GNUNET_SERVER_MAX_MESSAGE_SIZE - 1); + h->client = client; + h->cfg = cfg; + h->stats = GNUNET_STATISTICS_create ("vectorproduct-api", cfg); + return h; +} + + +/** + * Disconnect from the vectorproduct service. + * + * @param h handle to the vectorproduct + */ +void +GNUNET_VECTORPRODUCT_disconnect (struct GNUNET_VECTORPRODUCT_Handle * h) +{ + struct GNUNET_VECTORPRODUCT_QueueEntry * qe; + + LOG (GNUNET_ERROR_TYPE_INFO, + "Disconnecting from VectorProduct\n"); + + while (NULL != h->queue_head) + { + GNUNET_assert (NULL != (qe = free_queue_head_entry (h))); + qe->response_proc (qe, NULL, GNUNET_VECTORPRODUCT_Status_ServiceDisconnected); + } + + if (h->client != NULL) + { + GNUNET_CLIENT_disconnect (h->client); + h->client = NULL; + } + + GNUNET_STATISTICS_destroy (h->stats, GNUNET_NO); + h->stats = NULL; +} + +/* end of ext_api.c */ diff --git a/src/scalarproduct/vectorproduct_testing.h b/src/scalarproduct/vectorproduct_testing.h @@ -0,0 +1,129 @@ +/* + * File: vectorproduct_testing.h + * Author: gnunet + * + * Created on June 29, 2013, 7:39 PM + */ + +#ifndef VECTORPRODUCT_TESTING_H +#define VECTORPRODUCT_TESTING_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct GNUNET_VECTORPRODUCT_TESTING_handle +{ + /** + * Testing library system handle + */ + struct GNUNET_TESTING_System *tl_system; + + /** + * head DLL of peers + */ + struct PeerContext *p_head; + + /** + * tail DLL of peers + */ + struct PeerContext *p_tail; +}; + +struct PeerContext +{ + /** + * Next element in the DLL + */ + struct PeerContext *next; + + /** + * Previous element in the DLL + */ + struct PeerContext *prev; + + /** + * Peer's testing handle + */ + struct GNUNET_TESTING_Peer *peer; + + /** + * Peer identity + */ + struct GNUNET_PeerIdentity id; + + /** + * Handle for the peer's ARM process + */ + struct GNUNET_OS_Process *arm_proc; + + /** + * Pointer to Vector Product Handle + */ + struct GNUNET_VECTORPRODUCT_Handle *vh; + + /** + * Closure for the callbacks + */ + void *cb_cls; + + /** + * An unique number to identify the peer + */ + unsigned int no; + + /** + * Peer's configuration + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Pointer to the master testing handle + */ + struct GNUNET_VECTORPRODUCT_TESTING_handle * vth; + + /** + * Callback when two peers are connected and both have called the connect callback + * to notify clients about a new peer + */ + void (*start_cb) (struct PeerContext * p, void *cls); + +// /** +// * Pointer to function where the test occurs +// */ +// GNUNET_VECTORPRODUCT_TESTING_start_cb start_cb; +}; + +/** + * Callback when two peers are connected and both have called the connect callback + * to notify clients about a new peer + */ +typedef void (*GNUNET_VECTORPRODUCT_TESTING_start_cb) (struct PeerContext * p, + void *cls); + +struct GNUNET_VECTORPRODUCT_TESTING_handle * +GNUNET_VECTORPRODUCT_TESTING_init(); + +static void +GNUNET_VECTORPRODUCT_TESTING_done(struct GNUNET_VECTORPRODUCT_TESTING_handle * vth); + +struct PeerContext * +GNUNET_VECTORPRODUCT_TESTING_start_peer (struct GNUNET_VECTORPRODUCT_TESTING_handle * vth, + const char *cfgname, int peer_id, + GNUNET_VECTORPRODUCT_TESTING_start_cb start_cb, + void *cb_cls); + +static void +GNUNET_VECTORPRODUCT_TESTING_stop_peer + (struct GNUNET_VECTORPRODUCT_TESTING_handle * vth, + struct PeerContext *p); + + + + +#ifdef __cplusplus +} +#endif + +#endif /* VECTORPRODUCT_TESTING_H */ +